PyTorch编程基础


1.Tensor(张量)

标量、向量、矩阵、张量

  • 标量:一个数(0 维)
  • 向量:一串数(1 维)
  • 矩阵:一张二维表(2 维)
  • 张量:所有维度数组的总称(0/1/2/3/4... 维)

1.1.Tensor初始化

1.1.1.从数据初始化

从python列表初始化tensor

从numpy ndarray初始化tensor

1.1.2.从其他tensor对象初始化

1.1.3.从常数或者随机初始化

1.2.Tensor的属性

1.2.1.张量的形状

1.2.2.张量的数据类型

1.2.3.张量所在的设备

设备只有cpu:

设备有gpu:

device='cuda:0'------0表示第一块gpu显卡

  • 只有一块 NVIDIA 显卡时,它的编号就是 0
  • 所以 device='cuda:0' 就是指定用这唯一的 GPU

1.2.4.梯度计算相关

1.3.将Tensor移动到设备中

1.4.张量的形状操作

1.5.张量的索引和切片

1.6.张量的合并

1.7.张量的运算

1.7.1.按元素运算

加法:

减法:

乘法:

除法:

1.7.2.汇总运算

1.7.3.广播机制

1.7.4.点积运算

1.7.5.张量乘法

2.计算图和自动微分

PyTorch有自动微分的功能。需要计算梯度的tensor对象需要将requires_grad设置为True。

PyTorch会自动生成一个计算图,计算图中的每个节点记录了对应的微分函数。计算图可以通过 torchviz这个三方包进行可视化。torchviz的安装过程如下:

上边代码生成的计算图如下:

基于计算图,pytorch可以自动计算梯度。计算图中自变量的梯度保存在.grad属性中,中间变量默认不保存梯度。

实操:

python 复制代码
# 安装 Python 用的 graphviz 库(接口)
pip install graphviz -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装 Mac 系统底层的画图工具(dot)
conda install -c conda-forge graphviz -y
python 复制代码
import torch 
from torchviz import make_dot

# 测试自动微分和计算图的代码

# 设置随机种子
torch.manual_seed(0)

# 参数w和b,打开了梯度追踪,独立同分布
# weight
w = torch.randn(2, 1, requires_grad=True) # randn 正态分布
# bias
b = torch.rand(1, requires_grad=True) # rand 均匀分布

# 生成数据样本
x = torch.rand(100, 2)
y = (x[:, 1] > x[:, 0]).int().reshape(-1, 1)

x_batch1 = x[:50]
y_batch1 = y[:50]
x_batch2 = x[50:]
y_batch2 = y[50:]

# 先处理batch1
# 前向传播
z = torch.matmul(x_batch1, w) + b
# print(z.requires_grad) # True
preds = torch.sigmoid(z)
# print(preds.requires_grad) # True

# 计算loss|交叉墒损失
loss = -y_batch1 * torch.log(preds+1e-10) - (1-y_batch1) * torch.log(1-preds+1e-10)

# 反向传播计算梯度
loss.mean().backward()

# 反向传播之后,梯度存在打开梯度追踪tensor的grad属性中
print(f"w.grad: {w.grad}")
# print(f"w: {w} \nw.data: {w.data} \nw.grad: {w.grad}")
# print(f"b: {b} \nb.data: {b.data} \nb.grad: {b.grad}")
# print(f"z: {z} \nz.data: {z.data} \nz.grad: {z.grad}") # z.grad 会算到,但不会保存下来,只会保存叶子结点(自变量)的梯度,为了节省内存

# 清空梯度,否则梯度会累加
w.grad.zero_()
b.grad.zero_()
print(f"w.grad: {w.grad}")

# 处理batch2
# 前向传播
z = torch.matmul(x_batch2, w) + b
preds = torch.sigmoid(z)

# 计算loss
loss = -y_batch2 * torch.log(preds+1e-10) - (1-y_batch2) * torch.log(1-preds +1e-10)

# 反向传播计算梯度
loss.mean().backward()

# 查看梯度
print(f"w.grad: {w.grad}")

