機器學習十講-第三講分類


感知機

原理

 

 

 下面用一個 perception 函數實現上述算法。為了深入觀察算法運行過程,我們保留了每一輪迭代的參數 ww,並對每一輪迭代中隨機選取的樣本也進行了記錄。所以,perception 函數返回三個取值: 最終學習到的參數 w, 每輪迭代的參數 W, 每輪迭代隨機選取的樣本 mis_samples 。

代碼實現

def perception(X,y,learning_rate,max_iter=1000):
    w = pd.Series(data=np.zeros_like(X.iloc[0]),index=X.columns) # 初始化參數 w0
    W = [w] # 定義一個列表存放每次迭代的參數
    mis_samples = [] # 存放每次誤分類的樣本
    
    for t in range(max_iter):
        
        # 2.1 尋找誤分類集合 M
        m = (X.dot(w))*y #yw^Tx < 0 的樣本為誤分類樣本
        X_m = X[m <= 0]  # 誤分類樣本的特征數據
        y_m = y[m <= 0]  # 誤分類樣本的標簽數據
        
        if(len(X_m) > 0): # 如果有誤分類樣本,則更新參數;如果不再有誤分類樣本,則訓練完畢。
            # 2.2 從 M 中隨機選取一個樣本 i 
            i = np.random.randint(len(X_m))
            mis_samples.append(X_m.iloc[i,:])
            # 2.3 更新參數 w 
            w = w + learning_rate * y_m.iloc[i]*X_m.iloc[i,:]
            W.append(w)
        else: 
            break
            
    mis_samples.append(pd.Series(data=np.zeros_like(X.iloc[0]),index=X.columns))
    return w,W,mis_samples
w_percept,W,mis_samples = perception(data[["x1","x2","ones"]], data["label"],1,max_iter=1000)

將學習到的感知機的決策直線可視化,觀察分類效果。

x1 = np.linspace(-6, 6, 50)
x2 = - (w_percept[0]/w_percept[1])*x1 - w_percept[2]/w_percept[1]
plt.figure(figsize=(8, 8)) #設置圖片尺寸

plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 類別為1的數據繪制成洋紅色
plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 類別為-1的數據繪制成深綠色
plt.plot(x1,x2,c="gray") # 畫出分類直線

plt.xlabel("$x_1$") #設置橫軸標簽
plt.ylabel("$x_2$") #設置縱軸標簽
plt.title('手動實現的感知機模型')
plt.xlim(-6,6) #設置橫軸顯示范圍
plt.ylim(1,5) #設置縱軸顯示范圍
plt.show()

展示:

 

 

 邏輯回歸

原理

 

 

 代碼實現

梯度下降法求解邏輯回歸

import numpy as np
# 定義梯度下降法求解的迭代公式
def logistic_regression(X,y,learning_rate,max_iter=1000):
    # 初始化w
    w = np.zeros(X.shape[1])
    for t in range(max_iter):      
        # 計算yX
        yx = y.values.reshape((len(y),1)) * X 
        # 計算1 + e^(yXW)
        logywx = (1 +  np.power(np.e,X.dot(w)*y)).values.reshape(len(y),1) 
        w_grad = np.divide(yx,logywx).sum()
        # 迭代w
        w = w + learning_rate * w_grad    
    return w

我們將數據及標簽帶入上面定義的函數,學習率設為 0.5 ,迭代次數為1000次,輸出訓練好的參數,並將分類結果進行可視化。

# 輸出訓練好的參數
w = logistic_regression(data[["x1","x2","ones"]], data["label"],0.5,max_iter=1000)  
print(w)

# 可視化分類結果
x1 = np.linspace(-6, 6, 50)
x2 = - (w[0]/w[1])*x1 - w[2]/w[1]

plt.figure(figsize=(8, 8))
plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 類別為1的數據繪制成洋紅色
plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 類別為-1的數據繪制成深綠色
plt.plot(x1,x2,c="gray")

plt.xlabel("$x_1$")
plt.ylabel("$x_2$")
plt.xlim(-6,6)
plt.ylim(1,5)
plt.show()

 

 

 

隨機梯度下降法求解邏輯回歸

 

 

 

