決策邊界
由邏輯迴歸的原理知,對於我們求得的概率:
當時,,此時,即樣本被分類爲類別1,否則樣本將被分類爲類別0。而邏輯迴歸是基於線性迴歸的,這個影響分類結果的超平面就被稱之爲決策邊界。如果僅有兩個特徵,則這個決策邊界就是一條直線。以上篇文章中使用的分離出來的鳶尾花數據爲例,我們來繪製一下相應的決策邊界,繪製之前要知道求得的和截距值:
下面繪製決策邊界:
'''繪製決策邊界'''
def x2(x1):
return (-log_reg.coef_[0] * x1 - log_reg.interception_)/log_reg.coef_[1]
x1_plot = np.linspace(4,8,1000)
x2_plot = x2(x1_plot)
plt.plot(x1_plot,x2_plot)
plt.scatter(X[y==0,0],X[y==0,1],color='r')
plt.scatter(X[y==1,0],X[y==1,1],color='b')
plt.show()
這樣一來對於一個新來的樣本,如果,即樣本落到直線下方,我們將它分類爲1,相反則分類爲0。如果落在決策邊界上,則分類爲任一類都是可以的。
由此也可以看出,目前爲止,邏輯迴歸的本質還是基於線性迴歸的,不過我們也可以像多項式迴歸一樣,加入多項式項,這樣就可以得到不規則的決策邊界。首先來封裝一個決策邊界繪製函數:
'''繪圖查看決策邊界'''
def plot_decision_boundary(model,axis):
'''axis是x軸y軸對應的範圍'''
x0,x1 = np.meshgrid(
np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1,1),
np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1,1)
)
x_new=np.c_[x0.ravel(),x1.ravel()]
y_predict=model.predict(x_new)
zz = y_predict.reshape(x0.shape)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
再對我們的邏輯迴歸繪製決策邊界:
plot_decision_boundary(log_reg,axis=[4, 7.5, 1.5, 4.5])
plt.scatter(X[y==0,0],X[y==0,1],color='r')
plt.scatter(X[y==1,0],X[y==1,1],color='b')
plt.show()
我們也可以繪製其它分類算法的決策邊界,比如knn,在同樣的樣本數據下繪製knn算法的決策邊界:
'''knn決策邊界是沒有表達式的'''
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train,y_train)
knn_clf.score(X_test,y_test)
plot_decision_boundary(knn_clf,axis=[4, 7.5, 1.5, 4.5])
plt.scatter(X[y==0,0],X[y==0,1],color='r')
plt.scatter(X[y==1,0],X[y==1,1],color='b')
plt.show()
可以看到knn決策邊界是不規則的。由於knn算法支持多分類,鳶尾花數據集共三個類別,下面繪製有三個類別的決策邊界,不過爲了可視化方便,特徵仍然是取前兩個:
'''knn天然支持多分類,三分類決策邊界'''
X2 = iris.data[:,:2]
y2 = iris.target
knn_clf_all = KNeighborsClassifier()
knn_clf_all.fit(X2,y2)
plot_decision_boundary(knn_clf_all,axis=[4, 8, 1.5, 4.5])
plt.scatter(X2[y2==0,0],X2[y2==0,1],color='r')
plt.scatter(X2[y2==1,0],X2[y2==1,1],color='g')
plt.scatter(X2[y2==2,0],X2[y2==2,1],color='b')
plt.show()
這個的決策邊界更是不規則,由模型的複雜度理論知道,knn算法中k越小,模型複雜度就越高,也越容易出現過擬合,這樣的決策邊界往往是過擬合的。將k增大到50,再繪製一次決策邊界:
'''指定k=50重新繪製'''
knn_clf2 = KNeighborsClassifier(50)
knn_clf2.fit(X2,y2)
plot_decision_boundary(knn_clf2,axis=[4, 8, 1.5, 4.5])
plt.scatter(X2[y2==0,0],X2[y2==0,1],color='r')
plt.scatter(X2[y2==1,0],X2[y2==1,1],color='g')
plt.scatter(X2[y2==2,0],X2[y2==2,1],color='b')
plt.show()
此時決策邊界就要光滑很多,因爲k增大了,模型複雜度也相應降低,模型泛化能力更強。
邏輯迴歸中添加多項式特徵
實際中並非所有決策邊界都能用直線表示,如下面的二分類就是一個非線性決策邊界:
我們能不能讓邏輯迴歸學習到這樣的決策邊界呢?答案是肯定的,可以在邏輯迴歸中添加多項式特徵項,由於多項式可以任意精度逼近任何曲線,加入多項式項的邏輯迴歸自然也能有任意形狀的決策邊界。下面在模擬數據集上測試一下加入多項式特徵的邏輯迴歸。生成模擬數據集:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(666)
X = np.random.normal(0,1,size=(200,2))
'''特徵平方和小於1.5歸類爲1'''
y = np.array(X[:,0]**2 + X[:,1]**2 <1.5,dtype=int)
數據集圖示:
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
首先使用基於線性迴歸的邏輯迴歸:
from play_Ml.LogisticRegression import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X,y)
分類準確率:
這個準確率是比較低的,相應的決策邊界:
plot_decision_boundary(log_reg,axis=[-4,4,-4,4])
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
決策邊界也和實際大不相符。
加入多項式特徵後使用邏輯迴歸:
'''添加多項式'''
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
def PolynomialLogisticRegression(degree):
return Pipeline([
('poly',PolynomialFeatures(degree=degree)),
('std_scaler',StandardScaler()),
('log_reg',LogisticRegression())
])
poly_log_reg = PolynomialLogisticRegression(degree=2)
poly_log_reg.fit(X,y)
poly_log_reg.score(X,y)
我們仍然是使用了Pipeline來將這些過程封裝到了一起,使用的邏輯迴歸卻是我們自己寫的,不過由於邏輯是模擬sklearn中的邏輯迴歸,所以可以無縫銜接使用,此時準確率:
準確率已有95%,相應的決策邊界:
加入多項式特徵項後和真實的邊界已非常接近。
關於加入多項式特徵的邏輯迴歸需要注意的是,多項式迴歸中如果次數過高會出現過擬合,因此邏輯迴歸使用的多項式次數很高時也會出現過擬合,所以在邏輯迴歸中一般都會對模型進行正則化,關於邏輯迴歸的正則化,將在下一篇介紹。