在机器学习模型训练过程中,准确率(acc)曲线的波动和增长模式受多种因素影响。

在机器学习模型训练过程中,准确率(acc)曲线的波动和增长模式受多种因素影响。

1. 准确率波动(增减交替)的原因

学习率过高

  • 现象:参数更新步长过大,导致模型在最优解附近震荡,无法稳定收敛。
  • 解决方案:降低学习率或使用学习率调度器动态调整。

数据分布不均

  • 现象:训练数据中正负样本的随机性可能导致批次间分布波动,影响梯度方向。
  • 解决方案:增加批次大小或使用更稳定的优化器(如 RMSprop、Adam)。

模型容量不足

  • 现象:简单的线性模型可能无法完全捕捉复杂模式,导致训练不稳定。
  • 解决方案:增加模型复杂度(如添加隐藏层)或引入非线性激活函数。

2. 先快速提升后缓慢增长的原因

梯度消失 / 爆炸

  • 现象:Sigmoid 激活函数在输入值较大或较小时梯度趋近于 0,导致训练后期更新缓慢。
  • 解决方案:改用 ReLU 等更适合深度网络的激活函数。

优化算法特性

  • 现象:Adam 等自适应优化器在初期快速收敛,但后期可能因学习率衰减而停滞。
  • 解决方案:尝试 SGD + 动量或调整优化器参数(如betas)。

过拟合

  • 现象:模型在训练集上表现越来越好,但泛化能力下降。
  • 解决方案:添加正则化(如 L2 正则化、Dropout)或提前停止训练。

3. 优化建议

以下是优化代码的具体实现:

python
 
运行
 
 
 
 
import torch
import torch.nn as nn
import numpy as np
from matplotlib import pyplot as plt
from torch.optim.lr_scheduler import StepLR

class ImprovedModel(nn.Module):
    def __init__(self, input_size):
        super(ImprovedModel, self).__init__()
        # 增加模型复杂度,使用多层网络
        self.layer1 = nn.Linear(input_size, 10)
        self.layer2 = nn.Linear(10, 1)
        self.activation = nn.ReLU()  # 改用ReLU激活函数
        self.output_act = nn.Sigmoid()  # 输出层用Sigmoid保持二分类特性
        self.loss = nn.BCELoss()  # 改用更适合二分类的交叉熵损失

    def forward(self, x, y=None):
        x = self.activation(self.layer1(x))
        y_pred = self.output_act(self.layer2(x))
        if y is not None:
            return self.loss(y_pred, y)
        else:
            return y_pred

def main():
    # 配置参数
    epoch_num = 100
    batch_size = 64  # 增大批次大小提高稳定性
    train_sample = 4000
    input_size = 5
    learning_rate = 0.005  # 适当提高初始学习率
    
    # 建立改进模型
    model = ImprovedModel(input_size)
    optim = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=1e-5)  # 添加L2正则化
    scheduler = StepLR(optim, step_size=20, gamma=0.5)  # 学习率调度器,每20轮衰减一半
    
    log = []
    train_x, train_y = build_dataset(train_sample)

    for epoch in range(epoch_num):
        model.train()
        watch_loss = []
        for batch_index in range(train_sample // batch_size):
            x = train_x[batch_index * batch_size : (batch_index + 1) * batch_size]
            y = train_y[batch_index * batch_size : (batch_index + 1) * batch_size]
            loss = model(x, y)
            loss.backward()
            optim.step()
            optim.zero_grad()
            watch_loss.append(loss.item())
        
        print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
        acc = evaluate(model)
        log.append([acc, float(np.mean(watch_loss))])
        scheduler.step()  # 更新学习率
    
    # 保存模型并画图
    torch.save(model.state_dict(), "improved_model.bin")
    plt.figure(figsize=(10, 5))
    plt.subplot(1, 2, 1)
    plt.plot(range(len(log)), [l[0] for l in log], label="acc", color='blue')
    plt.title("Accuracy")
    plt.legend()
    
    plt.subplot(1, 2, 2)
    plt.plot(range(len(log)), [l[1] for l in log], label="loss", color='red')
    plt.title("Loss")
    plt.legend()
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    main()
 

4. 其他调试技巧

  1. 可视化梯度:监控梯度范数,检测梯度消失或爆炸。
    python
     
    运行
     
     
     
     
    # 在反向传播后添加
    total_norm = 0
    for p in model.parameters():
        param_norm = p.grad.data.norm(2)
        total_norm += param_norm.item() ** 2
    total_norm = total_norm ** 0.5
    print(f"梯度范数: {total_norm}")
    
     
  2. 早停策略:当验证集准确率不再提升时停止训练。
    python
     
    运行
     
     
     
     
    best_acc = 0
    patience = 10
    counter = 0
    
    for epoch in range(epoch_num):
        # 训练代码...
        current_acc = evaluate(model)
        if current_acc > best_acc:
            best_acc = current_acc
            counter = 0
            torch.save(model.state_dict(), "best_model.bin")
        else:
            counter += 1
            if counter >= patience:
                print("早停触发!")
                break
    
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  3. 批量归一化:在网络层间添加BatchNorm1d提高训练稳定性。

总结

通过调整学习率策略、增加模型复杂度、改进损失函数和添加正则化,你可以获得更平滑的训练曲线和更快的收敛速度。不同的参数设置会导致训练动态变化,建议多尝试几种组合找到最适合你任务的配置。
posted @ 2025-06-24 00:04  m516606428  阅读(225)  评论(0)    收藏  举报