PyTorch框架使用

test123456 · 2026/6/3 09:17:55

PyTorch框架使用

PyTorch框架简介

什么是PyTorch

PyTorch是一个基于Python的科学计算包

PyTorch安装

pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple

PyTorch特点

PyTorch与TensorFlow的比较

  • PyTorch与TensorFlow的区别:PyTorch是基于动态图(动态计算图)的,而TensorFlow 1.x是基于静态计算图的(TensorFlow 2.x支持动态图)。这使得PyTorch在灵活性和调试方面优于TensorFlow,尤其是在研究和原型设计中。此外,PyTorch的API设计更加贴近Python,易于学习和使用。
  • TensorFlow 2.x(引入了Eager Execution)和PyTorch都支持动态图,但PyTorch因其更直观的编程模式和调试支持,在学术界和一些工业界应用中更为流行。

PyTorch发展历史

1733816358935

张量创建

什么是张量

张量是PyTorch中的核心数据抽象

1733817329667

==多个二维张量组成三维张量==

1733817373409

==多个三维张量组成四维张量==

==多个四维张量组成五维张量==

1733817419397

基本创建方式

  • 张量的数据类型有

    1662778155632

  • 张量中默认的数据类型是float32(torch.FloatTensor)

线性和随机张量

结果


tensor([0, 2, 4, 6, 8])
tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

结果

tensor([[ 0.7890, -0.8234, -0.2280],
        [-1.1156,  0.1307, -0.0884]])
随机数种子: 38292549120300
tensor([[ 0.3607, -0.2859, -0.3938],
        [ 0.2429, -1.3833, -2.3134]])
随机数种子: 100

指定值张量

结果

tensor([[10, 10, 10],
        [10, 10, 10]])
tensor([[20, 20, 20],
        [20, 20, 20]])

指定元素类型张量

张量类型转换

张量转换为NumPy数组

NumPy数组转换为张量

提取标量张量的数值

张量数值计算

基本运算

加减乘除取负号:

点乘运算

点乘(Hadamard)也称为元素级乘积,指的是相同形状的张量对应位置的元素相乘,使用mul和运算符 * 实现。

1733825192893

data1 = torch.tensor([[1, 2], [3, 4]])
data2 = torch.tensor([[5, 6], [7, 8]])
# 第一种方式
data = torch.mul(data1, data2)
print(data)
# 第二种方式
data = data1 * data2
print(data)

矩阵乘法运算

矩阵乘法运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。

运算

第一行

列	计算	结果
第 1 列	1×5 + 2×7	19​
第 2 列	1×6 + 2×8	22​
第 3 列	1×7 + 2×9	25​
第 4 列	1×8 + 2×10	28​

image-20260603161916821

张量运算函数

张量索引操作

我们在操作张量时,经常需要去获取某些元素就进行处理或者修改操作,在这里我们需要了解在torch中的索引操作。

import torch


# 随机生成数据
torch.manual_seed(10)
data = torch.randint(0, 10, [4, 5])
print(data)

# 1.简单行、列索引
print(data[0])
print(data[:, 0])

# 2.列表索引
# 返回 (0, 1)、(3, 2) 两个位置的元素,一维返回坐标
print(data[[0, 3], [1, 2]])
# 返回 0、1 行的 2、3 列共4个元素,二维返回块
print(data[[[0], [1]], [2, 3]])

# 3.范围索引
# 前3行的前2列数据
print(data[:3, :2])
# 第2行到最后的前2列数据
print(data[2:, :2])




结果
tensor([[7, 5, 2, 7, 2],
        [5, 7, 2, 1, 5],
        [6, 3, 1, 0, 6],
        [3, 4, 0, 6, 2]])
tensor([7, 5, 2, 7, 2])
tensor([7, 5, 6, 3])
tensor([5, 0])
tensor([[2, 7],
        [2, 1]])
tensor([[7, 5],
        [5, 7],
        [6, 3]])
tensor([[6, 3],
        [3, 4]])