# 计算图可视化
dot_graph = make_dot(loss.mean(), params={"weight": w, "bias": b}) # 生成结构化的数据
# print(dot_graph)
dot_graph.render("graph", format="png") # 将结构化的数据渲染成图

3.PyTorch训练过程

4.神经网络的搭建

4.1.神经网络的基类:torch.nn.Module()

torch.nn.Module() 是所有类型神经网络的基类。这里所说的"神经网络",可以是单层的网络结构, 或者是多层构成的"块",以及完整的一个神经网络模型。

PyTorch官方文档:https://docs.pytorch.org/docs/stable/generated/torch.nn.Module.htmlhttps://docs.pytorch.org/docs/stable/generated/torch.nn.Module.html

class torch.nn.Module(*args, **kwargs)

所有神经网络模块的基类。

你的模型也应当继承这个类。

模块内部还可以包含其他模块,使得它们可以嵌套成树形结构。你可以像普通属性一样赋值这些子模块:

python 复制代码
import torch.nn as nn
import torch.nn.functional as F

# 定义自己的模型,必须继承 nn.Module
class Model(nn.Module):

    # 第一步:构造函数 ------ 搭网络的"层"
    def __init__(self) -> None:
        super().__init__()  # 必须写!调用父类初始化
        
        # 定义网络层(卷积层)
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

    # 第二步:前向传播 ------ 定义数据怎么走
    def forward(self, x): 
        # 卷积 + 激活
        x = F.relu(self.conv1(x))  
        return F.relu(self.conv2(x))

以这种方式赋值的子模块会被自动注册,并且在你调用 .to() 等方法时,它们的参数也会被一并转换(例如转到 GPU)。

注意

正如上面的示例所示,在对子类进行属性赋值之前,必须先调用父类的 __init__() 方法。

变量

training (bool) -- 一个布尔值,表示当前模块处于训练模式还是评估模式。

python 复制代码
import torch
import torch.nn as nn


class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(3, 5)
        self.relu1 = nn.ReLU()
        self.fc2 = nn.Linear(5, 1)

    def forward(self, x): # 这里的x是【副本】,不是外面那个x
        # x 是一个tensor,不要在函数里该输入tensor(或者array、list、dict)的元素内容
        print("here is forward.")
        x = self.fc1(x) # 改的是内部x,这种是做对象的赋值,不是改元素内容
        x = self.relu1(x)
        x = self.fc2(x)
        return x # 计算结果返回给了 preds
    

if __name__ == '__main__':
    model = SimpleModel()

    x = torch.randn(2, 3)   # 创建了一个【原始输入x】
    print(x)                # 打印原始x

    preds = model(x)   # 会自动调用forward,把x传给模型 → forward执行了!
    print(x)           # 又打印了【最开始那个原始x】

    print(preds) # 这才是 forward 计算后的结果

forward 内部根本没有修改外面那个张量原本的内存,只新建了内存、改了局部变量。

💡工程规范级别的正确认知/编程顶级好习惯:函数的input如果是一个复杂类型,不要在函数里改变它的内容。

复杂类型(list、dict、tensor、numpy array)传参时,传的是「内存地址引用」,在函数里改它,外面的变量会一起变,这叫隐形副作用(side effect)。

  • x[0][0]:Python 列表写法,PyTorch 也兼容
  • x[0,0]:PyTorch / NumPy 简洁写法(推荐)
  • 功能 100% 相同,都原地改数据,都影响外部变量
  • 简单类型(int、str、float)传的是值,随便改。
  • 复杂类型(list、dict、array、tensor)传的是地址,千万别直接改。

4.1.1.主要的属性

a、training(bool):

模型处于训练评测 模式。对于有的神经网络结构(比如Dropout、BatchNorm这两层),在训练 和**推理(测试/评估)**阶段的行为不同,需要加以区分。

  • 训练 train:training=True → 更新参数

  • **推理 inference:**training=False → 模型跑前向,只输出结果(部署上线、实际使用)

  • **测试 / 评估 test/eval:**training=False → 推理 + 打分(算指标,看模型准不准)

training = True / False 决定了网络是 "训练模式" 还是 "测试模式"。


b、parameters():

