牛顿法和拟牛顿 BFGS 法实现 Logistic 回归¶
推导二元 Logistic 回归的 Hessian 矩阵,利用牛顿法和拟牛顿 BFGS 法求回归系数的极大似然估计。所得模型在训练样本的预测准确度为 78%。

问题描述:¶

数据文件:¶

推导似然函数的 Hessian 矩阵¶
似然函数的梯度¶

似然函数的 Hessian 矩阵¶

牛顿法算法步骤¶

回溯线搜索的牛顿法¶
![]()
拟牛顿 BFGS 法算法步骤¶


拟牛顿 BFGS 法中\(B\)矩阵的迭代公式¶


使用 Python 将牛顿法和拟牛顿 BFGS 法应用于求解\(\beta\)的极大似然估计¶
Python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
# 支持中文
matplotlib.rcParams["font.sans-serif"] = ["SimSun"]
matplotlib.rcParams["axes.unicode_minus"] = False
导入数据¶
求对数似然函数的相反数值,即\(J(\beta)\)¶
Python
def f(X, Y, beta):
sum = 0
for i in range(Y.shape[0]):
product = np.exp(beta @ X[i])
if Y[i][0] == 1:
sum = sum + np.log(product / (1 + product))
else:
sum = sum + np.log(1 / (1 + product))
return -sum
求梯度向量¶
Python
def gradient(X, Y, beta):
# 将梯度向量初始化为零向量
gradient_vector = np.zeros(X.shape[1])
# 将 i 在 1 到 n 上遍历并加和,求梯度的值
for i in range(Y.shape[0]):
product = np.exp(beta @ X[i])
if Y[i][0] == 1:
gradient_vector = gradient_vector - X[i] / (1 + product)
else:
gradient_vector = gradient_vector + product * X[i] / (1 + product)
return gradient_vector
求 Hessian 矩阵。此题中的 Hessian 矩阵只与\(X\)和\(\beta\)有关,与\(Y\)无关¶
Python
def hessian(X, beta):
# 将 S 矩阵初始化。注意要初始化为 0.0,如果初始化为 0,则不能储存小数
S = np.diag(np.full(Y.shape[0], 0.0))
for i in range(Y.shape[0]):
product = np.exp(beta @ X[i])
u = product / (1 + product)
S[i, i] = u * (1 - u)
return X.T @ S @ X
应用牛顿法,结合回溯线搜索求\(\beta\)的极大似然估计¶
值得注意的是,最初我没有用回溯线搜索控制步长,导致\(\beta\)的模长很大,代入到梯度公式中,由于要计算指数,而\(\beta\)出现在指数中,从而指数的结果过于巨大甚至溢出。这说明不控制步长的话结果可能不收敛,反而在朝目标值的反方向迭代。
由此可见,回溯线搜索等控制步长的方法具有很大的实际意义。
Python
# 设置 beta 的初始点
beta = np.ones(X.shape[1])
# 计算当前梯度的模
norm = np.linalg.norm(gradient(X, Y, beta))
norm_list = [norm]
# 当梯度的模长大于精度要求时,需要一直迭代
while norm > 1e-5:
# 求 Hessian 矩阵
hessian_matrix = hessian(X, beta)
# 求 Hessian 矩阵的逆
hessian_matrix_inverse = np.linalg.inv(hessian_matrix)
# 求 beta 的迭代方向和长度
v = -hessian_matrix_inverse @ gradient(X, Y, beta)
# 设置步长倍数的初始值
t = 1
# 回溯线性搜索,检查目前的步长是否满足要求,若步长太大,则需要缩小步长为原来的 0.5
while f(X, Y, beta + t * v) > f(X, Y, beta) + 0.4 * t * gradient(X, Y, beta) @ v:
t = 0.5 * t
# 迭代更新 beta
beta = beta + t * v
# 求当前梯度的模长
norm = np.linalg.norm(gradient(X, Y, beta))
# 记录当前梯度的模长
norm_list.append(norm)
print("beta 的最大似然估计是:", beta)
绘制迭代次数与梯度的模¶
Python
fig = plt.figure(figsize=(5, 4), dpi=200)
plt.plot(norm_list, "r-o", markerfacecolor="none") # 空心圆用 markerfacecolor='none',
plt.xlabel("迭代次数")
plt.ylabel("梯度的模")