# 4.布尔索引
# 第2列(索引从0开始)大于1的行数据,bool条件在行
print(data[data[:, 2] > 1])
# 第一行(索引0开始)大于4的列数据,bool条件在哪就输出哪
print(data[:, data[1] > 4])

# 5.多维索引
# 随机生成三维数据,3个4行5列矩阵
data = torch.randint(0, 10, [3, 4, 5])
print(data)
# 获取0轴上的第一个数据
print(data[0, :, :])
# 获取1轴上的第一个数据
print(data[:, 0, :])
# 获取2轴上的第一个数据
print(data[:, :, 0])

结果
tensor([[7, 5, 2, 7, 2],
        [5, 7, 2, 1, 5]])
tensor([[7, 5, 2],
        [5, 7, 5],
        [6, 3, 6],
        [3, 4, 2]])
tensor([[[8, 9, 2, 0, 9],
         [9, 4, 4, 9, 4],
         [4, 5, 4, 0, 8],
         [9, 3, 0, 9, 3]],

        [[7, 9, 5, 2, 9],
         [6, 6, 2, 8, 9],
         [6, 9, 1, 9, 2],
         [1, 2, 9, 7, 2]],

        [[9, 4, 4, 9, 4],
         [4, 8, 3, 1, 8],
         [2, 6, 1, 4, 9],
         [1, 8, 9, 7, 4]]])
tensor([[8, 9, 2, 0, 9],
        [9, 4, 4, 9, 4],
        [4, 5, 4, 0, 8],
        [9, 3, 0, 9, 3]])
tensor([[8, 9, 2, 0, 9],
        [7, 9, 5, 2, 9],
        [9, 4, 4, 9, 4]])
tensor([[8, 9, 4, 9],
        [7, 6, 6, 1],
        [9, 4, 2, 1]])

image-20260603165119859

第一行(索引0开始)大于4的列数据,bool条件在哪就输出哪

image-20260603165409129

想象data为

┌───────────────────────┐ │ Layer 0 │ │ ┌───────────────┐ │ │ │ 4 × 5 矩阵 │ │ │ └───────────────┘ │ │ │ │ Layer 1 │ │ ┌───────────────┐ │ │ │ 4 × 5 矩阵 │ │ │ └───────────────┘ │ │ │ │ Layer 2 │ │ ┌───────────────┐ │ │ │ 4 × 5 矩阵 │ │ │ └───────────────┘ │ └───────────────────────┘

写法 切的是
data[0] 抽一层
data[:, 0, :] 每层切一行
data[:, :, 0] 每层切一列

张量形状操作

张量形状操作是指对张量的维度进行变换的一 系列操作。

张量的形状则描述了每个维度上的元素数量。

reshape

保证张量数据不变的前提下改变数据的维度

import torch

data = torch.tensor([[10, 20, 30], [40, 50, 60]])
# 1. 使用 shape 属性或者 size 方法都可以获得张量的形状
print(data.shape, data.shape[0], data.shape[1])
print(data.size(), data.size(0), data.size(1))

# 2. 使用 reshape 函数修改张量形状
new_data = data.reshape(1, 6)
print(new_data.shape)

结果

torch.Size([2, 3]) 2 3
torch.Size([2, 3]) 2 3
torch.Size([1, 6])

squeeze和unsqueeze

squeeze:删除指定位置形状为1的维度,不指定位置删除所有形状为1的维度,==降维==

unsqueeze:在指定位置添加形状为1的维度,==升维==

mydata1 = torch.tensor([1, 2, 3, 4, 5])           # 形状5  
print('mydata1--->', mydata1.shape, mydata1) # 一个普通的数组 1维数据
mydata2 = mydata1.unsqueeze(dim=0)
print('在0维度上 拓展维度:', mydata2, mydata2.shape)  # 1*5
mydata3 = mydata1.unsqueeze(dim=1)
print('在1维度上 拓展维度:', mydata3, mydata3.shape)  # 5*1
mydata4 = mydata1.unsqueeze(dim=-1)
print('在-1维度上 拓展维度:', mydata4, mydata4.shape) # 5*1
mydata5 = mydata4.squeeze()
print('压缩维度:', mydata5, mydata5.shape)  # 1*5

