>[name=澤]本來想做個gpt的二元分類問題,但我找不到現成的資料,我們就拿horse colic這個資料集實作吧@@
那確定好我們的任務後,首先幫大家回顧一下機器學習的步驟:

### step1:資料收集&介紹
[資料集連結](https://archive.ics.uci.edu/dataset/47/horse+colic)
該資料集具有28個屬性,並且有30%的缺失值,共有368筆資料,其中包含馬的年齡、呼吸頻率、體溫等,如下圖,那我們就來預測這個馬是否還存活吧(即屬性23(outcome))

### step2: 資料清理
既然得知我們想要預測的屬性,首先我們看一下attruible,哪些資料是需要的呢?那些屬性是和馬的生死無關的?

觀察後我們發現屬性 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)
```

>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函數,就是用它來做轉換。

[圖片來源](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(支持向量機)
它的基礎概念滿簡單的,找到一條決策邊界,讓不同類別被完美分開,你也可以透過核技巧去將資料投射到更高維度的空間,透過多維度的投影,將原本在二維空間中不可分的點到了高維空間就可以分開了。

>[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 個特徵。

[圖片來源](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)
首先我們先觀察下有什麼參數

你可以看到大致上有這些:
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,但可能數據的部分特徵無法捕捉,請看下圖:

[圖片連結](https://blog.csdn.net/bigFatCat_Tom/article/details/93790753)
coef0:預設 0.0,核函數中的常數項,只對poly、sigmoid有用。

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}")
```
結果如下:

### 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()
```

想要完整的程式碼嗎?去吧,我把程式碼都放這:
[自主學習_監督式學習範例](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)