【Pytorch】2025 Pytorch基础入门教程(完整详细版)

在这里插入图片描述
想搞定 PyTorch 核心操作?这份张量指南超实用!从创建、运算到梯度计算、模型保存全拆解,帮你快速夯实深度学习基础~📌点赞关注小编哦~

一、张量创建

1.1 基本创建方式

1)torch.tensor 根据指定数据创建张量 (最常用)
2)torch.Tensor 根据形状创建张量,其也可用来创建指定数据的张量
3)torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量

Tensor是PyTorch中最基础的数据结构,简单说就是多维矩阵,能借助GPU加速运算,是承载数据和运算的核心,其基本数据类型如下

数据类型CPU 张量(类型别名)GPU 张量(类型别名)备注(dtype 对应)
8位无符号整型(uint8)torch.ByteTensortorch.cuda.ByteTensor范围 0~255,对应 dtype=torch.uint8
8位有符号整型(int8)torch.CharTensortorch.cuda.CharTensor范围 -128~127,对应 dtype=torch.int8
16位有符号整型(int16)torch.ShortTensortorch.cuda.ShortTensor对应 dtype=torch.int16
32位有符号整型(int32)torch.IntTensortorch.cuda.IntTensor对应 dtype=torch.int32
64位有符号整型(int64)torch.LongTensortorch.cuda.LongTensor修正拼写错误,对应 dtype=torch.int64
16位浮点型(半精度,float16)torch.HalfTensortorch.cuda.HalfTensorGPU 加速常用,对应 dtype=torch.float16
32位浮点型(float32)torch.FloatTensortorch.cuda.FloatTensor对应 dtype=torch.float32(默认类型)
64位浮点型(float64)torch.DoubleTensortorch.cuda.DoubleTensor对应 dtype=torch.float64
布尔类型(bool)torch.BoolTensortorch.cuda.BoolTensor对应 dtype=torch.bool(Python 布尔映射)
import numpy as np
import torch

# 1. 根据已有数据创建张量
def test01():
    # 1. 创建张量标量
    data = torch.tensor(10)
    print(data)

    # 2. numpy 数组,由于 data 为 float64,下面代码也使用该类型
    data = np.random.randn(2, 3)
    data = torch.tensor(data)
    print(data)

    # 3. 列表,下面代码使用默认元素类型 float32
    data = [[10., 20., 30.], [40., 50., 60.]]
    data = torch.tensor(data)
    print(data)

    print('-' * 50)


# 2. 创建指定形状的张量
def test02():
    # 1. 创建2行3列的张量,默认dtype为float32
    data = torch.Tensor(2, 3)
    print(data)

    # 2. 注意:如果传递列表,则创建包含指定元素的张量
    data = torch.Tensor([10])
    print(data)

    data = torch.Tensor([10, 20])
    print(data)

    print('-' * 50)
    
# 3. 使用具体类型的张量
def test03():
    # 1. 创建2行3列,dtype为int32的张量
    data = torch.IntTensor(2, 3)
    print(data)

    # 2. 注意:如果传递的元素类型不正确,则会进行类型转换
    data = torch.IntTensor([-2.5, -3.3])
    print(data)

    # 3. 其他的类型
    # data = torch.ShortTensor([2, 3])  # int16
    # data = torch.LongTensor([2, 3])    # int64
    # data = torch.FloatTensor([2.5, 3.3])  # float32
    # print(data)

    print('-' * 50)

if __name__ == '__main__':
    test01()
    test02()
    test03()
# 运行结果
tensor(10)
tensor([[ 0.7219,  0.7726, -1.1605],
        [ 0.1611,  0.4240, -0.0588]], dtype=torch.float64)
tensor([[10., 20., 30.],
        [40., 50., 60.]])
--------------------------------------------------
tensor([[1.7608e+13, 1.2752e-42, 3.0000e+01],
        [4.0000e+01, 5.0000e+01, 6.0000e+01]])
tensor([10.])
tensor([10., 20.])
--------------------------------------------------
tensor([[1434459200,        910, 1056259574],
        [1075273419, 1062076309, 1059627882]], dtype=torch.int32)
tensor([-2, -3], dtype=torch.int32)

1.2 创建线性和随机张量

1)torch.arange 和 torch.linspace 创建线性张量
2)torch.random.init_seed 和 torch.random.manual_seed 随机种子设置

  • torch.random.initial_seed() 用于获取当前随机数生成器的初始种子值
    在没有手动设置随机种子的情况下,PyTorch 的随机数生成器会根据系统时钟等因素自动生成一个初始种子,调用这个函数就可以查看这个自动生成的种子值

  • torch.random.manual_seed(seed) 用于手动设置随机数生成器的种子
    当你设置了一个固定的种子值后,后续的随机数生成操作就会变得可复现,即每次运行代码,只要设置相同的随机种子,就会得到相同的随机数序列

3)torch.randn 创建随机张量

import torch

# 1. 创建线性空间的张量
def test01():
    # 1. 在指定区间按照步长生成元素 [start, end, step)
    data = torch.arange(0, 10, 2)
    print(data)

    # 2. 在指定区间按照元素个数生成
    data = torch.linspace(0, 11, 10)
    print(data)