# 定義隨機梯度下降法求解的迭代公式
def logistic_regression_sgd(X,y, learning_rate, max_iter=1000): 
    # 初始化w
    w = np.zeros(X.shape[1])
    for t in range(max_iter):
        # 隨機選擇一個樣本
        i = np.random.randint(len(X))
        # 計算yx
        yixi = y[i] * X.values[i]
         # 計算1 + e^(yxW)
        logyiwxi = 1 +  np.power(np.e, w.T.dot(X.values[i])*y[i])
        w_grad = yixi / logyiwxi
        
        # 迭代w
        w = w + learning_rate * w_grad  
        
    return w

我們將學習率設為 0.5,迭代次數為1000次,並輸出訓練好的參數,將分類結果可視化。

# 輸出訓練好的參數
w = logistic_regression_sgd(data[["x1","x2","ones"]], data["label"],0.5,max_iter=1000)  
print(w)

# 可視化分類結果
x1 = np.linspace(-6, 6, 50)
x2 = - (w[0]/w[1])*x1 - w[2]/w[1]

plt.figure(figsize=(8, 8))
plt.scatter(data_pos["x1"],data_pos["x2"],c="#E4007F",marker="^") # 類別為1的數據繪制成洋紅色
plt.scatter(data_neg["x1"],data_neg["x2"],c="#007979",marker="o") # 類別為-1的數據繪制成深綠色
plt.plot(x1,x2,c="gray")

plt.xlabel("$x_1$")
plt.ylabel("$x_2$")
plt.xlim(-6,6)
plt.ylim(1,5)
plt.show()

 

 

 

向量機

原理

 

 

 代碼實現

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# 定義函數
def linear_svm(X,y,lam,max_iter=2000):  
    w = np.zeros(X.shape[1]) # 初始化w
    support_vectors = [] # 創建空列表保存支持向量
    
    for t in range(max_iter): # 進行迭代
        
        learning_rate = 1/(lam * (t + 1)) # 計算本輪迭代的學習率
        i = np.random.randint(len(X)) # 從訓練集中隨機抽取一個樣本
        ywx = w.T.dot(X.values[i])*y[i]  # 計算y_i w^T x_i
        
        if ywx < 1:# 進行指示函數的判斷
            w = w - learning_rate * lam*w + learning_rate * y[i] * X.values[i] # 更新參數       
        else:
            w = w - learning_rate * lam*w # 更新參數
    for i in range(len(X)):
        ywx = w.T.dot(X.values[i])*y[i]  # 計算y_i w^T x_i
        if ywx <= 1: # 根據樣本是否位於間隔附近判斷是否為支持向量
            support_vectors.append(X.values[i])
    
    return w,support_vectors

需要注意的是,線性支持向量機的正則化項通常不包括截距項,我們可以將數據進行中心化,再調用上述代碼。

# 對訓練集數據進行歸一化,則模型無需再計算截距項
X = data[["x1","x2"]].apply(lambda x: x - x.mean())
# 訓練集標簽
y = data["label"]
w,support_vectors = linear_svm(X,y, lam=0.05, max_iter=5000)

將得到的超平面可視化,同時將兩個函數間隔為 1 的線也繪制出來。對於所有不滿足約束的樣本,使用圓圈標記出來。

# 創建繪圖框
plt.figure(figsize=(8, 8))
# 繪制兩類樣本點
X_pos = X[ y==1 ]
X_neg = X[ y==-1 ]
plt.scatter(X_pos["x1"],X_pos["x2"],c="#E4007F",marker="^") # 類別為1的數據繪制成洋紅色
plt.scatter(X_neg["x1"],X_neg["x2"],c="#007979",marker="o") # 類別為-1的數據繪制成深綠色

# 繪制超平面
x1 = np.linspace(-6, 6, 50)
x2 = - w[0]*x1/w[1]
plt.plot(x1,x2,c="gray")

# 繪制兩個間隔超平面
plt.plot(x1,-(w[0]*x1+1)/w[1],"--",c="#007979")
plt.plot(x1,-(w[0]*x1-1)/w[1],"--",c="#E4007F")

# 標注支持向量
for x in support_vectors:
    plt.plot(x[0],x[1],"ro", linewidth=2, markersize=12,markerfacecolor='none')
    
# 添加軸標簽和限制軸范圍
plt.xlabel("$x_1$")
plt.ylabel("$x_2$")
plt.xlim(-6,6)
plt.ylim(-2,2)

 

 

 實例-新聞主題分類

步驟

讀取數據

raw_train = pd.read_csv("./input/chinese_news_cutted_train_utf8.csv",sep="\t",encoding="utf8")
raw_test = pd.read_csv("./input/chinese_news_cutted_test_utf8.csv",sep="\t",encoding="utf8")

