深度学习_原理和进阶_PyTorch入门(2)后续语法3
后续语法3
1,PyTorch-广播机制
import torch"""
1,什么是广播机制
定义:广播机制是一种让 PyTorch (以及 NumPy 等其他科学
计算库)在进行元素级运算时,能够处理形状不完全相同的张量
的方法。它允许较小的张量“扩展”或者“广播”成较大的张量,以
便可以进行运算。
核心思想: 广播机制的目标是在不复制数据的情况下,使得不
同形状的张量能够进行运算。
优势:
代码简洁: 不需要显式地对张量进行复制或者重塑,代码更加简洁和易读。
内存高效: 避免不必要的数据复制,节约内存空间,提升计算效率。2,PyTorch 中的广播遵循以下两个基本规则:
(1:维度对齐
(2:维度扩展
"""# 1,相同形状的张量
scalar1 = torch.full((2, 2), 100)
scalar2 = torch.full((2, 2), 12)
scalar3 = scalar2 + scalar1
print(f"相同形状的张量相加,正常元素加法:{scalar3}")# 2,维度拓展 维度大小不同,但是对应维度长度相同
scalar1 = torch.full((2, 2), 100)
scalar2 = torch.tensor([12, 12])
scalar3 = scalar2 + scalar1
# 逻辑等价(2) 被广播成了 (2,2)
print(f"对应维度长度相同加法:{scalar3}")# 3,维度对齐 维度大小相同,其中一个维度长度为1
scalar1 = torch.full((2, 2), 100)
scalar2 = torch.full((1, 2), 12)
scalar3 = torch.full((2, 1), 12)
# 逻辑等价与 (2,1)和(1,2)都被广播成了(2,2)
scalar4 = scalar1 + scalar2 + scalar3
print(f"对应维度长度相同加法:{scalar4}")#4,维度大小和对应维度长度都不相同情况,会报错
scalar1 = torch.full((2, 2), 100)
scalar2 = torch.tensor([12, 12,12])
scalar3 = scalar2 + scalar1
相同形状的张量相加,正常元素加法:tensor([[112, 112],
[112, 112]])
对应维度长度相同加法:tensor([[112, 112],
[112, 112]])
对应维度长度相同加法:tensor([[124, 124],
[124, 124]])
Traceback (most recent call last):
File "D:\PycharmProjects\AIStudy\baizhanAI\深度学习_原理进阶\PyTorch\9_PyTorch_广播机制.py", line 44, in <module>
scalar3 = scalar2 + scalar1
~~~~~~~~^~~~~~~~~
RuntimeError: The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 1
2,PyTorch索引操作
import torch"""
索引分类
1,行列索引
2,切片索引
3,列表u松阴
4,多维索引
5,布尔索引
6,省略号'...'使用
7,组合索引
8,通过索引赋值在深度学习中经常用于:
数据预处理
批量处理
特征选择
注意力机制
"""scalar1 = torch.arange(1, 10, 1) # 左闭右开
# 转换为 3*3的矩阵
matrix1 = scalar1.reshape(3, 3)
print(f"matrix1:{matrix1}")
print("---------行列索引-------------")print(f"一行一列:{matrix1[0, 0]}") # 一行一列
print(f"1行三列:{matrix1[0, 2]}") # 1行三列
print(f"第二行:{matrix1[1]}")
print(f"第1列:{matrix1[:, 0]}")
print(f"前两行:{matrix1[0:2]}")
print(f"前两列:{matrix1[:, 0:2]}")print("----------切片索引--------------")
print(f"隔行隔列选择前两行列:{matrix1[::2, ::2]}")print("------------负数索引------------")
print(f"最后一行:{matrix1[-1]}")
print(f"最后一列:{matrix1[:, -1]}")
print(f"最后一行的最后一列:{matrix1[-1, -1]}")print("----------使用列表或tensor作为索引-------------")
indies = torch.tensor([1, -1])
print(f"第二行和最后一行:{matrix1[indies]}")
print(f"选着一三列:{matrix1[:, [0, 2]]}")
print(f"第二行和最后一行:{matrix1[indies]}")
rows = torch.tensor([0, 1])
columns = torch.tensor([0, 1])
print(f"{matrix1[rows, columns]}")print("-------------布尔索引-------------------")
mask = matrix1 > 6
print(f"选择>6的元素:{matrix1[mask]}")
# 组合条件
mask = (matrix1 > 3) & (matrix1 < 8)
print(f"选择3到8之间的所有元素:{matrix1[mask]}")# 行或列的布尔索引
row_mask = torch.tensor([True, False, True])
print(f"选择第1和第3行:{matrix1[row_mask]}")print("----------- ... 表示任意数量的:-------------")
tensor_4d = torch.rand(3, 4, 5, 6)
print(tensor_4d[0, ...]) # 等价于tensor_4d[0, :, :, :]
print(tensor_4d[..., 0]) # 等价于tensor_4d[:, :, :, 0]
# 混合使用多种索引方式
print('-------混合使用多种索引方式----------')
print(f"前三行的1,2列:{matrix1[0:3, [0, 1]]}")
mask = matrix1[:, 0] > 3
print(f"第一列大于3的行:{matrix1[mask]}")
print(f"满足条件行的部分列:{matrix1[mask, 1:]}")
print(f"所有维度前两列:{matrix1[..., 0:2]}")print(f"---------------使用索引修改tensor的值------------")
matrix1[2][2] = 666
print(f"666:{matrix1}")
matrix1[1] = torch.zeros(3)
print(f"修改整行:{matrix1}")matrix1[matrix1 > 5] = 1
print(f"条件修改:{matrix1}")运行结果太多了就不展示了3,PyTorch张量形状操作
import torch
from numpy.array_api import reshape"""
Tensor的形状描述了其在每个维度上的元素数量,例如torch.Tensor(2,3,4) 表示他又两个二维数组,每个数组有三个一维列表,每个列表有四个标量
常用方法
tensor.shape与tensor.size() 都返回一个元组,表示Tensor的形状
torch.reshape()与torch.view() 改变tensor的形状
可以处理非连续的tensor,
tensor必须是连续的,如果tensor不连续会报错
"""
print(torch.Tensor(2, 3, 4))# 生成一个张量,形状为(2,3,4),元素从0到23
data_3d = torch.arange(24).reshape(2, 3, 4)# 两种方法都返回形状相同的元组shape_1 = data_3d.shape
size_1 = data_3d.size()
print(f"shape_1:{shape_1},\n size_1:{size_1}")print("--------------reshape和view重定形状--------------")
# reshape操作
reshaped = data_3d.reshape(3, 8)
viewed = data_3d.view(3, 8)
print(f"reshaped:{reshaped},\n viewed:{viewed}")# 注意
data_3d2 = data_3d.transpose(0, 1)
#不是连续的内容,会报错
viewed1 = data_3d2.view(3, 8)
print(f"不是连续内容:{viewed1}")
运行结果:
tensor([[[3.7653e-39, 7.6225e-39, 1.0102e-38, 9.3674e-39],
[1.0469e-38, 7.6225e-39, 6.9796e-39, 6.1531e-39],
[9.6429e-39, 1.0102e-38, 6.1531e-39, 1.0010e-38]],
[[1.0194e-38, 9.2755e-39, 1.0653e-38, 4.5001e-39],
[7.6225e-39, 1.0102e-38, 9.3674e-39, 1.0469e-38],
[9.0918e-39, 8.0817e-39, 4.7755e-39, 9.1836e-39]]])
shape_1:torch.Size([2, 3, 4]),
size_1:torch.Size([2, 3, 4])
--------------reshape和view重定形状--------------
reshaped:tensor([[ 0, 1, 2, 3, 4, 5, 6, 7],
[ 8, 9, 10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21, 22, 23]]),
viewed:tensor([[ 0, 1, 2, 3, 4, 5, 6, 7],
[ 8, 9, 10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21, 22, 23]])
Traceback (most recent call last):
File "D:\PycharmProjects\AIStudy\baizhanAI\深度学习_原理进阶\PyTorch\12_PyTorch-张量形状操作.py", line 32, in <module>
viewed1 = data_3d2.view(3, 8)
^^^^^^^^^^^^^^^^^^^
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
4,PyTorch张量升维与降维
import torch"""
常用方法
torch.unsqueeze() 用在指定位置加一个维度,通常用于增加批次维度
torch.squeeze() 用于移除大小为1的维度,可简化Tensor形状
"""
print("--------------------unsqueeze添加维度------------------")
x1 = torch.randn(3,4)
print(x1)
x1_new = x1.unsqueeze(0) #最前面加一个维度 [1,3,4]
print(x1_new)
x1_new2 = x1.unsqueeze(1) #第二个位置添加一个维度
print(x1_new2)
x1_new3 = x1.unsqueeze(-1) #最后1个位置添加一个维度[3,4,1]
print(x1_new3)print("-------------------删除一个维度-------------------")
x = torch.arange(6).reshape(1,2,3)
x_new1 = x.squeeze()
print(x_new1.shape)运行结果:
--------------------unsqueeze添加维度------------------
tensor([[-1.0873, -0.7961, -0.4153, 1.7513],
[-0.8825, 0.1612, -0.6903, 1.0875],
[ 0.1964, 0.6350, 0.0359, 1.1037]])
tensor([[[-1.0873, -0.7961, -0.4153, 1.7513],
[-0.8825, 0.1612, -0.6903, 1.0875],
[ 0.1964, 0.6350, 0.0359, 1.1037]]])
tensor([[[-1.0873, -0.7961, -0.4153, 1.7513]],
[[-0.8825, 0.1612, -0.6903, 1.0875]],
[[ 0.1964, 0.6350, 0.0359, 1.1037]]])
tensor([[[-1.0873],
[-0.7961],
[-0.4153],
[ 1.7513]],
[[-0.8825],
[ 0.1612],
[-0.6903],
[ 1.0875]],
[[ 0.1964],
[ 0.6350],
[ 0.0359],
[ 1.1037]]])
-------------------删除一个维度-------------------
torch.Size([2, 3])
5,PyTorch张量维度交换
import torch"""
torch.transpose() 和torch.permute 用于修改维度顺序。通常用于图像格式转换,序列处理
transpose只能交换两个维度
permute可以任意重排所有维度
"""x = torch.arange(24).reshape(2,3,4)print("----torch.transpose() 和torch.permute 用于修改维度顺序----")
print(f"x:{x.shape}")x_tran = x.transpose(0,1)
print(f"x_tran:{x_tran.shape}")x_per= x.permute(1,2,0) #dim1,dim2,dim0
print(f"x_per:{x_per.shape}")运行结果:----torch.transpose() 和torch.permute 用于修改维度顺序----
x:torch.Size([2, 3, 4])
x_tran:torch.Size([3, 2, 4])
x_per:torch.Size([3, 4, 2])
6,PyTorch 张量内存连续性
import torch"""
张量在内存默认是按维度顺序连续存储
torch.is_contiguous() 判断张量是否连续存储
torch.contiguous() 返回内存连续的新张量(若已连续则返回自身)
"""print("--------torch.is_contiguous()---------")
x = torch.arange(32).reshape(2,4,4)
x_tran = x.transpose(0,1)
print(f"是否连续:{x.is_contiguous()}")
print(f"交换维度后是否连续:{x_tran.is_contiguous()}")print("--------torch.contiguous()---------")
x_con = x_tran.contiguous()
print(f"处理后的连续性:{x_con.is_contiguous()}")运行结果:
--------torch.is_contiguous()---------
是否连续:True
交换维度后是否连续:False
--------torch.contiguous()---------
处理后的连续性:True
7,PyTorch 张量拼接
import torch"""
张量拼接就是将多个张量按照特定维度组合成一个更大的张量
为什么需要拼接操作:数据合并、特征融合、批处理等
实际应用场景:图像处理、自然语言处理、多模态融合等
torch.cat(tensors, dim=0, out=None) 别名 torch.concatenate()
tensors:要连接的张量序列(列表或元组)
dim:沿着哪个维度连接
out:输出张量(可选)注意:
拼接维度长度需要匹配
数据类型需要一致
设备一致(CPU / GPU)torch.stack(tensor,dim=0,out)
与cat的关键区别:创建新的维度进行堆叠
工作原理:沿着新维度堆叠张量
关键要求:所有张量的形状必须完全相同
"""# 设置随机种子
torch.manual_seed(100)
# 创建张量
a1 = torch.ones(2, 3)
b1 = torch.zeros(2, 3)
print("--------cat------")
print(f"a1:{a1} \n b1:{b1}")
# 沿着0和1维拼接
c1_dim0 = torch.cat((a1, b1), dim=0)
print(f"沿着0维拼接 c1_dim0: {c1_dim0}")
c1_dim1 = torch.cat((a1, b1), dim=1)
print(f"沿着1维拼接 c1_dim1: {c1_dim1}")print("--------stack------")c1_stack = torch.stack((a1, b1), dim=0)
print(f"stack拼接 c1_stack: {c1_stack} \n 新形状:{c1_stack.shape}")#指定加到第三个维度
c2_stack = torch.stack((a1, b1), dim=2)
print(f"stack拼接 c2_stack: {c2_stack} \n 新形状:{c2_stack.shape}")运行结果:
--------cat------
a1:tensor([[1., 1., 1.],
[1., 1., 1.]])
b1:tensor([[0., 0., 0.],
[0., 0., 0.]])
沿着0维拼接 c1_dim0: tensor([[1., 1., 1.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.]])
沿着1维拼接 c1_dim1: tensor([[1., 1., 1., 0., 0., 0.],
[1., 1., 1., 0., 0., 0.]])
--------stack------
stack拼接 c1_stack: tensor([[[1., 1., 1.],
[1., 1., 1.]],
[[0., 0., 0.],
[0., 0., 0.]]])
新形状:torch.Size([2, 2, 3])
stack拼接 c2_stack: tensor([[[1., 0.],
[1., 0.],
[1., 0.]],
[[1., 0.],
[1., 0.],
[1., 0.]]])
新形状:torch.Size([2, 3, 2])
8,PyTorch自动微分
import torch"""
自动微分模块可以自动梯度计算,不用手动推到和编码梯度,可以自动计算张量操作的导数
"""# # 简单的房价预测模型
# def predict_house_price(size, w, b): # w权重,b偏置
# return size * w + b
#
#
# # 假设实际数据
# real_price = 100
# house_size = 50
# w = 1.5
# b = 10
#
# # 预测价格
# predicted_price = predict_house_price(house_size, w, b)
# # 计算误差
# error = predicted_price - real_price
#
# print(f"误差:{error}")"""
自动微分可以自动计算复杂函数的导数,而不需要手动推导和编写导数计算代码
"""# 手动计算梯度
def manual_gradient_calculation():house_size = 50real_price = 100w = 1.5b = 10predicted_price = house_size * w + berror = predicted_price - real_price# 假设损失函数为平方误差 L = (predicted -real)^2dl_dw = 2 * error * house_size # 梯度计算dl/dwdl_db = 2 * error # 梯度计算:dL/db = 2*(预测值-真实值)return dl_dw, dl_db# PyTorch自动微分部分
def pytorch_autograd():house_size = torch.tensor([50.0])real_price = torch.tensor([100.0])w = torch.tensor([1.5], requires_grad=True)b = torch.tensor([10.0], requires_grad=True)predicted_price = house_size * w + bloss = torch.nn.MSELoss()(predicted_price, real_price)loss.backward()return w.grad.item(), b.grad.item() # 计算梯度# 验证结果
if __name__ == "__main__":# 手动计算梯度manual_dw, manual_db = manual_gradient_calculation()print(f"手动计算梯度: manual_dw:{manual_dw}, manual_db:{manual_db}")#自动计算梯度auto_dw,auto_db = pytorch_autograd()print(f"自动微分: auto_dw:{auto_dw}, auto_db:{auto_db}")运行结果:
手动计算梯度: manual_dw:-1500.0, manual_db:-30.0
自动微分: auto_dw:-1500.0, auto_db:-30.0