# 2. 创建随机张量
def test02():
    # 1. 创建随机张量
    data = torch.randn(2, 3)  # 创建2行3列张量
    print(data)

    # 2. 随机数种子设置
    print('随机数种子:', torch.random.initial_seed())
    torch.random.manual_seed(100)
    print('随机数种子:', torch.random.initial_seed())

# 3. 随机种子的复现作用测试
def test03():
    # 设置随机种子
    torch.random.manual_seed(100)

    # 创建一个随机初始化的张量
    tensor1 = torch.randn(3, 3)
    print("Tensor 1:\n", tensor1)

    # 再次设置相同的随机种子
    torch.random.manual_seed(100)

    # 再次创建一个随机初始化的张量
    tensor2 = torch.randn(3, 3)
    print("Tensor 2:\n", tensor2)

if __name__ == '__main__':
    test01()
    #test02()
    #test03()

📌关于计算机中的随机数的原理?

  • 本质: 计算机中没有真随机数,全都是 伪随机数!!!
  • 伪随机数: 都是一个起始值,按照一系列确定的公式计算出来的值!!!
    • 为什么要给一个初始化种子(100)?就是给这个确定的起始值

1.3 创建 01张量

1)torch.ones 和 torch.ones_like 创建全1张量
2)torch.zeros 和 torch.zeros_like 创建全0张量
3)torch.full 和 torch.full_like 创建全为指定值张量

import torch

# 1. 创建全0张量
def test01():
    # 1. 创建指定形状全0张量
    data = torch.zeros(2, 3)
    print(data)

    # 2. 根据张量形状创建全0张量
    data = torch.zeros_like(data)
    print(data)
        
    print('-' * 50)
    
# 2. 创建全1张量
def test02():
    # 1. 创建指定形状全1张量
    data = torch.ones(2, 3)
    print(data)

    # 2. 根据张量形状创建全1张量
    data = torch.ones_like(data)
    print(data)
    
    print('-' * 50)

# 3. 创建全为指定值的张量
def test03():
    # 1. 创建指定形状指定值的张量
    data = torch.full([2, 3], 10)
    print(data)

    # 2. 根据张量形状创建指定值的张量
    data = torch.full_like(data, 20)
    print(data)


if __name__ == '__main__':
    test01()
    test02()
    test03()
# 运行结果
tensor([[0., 0., 0.],
        [0., 0., 0.]])
tensor([[0., 0., 0.],
        [0., 0., 0.]])
--------------------------------------------------
tensor([[1., 1., 1.],
        [1., 1., 1.]])
tensor([[1., 1., 1.],
        [1., 1., 1.]])
--------------------------------------------------
tensor([[10, 10, 10],
        [10, 10, 10]])
tensor([[20, 20, 20],
        [20, 20, 20]])

1.4 张量类型的转换

1)tensor.type(torch.DoubleTensor)
2)torch.double

import torch

def test():
    data = torch.full([2, 3], 10)
    print(data.dtype)

    # 将 data 元素类型转换为 float64 类型
    # 1. 第一种方法
    data = data.type(torch.DoubleTensor)
    print(data.dtype)

    # 转换为其他类型(示例,可按需取消注释使用)
    # data = data.type(torch.ShortTensor)
    # data = data.type(torch.IntTensor)
    # data = data.type(torch.LongTensor)
    # data = data.type(torch.FloatTensor)

    # 2. 第二种方法
    data = data.double()
    print(data.dtype)

    # 转换为其他类型(示例,可按需取消注释使用)
    data = data.short()
    print(data.dtype)
    data = data.int()
    print(data.dtype)
    data = data.long()
    print(data.dtype)
    data = data.float()
    print(data.dtype)

if __name__ == "__main__":
    test()
# 运行结果
torch.int64
torch.float64
torch.float64
torch.int16
torch.int32
torch.int64
torch.float32

📌关于模型参数的精度?

  • np.random.randn() — 默认精度为 float64
  • torch.randn() — PyTorch中的浮点值默认精度为 float32
  • 混合精度训练: float32float16bfloat16 混合训练(节省显存)
  • DeepSeek-V3: float8 + float16 + float32 混合训练

二、张量数值运算

2.1 张量基本运算

基本运算中,包括 add、sub、mul、div、neg 等函数, 以及这些函数的带下划线的版本 add_、sub_、mul_、div_、neg_, 其中带下划线的版本为修改原数据

import numpy as np
import torch

def test():
    data = torch.randint(0, 10, [2, 3])
    print(data)
    print('-' * 50)

    # 1. 不修改原数据
    new_data = data.add(10)  # 等价 new_data = data + 10
    print(new_data)
    print(data)
    print('-' * 50)

    # 2. 直接修改原数据
    # 注意: 带下划线的函数为修改原数据本身
    data.add_(10)  # 等价 data += 10
    print(data)

    # 3. 其他函数
    print(data.sub(100))  #对张量 data 中的每个元素都减去 100
    print(data.mul(100))
    print(data.div(100))
    print(data.neg())
    print(data.abs())

if __name__ == '__main__':
    test()
# 运行结果
tensor([[8, 7, 4],
        [6, 0, 1]])
--------------------------------------------------
tensor([[18, 17, 14],
        [16, 10, 11]])
tensor([[8, 7, 4],
        [6, 0, 1]])
--------------------------------------------------
tensor([[18, 17, 14],
        [16, 10, 11]])
tensor([[-82, -83, -86],
        [-84, -90, -89]])
tensor([[1800, 1700, 1400],
        [1600, 1000, 1100]])
tensor([[0.1800, 0.1700, 0.1400],
        [0.1600, 0.1000, 0.1100]])
tensor([[-18, -17, -14],
        [-16, -10, -11]])
tensor([[18, 17, 14],
        [16, 10, 11]])

2.2 阿达玛积

阿达玛积指的是矩阵对应位置的元素相乘

import numpy as np
import torch

def test():

    data1 = torch.tensor([[1, 2], [3, 4]])
    data2 = torch.tensor([[5, 6], [7, 8]])

    # 第一种方式
    data = torch.mul(data1, data2)
    print(data)
    print('-' * 50)

    # 第二种方式
    data = data1 * data2
    print(data)

if __name__ == '__main__':
    test()
# 运行结果
tensor([[ 5, 12],
        [21, 32]])
--------------------------------------------------
tensor([[ 5, 12],
        [21, 32]])

2.3 点积运算

点积运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)
运算符 @ 用于进行两个矩阵的点乘运算
torch.mm 用于进行两个矩阵点乘运算,要求输入的矩阵为 2 维
torch.bmm 用于批量进行矩阵点乘运算,要求输入的矩阵为 3 维

📌torch.matmul 对进行点乘运算的两矩阵形状没有限定.
a. 对于输入都是二维的张量相当于 mm 运算.
b. 对于输入都是三维的张量相当于 bmm 运算
c. 对数输入的 shape 不同的张量,对应的最后几个维度必须符合矩阵运算规则

import numpy as np
import torch

# 1. 点积运算
def test01():

    data1 = torch.tensor([[1, 2], [3, 4], [5, 6]])
    data2 = torch.tensor([[5, 6], [7, 8]])

    # 第一种方式
    data = data1 @ data2
    print(data)
    print('-' * 50)

    # 第二种方式
    data = torch.mm(data1, data2)
    print(data)
    print('-' * 50)

    # 第三种方式
    data = torch.matmul(data1, data2)
    print(data)
    print('-' * 50)

# 2. torch.mm 和 torch.matmul 的区别
def test02():

    # matmul 可以两个维度可以不同
    # 第一个张量: (3, 4, 5)
    # 第二个张量: (5, 4)
    # torch.mm 不可以相乘,而 matmul 则可以相乘

    print(torch.matmul(torch.randn(2, 3, 4, 5), torch.randn(1, 5, 4)).shape)
    # print(torch.matmul(torch.randn(5, 4), torch.randn(3, 4, 5)).shape)
    # print(torch.mm(torch.randn(3, 4, 5), torch.randn(5, 4)).shape)
    # print(torch.mm(torch.randn(5, 4), torch.randn(3, 4, 5)).shape)

# 3. torch.mm 函数的用法(注:实际用的是 torch.bmm,这里注释可能笔误)
def test03():
    # 批量点积运算
    # 第一个维度为 batch_size
    # 矩阵的二三维要满足矩阵乘法规则
    data1 = torch.randn(3, 4, 5)
    data2 = torch.randn(3, 5, 8)

    data = torch.bmm(data1, data2)
    print(data.shape)

if __name__ == '__main__':
    test01()  
    test02()  
    test03()
# 运行结果
tensor([[19, 22],
        [43, 50],
        [67, 78]])
--------------------------------------------------
tensor([[19, 22],
        [43, 50],
        [67, 78]])
--------------------------------------------------
tensor([[19, 22],
        [43, 50],
        [67, 78]])
--------------------------------------------------
torch.Size([2, 3, 4, 4])
torch.Size([3, 4, 8])

2.4 指定运行设备

PyTorch 默认会将张量创建在 CPU 控制的内存中, 即: 默认的运算设备为 CPU。我们也可以将张量创建在 GPU 上, 能够利用对于矩阵计算的优势加快模型训练。 将张量移动到 GPU 上有三种方法:

  1. 使用 cuda 方法 2. 直接在 GPU 上创建张量 3. 使用 to 方法指定设备
import torch

# 1. 使用 cuda 方法
def test01():
    data = torch.tensor([10, 20, 30])
    print('存储设备:', data.device)

    # 如果安装的不是 gpu 版本的 PyTorch
    # 或电脑本身没有 NVIDIA 卡的计算环境
    # 下面代码可能会报错
    data = data.cuda()
    print('存储设备:', data.device)

    # 使用 cpu 函数将张量移动到 cpu 上
    data = data.cpu()
    print('存储设备:', data.device)

    # 输出结果:
    # 存储设备: cpu
    # 存储设备: cuda:0
    # 存储设备: cpu