查看訓練集的前5行。

raw_train.head()

 

 為了簡單,我們這里先只考慮二分類,我們選取主題為"科技"和“文化”新聞。

raw_train_binary = raw_train[((raw_train["分類"] == "科技") | (raw_train["分類"] == "文化"))]
raw_test_binary = raw_test[((raw_test["分類"] == "科技") | (raw_test["分類"] == "文化"))]

 

 

 

 

 

 

 

模型效果評估

下面使用混淆矩陣來分析模型在測試集上的表現。混淆矩陣從樣本的真實標簽和模型預測標簽兩個維度對測試集樣本進行分組統計,然后以矩陣的形式展示。借助混淆矩陣可以很好地分析模型在每一類樣本上的分類效果。為了更直觀地分析,我們借助 Python 中的可視化庫 Seaborn 提供的 heatmap 函數,將線性支持向量機模型的混淆矩陣進行可視化。

from sklearn.metrics import confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5,5))
# 設置正常顯示中文
sns.set(font='SimHei')
# 繪制熱力圖
y_svm_pred = lsvm_clf.predict(X_test) # 預測標簽
y_test_true = raw_test_binary["分類"] #真實標簽
confusion_matrix = confusion_matrix(y_svm_pred,y_test_true)#計算混淆矩陣
ax = sns.heatmap(confusion_matrix,linewidths=.5,cmap="Greens",
                 annot=True, fmt='d',xticklabels=lsvm_clf.classes_, yticklabels=lsvm_clf.classes_)
ax.set_ylabel('真實')
ax.set_xlabel('預測')
ax.xaxis.set_label_position('top') 
ax.xaxis.tick_top()
ax.set_title('混淆矩陣熱力圖')

 

 可見:正對角線還是比較集中的,代表效果還不錯

總結

在本案例中,我們使用隨機梯度方法實現了三種使用回歸的思想來解決分類問題的模型:感知機、邏輯回歸和線性支持向量機。在實現時主要使用了 NumPyPandas 和 Matplotlib 等 Python 庫。在 Sklearn 中,linear.model.SGDClassifier 類實現了常見算法的隨機梯度下降實現。我們使用該類,在一份中文新聞數據上分別用隨機梯度下降算法訓練了感知機、邏輯回歸和線性支持向量機模型,實現了對中文新聞主題的分類。最后,使用 Sklearn.metrics 實現的模型評價方法,用正確率和混淆矩陣對分類效果進行了簡單的分析。

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
yfx = np.linspace(-4, 4, 500)
perception = [0 if i >= 0 else -i for i in yfx]
hinge = [(1-i) if i <= 1 else 0 for i in yfx]
log = np.log2(1 + np.power(np.e,-yfx))
plt.figure(figsize=(8, 6))
plt.plot(yfx,perception,c="b",label="感知機損失")
plt.plot(yfx,hinge,c="g",label="合頁損失(SVM)")
plt.plot(yfx,log,c="red",label="對數損失(LR)")
plt.hlines(1,-4,0)
plt.vlines(0,0,1)
plt.xlabel("$yf(x)$")
plt.ylabel("$L_i(y_i,yf(x))$")
plt.xlim(-4,4)
plt.ylim(0,6)
plt.legend()

 

 以下為繪制三種分類模型的從回歸到分類的映射函數。

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#創建畫布並引入axisartist工具。
import mpl_toolkits.axisartist as axisartist
#創建畫布
fig = plt.figure(figsize=(8, 8))
fx = np.linspace(-10, 10, 500)
step = [1 if i >= 0 else -1 for i in fx]
tanh = np.tanh(fx)
sigmoid = 1/(1 + np.power(np.e,-fx))
plt.axhline(0,-10,10,color="k")
plt.axvline(0,-2,2,color="k")
plt.plot(fx,step,c="b",label="step")
plt.plot(fx,tanh,c="g",label="tanh")
plt.plot(fx,sigmoid,c="red",label="sigmoid")
plt.xlabel("$f$")
plt.ylabel("$H(f)$")
plt.grid(False)
plt.xlim(-10, 10)
plt.ylim(-2,2)
plt.axis('off')
plt.legend()

 

 

 

raw_train = pd.read_csv("./input/chinese_news_cutted_train_utf8.csv",sep="\t",encoding="utf8")raw_test = pd.read_csv("./input/chinese_news_cutted_test_utf8.csv",sep="\t",encoding="utf8")


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM