前言
本文會利用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)。以下是它們之間的主要差異:
- 特徵選取(Feature Selection):
- 定義: 特徵選取是指從原始特徵集中選擇一個子集,而不對特徵進行修改或變換。選擇的特徵是原始特徵的子集,可能是原始特徵的某些子集或者所有特徵的一個子集。
- 目的: 減少特徵維度,提高模型訓練的效率,去除冗餘或不重要的特徵,並避免過擬合。
- 方法: 基於統計方法、信息論、機器學習模型的特徵重要性等,來選擇最具代表性的特徵。
- 特徵萃取(Feature Extraction):
- 定義: 特徵萃取是指通過對原始特徵進行某種變換或轉換,創建新的特徵集。這種轉換通常是非線性的,可以將原始特徵映射到一個新的低維度空間。
- 目的: 降低特徵維度,同時保留數據的最重要信息,提高模型的泛化能力,減少過度擬合的風險。
- 方法: 常見的特徵萃取方法包括主成分分析(PCA)、獨立成分分析(ICA)、t-分佈隨機鄰域嵌入(t-SNE)等。
總體而言,特徵選取是在保留原始特徵的基礎上去除一些不必要的特徵,而特徵萃取則是通過轉換原始特徵來創建新的特徵
程式範例
breast cancer_class 特徵選取VS特徵萃取.ipynb
Streamlit web APP 乳癌預測
Streamlit web APP實現的程式範例會在下一篇文章有更多的介紹
此範例在Colab上實做,故採用.ipynb的方式呈現,以下將會分段介紹,如程式範例連結的順序說明。
載入相關套件
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
1. 載入資料集
ds = datasets.load_breast_cancer()
加載了乳腺癌資料集。這是一個常用的二元分類資料集,用於預測乳腺腫瘤是良性還是惡性
2. 資料清理、資料探索與分析
後面將採用pandas的方式來做這三步驟
查看資料夾說明
# 資料集說明
print(ds.DESCR)
返回的資料集是乳腺癌資料集,包含了30個特徵變數,用來預測乳腺腫瘤是良性(benign)還是惡性(malignant)。以下是該資料集的相關說明:
乳腺癌資料集(Breast Cancer Dataset)
特徵變數(Features):
- radius_mean: 平均半徑
- texture_mean: 平均紋理
- perimeter_mean: 平均周長
- area_mean: 平均面積
- smoothness_mean: 平均光滑度
- compactness_mean: 平均緊湊性
- concavity_mean: 平均凹度
- concave points_mean: 平均凹點
- symmetry_mean: 平均對稱性
- fractal_dimension_mean: 平均分形維度
- radius_se: 標準誤半徑
- texture_se: 標準誤紋理
- perimeter_se: 標準誤周長
- area_se: 標準誤面積
- smoothness_se: 標準誤光滑度
- compactness_se: 標準誤緊湊性
- concavity_se: 標準誤凹度
- concave points_se: 標準誤凹點
- symmetry_se: 標準誤對稱性
- fractal_dimension_se: 標準誤分形維度
- radius_worst: 最大半徑
- texture_worst: 最大紋理
- perimeter_worst: 最大周長
- area_worst: 最大面積
- smoothness_worst: 最大光滑度
- compactness_worst: 最大緊湊性
- concavity_worst: 最大凹度
- concave points_worst: 最大凹點
- symmetry_worst: 最大對稱性
- fractal_dimension_worst: 最大分形維度
目標變數(Target):
- target: 0 表示良性(benign),1 表示惡性(malignant)
轉換為 DataFrame 格式
轉換成DataFrame的格式才方便後續去做資料探索及分析處理
import pandas as pd
df = pd.DataFrame(ds.data, columns=ds.feature_names)
df

df
查看目標值名稱
ds.target_names

目標值名稱
觀察資料集彙總資訊
df.info()
目的是看資料集是否有缺失值,或每個特徵的數量是否都符合,及資料格式

查詢是否有遺失值
都是0代表都沒遺失。
df.isnull().sum()

特徵的描述統計量
這些統計量提供了對數據分布的基本了解
# 描述統計量
df.describe()
- count: 樣本數量,表示每個特徵中非缺失值的數量。
- mean: 平均值,表示每個特徵的平均數值。
- std: 標準差,衡量數據值的分散程度。
- min: 最小值,每個特徵中的最小數值。
- 25%: 第一四分位數(Q1),25% 百分位數,表示數據的前 25% 範圍內的值。
- 50%: 中位數(Q2),50% 百分位數,表示數據的中位數或第二四分位數。
- 75%: 第三四分位數(Q3),75% 百分位數,表示數據的前 75% 範圍內的值。
- max: 最大值,每個特徵中的最大數值。

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')
測試選取2個特徵的所有組合
測試所有的兩個特徵的組合,從中得到哪兩個特徵是最重要的
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()
- 建立 K 最近鄰分類器:
knn = KNeighborsClassifier(n_neighbors=11)
創建了一個 K 最近鄰分類器,其中 n_neighbors=11 表示選擇 11 個最近的鄰居進行分類。
- 建立 SequentialFeatureSelector:
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的部分
# 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` 就是降維後的訓練和測試數據,形狀為 `(樣本數, 主成分數)`。這有助於減少數據的維度,同時保持數據中的主要變異性。
scikit-learn 中已經提供了現成的 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%