# 2. 直接将张量创建在 GPU 上
def test02():
    data = torch.tensor([10, 20, 30], device='cuda:0')
    print('存储设备:', data.device)

    # 使用 cpu 函数将张量移动到 cpu 上
    data = data.cpu()
    print('存储设备:', data.device)

    # 输出结果:
    # 存储设备: cuda:0
    # 存储设备: cpu


# 3. 使用 to 方法
def test03():
    data = torch.tensor([10, 20, 30])
    print('存储设备:', data.device)

    data = data.to('cuda:0')
    print('存储设备:', data.device)

    # 输出结果:
    # 存储设备: cpu
    # 存储设备: cuda:0


# 4. 存储在不同设备的张量不能运算
def test04():
    data1 = torch.tensor([10, 20, 30], device='cuda:0')
    data2 = torch.tensor([10, 20, 30])
    print(data1.device, data2.device)

    # RuntimeError: Expected all tensors to be on the same device,
    # but found at least two devices, cuda:0 and cpu!
    data = data1 + data2
    print(data)


if __name__ == '__main__':
    test01()
    test02()
    test03()
    test04()

三、张量类型转换

3.1 张量转换为 numpy 数组

使用 Tensor.numpy 函数可以将张量转换为 ndarray 数组,但是共享内存,可以使用 copy 函数避免共享

# 1. 将张量转换为 numpy 数组
def test01():

    data_tensor = torch.tensor([2, 3, 4])
    # 使用张量对象中的 numpy 函数进行转换
    data_numpy = data_tensor.numpy()

    print(type(data_tensor))
    print(type(data_numpy))

    # 注意: data_tensor 和 data_numpy 共享内存
    # 修改其中的一个, 另外一个也会发生改变
    # data_tensor[0] = 100
    data_numpy[0] = 100

    print(data_tensor)
    print(data_numpy)

# 2. 使用 torch.tensor 函数
def test02():
    data_numpy = np.array([2, 3, 4])

    data_tensor = torch.tensor(data_numpy)

    # numpy 和 tensor 不共享内存
    # data_numpy[0] = 100
    data_tensor[0] = 100

    print(data_tensor)
    print(data_numpy)

# 3. 标量张量和数字的转换
def test03():
    # 当张量只包含一个元素时,可以通过 item 函数提取出该值
    data = torch.tensor([30, ])
    print(data.item())

    data = torch.tensor(30)
    print(data.item())

    # data1 = torch.tensor([1.55, 2.55])
    data1 = torch.tensor([1.55])
    print(data1.item())


if __name__ == '__main__':
    test01()
    test02()
    test03()
<class 'torch.Tensor'>
<class 'numpy.ndarray'>
tensor([100,   3,   4])
[100   3   4]
tensor([100,   3,   4], dtype=torch.int32)
[2 3 4]
30
30
1.5499999523162842

📌Python代码中如何比较两个数的大小?

方法1: 直接比较(适用于整数/精确值)

if x == y:  
    print('hello world')  
else:  
    print('bad!')  

方法2: 浮点数差值比较(避免精度误差)

if (x - y) < 1e-6:  # 设定极小区间(如1e-6)  
    print('hello world')  
else:  
    print('bad!')  

四、张量拼接操作

张量的拼接操作在神经网络搭建过程中是非常常用的方法,例如:在后面将要学习到的残差网络、注意力机制中都使用到了张量拼接

4.1 torch.cat 函数的使用

import torch

def test01():
    data1 = torch.randint(0, 10, [3, 5, 4])
    data2 = torch.randint(0, 10, [3, 5, 4])

    print(data1)
    print(data2)
    print('-' * 50)

    # 1. 按0维度拼接
    new_data = torch.cat([data1, data2], dim=0)
    print(new_data.shape)
    print(new_data)
    print('-' * 50)

    # 2. 按1维度拼接
    new_data = torch.cat([data1, data2], dim=1)
    print(new_data.shape)
    print(new_data)
    print('-' * 50)

    # 3. 按2维度拼接
    new_data = torch.cat([data1, data2], dim=2)
    print(new_data.shape)
    print(new_data)
    print('-' * 50)

    '''
    new_data = torch.cat([data1, data2], dim=-1)
    print(new_data.shape)

    new_data = torch.cat([data1, data2], dim=-3)
    print(new_data.shape)
    '''
    
def test02():
    data1 = torch.randint(0, 10, [2, 3])
    data2 = torch.randint(0, 10, [2, 3])
    # data2= torch.randint(0, 10, [2, 3, 4])
    print(data1)
    print(data2)

    new_data1 = torch.cat([data1, data2], dim=0)

    print('-----------------------------')
    
    new_data2 = torch.stack([data1, data2], dim=0)
    print(new_data1.shape)
    print(new_data2.shape)
    print(new_data1)
    print(new_data2)
    #print('-' * 50)

    # x = torch.Tensor([1])
    # y = torch.Tensor([2])
    # z = torch.stack([x, y],dim=0)
    # print(z)

    '''
    new_data = torch.stack([data1, data2], dim=1)
    print(new_data.shape)
    print(new_data)
    print('---------------------')

    new_data = torch.stack([data1, data2], dim=2)
    print(new_data.shape)
    print(new_data)
    '''
    
if __name__ == '__main__':
    test01()  
    test02()
