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

为何我的PyTorch神经网络无法学习含噪声的XOR问题?

解决PyTorch中含噪声XOR问题模型无法学习的问题

你的代码存在几个关键问题,导致模型无法更新权重、无法学习,以下是具体问题和修复方案:

核心问题分析

  • 训练轮数过少:仅50轮训练不足以让多层网络学会XOR模式,这类简单非线性问题通常需要数千轮训练才能收敛。
  • Sigmoid激活的梯度消失:Sigmoid函数在输入绝对值较大时梯度趋近于0,初始的线性层输出很容易落在这个饱和区域,导致反向传播时梯度无法有效传递到前层权重,权重几乎不更新。
  • 学习率设置不合理:SGD优化器搭配0.01的学习率过小,再加上Sigmoid的梯度衰减,权重更新幅度微乎其微。
  • 权重初始化默认值的局限:PyTorch默认的Linear层初始化可能让Sigmoid一开始就进入饱和状态,进一步加剧梯度消失问题。

修复后的代码

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
import torch

# 生成含噪声的XOR数据
X, y = make_blobs(n_samples=200, n_features=2, cluster_std=.1,
                  centers= [(1,1), (1,0), (0,0),(0,1)])
y[y==2] = 0
y[y==3] = 1

x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=19)

# 转换为PyTorch张量,同时调整标签维度匹配模型输出
x_train = torch.FloatTensor(x_train)
x_test = torch.FloatTensor(x_test)
y_train = torch.FloatTensor(y_train).unsqueeze(1)
y_test = torch.FloatTensor(y_test).unsqueeze(1)

class XOR(torch.nn.Module): 
    def __init__(self): 
        super(XOR, self).__init__()
        self.layer1 = torch.nn.Linear(2, 4)  # 增加神经元数量提升拟合能力
        self.layer2 = torch.nn.Linear(4, 1)
        self.non_linear = torch.nn.ReLU()  # 替换Sigmoid为ReLU,避免梯度消失
        
        # 手动初始化权重,避免激活函数初始饱和
        torch.nn.init.xavier_uniform_(self.layer1.weight)
        torch.nn.init.xavier_uniform_(self.layer2.weight)

    def forward(self, x): 
        output = self.layer1(x)
        output = self.non_linear(output)
        output = self.layer2(output)
        output = torch.nn.Sigmoid()(output)  # 仅输出层用Sigmoid适配BCELoss
        return output

model = XOR()
criterion = torch.nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)  # 调大学习率
# 也可替换为Adam优化器:optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

model.train()
epoch = 5000  # 大幅增加训练轮数
for e in range(epoch):
    y_pred = model(x_train)
    loss = criterion(y_pred, y_train)
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    # 每500轮打印一次loss,避免输出冗余
    if (e + 1) % 500 == 0:
        print(f'Epoch {e+1}: train loss: {loss.item():.4f}')

model.eval()
with torch.no_grad():  # 评估阶段关闭梯度计算,节省资源
    # 训练集准确率计算
    y_pred = model(x_train)
    y_pred = (y_pred > 0.5).int()
    train_acc = torch.sum(y_pred == y_train.int()) / y_train.shape[0]
    print(f"train ACC: {train_acc.float():.4f}")

    # 测试集准确率计算
    y_pred = model(x_test)
    y_pred = (y_pred > 0.5).int()
    test_acc = torch.sum(y_pred == y_test.int()) / y_test.shape[0]
    print(f"test ACC: {test_acc.float():.4f}")

关键修改点说明

  1. 替换激活函数:用ReLU替换隐藏层的Sigmoid,避免梯度消失问题;仅在输出层保留Sigmoid,适配BCELoss的输入要求。
  2. 增加训练轮数:从50轮改为5000轮,给模型足够的学习时间。
  3. 调优学习率:将SGD的学习率从0.01提升到0.1,确保权重有足够的更新幅度;也可以直接使用Adam优化器,它的自适应学习率对新手更友好。
  4. 优化权重初始化:使用Xavier初始化方法,让线性层的输出更适合激活函数,避免初始状态就进入饱和区。
  5. 调整标签维度:将y_train和y_test转为二维张量,和模型输出的维度保持一致,避免计算loss时的维度不匹配问题。
  6. 评估阶段优化:使用torch.no_grad()关闭梯度计算,节省内存和计算资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:19:53