后续语法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 \
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 \
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 = 50
real_price = 100
w = 1.5
b = 10
predicted_price = house_size * w + b
error = predicted_price - real_price
# 假设损失函数为平方误差 L = (predicted -real)^2
dl_dw = 2 * error * house_size # 梯度计算dl/dw
dl_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 + b
loss = 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