赞
踩
逻辑回归是一个分类模型,其与线性回归的区别在于最后加了一个sigmoid激活函数将输出转化为概率,从而完成分类的功能
逻辑回归函数是:
y = 1 1 + e − ( w T x + b ) = p (1) y=\frac{1}{1+e^{-(\bm{w}^{\rm{T}}\bm{x}+b)}}=p\tag{1} y=1+e−(wTx+b)1=p(1)
这里的输出值指的是样本属于类别的概率用 p p p表示.
公式1可以变换为(由2推导1还是很直接的):
ln y 1 − y = w T x + b (2) \ln \frac{y}{1-y}=\bm{w}^{\rm{T}}\bm{x}+b \tag{2} ln1−yy=wTx+b(2)
将 y y y 视为样本为正例的可能性, 1 − y 1-y 1−y 是其反例的可能性,再取对数构成对数几率,通过线性回归模型的预测结果逼近真实标记的对数几率,因此称为”对数几率回归”. 使得每个样本属于其真实标记的概率越大越好。将 w \bm{w} w与 b b b统一表示成 θ \theta θ,则逻辑回归的似然函数:
L ( θ ) = ∏ i = 1 n P ( y i ∣ x i ; θ ) = ∏ i = 1 n ( h θ ( x i ) ) y i ( 1 − h θ ( x i ) 1 − y i ) (3) L(\theta)=\prod_{i=1}^n P(y_i|\bm{x}_i;\theta)=\prod_{i=1}^n(h_{\theta}(\bm{x}_i))^{y_i}(1-h_{\theta}(\bm{x}_i)^{1-y_i}) \tag{3} L(θ)=i=1∏nP(yi∣xi;θ)=i=1∏n(hθ(xi))yi(1−hθ(xi)1−yi)(3)
这里的 h θ ( ⋅ ) h_{\theta}(\cdot) hθ(⋅)对应着概率输出函数,就是样本属于对应标签的概率值, n n n代表样本数量,这里指的是二分类。
对数似然函数:
l ( θ ) = log L ( θ ) = ∑ i = 1 n ( y i log h θ ( x i ) + ( 1 − y i ) log ( 1 − h θ ( x i ) ) ) (4) l(\theta)=\log L(\theta)=\sum_{i=1}^n \big(y_i\log h_{\theta}(\bm{x}_i)+(1-y_i)\log(1-h_{\theta}(\bm{x}_i))\big)\tag{4} l(θ)=logL(θ)=i=1∑n(yiloghθ(xi)+(1−yi)log(1−hθ(xi)))(4)
此问题是一个最大化问题,引入 J ( θ ) = − 1 n l ( θ ) J(\theta)=-\frac{1}{n}l(\theta) J(θ)=−n1l(θ)从而转换为最小化问题。
这里的损失函数是凸函数,可以对一个样本的损失函数求二阶导数判断恒大于等于0,因此可以判断存在全局最小值,可以通过梯度下降法、牛顿法求解参数。参考链接:逻辑回归LR(Logistic Regression)原理以及代码实践 - 简书 (jianshu.com)
损失函数对 θ \theta θ进行求导:
∂ J ( θ ) ∂ θ = 1 n ∑ i = 1 n ( h θ ( x i ) − y i ) x i (5) \frac{\partial J(\theta)}{\partial \theta}=\frac{1}{n}\sum_{i=1}^n(h_{\theta}(\bm{x}_i)-y_i)\bm{x}_i \tag{5} ∂θ∂J(θ)=n1i=1∑n(hθ(xi)−yi)xi(5)
这里的 x i \bm{x}_i xi组成带有b的数据矩阵 X X X形状是[n x (m+1)],输出与标签的差值形状是[n, 1],因此这里用矩阵乘法为 1 n X T E \frac{1}{n}X^TE n1XTE,这一步在写代码时特别重要
算法流程:
注意首先需要将数据矩阵进行填充,偏置项纳入权重向量中,这样就不用单独更新偏置,数据矩阵[n x m], 在第一列和最后一列进行添加,那么权重中的第一项或者最后一项代表的是偏置,这样写代码量减少也更加清晰,输出要明确矩阵的形状,这样进行矩阵乘法时不易出错
import matplotlib.pyplot as plt import numpy as np def LogisticRegression_self(X, y, iterations, learning_rate): # X:[n, m], y:[n, 1] X = np.concatenate([X, np.ones((X.shape[0], 1))], axis=1) # [n, m+1] y = y.reshape(len(y), 1) # [n, 1] weights = np.zeros((X.shape[1], 1)) # [m+1, 1] errors_cache = [] # loss存储列表 for i in range(iterations): # 1. 计算输出 out = 1/(1+np.exp(-(X.dot(weights)))) # X[n, m+1] x Weights[m+1, 1] -> [n, 1] loss = - np.sum(y * np.log(out) + (1 - y) * np.log(1 - out)) # 误差 errors_cache.append(loss) # 2. 计算梯度 weights_grad = 1/len(X)*(X.T).dot(out - y) # 梯度 # 3. 通过梯度进行更新权重 weights = weights - learning_rate*(weights_grad) # 更新权重 return np.array(weights), errors_cache # 数据准备 X = np.array([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [2, 1], [3, 2], [4, 3], [5, 3], [6, 5]]) Y = np.array([1, 1, 1, 1, 1, 0, 0, 0, 0, 0]) # 逻辑回归进行二分类 weights, errors = LogisticRegression_self(X, Y, iterations=3000, learning_rate=0.1) x1 = np.linspace(0, 7, 3000) y1 = -(weights[0]*x1 + weights[2]) / weights[1] plt.plot(x1, y1) # 可视化超平面 plt.plot(np.linspace(0, 7, 3000), errors) # 可视化损失 plt.scatter(X[:, 0], X[:, 1], c=Y) # 可视化样本 plt.show()
注意到代码部分,可视化超平面,这里相当于 a x + b y + c = 0 ax+by+c=0 ax+by+c=0,移项后 y = − − a x + c b y=-\frac{-ax+c}{b} y=−b−ax+c,从这个层面理解超平面表达式更容易理解。
实验结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。