练习_第一个网络

代码及效果

import torch
import torch.nn.functional as F     # 激励函数都在这
import matplotlib.pyplot as plt

x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)  # x data (tensor), shape=(100, 1)
y = x.pow(2) + 0.2*torch.rand(x.size())                 # noisy y data (tensor), shape=(100, 1)

# 画图
# plt.scatter(x.data.numpy(), y.data.numpy())
# plt.show()


class Net(torch.nn.Module):  # 继承 torch 的 Module
    def __init__(self, n_feature, n_hidden, n_output):
        super().__init__()     # 继承 __init__ 功能。# 更简洁,Python 3 推荐写法
        # 定义每层用什么样的形式
        self.hidden = torch.nn.Linear(n_feature, n_hidden)   # 隐藏层线性输出
        self.predict = torch.nn.Linear(n_hidden, n_output)   # 输出层线性输出

    def forward(self, x):   # 这同时也是 Module 中的 forward 功能
        # 正向传播输入值, 神经网络分析出输出值
        x = F.relu(self.hidden(x))      # 激励函数(隐藏层的线性值)
        x = self.predict(x)             # 输出值
        return x

net = Net(n_feature=1, n_hidden=10, n_output=1)

plt.ion()   # 画图
plt.show()

# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)  # 传入 net 的所有参数, 学习率
loss_func = torch.nn.MSELoss()      # 预测值和真实值的误差计算公式 (均方差)

for t in range(100):
    prediction = net(x)     # 喂给 net 训练数据 x, 输出预测值

    loss = loss_func(prediction, y)     # 计算两者的误差

    optimizer.zero_grad()   # 清空上一步的残余更新参数值
    loss.backward()         # 误差反向传播, 计算参数更新值
    optimizer.step()        # 将参数更新值施加到 net 的 parameters 上
    # 接着上面来
    if t % 5 == 0:
        # plot and show learning process
        plt.cla()
        plt.scatter(x.data.numpy(), y.data.numpy())
        plt.plot(x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)
        plt.text(0.5, 0, 'Loss=%.4f' % loss.item(), fontdict={'size': 20, 'color': 'red'})
        plt.pause(0.1)
plt.ioff()  # 停止画图
plt.show()

微信截图_20250711134358

微信截图_20250711134412

1建立数据集

我们创建一些假数据来模拟真实的情况. 比如一个一元二次函数: y = a * x^2 + b, 我们给 y 数据加上一点噪声来更加真实的展示它.

import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt

x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)  # x data (tensor), shape=(100, 1)
y = x.pow(2) + 0.2*torch.rand(x.size())                 # noisy y data (tensor), shape=(100, 1)

# 画图
plt.scatter(x.data.numpy(), y.data.numpy())
plt.show()

1.1torch.linspace(-1, 1, 100)

[-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000]

生成从-1到1之间的100个等间隔数值,形成一个一维张量,形状是 (100,)。

1.2torch.unsqueeze(..., dim=1)

[[-1.0000],
 [-0.9798],
 [-0.9596],
 ...
 [ 0.9596],
 [ 0.9798],
 [ 1.0000]]

在第 1 维(列方向)增加一个维度,结果变成二维张量,形状为 (100, 1),这样更适合于神经网络输入。

1.3torch.rand(x.size()) 是什么

x 是一个 100 行、1 列的数据,x.size() 就是告诉别人:“我这个数据有 100 行、1 列”。
torch.rand(x.size()) 它是让 PyTorch 生成一个和 x 形状一样的随机数表,里面的数字是 0~1 之间的随机数。

2建立神经网络

这段代码定义了一个简单的两层神经网络,可以用来做回归任务(比如根据 x 预测 y),它的结构是:

输入层:1 个神经元
隐藏层:10 个神经元 + ReLU 激活函数
输出层:1 个神经元(没有激活函数)

class Net(torch.nn.Module):  # 继承 torch 的 Module
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()     # 继承 __init__ 功能
        # 定义每层用什么样的形式
        self.hidden = torch.nn.Linear(n_feature, n_hidden)   # 隐藏层线性输出
        self.predict = torch.nn.Linear(n_hidden, n_output)   # 输出层线性输出

    def forward(self, x):   # 这同时也是 Module 中的 forward 功能
        # 正向传播输入值, 神经网络分析出输出值
        x = F.relu(self.hidden(x))      # 激励函数(隐藏层的线性值)
        x = self.predict(x)             # 输出值
        return x

net = Net(n_feature=1, n_hidden=10, n_output=1)

2.1为什么必须写成__init__()

因为它是 Python 中的一个 魔法方法(Magic Method),它有特殊的含义和作用。

🔍 什么是魔法方法?
在 Python 中,以双下划线开头和结尾的方法(比如__init__、__str__等)是“魔法方法”,它们不是普通函数,而是被 Python 解释器特别对待的。

🔍__init__()是什么意思?
当你创建一个类的实例时,Python 会自动调用这个类的__init__() 方法。

2.2初始化

