深度学习 pytorch的使用(张量1)
五、张量索引操作
简单行、列索引、列表索引、范围索引、布尔索引、多维索引
import torch # 数据 data = torch.randint(0,10,[4,5]) print(data)
tensor([[7, 6, 9, 4, 6],
[1, 9, 0, 9, 2],
[5, 7, 1, 7, 4],
[1, 2, 7, 2, 1]])"""1.简单行、列索引""" print(data[0]) # 第一行 print(data[:,0]) # 第一列
tensor([7, 6, 9, 4, 6])
tensor([7, 1, 5, 1])"""2.列表索引""" # 返回(0,1)、(1,2)两个位置的元素 print(data[[0,1],[1,2]])
tensor([6, 0])
# 返回0、1行的1、2列共4个元素 # print(data[[0],[1],[1,2]])
"""3.范围索引""" # 前3 行的前2列数据 print(data[:3,:2]) # 前2行到最后的前2列数据 print(data[2:,:2])
tensor([[7, 6],
[1, 9],
[5, 7]])
tensor([[5, 7],
[1, 2]])"""4.布尔索引""" # 第三列大于5的行数据 print(data[data[:,2]>5]) # 第二行大于5的列数据 print(data[:,data[1]>5])
tensor([[7, 6, 9, 4, 6],
[1, 2, 7, 2, 1]])
tensor([[6, 4],
[9, 9],
[7, 7],
[2, 2]])"""5.多维索引""" data = torch.randint(0,10,[3,4,5]) print(data) print('-'*50,'\n')print(data[0,:,:]) print(data[:,0,:]) print(data[:,:,0])
tensor([[[5, 5, 5, 9, 1],
[1, 5, 3, 8, 0],
[8, 3, 8, 2, 8],
[7, 2, 5, 0, 4]],[[4, 7, 2, 1, 3],
[9, 2, 7, 5, 2],
[0, 0, 4, 8, 7],
[3, 6, 6, 5, 3]],[[1, 7, 5, 5, 4],
[7, 8, 9, 5, 0],
[0, 0, 3, 5, 5],
[2, 0, 8, 3, 6]]])
--------------------------------------------------tensor([[5, 5, 5, 9, 1],
[1, 5, 3, 8, 0],
[8, 3, 8, 2, 8],
[7, 2, 5, 0, 4]])
tensor([[5, 5, 5, 9, 1],
[4, 7, 2, 1, 3],
[1, 7, 5, 5, 4]])
tensor([[5, 1, 8, 7],
[4, 9, 0, 3],
[1, 7, 0, 2]])
六、张量形状操作
1.reshape 函数可以在保证张量数据不变的前提下改变数据的维度 2.transpose 函数可以实现交换张量形状的指定维度,permute可以一次交换更多的维度 3.view 函数也可以用于修改张量的形状,但是它要求被转换的张量内存必须连续,所以易班配合contiguous函数使用 4.squeeze 和 unsqueeze 函数可以用来增加或者减少维度
1、reshape 函数的用法
reshape 函数可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状,在神经网络学习时,会经常使用该函数来调节数据的形状,以适配不同网络层之间的数据传递
import torchdata = torch.tensor([[10,20,30],[40,50,60]]) print(data) # tensor([[10, 20, 30], # [40, 50, 60]])# 1.使用shape属性或者size方法都可以获得张量的形状 print(data.shape,data.shape[0],data.shape[1]) # torch.Size([2, 3]) 2 3 print(data.size(),data.size(0),data.size(1)) # torch.Size([2, 3]) 2 3# 2.使用reshape函数修改张量形状 new_data = data.reshape(1,6) print(new_data) print(new_data.shape) # tensor([[10, 20, 30, 40, 50, 60]]) # torch.Size([1, 6])
2、transpose 和 permute 函数的使用
transpose 函数可以实现交换张量形状的指定维度, 例如: 一个张量的形状为 (2, 3, 4) 可以通过 transpose 函数把 3 和 4 进行交换, 将张量的形状变为 (2, 4, 3) permute 函数可以一次交换更多的维度
import torch import numpy as npdata = torch.tensor(np.random.randint(0, 10, [3, 4, 5])) print('data shape:', data.size()) # data shape: torch.Size([3, 4, 5])# 1. 交换1和2维度 new_data = torch.transpose(data, 1, 2) print('data shape:', new_data.size()) # data shape: torch.Size([3, 5, 4])# 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()) # new_data shape: torch.Size([4, 5, 3])# 3. 使用 permute 函数将形状修改为 (4, 5, 3) new_data = torch.permute(data, [1, 2, 0]) print('new_data shape:', new_data.size()) # new_data shape: torch.Size([4, 5, 3])
3、view 和 contigous 函数的用法
view 函数也可以用于修改张量的形状,但是其用法比较局限,只能用于存储在整块内存中的张量。 在 PyTorch 中,有些张量是由不同的数据块组成的,它们并没有存储在整块的内存中, view 函数无法对这样的张量进行变形处理,例如: 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。
import torch data = torch.tensor([[10, 20, 30], [40, 50, 60]]) print('data shape:', data.size()) # data shape: torch.Size([2, 3])# 1. 使用 view 函数修改形状 new_data = data.view(3, 2) print('new_data shape:', new_data.shape) # new_data shape: torch.Size([3, 2])# 2. 判断张量是否使用整块内存 print('data:', data.is_contiguous()) # True # data: 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()) # True new_data = new_data.contiguous().view(2, 3) print('new_data shape:', new_data.shape) # new_data shape: torch.Size([2, 3])
4、 squeeze 和 unsqueeze 函数的用法
import torch import numpy as npdata = 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])
七、张量运算函数
1、常见运算函数
PyTorch 为每个张量封装很多实用的计算函数,例如计算均值、平方根、求和等等
import torchdef 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([[7., 5., 9.],
[6., 1., 6.]], dtype=torch.float64)
--------------------------------------------------
tensor(5.6667, dtype=torch.float64)
tensor([6.5000, 3.0000, 7.5000], dtype=torch.float64)
tensor([7.0000, 4.3333], dtype=torch.float64)
--------------------------------------------------
tensor(34., dtype=torch.float64)
tensor([13., 6., 15.], dtype=torch.float64)
tensor([21., 13.], dtype=torch.float64)
--------------------------------------------------
tensor([[49., 25., 81.],
[36., 1., 36.]], dtype=torch.float64)
--------------------------------------------------
tensor([[2.6458, 2.2361, 3.0000],
[2.4495, 1.0000, 2.4495]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.0966e+03, 1.4841e+02, 8.1031e+03],
[4.0343e+02, 2.7183e+00, 4.0343e+02]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.9459, 1.6094, 2.1972],
[1.7918, 0.0000, 1.7918]], dtype=torch.float64)
tensor([[2.8074, 2.3219, 3.1699],
[2.5850, 0.0000, 2.5850]], dtype=torch.float64)
tensor([[0.8451, 0.6990, 0.9542],
[0.7782, 0.0000, 0.7782]], dtype=torch.float64)
八、自动微分模块
自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。 自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中, Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新pytorch 中非常重要的自动微分模型的使用和理解 对需要计算梯度的张量需要设置requires_grad=True属性, 并且需要注意的是梯度是累计的,在每次计算梯度前需要进行梯度清零。
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)# 3. 多标量梯度计算 # y = x1 ** 2 + x2 ** 2 + x1*x2 def test03():# 定义需要计算梯度的张量x1 = torch.tensor(10, requires_grad=True, dtype=torch.float64)x2 = torch.tensor(20, requires_grad=True, dtype=torch.float64)# 经过中间的计算y = x1**2 + x2**2 + x1*x2# 将输出结果变为标量y = y.sum()# 自动微分y.backward()# 打印两个变量的梯度print(x1.grad, x2.grad)# 4. 多向量梯度计算 def test04():# 定义需要计算梯度的张量x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)x2 = torch.tensor([30, 40], requires_grad=True, dtype=torch.float64)# 经过中间的计算y = x1 ** 2 + x2 ** 2 + x1 * x2print(y)# 将输出结果变为标量y = y.sum()# 自动微分y.backward()# 打印两个变量的梯度print(x1.grad, x2.grad)if __name__ == '__main__':test04()
tensor(20., dtype=torch.float64) tensor([ 5., 10., 15., 20.], dtype=torch.float64) tensor(40., dtype=torch.float64) tensor(50., dtype=torch.float64) tensor([1300., 2800.], dtype=torch.float64, grad_fn=<AddBackward0>) tensor([50., 80.], dtype=torch.float64) tensor([ 70., 100.], 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 ** 2print(y.requires_grad)# 第二种方式: 对函数进行装饰@torch.no_grad()def my_func(x):return x ** 2print(my_func(x).requires_grad)# 第三种方式torch.set_grad_enabled(False)y = x ** 2print(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 + 20f2 = f1.mean()# 默认张量的 grad 属性会累计历史梯度值# 所以, 需要我们每次手动清理上次的梯度# 注意: 一开始梯度不存在, 需要做判断if x.grad is not None:x.grad.data.zero_()f2.backward()print(x.grad)# 3. 梯度下降优化最优解 def test03():# y = x**2x = 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.gradprint('%.10f' % x.data)if __name__ == '__main__':test01()test02()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 140421811165776print(id(x1.data), id(x2.data))x2.data = torch.tensor([100, 200])print(x1)print(x2)# x2 不会自动计算梯度: Falseprint(x2.requires_grad)if __name__ == '__main__':test01()test02()
[10. 20.]
1865743596496 1865743596496
tensor([10., 20.], dtype=torch.float64, requires_grad=True)
tensor([100, 200])
False