0x00 前言
在逻辑回归算法中,可以求出各个参数的系数和截距,即参数组 值。那这个参数有几何意义么?
在本篇文章中会引出分类文中非常重要的一个概念:决策边界。通过对决策边界的学习,可以更加好的可视化分类结果。进而更好的理解分类算法。
0x01 决策边界
1.1 什么是决策边界
回顾逻辑回归分类的原理:通过训练的方式求出一个n+1维向量 ,每当新来一个样本 时,与参数 进行点乘,结果带入sigmoid函数,得到的值为该样本发生我们定义的 事件发生的概率值。如果概率大于0.5,分类为1;否则分类为0。
对于公式:
当t>0时, ,因此 ; 当t>0时, ,因此 ;
也就是,其中有一个边界点 ,大于这个边界点,分类为1,小于这个边界点。我们称之为决策边界(decision boundary)。
决策边界: 同时也代表一个直线:假设 有两个特征 ,那么有
这是一个直线,可以将数据集分成两类。可以改写成我们熟悉的形式:
1.2 决策边界的可视化
在鸢尾花数据集中,画出决策边界。首先可视化数据集
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
X = X[y<2,:2]
y = y[y<2]
plt.scatter(X[y==0,0], X[y==0,1], color="red")
plt.scatter(X[y==1,0], X[y==1,1], color="blue")
plt.show()
然后使用逻辑回归进行训练:
from playML.model_selection import train_test_split
from playML.LogisticRegression import LogisticRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, seed=666)
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)
log_reg.coef_
"""
输出:
array([ 3.01796521, -5.04447145])
"""
log_reg.intercept_
"""
输出:
-0.6937719272911228
"""
现在按照公式,创建一个函数x2,一旦传来一个x1,就根据公式得到决策边界的直线:
def x2(x1):
return (-log_reg.coef_[0] * x1 - log_reg.intercept_) / log_reg.coef_[1]
# 生成一条直线,x为4~8范围内的1000点 y为x2函数的调用,
x1_plot = np.linspace(4, 8, 1000)
x2_plot = x2(x1_plot)
plt.scatter(X[y==0,0], X[y==0,1], color="red")
plt.scatter(X[y==1,0], X[y==1,1], color="blue")
plt.plot(x1_plot, x2_plot)
plt.show()
这条直线就是决策边界,新来的点在直线上方分类为0,直线下方分类为1。
但是可以看出,线性决策边界只是一根直线,很简单,因此加入多项式项,使得分类算法的决策边界不再规律。
1.3 线性&非线性决策边界
所谓决策边界就是能够把样本正确分类的一条边界,主要有线性决策边界(linear decision boundaries)和非线性决策边界(non-linear decision boundaries)。
注意:决策边界是假设函数的属性,由参数决定,而不是由数据集的特征决定。
下面主要举一些例子,形象化的来说明线性决策边界和非线性决策边界。先看一个线性决策边界的例子:
对于线性这条直线可以比较完美地将数据分成两类。但是直线的分类方式,太简单了。
如果遇到下图的情况,就不能用一个直线将其进行分类了,而是可以用一个圆将数据进行分类。下面来看一下非线性的决策边界的例子:
0x02 逻辑回归的非线性决策边界
2.1 线性回归转换成多项式回归
对于线性这条蓝色的直线可以比较完美地将数据分成两类。但是直线的分类方式,太简单了。
如果我们遇到下图的情况,我们就不能用一个直线将其进行分类了,而是可以用一个圆将数据进行分类。
那么如何使用逻辑回归算法得到非直线的决策边界呢?
我们回忆一下中学的数据知识——圆的表达式:
为了让逻辑回归学习到这样的决策边界,我们需要引入多项式项。如果将上式中的 整体看作第一个特征 ,将 整体看作第二个特征 ,那么则可以转换成线性回归的问题:其中 前的系数为1, 前系数也为1,且 是系数 。
这样就从 来说还是线性边界,针对于 来说变成了非线性的圆形决策边界。这就从线性回归转换成多项式回归,同理为逻辑回归添加多项式项,就可以对非线性的方式进行比较好的分类,决策边界就是曲线的形状。
2.2 代码
使用多项式的思路,如何对非线性决策边界的数据进行很好的分类
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(666)
X = np.random.normal(0, 1, size=(200, 2))
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 myAlgorithm.LogisticRegression import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X, y)
log_reg.score(X, y)
# 绘制决策边界的方法
def plot_decision_boundary(model, axis):
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, 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()
然后使用逻辑回归的方式添加多项式特征,对上面的样本进行划分:
为逻辑回归算法添加多项式项。设置pipeline。列表中每个元素是管道中的一步,每一步是一个元组,元组第一个元素是字符串表示做什么,第二个元素是类的对象。管道的第一步是添加多项式项,第二部是归一化,第三部进行逻辑回归过程,返回实例对象。关于管道的具体用法,可以看看相关的文章。
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
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)
"""
输出:
Pipeline(steps=[('poly', PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LogisticRegression())])
"""
poly_log_reg.score(X, y)
"""
输出:
0.94999999999999996
"""
plot_decision_boundary(poly_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()
下面我们更改degree参数(多项式拓展的阶数),将其变大(那肯定会过拟合):
poly_log_reg2 = PolynomialLogisticRegression(degree=20)
poly_log_reg2.fit(X, y)
plot_decision_boundary(poly_log_reg2, 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()
最终的结果,决策边界的外侧比较奇怪,这就是典型的过拟合。
0xFF 总结
决策边界是分类中非常重要的一个概念。线性决策边界就是一条直线,而在真实数据,很少是一根直线就能分类的,通常都要加上多项式项,也就是非线性的决策边界。这样才能解决更复杂的问题。
但是多项式项的阶数越大,越容易过拟合。那么就要进行模型的正则化。下一章就在逻辑回归中使用正则化,且看看sklearn中是如何使用逻辑回归的。
热门文章