>[name=澤]本來想做個gpt的二元分類問題,但我找不到現成的資料,我們就拿horse colic這個資料集實作吧@@ 那確定好我們的任務後,首先幫大家回顧一下機器學習的步驟: ![範例圖](https://hackmd.io/_uploads/HyBhW_cQC.jpg) ### step1:資料收集&介紹 [資料集連結](https://archive.ics.uci.edu/dataset/47/horse+colic) 該資料集具有28個屬性,並且有30%的缺失值,共有368筆資料,其中包含馬的年齡、呼吸頻率、體溫等,如下圖,那我們就來預測這個馬是否還存活吧(即屬性23(outcome)) ![image](https://hackmd.io/_uploads/rJXF9NcmR.png =60%x) ### step2: 資料清理 既然得知我們想要預測的屬性,首先我們看一下attruible,哪些資料是需要的呢?那些屬性是和馬的生死無關的? ![image](https://hackmd.io/_uploads/H1rSBJimC.png =50%x) 觀察後我們發現屬性 3(hospital_number)、 25(lesion_site) 、26(lesion_type)、27(lesion_subtype)、28(cp_data)是不相關的,因此先將這些屬性刪除吧! ```pyhton= # load train/test data data = np.genfromtxt(fname='/content/drive/MyDrive/horse-colic/horse-colic.data',delimiter=' ',dtype=float) data_test = np.genfromtxt(fname='/content/drive/MyDrive/horse-colic/horse-colic.test',delimiter=' ',dtype=float) # data pre-processing #change nd.array to dataframe,and give it index train = pd.DataFrame(data,columns=range(1,29),index=range(1,301)) test = pd.DataFrame(data_test,columns=range(1,29),index=range(1,69)) # drop the Attributes 3 25 26 27 28,(index is begin 1) train = train.drop(columns=[3, 25, 26, 27, 28]) test = test.drop(columns=[3, 25, 26, 27, 28]) ``` 首先我將資料從nd.array轉成資料表,並且給予其標籤,阿記得python的range並不包含其最後一個數字(stop),所以我才寫,columns=range(1,29),index=range(1,301) >[name=澤]其實我有點懶得切測試訓練資料,所以直接套現成的,但如果想切很推薦使用sklearn提供的train_test_split,非常好用! [切割套件連結請點我](https://scikit-learn.org/0.19/modules/generated/sklearn.model_selection.train_test_split.html) 不過接下來我們發現,outcome這個屬性有三種,1,2,3,因為他將安樂死獨立出來,因此我們須將3(安樂死)替換成2(死亡),以下是程式碼: ``` python= # change label euthanized with died for index, row in train.iterrows(): if row[23] == 3: train.loc[index, 23] = 2 for index, row in test.iterrows(): if row[23] == 3: train.loc[index, 23] = 2 ``` >[name=澤]iterrows是迭代器,他會遍歷每個元素 你以為到這裡資料清洗就結束了嗎?錯!!!還有缺失值要補上呢!不過也有一些feature selection的套件'可以使用,可以自動選擇那些特徵是不用的 [feature selection](https://scikit-learn.org/stable/modules/feature_selection.html) 那該如何補上缺失值呢?,這裡我們也使用sklearn套件的[KNNImputer](https://scikit-learn.org/stable/modules/generated/sklearn.impute.KNNImputer.html) ```python= # NA fill in,because model can not access NA imputer = KNNImputer(n_neighbors = 3, metric='nan_euclidean') train = imputer.fit_transform(train) test = imputer.transform(test) # make the label(or target),so use y y_train = train[:,22] y_test = test[:,22] print('after change label euthanized to died') print(y_train) ``` ![image](https://hackmd.io/_uploads/HyTYkeo7R.png) >nan_euclidean你可以想成找缺失值附近的鄰居,就是歐基里德距離拉,y_train就是預測目標拉 ### step3: 選擇模型 我們選擇四種模型來挑戰這個任務,首先登場的是我們一號選手KNN #### KNN 該模型又稱為最近鄰居法,簡單來說有以下步驟 * 1.決定k值(鄰居數量) * 2.決定距離計算方法(歐基里德、明可夫斯基、曼哈頓距離等) * 3.選擇你想加入哪一群 聽起來很簡單吧,我覺得他是相對直觀的模型 一樣連結在這,這裡先不細講參數部分[sklearn-KNN](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html) ```python= from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score model = KNeighborsClassifier(n_neighbors=3, weights='distance', algorithm='auto', leaf_size=20, p=2) model.fit(train,y_train) pred = model.predict(test) acc = accuracy_score(y_test, pred) print(f"Accuracy: {acc}") # Accuracy: 0.7352941176470589 ``` #### 邏輯迴歸(Logistic Regression) 這個我們介紹過拉,就不贅述了,簡單來說他會記錄每個觀測值的條件機率,並將其相加以產生預測機率。對於二元分類,小於 0.5 的機率將預測 0,而大於 0 的機率將預測 1,我們之前有介紹過sigmod函數,就是用它來做轉換。 ![image](https://hackmd.io/_uploads/Hkzqqfo7C.png =50%x) [圖片來源](https://ithelp.ithome.com.tw/m/articles/10269006) [點我看Logistic Regression](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html) ```python= from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score model = LogisticRegression().fit(train, y_train) pred = model.predict(test) acc = accuracy_score(y_test, pred) print(f"Accuracy: {acc}") #Accuracy: 1.0 ``` #### SVM(支持向量機) 它的基礎概念滿簡單的,找到一條決策邊界,讓不同類別被完美分開,你也可以透過核技巧去將資料投射到更高維度的空間,透過多維度的投影,將原本在二維空間中不可分的點到了高維空間就可以分開了。 ![image](https://hackmd.io/_uploads/rk6j1fiXR.png =50%x) >[name=澤]其實SVM可以玩的東西滿多的,比如用多項式核和RBF核等,那連結我一樣放這大家可以看看~ [SVM](https://scikit-learn.org/stable/modules/svm.html) 這邊我們就寫個簡單的模型玩玩就好啦~ ```python= from sklearn import svm from sklearn.metrics import accuracy_score # Radial Basis Function 高斯轉換 model = svm.SVC(kernel="rbf", gamma="auto",decision_function_shape='ovo') model.fit(train, y_train) pred = model.predict(test) acc = accuracy_score(y_test, pred) print(f"Accuracy: {acc}") #Accuracy: 0.6911764705882353 ``` #### random forest(隨機森林) 許多不同的決策樹所組成的森林,這種方法又稱為Ensemble Method(集成方法),透過多棵決策樹投票表決最終的類別,而隨機森林的隨機是先隨機抽n筆資料,這些資料室可以被隨機抽取的,就是 Bootstrap,而在 sklearn 中,最多隨機選取 log2N 個特徵。 ![image](https://hackmd.io/_uploads/SJSx9fi7A.png =50%x) [圖片來源](https://ithelp.ithome.com.tw/m/articles/10272586) [點我看random forest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html) ```python= from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score model = RandomForestClassifier(n_estimators=100, criterion = 'gini',max_depth=10) model.fit(train, y_train) pred = model.predict(test) acc = accuracy_score(y_test, pred) print(f"Accuracy: {acc}") #Accuracy: 1.0 ``` ### step4: 調整參數 好啦,接下來講一下調整參數,剛剛看了那麼多模型,但都不確定裡面的參數定義是啥對吧,那有沒有一種方式可以幫我們直接找出最好的參數勒? >[name=澤]接下來我們以svm模型的參數使用GridSearchCV和Pipeline找出最好參數 [pipline](https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html) [GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html) 首先我們先觀察下有什麼參數 ![image](https://hackmd.io/_uploads/rk4MmZaX0.png) 你可以看到大致上有這些: C:float,預設1.0,錯誤項的懲罰係數,svm中正規化參數,C值越大,分類錯誤懲罰越嚴厲,但也可能導致overfitting,訓練資料上表現好,但測試集不佳,反之C越小,模型能容忍一些錯誤,泛化能力可能更強,但也可能導致underfitting。 kernel:有linear(線性核參數), poly(多項式核參數), rbf(高斯核/預設), sigmoid(sigmod核函數),precomputed(核矩陣),當然你也可以給自定義的核函數計算核矩陣。 degree:int,預設為3,不可為負數,該參數僅能用於poly,代表其的階數。 gamma:有scale(預設)、auto或浮點數,只對rbf(高斯核/預設),sigmoid(sigmod核函數),precomputed(核矩陣)有用,主要是對低維度樣本進行高度映射(決定數據到高維空間的曲率),較小的gamms可使決策邊界更加平滑,有助於減少模型複雜度,避免overfitting,但可能數據的部分特徵無法捕捉,請看下圖: ![image](https://hackmd.io/_uploads/Hku3Ow1V0.png =80%x) [圖片連結](https://blog.csdn.net/bigFatCat_Tom/article/details/93790753) coef0:預設 0.0,核函數中的常數項,只對poly、sigmoid有用。 ![image](https://hackmd.io/_uploads/ryTxsw1EA.png) shrinking:bool,預設true,是否採用啟發式收縮。 probability:bool,預設true,是否使用概率估計,不過一定要在fit()前使用。 tol:float, default=1e-3,停止訓練誤差精度。 cache_size:訓練所需內存,預設200MB。 class_weight:balanced、default=None,給每個類別設置不同的懲罰參數C,沒有給的話則給所以類別1。 verbose:bool, default=False,是否啟用詳細輸出,不過啟用該設置可能導致poly沒法正常工作。 max_iter:int,預設-1,最大迭代次數,-1代表不限制。 >還有一些但我就不介紹完,自己去看吧~ 好的,那我現在想搭配GridSearchCV和pipline找出最佳參數,該怎麼做呢? ```python= from sklearn.svm import SVC from sklearn.metrics import accuracy_score from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler # 設置 Pipeline pipe = Pipeline([('scaler', StandardScaler()), ('svc', SVC())]) # scaler可以參考https://scikit-learn.org/stable/modules/preprocessing.html StandardScaler是平均&變異數標準化 # 設置參數 param_grid = { 'svc__C': [0.1, 1, 10, 100], 'svc__kernel': ['linear', 'poly', 'rbf', 'sigmoid'], 'svc__gamma': ['scale', 'auto'], 'svc__degree': [2, 3, 4], # 只在 kernel 為 'poly' 時有效 'svc__coef0': [0.0, 0.1, 0.5] # 只在 kernel 為 'poly' 或 'sigmoid' 時有效 } # 設置 GridSearchCV cv是交叉驗證次數,scoring是評估標準,n_jobs示使用全部可用的CPU核心,verbose設置日誌信息 grid_search = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy', n_jobs=-1, verbose=2) # 進行參數搜尋 grid_search.fit(train, y_train) # 最佳參數和準確率 best_model = grid_search.best_estimator_ pred = best_model.predict(test) acc = accuracy_score(y_test, pred) print(f"Best Parameters: {grid_search.best_params_}") print(f"Accuracy: {acc}") ``` 結果如下: ![image](https://hackmd.io/_uploads/Sk4bXu14C.png) ### step5: 分析、預測結果 分析的方式很多,我們就舉AUC跟F1_score吧! ```python= from sklearn.preprocessing import LabelEncoder # 重新編碼標籤,本來是1 2 ,更改一下 le = LabelEncoder() y_train_encoded = le.fit_transform(y_train) y_test_encoded = le.transform(y_test) # 計算 F1-score f1 = f1_score(y_test_encoded, pred, average='weighted') auc = roc_auc_score(y_test_encoded, best_model.decision_function(test)) # 混淆矩陣和分類報告 conf_matrix = confusion_matrix(y_test_encoded, pred) class_report = classification_report(y_test_encoded, pred) # 輸出結果 print(f"F1 Score: {f1}") print(f"AUC: {auc}") print(f"Classification Report:\n{class_report}") # 繪製 ROC 曲線,僅當有兩個類別時 fpr, tpr, _ = roc_curve(y_test_encoded, best_model.decision_function(test)) plt.figure() plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {auc:.2f})') plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver Operating Characteristic (ROC)') plt.legend(loc="lower right") plt.show() ``` ![image](https://hackmd.io/_uploads/BylJEtJNR.png =80%x) 想要完整的程式碼嗎?去吧,我把程式碼都放這: [自主學習_監督式學習範例](https://colab.research.google.com/drive/1WoWMkGJFfhcyZ1CrjL8SHI4q4hMvffJp#scrollTo=row6BWY0H5L3) ### 參考連結: [核模型 - 支持向量機 (SVM)](https://ithelp.ithome.com.tw/m/articles/10270447) [ML入門(十一)支援向量機](https://medium.com/chung-yi/ml%E5%85%A5%E9%96%80-%E5%8D%81%E4%B8%80-%E6%94%AF%E6%8F%B4%E5%90%91%E9%87%8F%E6%A9%9F-support-vector-machine-svm-c8c1bb1c970f) [random forest](https://medium.com/chung-yi/ml%E5%85%A5%E9%96%80-%E5%8D%81%E4%B8%83-%E9%9A%A8%E6%A9%9F%E6%A3%AE%E6%9E%97-random-forest-6afc24871857) [多棵決策樹更厲害:隨機森林 (Random forest)](https://ithelp.ithome.com.tw/m/articles/10272586) [SVM參數介紹](https://blog.csdn.net/github_39261590/article/details/75009069)