1.前言
这几天一直在学习逻辑回归(在书上是叫逻辑斯提回归),听着这个名词并没有其他的机器学习方法那么容易知道他是做什么的。而实际上,逻辑回归类似与线性回归,只不过线性回归中是将每个数据样本(可能这个样本是一个向量)进行回归,而回归的结果是一个值,是一个预测的回归值。而逻辑回归就是在线性回归的基础上将这个回归值,通过函数计算得到一个概率值,再通过概率值来进行分类,从而得到分类的效果。
2.逻辑回归的数学理论
前言所讲到的逻辑回归是通过利用线性回归的基础通过某个函数进行分类(计算概率)。比较常见的就是一个sigmoid函数:
要计算的目标结果变了,那么自然我们逻辑回归模型的损失函数J(θ)就要改变。我们先从p_hat这个概率值的损失函数进行讨论,不难看出,我们的原则就是若p_hat超过0.5的话,就将这个带预测样本归为1类,而若不超过0.5,就归为0类,达到分类的目标。
cost函数的目的就是要惩罚那些分类错误的情况:举个例子就是,当一个训练样本本来是1类的,意味这最理想的状态就是p_hat是超过0.5的,但是通过计算得出的p_hat却低于0.5,可以看作错误分类的情况,那么对于这个样本的cost就应该很大很大。由这种思路,就可以很好理解这个cost函数了:
又或者换一种形式:
这两者是等价的。
那么对于n个训练样本就有n个cost,将这些样本的cost值全部累加起来自然就能得到一个模型的总损失函数J(θ):
到这里损失函数就很容易理解了,自然我们想用的梯度下降法来求得θ参数,就应该得到损失函数的导数:
用梯度下降法就能求出最佳的θ,从而使得模型损失最小。逻辑回归模型就很容易理解了。
3.逻辑回归代码:
import numpy as np
from sklearn.metrics import accuracy_score
class LogisticRegression:
def __init__(self):
self.coef = None
self.theta = None
self.interception = None
#当传进来的t是一个向量,那么就自然的sigmoid函数后就是一个向量
#公式中的y_hat相当与p_hat,也是一个向量
def _sigmoid(self , t):
return 1. / (1. + np.exp(t))
def fit(self , x_train , y_train):
# 损失函数,自变量是theta,因变量是J,对不同的x_train或者不同的theta,会有不同的J
# 在梯度下降法中,主要考虑theta变化后,损失函数J变成什么
def J(x_train , y_train , theta):
assert x_train.shape[0] == y_train.shape[0] , "The shape of x_train is not equal to y_train."
p_hat = self._sigmoid(x_train.dot(theta))
try:
return - np.sum(p_hat * np.log(p_hat) + (1 - p_hat) * np.log(1 - p_hat)) / len(y_train)
except:
return float('inf')
#损失函数的导数,也需要用到训练集,以及参数theta
#区别开类的theta是最终theta,fit的theta是仍然在调整的theta
def dJ(x_train , y_train ,theta):
assert x_train.shape[0] == y_train.shape[0], "The shape of x_train is not equal to y_train."
return x_train.T.dot(self._sigmoid(x_train.dot(theta)) - y_train) / len(y_train)
def gradient_descent(x_train , y_train , initial_theta , eta = 0.01 , n_iter = 1e4 , epsional = 1e-8):
count = 0
theta = initial_theta
while count < n_iter:
history_theta = theta
gradient = dJ(x_train , y_train , theta)
theta = theta - gradient * eta
if(abs(J(x_train , y_train, theta) - J(x_train , y_train, history_theta)) < epsional):
break
count += 1
return theta
X_b = np.hstack([np.ones((x_train.shape[0] , 1)) ,x_train])
initial_theta = np.zeros(X_b.shape[1])
theta = gradient_descent(X_b , y_train , initial_theta)
self.theta = theta
self.coef = theta[1:]
self.interception = theta[0]
def predict(self,x_predict):
assert self.theta is not None , "The model needs to be fitted before you predict."
assert x_predict.shape[1] == len(self.coef) , "No."
x = x_predict.copy()
x1 = np.hstack([np.ones((len(x) , 1)) , x])
p_hat = self._sigmoid(x1.dot(self.theta))
return np.array(p_hat >= 0.5 , dtype='int')
def score(self , y_predict , y_test):
assert y_test.shape[0] == y_predict.shape[0] , "The shape of x_predict need to be equal to y_predict."
return accuracy_score(y_predict , y_test)
def __repr__(self):
return "Logistic Regression()"