用 Python 动态演示线性回归与损失函数下降
主题
线性回归的目标是找到一条直线(模型参数 \(\theta_0, \theta_1\)),使得预测值 \(\hat{y}\) 尽可能接近真实值 \(y\)。模型形式为:
\[Loss=\frac{1}{2m}(y-\hat{y})
\]
我们的目标是最小化\(\textbf{平方误差损失函数}\)
\[J(\theta)=\frac{1}{2m}\sum_{i=1}^{m}\left(\hat{y_i}-y_i\right)^2
\]
梯度下降法(Gradient Descent)
为了求解参数 \(\theta = [\theta_0, \theta_1]^T\),我们采用梯度下降:
\[\theta\sim\theta-\alpha\cdot \nabla_\theta J(\theta)
\]
其中学习率 \(\alpha\) 控制每次更新的步长。
代码
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 生成模拟数据 (带噪声的线性关系)
np.random.seed(0)
X = np.linspace(0, 10, 50).reshape(-1, 1) # 特征 (50个点)
y = 3 * X + 5 + np.random.randn(50, 1) * 3 # 标签 (y=3x+5+噪声)
# 添加偏置列 (x0=1)
X_b = np.c_[np.ones((X.shape[0], 1)), X]
# 初始化参数 (theta0, theta1)
theta = np.random.randn(2, 1)
# 学习率
lr = 0.05
# 迭代次数
epochs = 100
# 保存每一步的参数和loss
thetas = []
losses = []
def compute_loss(X_b, y, theta):
m = len(y)
y_pred = X_b.dot(theta)
return (1/(2*m)) * np.sum((y_pred - y)**2)
# 梯度下降过程
for i in range(epochs):
gradients = (1/len(y)) * X_b.T.dot(X_b.dot(theta) - y)
theta = theta - lr * gradients
thetas.append(theta.copy())
losses.append(compute_loss(X_b, y, theta))
# -------- 动态显示 --------
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
# 左图:散点+拟合直线
ax1.scatter(X, y, color="blue", label="训练数据")
line, = ax1.plot([], [], color="red", lw=2, label="拟合直线")
ax1.legend()
ax1.set_title("线性回归拟合")
# 右图:损失函数曲线
ax2.set_xlim(0, epochs)
ax2.set_ylim(0, max(losses))
ax2.set_title("损失函数变化")
ax2.set_xlabel("迭代次数")
ax2.set_ylabel("损失")
loss_line, = ax2.plot([], [], color="green", lw=2)
def update(frame):
theta = thetas[frame]
y_pred = X_b.dot(theta)
# 更新拟合直线
line.set_data(X, y_pred)
# 更新损失函数
loss_line.set_data(range(frame+1), losses[:frame+1])
return line, loss_line
ani = FuncAnimation(fig, update, frames=epochs, interval=100, blit=True)
plt.show()
浙公网安备 33010602011771号