在PyTorch中测试多种损失函数是否需创建多个神经网络实例?
嗨,这个问题问到点子上了,咱们先把两种方案的问题和正确做法理清楚:
先给结论:
你的第一种思路(创建两个独立网络实例)是可行的,但要补全细节保证对比公平;而第二种思路(同一个网络同时用两个损失反向传播)是完全错误的,根本达不到对比两种损失效果的目的。
为什么第二种方案不行?
你写的第二种代码里,los_1.backward()会把损失1的梯度计算出来并累积到网络参数的.grad属性里,紧接着los_2.backward()又会把损失2的梯度也加进去,最后opt.step()是用两个损失的梯度之和来更新权重——这本质上是把两种损失合并成一个复合损失来训练网络,而不是分别用两种损失训练两个独立的模型,完全偏离了你“对比两种损失效果”的目标。
正确的实现方式
方式一:双网络实例(直观,适合同时训练)
这种方法需要创建两个网络,关键是要让它们从完全相同的初始权重开始训练,不然对比就没有意义了。另外你代码里的optim.SGD('params')是错误的,要传入对应网络的参数:
import torch import torch.nn as nn import torch.optim as optim # 假设你的ANN网络定义如下 class ANN(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(20, 64) self.fc2 = nn.Linear(64, 10) def forward(self, x): x = torch.relu(self.fc1(x)) return self.fc2(x) # 初始化两个网络,并复制权重保证初始状态一致 network_loss1 = ANN().cuda() network_loss2 = ANN().cuda() network_loss2.load_state_dict(network_loss1.state_dict()) # 复制初始权重 # 定义两种损失函数 criterion_loss1 = nn.CrossEntropyLoss() criterion_loss2 = nn.MSELoss() # 分别为两个网络创建优化器 optimizer_loss1 = optim.SGD(network_loss1.parameters(), lr=0.01, momentum=0.9) optimizer_loss2 = optim.SGD(network_loss2.parameters(), lr=0.01, momentum=0.9) num_epochs = 20 data_loader = torch.utils.data.DataLoader(...) # 替换成你的数据加载器 for epoch in range(num_epochs): # 遍历数据集,分别训练两个网络 for batch_idx, (inputs, labels) in enumerate(data_loader): inputs, labels = inputs.cuda(), labels.cuda() # 训练使用损失1的网络 optimizer_loss1.zero_grad() outputs1 = network_loss1(inputs) loss1 = criterion_loss1(outputs1, labels) loss1.backward() optimizer_loss1.step() # 训练使用损失2的网络 optimizer_loss2.zero_grad() outputs2 = network_loss2(inputs) loss2 = criterion_loss2(outputs2, labels.float()) # 根据损失类型调整标签格式 loss2.backward() optimizer_loss2.step() # 每个epoch结束后,可以记录两个网络的损失、精度等指标,方便后续对比 print(f"Epoch {epoch+1} | Loss1: {loss1.item():.4f} | Loss2: {loss2.item():.4f}")
方式二:单网络实例(节省显存,适合资源有限的情况)
如果你的显存不够同时放两个网络,可以用单网络分两次训练:先训练损失1,保存训练结果;然后把网络重置回初始状态,再训练损失2。这样也能保证对比的公平性:
import torch import torch.nn as nn import torch.optim as optim class ANN(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(20, 64) self.fc2 = nn.Linear(64, 10) def forward(self, x): x = torch.relu(self.fc1(x)) return self.fc2(x) network = ANN().cuda() initial_weights = network.state_dict() # 保存初始权重,用于后续重置 # 先训练损失1的情况 criterion_loss1 = nn.CrossEntropyLoss() optimizer_loss1 = optim.SGD(network.parameters(), lr=0.01, momentum=0.9) loss_history_loss1 = [] for epoch in range(num_epochs): epoch_loss = 0.0 for inputs, labels in data_loader: inputs, labels = inputs.cuda(), labels.cuda() optimizer_loss1.zero_grad() outputs = network(inputs) loss = criterion_loss1(outputs, labels) loss.backward() optimizer_loss1.step() epoch_loss += loss.item() avg_loss = epoch_loss / len(data_loader) loss_history_loss1.append(avg_loss) print(f"Loss1 Training Epoch {epoch+1}: Avg Loss = {avg_loss:.4f}") # 保存损失1训练后的权重 trained_weights_loss1 = network.state_dict() # 重置网络到初始状态,训练损失2的情况 network.load_state_dict(initial_weights) criterion_loss2 = nn.MSELoss() optimizer_loss2 = optim.SGD(network.parameters(), lr=0.01, momentum=0.9) loss_history_loss2 = [] for epoch in range(num_epochs): epoch_loss = 0.0 for inputs, labels in data_loader: inputs, labels = inputs.cuda(), labels.cuda() optimizer_loss2.zero_grad() outputs = network(inputs) loss = criterion_loss2(outputs, labels.float()) loss.backward() optimizer_loss2.step() epoch_loss += loss.item() avg_loss = epoch_loss / len(data_loader) loss_history_loss2.append(avg_loss) print(f"Loss2 Training Epoch {epoch+1}: Avg Loss = {avg_loss:.4f}") # 对比两个损失的训练历史,或者加载权重测试模型性能
最后提醒
不管用哪种方式,都要保证两个训练过程的所有其他变量一致:比如学习率、优化器参数、训练轮数、数据加载顺序(如果用随机采样的话,要设置相同的随机种子),这样对比出来的结果才是两种损失函数的真实差异。
内容的提问来源于stack exchange,提问作者BadBayesian
相关产品推荐
相关产品推荐

