day02 pytorch介绍与安装

1 框架介绍与安装¶

  • 本章节主要带领大家学习使用深度学习框架 PyTorch:

PyTorch 介绍¶

  • 在2017年1月, Facebook的人工智能研究院 (FAIR) 向世界推出了PyTorch. 这个基于Torch的框架, 以其Python语言作为前端, 同时为深度学习研究者和开发者提供了两大核心优势:
  • 一是强大的GPU加速张量计算能力, 其并行计算能力在当时与NumPy相媲美.
  • 二是内置的自动微分系统, 使得构建深度神经网络变得更加直观和高效.
  • 2018年10月, 在NeurIPS 2018会议上, Facebook宣布了PyTorch 1.0的发布. 这个版本的推出, 标志着PyTorch在商业化进程中取得了重要进展.
  • 在2019年前, Tensorflow一直作为深度学习系统中的领头存在, 而以2019年为分界线, Pytorch异军突起, 逐渐成为了开发者和研究人员最为喜爱的框架. 随着Pytorch的不断普及和完善, 其生态也越发蓬勃.
  • 在AI领域, huggingface社区的开源的transformers库使用pytorch实现了市面上绝大多数开源的预训练模型.
  • 微软的分布式训练框架deepspeed也支持Pytorch, 由于Pytorch备受研究人员的青睐, 近年来绝大多数开源神经网络架构都采用Pytorch实现.

PyTorch 安装¶

  • https://github.com/pytorch/pytorch
  • 安装:
  • pip install torch==2.1.0
  • 通过本章节的学习, 同学们将会了解Pytorch的发展历史, 并掌握 PyTorch 深度学习框架的安装.

1 张量的创建¶

学习目标¶

  • 掌握张量创建
    PyTorch 是一个 Python 深度学习框架,它将数据封装成张量(Tensor)来进行运算。PyTorch 中的张量就是元素为同一种数据类型的多维矩阵。在 PyTorch 中,张量以 "类" 的形式封装起来,对张量的一些运算、处理的方法被封装在类中。

1. 基本创建方式¶

  • torch.tensor 根据指定数据创建张量
  • torch.Tensor 根据形状创建张量, 其也可用来创建指定数据的张量
  • torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量
import torch
import numpy as np
import random

# 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)


# 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)


# 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()  # int16
    data = torch.LongTensor()   # int64
    data = torch.FloatTensor()  # float32
    data = torch.DoubleTensor() # float64


if __name__ == '__main__':
    test02()

程序输出结果:

tensor(10)
tensor([[ 0.1345,  0.1149,  0.2435],
        [ 0.8026, -0.6744, -1.0918]], dtype=torch.float64)
tensor([[10., 20., 30.],
        [40., 50., 60.]])
tensor([[0.0000e+00, 3.6893e+19, 2.2018e+05],
        [4.6577e-10, 2.4158e-12, 1.1625e+33]])
tensor([10.])
tensor([10., 20.])
tensor([[         0, 1610612736, 1213662609],
        [ 805308409,  156041223,          1]], dtype=torch.int32)
tensor([2, 3], dtype=torch.int32)

2. 创建线性和随机张量¶

  • torch.arange 和 torch.linspace 创建线性张量
  • torch.random.init_seed 和 torch.random.manual_seed 随机种子设置
  • 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())


if __name__ == '__main__':
    test02()

程序输出结果:

tensor([0, 2, 4, 6, 8])
tensor([ 0.0000,  1.2222,  2.4444,  3.6667,  4.8889,  6.1111,  7.3333,  8.5556,
         9.7778, 11.0000])
tensor([[-0.5209, -0.2439, -1.1780],
        [ 0.8133,  1.1442,  0.6790]])
随机数种子: 4508475192273306739
随机数种子: 100

3. 创建01张量¶

  • torch.ones 和 torch.ones_like 创建全1张量
  • torch.zeros 和 torch.zeros_like 创建全0张量
  • 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)


