天天看点

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

逻辑回归其实是只有一个神经元的神经网络,是一个天然的二分类算法,如下图所示(图为网图):

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

前面这部分是一个线性的模型,如果你熟悉线性回归的话应该不陌生。

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

写出来应该是这样:

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

和多元线性回归的式子是一模一样的。

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

中间这部分是激活函数,一般是S型函数sigmoid,其中

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)
import numpy as np
def sigmoid(z):
    return 1. / (1. + np.exp(-z))

#画出sigmoid函数图像
import matplotlib.pyplot as plt
x = np.linspace(-10,10,100)
y = sigmoid(x)
plt.plot(x,y)
plt.title("sigmoid(z)")
plt.show()           
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

你可以看出来sigmoid函数的值域是(0,1)之间,如果函数的输出值大于0.5,就认为它属于1这类,小于0.5就属于0类。

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

以上就是逻辑回归的执行流程。

有关线性回归的部分我就不解释了,没有这个基础的可以看我专栏的其他文章:

夜星辰:多元线性回归(附代码实现)​zhuanlan.zhihu.com

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

夜星辰:一元线性回归(附代码实现)​zhuanlan.zhihu.com

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

夜星辰:为什么线性回归也是神经网络​zhuanlan.zhihu.com

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)
所有的AI问题,其实都能分为两个部分,一部分是模型,还有一部分是优化

我们刚刚介绍了逻辑回归的模型部分,现在的问题是如何优化。

逻辑回归的优化和线性回归有些不同,线性回归能直接用公式解出最佳的参数,如:正规方程,最小二乘法。

但是逻辑回归

只能用梯度下降法来逐步优化参数

按照优化的套路,我们的目标是使损失函数最小,逻辑回归的损失函数是交叉熵。

交叉熵损失函数

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

(其中y是真实值标签,

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

是预测值)

这个损失函数咋一看非常难懂,不过考虑到逻辑回归只是一个二分类算法的话,标签y的值只能是0,或者1。我们就可以试着把交叉熵损失函数分解成更简单的形式。

当标签即真实值y是0时:

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

当标签即真实值y是1时

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)
y_hat = np.linspace(0.01,0.99,100) #防止log0错误
y_0 = -np.log(1-y_hat)
plt.plot(y_hat,y_0)
plt.title("if y = 0 cost = $-ln(1-hat{y})$")
plt.show()           
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)
y_1 = -np.log(y_hat)
plt.plot(y_hat,y_1)
plt.title("if y = 1 $cost = -ln(hat{y})$")
plt.show()           
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

由上图可知,

预测值和真实值越接近,loss就会越小,反之,预测值和真实值相差越大,loss的值就会越大。

由此我们可以根据损失函数求其参数的梯度,使用梯度下降法求出其最优解。

梯度下降的优化公式

偏导数我都算好了,以下是优化的公式,别的不知道可以,这个不知道铁定写不出代码来。

逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

因为逻辑回归是一个天然的二分类算法,所以这次我们就使用sklearn自带的乳腺癌预测数据集来测试代码。

导入数据并划分测试集

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer

cancer = load_breast_cancer()
x_data = cancer.data
y_data = cancer.target

x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2, random_state=666666)           

生成偏置b

操作和之前的多元线性回归一样

X_b = np.hstack([np.ones((len(x_train), 1)), x_train])
X_b           
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

生成系数

theta = np.zeros(X_b.shape[1]) #这个系数可以随机初始化,你可以初始化为正态分布的
#theta = np.random.randn(X_b.shape[1])           

系数与特征做矩阵乘法输入到sigmoid函数里

print("{:.10f}".format(sigmoid(X_b.dot(theta))[0])) #得到预测结果           

以上就完成了一次正向传播,预测。

优化参数

#损失函数
def J(theta):
    y_hat = sigmoid(X_b.dot(theta))
    return - np.sum(y_train*np.log(y_hat) + (1-y_train)*np.log(1-y_hat)) / len(y_train)

#梯度
def dJ(theta):
    return X_b.T.dot(sigmoid(X_b.dot(theta)) - y_train) / len(y_train)

#梯度下降法优化
iter_num = 0
max_iter = 10000
learing_rate = 0.01
while iter_num < max_iter:
    iter_num += 1
    last_theta = theta
    theta = theta - learing_rate * dJ(theta)
    if (abs(J(theta) - J(last_theta)) < 1e-7):
        break           

结果优化后得到了最终参数

intercept = theta[0] #截距
coef = theta[1:] #系数           
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

得到预测结果

y_predict = sigmoid(X_b.dot(theta))
y_predict = np.array(y_predict >= 0.5 ,dtype='int')
y_predict           
逻辑回归代码matlab_LogisticRegression逻辑回归(附代码实现)

将其封装好,和sklearn一样:

import numpy as np
class MyLogisticRegression:
    def __init__(self,learning_rate=0.001,max_iter=10000):
        self._theta = None
        self.intercept_ = None
        self.coef_ = None
        self.learning_rate = learning_rate
        self.max_iter = max_iter
    
    def _sigmoid(self,z):
        return 1. / (1. + np.exp(-z))
    
    def fit(self,x_train,y_train):
        def J(theta, X_b, y_train):
            y_hat = self._sigmoid(X_b.dot(theta))
            return - np.sum(y_train*np.log(y_hat) + (1-y_train)*np.log(1-y_hat)) / len(y_train)
        
        def dJ(theta, X_b, y_train):
            y_hat = self._sigmoid(X_b.dot(theta))
            return X_b.T.dot(y_hat - y_train) / len(y_train)
        
        
        X_b = np.hstack([np.ones((len(x_train), 1)), x_train])
        self._theta = np.random.randn(X_b.shape[1]) #这里我用了随机初始化,初始化为正态分布
        iter_num = 0
        while iter_num < self.max_iter:
            iter_num += 1
            last_theta = self._theta
            self._theta = self._theta - self.learning_rate * dJ(self._theta,X_b,y_train)
            if (abs(J(self._theta,X_b,y_train) - J(last_theta,X_b,y_train)) < 1e-7):
                break
        
        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]
        return self
    
    def predict(self,x_predict):
        X_b = np.hstack([np.ones((len(x_predict), 1)), x_predict])
        y_predict = self._sigmoid(X_b.dot(self._theta))
        y_predict = np.array(y_predict >= 0.5 , dtype = 'int')
        return y_predict
    
    def score(self,x_test,y_test):
        y_predict = self.predict(x_test)
        sum_acc = np.sum(y_predict==y_test)
        return sum_acc / len(y_test)
        
    def __repr__(self):
        return "LogisticRegression()"