应用拟牛顿法,结合回溯线搜索求\(\beta\)的极大似然估计¶
Python
# 设置 beta 的初始点
beta = np.ones(X.shape[1])
# 设置 B 的初始值,为一个对称正定矩阵
B = np.diag(np.full(X.shape[1], 10))
# 当梯度的模长大于精度要求时,需要一直迭代
# 求 g_k
g_k = gradient(X, Y, beta)
# 计算当前梯度的模
norm = np.linalg.norm(g_k)
norm_list = [norm]
while norm > 1e-5:
# 求迭代的方向和步长
p = -np.linalg.inv(B) @ g_k
# 设置步长倍数的初始值
t = 1
# 回溯线性搜索,检查目前的步长是否满足要求,若步长太大,则需要缩小步长为原来的 0.5
while f(X, Y, beta + t * p) > f(X, Y, beta) + 0.4 * t * g_k @ p:
t = 0.5 * t
# 迭代更新 B
# 求 g_{k+1}
g_k_plus_1 = gradient(X, Y, beta + t * p)
# 求 y_k
y_k = g_k_plus_1 - g_k
# 求 sigma_k
sigma_k = t * p
# 迭代更新 B
B = (
B
+ (np.outer(y_k, y_k)) / (y_k @ sigma_k)
- (np.outer(B.dot(sigma_k), (sigma_k.T)).dot(B.T)) / (sigma_k.T @ B @ sigma_k)
)
# 迭代更新 beta
beta = beta + t * p
# 求当前梯度
g_k = gradient(X, Y, beta)
# 求当前梯度的模长
norm = np.linalg.norm(g_k)
# 记录当前梯度的模长
norm_list.append(norm)
print("beta 的最大似然估计是:", beta)
绘制迭代次数与梯度的模¶
Python
fig = plt.figure(figsize=(5, 4), dpi=200)
plt.plot(norm_list, "r-o", markerfacecolor="none") # 空心圆用 markerfacecolor='none',
plt.xlabel("迭代次数", usetex=False) # 中文不用 tex 渲染,否则会报错
plt.ylabel("梯度的模", usetex=False)

利用训练出的\(\beta\)值,对\(Y\)进行预测¶
Python
df["p_predict"] = np.nan
df["Y_predict"] = np.nan
df["correct"] = np.nan
df["p_predict"] = df.apply(
lambda line: 1 / (1 + np.exp(-line.iloc[2:7] @ beta)), axis=1
)
# 设定判断阈值
hurdle = 0.5
df["Y_predict"] = df.apply(lambda line: 1 if line["p_predict"] > hurdle else 0, axis=1)
df["correct"] = df.apply(
lambda line: 1 if line["Y_predict"] == line["Y"] else 0, axis=1
)
计算预测准确率¶
Text Only
## Unnamed: 0 Y X1 X2 ... X5 p_predict Y_predict correct
## 0 1 0 0.227077 -0.492374 ... 0.337587 0.074140 0 1
## 1 2 1 -1.439121 -0.898333 ... -1.729708 0.840268 1 1
## 2 3 0 -0.252111 -0.274725 ... -0.705981 0.654752 1 0
## 3 4 1 -1.736983 -1.197634 ... -1.266615 0.471904 0 0
## 4 5 1 -1.081011 -0.283816 ... -0.937780 0.639866 1 1
## .. ... .. ... ... ... ... ... ... ...
## 195 196 1 -0.555757 -0.483450 ... -1.764113 0.993497 1 1
## 196 197 0 -0.581243 -0.590711 ... 0.485322 0.057910 0 1
## 197 198 0 -1.752883 -0.934694 ... 0.876816 0.004954 0 1
## 198 199 1 0.397383 0.231328 ... 0.902995 0.121900 0 0
## 199 200 1 1.127998 1.487247 ... 0.228712 0.963261 1 1
##
## [200 rows x 10 columns]