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 上有两种方法:
- 使用 cuda 方法
- 直接在 GPU 上创建张量
- 使用 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时代追的最近的时代. 身为差距最短.
- Google: 1998 - 2024, 世界第一!!!
-
问题2: 关于当前市场上的几大开发框架?
- Tensorflow:
- 2014 0.x --- 1.x --- 所有同学们的幸福🥰 (彻底忘掉1.x时代, 静态图模式)
- 2024年, Google内部很大比例的工程师依然在用1.x (内幕🍊)
- 2019 2.x --- 动态图模式 --- 工业界全部用2.x
- 2014 0.x --- 1.x --- 所有同学们的幸福🥰 (彻底忘掉1.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:
- 太小众, 偏学术, 不建议学.
- Tensorflow:
-
问题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 (校园工具)
- 1: 下载anaconda, 安装conda环境. https://www.anaconda.com/
-
问题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)
- 一个数值在内存中占了多少个比特位.
- 不是计算机专业的小伙伴可能有点困惑.
- 今天的一个小作业, 回去查查!!! ⭕️
- 如果不指定数据精度, 默认就是float32 --- 也是神经网络训练最基准的数据精度.
-
问题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: 学习的过程中一定要脑洞大开!!! (小朱老师👩🏻🏫的建议)
- 很多的代码, 回去后各种实验, 多实验你就很熟悉一些情况.
- 面试中写代码这件事:
- 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是什么维度都可以接受.
- 1: transpose(data, x, y) --- 每次只能调整2个维度的组合.
-
问题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小伙伴, 磁盘重新整理)
- 1: 内存连续是内存的事 --- 16GB, 32GB......硬盘连续是磁盘的事 --- 512GB, 1TB
-
🍇第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的代码实现又不一样
- ⭕️各有所长, 尽量同一家的看👁
- BERT: 第一版本BERT谷歌发布的Tensorflow的版本 (1.x版本)
- 2: 你连论文的代码复现都不会, 别整AI啦🌶 !!!
- ❎ --- 不对, 但是又不是完全对.....
- 2.1: 大厂的算法岗, 独角兽的算法岗 --- 能力基本是个正常水平.
- 2.2: 代码复现在AI里面圈里⭕️是属于偏高端的能力.......(10% ~ 20%)
- 3: 2025了, BERT + GPT (Transformer的源代码已经是标配了)
- 1: AI一个新的模型, 一个新的架构, paper的形式发出来 --- 代码的实现一个公司一个样, 一个小队伍一个样.
-
问题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的源代码
- 能写自动微分模块的在AI里面圈里⭕️是属于顶尖级的能力.......(万分之一) --- 巨佬
-
问题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中了.
-
作业: 回去提前预习一下反向传播算法.

浙公网安备 33010602011771号