4. 权重初始化方法比较
任务说明
本案例在相同结构的MLP上,分别使用Xavier初始化、He初始化和默认随机初始化(PyTorch默认的均匀分布初始化)训练模型,在合成二分类数据集上比较三种初始化方法对训练收敛速度和最终性能的影响。
实现方案
构建一个3层MLP(输入维度10,隐藏层64和32,输出1),使用Sigmoid激活函数。通过reset_parameters方法分别应用三种初始化策略,在相同数据、相同优化器(SGD, lr=0.1)和相同损失函数(BCEWithLogitsLoss)下训练200个epoch,记录每个epoch的训练损失和最终测试准确率。
代码实现
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
# 固定随机种子保证可复现
torch.manual_seed(42)
np.random.seed(42)
# 生成合成二分类数据集
def generate_data(n_samples=2000, n_features=10):
X = torch.randn(n_samples, n_features)
# 真实权重生成标签(线性可分+噪声)
true_w = torch.randn(n_features) * 2
logits = X @ true_w
prob = torch.sigmoid(logits)
y = (prob > 0.5).float()
return X, y
X, y = generate_data()
# 划分训练/测试集
train_idx = np.random.choice(len(X), size=1600, replace=False)
test_idx = np.array([i for i in range(len(X)) if i not in train_idx])
X_train, y_train = X[train_idx], y[train_idx]
X_test, y_test = X[test_idx], y[test_idx]
# 定义MLP模型
class MLP(nn.Module):
def __init__(self, init_method='default'):
super().__init__()
self.fc1 = nn.Linear(10, 64)
self.fc2 = nn.Linear(64, 32)
self.fc3 = nn.Linear(32, 1)
self.sigmoid = nn.Sigmoid()
self._apply_init(init_method)
def _apply_init(self, method):
for m in self.modules():
if isinstance(m, nn.Linear):
if method == 'xavier':
nn.init.xavier_uniform_(m.weight)
elif method == 'he':
nn.init.kaiming_uniform_(m.weight, nonlinearity='relu')
# 'default' 使用PyTorch默认初始化,不做任何操作
nn.init.zeros_(m.bias)
def forward(self, x):
x = self.sigmoid(self.fc1(x))
x = self.sigmoid(self.fc2(x))
x = self.fc3(x)
return x.squeeze(-1)
# 训练函数
def train_model(init_method, epochs=200):
model = MLP(init_method)
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)
losses = []
for epoch in range(epochs):
model.train()
optimizer.zero_grad()
logits = model(X_train)
loss = criterion(logits, y_train)
loss.backward()
optimizer.step()
losses.append(loss.item())
# 测试
model.eval()
with torch.no_grad():
test_logits = model(X_test)
preds = (torch.sigmoid(test_logits) > 0.5).float()
acc = (preds == y_test).float().mean().item()
return losses, acc
# 运行三种初始化方法
methods = ['xavier', 'he', 'default']
results = {}
for method in methods:
losses, acc = train_model(method)
results[method] = {'losses': losses, 'acc': acc}
print(f"{method} 初始化 - 测试准确率: {acc:.4f}")
# 绘制训练损失曲线
plt.figure(figsize=(10, 6))
colors = {'xavier': '#2e7d32', 'he': '#c2185b', 'default': '#6d4c41'}
for method in methods:
plt.plot(results[method]['losses'], label=f"{method} (acc={results[method]['acc']:.4f})",
color=colors[method], linewidth=2)
plt.xlabel('Epoch')
plt.ylabel('Training Loss')
plt.title('Weight Initialization Comparison')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('init_comparison.png', dpi=150)
plt.show()
运行与验证
运行方式:将上述代码保存为init_comparison.py,在已安装PyTorch和Matplotlib的Python环境中执行python init_comparison.py。
验证方法:观察控制台输出的三种初始化方法的测试准确率,以及生成的init_comparison.png损失曲线图。判定标准:
- Xavier和He初始化的训练损失应比默认随机初始化下降更快(前50个epoch内损失值更小);
- 最终测试准确率应满足:Xavier和He初始化不低于默认随机初始化;
- 三种方法使用相同数据和超参数,差异仅来自初始化方式,因此损失曲线的分离趋势可直接归因于初始化策略。
预期输出:由于固定了随机种子,代码逻辑可唯一推出三种初始化方法的测试准确率数值(例如Xavier约0.95、He约0.94、默认约0.90),具体数值以实际执行为准。损失曲线应显示Xavier和He的收敛速度明显快于默认初始化。