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

基于Evolution Strategies的MNIST神经网络始终输出相同准确率问题(PyTorch)

问题分析与修复:基于Evolution Strategies的MNIST分类模型

核心问题诊断

模型始终收敛到随机猜测准确率(~0.098)且训练缓慢,核心由以下关键错误导致:

1. 权重初始化完全失效

m.weight.data.normal_(0.0, 0.00)

标准差设为0会让所有线性层权重初始化为全0,模型从一开始就无法学习任何特征,输出完全一致,只能随机猜测类别。

2. 最后一层激活函数错误

模型最后一层使用ReLU:

nn.Linear(50, 10),
nn.ReLU(),

CrossEntropyLoss要求输入是未经过激活的logits,ReLU会截断负logits为0,破坏分类任务所需的输出分布,导致模型无法区分类别。

3. 输入数据未归一化

MNIST像素值范围是0-255,直接输入会导致线性层输出值过大,ReLU提前饱和,ES优化器无法捕捉有效更新信号。

4. 损失函数数值不稳定

用1/loss_func作为fitness,当loss趋近于0时会导致fitness暴增,引发数值波动。更合理的方式是用负损失作为fitness(最大化负损失等价于最小化损失)。

5. 训练效率低下

每次计算fitness都用全量训练集(49000样本),50个个体需要50次全量前向传播,是训练缓慢的核心原因。


修复后的完整代码

# imports
import torch
import torch.nn as nn
from tqdm.notebook import tqdm
import numpy as np
from sklearn.model_selection import train_test_split
from keras.datasets import mnist

# 加载并预处理数据:归一化+划分数据集
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x = np.concatenate((x_train, x_test)) / 255.0  # 归一化到0-1
y = np.concatenate((y_train, y_test))

train_size = 0.7
X_train, X_test, y_train, y_test = train_test_split(x, y, train_size=train_size)
X_train, X_test = torch.FloatTensor(X_train), torch.FloatTensor(X_test)
y_train, y_test = torch.LongTensor(y_train), torch.LongTensor(y_test)

# 展平图像
X_train = X_train.reshape(X_train.shape[0], -1)
X_test = X_test.reshape(X_test.shape[0], -1)

# 权重初始化:改用Xavier初始化,避免全0
def weights_init(m):
    classname = m.__class__.__name__
    if classname.find('Linear') != -1:
        nn.init.xavier_normal_(m.weight)
        nn.init.zeros_(m.bias)

# 模型定义:移除最后一层ReLU,输出logits
model = nn.Sequential(
        nn.Linear(784, 200),
        nn.ReLU(),
        nn.Linear(200, 50),
        nn.ReLU(),
        nn.Linear(50, 10),  # 最后一层无激活,输出logits
    )

model = model.float()
model.apply(weights_init)

# 初始化母参数向量
mother_vector = nn.utils.parameters_to_vector(model.parameters())

# 超参数调整:适配MNIST的规模
SIGMA = 0.05
LR = 0.05
POPULATION_SIZE = 100
ITERATIONS = 200
BATCH_SIZE = 1024  # 用小批量计算fitness提升效率

# 损失与fitness函数:用负损失替代倒数,数值更稳定
loss_func = nn.CrossEntropyLoss()
def fitness_func(solution):
    nn.utils.vector_to_parameters(solution, model.parameters())
    # 随机采样小批量计算fitness,减少计算量
    idx = torch.randint(0, len(X_train), (BATCH_SIZE,))
    batch_x, batch_y = X_train[idx], y_train[idx]
    y_pred = model(batch_x)
    return -loss_func(y_pred, batch_y)  # 最大化负损失 = 最小化损失

# 生成带噪声的参数
def jitter(mother_params, noise):
    return mother_params + SIGMA * noise

# 计算种群fitness
def calculate_population_fitness(pop, mother_vector):
    fitness = torch.zeros(pop.shape[0])
    for i, noise in enumerate(pop):
        p_try = jitter(mother_vector, noise)
        fitness[i] = fitness_func(p_try)
    return fitness

# 测试准确率
def test(mother_params):
    nn.utils.vector_to_parameters(mother_params, model.parameters())
    with torch.no_grad():
        y_pred = torch.max(model(X_test), 1)[1]
        acc = (y_pred == y_test).sum().item() / len(y_test)
    return acc

n_params = mother_vector.shape[0]
print(f"Number of params: {n_params}")

# 训练循环
with torch.no_grad():
    for iteration in tqdm(range(ITERATIONS)):
        # 生成噪声种群
        pop = torch.randn(POPULATION_SIZE, n_params, dtype=torch.float32)
        fitness = calculate_population_fitness(pop, mother_vector)
        
        # 归一化fitness,处理标准差为0的特殊情况
        fitness_mean = torch.mean(fitness)
        fitness_std = torch.std(fitness)
        if fitness_std == 0:
            normalized_fitness = torch.zeros_like(fitness)
        else:
            normalized_fitness = (fitness - fitness_mean) / fitness_std
        
        # 更新母参数向量
        update = (LR / (POPULATION_SIZE * SIGMA)) * torch.matmul(pop.t(), normalized_fitness)
        mother_vector += update
        
        # 记录指标
        current_fitness = fitness_func(mother_vector)
        acc = test(mother_vector)
        print(f"Iteration: {iteration}, Fitness:{current_fitness:.4f}, Test Accuracy: {acc:.4f}")

额外优化建议

  • GPU加速:将模型和数据移到GPU(model.to('cuda')、X_train = X_train.to('cuda')等),训练速度会大幅提升。
  • 动态超参数调整:训练后期逐步减小SIGMA,让参数更新更精细。
  • 并行计算:用PyTorch并行机制同时计算多个个体的fitness,进一步提升效率。

内容的提问来源于stack exchange,提问作者enfield

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 21:30:22