我这个类是从 torch.nn.Module 继承来的,请先运行它自带的初始化方法,然后再做我自己的初始化。

super(Net, self).__init__()#python 2
super().__init__()#python 3

❓ self 是什么?
self 是类的实例本身。
所有你想保存的数据、层、变量,都要通过 self.xxx = xxx 来保存。
后面你才能在其他方法中访问这些内容。

2.3torch.nn.Linear(n_hidden, n_output)

这是 PyTorch 中的一个全连接层(线性层)
in_features:输入特征的数量(输入维度)
out_features:输出特征的数量(输出维度)

2.4什么是 forward 函数

在 PyTorch 中:forward() 是每个神经网络类必须定义的方法,用于描述 输入数据是如何在网络中一层一层向前传递的。
你可以把它想象成:
“数据从入口进来,经过一系列变换,最后从出口出去”
这就是所谓的“前向传播”(Forward Propagation)。

2.4.1举个例子理解整个流程

输入 x = 2
↓
通过 hidden 层 → 得到线性输出(比如 [0.5, -0.3, 0.8, ..., 1.2])
↓
通过 ReLU → 负数变 0,变成 [0.5, 0, 0.8, ..., 1.2]
↓
通过 predict 层 → 得到最终输出(比如 1.7)

2.5forward()什么时候执行

forward() 是神经网络中的核心函数,它在你调用 net(x) 的时候自动执行,用于描述数据在网络中是如何一层一层向前传递的。
🔍 为什么可以这样写?net(x) 是怎么变成调用 forward() 的?
这是因为 PyTorch 的 torch.nn.Module 类已经帮你做了这件事:

所有继承自 torch.nn.Module 的类,都可以像函数一样被调用(比如 net(x))
它们的 forward() 方法会被自动调用

2.5.1前向传播流程图(简单版)

输入 x
   ↓
net(x)   ←←← 触发 forward()
   ↓
forward(x) 函数开始执行:
   ↓
经过 hidden 层 → ReLU 激活 → predict 输出层
   ↓
返回输出结果 y

3训练网络

# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)  # 传入 net 的所有参数, 学习率
loss_func = torch.nn.MSELoss()      # 预测值和真实值的误差计算公式 (均方差)

for t in range(100):
    prediction = net(x)     # 喂给 net 训练数据 x, 输出预测值

    loss = loss_func(prediction, y)     # 计算两者的误差

    optimizer.zero_grad()   # 清空上一步的残余更新参数值
    loss.backward()         # 误差反向传播, 计算参数更新值
    optimizer.step()        # 将参数更新值施加到 net 的 parameters 上

3.1训练流程图

输入数据 x ─→ net(x) ─→ 得到预测值 prediction
                              ↓
                      loss = MSE(prediction, y)
                              ↓
                     loss.backward() → 计算梯度
                              ↓
                   optimizer.step() → 更新参数

整个训练循环就是一个“喂数据 → 算误差 → 反向传播 → 更新参数”的过程,重复多次后,模型就能学会数据中的规律。

3.2x是100行1维,y也是100行1维吗?loss_func是计算每行对应的y吗?

执行过程详解

  1. prediction = net(x)
    把所有 100 个样本一次性喂给神经网络
    网络会为每个样本输出一个预测值
    所以 prediction 的形状也会是 (100, 1)
    你可以把它理解成
输入 x       ┌─────┐      输出 prediction
┌──────┐     │     │     ┌──────────┐
│x[0]  │ ─→ │ net │ ─→ │pred[0]   │
├──────┤     │     │     ├──────────┤
│x[1]  │ ─→ │     │ ─→ │pred[1]   │
├──────┤     │     │     ├──────────┤
│ ...  │     │     │     │ ...      │
├──────┤     │     │     ├──────────┤
│x[99] │ ─→ │     │ ─→ │pred[99]  │
└──────┘     └─────┘     └──────────┘
  1. loss = loss_func(prediction, y)
    这里使用的是 MSELoss(),即均方误差损失函数
    它会对每一个样本的预测值和真实值做差平方,然后取平均
    所以:

✅ loss_func 确实是对每一行的预测值和真实值分别计算误差,最后求平均。

3.3清空梯度(Zero Grad)

optimizer.zero_grad()  # 清空上一次的梯度,防止累加

每次反向传播后,PyTorch 会把梯度保存下来
如果不清空,下次计算的梯度就会叠加上去,导致错误

3.4反向传播(Backward Pass)

loss.backward()  # 反向传播,计算每个参数的梯度

根据当前的损失值,计算出每个参数对损失的影响(也就是梯度)
PyTorch 会自动帮你完成链式法则(Chain Rule)的求导过程
梯度会被保存在 .grad 属性里

3.5参数更新(Step)

optimizer.step()  # 使用优化器将梯度应用到参数上,完成一次更新

这一步才是真正地“训练”模型
根据计算出的梯度和学习率,更新网络中的权重和偏置
模型通过不断学习,逐渐变得更准确

posted @ 2025-07-11 13:45  13149942875  阅读(19)  评论(0)    收藏  举报