tensor([[[4, 9, 8, 2],
         [2, 7, 5, 0],
         [5, 0, 5, 0],
         [1, 7, 2, 4],
         [5, 6, 1, 3]],

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

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

        [[3, 7, 9, 0],
         [4, 2, 1, 5],
         [0, 4, 6, 1],
         [3, 3, 9, 4],
         [1, 1, 3, 0]],

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

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

        [[8, 0, 3, 6],
         [4, 6, 1, 6],
         [6, 3, 6, 6],
         [3, 1, 2, 6],
         [5, 5, 8, 1]],

        [[5, 9, 2, 6],
         [5, 3, 4, 5],
         [4, 3, 2, 6],
         [2, 9, 5, 0],
         [7, 9, 5, 0]],

        [[3, 7, 9, 0],
         [4, 2, 1, 5],
         [0, 4, 6, 1],
         [3, 3, 9, 4],
         [1, 1, 3, 0]],

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

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

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

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

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

📌torch.cat() 和 torch.stack() 的区别?

  • torch.cat():连接两个张量,指定维度(如dim=0),结果维度不变!!!
  • torch.stack():拼接两个张量,指定维度(如dim=0),结果维度+1(在指定dim上新增一个维度!!!)

五、张量索引操作

我们在操作张量时,经常需要去进行获取或者修改操作,掌握张量的花式索引操作是必须的一项能力

import torch

# 1. 简单行、列索引
def test01(data):
    print("简单行、列索引:")
    print("第0行数据:", data[0])          # 获取第0行数据
    print("所有行的第0列数据:", data[:, 0])  # 获取所有行的第0列数据
    print('-' * 50)


# 2. 列表索引
def test02(data):
    print("列表索引:")
    # 返回 (0, 1)、(1, 2) 两个位置的元素
    print("(0,1)和(1,2)位置元素:", data[[0, 1], [1, 2]])
    # 返回 0、1 行的 1、2 列共4个元素(二维索引)
    print("0-1行的1-2列元素:\n", data[[[0], [1]], [1, 2]])
    print('-' * 50)


# 3. 范围索引
def test03(data):
    print("范围索引:")
    # 前3行的前2列数据(行:0-2,列:0-1)
    print("前3行前2列:\n", data[:3, :2])
    # 第2行到最后一行的前2列数据(行:2-末尾,列:0-1)
    print("第2行及以后的前2列:\n", data[2:, :2])
    print('-' * 50)


# 4. 布尔索引
def test04(data):
    print("布尔索引:")
    # 第2列大于5的行数据
    print("第2列>5的行:\n", data[data[:, 2] > 5])
    # 第1行大于5的列数据
    print("第1行>5的列:", data[:, data[1] > 5])
    print('-' * 50)


# 5. 多维索引(三维张量)
def test05():
    print("多维索引(三维张量):")
    # 创建3×4×5的三维随机整数张量(0-9)
    data = torch.randint(0, 10, [3, 4, 5])
    print("原始三维张量:\n", data)
    print("=" * 50)
    
    print("第0层的4×5矩阵:\n", data[0, :, :])  # 第0个维度全取
    print("每一层的第0行:\n", data[:, 0, :])    # 第1个维度取第0行
    print("每一层每一行的第0列:\n", data[:, :, 0])  # 第2个维度取第0列
    print("张量形状:", data.shape)
    print('-' * 50)


if __name__ == '__main__':
    # 定义一个5行6列的二维张量(0-9随机整数),供test01-test04使用
    data_2d = torch.randint(0, 10, [5, 6])
    print("基础二维张量:\n", data_2d)
    print("=" * 50)
    
    # 依次调用索引测试函数(传入二维张量)
    test01(data_2d)
    test02(data_2d)
    test03(data_2d)
    test04(data_2d)
    
    # 测试三维张量索引(内部独立定义数据)
    test05()

# 运行结果
基础二维张量:
 tensor([[1, 1, 7, 9, 6, 3],
        [4, 7, 2, 1, 3, 4],
        [6, 6, 0, 9, 9, 6],
        [0, 2, 9, 6, 3, 1],
        [7, 4, 2, 6, 7, 4]])
==================================================
简单行、列索引:
第0行数据: tensor([1, 1, 7, 9, 6, 3])
所有行的第0列数据: tensor([1, 4, 6, 0, 7])
--------------------------------------------------
列表索引:
(0,1)(1,2)位置元素: tensor([1, 2])
0-1行的1-2列元素:
 tensor([[1, 7],
        [7, 2]])
--------------------------------------------------
范围索引:
前3行前2列:
 tensor([[1, 1],
        [4, 7],
        [6, 6]])2行及以后的前2列:
 tensor([[6, 6],
        [0, 2],
        [7, 4]])
--------------------------------------------------
布尔索引:
第2>5的行:
 tensor([[1, 1, 7, 9, 6, 3],
        [0, 2, 9, 6, 3, 1]])1>5的列: tensor([[1],
        [7],
        [6],
        [2],
        [4]])
--------------------------------------------------
多维索引(三维张量):
原始三维张量:
 tensor([[[4, 1, 8, 9, 7],
         [5, 4, 3, 3, 4],
         [2, 0, 8, 6, 3],
         [8, 7, 2, 7, 4]],

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

        [[6, 6, 3, 3, 1],
         [7, 6, 7, 1, 6],
         [3, 4, 2, 5, 2],
         [3, 5, 3, 0, 1]]])
==================================================0层的4×5矩阵:
 tensor([[4, 1, 8, 9, 7],
        [5, 4, 3, 3, 4],
        [2, 0, 8, 6, 3],
        [8, 7, 2, 7, 4]])
每一层的第0行:
 tensor([[4, 1, 8, 9, 7],
        [8, 9, 1, 9, 7],
        [6, 6, 3, 3, 1]])
每一层每一行的第0列:
 tensor([[4, 5, 2, 8],
        [8, 9, 2, 7],
        [6, 7, 3, 3]])
张量形状: torch.Size([3, 4, 5])

张量操作是 PyTorch 的基石,吃透这些能为深度学习项目铺路。趁热打铁,用这些知识开启你的 AI 开发实战吧!

六、张量形状操作

  • 学习目标

  • 掌握reshape, transpose, permute, view, contiguous, squeeze, unsqueeze等函数使用

  • 在我们后面搭建网络模型时,数据都是基于张量形式的表示,网络层与层之间很多都是以不同的 shape 的方式进行表现和运算,我们需要掌握对张量形状的操作,以便能够更好处理网络各层之间的数据连接

6.1 reshape 函数的用法

  • reshape 函数可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状,在后面的神经网络学习时,会经常使用该函数来调节数据的形状,以适配不同网络层之间的数据传递。
import torch
import numpy as np

def test():
    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)

if __name__ == '__main__':
    test()
# 运行结果
torch.Size([2, 3]) 2 3
torch.Size([2, 3]) 2 3
torch.Size([1, 6])

6.2 transpose 和 permute 函数的使用

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

  • 📌permute 函数可以一次交换更多的维度

import torch
import numpy as np

def test():
    data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
    print('data shape:', data.shape)
    
    # 1、交换1和2维度
    new_data = torch.transpose(data, 1, 2)
    print('new_data shape:', new_data.shape)

    data[0,0,0] = 1000
    print("data:",data)
    print("new_data:", new_data)
    print("new_data shape:", new_data.size())
    print("="*50)
    
    # 2、将 data 的形状修改为 (4, 5, 3)
    new_data = torch.transpose(data, 0, 1)
    print('new_data shape:', new_data.shape)
    new_data = torch.transpose(new_data, 1, 2)
    print('new_data shape:', new_data.shape)
    print("="*50)
    
    # 3、使用 permute 函数将形状修改为 (4, 5, 3)
    new_data = torch.permute(data, [1, 2, 0])
    print('new_data shape:', new_data.shape)

if __name__ == '__main__':
    test()
# 运行结果
data shape: torch.Size([3, 4, 5])
new_data shape: torch.Size([3, 5, 4])
data: tensor([[[1000,    4,    6,    0,    7],
         [   2,    4,    9,    6,    3],
         [   4,    8,    8,    9,    3],
         [   6,    6,    4,    6,    1]],

        [[   1,    4,    2,    0,    5],
         [   0,    0,    0,    7,    9],
         [   5,    0,    2,    2,    4],
         [   1,    2,    5,    6,    0]],

        [[   1,    2,    9,    1,    4],
         [   3,    2,    0,    7,    6],
         [   9,    5,    4,    2,    7],
         [   7,    9,    1,    1,    1]]], dtype=torch.int32)
new_data: tensor([[[1000,    2,    4,    6],
         [   4,    4,    8,    6],
         [   6,    9,    8,    4],
         [   0,    6,    9,    6],
         [   7,    3,    3,    1]],

        [[   1,    0,    5,    1],
         [   4,    0,    0,    2],
         [   2,    0,    2,    5],
         [   0,    7,    2,    6],
         [   5,    9,    4,    0]],

        [[   1,    3,    9,    7],
         [   2,    2,    5,    9],
         [   9,    0,    4,    1],
         [   1,    7,    2,    1],
         [   4,    6,    7,    1]]], dtype=torch.int32)
new_data shape: torch.Size([3, 5, 4])
==================================================
new_data shape: torch.Size([4, 3, 5])
new_data shape: torch.Size([4, 5, 3])
==================================================
new_data shape: torch.Size([4, 5, 3])

6.3 view 和 contigous 函数的用法

  • 📌 view 函数也可以用于修改张量的形状,但是其用法比较局限,只能用于存储在整块内存中的张量。在 PyTorch 中,有些张量是由不同的数块组成的,它们并没有存储在整块的内存中,view 函数无法对这样的张量进行变形处理。例如一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作
import torch
import numpy as np

def test():
    data = torch.tensor([[10, 20, 30], [40, 50, 60]])
    print('data shape:', data.shape)
    
    # 1、使用 view 函数修改形状
    new_data = data.view(3, 2)
    print('new_data shape:', new_data.shape)
    
    # 2、判断张量是否使用整块内存
    print('data:', data.is_contiguous())  # True
    
    # 3、使用 transpose 函数修改形状
    new_data = torch.transpose(data, 0, 1)
    print('new_data:', new_data.is_contiguous())  # False
    print("="*50)
    # new_data = new_data.view(2, 3)  # RuntimeError
    new_data = new_data.reshape(2,3)
    print('new_data.shape:',new_data.shape)
    print(new_data.is_contiguous())
    print("="*50)
    
    # 需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数
    print('new_data.contiguous():', new_data.contiguous().is_contiguous())
    new_data = new_data.contiguous().view(2, 3)
    print('new_data shape:', new_data.shape)

if __name__ == '__main__':
    test()
# 运行结果
data shape: torch.Size([2, 3])
new_data shape: torch.Size([3, 2])
data: True
new_data: False
==================================================
new_data.shape: torch.Size([2, 3])
True
==================================================
new_data.contiguous(): True
new_data shape: torch.Size([2, 3])

6.4 squeeze 和 unsqueze 函数的用法

  • squeeze 函数用于删除 shape 为 1 的维度,unsqueeze 在指定维度添加 1,以增加数据的形状。
import torch
import numpy as np

def test():
    data = torch.tensor(np.random.randint(0, 10, [1, 3, 1, 5]))
    print('data shape:', data.size())
    
    # 1、去掉值为1的维度
    new_data = data.squeeze()
    print('new_data shape:', new_data.size())  # torch.Size([3, 5])
    
    # 2、去掉指定位置为1的维度,注意:如果指定位置不是1则不删除
    new_data = data.squeeze(2)
    print('new_data shape:', new_data.size())  # torch.Size([3, 5])
    
    # 3、在2维度增加一个维度(原代码中维度索引为 -1 ,即最后一维)
    new_data = data.unsqueeze(-1)
    print('new_data shape:', new_data.size())  # torch.Size([1, 3, 1, 5, 1])

if __name__ == '__main__':
    test()
# 运行结果
data shape: torch.Size([1, 3, 1, 5])
new_data shape: torch.Size([3, 5])
new_data shape: torch.Size([1, 3, 5])
new_data shape: torch.Size([1, 3, 1, 5, 1])

七、 张量运算函数

  • 学习目标
  • 掌握张量相关运算函数

7.1 常见运算函数

  • PyTorch 为每个张量封装很多实用的计算函数,例如计算均值、平方根、求和等等
import torch

def test():
    data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
    print(data)
    print('-' * 50)
    
    # 1. 计算均值  
    print(data.mean())  
    print(data.mean(dim=0))  # 📌 按列计算均值  
    print(data.mean(dim=1))  # 📌 按行计算均值  
    print('-' * 50)  
    
    # 2. 计算总和  
    print(data.sum())  
    print(data.sum(dim=0))  
    print(data.sum(dim=1))  
    print('-' * 50)  
    
    # 3. 计算平方  
    print(data.pow(2))  
    print('-' * 50)  
    
    # 4. 计算平方根  
    print(data.sqrt())  
    print('-' * 50)  
    
    # 5. 指数计算,e^n 次方  
    print(data.exp())  
    print('-' * 50)  
    
    # 6. 对数计算  
    print(data.log())    # 以 e 为底  
    print(data.log10())  
    print(data.log2())  

if __name__ == '__main__':  
    test()  
# 运行结果
tensor([[2., 7., 1.],
        [0., 1., 3.]], dtype=torch.float64)
--------------------------------------------------
tensor(2.3333, dtype=torch.float64)
tensor([1., 4., 2.], dtype=torch.float64)
tensor([3.3333, 1.3333], dtype=torch.float64)
--------------------------------------------------
tensor(14., dtype=torch.float64)
tensor([2., 8., 4.], dtype=torch.float64)
tensor([10.,  4.], dtype=torch.float64)
--------------------------------------------------
tensor([[ 4., 49.,  1.],
        [ 0.,  1.,  9.]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.4142, 2.6458, 1.0000],
        [0.0000, 1.0000, 1.7321]], dtype=torch.float64)