# 2. 创建全1张量
def test02():

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

    # 2. 根据张量形状创建全0张量
    data = torch.ones_like(data)
    print(data)


# 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]])

4. 张量元素类型转换¶

  • tensor.type(torch.DoubleTensor)
  • 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()
    # data = data.int()
    # data = data.long()
    # data = data.float()


if __name__ == '__main__':
    test()

程序输出结果:

torch.int64
torch.float64
torch.float64

5. 小节¶

在本小节中,我们主要学习了以下内容:
1. 创建张量的方式
    1. torch.tensor 根据指定数据创建张量
    2. torch.Tensor 根据形状创建张量, 其也可用来创建指定数据的张量
    3. torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量
  • 创建线性和随机张量 - torch.arange 和 torch.linspace 创建线性张量
    • torch.random.init_seed 和 torch.random.manual_seed 随机种子设置
    • torch.randn 创建随机张量
  • 创建01张量 - torch.ones 和 torch.ones_like 创建全1张量
    • torch.zeros 和 torch.zeros_like 创建全0张量
    • torch.full 和 torch.full_like 创建全为指定值张量
  • 张量元素类型转换 - tensor.type(torch.DoubleTensor)
    • torch.double()

2 张量数值计算¶

学习目标¶

  • 掌握张量基本运算
  • 掌握阿达玛积、点积运算
  • 掌握PyTorch指定运算设备
    PyTorch 计算的数据都是以张量形式存在, 我们需要掌握张量各种运算. 并且, 我们可以在 CPU 中运算, 也可以在 GPU 中运算.

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('-' * 50)

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

    # 3. 其他函数
    print(data.sub(100))
    print(data.mul(100))
    print(data.div(100))
    print(data.neg())


if __name__ == '__main__':
    test()

程序输出结果:

tensor([[3, 7, 4],
        [0, 0, 6]])
--------------------------------------------------
tensor([[13, 17, 14],
        [10, 10, 16]])
--------------------------------------------------
tensor([[13, 17, 14],
        [10, 10, 16]])
tensor([[-87, -83, -86],
        [-90, -90, -84]])
tensor([[1300, 1700, 1400],
        [1000, 1000, 1600]])
tensor([[0.1300, 0.1700, 0.1400],
        [0.1000, 0.1000, 0.1600]])
tensor([[-13, -17, -14],
        [-10, -10, -16]])

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)
    print('-' * 50)


if __name__ == '__main__':
    test()

程序输出结果:

tensor([[ 5, 12],
        [21, 32]])
--------------------------------------------------
tensor([[ 5, 12],
        [21, 32]])
--------------------------------------------------

3. 点积运算¶

点积运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。
  • 运算符 @ 用于进行两个矩阵的点乘运算
  • torch.mm 用于进行两个矩阵点乘运算, 要求输入的矩阵为2维
  • torch.bmm 用于批量进行矩阵点乘运算, 要求输入的矩阵为3维
  • torch.matmul 对进行点乘运算的两矩阵形状没有限定. - 对于输入都是二维的张量相当于 mm 运算.
    • 对于输入都是三维的张量相当于 bmm 运算
    • 对数输入的 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.matmull 的区别
def test02():

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

    print(torch.matmul(torch.randn(3, 4, 5), torch.randn(5, 4)).shape)
    print(torch.matmul(torch.randn(5, 4), torch.randn(3, 4, 5)).shape)


# 3. torch.mm 函数的用法
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([3, 4, 4])
torch.Size([3, 5, 5])
torch.Size([3, 4, 8])

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__':
    test04()

程序输出结果:

存储设备: cpu
存储设备: cuda:0
存储设备: cpu
存储设备: cuda:0
存储设备: cpu
存储设备: cpu
存储设备: cuda:0
cuda:0 cpu

5. 小节¶