结果

mydata1---> torch.Size([5]) tensor([1, 2, 3, 4, 5])
在0维度上 拓展维度: tensor([[1, 2, 3, 4, 5]]) torch.Size([1, 5])
在1维度上 拓展维度: tensor([[1],
        [2],
        [3],
        [4],
        [5]]) torch.Size([5, 1])
在-1维度上 拓展维度: tensor([[1],
        [2],
        [3],
        [4],
        [5]]) torch.Size([5, 1])
压缩维度: tensor([1, 2, 3, 4, 5]) torch.Size([5])

transpose和permute

transpose:实现交换张量形状的指定维度, 例如: 一个张量的形状为 (2, 3, 4) ,把 3 和 4 进行交换, 将张量的形状变为 (2, 4, 3)

permute:一次交换更多的维度

data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print('data shape:', data.size())
# 1. 交换1和2维度
mydata2 = torch.transpose(data, 1, 2)
print('mydata2.shape--->', mydata2.shape)
# 2. 将data 的形状修改为 (4, 5, 3), 需要变换多次
mydata3 =  torch.transpose(data, 0, 1)
mydata4 = torch.transpose(mydata3, 1, 2)
print('mydata4.shape--->', mydata4.shape)
# 3. 使用 permute 函数将形状修改为 (4, 5, 3)
# 3-1 方法1
mydata5 = torch.permute(data, [1, 2, 0])
print('mydata5.shape--->', mydata5.shape)
# 3-2 方法2
mydata6 = data.permute([1, 2, 0])
print('mydata6.shape--->', mydata6.shape)

结果

data shape: torch.Size([3, 4, 5])
mydata2.shape---> torch.Size([3, 5, 4])
mydata4.shape---> torch.Size([4, 5, 3])
mydata5.shape---> torch.Size([4, 5, 3])
mydata6.shape---> torch.Size([4, 5, 3])

view和contiguous

view函数也可以用于修改张量的形状,只能用于修改连续的张量。在PyTorch中,有些张量的底层数据在内存中的存储顺序与其在张量中的逻辑顺序不一致,view函数无法对这样的张量进行变形处理,例如: 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。

contiguous:将不连续张量转为连续张量

is_contiguous:判断张量是否连续,返回True或False

# 1 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作
# 若要使用view函数, 需要使用contiguous() 变成连续以后再使用view函数
# 2 判断张量是否连续
data = torch.tensor([[10, 20, 70],[40, 50, 60]])
print('data--->', data, data.shape)
# 1 判断张量是否连续
print(data.is_contiguous()) # True
# 2 view
mydata2 = data.view(3, 2)
print('mydata2--->', mydata2, mydata2.shape)
# 3 判断张量是否连续
print('mydata2.is_contiguous()--->', mydata2.is_contiguous())
# 4 使用 transpose 函数修改形状
mydata3 = torch.transpose(data, 0, 1)  
print('mydata3--->', mydata3, mydata3.shape)
print('mydata3.is_contiguous()--->', mydata3.is_contiguous())
# 5 需要先使用 contiguous 函数转换为连续的张量,再使用 view 函数
print (mydata3.contiguous().is_contiguous())
mydata4 = mydata3.contiguous().view(2, 3)
print('mydata4--->', mydata4.shape, mydata4)

结果

data---> tensor([[10, 20, 70],
        [40, 50, 60]]) torch.Size([2, 3])
True
mydata2---> tensor([[10, 20],
        [70, 40],
        [50, 60]]) torch.Size([3, 2])
mydata2.is_contiguous()---> True
mydata3---> tensor([[10, 40],
        [20, 50],
        [70, 60]]) torch.Size([3, 2])
mydata3.is_contiguous()---> False
True
mydata4---> torch.Size([2, 3]) tensor([[10, 40, 20],
        [50, 70, 60]])