模型参数的迭代器(一个模型所有的参数都会被放到parameters()这个迭代器里)。

python 复制代码
for param in model.parameters():
    print(param)

所有类型为Parameter (Tensor的子类,required_grad自动打开) 的成员变量都会被自动加入到 parameters()中。


源码:

c、named_parameters():

带名称的模型参数迭代器,参数的名称取决于Parameter类型的变量名。

python 复制代码
for name, param in model.named_parameters():
    if "weight" in name:
        print(name, param.size())

4.1.2.子类必须要实现的方法

a、init():

初始化函数,必须要先调用基类的初始化函数。

b、forward():

网络的正向传播逻辑 (由于自动微分功能,反向传播是自动进行的,无需关注)。


登录github授权使用自动预测生成代码功能。

4.1.3.其他常用的方法

a、train() :

将模型设置为训练模型。

b、eval() :

将模型设置为评估模式。

c、state_dict() :

返回模型的参数词典。

d、load_state_dict():

加载模型参数。

  • state_dict() = 纯参数 K-V 字典
  • 不包含任何网络拓扑结构
  • 加载必须依赖相同结构的模型类代码
python 复制代码
# 导入PyTorch核心库
import torch
# 导入PyTorch神经网络模块
import torch.nn as nn
# 导入有序字典(保证参数顺序不变)
from collections import OrderedDict


# 定义一个全连接层类,继承自nn.Module(所有PyTorch模型的基类)
class FCLayer(nn.Module):
    # 初始化函数:输入维度、输出维度、激活函数类型
    def __init__(self, input_dim, output_dim, activation="linear"):
        # 调用父类nn.Module的初始化方法
        super().__init__()
        
        # 创建一个有序的层容器,用于按顺序存放网络层
        self.layers = nn.Sequential()
        
        # 向容器中添加一个线性层(全连接层)
        # 层名:LinearLayer,输入维度input_dim,输出维度output_dim,开启偏置项
        self.layers.add_module(
            "LinearLayer", 
            nn.Linear(in_features=input_dim, out_features=output_dim, bias=True)
        )

        # 根据传入的激活函数名称,添加对应的激活层
        if activation == "relu":
            # 添加ReLU激活函数
            self.layers.add_module("Activation", nn.ReLU())
        elif activation == "sigmoid":
            # 添加Sigmoid激活函数
            self.layers.add_module("Activation", nn.Sigmoid())
        elif activation == "linear":
            # 线性激活=不做任何操作,直接跳过
            pass
        else:
            # 传入不支持的激活函数时,抛出异常
            raise ValueError("Unknown activation function")

    # 前向传播函数:定义数据在网络中的流动路径
    def forward(self, x):
        # 将输入x传入层容器,依次经过线性层+激活层,返回输出结果
        return self.layers(x)


# ==================== 主程序入口 ====================
if __name__ == "__main__":
    # 1. 创建第一个模型实例:输入10维,输出5维,激活函数用ReLU
    model = FCLayer(10, 5, activation="relu")

    # 2. 创建空的有序字典,用于存储模型最优参数
    best_model_state_dict = OrderedDict()

    # 3. 遍历model的所有参数(权重、偏置)
    for name in model.state_dict():
        # 对每个参数进行深拷贝,存入有序字典
        # .clone() 保证新参数独立,不与原模型共享内存
        best_model_state_dict[name] = model.state_dict()[name].clone()

    # 4. 创建第二个模型,结构必须和第一个完全一致
    model2 = FCLayer(10, 5, activation="relu")

    # 5. 将拷贝好的参数加载到第二个模型中
    # 注意:只加载参数,网络结构由FCLayer类提供
    model2.load_state_dict(best_model_state_dict)
  • state_dict() 只存参数 K-V 对,不存网络结构
  • 模型结构来自 class FCLayer,不是来自保存的文件
  • .clone() 深拷贝 = 两个模型参数完全独立,互不影响
  • load_state_dict 必须保证模型结构一模一样才能正常加载
e、to() :

将模型导入到对应的设备。

python 复制代码
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)

4.2.一些内置的网络结构

4.2.1.nn.Linear():