在本小节中,我们主要学习的主要内容如下:

  • 张量基本运算函数 add、sub、mul、div、neg 等函数, add_、sub_、mul_、div_、neg_ 等 inplace 函数
  • 张量的阿达玛积运算 mul 和运算符 * 的用法
  • 点积运算: - 运算符 @ 用于进行两个矩阵的点乘运算
    • torch.mm 用于进行两个矩阵点乘运算, 要求输入的矩阵为2维
    • torch.bmm 用于批量进行矩阵点乘运算, 要求输入的矩阵为3维
    • torch.matmul 对进行点乘运算的两矩阵形状没有限定. - 对于输入都是二维的张量相当于 mm 运算.
      • 对于输入都是三维的张量相当于 bmm 运算
      • 对数输入的 shape 不同的张量, 对应的最后几个维度必须符合矩阵运算规则
  • 将变量移动到 GPU 设备的方法,例如: cuda 方法、直接在 GPU 上创建张量、使用 to 方法指定设备

3 张量类型转换¶

学习目标¶

  • 掌握张量类型转换方法
    张量的类型转换也是经常使用的一种操作,是必须掌握的知识点。在本小节,我们主要学习如何将 numpy 数组和 PyTorch Tensor 的转化方法.

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)


if __name__ == '__main__':
    test01()

2. numpy 转换为张量¶

  • 使用 from_numpy 可以将 ndarray 数组转换为 Tensor,默认共享内存,使用 copy 函数避免共享。
  • 使用 torch.tensor 可以将 ndarray 数组转换为 Tensor,默认不共享内存。
# 1. 使用 from_numpy 函数
def test01():

    data_numpy = np.array([2, 3, 4])
    # 将 numpy 数组转换为张量类型
    # 1. from_numpy
    # 2. torch.tensor(ndarray)

    # 浅拷贝
    data_tensor = torch.from_numpy(data_numpy)

    # nunpy 和 tensor 共享内存
    # data_numpy[0] = 100
    data_tensor[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)

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

    print(data_tensor)
    print(data_numpy)


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

3. 标量张量和数字的转换¶

对于只有一个元素的张量,使用 item 方法将该值从张量中提取出来。

# 3. 标量张量和数字的转换
def test03():

    # 当张量只包含一个元素时, 可以通过 item 函数提取出该值
    data = torch.tensor([30,])
    print(data.item())

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


if __name__ == '__main__':
    test03()

程序输出结果:

30
30

小节¶

在本小节中, 我们主要学习了 numpy 和 tensor 互相转换的规则, 以及标量张量与数值之间的转换规则。


4 张量拼接操作¶

学习目标¶

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

1. torch.cat 函数的使用¶

torch.cat 函数可以将两个张量根据指定的维度拼接起来.
import torch


def test():

    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('-' * 50)

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

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


if __name__ == '__main__':
    test()

程序输出结果:

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

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

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

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

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

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

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

2. torch.stack 函数的使用¶

torch.stack 函数可以将两个张量根据指定的维度叠加起来.
import torch


def test():

    data1= torch.randint(0, 10, [2, 3])
    data2= torch.randint(0, 10, [2, 3])
    print(data1)
    print(data2)

    new_data = torch.stack([data1, data2], dim=0)
    print(new_data.shape)

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

    new_data = torch.stack([data1, data2], dim=2)
    print(new_data.shape)


if __name__ == '__main__':
    test()

程序输出结果:

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

3. 小节¶

张量的拼接操作也是在后面我们经常使用一种操作。cat 函数可以将张量按照指定的维度拼接起来,stack 函数可以将张量按照指定的维度叠加起来。


5 张量索引操作¶

学习目标¶

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

1. 简单行、列索引¶

准备数据

import torch

data = torch.randint(0, 10, [4, 5])
print(data)
print('-' * 50)

程序输出结果:

tensor([[0, 7, 6, 5, 9],
        [6, 8, 3, 1, 0],
        [6, 3, 8, 7, 3],
        [4, 9, 5, 3, 1]])
--------------------------------------------------
# 1. 简单行、列索引
def test01():

    print(data[0])
    print(data[:, 0])
    print('-' * 50)

if __name__ == '__main__':
    test01()

程序输出结果:

tensor([0, 7, 6, 5, 9])
tensor([0, 6, 6, 4])
--------------------------------------------------

