机器学习系列(二十七)——决策边界与Logistic回归使用多项
决策边界
由逻辑回归的原理知,对于我们求得的概率:
当时,,此时,即样本被分类为类别1,否则样本将被分类为类别0。而逻辑回归是基于线性回归的,这个影响分类结果的超平面就被称之为决策边界。如果仅有两个特征,则这个决策边界就是一条直线。以上篇文章中使用的分离出来的鸢尾花数据为例,我们来绘制一下相应的决策边界,绘制之前要知道求得的和截距值:
x.png下面绘制决策边界:
'''绘制决策边界'''
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算法支持多分类,鸢尾花数据集共三个类别,下面绘制有三个类别的决策边界,不过为了可视化方便,特征仍然是取前两个:
'''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=50决策边界
此时决策边界就要光滑很多,因为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%,相应的决策边界:
决策边界
加入多项式特征项后和真实的边界已非常接近。
关于加入多项式特征的逻辑回归需要注意的是,多项式回归中如果次数过高会出现过拟合,因此逻辑回归使用的多项式次数很高时也会出现过拟合,所以在逻辑回归中一般都会对模型进行正则化,关于逻辑回归的正则化,将在下一篇介绍。