You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:44:59