2. 列表索引¶

# 2. 列表索引
def test02():

    # 返回 (0, 1)、(1, 2) 两个位置的元素
    print(data[[0, 1], [1, 2]])
    print('-' * 50)

    # 返回 0、1 行的 1、2 列共4个元素
    print(data[[[0], [1]], [1, 2]])
if __name__ == '__main__':
    test02()

程序输出结果:

tensor([7, 3])
--------------------------------------------------
tensor([[7, 6],
        [8, 3]])

3. 范围索引¶

# 3. 范围索引
def test03():
    # 前3行的前2列数据
    print(data[:3, :2])
    # 第2行到最后的前2列数据
    print(data[2:, :2])
if __name__ == '__main__':
    test03()

程序输出结果:

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

4. 布尔索引¶

# 布尔索引
def test():

    # 第2列大于5的行数据
    print(data[data[:, 2] > 5])
    # 第1行大于5的列数据
    print(data[:, data[1] > 5])
if __name__ == '__main__':
    test04()

程序输出结果:

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

5. 多维索引¶

# 多维索引
def test05():

    data = torch.randint(0, 10, [3, 4, 5])
    print(data)
    print('-' * 50)

    print(data[0, :, :])
    print(data[:, 0, :])
    print(data[:, :, 0])


if __name__ == '__main__':
    test05()

程序输出结果:

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

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

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

6 张量形状操作¶

学习目标¶

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

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])

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.size())

    # 1. 交换1和2维度
    new_data = torch.transpose(data, 1, 2)
    print('data shape:', new_data.size())

    # 2. 将 data 的形状修改为 (4, 5, 3)
    new_data = torch.transpose(data, 0, 1)
    new_data = torch.transpose(new_data, 1, 2)
    print('new_data shape:', new_data.size())

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


if __name__ == '__main__':
    test()

程序运行结果:

data shape: torch.Size([3, 4, 5])
data shape: torch.Size([3, 5, 4])
new_data shape: torch.Size([4, 5, 3])
new_data shape: torch.Size([4, 5, 3])

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.size())

    # 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
    # new_data = new_data.view(2, 3)  # RuntimeError

    # 需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数
    print(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
True
new_data shape: torch.Size([2, 3])

4. squeeze 和 unsqueeze 函数的用法¶

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维度增加一个维度
    new_data = data.unsqueeze(-1)
    print('new_data shape:', new_data.size())  # torch.Size([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])

5. 小节¶

本小节带着同学们学习了经常使用的关于张量形状的操作,我们用到的主要函数有:

  • reshape 函数可以在保证张量数据不变的前提下改变数据的维度.
  • transpose 函数可以实现交换张量形状的指定维度, permute 可以一次交换更多的维度.
  • view 函数也可以用于修改张量的形状, 但是它要求被转换的张量内存必须连续,所以一般配合 contiguous 函数使用.
  • squeeze 和 unsqueeze 函数可以用来增加或者减少维度.

7 张量运算函数¶

学习目标¶

  • 掌握张量相关运算函数

1. 常见运算函数¶

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

import torch


def test():

    data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
    print(data)
    print('-' * 50)

    # 1. 计算均值
    # 注意: tensor 必须为 Float 或者 Double 类型
    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.log2())
    print(data.log10())


if __name__ == '__main__':
    test()

程序运行结果:

tensor([[4., 0., 7.],
        [6., 3., 5.]], dtype=torch.float64)
--------------------------------------------------
tensor(4.1667, dtype=torch.float64)
tensor([5.0000, 1.5000, 6.0000], dtype=torch.float64)
tensor([3.6667, 4.6667], dtype=torch.float64)
--------------------------------------------------
tensor(25., dtype=torch.float64)
tensor([10.,  3., 12.], dtype=torch.float64)
tensor([11., 14.], dtype=torch.float64)
--------------------------------------------------
tensor([[16.,  0., 49.],
        [36.,  9., 25.]], dtype=torch.float64)