--------------------------------------------------
tensor([[7.3891e+00, 1.0966e+03, 2.7183e+00],
        [1.0000e+00, 2.7183e+00, 2.0086e+01]], dtype=torch.float64)
--------------------------------------------------
tensor([[0.6931, 1.9459, 0.0000],
        [  -inf, 0.0000, 1.0986]], dtype=torch.float64)
tensor([[0.3010, 0.8451, 0.0000],
        [  -inf, 0.0000, 0.4771]], dtype=torch.float64)
tensor([[1.0000, 2.8074, 0.0000],
        [  -inf, 0.0000, 1.5850]], dtype=torch.float64)

八、 自动微分模块

  • 学习目标

  • 掌握梯度计算

  • 自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中,Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新

8.1 梯度基本计算

我们使用 backward 方法、grad 属性来实现梯度的计算和访问。

import torch  


# 1. 单标量梯度的计算  
# y = x**2 + 20  
def test01():  
    # 定义需要求导的张量  
    # 张量的类型必须是浮点类型  
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)  
    # 变量经过中间计算  
    f = x ** 2 + 20  
    
    # 自动微分  
    f.backward()  
    
    # 打印 x 变量的梯度  
    # backward 函数计算的梯度值会存储在张量的 grad 属性中  
    print(x.grad)  


