Pytorch学习-张量和Autograd

本文介绍了PyTorch中张量的创建、类型转换、属性查询、索引切片、连接操作以及各种数学运算,包括加减乘除、矩阵乘法和按元素运算。此外,还详细讲解了Autograd如何支持自动求导,用于神经网络的反向传播计算参数梯度。
摘要由CSDN通过智能技术生成

张量

先行操作:导入相关包

import torch
import numpy as np

张量的初始化

使用列表和numpy数组对张量进行初始化

data=[[1,2],[3,4]]  #list 内有两个元素,每个元素是一个内含两个元素的列表
np_array=np.array(data)  #numpy数组,一个二维数组 2x2
t=torch.tensor(data)   #用列表进行初始化
tensor_from_np=torch.from_numpy(np_array) #用numpy数组初始化张量

#类型转换
np_from_tensor = t.numpy()  # 将Tensor对象转换为NumPy数组
listdata = tensor_from_np.tolist()  # 将Tensor对象转换为list数组
print(t, '\n', np_from_tensor, '\n', listdata)

张量间进行初始化

tensor_data = t.clone()  # 返回与t值相同的tensor,新对象存储在新的内存中
new_data = t.detach()  # 返回与t完全相同的tensor,新对象与旧对象t共享内存
ones_data = torch.ones_like(t)  # 和t形状一致的全1张量
zeros_data = torch.zeros_like(t)  # 和t形状一致的全0张量
rand_data = torch.rand_like(t, dtype=torch.float)  # 和t形状一致的随机浮点数张量

张量的属性

张量的属性描述了其形状、数据类型、存储设备及在内存中的存储形式等信息。

tensor = torch.rand(3,4)  # 3x4的随机tensor
print(f"张量形状: {tensor.shape}")
print(f"张量数据类型: {tensor.dtype}")
print(f"张量存储设备: {tensor.device}")

Tensor也可存储的GPU上进行加速运算,例如:

device = "cuda" if torch.cuda.is_available() else "cpu" #判断是否可用CUDA加速运算
print(device)
tensor = tensor.to(device) #将Tensor放到GPU上
print(f"张量存储设备: {tensor.device}")

张量的索引和切片

张量的索引和切片类似NumPy的索引和切片。示例如下:

tensor = torch.randint(1,100,(4, 4))  #4行4列,各元素值为[1,100)区间的随机数
print(f"第一行: {tensor[0]}")
print(f"第一列: {tensor[:, 0]}")
print(f"最后一列: {tensor[..., -1]}")  # ...和:是一样的
tensor[:, 1] = 0  # 第二列置为0
print(tensor)

张量间的连接

tensor1 = torch.randint(1,100,(4, 4))
tensor2 = torch.rand(3,4) 
tensor3 = torch.rand(4,2) 
t1 = torch.cat([tensor1, tensor2], dim=0)  # 纵向连接
t2 = torch.cat((tensor1, tensor3), dim=1)  # 横向连接,用()和[]均可
print(tensor1.shape, tensor2.shape, tensor3.shape, t1.shape, t2.shape)

torch.Size([4, 4]) torch.Size([3, 4]) torch.Size([4, 2]) torch.Size([7, 4]) torch.Size([4, 6])
除了torch.cat函数外,torch.stack函数也可以进行连接,不过各个被链接的张量的各个维度值都应一致,返回的新张量维度会多增加一维。示例如下:

tensor1 = torch.randint(1,100,(2, 4))
tensor2 = torch.zeros(2,4) 
tensor3 = torch.ones(2,4) 
t1 = torch.stack([tensor1, tensor2, tensor3],dim=0)  #dim=0:在维度0处进行拼接
t2 = torch.stack((tensor1, tensor2, tensor3),dim=1)
t3 = torch.stack((tensor1, tensor2, tensor3),dim=2)
print(tensor1.shape, tensor2.shape, tensor3.shape, t1.shape, t2.shape, t3.shape)
t1,t2,t3

t1,t2,t3张量均从二维张量转变成三维张量

张量的运算

张量作为矩阵,可以进行加、减、乘、转置、按元素乘、按元素除等操作。

张量的加法运算

tensor1 = torch.randint(1,100,(2, 4))
tensor2 = torch.ones(2,4) 
t_add1 = tensor1 + tensor2  # 张量加法,本质上是按元素相加
t_add2 = tensor1.add(tensor2)  # 与上面的操作是一致的
t_add3 = torch.add(tensor1, tensor2)  # 与上面的操作是一致的

t_add4 = tensor1 + 3  # 张量所有元素都加3,得到新的张量,原张量未改变
t_add5 = tensor1.add(3)  # 与上面的操作是一致的
t_add6 = torch.add(tensor1, 3)  # 与上面的操作是一致的

t_add7 = tensor1.add_(3)  # 张量所有元素都加3,原张量tensor1也被修改。

张量的减法运算

t_sub1 = tensor1 - tensor2  # 张量减法
t_sub2 = tensor1.sub(tensor2)  # 与上面的操作是一致的
t_sub4 = 1 - tensor2  # 张量所有元素都被1减
t_sub6 = torch.sub(tensor1, 1)  # 张量所有元素都减1

张量的乘法(矩阵间的乘法,结果仍然是矩阵)

#张量乘法,参与运算的张量类型需一致
t_matmul1 = tensor1.float() @ tensor2.T  # T属性表示张量转置,按单精度浮点数(32bit)进行相乘
t_matmul2 = tensor1.matmul(tensor2.T.long())  # 与上面的操作不同,两张量按长整型(64bit)进行相乘

按元素相乘/除

