1.什么是逻辑回归
逻辑回归:解决分类问题
回归问题怎么解决分类问题?
将样本的特征和样本发生的概率联系起来,概率是一个数。
对于线性回归来说,我们得到一个函数f,将样本x输入f后,得到的值y就是要预测的值;
而对于逻辑回归来说,我们要得到一个函数f,我们将样本x输入f以后,f会计算出y一个概率值p,之后我们使用这个概率值p来进行分类,如果p>=0.5,也就是有百分之50以上的概率发生的话,我们就让这个概率的值为1,否则让他为1,当然1和0在不同的场景下代表不同的意思。
线性回归计算出来的值域是负无穷到正无穷,而我们使用逻辑回归得出来的p是只取0到之间的个值的。这使得我们不能直接使用线性回归的方法,单单从应用的角度来说,但是这样做不够好,因为我们的逻辑回归的值域是有限制的,使用线性回归或者多项式回归拟合出来的直线或者曲线肯定会比较差。
我们可以在线性回归的结果基础上,添加一个σ函数,将结果转换成0到1之间
绘制sigmoid函数
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(t):
return 1/(1+np.exp(-t))
x = np.linspace(-10,10,100)
y = sigmoid(x)
plt.plot(x,y)
2.逻辑回归的损失函数
对于逻辑回归,定义他的损失函数比较困难。
- 当p=0的时候,按照我们的分类,我们应该把样本分为y^=0(注意这里是预测值)这一类。但是这个样本实际是y=1(注意这里是真值),显然我们分错了,此时,我们对他进行惩罚,这个惩罚是正无穷的;随着p逐渐变大,我们的损失越来越小。当p=1的时候,我们会将样本分类为y=1,此时和这个样本真实的y=1是一致的,那么此时-log(0)也就是没有任何损失。
- 另一半式子反之亦然。
(注意区别if 中的y=1是真值;我们使用σ函数求出来的是预测值)
我们用两个损失函数训练模型是不太方便的,可以将他们合成一个式子如下。当y=1的时候,后半部分是0,所以就只剩下-log(p</sup>).当y=0的时候,前半部分是0,就只剩下-log(1-p<sup>)。
3.逻辑回归函数损失的梯度
1.对σ(t)求导
2.对log(σ(t))进行求导
3.对J(θ)的前一部分求导
4.对log(1-σ(t))进行求导
5.对J(θ)的后一部分求导
6.整合两部分的求导结果
7.整合对J(θ)所有θ的求导结果
8.对比之前的线性回归的损失函数的导数形式,找到相似点
9.向量化
4.实现逻辑回归算法
1.逻辑回归算法的封装实现
class LogisticRegression:
def __init__(self):
"""初始化Logistic Regression模型"""
# 系数向量(θ1,θ2,.....θn)
self.coef_ = None
# 截距 (θ0)
self.interception_ = None
# θ向量
self._theta = None
def _sigmoid(self, t):
return 1. / (1.+np.exp(-t))
def fit(self, X_train, y_train, eta=0.01, n_iters=1e4):
"""根据训练数据集X_train,y_train, 使用梯度下降法训练Logistic Regression 模型"""
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
def J(theta, X_b, y):
y_hat = self._sigmoid(X_b.dot(theta))
try:
return - np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat)) / len(y)
except:
return float('inf')
def dJ(theta, X_b, y):
return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(X_b)
def gradient_descent(X_b, y, initial_theta, eta, n_iters=n_iters, epsilon=1e-8):
"""
梯度下降法封装
X_b: X特征矩阵
y: 结果向量
initial_theta:初始化的theta值
eta:学习率η
n_iters: 最大循环次数
epsilon: 精度
"""
theta = initial_theta
i_iters = 0
while i_iters < n_iters:
"""
如果theta两次变化之间的损失函数值的变化小于我们定义的精度
则可以说明我们已经找到了最低的损失函数值和对应的theta
如果循环次数超过了我们设置的循环次数,
则说明可能由于η设置的过大导致无止境的循环
"""
gradient = dJ(theta, X_b, y)
last_theta = theta
theta = theta - eta * gradient
if abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon:
break
i_iters += 1
return theta
X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
initial_theta = np.zeros(X_b.shape[1])
self._theta = gradient_descent(X_b, y_train, initial_theta, eta)
self.interception_ = self._theta[0]
self.coef_ = self._theta[1:]
return self
def predict_proba(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果概率向量"""
assert self.coef_ is not None and self.interception_ is not None,\
"must fit before predict"
assert X_predict.shape[1] == len(self.coef_),\
"the feature number of X_predict must be equal to X_train"
X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
return self._sigmoid(X_b.dot(self._theta))
def predict(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
assert self.coef_ is not None and self.interception_ is not None,\
"must fit before predict"
assert X_predict.shape[1] == len(self.coef_),\
"the feature number of X_predict must be equal to X_train"
proba = self.predict_proba(X_predict)
return proba >= 0.5
def score(self, X_test, y_test):
"""根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""
y_predict = self.predict(X_test)
return accuracy_score(y_test, y_predict)
def __repr__(self):
return "LinearRegression()"
2.测试我们的逻辑回归算法
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 只取前两个特征;只取y=0,1
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')
<matplotlib.collections.PathCollection at 0x1a1d231ba8>
使用我们自己的逻辑回归算法
from machine_learning.module_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y)
from machine_learning.LogisticRegression import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
LinearRegression()
# 对于所有的测试数据,我们的LogisticRegression全都正确的进行了分类
log_reg.score(X_test,y_test)
1.0
观察逻辑回归预测的概率值p与y_test的关系
- p越趋近于0,逻辑回归算法越愿意将数据预测为0
- p越趋近于1,逻辑回归算法越愿意将数据预测为1
如第一个测试数据的预测概率值为0.02945732,对应的y_test为0
log_reg.predict_proba(X_test)
array([0.02945732, 0.10411811, 0.72452652, 0.95194827, 0.00436497,
0.04378511, 0.98773424, 0.15855562, 0.72452652, 0.18983267,
0.91446368, 0.92348443, 0.88028026, 0.053248 , 0.00987992,
0.98551291, 0.98002369, 0.98183066, 0.99639662, 0.07152432])
y_test
array([0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0])
log_reg.coef_
array([ 3.02265606, -5.07877569])
log_reg.interception_
-0.7235198625270994
5.决策边界
通过上面推导可以得出
决策边界θT·Xb=0 表示的实际上是一条直线,如果有两个特征,那就是在二维平面上的一条直线,x1是横轴,x2是纵轴
观察逻辑回归预测的概率值p与y_test的关系
- p越趋近于0,逻辑回归算法越愿意将数据预测为0
- p越趋近于1,逻辑回归算法越愿意将数据预测为1
如第一个测试数据的预测概率值为0.02945732,对应的y_test为0
log_reg.predict_proba(X_test)
array([0.02945732, 0.10411811, 0.72452652, 0.95194827, 0.00436497,
0.04378511, 0.98773424, 0.15855562, 0.72452652, 0.18983267,
0.91446368, 0.92348443, 0.88028026, 0.053248 , 0.00987992,
0.98551291, 0.98002369, 0.98183066, 0.99639662, 0.07152432])
y_test
array([0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0])
log_reg.coef_
array([ 3.02265606, -5.07877569])
log_reg.interception_
-0.7235198625270994
描述决策边界
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.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)
[<matplotlib.lines.Line2D at 0x1a1dcb6d68>]
通过观察上面的图可以发现,如果新来一个数据点,落在了直线(决策边界)的上方,对应的x1θ1+x2θ2+θ>0,也就是p>0.5,那么我们就将他分类为1;反之,则分类为0。这就是决策曲线
不过无论是KNN,还是逻辑回归算法,我们依然可以加入多项式项,使得他的决策边界不再是一根直线,对于这种情况,我们就不能简单的求出这根直线的方程。然后将整根直线画出来来看到这个决策的边界。这个时候我们需要一个绘制不规则的决策边界的方法
在我们的决策平面上,分布着很多点,对于每一个点,我们都使用我们的模型来判断他分为哪一类。然后将这些颜色绘制出来得到的结果就是决策边界
不规则的决策边界的绘制方法
def plot_decision_boundary(model,axis):
"""
model:模型
axis:坐标轴的范围;0123对应的就是x轴和y轴的范围
"""
# 使用linspace将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,linspace=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='red')
plt.scatter(X[y==1,0],X[y==1,1],color='blue')
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x1a1e76f0f0>
KNN的决策边界
对于KNN来说,这个决策边界是没有表达式的,因为我们不是使用数学求解的方式。而是使用距离投票的方式
但是我们依然可以使用上面的方式绘制决策边界
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train,y_train)
knn_clf.score(X_test,y_test)
1.0
# 绘制KNN算法的决策边界。可以看到是一根弯弯曲曲的曲线
plot_decision_boundary(knn_clf,axis=[4,7.5,1.5,4.5])
plt.scatter(X[y==0,0],X[y==0,1],color='red')
plt.scatter(X[y==1,0],X[y==1,1],color='blue')
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x1a204d5dd8>
# 绘制三个不同类别
knn_clf_all = KNeighborsClassifier()
knn_clf_all.fit(iris.data[:,:2],iris.target)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=2,
weights='uniform')
plot_decision_boundary(knn_clf_all,axis=[4,8,1.5,4.5])
plt.scatter(iris.data[iris.target==0,0],iris.data[iris.target==0,1])
plt.scatter(iris.data[iris.target==1,0],iris.data[iris.target==1,1])
plt.scatter(iris.data[iris.target==2,0],iris.data[iris.target==2,1])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x1a1ef88f98>
通过绘制的结果,可以看出,绘制出的结果是非常不规则的,甚至在黄色的部分还掺杂着一些蓝色的绿色的点。
这就是一种过拟合的表现。可以看到上面的knn_clf_all的n_neighbors(k)为5。之前的讨论曾经说过,
k越小,那么我们的模型就越复杂,在这里,我们可视化的看到了复杂的含义–就是模型非常的不规整
# 调整k为50
knn_clf_all = KNeighborsClassifier(n_neighbors=50)
knn_clf_all.fit(iris.data[:,:2],iris.target)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=50, p=2,
weights='uniform')
plot_decision_boundary(knn_clf_all,axis=[4,8,1.5,4.5])
plt.scatter(iris.data[iris.target==0,0],iris.data[iris.target==0,1])
plt.scatter(iris.data[iris.target==1,0],iris.data[iris.target==1,1])
plt.scatter(iris.data[iris.target==2,0],iris.data[iris.target==2,1])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x1a20376a58>
调整过后的样子已经比上面的决策边界规整了很多。整体分成了三大块,非常的清晰
通过这样一个例子,再次印证了对于KNN算法来说k越大,模型越简单,对于决策边界的划分就是决策越规整,分块越明显
6.在逻辑回归中使用多项式回归
逻辑回归,实际上是在决策平面中找到一根直线,通过使用这根直线。用这条直线来分割所有样本的分类,用这样一个例子可以看到。为什么逻辑回归只能解决二分类问题,因为这根直线只能将我们的特征平面分成两部分。
不过即使如此还是存在一个,就是直线太简单了,比如如下的情况 ,依然是在特征平面分成了两部分,但是对于这样的分布来说,是不可能使用一根直线来分割的。但是可以使用一个圆形。但是对于一个圆形,他的数学表达式是x12+x22-r2=0。
这样,我们只要引入多项式项就好了。把x12和x22分别作为一个特征项。我们学习到的他们前面的系数都是1 。针对x12和x22依然是一个线性关系。但是对于x1,和x2就是曲线了
逻辑回归中添加多项式项
import numpy as np
import matplotlib.pyplot as plt
# 模拟测试用例
np.random.seed(666)
# 两个特征的样本X
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])
<matplotlib.collections.PathCollection at 0x110f30f60>
使用逻辑回归
from machine_learning.LogisticRegression import LogisticRegression
log_reg = LogisticRegression()
# 直接使用逻辑回归。
log_reg.fit(X,y)
LinearRegression()
log_reg.score(X,y)
0.605
def plot_decision_boundary(model,axis):
"""
model:模型
axis:坐标轴的范围;0123对应的就是x轴和y轴的范围
"""
# 使用linspace将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,linspace=5,cmap=custom_cmap)
# 显然有非常多的错误分类,所以导致我们的分类准确度只有0.605
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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x110dc5c50>
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)
Pipeline(memory=None,
steps=[('poly', PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LinearRegression())])
# 使用多项式回归后分数达到了百分之95.结果非常好
poly_log_reg.score(X,y)
0.95
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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x11afbfe10>
poly_log_reg2 = PolynomialLogisticRegression(degree=20)
poly_log_reg2.fit(X,y)
Pipeline(memory=None,
steps=[('poly', PolynomialFeatures(degree=20, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LinearRegression())])
print(poly_log_reg.score(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])
0.95
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x11b10c710>
使用degree=20得出的决策边界的外面变的很奇怪。出现这样的情况就是因为degree=20太大了。导致边界的形状非常的不规则。此时显然发生了过拟合
线性回归添加了多项式项后。degree这个阶数越大,模型越复杂,就越容易发生过拟合
7.scikit-learn中的逻辑会回归
另一种正则化
C·J(θ) + L1
C·J(θ) + L2
使用这种正则化。如果C很小。那我们的任务就是集中精力调整L1或者L2的大小;如果C很大,那我们的任务就是集中精力调整原损失函数J(θ)的大小
使用这种正则化的好处是。我们不得不进行正则化;因为L1/L2前面的系数不能为0
scikit-learn中的逻辑会回归
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]<1.5,dtype='int')
# 添加一些噪音
for _ in range(20):
y[np.random.randint(200)] = 1
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
<matplotlib.collections.PathCollection at 0x1a1888c390>
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
# C=1.0 说明默认的正则化超参数C=1.0;penalty=l2,说明默认使用l2正则化
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
# 0.82 因为我们生成的数据是具有二次项的。但是我们这里使用的是线性逻辑回归
print(log_reg.score(X_train,y_train))
print(log_reg.score(X_test,y_test))
0.7933333333333333
0.86
def plot_decision_boundary(model,axis):
"""
model:模型
axis:坐标轴的范围;0123对应的就是x轴和y轴的范围
"""
# 使用linspace将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,linspace=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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x107c2c278>
尝试使用多项式回归
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_train,y_train)
Pipeline(memory=None,
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(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False))])
poly_log_reg.score(X_train,y_train)
0.9133333333333333
poly_log_reg.score(X_test,y_test)
0.94
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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x10b0dec18>
尝试增大多项式项degree的值
poly_log_reg2 = PolynomialLogisticRegression(degree=20)
poly_log_reg2.fit(X_train,y_train)
Pipeline(memory=None,
steps=[('poly', PolynomialFeatures(degree=20, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False))])
poly_log_reg2.score(X_train,y_train)
0.94
# 模型返回能力变脆。因为出现了过拟合
poly_log_reg2.score(X_test,y_test)
0.92
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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x10a836630>
尝试调整正则化超参数C
def PolynomialLogisticRegression(degree,C):
return Pipeline([
('poly',PolynomialFeatures(degree=degree)),
('std_scaler',StandardScaler()),
('log_reg',LogisticRegression(C=C))
])
# C=0.1 相当于让模型正则化那一项起更大的作用
poly_log_reg3 = PolynomialLogisticRegression(degree=20,C=0.1)
poly_log_reg3.fit(X_train,y_train)
Pipeline(memory=None,
steps=[('poly', PolynomialFeatures(degree=20, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False))])
poly_log_reg3.score(X_train,y_train)
0.8533333333333334
# C=0.1 模型泛化能力并没有降低
poly_log_reg3.score(X_test,y_test)
0.92
# 虽然边界还是比较奇怪,但是比之前degree=20要好很多
plot_decision_boundary(poly_log_reg3,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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x110799400>
尝试使用L1正则项
def PolynomialLogisticRegression(degree,C,penalty='l2'):
return Pipeline([
('poly',PolynomialFeatures(degree=degree)),
('std_scaler',StandardScaler()),
('log_reg',LogisticRegression(C=C,penalty=penalty))
])
poly_log_reg4 = PolynomialLogisticRegression(degree=20,C=0.1,penalty='l1')
poly_log_reg4.fit(X_train,y_train)
Pipeline(memory=None,
steps=[('poly', PolynomialFeatures(degree=20, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('log_reg', LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l1', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False))])
poly_log_reg4.score(X_train,y_train)
0.8266666666666667
# score变低是由于数据比较简单。主要看决策边界
poly_log_reg4.score(X_test,y_test)
0.9
# 已经非常接近之前的正常的决策边界了
plot_decision_boundary(poly_log_reg4,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])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x1108f89e8>
8.OvR与OvO
1.什么是OvR与OvO
逻辑回归只可以解决二分类问题。我们可以对逻辑回归稍加改造,让他解决多分类问题:
OvR (One vs Rest – 一针对剩余)
假设一共有4个类别,选取其中的某一个类别(假设红色)(One),而对于剩下的类别,把他们融合在一起,把他称之为其他的类别(Rest),这样就把一个四分类问题转换成了二分类问题,转换成了使红色的概率是多少,是非红色的概率是多少
以此类推,我们分别进行四次分类,哪次获得的类别得分最高,我们就任务他属于哪一个类别。对于逻辑回归来说,就是我们的概率P
复杂度由T变成了N*T->O(N)
OvO (One vs One – 一对一的进行比较)
每次就从N个类别挑出两个类别(比如这里挑出红蓝两个类别),然后进行二分类任务,看对于这个任务来说,我们的样本点是属于哪个类别。然后依次类推进行扩展,如果我们有4个类别需要分类,那我们就能形成6个两两的对C42(排列组合公式4*3/2=6),也就是6个二分类问题。对于这6个分类结果,判定他在哪个类别中数量最大,就判定他是哪个类别
复杂度是O(N2),但是分类结果相比OVR是更加准确的,这是因为每次只用真实的两个类别进行比较,所以他更倾向于真实的样本属于哪个类别
2.sklearn中的OvR与OvO
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
# 为了可视化,先使用两个类别
X = iris.data[:,:2]
y = iris.target
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
# multi_class='ovr' sklearn 默认支持多分类任务,而且默认使用ovr方式
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
# 分数不好使由于我们只使用了两个维度
log_reg.score(X_test,y_test)
0.6578947368421053
def plot_decision_boundary(model,axis):
"""
model:模型
axis:坐标轴的范围;0123对应的就是x轴和y轴的范围
"""
# 使用linspace将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,linspace=5,cmap=custom_cmap)
# 描绘三分类的决策边界
plot_decision_boundary(log_reg,axis=[4,8.5,1.5,4.5])
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.scatter(X[y==2,0],X[y==2,1])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x113f38ba8>
使用OvO
# sklearn中计算logistic不是简单的使用梯度下降法,他是使用更快的一种方法,所以需要修改solver参数
# 默认solver='liblinear',为了正确的调用OvO,缓存newton-cg
log_reg2 = LogisticRegression(multi_class='multinomial',solver="newton-cg")
log_reg2.fit(X_train,y_train)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='multinomial',
n_jobs=1, penalty='l2', random_state=None, solver='newton-cg',
tol=0.0001, verbose=0, warm_start=False)
# 分类准确度比使用OvR的时候要高了很多
log_reg2.score(X_test,y_test)
0.7894736842105263
# 从直观的角度看,决策边界也准确了很多
plot_decision_boundary(log_reg2,axis=[4,8.5,1.5,4.5])
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.scatter(X[y==2,0],X[y==2,1])
/anaconda3/lib/python3.6/site-packages/matplotlib/contour.py:967: UserWarning: The following kwargs were not used by contour: 'linspace'
s)
<matplotlib.collections.PathCollection at 0x111b0b6d8>
尝试使用所有的数据特征
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
log_reg.score(X_test,y_test)
0.9473684210526315
log_reg2 = LogisticRegression(multi_class='multinomial',solver="newton-cg")
log_reg2.fit(X_train,y_train)
# 使用OvO的方式预测结果达到了百分之百;
# 注意:这里由于数据集比较小,耗时的差别比较小
log_reg2.score(X_test,y_test)
1.0
使用sklearn中的OvO and OvR
from sklearn.multiclass import OneVsRestClassifier
ovr = OneVsRestClassifier(log_reg)
ovr.fit(X_train,y_train)
ovr.score(X_test,y_test)
0.9473684210526315
from sklearn.multiclass import OneVsOneClassifier
ovo = OneVsOneClassifier(log_reg)
ovo.fit(X_train,y_train)
ovo.score(X_test,y_test)
1.0