张量拼接操作

张量拼接操作用于组合来自不同来源或经过不同处理的数据。

cat/concat

沿着现有维度连接一系列张量。所有输入张量除了指定的拼接维度外,其他维度必须匹配。

import torch


# 场景1: 标量 张量
data1 = torch.randint(0, 10, [1, 2, 3])
data2 = torch.randint(0, 10, [1, 2, 3])
print(data1)
print(data2)
# 1. 按0维度拼接,维度(2,2,3)
new_data = torch.cat([data1, data2], dim=0)
print(new_data)
print(new_data.shape)
print("-"*100)
# 2. 按1维度拼接
new_data = torch.cat([data1, data2], dim=1)
print(new_data)
print(new_data.shape)
print("-"*100)
# 3. 按2维度拼接
new_data = torch.cat([data1, data2], dim=2)
print(new_data)
print(new_data.shape)

结果

tensor([[[7, 6, 0],
         [2, 0, 1]]])
tensor([[[2, 5, 3],
         [1, 9, 5]]])
tensor([[[7, 6, 0],
         [2, 0, 1]],

        [[2, 5, 3],
         [1, 9, 5]]])
torch.Size([2, 2, 3])
----------------------------------------------------------------------------------------------------
tensor([[[7, 6, 0],
         [2, 0, 1],
         [2, 5, 3],
         [1, 9, 5]]])
torch.Size([1, 4, 3])
----------------------------------------------------------------------------------------------------
tensor([[[7, 6, 0, 2, 5, 3],
         [2, 0, 1, 1, 9, 5]]])
torch.Size([1, 2, 6])

stack

在一个新的维度上连接一系列张量,这会增加一个新维度,并且所有输入张量的形状必须完全相同。

import torch
data1 = torch.randint(0, 10, [2, 3])
data2 = torch.randint(0, 10, [2, 3])
print(data1)
print(data2)
# 1. 在0维度上拼接
new_data = torch.stack([data1, data2], dim=0)
print(new_data)
print(new_data.shape)
print('-'*100)
# 2. 在1维度上拼接
new_data = torch.stack([data1, data2], dim=1)
print(new_data)
print(new_data.shape)
print('-'*100)
# 3. 在2维度上拼接
new_data = torch.stack([data1, data2], dim=2)
print(new_data)
print(new_data.shape)

结果

tensor([[9, 9, 7],
        [4, 2, 3]])
tensor([[6, 9, 3],
        [2, 4, 2]])
tensor([[[9, 9, 7],
         [4, 2, 3]],

        [[6, 9, 3],
         [2, 4, 2]]])
torch.Size([2, 2, 3])
----------------------------------------------------------------------------------------------------
tensor([[[9, 9, 7],
         [6, 9, 3]],

        [[4, 2, 3],
         [2, 4, 2]]])
torch.Size([2, 2, 3])
----------------------------------------------------------------------------------------------------
tensor([[[9, 6],
         [9, 9],
         [7, 3]],

        [[4, 2],
         [2, 4],
         [3, 2]]])
torch.Size([2, 3, 2])

自动微分模块

自动微分就是自动计算梯度值,也就是计算导数。

训练神经网络时,最常用的算法就是反向传播。在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整。为了计算这些梯度,PyTorch内置了名为 torch.autograd 的微分模块。它支持任意计算图的自动梯度计算:

1733908229516

接下来我们使用这个结构进行自动微分模块的介绍,我们使用 backward 方法、grad 属性来实现梯度的计算和访问。

梯度基本计算

  • ==pytorch不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导==
    • x如果是张量,y必须是标量(一个值)才可以进行求导
  • 计算梯度: y.backward(), y是一个标量
  • 获取x点的梯度值: x.grad, 会累加上一次的梯度值

梯度下降法求最优解

梯度计算注意点

自动微分模块应用

import torch

