本文會利用sklearn引入breast_cancer datasets來訓練,在處理數據的部份,特徵工程用兩種方式去做處理,分別是特徵選取與特徵萃取的方式去做比較。
特徵選取的方法中,使用了KNN 分類器來選出最重要的兩個特徵
特徵萃取的方法中,使用了PCA降維到兩個特徵
資料集採用:breast_cancer datasets
模型採用 : LogisticRegression()邏輯斯回歸(二分類)
特徵工程:分三種,一、不做特徵工程,二、特徵選取,三、特徵萃取
實驗目的:分別比較其優劣
準確率 : 97.37%(不做特徵工程)> 95.61%特徵萃取(PCA) > 91.22%特徵選取(Knn-2)
做了PCA準確率還有95.61%,相比於不做特徵工程的狀況下,對於未知的資料可能預測能力會更好一些,畢竟不會受其他不重要的特徵影響。
在30個特徵中挑出2個最重要的特徵來訓練預測,還有91.22%的準確率代表其他28的特徵的存在感是多麼薄弱
總結一下兩者的優缺點:
優點 :
缺點:
資訊損失: 可能會將重要的資訊去除,或忽略掉相關聯的資料
特徵選取(Feature Selection)和特徵萃取(Feature Extraction)是機器學習中兩個不同的概念,用於處理數據的特徵(features)。以下是它們之間的主要差異:
總體而言,特徵選取是在保留原始特徵的基礎上去除一些不必要的特徵,而特徵萃取則是通過轉換原始特徵來創建新的特徵
Streamlit web APP實現的程式範例會在下一篇文章有更多的介紹
此範例在Colab上實做,故採用.ipynb的方式呈現,以下將會分段介紹,如程式範例連結的順序說明。
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
ds = datasets.load_breast_cancer()
加載了乳腺癌資料集。這是一個常用的二元分類資料集,用於預測乳腺腫瘤是良性還是惡性
後面將採用pandas的方式來做這三步驟
# 資料集說明
print(ds.DESCR)
返回的資料集是乳腺癌資料集,包含了30個特徵變數,用來預測乳腺腫瘤是良性(benign)還是惡性(malignant)。以下是該資料集的相關說明:
轉換成DataFrame的格式才方便後續去做資料探索及分析處理
import pandas as pd
df = pd.DataFrame(ds.data, columns=ds.feature_names)
df
ds.target_names
df.info()
目的是看資料集是否有缺失值,或每個特徵的數量是否都符合,及資料格式
都是0代表都沒遺失。
df.isnull().sum()
這些統計量提供了對數據分布的基本了解
# 描述統計量
df.describe()
察箱型圖,你可以得到以下資訊:
# 箱型圖
import seaborn as sns
sns.boxplot(data=df)
有箱型圖就可見,只有兩個特徵是可參考而已,其他特徵值相似度都太高無參考價值
若兩者差異太大,也會影響到訓練出來的結果
# y 各類別資料筆數統計
import seaborn as sns
sns.countplot(x=y)
# 以Pandas函數統計各類別資料筆數
pd.Series(y).value_counts()
#出現以下資料
# 1 357
# 0 212
因為許多機器學習庫(如 scikit-learn)通常接受 NumPy 陣列作為輸入而不是 DataFrame,所以要轉回Numpy的模式
# 指定X,並轉為 Numpy 陣列
X = df.values
# 資料分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.2)
# 查看陣列維度
X_train.shape, X_test.shape, y_train.shape, y_test.shape
標準化可以幫助模型更好地處理不同尺度的特徵,提高模型的性能和收斂速度。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_std = scaler.fit_transform(X_train)
X_test_std = scaler.transform(X_test)
fit_transform
用於學習訓練集的統計參數並同時進行轉換,而 transform
僅用於對數據進行轉換
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression()
定義clf物件為邏輯斯模型
clf.fit(X_train_std, y_train)
得知全部的三十個特徵一起訓練出的模型,去預測驗證集得到 98.25%的準確率
# 計算準確率
print(f'{accuracy_score(y_test, y_pred)*100:.2f}%')
# 98.25%
也有用混淆矩陣來評估,是把假的當成真的,還是真的當成假的
# 混淆矩陣圖
from sklearn.metrics import ConfusionMatrixDisplay
import matplotlib.pyplot as plt
disp = ConfusionMatrixDisplay(confusion_matrix=confusion_matrix(y_test, y_pred)
, display_labels=ds.target_names)
disp.plot()
plt.show()
訓練好的模型當然要存起來,因為有特徵縮放,也要將特徵縮放的參數一併儲存喔
# # 模型存檔
import joblib
joblib.dump(clf, 'model.breast')
joblib.dump(scaler, 'scaler.breast')
測試所有的兩個特徵的組合,從中得到哪兩個特徵是最重要的
from sklearn.metrics import accuracy_score
from sklearn.feature_selection import SequentialFeatureSelector
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=11)
sfs = SequentialFeatureSelector(knn, n_features_to_select=2)
sfs.fit(X_train_std, y_train)
sfs.get_support()
knn = KNeighborsClassifier(n_neighbors=11)
創建了一個 K 最近鄰分類器,其中 n_neighbors=11 表示選擇 11 個最近的鄰居進行分類。sfs = SequentialFeatureSelector(knn, n_features_to_select=2)
創建了一個 SequentialFeatureSelector
物件,指定了要使用的分類器 knn 和要選擇的特徵數目 n_features_to_select=2。
sfs.fit(X_train_std, y_train)
使用標準化後的訓練數據 X_train_std
和對應的目標變數 y_train
來擬合模型,進行特徵選擇。sfs.get_support()
這個方法返回一個布林陣列,指示每個特徵是否被選擇。如果特徵被選擇,對應的布林值為 True;否則,為 False。印出worst perimeter與worst smoothness 這兩個經過特徵選取的結果
# 特徵選取名稱
sfs.get_feature_names_out(column_list)
#印出結果# array(['worst perimeter', 'worst smoothness'], dtype=object)
將原先的訓練資料轉換成只有特徵選取的部分,順便印出形狀確認資料維度
sfs.transform(X_train_std).shap
因為資料轉換是由特徵縮放過得去轉換,所以這邊不用在做一次特徵縮放了
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression()
clf.fit(sfs.transform(X_train_std), y_train)
clf.score(sfs.transform(X_test_std), y_test)
#得出結果 : 0.91
換做PCA的部分
# PCA 函數實作
def PCA_numpy(X, X_test, no):
cov_mat = np.cov(X.T)
# 計算特徵值(eigenvalue)及對應的特徵向量(eigenvector)
eigen_val, eigen_vecs = np.linalg.eig(cov_mat)
# 合併特徵向量及特徵值
eigen_pairs = [(np.abs(eigen_val[i]), eigen_vecs[:,i]) for i in range(len(eigen_vecs))]
# 針對特徵值降冪排序
eigen_pairs.sort(key=lambda x: x[0], reverse=True)
w = eigen_pairs[0][1][:, np.newaxis]
for i in range(1, no):
w = np.hstack((w, eigen_pairs[i][1][:, np.newaxis]))
# 轉換:矩陣相乘 (n, m) x (m, 2) = (n, 2)
return X.dot(w), X_test.dot(w)
X_train_pca, X_test_pca = PCA_numpy(X_train_std, X_test_std, 2) # 取 2 個特徵
X_train_pca.shape, X_test_pca.shape
這個PCA函數的目的是執行主成分分析(Principal Component Analysis),將原始的特徵空間轉換為更低維度的空間,同時保留數據集中的主要變異性。以下是對函數主要部分的解釋:
cov_mat = np.cov(X.T)
: 計算輸入資料 X
的協方差矩陣,X.T
是 X
的轉置。eigen_val, eigen_vecs = np.linalg.eig(cov_mat)
: 使用 np.linalg.eig
函數計算協方差矩陣的特徵值和特徵向量。eigen_pairs = [(np.abs(eigen_val[i]), eigen_vecs[:, i]) for i in range(len(eigen_vecs))]
: 創建特徵值和特徵向量對的列表,其中 np.abs
用於取特徵值的絕對值。eigen_pairs.sort(key=lambda x: x[0], reverse=True)
: 將特徵值特徵向量對按照特徵值的降冪順序進行排序。w = eigen_pairs[0][1][:, np.newaxis]
: 初始化 w
,將第一個特徵向量轉為列向量。for i in range(1, no): w = np.hstack((w, eigen_pairs[i][1][:, np.newaxis]))
: 進行迴圈,將後續的特徵向量以列的形式附加到 w
中,以構建降維的轉換矩陣。return X.dot(w), X_test.dot(w)
: 使用 dot
方法進行矩陣相乘,將訓練數據和測試數據投影到所選擇的主成分上。X_train_pca.shape, X_test_pca.shape
返回降維後的訓練和測試數據的形狀。這樣,`X_train_pca` 和 `X_test_pca` 就是降維後的訓練和測試數據,形狀為 `(樣本數, 主成分數)`。這有助於減少數據的維度,同時保持數據中的主要變異性。
也可以用這行來取代上方的範例,主要上方的範例是解釋其PCA的流程
from sklearn.decomposition import PCA
# 創建 PCA 實例,指定要保留的主成分數量
pca = PCA(n_components=2)
# 在訓練數據上擬合 PCA 模型,同時進行轉換
X_train_pca_sklearn = pca.fit_transform(X_train_std)
X_test_pca_sklearn = pca.transform(X_test_std)
# 查看降維後的數據形狀
X_train_pca_sklearn.shape, X_test_pca_sklearn.shape
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression()
# 計算準確率
y_pred = clf.predict(X_test_pca)
print(f'{accuracy_score(y_test, y_pred)*100:.2f}%')
#印出結果 94.74%