# 2. 单向量梯度的计算  
# y = x**2 + 20  
def test02():  
    # 定义需要求导张量  
    x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)  
    f1 = x ** 2 + 20  
    
    # 注意:  
    # 由于求导的结果必须是标量  
    # 而 f1 的结果是: tensor([120., 420., ...])  
    # 所以,不能直接自动微分  
    # 需要将结果计算为标量才能进行计算  
    f2 = f1.mean()  # f2 = 1/2 * x  
    
    # 自动微分  
    f2.backward()  
    
    # 打印 x 变量的梯度  
    print(x.grad)  


if __name__ == '__main__':  
    test01()  
    test02()  # 若需测试可取消注释  
# 运行结果
tensor(20., dtype=torch.float64)
tensor([5., 10., 15., 20.], dtype=torch.float64)

8.2 控制梯度计算

  • 我们可以通过一些方法使得在 requires_grad=True 的张量在某些时候计算不进行梯度计算。
##### 1. 控制不计算梯度  

def test01():  
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)  
    print(x.requires_grad)  
    
    # 第一种方式:对代码进行装饰  
    with torch.no_grad():  
        y = x ** 2  
        print(y.requires_grad)  
    
    # 第二种方式:对函数进行装饰  
    @torch.no_grad()  
    def my_func(x):  
        return x ** 2  
    print(my_func(x).requires_grad)  
    
    # 第三种方式  
    torch.set_grad_enabled(False)  
    y = x ** 2  
    print(y.requires_grad)  
    