# 输入张量 2*5
x = torch.ones(2, 5)
# 目标值是 2*3    
y = torch.zeros(2, 3)
# 设置要更新的权重和偏置的初始值
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
# 设置网络的输出值
z = torch.matmul(x, w) + b  # 矩阵乘法
# 设置损失函数,并进行损失的计算
loss = torch.nn.MSELoss()
loss = loss(z, y)
# 自动微分
loss.backward()
# 打印 w,b 变量的梯度
# backward 函数计算的梯度值会存储在张量的 grad 变量中
print("W的梯度:", w.grad)
print("b的梯度", b.grad)

PyTorch构建线性回归模型

我们使用 PyTorch 的各个组件来构建线性回归模型。在pytorch中进行模型构建的整个流程一般分为四个步骤:

1733908449747

要使用的API:

import torch
from torch.utils.data import TensorDataset  # 构造数据集对象
from torch.utils.data import DataLoader  # 数据加载器
from torch import nn  # nn模块中有平方损失函数和假设函数
from torch import optim  # optim模块中有优化器函数
from sklearn.datasets import make_regression  # 创建线性回归模型数据集
import matplotlib.pyplot as plt


plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号


# 构造数据集
def create_dataset():
    x, y, coef = make_regression(n_samples=100,
                                 n_features=1,
                                 noise=10,
                                 coef=True,
                                 bias=14.5,
                                 random_state=0)

    # 将构建数据转换为张量类型
    x = torch.tensor(x)
    y = torch.tensor(y)

    return x, y, coef


# 训练模型
def train():
    # 构造数据集
    x, y, coef = create_dataset()

    # 构造数据集对象
    dataset = TensorDataset(x, y)

    # 构造数据加载器
    # dataset=:数据集对象
    # batch_size=:批量训练样本数据
    # shuffle=:样本数据是否进行乱序
    dataloader = DataLoader(dataset=dataset, batch_size=16, shuffle=True)

    # 构造模型
    # in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size
    # out_features指的是输出的二维张量的大小,即输出的[batch_size,size]中的size
    model = nn.Linear(in_features=1, out_features=1)

    # 构造平方损失函数
    criterion = nn.MSELoss()

    # 构造优化函数
    # params=model.parameters():训练的参数,w和b
    # lr=1e-2:学习率, 1e-2为10的负二次方
    print("w和b-->", list(model.parameters()))
    print("w-->", model.weight)
    print("b-->", model.bias)
    optimizer = optim.SGD(params=model.parameters(), lr=1e-2)

    # 初始化训练次数
    epochs = 100
    # 损失的变化
    epoch_loss = [] 
    total_loss=0.0 
    train_sample=0.0
    for _ in range(epochs):
        for train_x, train_y in dataloader:
            # 将一个batch的训练数据送入模型
            y_pred = model(train_x.type(torch.float32))
            # 计算损失值,均方误差,当前批次所有样本的平均误差 
            loss = criterion(y_pred, train_y.reshape(-1, 1).type(torch.float32))
            total_loss += loss.item() 
            # loss是平均误差,所以样本数+1
            train_sample += 1
            # 梯度清零
            optimizer.zero_grad()
            # 自动微分(反向传播)
            loss.backward()
            # 更新参数
            optimizer.step()
        # 计算所有batch的平均误差作为当前epoch的误差 
        epoch_loss.append(total_loss/train_sample)
        
    # 打印回归模型的w
    print(model.weight)
    # 打印回归模型的b
    print(model.bias)
    
    # 绘制损失变化曲线 
    plt.plot(range(epochs), epoch_loss) 
    plt.title('损失变化曲线') 
    plt.grid() 
    plt.show()

    # 绘制拟合直线
    plt.scatter(x, y)
    x = torch.linspace(x.min(), x.max(), 1000)
    y1 = torch.tensor([v * model.weight + model.bias for v in x])
    y2 = torch.tensor([v * coef + 14.5 for v in x])
    plt.plot(x, y1, label='训练')
    plt.plot(x, y2, label='真实')
    plt.grid()
    plt.legend()
    plt.show()


if __name__ == '__main__':
    train()

1733909608918

1733909623818