#按元素相乘,参加运算的张量形状应一致
t_mul1 = tensor1 * tensor2  
t_mul3 = torch.mul(tensor1, tensor2)  # 与上面的操作是一致的
t_mul4 = tensor1 * 3  # 张量每元素都乘以3
t_mul5 = tensor1.mul(3)  # 与上面的操作是一致的

#按元素相除,参加运算的张量形状应一致
t_div1 = tensor1 / tensor1  
t_div2 = tensor1.div(tensor2) 
t_div3 = torch.div(tensor1, tensor2)  # 与上面的操作是一致的

pytorch中内置的函数,如dot,mm

t1=torch.randn((5))  # 一维张量
t2=torch.ones((5))  # 一维张量
t3=torch.randn((2,5))  # 二维张量
t4=torch.ones((5,2))  # 二维张量
t_d1 = torch.dot(t1, t2)  # dot函数仅支持两个一维向量的点集
t_d2 = torch.matmul(t1, t2)  # 与上面的操作是一致的
print(t_d1 == t_d2)  # 观察两个结果是否相同

t_m1 = torch.mm(t3, t4)  # mm函数仅支持两个二维张量的相乘
t_m2 = torch.matmul(t3, t4)  # 与上面的操作一致
print(t_m1 == t_m2)  # 观察两个结果是否相同

t_n1 = torch.matmul(t3, t1) # 二维张量和一维张量相乘,一维张量自动维度扩展, 结果会删掉扩展维度
print(t_n1.shape)  # 打印计算结果形状
t_n2 = torch.matmul(t3, t1.view(5,1)).T # 手动扩展进行计算,与上面的操作结果一致
print(t_n1 == t_n2)  # 观察两个结果是否相同

若要取张量中的某个元素,变为普通的数据类型进行运算,可使用item()函数。

tensor = torch.randperm(10)
sum = tensor.sum()
sum_item = sum.item()
print(tensor)
print(sum, type(sum))
print(sum_item, type(sum_item))

张量的其他运算

PyTorch提供的张量运算函数是非常丰富的,如幂运算、指数运算(以e为底数)、对数运算、近似运算(如取整)、统计运算(如取平均值)等等,在此不再给出示例,只列举一些常用的函数,读者可自行练习这些函数的使用方法。

Autograd自动求导

神经网络模型训练时,最常用的是BP算法,即反向传播算法。模型参数根据梯度进行学习,为了计算梯度,PyTorch提供了Autograd支持自动求导来计算梯度。 下面以最简单的一层神经网络为例,给出Autograd进行反向传播优化参数的示例。例子中,设x和y是真实数据,z为模型预测值,设z = w * x + b,则w和b是模型需要优化的参数,通过loss损失可根据梯度下降法来优化w和b。

import torch
from torch.nn.functional import binary_cross_entropy_with_logits
x = torch.ones(5)  # 输入x设为[1.,1.,1.,1.,1.]
# print('x',x.shape)
y = torch.zeros(3)  # 输出y设为[0.,0.,0.]
w = torch.randn(5, 3, requires_grad=True)  # w形状为[5, 3],梯度计算设为True
# print('w',w.shape)
b = torch.randn(3, requires_grad=True)  # b形状为[1, 3],梯度计算设为True
z = torch.matmul(x, w) + b  # z = w * x + b
loss = binary_cross_entropy_with_logits(z, y)  # 计算损失根据loss来优化网络参数
loss.backward()  # 损失反向传播进行自动求导,得到参数梯度
print(w.grad)  # 输出w的梯度,存储在w的grad属性中,grad属性也是张量
print(b.grad)  # 输出b的梯度

loss.backward() 返回的是 None。它的作用是进行反向传播计算梯度,并将计算得到的参数梯度存储在对应的张量的 grad 属性中。具体来说,w.grad 和 b.grad 分别表示损失函数关于权重矩阵 w 和偏置向量 b 的梯度值。

当调用 loss.backward() 方法时,PyTorch 会自动计算每个参数的梯度,并将其存储在对应的 .grad 属性中。需要注意的是,每次调用 backward() 方法时,计算得到的梯度会累加到之前存储的梯度上,因此如果需要每次单独计算梯度,需要通过 zero_grad() 方法将之前的梯度清零。

需要注意的是,在调用 backward() 方法之前,必须保证相关的张量都设置了 requires_grad=True,才能够计算出相应张量的梯度。

参数梯度算法的意义

参数梯度是指在机器学习算法中,用于调整模型参数的一种计算方法。在训练模型时,我们通常需要通过最小化损失函数来优化模型的参数。而参数梯度就是指损失函数对于模型参数的偏导数(即梯度)

通过计算参数梯度,我们可以确定当前参数取值下,损失函数下降最快的方向,并据此更新参数,使得损失函数的值逐渐减小,从而使模型更加拟合训练数据。在深度学习中,参数梯度通常使用反向传播算法来计算。
损失函数下降最快的方向即在某一个变量上的偏导数是最大的。即该变量大小变化一点,相较于其他变量,损失函数的变化最大。

停止梯度跟踪

默认情况下,所有张量的requires_grad属性被设置为True,表示都在跟踪梯度历史,但是有些情况下并不需要跟踪梯度,比如测试集上的评估就不需要反向传播来跟踪梯度,可用以下代码来停止对计算结果的梯度跟踪,
即无需调用bp函数,即使有输入也不更改权值

第一种方法with torch.no_grad()

z = torch.matmul(x, w)+b  # z = w * x + b
print(z.requires_grad)     #True
with torch.no_grad():  # 禁用梯度跟踪
    z = torch.matmul(x, w)+b
print(z.requires_grad)    #False

第二种方法

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值