PyTorch联邦学习每轮测试评估结果一致问题求助
问题:联邦学习中PyTorch模型测试结果始终为随机水平
问题场景
在联邦学习部署中,需要用PyTorch每轮测试模型的损失与准确率,待测试权重为numpy数组列表。但每次测试结果完全一致,数值约为Test_loss: 2.306, accuracy: 0.10,处于随机猜测的水平。
模型代码
import torch import torch.nn as nn import torch.nn.functional as F from torchvision import datasets, transforms from torch.utils.data import DataLoader, Compose DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") class Net(nn.Module): """Model (simple CNN adapted from 'PyTorch: A 60 Minute Blitz')""" def __init__(self) -> None: super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) x = x.view(-1, 16 * 5 * 5) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) return self.fc3(x)
原测试相关方法
def load_testset(): """Load CIFAR-10 (test set).""" trf = Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) testset = datasets.CIFAR10("./data", train=False, download=True, transform=trf) return DataLoader(testset), testset def get_AccuracyAndLoss(weights): # Load the existing weights list weights_list = weights for i, weights in enumerate(weights_list): layer_name = 'layer_' + str(i) setattr(Net(), layer_name, nn.Parameter(torch.from_numpy(weights))) # Load model and data (simple CNN, CIFAR-10) net = Net().to(DEVICE) testloader, test_set = load_testset() criterion = torch.nn.CrossEntropyLoss() correct, total, loss = 0, 0, 0.0 net.eval() with torch.no_grad(): for images, labels in testloader: images, labels = images.to(DEVICE), labels.to(DEVICE) outputs = net(images) loss += criterion(outputs, labels).item() _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Test_loss: %.3f, accuracy: %.2f' % (loss/len(testloader), correct / total))
问题根源
核心错误在于权重加载逻辑完全无效:
- 循环中
setattr(Net(), layer_name, ...)每次创建一个全新的Net实例,给这个临时实例添加自定义参数,但这个实例创建后就被丢弃,完全没有和后续测试用的net关联。 - 最终测试用的
net = Net().to(DEVICE)是全新初始化的模型,参数都是随机值。CIFAR-10有10个类别,随机猜测的准确率约为10%,交叉熵损失约为ln(10)≈2.302,和你得到的结果完全匹配。
修正后的测试方法
def get_AccuracyAndLoss(weights): # 1. 创建模型实例并移动到设备 net = Net().to(DEVICE) # 2. 将numpy权重加载到模型参数中 with torch.no_grad(): # 遍历模型参数和权重列表,逐个赋值 for param, weight_np in zip(net.parameters(), weights): # 将numpy数组转为torch张量,移动到对应设备,然后复制到参数中 param.copy_(torch.from_numpy(weight_np).to(DEVICE)) # 3. 加载测试集 testloader, _ = load_testset() criterion = torch.nn.CrossEntropyLoss() correct, total, loss = 0, 0, 0.0 net.eval() with torch.no_grad(): for images, labels in testloader: images, labels = images.to(DEVICE), labels.to(DEVICE) outputs = net(images) loss += criterion(outputs, labels).item() _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Test_loss: %.3f, accuracy: %.2f' % (loss/len(testloader), correct / total))
关键注意事项
- 权重顺序匹配:确保传入的
weights列表的顺序,和net.parameters()返回的参数顺序完全一致。可以通过for name, param in net.named_parameters(): print(name)打印参数名,核对权重列表的顺序是否对应各层参数。 - 形状匹配:每个numpy权重数组的形状必须和对应模型参数的形状一致,比如
conv1的权重形状是(6,3,5,5),如果联邦学习中传递的权重形状错误,会直接报错,需检查权重的保存和传递逻辑。 - 禁用梯度计算:用
torch.no_grad()包裹权重赋值和测试过程,避免不必要的计算图构建,提升效率。
内容的提问来源于stack exchange,提问作者kyro121
相关产品推荐
相关产品推荐

