Logistic 回归原理分析与 Python 实现

879 阅读4分钟
原文链接: www.codebelief.com

一、Logistic 回归原理分析

目标函数(sigmoid函数):

p(y=1|x;\theta)=h_\theta(x)=\frac{1}{1+\exp(\theta^Tx)}

这里的 \theta 是模型参数,也就是回归系数,\sigma 是sigmoid函数。这样 y=0 的“概率”就是:

p(y=0|x;\theta)=1-h_\theta(x)=\frac{exp(\theta^Tx)}{1+exp(\theta^Tx)}

对于逻辑斯蒂回归而言,可以得到如下的对数几率:

log(\frac{h_\theta(x)}{1-h_\theta(x)})=\theta_0+\theta_1x_1+\cdots+\theta_mx_m

在参数学习时,可以用极大似然估计方法求解。假设我们有n个独立的训练样本{(x1, y1) ,(x2, y2), …, (xn, yn)}; y={0, 1}。那每一个观察到的样本(xi, yi)出现的概率是:

p(y^{(i)},x^{(i)})=p(y^{(i)}=1|x^{(i)})^{y^{(i)}}(1-p(y^{(i)}=1|x^{(i)}))^{1-y^{(i)}}

对于整个样本集,每个样本的出现都是独立的,n个样本出现的似然函数为(n个样本的出现概率是他们各自的概率乘积):

L(\theta)=\prod p(y^{(i)},x^{(i)})=\prod p(y^{(i)}=1|x^{(i)})^{y^{(i)}}(1-p(y^{(i)}=1|x^{(i)}))^{1-y^{(i)}}

对数似然函数为:

logit(\theta)=\sum (y^{(i)} logh_\theta(x^{(i)}) + (1-y^{(i)}) log(1-h_\theta(x^{(i)})) )

我们希望求得能使函数 logit(θ) 取得最大值的参数 θ,因此我们设模型的代价函数(cost function)为:

l(\theta)=-logit(\theta)

这样,求解最佳参数 \theta,就可转化为求最小代价函数 l(\theta)。

二、梯度下降法

1) 初始化参数 \theta=(0,\theta_1,\theta_2,…,\theta_n )^T,初始值为 \theta=(0, 0, 0, …, 0 )^T

2) 获取样本X,在每个样本前添加一个1,x=(1,x_1,x_2,…,x_n )^T

3) 设定步长α

4) 利用 sigmoid 函数对样本进行预测

5) 根据对数似然函数计算梯度 g=\nabla_\theta l(\theta)

6) 更新参数 \theta=\theta-\alpha g

重复步骤4~6,直到结果精度满足要求,或达到一定次数时迭代终止。

加入正则项的情况

代价函数增加正则项 l(\theta)=-logit(\theta)+\frac{\lambda}{2m}\sum_{j=1}^m\theta_j^2

因此步骤 5 修改为 g=\nabla_\theta l(\theta)+\frac{\lambda}{m}\theta

三、牛顿法

1) 初始化参数 \theta=(0,\theta_1,\theta_2,…,\theta_n )^T,初始值为 \theta=(0, 0, 0, …, 0 )^T

2) 获取样本X,在每个样本前添加一个1,x=(1,x_1,x_2,…,x_n )^T

3) 利用 sigmoid 函数对样本进行预测

4) 根据对数似然函数计算梯度 g=\nabla_\theta l(\theta)

5) 根据对数似然函数计算 Hessian 矩阵H,H_{ij}=\frac{\partial_2 l(\theta)}{\partial\theta_i \partial\theta_j}

6) 更新参数 \theta=\theta-H^{-1}g

重复步骤 3~6,知道结果精度满足要求,或达到一定次数时迭代终止。

加入正则项的情况

修改步骤 4、5

4) g=\nabla_\theta l(\theta)+\frac{\lambda}{m}\theta

5) 计算 Hessian 矩阵 H,再让 H 加上下面这一项:

\frac{\lambda}{m} \begin{pmatrix} 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & ... & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}

四、代码实现

注:代码为 Python3 版本

完整的代码与样本放在了 Github 上,点此查看

1. 梯度下降

#############################################
# 梯度下降法估计参数
# X 是样本特征矩阵,每一行表示一个样本
# Y 是样本 X 中对应的标签,数组类型
# alpha 表示步长
#############################################
def gradientDescent(X, Y, alpha=0.001):
    # 在矩阵 X 的第一列插入 1
    X = np.insert(X, 0, 1, 1)
    # m 是训练样本数,n-1 是样本的特征数
    m, n = X.shape
    # 初始化 theta 值
    theta = np.mat(np.zeros(n))

    # 迭代求解theta
    for i in range(2000):
        theta -= alpha * gradient(X, Y, theta)

    debug("theta:", theta)
return theta

2. 梯度下降(带正则项)

#############################################
# 正则化的梯度下降法估计参数
# X 是样本特征矩阵,每一行表示一个样本
# Y 是样本 X 中对应的标签,数组类型
# alpha 表示步长
#############################################
def regularizedGradientDescent(X, Y, alpha=0.001):
    # 在矩阵 X 的第一列插入 1
    X = np.insert(X, 0, 1, 1)
    # m 是训练样本数,n-1 是样本的特征数
    m, n = X.shape
    # 初始化 theta 值
    theta = np.mat(np.zeros(n))
    # 惩罚系数
    lambda_ = 0.01 / Y.size

    # 迭代求解theta
    for i in range(200000):
        theta -= alpha * gradient(X, Y, theta)
        theta[0,1:] -= lambda_ * theta[0,1:]

    debug("theta:", theta)
return theta

3. 牛顿法

#############################################
# 牛顿法估计参数
# X 为特征矩阵,Y 为标签,iterNum 为迭代次数
#############################################
def newtonMethod(X, Y, iterNum=10):
    # 在矩阵 X 的第一列插入 1
    X = np.insert(X, 0, 1, 1)
    # m 是训练样本数,n-1 是样本的特征数
    m, n = X.shape
    # 初始化 theta 值
    theta = np.mat(np.zeros(n))

    # 迭代求解theta
    for iterIndex in range(iterNum):
        g = gradient(X, Y, theta)
        H = hessianMatrix(X, Y, theta)
        theta -= (H.I * g.T).T
        debug("theta({}):\n{}\n".format(iterIndex + 1, theta))

return theta

4. 牛顿法(带正则项)

#############################################
# 正则化牛顿法估计参数
# X 为特征矩阵,Y 为标签,iterNum 为迭代次数
#############################################
def regularizedNewtonMethod(X, Y, iterNum=10):
    # 在矩阵 X 的第一列插入 1
    X = np.insert(X, 0, 1, 1)
    # m 是训练样本数,n-1 是样本的特征数
    m, n = X.shape
    # 初始化 theta 值
    theta = np.mat(np.zeros(n))
    # 惩罚系数
    lambda_ = 0.01 / Y.size
    # hessian 矩阵正则项
    A = np.eye(theta.size)
    A[0,0] = 0

    # 迭代求解theta
    for iterIndex in range(iterNum):
        g = gradient(X, Y, theta) + lambda_ * theta
        H = hessianMatrix(X, Y, theta) + lambda_ * A
        theta -= (H.I * g.T).T
        debug("theta({}):\n{}\n".format(iterIndex + 1, theta))

return theta

相关文章