概述
主成分分析(Principal Component Analysis,PCA)是一種常用的資料降維和特徵提取技術,用於將高維資料轉換為低維的特徵空間。其目標是透過線性變換將原始特徵轉化為一組新的互相無關的變數,這些新變數稱為主成分,它們按照方差遞減的順序排列,以保留儘可能多的原始資料資訊。
主成分分析的基本思想可以總結如下:
- 尋找新的特徵空間:PCA透過線性變換,尋找一組新的特徵空間,使得新的特徵具有以下性質:
- 主成分具有最大的方差,儘可能保留原始資料的資訊。
- 不同主成分之間彼此無關,即它們是正交的(互相垂直)。
- 降低資料維度:保留方差較大的主成分,捨棄方差較小的主成分,從而實現資料降維。
主成分分析的步驟如下:
- 中心化資料:將原始資料進行中心化,使得資料的均值為零。
- 計算協方差矩陣:計算特徵之間的協方差矩陣,描述了特徵之間的線性關係。
- 計算特徵值和特徵向量:對協方差矩陣進行特徵值分解,得到特徵值和對應的特徵向量。
- 選擇主成分:按照特徵值的大小選擇保留的主成分數量,通常選擇方差較大的前幾個主成分。
- 得到新的特徵空間:將原始特徵投影到選定的主成分上,得到新的特徵空間。
主成分分析的應用包括降維、去除資料噪聲、資料視覺化、特徵選擇等。透過保留最重要的特徵,可以在減少資料維度的同時保持對資料的關鍵資訊進行捕獲。
在實際使用中,有時會將各個變數進行標準化,此時的協方差矩陣就相當於原始資料的相關係數矩陣。所以Alink的主成分分析元件提供了兩種計算選擇,引數CalculationType可以設定為相關係數矩陣(CORR)或者協方差矩陣(COV),預設為相關係數矩陣,即對標準化後的資料計算其主成分。
Alink庫中的實現與應用
示例
以美國50個州的7種犯罪率為例,做主成分分析。這7種犯罪分別是:"murder", "rape", "robbery", "assault", "burglary", "larceny", "auto"。從這7個變數出發來評價各州的治安和犯罪情況是很難的,而使用主成分分析可以把這些變數概括為2-3個綜合變數(即主成分),便於更簡便的分析這些資料。
/**
* 主成分分析
* 1.基於預設的計算方式(CORR),計算主成分
* 2.設定K為4,將原先的7個維度降低到4個維度
* 3.輸出向量列,使用VectorToColumnsBatchOp組元件將向量列轉為4個資料列,名稱分別為"prin1, prin2, prin3, prin4"
* */
static void c_1() throws Exception {
MemSourceBatchOp source = new MemSourceBatchOp(CRIME_ROWS_DATA, CRIME_COL_NAMES);
source.lazyPrint(10, "Origin data");
BatchOperator <?> pca_result = new PCA()
.setK(4)
.setSelectedCols("murder", "rape", "robbery", "assault", "burglary", "larceny", "auto")
.setPredictionCol(VECTOR_COL_NAME)
.enableLazyPrintModelInfo()
.fit(source)
.transform(source)
.link(
new VectorToColumnsBatchOp()
.setVectorCol(VECTOR_COL_NAME)
.setSchemaStr("prin1 double, prin2 double, prin3 double, prin4 double")
.setReservedCols("state")
)
.lazyPrint(10, "state with principle components");
pca_result
.select("state, prin1")
.orderBy("prin1", 100, false)
.lazyPrint(-1, "Order by prin1");
pca_result
.select("state, prin2")
.orderBy("prin2", 100, false)
.lazyPrint(-1, "Order by prin2");
BatchOperator.execute();
}
當然還可以先將資料標準化後再做主成分分析。如下
/**
* 主成分分析
* 1. 先將資料標準化
* 2. 設定計算方式為協方差計算,設定K為4,將原先的7個維度降低到4個維度
* 3.輸出向量列,使用VectorToColumnsBatchOp組元件將向量列轉為4個資料列,名稱分別為"prin1, prin2, prin3, prin4"
* */
static void c_2() throws Exception {
MemSourceBatchOp source = new MemSourceBatchOp(CRIME_ROWS_DATA, CRIME_COL_NAMES);
Pipeline std_pca = new Pipeline()
.add(
new StandardScaler()
.setSelectedCols("murder", "rape", "robbery", "assault", "burglary", "larceny", "auto")
)
.add(
new PCA()
.setCalculationType(CalculationType.COV)
.setK(4)
.setSelectedCols("murder", "rape", "robbery", "assault", "burglary", "larceny", "auto")
.setPredictionCol(VECTOR_COL_NAME)
.enableLazyPrintModelInfo()
);
std_pca
.fit(source)
.transform(source)
.link(
new VectorToColumnsBatchOp()
.setVectorCol(VECTOR_COL_NAME)
.setSchemaStr("prin1 double, prin2 double, prin3 double, prin4 double")
.setReservedCols("state")
)
.lazyPrint(10, "state with principle components");
BatchOperator.execute();
}
應用
在聚類方面的應用
主要透過降維來減少特徵的維度,從而在聚類過程中降低資料的複雜度和計算成本,同時提高聚類的效果。主要實現過程如下:
- 使用 PCA 對資料進行降維,得到新的特徵空間。設定降維後的維度,通常選擇較小的維度以減少特徵數。
- 在降維後的特徵空間上應用聚類演算法,比如 K-means、DBSCAN 等。
- 使用適當的聚類評估指標,如輪廓係數等,來評估聚類的效果。
示例程式碼如下:
/**
* 聚類+主成分分析
* 1. 將資料降維,只使用5%的維度資料
* 2. K-Means聚類:分別將原始資料與主成分分析後的資料做聚類操作
* */
static void c_3() throws Exception {
AkSourceBatchOp source = new AkSourceBatchOp().setFilePath(DATA_DIR + SPARSE_TRAIN_FILE);
source
.link(
new PcaTrainBatchOp()
.setK(39)
.setCalculationType(CalculationType.COV)
.setVectorCol(VECTOR_COL_NAME)
.lazyPrintModelInfo()
)
.link(
new AkSinkBatchOp()
.setFilePath(DATA_DIR + PCA_MODEL_FILE)
.setOverwriteSink(true)
);
BatchOperator.execute();
BatchOperator <?> pca_result = new PcaPredictBatchOp()
.setVectorCol(VECTOR_COL_NAME)
.setPredictionCol(VECTOR_COL_NAME)
.linkFrom(
new AkSourceBatchOp().setFilePath(DATA_DIR + PCA_MODEL_FILE),
source
);
Stopwatch sw = new Stopwatch();
KMeans kmeans = new KMeans()
.setK(10)
.setVectorCol(VECTOR_COL_NAME)
.setPredictionCol(PREDICTION_COL_NAME);
sw.reset();
sw.start();
kmeans
.fit(source)
.transform(source)
.link(
new EvalClusterBatchOp()
.setVectorCol(VECTOR_COL_NAME)
.setPredictionCol(PREDICTION_COL_NAME)
.setLabelCol(LABEL_COL_NAME)
.lazyPrintMetrics("KMeans")
);
BatchOperator.execute();
sw.stop();
System.out.println(sw.getElapsedTimeSpan());
sw.reset();
sw.start();
kmeans
.fit(pca_result)
.transform(pca_result)
.link(
new EvalClusterBatchOp()
.setVectorCol(VECTOR_COL_NAME)
.setPredictionCol(PREDICTION_COL_NAME)
.setLabelCol(LABEL_COL_NAME)
.lazyPrintMetrics("KMeans + PCA")
);
BatchOperator.execute();
sw.stop();
System.out.println(sw.getElapsedTimeSpan());
}