# 2. 注意:累计梯度
def test02():  
    # 定义需要求导张量  
    x = torch.tensor([10., 20., 30., 40.], requires_grad=True, dtype=torch.float64)  
    for _ in range(3):  
        f1 = x ** 2 + 20  
        f2 = f1.mean()  
        print(f'f2=', f2)  

        # 默认张量的 grad 属性会累计历史梯度值  
        # 所以,需要我们每次手动清理上次的梯度  
        # 注意:一开始梯度不存在,需要做判断  
        if x.grad is not None:  
            x.grad.data.zero_()  

        f2.backward()  
        print(x.grad)  
        print('-' * 50)  


# 3. 梯度下降优化最优解  
def test03():  
    # y = x**2  
    x = torch.tensor(10., requires_grad=True, dtype=torch.float64)  
    count = 0  
    for _ in range(50000):  
        # 正向计算  
        f = x ** 2  

        # 梯度清零  
        if x.grad is not None:  
            x.grad.data.zero_()  

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

        # 更新参数 (variable) data: Tensor | Any  
        x.data = x.data - 0.001 * x.grad  

        count += 1  
        if count % 500 == 0:  
            print(f'{count:10} {x.data}')  


if __name__ == '__main__':  
    # test01()  
    # test02()  
    test03()  
 
if __name__ == '__main__':  
    test01()  

九、模型的保存加载

  • 学习目标
  • 掌握PyTorch保存模型的方法
  • 神经网络的训练有时需要几天、几周,甚至几个月,为了在每次使用模型时避免高代价的重复训练,我们就需要将模型序列化到磁盘中,使用的时候反序列化到内存中

9.1 保存模型参数

import torch  
import torch.nn as nn  

# 假设我们有一个模型  
class SimpleModel(nn.Module):  
    def __init__(self):  
        super(SimpleModel, self).__init__()  
        self.fc = nn.Linear(10, 1)  

    def forward(self, x):  
        return self.fc(x)  

model = SimpleModel()  

# 📌保存模型的参数  
torch.save(model.state_dict(), 'model_weights.pth')  

9.2 保存全部模型

import torch  
import torch.nn as nn  

# 假设我们有一个模型  
class SimpleModel(nn.Module):  
    def __init__(self):  
        super(SimpleModel, self).__init__()  
        self.fc = nn.Linear(10, 1)  

    def forward(self, x):  
        return self.fc(x)  

model = SimpleModel()  

# 保存全部模型  
torch.save(model, 'model.pth')  

9.3 加载模型参数

# 创建一个与保存时相同结构的模型  
model = SimpleModel()  

# 加载模型的参数  
model.load_state_dict(torch.load('model_weights.pth'))  
print(model)  

  
# 保存模型的参数  
# torch.save(model.state_dict(), 'model_weights.pth')  
# print(model)  
# print('---------------------')  
# print(model.state_dict())  

# 保存完整的模型  
# torch.save(model, 'model_weights_1.pth')  
# print(model)  

9.4 加载全部模型

model = torch.load('model.pth')  
print(model)  

9.5 注意事项

  • 模型结构:如果你只保存了模型的参数,那么在加载时需要确保你有与保存时相同的模型结构
  • 设备兼容性:如果你在一个设备上保存了模型(例如GPU),而在另一个设备上加载(例如CPU),你可能需要使用 map_location 参数来指定设备:
    device = torch.device('cpu')  
    model.load_state_dict(torch.load('model_weights.pth', map_location=device))  
    

📌吃透这些技巧,PyTorch 开发难题迎刃而解,让模型训练更顺畅,助你在 AI 实践中快速进阶!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值