逻辑回归
算法基础理解
前面我们讲到过线性回归算法和多项式回归算法,算法的本质以及目的便是求出 ý = f(x),如果给函数输入一个预测样本 x ,经过 f(x) 运算后得到一个 ý;但是这种方法存在问题,那便是,我们得到的预测值是处在负无穷到正无穷之间的,没有限制,这样做的弊端很大。
但是逻辑回归不同,逻辑回归的原理是将样本的特征和样本发生的概率结合起来,预测样本发生的概率,然后再对样本发生的概率进行分析,同时这也是逻辑回归算法和其他算法的不同点。
逻辑回归算法既可以解决分类问题(二分类,也可以通过改进解决多分类问题),也可以解决回归问题。
公式推导以及代码封装
代码封装
class LogisticRegression:
def __init__(self):
self.coef_ = None
self.intercept_ = None
self._theta = None
'''tool function'''
def _sigmod(self,t):
return 1. / (1. + np.exp(-t))
'''use the gradient to fit the model'''
def fit(self,X_train,y_train,eta=0.01,n_iters = 1e4):
'''check'''
assert X_train.shape[0] == y_train.shape[0],\
"the size must be valid"
def J(theta,X_b,y):
y_hat = self._sigmod(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._sigmod(X_b.dot(theta)) - y) / len(X_b)
def gradient_decent(X_b,y,initial_theta,eta,n_iters=1e4,epsilon = 1e-8):
theta = initial_theta
cur_iter = 0
while cur_iter < n_iters:
last_theta = theta
gradient = DJ(theta,X_b,y)
theta = theta - eta*gradient
while(abs(J(theta,X_b,y) - J(last_theta,X_b,y))):
break
cur_iter += 1
return theta
'''main step'''
X_b = np.hstack([np.ones((len(X_train),1)),X_train])
initial_theta = np.zeros(X_b.shape[1])
self._theta = gradient_decent(X_b,y_train,initial_theta,eta,n_iters)
self.coef_ = self._theta[1:]
self.intercept_ = self._theta[0]
return self
def predict_proda(self,X_predict):
'''check'''
assert self.coef_ is not None and self.intercept_ is not None,\
"predict after fit"
assert X_predict.shape[1] == len(self.coef_),\
"the size must be valid"
X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
return self._sigmod(X_b.dot(self._theta))
'''real predict'''
def predict(self,X_predict):
'''check'''
assert self.coef_ is not None and self.intercept_ is not None, \
"predict after fit"
assert X_predict.shape[1] == len(self.coef_), \
"the size must be valid"
proda = self.predict_proda(X_predict)
return np.array(proda>=0.5,dtype='int')
def __repr__(self):
return "LogisticRegression()"
决策边界
为了可视化模型,我们在分类问题中有一个十分重要的工具,叫做决策边界。其定义简单明了,我们在特征空间内部,根据不同的特征对样本进行分类,不同类型之间的分界线就是决策边界,通过决策边界我们可以更好的可视化模型。值得注意的是,处于决策边界上的点我们归为哪一类都可以,但是这种情况很少发生。
绘制决策边界代码封装:(二维空间内)
from matplotlib.colors import ListedColormap
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)
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
导入鸢尾花数据集数据
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 sklearn.model_selection import train_test_split
# 绘制逻辑回归模型的决策边界
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
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')
plt.show()
KNN模型
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(X_train, y_train)
plot_decision_boundary(knn_clf, axis=[4, 7.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.show()
逻辑回归(使用多项式特征)
我们之前大多数实现的都是接近于在我们的数据集中找到一条直线分割两部分数据集,也可以理解为解决的基本上都是二分类问题,但是现实中大多数都是多分类问题,我们可以借鉴多项式回归的做法,在逻辑回归中引入多项式特征,实现多分类问题。
代码实现:
模拟数据集
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 sklearn.linear_model import LogisticRegression
log = LogisticRegression()
log.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
from sklearn.linear_model import LogisticsRegression
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)
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()
补充点:scikit-learn中进行多项式逻辑回归时为了解决过拟合的问题引入的正则项是 C.J(θ) + L2 ,L2是默认,我们可以通过管道以及调参进行调整。
OVR & OVO
使用逻辑回归算法可以通过另外的方式来解决多分类问题,正如本节所讲的OVR以及OVO。
OVR(One vs Rest):一对剩余的意思,有时候我们也将其称为OVA(One vs All),其主要的原理便是,取一种样本作为一类,让其余的样本变为另外一类,这样就变成了n个二分类问题,使用逻辑回归算法训练出n个模型,将待预测的样本传入这n个模型中,所得到的的概率最高的模型对应的样本类型便是该预测样本的类型。
OVO(One VS One):一共具有n个样本,两两结合,一共产生了Cn2中情况,也产生了Cn2个预测结果,种类的最多的便是预测的样本类型。