能否在Pytorch或TensorFlow中对已冻结神经网络做宽度方向节点扩展
冻结神经网络宽度扩展的实现方案

该操作在PyTorch和TensorFlow中都可以实现,以下是基于你提供的初始模型的PyTorch实现方案,TensorFlow实现思路一致,仅API调用有差异:
实现逻辑说明
- PyTorch中全连接层
nn.Linear的权重参数维度为(输出特征数, 输入特征数),偏置参数维度为(输出特征数,) - 扩展时优先保留原模型所有已冻结的权重值,仅对新增维度对应的参数做随机初始化
- 通过梯度钩子强制原有权重对应的梯度在反向传播时清零,保证原有权重不会被更新,仅新增参数参与训练
完整实现代码
import torch import torch.nn as nn # 原模型定义及冻结流程 class Net(nn.Module): def __init__(self): super(Net,self).__init__() self.fc1 = nn.Linear(3, 5) self.fc2 = nn.Linear(5, 3) self.fc3 = nn.Linear(3, 1) def forward(self,x): x = self.fc1(x) x = self.fc2(x) x = self.fc3(x) return x # 初始化原模型并冻结所有参数 original_model = Net() for param in original_model.parameters(): param.requires_grad = False # ---------------------- 模型宽度扩展 ---------------------- # 扩展后的维度配置 h1_new = 7 # 原5个节点+新增2个 h2_new = 4 # 原3个节点+新增1个 out_new = 2 # 原1个输出+新增1个 # 1. 扩展第一层fc1:输入维度3不变,输出从5扩展到7 extended_fc1 = nn.Linear(3, h1_new) # 保留原fc1的权重和偏置 extended_fc1.weight.data[:5, :] = original_model.fc1.weight.data extended_fc1.bias.data[:5] = original_model.fc1.bias.data # 注册梯度钩子:反向传播时把原有5个节点对应的梯度置0 def freeze_original_out_grad(grad, original_dim): grad_clone = grad.clone() grad_clone[:original_dim] = 0. return grad_clone extended_fc1.weight.register_hook(lambda grad: freeze_original_out_grad(grad, 5)) extended_fc1.bias.register_hook(lambda grad: freeze_original_out_grad(grad, 5)) # 2. 扩展第二层fc2:输入从5扩展到7,输出从3扩展到4 extended_fc2 = nn.Linear(h1_new, h2_new) # 保留原fc2中对应原输入、原输出位置的权重 extended_fc2.weight.data[:3, :5] = original_model.fc2.weight.data extended_fc2.bias.data[:3] = original_model.fc2.bias.data # 注册梯度钩子:原有输出、原有输入对应的权重梯度清零 def fc2_grad_hook(grad, original_out, original_in): grad_clone = grad.clone() grad_clone[:original_out, :original_in] = 0. return grad_clone extended_fc2.weight.register_hook(lambda grad: fc2_grad_hook(grad, 3, 5)) extended_fc2.bias.register_hook(lambda grad: freeze_original_out_grad(grad, 3)) # 3. 扩展输出层fc3:输入从3扩展到4,输出从1扩展到2 extended_fc3 = nn.Linear(h2_new, out_new) # 保留原fc3的权重 extended_fc3.weight.data[:1, :3] = original_model.fc3.weight.data extended_fc3.bias.data[:1] = original_model.fc3.bias.data # 注册梯度钩子 def fc3_grad_hook(grad, original_out, original_in): grad_clone = grad.clone() grad_clone[:original_out, :original_in] = 0. return grad_clone extended_fc3.weight.register_hook(lambda grad: fc3_grad_hook(grad, 1, 3)) extended_fc3.bias.register_hook(lambda grad: freeze_original_out_grad(grad, 1)) # 组装扩展后的新模型 class ExtendedNet(nn.Module): def __init__(self): super().__init__() self.fc1 = extended_fc1 self.fc2 = extended_fc2 self.fc3 = extended_fc3 def forward(self, x): x = self.fc1(x) x = self.fc2(x) x = self.fc3(x) return x extended_model = ExtendedNet() # ---------------------- 效果验证 ---------------------- test_input = torch.rand(5,3) original_output = original_model(test_input) extended_output = extended_model(test_input) print("扩展后模型输出维度:", extended_output.shape) # 输出为torch.Size([5, 2]),符合要求 print("原有逻辑一致性验证误差:", torch.abs(original_output - extended_output[:,0].unsqueeze(1)).sum().item()) # 误差为0或极小浮点误差,证明原有冻结权重完全保留,未被修改
内容的提问来源于stack exchange,提问作者Eka
相关产品推荐
相关产品推荐

