逻辑回归(数学推导+python实现+sklearn相关包使用)
1. 原理讲解及数学公式推导
逻辑回归(logistic regression)也叫做对数几率回归, 其实它是一种分类方法
在上一章,我们介绍了最基本的线性回归,那么如何进行分类任务呢?
注意上一章讲过的广义线性模型(generalized linear regression), 只要找到一个单调可微函数, 接近单位阶跃函数,但是要连续,可以把预测的回归值和输出标记
y ϵ{0,1}
y
ϵ
{
0
,
1
}
联系起来(二分类任务中)
我们只能寻找单位阶跃函数的替代函数(surrogate function), \ 接下来让我们请出Sigmoid函数中的代表: logistic function(逻辑回归函数或对数几率函数) :
函数图像如下:
注: Sigmoid函数即形似S型的一类函数,logistic function是其最典型的代表
将此函数代入广义线性模型,得到的线性模型如下:
y y 可以被看成类后验概率 即被当作正例的概率, 很自然地让 y>=0.5 y >= 0.5 的归为正例, y<0.5 y < 0.5 则归为反例
代价函数
我们同样使用均方误差即误差的平方和来替代:
但是如果我们将 ϕ(z)=11+e−(θTxx+b) ϕ ( z ) = 1 1 + e − ( θ T x x + b ) 代入上式,会发现代价函数并不是个凸函数,这样不利于进行最优化求解。
因为 ϕ(z) ϕ ( z ) 可以看做正例的后验估计,那么可以得到:
这两个式子可以结合起来变为一般形式:
我们通过给定的数据集进行 极大似然法(maximum likelihood method)估计参数 θθ θ θ :
为了方便计算,我们在两边同时取对数,使用 对数似然(log likelihood):
现在思路清晰了,我们的目标就是要使它最大,那么代价函数就取个负号吧
其实等同于:
该函数第一部分在0时最大,1时最小取0,单调递减,即说明偏离1代价越来越大
第二部分越偏离0越大
最优化: 梯度下降
这一章我们来探讨为什么沿梯度反方向下降最快,根据泰勒展开,当
δδ
δ
δ
取无穷小, 我们有:
(注: 以下
xx
x
x
和
δδ
δ
δ
都是向量):
则很明显,当 θ=π θ = π 时, 即沿 f′(xx) f ′ ( x x ) 的负方向时下降是最快的
接下来让我们先对 logistic l o g i s t i c 函数求导:
发现特别有意思的一点:
知道了这个有用的性质,让我们来手推梯度下降公式中求偏导部分吧:
因此,梯度下降:
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
所以我们的代价函数就变成这种:
注意两点:
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
]
}
推广到线代形式
转化为我们写代码需要的线代公式,代价函数:
梯度:
注意结果是一个(n+1, 1)的向量
梯度下降:
until convergence{
}
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)) # 先计算phi(z)
theta_add = theta.copy()
theta_add[0] = 0 # theta 0 不需要加正则化,所以计算代价时让theta_0 = 0
reg = np.dot(np.transpose(theta_add), theta_add) # 正则化项*2/lambda
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
未完待续