https://docs.pytorch.org/docs/stable/generated/torch.nn.Linear.htmlhttps://docs.pytorch.org/docs/stable/generated/torch.nn.Linear.html

4.2.2.非线性层:

a、nn.Sigmoid:

https://docs.pytorch.org/docs/stable/generated/torch.nn.Sigmoid.html#torch.nn.Sigmoidhttps://docs.pytorch.org/docs/stable/generated/torch.nn.Sigmoid.html#torch.nn.Sigmoid

b、nn.ReLU:

https://docs.pytorch.org/docs/stable/generated/torch.nn.ReLU.html#torch.nn.ReLUhttps://docs.pytorch.org/docs/stable/generated/torch.nn.ReLU.html#torch.nn.ReLU

c、nn.Softmax:

https://docs.pytorch.org/docs/stable/generated/torch.nn.Softmax.html#torch.nn.Softmaxhttps://docs.pytorch.org/docs/stable/generated/torch.nn.Softmax.html#torch.nn.Softmax

4.2.3.nn.Sequential():

https://docs.pytorch.org/docs/stable/generated/torch.nn.Sequential.htmlhttps://docs.pytorch.org/docs/stable/generated/torch.nn.Sequential.html

4.3.损失函数

多种损失函数在PyTorch中也被封装为nn.Module()的子类。

4.3.1.nn.MSELoss :

https://docs.pytorch.org/docs/stable/generated/torch.nn.MSELoss.html#torch.nn.MSELosshttps://docs.pytorch.org/docs/stable/generated/torch.nn.MSELoss.html#torch.nn.MSELoss均方误差损失,用于回归问题。

4.3.2.nn.BCELoss :

https://docs.pytorch.org/docs/stable/generated/torch.nn.BCELoss.html#torch.nn.BCELosshttps://docs.pytorch.org/docs/stable/generated/torch.nn.BCELoss.html#torch.nn.BCELoss二元交叉熵损失,用于二类分类问题。计算损失时preds需要经过 sigmoid 函数转换为概率。

4.3.3.nn.BCEWithLogitsLoss:

https://docs.pytorch.org/docs/stable/generated/torch.nn.BCEWithLogitsLoss.html#torch.nn.BCEWithLogitsLosshttps://docs.pytorch.org/docs/stable/generated/torch.nn.BCEWithLogitsLoss.html#torch.nn.BCEWithLogitsLoss二元交叉熵损失,用于二类分类问题。计算损失时输入的是logits (不经过 sigmoid )。

4.3.4.nn.CrossEntropyLoss :

https://docs.pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLosshttps://docs.pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLoss交叉熵损失,用于分类问题。计算损失时输入的是logits (不经过 softmax )。

4.4.基于PyTorch重新实现之前的MLP网络

相关推荐
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【22】Agent 并行工具执行与超时 / 协作式取消实战
java·人工智能·spring
阿里云大数据AI技术1 天前
让 AI 帮你写大数据AI开发代码:MaxFrame Coding Skill 正式发布
人工智能·agent
麦哲思科技任甲林1 天前
大懒人AI结对工作模式——驾驭AI编程的进阶指南
人工智能·ai编程·结对编程·工作模式·ai赋能
Raink老师1 天前
【AI面试临阵磨枪】什么是 MCP(Model Control Protocol)、A2A(Agent-to-Agent)协议?
人工智能·面试·职场和发展·ai 面试
明月照山海-1 天前
机器学习周报四十一
人工智能·机器学习
Daydream.V1 天前
LSTM项目实战——情感分析项目
人工智能·rnn·lstm
byte轻骑兵1 天前
从收音机到蓝牙:LE Audio核心BASS服务解析与实战
人工智能·音视频·语音识别·le audio·低功耗音频
jr-create(•̀⌄•́)1 天前
正则化和优化算法区别
pytorch·深度学习·神经网络·算法
饭后一颗花生米1 天前
2026 AI加持下前端学习路线:从入门到进阶,高效突破核心竞争力
前端·人工智能·学习
默 语1 天前
“我跑不过我的代码“:今天北京半马,程序员追机器人追到开电瓶车
人工智能·机器人·openclaw