逻辑回归(数学推导+python实现+sklearn相关包使用)
1. 原理讲解及数学公式推导
逻辑回归(logistic regression)也叫做对数几率回归, 其实它是一种分类方法
在上一章,我们介绍了最基本的线性回归,那么如何进行分类任务呢?
注意上一章讲过的广义线性模型(generalized linear regression), 只要找到一个单调可微函数, 接近单位阶跃函数,但是要连续,可以把预测的回归值和输出标记 y ϵ{0,1} y ϵ { 0 , 1 } 联系起来(二分类任务中)
我们只能寻找单位阶跃函数的替代函数(surrogate function), \ 接下来让我们请出Sigmoid函数中的代表: logistic function(逻辑回归函数或对数几率函数) :
g(z)=11+e−z g ( z ) = 1 1 + e − z
函数图像如下:
图1 Logistic function
注: Sigmoid函数即形似S型的一类函数,logistic function是其最典型的代表
将此函数代入广义线性模型,得到的线性模型如下:
ϕ(z)=11+e−(θTxx+b) ϕ ( z ) = 1 1 + e − ( θ T x x + b )
y y 可以被看成类后验概率
p(y=1 | xx) p ( y = 1 | x x ) 即被当作正例的概率, 很自然地让
y>=0.5 y >= 0.5 的归为正例,
y<0.5 y < 0.5 则归为反例
代价函数
我们同样使用均方误差即误差的平方和来替代:
J(θθ)=∑i=1m(ϕ(z(i))−y(i))2 ,z(i)=θθTx(i)x(i)+b J ( θ θ ) = ∑ i = 1 m ( ϕ ( z ( i ) ) − y ( i ) ) 2 , z ( i ) = θ θ T x ( i ) x ( i ) + b
但是如果我们将
ϕ(z)=11+e−(θTxx+b) ϕ ( z ) = 1 1 + e − ( θ T x x + b ) 代入上式,会发现代价函数并不是个凸函数,这样不利于进行最优化求解。
因为
ϕ(z) ϕ ( z ) 可以看做正例的后验估计,那么可以得到:
p(y=1| x; θx; θ)=ϕ(z)=ϕ(θTxθTx+b) p ( y = 1 | x ; θ x ; θ ) = ϕ ( z ) = ϕ ( θ T x θ T x + b )
p(y=0| x; θx; θ)=1−ϕ(z)=1−ϕ(θTxθTx+b) p ( y = 0 | x ; θ x ; θ ) = 1 − ϕ ( z ) = 1 − ϕ ( θ T x θ T x + b )
这两个式子可以结合起来变为一般形式:
p(y| x; θx; θ)=ϕ(z)y(1−ϕ(z))1−y p ( y | x ; θ x ; θ ) = ϕ ( z ) y ( 1 − ϕ ( z ) ) 1 − y
我们通过给定的数据集进行
极大似然法(maximum likelihood method)估计参数
θθ θ θ :
L(θθ)=∏i=1mp(y(i)| xx(i);θθ)=∏i=1mϕ(z(i))y(i)(1−ϕ(z(i)))1−y(i)(1)(2) (1) L ( θ θ ) = ∏ i = 1 m p ( y ( i ) | x x ( i ) ; θ θ ) (2) = ∏ i = 1 m ϕ ( z ( i ) ) y ( i ) ( 1 − ϕ ( z ( i ) ) ) 1 − y ( i )
为了方便计算,我们在两边同时取对数,使用
对数似然(log likelihood):
l(θθ)=lnL(θθ)=∑i=1m[y(i)ln(ϕ(z(i)))+(1−y(i))ln(1−ϕ(z(i)))] l ( θ θ ) = l n L ( θ θ ) = ∑ i = 1 m [ y ( i ) l n ( ϕ ( z ( i ) ) ) + ( 1 − y ( i ) ) l n ( 1 − ϕ ( z ( i ) ) ) ]
现在思路清晰了,我们的目标就是要使它最大,那么代价函数就取个负号吧
J(θθ)=−l(θθ)=∑i=1m[−y(i)ln(ϕ(z(i)))−(1−y(i))ln(1−ϕ(z(i)))] J ( θ θ ) = − l ( θ θ ) = ∑ i = 1 m [ − y ( i ) l n ( ϕ ( z ( i ) ) ) − ( 1 − y ( i ) ) l n ( 1 − ϕ ( z ( i ) ) ) ]
其实等同于:
J(θθ)={−∑mi=1ln(ϕ(z(i)))−∑mi=1ln(1−ϕ(z(i))), y(i)=1, y(i)=0 J ( θ θ ) = { − ∑ i = 1 m l n ( ϕ ( z ( i ) ) ) , y ( i ) = 1 − ∑ i = 1 m l n ( 1 − ϕ ( z ( i ) ) ) , y ( i ) = 0
该函数第一部分在0时最大,1时最小取0,单调递减,即说明偏离1代价越来越大
第二部分越偏离0越大
最优化: 梯度下降
这一章我们来探讨为什么沿梯度反方向下降最快,根据泰勒展开,当 δδ δ δ 取无穷小, 我们有:
(注: 以下 xx x x 和 δδ δ δ 都是向量):
f(x+δx+δ)−f(xx)≈f′(xx)⋅δδ f ( x + δ x + δ ) − f ( x x ) ≈ f ′ ( x x ) ⋅ δ δ
f′(xx)⋅δδ=∥f′(xx)∥⋅∥δδ∥⋅cosθ f ′ ( x x ) ⋅ δ δ = ‖ f ′ ( x x ) ‖ ⋅ ‖ δ δ ‖ ⋅ c o s θ
则很明显,当
θ=π θ = π 时, 即沿
f′(xx) f ′ ( x x ) 的负方向时下降是最快的
接下来让我们先对
logistic l o g i s t i c 函数求导:
g(z)=(11+e−z)′=−1(1+e−z)2 ⋅−e−z=e−z(1+e−z)2 g ( z ) = ( 1 1 + e − z ) ′ = − 1 ( 1 + e − z ) 2 ⋅ − e − z = e − z ( 1 + e − z ) 2
发现特别有意思的一点:
g′(z)=g(z)(1−g(z)) g ′ ( z ) = g ( z ) ( 1 − g ( z ) )
知道了这个有用的性质,让我们来手推梯度下降公式中求偏导部分吧:
∂J(θθ)∂θθj=∂∑mi=1[−y(i)ln(ϕ(z(i)))−(1−y(i))ln(1−ϕ(z(i)))]∂θθj=∑i=1m[−y(i)ϕ(z(i))+(1−y(i))1−ϕ(z(i))]⋅∂ϕ(z(i))∂θθj=∑i=1m[−y(i)ϕ(z(i))+(1−y(i))1−ϕ(z(i))]⋅ϕ(z(i))⋅(1−ϕ(z(i)))⋅∂z(i)∂θθj=∑i=1m[−(1−ϕ(z(i)))y(i)+ϕ(z(i))(1−y(i))]xx(i)j=∑i=1m(ϕ(z(i))−y(i))xx(i)j(3)(4)(5)(6)(7) (3) ∂ J ( θ θ ) ∂ θ θ j = ∂ ∑ i = 1 m [ − y ( i ) l n ( ϕ ( z ( i ) ) ) − ( 1 − y ( i ) ) l n ( 1 − ϕ ( z ( i ) ) ) ] ∂ θ θ j (4) = ∑ i = 1 m [ − y ( i ) ϕ ( z ( i ) ) + ( 1 − y ( i ) ) 1 − ϕ ( z ( i ) ) ] ⋅ ∂ ϕ ( z ( i ) ) ∂ θ θ j (5) = ∑ i = 1 m [ − y ( i ) ϕ ( z ( i ) ) + ( 1 − y ( i ) ) 1 − ϕ ( z ( i ) ) ] ⋅ ϕ ( z ( i ) ) ⋅ ( 1 − ϕ ( z ( i ) ) ) ⋅ ∂ z ( i ) ∂ θ θ j (6) = ∑ i = 1 m [ − ( 1 − ϕ ( z ( i ) ) ) y ( i ) + ϕ ( z ( i ) ) ( 1 − y ( i ) ) ] x x j ( i ) (7) = ∑ i = 1 m ( ϕ ( z ( i ) ) − y ( i ) ) x x j ( i )
因此,梯度下降:
until convergence:{
θθj=θθj−α⋅∑mi=1(ϕ(z(i))−y(i))xx(i)j θ θ j = θ θ j − α ⋅ ∑ i = 1 m ( ϕ ( z ( i ) ) − y ( i ) ) x x j ( i )
}
不过,要自己实现代码的话,一般来说代价函数要除以m(线性回归里除以2m是为了方便求导后消去2), 然后加上正则化项
正则化
为了解决过拟合,在逻辑回归里,有以下两种解决方案:
1. 减少特征量
2. 在现有特征情况下,减小 θj θ j
所以我们的代价函数就变成这种:
J(θθ)=1m∑i=1m[−y(i)ln(ϕ(z(i)))−(1−y(i))ln(1−ϕ(z(i)))]+λ2m∑j=1nθθ2j J ( θ θ ) = 1 m ∑ i = 1 m [ − y ( i ) l n ( ϕ ( z ( i ) ) ) − ( 1 − y ( i ) ) l n ( 1 − ϕ ( z ( i ) ) ) ] + λ 2 m ∑ j = 1 n θ θ j 2
注意两点:
1.正则化是从
θ1 θ 1 开始的,因为X第一列全是人为加上的常数项1,没有必要进行正则化
2.其中
λ λ 如果过大,会导致欠拟合;而
λ λ 过小,会导致过拟合
更新后的梯度下降:
until convergence:{
θθ0=θθ0−αm⋅∑mi=1(ϕ(z(i))−y(i))xx(i)0 θ θ 0 = θ θ 0 − α m ⋅ ∑ i = 1 m ( ϕ ( z ( i ) ) − y ( i ) ) x x 0 ( i )
θθj=θθj−αm⋅[∑mi=1(ϕ(z(i))−y(i))xx(i)j+λθθj] θ θ j = θ θ j − α m ⋅ [ ∑ i = 1 m ( ϕ ( z ( i ) ) − y ( i ) ) x x j ( i ) + λ θ θ j ]
}
推广到线代形式
转化为我们写代码需要的线代公式,代价函数:
J(θθ)=1m((Y−1)Tln(1−ϕ(Xθθ))−YTln(ϕ(Xθθ))+λ2θθTθθ) J ( θ θ ) = 1 m ( ( Y − 1 ) T l n ( 1 − ϕ ( X θ θ ) ) − Y T l n ( ϕ ( X θ θ ) ) + λ 2 θ θ T θ θ )
梯度:
注意结果是一个(n+1, 1)的向量
1m⋅[XT(ϕ(Xθθ)−Y)+λθθ] 1 m ⋅ [ X T ( ϕ ( X θ θ ) − Y ) + λ θ θ ]
梯度下降:
until convergence{
θθ=θθ−αm⋅[XT(ϕ(Xθθ)−Y)+λθθ] θ θ = θ θ − α m ⋅ [ X T ( ϕ ( X θ θ ) − Y ) + λ θ θ ]
}
2.python实现
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import math
import scipy
"""
注意接收的X是个向量
"""
def sigmoid(X):
return 1 / (1 + np.exp(-X))
"""
代价函数
theta: (n+1, 1) 参数向量
X: (m, n+1) 矩阵
y: (m, 1) 结果向量
lamb: 标量,控制正则化程度
"""
def get_cost(theta, X, y, lamb):
m = len(y)
phi = sigmoid(np.dot(X, theta))
theta_add = theta.copy()
theta_add[0] = 0
reg = np.dot(np.transpose(theta_add), theta_add)
return (np.dot(np.transpose(y-1), np.log(1 - phi)) - np.dot(np.transpose(y), np.log(phi)) + (lamb / 2 * reg)) / m
"""
计算梯度
"""
def get_gradient(theta, X, y, lamb):
m = len(y)
phi = sigmoid(np.dot(X, theta))
theta_add = theta.copy()
theta[0] = 0
return (np.dot(np.transpose(X), (phi-y)) + lamb*theta_add) / m
未完待续