吳恩達機器學習作業(五):支援向量機
目錄
在本練習中,我們將使用支援向量機(SVM)來構建垃圾郵件分類器。 我們將從一些簡單的2D資料集開始使用SVM來檢視它們的工作原理。 然後,我們將對一組原始電子郵件進行一些預處理工作,並使用SVM在處理的電子郵件上構建分類器,以確定它們是否為垃圾郵件。
我們要做的第一件事是看一個簡單的二維資料集,看看線性SVM如何對資料集進行不同的C值(類似於線性/邏輯迴歸中的正則化項)。
1)資料預處理
同樣的我們還是先看看我們的資料長什麼樣子:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb
from scipy.io import loadmat
raw_data = loadmat('ex6data1.mat')
raw_data
我們將其用散點圖表示,其中類標籤由符號表示(+表示正類,o表示負類)。
data = pd.DataFrame(raw_data['X'], columns=['X1', 'X2']) data['y'] = raw_data['y'] positive = data[data['y'].isin([1])] negative = data[data['y'].isin([0])] fig, ax = plt.subplots(figsize=(12,8)) ax.scatter(positive['X1'], positive['X2'], s=50, marker='x', label='Positive') ax.scatter(negative['X1'], negative['X2'], s=50, marker='o', label='Negative') ax.legend() plt.show()
2)Scikit-learn支援向量機
from sklearn import svm
svc = svm.LinearSVC(C=1, loss='hinge', max_iter=1000)
svc
首先,我們使用 C=1 看下結果如何。
svc.fit(data[['X1', 'X2']], data['y'])
svc.score(data[['X1', 'X2']], data['y'])
0.9803921568627451
其次,讓我們看看如果C的值越大,會發生什麼。
svc2 = svm.LinearSVC(C=100, loss='hinge', max_iter=1000) svc2.fit(data[['X1', 'X2']], data['y']) svc2.score(data[['X1', 'X2']], data['y']) 1.0
3)決策邊界比較
這次我們得到了訓練資料的完美分類,但是通過增加C的值,我們建立了一個不再適合資料的決策邊界。 我們可以通過檢視每個類別預測的置信水平來看出這一點,這是該點與超平面距離的函式。
data['SVM 1 Confidence'] = svc.decision_function(data[['X1', 'X2']])
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 1 Confidence'], cmap='seismic')
ax.set_title('SVM (C=1) Decision Confidence')
plt.show()
data['SVM 2 Confidence'] = svc2.decision_function(data[['X1', 'X2']])
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(data['X1'], data['X2'], s=50, c=data['SVM 2 Confidence'], cmap='seismic')
ax.set_title('SVM (C=100) Decision Confidence')
plt.show()
4)非線性SVM
現在我們將從線性SVM轉移到能夠使用核心進行非線性分類的SVM。 我們首先負責實現一個高斯核函式。 雖然scikit-learn具有內建的高斯核心,但為了實現更清楚,我們將從頭開始實現。
def gaussian_kernel(x1, x2, sigma):
return np.exp(-(np.sum((x1 - x2) ** 2) / (2 * (sigma ** 2))))
接下來,我們將檢查另一個數據集,這次用非線性決策邊界。
raw_data = loadmat('ex6data2.mat')
data = pd.DataFrame(raw_data['X'], columns=['X1', 'X2'])
data['y'] = raw_data['y']
positive = data[data['y'].isin([1])]
negative = data[data['y'].isin([0])]
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['X1'], positive['X2'], s=30, marker='x', label='Positive')
ax.scatter(negative['X1'], negative['X2'], s=30, marker='o', label='Negative')
ax.legend()
plt.show()
為了視覺化決策邊界,這一次我們將根據例項具有負類標籤的預測概率來對點做陰影。 從結果可以看出,它們大部分是正確的。
svc = svm.SVC(C=100, gamma=10, probability=True)
svc
data['Probability'] = svc.predict_proba(data[['X1', 'X2']])[:,0]
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(data['X1'], data['X2'], s=30, c=data['Probability'], cmap='Reds')
plt.show()
5)最優超引數
對於第三個資料集,我們給出了訓練和驗證集,並且任務是基於驗證集表現為SVM模型找到最優超引數。 雖然我們可以使用scikit-learn的內建網格搜尋來做到這一點,但是本著遵循練習的目的,我們將從頭開始實現一個簡單的網格搜尋。
raw_data = loadmat('ex6data3.mat')
X = raw_data['X']
Xval = raw_data['Xval']
y = raw_data['y'].ravel()
yval = raw_data['yval'].ravel()
C_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]
gamma_values = [0.01, 0.03, 0.1, 0.3, 1, 3, 10, 30, 100]
best_score = 0
best_params = {'C': None, 'gamma': None}
for C in C_values:
for gamma in gamma_values:
svc = svm.SVC(C=C, gamma=gamma)
svc.fit(X, y)
score = svc.score(Xval, yval)
if score > best_score:
best_score = score
best_params['C'] = C
best_params['gamma'] = gamma
best_score, best_params
(0.965, {'C': 0.3, 'gamma': 100})
6)垃圾郵件過濾器
在這一部分中,我們的目標是使用SVM來構建垃圾郵件過濾器。 在練習文字中,有一個任務涉及一些文字預處理,以獲得適合SVM處理的格式的資料。 然而,這個任務很簡單(將字詞對映到為練習提供的字典中的ID),而其餘的預處理步驟(如HTML刪除,詞幹,標準化等)已經完成。
spam_train = loadmat('spamTrain.mat')
spam_test = loadmat('spamTest.mat')
spam_train
X = spam_train['X']
Xtest = spam_test['Xtest']
y = spam_train['y'].ravel()
ytest = spam_test['ytest'].ravel()
X.shape, y.shape, Xtest.shape, ytest.shape
每個文件已經轉換為一個向量,其中1,899個維對應於詞彙表中的1,899個單詞。 它們的值為二進位制,表示文件中是否存在單詞。 在這一點上,訓練評估是用一個分類器擬合測試資料的問題。
svc = svm.SVC()
svc.fit(X, y)
print('Training accuracy = {0}%'.format(np.round(svc.score(X, y) * 100, 2)))
Training accuracy = 94.4%
print('Test accuracy = {0}%'.format(np.round(svc.score(Xtest, ytest) * 100, 2)))
Test accuracy = 95.3%