--------------------------------------------------
tensor([[2.0000, 0.0000, 2.6458],
        [2.4495, 1.7321, 2.2361]], dtype=torch.float64)
--------------------------------------------------
tensor([[5.4598e+01, 1.0000e+00, 1.0966e+03],
        [4.0343e+02, 2.0086e+01, 1.4841e+02]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.3863,   -inf, 1.9459],
        [1.7918, 1.0986, 1.6094]], dtype=torch.float64)
tensor([[2.0000,   -inf, 2.8074],
        [2.5850, 1.5850, 2.3219]], dtype=torch.float64)
tensor([[0.6021,   -inf, 0.8451],
        [0.7782, 0.4771, 0.6990]], dtype=torch.float64)

8 自动微分模块¶

学习目标¶

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

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

    # 注意:
    # 由于求导的结果必须是标量
    # 而 f 的结果是: tensor([120., 420.])
    # 所以, 不能直接自动微分
    # 需要将结果计算为标量才能进行计算
    f2 = f1.mean()  # f2 = 1/2 * x

    # 自动微分
    f2.backward()

    # 打印 x 变量的梯度
    print(x.grad)


if __name__ == '__main__':
    test01()

程序运行结果:

tensor(20., dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)

2. 控制梯度计算¶

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

import torch


# 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()

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

        f2.backward()
        print(x.grad)


# 3. 梯度下降优化最优解
def test03():

    # y = x**2
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)

    for _ in range(5000):

        # 正向计算
        f = x ** 2

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

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

        # 更新参数
        x.data = x.data - 0.001 * x.grad

        print('%.10f' % x.data)


if __name__ == '__main__':
    test01()
    print('--------------------')
    test02()
    print('--------------------')
    test03()

程序运行结果:

True
False
False
False
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)
tensor([ 5., 10., 15., 20.], dtype=torch.float64)

3. 梯度计算注意¶

当对设置 requires_grad=True 的张量使用 numpy 函数进行转换时, 会出现如下报错:
Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
此时, 需要先使用 detach 函数将张量进行分离, 再使用 numpy 函数.
注意: detach 之后会产生一个新的张量, 新的张量作为叶子结点,并且该张量和原来的张量共享数据, 但是分离后的张量不需要计算梯度。

import torch


# 1. detach 函数用法
def test01():

    x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)

    # Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
    # print(x.numpy())  # 错误
    print(x.detach().numpy())  # 正确


# 2. detach 前后张量共享内存
def test02():

    x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)

    # x2 作为叶子结点
    x2 = x1.detach()

    # 两个张量的值一样: 140421811165776 140421811165776
    print(id(x1.data), id(x2.data))
    x2.data = torch.tensor([100, 200])
    print(x1)
    print(x2)

    # x2 不会自动计算梯度: False
    print(x2.requires_grad)


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

程序运行结果:

10. 20.]
140495634222288 140495634222288
tensor([10., 20.], dtype=torch.float64, requires_grad=True)
tensor([100, 200])
False

4. 小节¶

本小节主要讲解了 PyTorch 中非常重要的自动微分模块的使用和理解。我们对需要计算梯度的张量需要设置 requires_grad=True 属性,并且需要注意的是梯度是累计的,在每次计算梯度前需要先进行梯度清零。


10 模型的保存加载¶

学习目标¶

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

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')

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')

3: 加载模型参数¶

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

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

4: 加载全部模型¶

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

