2026/1/19 5:30:29
网站建设
项目流程
用vs2012做网站,登录企业邮箱入口,网站建设需要用到什么,wordpress免费CDN加速1. 为什么你的模型“记性”这么差#xff1f;#xff08;痛点与背景#xff09;
想象一下#xff0c;你训练了一个神经网络来识别手写数字#xff08;MNIST#xff09;#xff0c;准确率高达 99%。
接着#xff0c;你希望能复用这个聪明的脑子#xff0c;让它继续学习…1. 为什么你的模型“记性”这么差痛点与背景想象一下你训练了一个神经网络来识别手写数字MNIST准确率高达 99%。接着你希望能复用这个聪明的脑子让它继续学习识别时尚单品Fashion-MNIST。你把模型拿来在“衣服鞋子”的数据集上跑了几轮训练。结果很棒它现在能完美识别运动鞋和衬衫了。但是当你随手扔给它一张数字 “7” 的图片时它却一脸自信地告诉你“这是一只靴子”这就是灾难性遗忘Catastrophic Forgetting。在传统的反向传播中为了让模型适应新任务Task B优化器会毫不留情地修改模型里的权重参数。它并不在乎这些参数之前对旧任务Task A有多重要只要能降低 Task B 的 Loss它就会大幅改变权重。结果就是旧知识的“神经连接”被彻底破坏了。EWCElastic Weight Consolidation的出现就是为了解决这个问题。它能让模型在学习新技能的同时优雅地“锁住”那些对旧技能至关重要的记忆。2. 概念拆解给神经元装上“弹簧”EWC 的论文里充满了费雪信息矩阵Fisher Information Matrix和黑森矩阵Hessian Matrix等高深术语但我们先忘掉数学用**“房间装修”**来打个比方。 生活化类比设计师的妥协把神经网络想象成一个刚刚装修好的房间。Task A旧任务这是一个“家庭影院”模式。为了达到最佳视听效果沙发权重 1、音响权重 2、投影仪权重 3必须摆在特定的位置。Task B新任务现在你想把这个房间改成“瑜伽室”。没有 EWC 的做法装修队进来为了腾出瑜伽空间直接把沙发扔出去把音响砸了。瑜伽室很完美但家庭影院彻底毁了。有 EWC 的做法你告诉装修队“有些东西你们随便动但有些东西很重要动起来很费劲。”不重要的权重比如墙角的绿植对家庭影院影响不大随便移。重要的权重比如投影仪对家庭影院极其重要。如果你非要移动它就像是在拉一根极其坚硬的弹簧。你可以稍微挪一点点但挪得越远弹簧的反作用力惩罚项就越大。EWC 的核心魔法就在于它能自动计算出哪些家具权重是“承重墙”哪些是“装饰品”。 原理图解逻辑训练 Task A正常训练找到最优权重。计算重要性Fisher Matrix分析 Task A 的 Loss 地形。如果某个权重稍微变动一下Loss 就剧烈飙升说明这个权重非常重要地形陡峭如果权重变了很多 Loss 还没啥反应说明它不重要地形平坦。训练 Task B在 Loss 函数后面加上一个 EWC 惩罚项那个弹簧。新任务的正常 Loss。这一项决定了你有多想“守旧”。值越大越难忘。费雪信息量重要性系数。越重要这一项越大改变参数带来的 Penalty 就越大。3. 动手实战PyTorch 实现 EWC我们将通过一个极简的例子先让模型拟合一个函数再拟合另一个函数看它能不能同时记住两者。环境准备你需要安装 PyTorchpip install torch matplotlib核心代码解析Python/* by 01130.hk - online tools website : 01130.hk/zh/alldns.html */ import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt import copy # # 1. 定义一个简单的神经网络 # class SimpleNet(nn.Module): def __init__(self): super(SimpleNet, self).__init__() # 为了演示我们用一个小型的网络 self.fc1 nn.Linear(10, 20) self.fc2 nn.Linear(20, 20) self.fc3 nn.Linear(20, 2) # 输出2类 def forward(self, x): x torch.relu(self.fc1(x)) x torch.relu(self.fc2(x)) return self.fc3(x) # # 2. EWC 核心类 (The Magic) # class EWC: def __init__(self, model, dataset): self.model model self.dataset dataset # 存储旧任务的最优参数 (theta_A*) self.params {n: p.data.clone() for n, p in self.model.named_parameters()} # 存储每个参数的重要性 (Fisher Information) self.fisher self._calculate_fisher() def _calculate_fisher(self): fisher {} # 初始化 Fisher 矩阵为 0 for n, p in self.model.named_parameters(): fisher[n] torch.zeros_like(p.data) self.model.eval() criterion nn.CrossEntropyLoss() # 遍历数据计算梯度的平方 # 这里的逻辑是梯度越大说明参数稍微一动 Loss 变化就大 - 参数越重要 for input_data, target in self.dataset: self.model.zero_grad() output self.model(input_data.unsqueeze(0)) # batch_size1 loss criterion(output, target.unsqueeze(0)) loss.backward() for n, p in self.model.named_parameters(): if p.grad is not None: # Fisher 近似等于 梯度的平方 fisher[n] p.grad.data ** 2 # 归一化 for n in fisher: fisher[n] / len(self.dataset) return fisher def penalty(self, new_model): loss 0 for n, p in new_model.named_parameters(): # EWC 公式: Sum( F * (new_theta - old_theta)^2 ) _loss self.fisher[n] * (p - self.params[n]) ** 2 loss _loss.sum() return loss # # 3. 模拟训练流程 # def get_data(task_id): # 模拟数据Task 1 输入全1Task 2 输入全0 if task_id 1: return [(torch.ones(10), torch.tensor(0)) for _ in range(100)] else: return [(torch.zeros(10), torch.tensor(1)) for _ in range(100)] # 实例化模型 model SimpleNet() optimizer optim.SGD(model.parameters(), lr0.1) criterion nn.CrossEntropyLoss() print( 开始训练 任务 A (识别全1向量)) data_a get_data(1) for epoch in range(5): for x, y in data_a: optimizer.zero_grad() loss criterion(model(x.unsqueeze(0)), y.unsqueeze(0)) loss.backward() optimizer.step() print(任务 A 训练完成。保存 EWC 状态...) # --- 关键步骤计算 Task A 的重要性权重 --- ewc EWC(model, data_a) print( 开始训练 任务 B (识别全0向量)同时开启 EWC 保护) data_b get_data(2) ewc_lambda 1000 # 惩罚力度越大越照顾旧任务 for epoch in range(5): total_loss 0 for x, y in data_b: optimizer.zero_grad() # 1. 计算新任务的 Loss loss_b criterion(model(x.unsqueeze(0)), y.unsqueeze(0)) # 2. 计算 EWC 惩罚项 (旧任务的记忆) loss_ewc ewc.penalty(model) # 3. 总 Loss loss loss_b (ewc_lambda * loss_ewc) loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch}: Loss {total_loss:.4f}) # # 4. 验证结果 # model.eval() test_a model(torch.ones(10).unsqueeze(0)) test_b model(torch.zeros(10).unsqueeze(0)) print(\n 最终测试 ) print(fTask A (应为类别0): 预测概率 {torch.softmax(test_a, dim1).detach().numpy()}) print(fTask B (应为类别1): 预测概率 {torch.softmax(test_b, dim1).detach().numpy()})代码划重点_calculate_fisher这是 EWC 的灵魂。我们在训练完 Task A 后立刻冻结模型通过反向传播拿到梯度。注意这里不用optimizer.step()我们只想要梯度值来计算$F_i$。penalty在训练 Task B 时每次迭代都会调用这个函数。它检查当前的参数偏离旧参数有多远并乘以重要性系数。4. 进阶深潜陷阱与最佳实践⚠️ 常见陷阱Fisher 计算开销在上面的代码中我们是一个样本一个样本算的为了代码清晰。在生产环境中这会非常慢。优化使用小批量Mini-batch来估算 Fisher 信息不需要遍历整个数据集随机采样几千个样本通常就足够了。Lambda 的平衡太小EWC 失效照样遗忘。太大模型被旧记忆“锁死”了根本学不进新东西欠拟合 Task B。这需要像调学习率一样去调参。 生产环境贴士多任务扩展如果你有 Task A, B, C... 怎么做通常的做法是维护一个累积的 Fisher 矩阵。当你学完 Task B 准备学 C 时你的锚点应该变成 Task B 的参数而 Fisher 矩阵应该是 A 和 B 的重要性之和。在线 EWC (Online EWC)这是一种更高效的变体解决了存储多个 Fisher 矩阵带来的内存爆炸问题。5. 总结与延伸核心知识点EWC 本质上是一种正则化Regularization技术。它通过费雪信息矩阵识别出神经网络中的“关键承重墙”并在学习新知识时强行保护这些区域从而在可塑性学习新知识和稳定性保持旧知识之间找到平衡。