写在前面
大模型作为当前前沿方向,许多框架还未成熟,必定有许多坑,几乎所有大模型编写框架都是采用PyTorch,所以学习好PyTorch是迈向大模型开发的基础。之前零零散散的学习了PyTorch,这里做一些总结和复习,这篇主要区B站上找了一些up(卢菁VIP课、刘二大人、我是土堆等)学习,另外也使用了大模型进行辅助(学习大模型肯定要知道怎么巧用大模型,没毛病吧),推荐在vscode中安装一个trea插件,方便编写代码。
导学
'''这节简单介绍下什么是PyTorch,为什么需要学习PyTorch'''
PyTorch框架介绍:Facebook的⼈⼯智能研究院 (FAIR) 向世界推出了PyTorch. 这个基于Torch的框架, 以其Python语⾔作为前端, 同时为深度学习研究者和开发者提供了两⼤核⼼优势: ⼀是强⼤的GPU加速张量计算能⼒ , 其并⾏计算能⼒在当时与NumPy相媲美。 ⼆是内置的⾃动微分系统, 使得构建深度神经⽹络变得更加直观和⾼效。
大模型相关支持:huggingface社区 的开源的transformers库使⽤pytorch实现了市⾯上绝⼤多数开源的预训练模型 。微软的分布式训练框架deepspeed也⽀持Pytorch, 由于Pytorch备受研究⼈员的⻘睐, 近年来绝⼤多数开源神经⽹络架构都采⽤Pytorch实现。
安装:这类网上教程巨多,不多赘述,可以采用直接官网安装 ,但安装缓慢,甚至可能断链,这里贴一个镜像下载 的安装教程https://zhuanlan.zhihu.com/p/612181449
Python List 、Numpy Array和PyTorch Tensor的区别:
为什么不直接用Python自带的List处理数组呢?看了下面这个表和例子就知道答案,大模型参数量动不动就上几十、几百、几千亿,从下面的示例可以看出,处理数据,nmpy效率时list的100倍之多,这只是考虑的数据的读取和简单的运算,如果把求导等复杂运算,GPU加速考虑进来,PyTorch Tensor效率高于Python List数千倍、数十万倍,甚至上百万倍。假设需要1B的大模型进行推理,一般情况下需要几秒时间即可得到答案,但假设用List处理可能就需要几天才能得到答案。
Python List | Numpy Array | PyTorch Tensor |
---|---|---|
最基础的动态数组,可存储任意类型的数据(异构) | 由 Python 的NumPy库提供,支持同构数据(元素类型必须一致) | 由PyTorch库提供,专为深度学习设计,与NumPy类似 但支持GPU加速 |
灵活性高 ,但计算效率较低(尤其是大规模数据) | 固定维度 (如 1D 向量、2D 矩阵),内存连续 ,计算效率高 | 支持自动求导(requires_grad=True时),用于构建神经网络 |
不支持向量化操作,需通过循环处理元素 | 支持向量化操作(无需显式循环),适合科学计算 | 与 NumPy 数组语法高度相似,可无缝转换 |
python
def test04():
#Numpy内存连续,计算效率高(对比python List)
# 创建大型数组/列表
#np_array = np.arange(1000000)
#py_list = list(range(1000000))
# timeit 模块来比较NumPy数组向量化操作和Python列表循环操作的性能差异。
numpy_time = timeit.timeit(
stmt='np_array + 1',
setup='import numpy as np; np_array = np.arange(100000)',
number=1000
)
'''
stmt:要测试的代码语句,这里是 np_array + 1,表示对 NumPy 数组的每个元素加 1。
setup:在执行测试代码前运行的初始化代码,这里导入 numpy 并创建一个包含 1000000 个整数的数组。
number:指定测试代码的执行次数(这里是 1000 次)。
timeit.timeit() 返回的是 number 次执行的总耗时(秒),因此需要除以 number 得到每次执行的平均耗时。
'''
list_time = timeit.timeit(
stmt='[x+1 for x in py_list]',
setup='py_list = list(range(100000))',
number=1000
)
print(f"NumPy 操作耗时: {numpy_time/1000:.6f} 秒/次") # 计算每次操作的平均耗时
print(f"列表操作耗时: {list_time/1000:.6f} 秒/次") # 计算每次操作的平均耗时
运行结果;
NumPy 操作耗时: 0.000047 秒/次
列表操作耗时: 0.005235 秒/次
张量的简单操作
**张量的创建、创建技巧、格式转换、**随机数生成器
python
def test01():
# 张量标量的创建
data = torch.tensor(10)
print(data) # 输出 tensor(10)
# 2. numpy 数组转化为张量
data = np.random.randn(2, 3)
data = torch.tensor(data)
print(data) # 输出两行三列的随机张量,类型默认为dtype=torch.float64
# 3. 列表转化为张量
data = [[10., 20., 30.], [40., 50., 60.]]
data = torch.tensor(data)
print(data) # 输出tensor([[10, 20, 30],[40, 50, 60]])
def test02():
# 1. 创建2⾏3列的张量
data = torch.Tensor(2, 3)
# 2. 注意: 如果传递列表, 则创建包含指定元素的张量
data = torch.Tensor([10])
print(data)
data = torch.Tensor([10, 20])
print(data)
#注意:torch.Tensor是一个类
#torch.tensor(10)和torch.Tensor([10])的区别
# 验证数据类型
print(torch.tensor(10).dtype) # torch.int64
print(torch.Tensor([10]).dtype) # 注意torch.Tensor会自动转化为torch.float32
# 验证形状
print(torch.tensor(10).shape) # torch.Size([])
# 3. 使⽤具体类型的张量
def test03():
# 1. 创建2⾏3列, dtype 为 int32 的张量
data = torch.IntTensor(2, 3)
# 2. 注意: 如果传递的元素类型不正确, 则会进⾏类型转换
data = torch.IntTensor([2.5, 3.3])
# 3. 其他的类型
data = torch.ShortTensor([2.5, 3.3]) # int16
data = torch.LongTensor([2.5, 3.3]) # int64
data = torch.FloatTensor([2.5, 3.3]) # float32
data = torch.DoubleTensor([2.5, 3.3]) # float64
#注意:
#1、对于整型,Pytorch默认的是int64,dtype字段默认不显示
#2、对于浮点型,Pytorch默认的是float32,dtype字段默认不显示
def test04():
# 1. 创建2行3列, dtype 为 int32 的张量
data = torch.zeros(2, 3, dtype=torch.int32)
print(data)
# 2. 注意: 如果传递的元素类型不正确, 则会进行类型转换
data = torch.tensor([2.5, 3.3], dtype=torch.int32)
print(data)
# 3. 其他的类型
# 同样使用 torch.tensor() 并通过 dtype 明确指定不同的数据类型。
data = torch.tensor([2.5, 3.3], dtype=torch.int16) # int16
print(data)
data = torch.tensor([2.5, 3.3], dtype=torch.int64) # int64
print(data)
data = torch.tensor([2.5, 3.3], dtype=torch.float32) # float32
print(data)
data = torch.tensor([2.5, 3.3], dtype=torch.float64) # float64
print(data)
def test05(): # 创建的技巧
data = torch.arange(0, 10, 2) # 步长为2
data = torch.linspace(0, 11, 10) # 均匀分布
data = torch.rand(2, 3) # 创建2行3列的随机张量,在区间 [0, 1) 上均匀分布
data = torch.randn(2, 3) # 从标准正态分布(高斯分布),即均值为0,方差为1
data = torch.zeros(2, 3)
data = torch.zeros_like(data) # 复制
data = torch.ones(2, 3)
data = torch.ones_like(data)
data = torch.full([2, 3], 10)
data = torch.full_like(data, 20)
def test06(): # 格式转换
data = torch.full([2, 3], 10.)
data = data.type(torch.DoubleTensor)
# 将 data 元素类型转换为 float64 类型
# 2. 第⼆种⽅法
data = data.double()
# 转换为其他类型
data = data.short()
data = data.int()
data = data.long()
data = data.float()
对于torch.Tensor与torch.tensor的区别其实没有太多实质性的区别,只是老版本用的torch.Tensor,新版本用的torch.tensor更灵活,大多数情况下都可以相互替换,但应当首选torch.tensor。
数据精度
PyTorch 默认使用float32 以优化计算效率(尤其适合GPU加速),但可能损失部分精度。 NumPy 默认使用 float64,适合科学计算中对精度要求高的场景。
随机数生成器
为了验证可复现性,需要使用随机数种子
torch.random.manual_seed(100) # 设置随机种子
张量的基本数学运算、统计运算、维度dim选择基本理解、深浅拷贝
python
#张量的基本数学计算
def test01():
x = torch.tensor([1.0, 2.3, 3.6])
# 绝对值
abs_x = torch.abs(x)
# 平方根
sqrt_x = torch.sqrt(x)
# 指数运算
exp_x = torch.exp(x)
# 对数运算
log_x = torch.log(x)
# 幂运算
pow_x = torch.pow(x, 2) # 或者 x ** 2
# 四舍五入
round_x = torch.round(x)
# 向上取整
ceil_x = torch.ceil(x)
# 向下取整
floor_x = torch.floor(x)
#张量的统计运算
def test02():
# 维度的选择,我的理解是dim代表维度,维度越小代表选取的原始张量范围越大,
# 比如dim=0代表选取原始张量的第0维,也就是最外层的中括号,范围为2,
# 再比如dim=1代表选取原始张量的第1维,也就是第1层的中括号,范围为3,依次类推
x = torch.randint(0, 10, [2, 3, 4, 5]) # 生成一个 2x3x4x5 的随机整数张量
print(x)
max_vals, max_indices = torch.max(x, dim=0)
print('dim = 0:max_vals, ', max_vals)
print('dim = 0:max_indices', max_indices)
max_vals, max_indices = torch.max(x, dim=1)
print('dim = 1:max_vals, ', max_vals)
print('dim = 1:max_indices', max_indices)
max_vals, max_indices = torch.max(x, dim=2)
print('dim = 2:max_vals, ', max_vals)
print('dim = 2:max_indices', max_indices)
max_vals, max_indices = torch.max(x, dim=3)
print('dim = 3:max_vals, ', max_vals)
print('dim = 3:max_indices', max_indices)
#张量的基础运算
def test03():
#randint生成离散均匀分布随机整数的函数
data = torch.randint(0, 10, [2, 3])
# 1. 不修改原数据
new_data = data.add(10) # 等价 new_data = data + 10
# 2. 直接修改原数据
# 注意: 带下划线的函数为修改原数据本身
data.add_(10) # 等价 data += 10
# 3. 其他函数
print(data.sub(100))
print(data.mul(100))
print(data.div(100))
print(data.neg())
# 注意: tensor 必须为 Float 或者 Double 类型
def test04():
data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
print(data.mean()) #计算均值
print(data.sqrt()) #计算平方根
print(data.exp()) #指数运算,e^n 次⽅
print(data.log()) #对数运算,以 e 为底
### 深拷贝与浅拷贝 ,记住torch.tensor函数是深拷贝,data.item()取值赋值也类似于深拷贝
# 1. 将张量转换为 numpy 数组
def test05():
data_tensor = torch.tensor([2, 3, 4])
# 使⽤张量对象中的 numpy 函数进⾏转换
data_numpy = data_tensor.numpy() # 浅拷贝
# 2. 使⽤ from_numpy 函数
def test06():
data_numpy = np.array([2, 3, 4])
# 将 numpy 数组转换为张量类型
data_tensor = torch.from_numpy(data_numpy)# 浅拷⻉
print(data_numpy)
# 3. 使⽤ torch.tensor 函数
def test07():
data_numpy = np.array([2, 3, 4])
data_tensor = torch.tensor(data_numpy)
# numpy 和 tensor 不共享内存,深拷贝
# 4. 标量张量和数字的转换
def test08():
# 当张量只包含⼀个元素时, 可以通过 item 函数提取出该值
data = torch.tensor([30,])
print(data.item())
data = torch.tensor(30)
print(data.item())
#应用场景:获取loss值
维度简单理解:dim越小,维度越低,看的越广,tensor括号越靠外面