学员问题

  • 问题1: 关于世界巨头公司的特点?

    • Google: 1998 - 2024, 世界第一!!!
      • 搜索🔍
      • 2003 Hadoop: 开启了大数据的时代.
      • 2014 Tensorflow: 开启了人工智能的时代.
      • 2017 Transformer
      • 2018 BERT
      • 2023-2024 被OpenAI小小的压制
      • ⭕️Google过去20多年一直特别喜欢发论文, 开源分享.
    • Facebook (Meta): 低调奢华有内涵!!!
      • 总体很低调, 但是发布了很多非常非常好用的工具, 框架.
      • 发论文, 很低调, 但是仔细看看后很重要 + 很实用.
      • Hive --- 大数据
      • Fasttext --- AI时代
      • Pytorch --- AI时代 🍊
    • 微软:
      • IT第二个时代的老大 (第一个时代老大是谁?) --- IBM
      • 明明已经赶不上互联网 + AI时代, 突然投资了OpenAI, 大逆转了
    • BATB:
      • 追赶阶段, AI时代追的最近的时代. 身为差距最短.
  • 问题2: 关于当前市场上的几大开发框架?

    • Tensorflow:
      • 2014 0.x --- 1.x --- 所有同学们的幸福🥰 (彻底忘掉1.x时代, 静态图模式)
        • 2024年, Google内部很大比例的工程师依然在用1.x (内幕🍊)
      • 2019 2.x --- 动态图模式 --- 工业界全部用2.x
    • ⭕️Pytorch:
      • 2017 动态图模式 (2018年)
      • 1.3, 1.6, 1.8, 1.10 比较主流的版本.
      • 2.1, 2.2, 2.4 初创公司 (版本迁移, 1.x -> 2.x)
    • PaddlePaddle:
      • 你只要会用了Pytorch, 代码迁移到飞浆1-2周. (不建议专门学, 用的时候看一下就会了)
    • Caffe:
      • 太小众, 偏学术, 不建议学.
  • 问题3: 关于同学们的环境问题?

    • 1: 下载anaconda, 安装conda环境. https://www.anaconda.com/
      • 一个安装包, 直接点击安装.
    • 2: 推荐以命令行的模式创建环境.
      • conda create -n deep_learning python=3.10. (3.10, 3.11, 3.12)
      • deep_learning是你自己起的环境名字, 程序员自主决定
      • 利用conda创建出来的虚拟环境, 跟vmware不是一回事 (不建议用vmware模式)
    • 3: 激活环境
      • Conda activate deep_learning
      • Source activate deep_learning
    • 4: 查看环境中已经安装好的包
      • Pip list
    • 5: 安装对应的各种包
      • pip install torch==2.3.0
      • pip install transformers
    • 6: 退出虚拟环境
      • Source deactivate deep_learning
      • Conda deactivate deep_learning
    • 至于IDE的选择, 怎么舒服怎么来.
      • vim
      • VScode
        • 需要专门为IDE选择解释器
      • Pycharm
      • Jupyter notebook (校园工具)
  • 问题4: 关于Pytorch的数据精度问题?

    • 如果不指定数据精度, 默认就是float32 --- 也是神经网络训练最基准的数据精度.
      • 1: 如果是整型, Pytorch默认的是torch.int64, 数据格式后面不显示; 其他的都显示torch.int16, torch.int32, torch.int8
      • 2: 如果是浮点型, Pytorch默认的是torch.float32, 数据格式后面不显示; 其他的都显示torch.float16, torch.float64
    • int16, int32, int64有什么区别? (LLL)
      • 一个数值在内存中占了多少个比特位.
      • 不是计算机专业的小伙伴可能有点困惑.
      • 今天的一个小作业, 回去查查!!! ⭕️
  • 问题5: 小朱老师提问 --- 计算机里有没有"真随机数"?

    • 没有🈚️!!! import random, random.rand() 生成随机数
    • 🍊计算机只有"伪随机数", 什么意思呢? 就是这些随机数都是"计算出来的" !!!
      • 1: 给一个起始值 (初始值)
      • 2: 按照一整套固定的公式算出来的数字🔢
  • 作业:

    • 第1次作业:
      • 1: 今天的一个小作业, 回去查查int16, int32, int64, 底层的内存机制!!!
      • 2: 第2章, 2.1之前 (张量创建) 代码跑完, 结果提交 + 截图提交.
      • 作业截止时间 12月8日 20:00 之前. 助教📮 + 小朱老师📮
        • 助教邮箱📮: wanghao5276@163.com (这个一定要传, 助教老师统计作业)
        • 小朱老师邮箱📮: 348811083@qq.com
  • 问题1: 学习的过程中一定要脑洞大开!!! (小朱老师👩🏻‍🏫的建议)

    • 很多的代码, 回去后各种实验, 多实验你就很熟悉一些情况.
    • 面试中写代码这件事:
      • 1: 刷Leetcode --- 淘汰赛 (50%)
      • 2: 就让你写一个日常工作中的代码 --- 老鸟🐦直接写基本不报错...... (新手写了20行, 里面bug好几行)
  • 问题2: 关于reshape函数的特点?

    • 关键: tensor.reshape(N, M) --- N * M = tensor所有的元素个数 (不能多, 也不能少)
  • 问题3: transpose, permute函数的特点?

    • 1: transpose(data, x, y) --- 每次只能调整2个维度的组合.
      • 不管传进来的data是什么维度, transpose只能一次处理2个维度.
    • 2: permute(data, [x, y, z]) --- 每次可以调整3个维度的组合.
      • 核心在于data张量的维度, 必须要和调整的维度保持一致. 但是data是什么维度都可以接受.
  • 问题4: 关于view()函数和contiguous()函数?

    • 1: 首先view()函数使用起来和reshape()函数特别像, 改变张量的shape
    • 2: view() 只能应用在内存连续的张量上; 如果data不连续, 使用data.contiguous()变成内存连续的张量.
    • 3: 发现data最初始的时候内存不连续, 使用data.reshape()之后内存连续了.
    • 🍊4: 如果data是内存连续的张量, 执行res = data.reshape()之后, res是一个共享内存的副本; 如果data内存不连续的张量, 执行res = data.reshape()之后, res是一个新创建的内存的张量.
    • ⭕️大作业: 内存连续不连续不明白. (湖海茫茫) --- 不是几分钟能说明白的.⭕️
      • 涉及到操作系统, 计算机体系结构.
      • 16GB --- 16000MB --- 16000000KB --- 每一个B (字节) 背后都有一个地址.
        • 栗子🌰: 变量 x = 100, 本质上是一个存储在内存条位置是2003657834563号地址的一个整数, 整数大小是100. 变量 y = 9.88, 本质上是一个存储在内存条位置是57383292617847984号地址的一个32位浮点数.
    • 栗子🌰: x = [1, 2, 3.3, 4, 5.2, 6.66] --- 这不叫内存连续, 这只是一个数组.
      • 存储连续就是内存地址连续. ✅ (风之子)
      • 内存连续和硬盘连续又有啥关系? 连续有啥用? (啊)
        • 1: 内存连续是内存的事 --- 16GB, 32GB......硬盘连续是磁盘的事 --- 512GB, 1TB
          • 内存 PK 硬盘 --- 两个设备
          • CPU PK GPU --- 两个设备
        • 2: ⭕️连续有啥用? (很有用, 很重要, 是传统编程和AI重点研究的问题)
          • 内存取数据也好, 硬盘取数据也好, 连续地址最快, 不连续就慢!!!
          • 内存不连续差距很小
          • 硬盘不连续差距很大. (Windows小伙伴, 磁盘重新整理)
  • 🍇第2次作业:

    • 从第2章数值计算, 到第6章结束的代码全跑一遍, 结果截图提交.
    • 大作业: 内存连续不连续回去好好查查, 写一段自己的理解吧.
      • 不建议现在看的太深入, 初步理解一下为主.
        • 1: 内存是有地址概念的.
        • 2: 内存连续很重要.
        • 3: 内存是分8位存储, 16为存储, 32为存储, 64位存储.
    • 第2次作业截止时间12月16日 20:00 吧.📮
  • 问题1: 关于AI领域内的源代码的问题? (小朱老师👩🏻‍🏫)

    • 1: AI一个新的模型, 一个新的架构, paper的形式发出来 --- 代码的实现一个公司一个样, 一个小队伍一个样.
      • BERT: 第一版本BERT谷歌发布的Tensorflow的版本 (1.x版本)
        • Huggingface发布了Pytorch版本, 后续谷歌发布2.x版本
        • 国内百度的版本, 阿里的版本, 清华的, 哈工大.......
      • GPT: 第一个版本谷歌发布 transformer的版本
        • OpenAI的GPT1, GPT2
        • ChatGLM3-6b, 4-9b 很多的代码实现已经不同了
        • Qwen系列的代码实现又不一样
        • DeepSeek v2的代码实现又不一样
      • ⭕️各有所长, 尽量同一家的看👁
    • 2: 你连论文的代码复现都不会, 别整AI啦🌶 !!!
      • ❎ --- 不对, 但是又不是完全对.....
      • 2.1: 大厂的算法岗, 独角兽的算法岗 --- 能力基本是个正常水平.
      • 2.2: 代码复现在AI里面圈里⭕️是属于偏高端的能力.......(10% ~ 20%)
    • 3: 2025了, BERT + GPT (Transformer的源代码已经是标配了)
  • 问题2: 8.1031e+03 --- 科学计数法?

    • \(e+03 : 10^3\)
    • \(8.1031e+03 = 8103.1\)
    • \(e-05 : 10^{-5}\)
    • \(8.1031e-05 = 0.000081031\)
  • 问题3: 为什么明明我在写Python的代码, 为什么非得用Pytorch呢? 我直接用Python + numpy不行吗?

    • 最最核心的原因就是: Tensorflow, Pytorch, PaddlePaddle, 提供了自动微分模块.
    • 自动微分模块也是当前全世界的几大框架中, 最值钱的部分.
      • 能写自动微分模块的在AI里面圈里⭕️是属于顶尖级的能力.......(万分之一) --- 巨佬
        • 飞桨系统, 小朱老师参与开发, 模块级别开发
        • nn.LSTM(), pd.LSTm()
        • 贾扬清 --- Pytorch, PaddlePaddle --- 参考了Caffe的源代码
  • 问题4: 怎么评价一个模型效果好还是不好? (虽然有点超纲 --- 双子)

    • 接下来这几个月都围绕这个关键指标
    • 准确率
    • 召回率
    • F1
    • BLEU
    • Rouge-1, Rouge-2
    • Rouge-L
    • 分类任务, 检测任务, 信息抽取任务, 多句关系任务, 机器翻译, 文本摘要
      • 都会抽象关键指标
      • 一句话: 🍊 大模型的评价一定是定量的!!!
    • 论文, 帖子: 大模型刷榜......刷的就是上面这些核心指标.
  • 问题5: 为什么requires_grad = True, 有时候还设置成False呢?

    • 模型训练的时候设置成True, 需要反向传播 + 梯度计算 + 参数更新.
    • 模型推理的时候设置成False, 不需要反向传播, 不需要计算梯度了.
  • 问题6: 关于每一轮x.grad梯度值会进行累加, 为什么❓❓❓

    • 1: 正向理解不清零肯定有问题 --- 未来神经网络的训练都是分批次进行的, batch_size = 32 (64, 128), x.grad累加的梯度值更新完了参数后, 如果不进行清零, 第二个批次进来后, 又有32个样本计算了梯度, x.grad已经累积了64条样本的梯度, 但是更新所面对的样本是第二个批次的32个样本, 发生了错位!!!
    • 2: 如果没有累加的功能 --- 反过来batch_size = 32怎么办呢???
      • x.grad --- 累加了32个样本的梯度 --- 直接进行一次x.grad / 32的操作, 用梯度的平均值来更新参数!!!
    • 3: 训练方法叫多轮反向传播 + 1次参数更新 ---- 显存的消耗会非常大........
      • 节省显存: 连续进行8个批次的反向传播 batch_size = 2, Out Of Memory, 逼着你只能batch_size = 1
      • 连续进行16次反向传播, 相当于积攒了batch_size = 16的效果, 16个梯度都累加到x.grad中了.
  • 作业: 回去提前预习一下反向传播算法.

posted @ 2025-10-26 19:19  凫弥  阅读(7)  评论(0)    收藏  举报