目的
- 本专栏更新深度学习基础,包括pytorch、tensorflow的使用以及CNN、RNN、yolo、GAN、LSTM、Transformer等神经网络的理论基础
前言
- Pytorch是最常用的深度学习框架,里面包含了大量关于构建神经网络及其运算的API
- Pytorch基础入门分为上下两篇文章,此为上
- 注意:Pytorch的学习在学完基础后,对Pytorch有一定了解后,一定要结合实践进行学习,光学一堆API函数没有任何意义
- 欢迎点赞 + 收藏 + 关注
Pytroch入门11的专题思维导图如下
1、张量的创建
学习目标
- 掌握张量的创建
- 张量的简介
在Pytorch中,数据都是封装成张量进行运算的,张量是同一种元素类型的多维矩阵
1、基本创建方法
- torch.tensor 根据数据创建张量
- torch.Tensor 根据形状创建张量,也可以创建指定数据的张量
- torch.IntTensor、torch.FloatTensor、torch.DoubleTensor等 创建指定类型的张量
import torch
import numpy as np
import random
# 1. 根据数据创建张量
def test01():
# 创建标量张量
data = torch.tensor(10)
print(data) # 输出 tensor(10)
# 其他数据转 tensor
data = np.random.randn(2, 3) # 生成(2,3)矩阵(0~1)
data = torch.tensor(data)
print(data) #数据类型 torch.float64
# 列表
data = [[10, 20, 30], [40, 50, 60]]
data = torch.tensor(data)
print(data)
# 2. 指定形状创建张量
def test02():
# (2,3)的张量,**默认类型为float**
data = torch.Tensor(2,3)
print(data)
# 传递列表,则就只创建传递列表的张量
data = torch.Tensor([10])
print(data) # print: tensor([10.])
data = torch.Tensor([10, 20])
print(data)
# 3. 创建指定类型的张量
def test03():
# int
data = torch.IntTensor(2,3)
print(data)
# 注意:如果传递元素类型不一致,会强制转换
data = torch.IntTensor([2.0, 3.5, 4.8])
print(data) # pytorch,如何创建指定类型的张量
# 其他类型
data = torch.FloatTensor() # float32
data = torch.DoubleTensor() # double64
data = torch.ShortTensor() # int16
data = torch.LongTensor() # int32
# 注意:没有orch.LongLongTensor()类型
if __name__ == "__main__":
test03()
tensor([[-549255968, 516, 0],
[ 0, 0, 0]], dtype=torch.int32)
tensor([2, 3, 4], dtype=torch.int32)
2、创建线性张量
- torch.arange和torch.linspace创建线性张量
- torch.random.init_seed和torch.random.manual_seed随机种子设置
- torch.randn 创建随机张量
import torch
# 1、创建线性空间的张量
def test01():
# 1. 在指定的区间按照步长生成元素 [step, end, step)
data = torch.arange(0, 10, 2)
print(data)
# 2. 在指定区间按照元素个数生成
data = torch.linspace(0, 11, 10)
print(data)
# 2、创建随机张量
def test02():
# 1. 创建随机张量
data = torch.randn(2, 3) # 创建2行3列的张量
print(data)
# 2. 随机数种子的设置
print('随机数种子: ', torch.random.initial_seed())
torch.random.manual_seed(100)
print('随机数种子:', torch.random.initial_seed())
if __name__ == '__main__':
test02()
tensor([[0.2001, 0.6427, 1.1013],
[0.3320, 0.2964, 0.6062]])
随机数种子: 46007482472300
随机数种子: 100
3、创建01张量
- torch.ones和torch.ones_like创建全1张量
- torch.zeros和torch.zeros_like创建全0张量
- torch.full和torch.full_like创建全指定值的张量
import torch
# 1、创建全0张量
def test01():
# 1. 创建指定形状的全0张量
data = torch.zeros(2,3)
print(data)
# 2. 根据**张量形状**创建全0张量
data = torch.zeros_like(data)
print(data)
# 2、创建全1张量
def test02():
# 1、创建指定形状全1张量
data = torch.ones(2,3)
print(data)
# 根据**张量形状**创建全1张量
data = torch.ones_like(data)
print(data)
# 3、创建全是指定数的张量
def test03():
# 1. 创建指定形状和值的张量
data = torch.full([2, 3], 8)
print(data)
# 2. 更具张量形状创建指定值的张量
data = torch.full_like(data, 20)
print(data)
if __name__ == '__main__':
test03()
tensor([[8, 8, 8],
[8, 8, 8]])
tensor([[20, 20, 20],
[20, 20, 20]])
4、张量元素类型转换
import torch
def test():
data = torch.full([2,3], 8)
print(data.dtype)
# 转化为 float类型
# 1、第一种方法
data = data.type(torch.DoubleTensor)
print(data.dtype)
# 2、第二种方法
data = data.double()
print(data.dtype)
if __name__ == '__main__':
test()
torch.int64
torch.float64
torch.float64
2、张量的数值计算
1、张量基本运算
基本运算常用api:
- add、sub、mul、div、neg
- add_、sub_、mul_、neg_ 这些版本为修改原数据
import numpy as np
import torch
def test():
data = torch.randint(0, 10, [2, 3])
print(data)
print('-' * 50)
# 1、不修改原数据
new_data = data.add(50)
print(new_data)
print('-' * 50)
# 2、修改原数据
data.add_(50)
print(data)
print(data.neg()) # 取反
if __name__ == '__main__':
test()
tensor([[6, 1, 7],
[8, 2, 0]])
--------------------------------------------------
tensor([[56, 51, 57],
[58, 52, 50]])
--------------------------------------------------
tensor([[56, 51, 57],
[58, 52, 50]])
tensor([[-56, -51, -57],
[-58, -52, -50]])
2、阿达玛积
指矩阵对应元素相乘
import torch
def test():
data1 = torch.tensor([[1, 2], [3, 4]])
data2 = torch.tensor([[5, 6], [7, 8]])
#1、第一种方法,注意:需要遵循矩阵运算规则
new_data = torch.mul(data1, data2)
print(new_data)
#2、第二种方法
new_data = data1 * data2
print(new_data)
if __name__ == '__main__':
test()
tensor([[ 5, 12],
[21, 32]])
tensor([[ 5, 12],
[21, 32]])
3、矩阵相乘(点积运算)
点积运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。
- 运算符 @
- torch.mm 用于两维矩阵
- torch.bmm 用于三维矩阵
- torch.matmul 用于多维
import numpy as np
import torch
#1 点积运算
def test01():
data1 = torch.tensor([[1, 2], [3, 4], [5, 6]])
data2 = torch.tensor([[5, 6], [7, 8]])
#1 第一种
data = data1 @ data2
print(data)
# 2 第二种
data = torch.mm(data1, data2)
print(data)
# 第三种
data = torch.matmul(data1, data2)
print(data)
#2、torch.mm 和 torch.matmul区别
def test02():
# torch.matmul维度可以不同
print(torch.matmul(torch.randn(2, 4, 5), torch.randn(5, 4)).shape) # 三维 * 两维
#3、torch.bmm 使用
def test03():
# .bmm 批量矩阵相乘
# 第一个参数为批量次数,必须一致
data1 = torch.randn(2, 4, 5) # 相当于第一个 [4, 5] * [5, 8], 第二个[4, 5] * [5, 8] 相当于分开乘
data2 = torch.randn(2, 5, 8)
print(torch.bmm(data1, data2))
if __name__ == "__main__":
test03()
tensor([[[-1.5538, 1.2162, 1.5792, -2.3072, -3.2645, -0.3950, 1.0962,
2.7147],
[ 1.6430, 1.5596, -1.4857, -1.0365, -0.7418, 0.2916, -0.8140,
0.3571],
[ 1.4194, 2.2233, -1.6982, 1.8784, 2.2567, 1.5027, -1.8993,
-7.0435],
[ 1.0284, -1.7581, -4.5965, -3.3414, -2.5268, 1.3937, -4.9438,
-1.8333]],
[[-1.3526, -1.6785, -0.0365, -0.0763, -1.1699, -0.8849, -1.3254,
-0.9677],
[-1.0104, -0.2635, -0.5428, -0.6463, 0.4798, -0.2916, -0.2097,
0.1552],
[ 0.6113, 2.0732, -1.1052, -2.5615, 4.2304, -0.8377, 0.4031,
1.2656],
[ 1.0027, 1.0019, 0.5648, -0.9055, 1.8673, -0.6666, -0.0486,
1.0447]]])
4、指定运算设备
PyTorch 默认会将张量创建在 CPU 控制的内存中, 即: 默认的运算设备为 CPU。我们也可以将张量创建在 GPU 上, 能够利用对于矩阵计算的优势加快模型训练。将张量移动到 GPU 上有三种方法:
- 使用 cuda 方法
- 直接在 GPU 上创建张量
- 使用 to 方法指定设备
import torch
# 1. 使用 cuda 方法
def test01():
data = torch.tensor([10, 20 ,30])
print('存储设备:', data.device)
# 注意要下载cuda
data = data.cuda()
print('存储设备:', data.device)
# 使用 cpu 函数将张量移动到 cpu 上
data = data.cpu()
print('存储设备:', data.device)
# 2. 直接将张量创建在 GPU 上
def test02():
data = torch.tensor([10, 20, 30], device='cuda:0')
print('存储设备:', data.device)
# 使用 cpu 函数将张量移动到 cpu 上
data = data.cpu()
print('存储设备:', data.device)
# 3. 使用 to 方法,常用*****
def test03():
data = torch.tensor([10, 20, 30])
print('存储设备:', data.device)
data = data.to('cuda:0')
print('存储设备:', data.device)
# 4. 存储在不同设备的张量不能运算
def test04():
data1 = torch.tensor([10, 20, 30], device='cuda:0')
data2 = torch.tensor([10, 20, 30])
print(data1.device, data2.device)
data = data1 + data2
print(data)
# 这样会报错
if __name__ == '__main__':
test03()
存储设备: cpu
存储设备: cuda:0
3、张量类型转换
1、张量转换为numpy数组
使用Tensor.numpy函数可以将张量转换为ndarray数组,但是共享内存,可以使用copy函数避免共享。
import torch
# 1、将张量转换为numpy数组
def test01():
data_tensor = torch.tensor([2, 3, 4])
# 转换为numpy数组
data_numpy = data_tensor.numpy()
print(type(data_tensor))
print(type(data_numpy))
# 注意:: data_tensor 和 data_tensor 共享内存
data_numpy[0] = 100
print(data_numpy)
print(data_tensor)
#2 对象拷贝避免共享内存
def test02():
data_tensor = torch.tensor([2, 3, 4])
data_numpy = data_tensor.numpy().copy() # 拷贝
print(type(data_tensor))
print(type(data_numpy))
# 修改
data_numpy[0] = 100
print(data_numpy)
print(data_tensor)
if __name__ == '__main__':
test02()
<class 'torch.Tensor'>
<class 'numpy.ndarray'>
[100 3 4]
tensor([2, 3, 4])
2、numpy转张量
- 使用from_numpy可以将ndarray数组转换为Tensor,默认共享内存,使用copy函数避免共享
- 使用torch.tensor可以将ndarry数组转换为Tensor,默认不共享内存
import numpy as np
import torch
# 1、使用 from_numpy 函数
def test01():
data_numpy = np.array([2, 3, 4])
# 浅拷贝
data_tensor = torch.from_numpy(data_numpy)
# 修改
data_numpy[0] = 100
print(data_numpy)
print(data_tensor)
# 2、使用 tensor.tensor 函数
def test02():
data_numpy = np.array([2, 3, 4])
data_tensor = torch.tensor(data_numpy)
data_numpy[0] = 100
print(data_numpy)
print(data_tensor)
if __name__ == '__main__':
test02()
[100 3 4]
tensor([2, 3, 4], dtype=torch.int32)
3、标量张量和数字的转换
对于只有一个元素的张量,使用item方法将该值从张量中提取出来
# 3 标量张量和数字转换
def test03():
# 当张量只包含一个元素时,可以通过 item 函数提取出该值
data = torch.tensor([30,])
print(data.item())
print(type(data))
data = torch.tensor(30)
print(data.item())
print(type(data))
if __name__ == '__main__':
test03()
30
<class 'torch.Tensor'>
30
<class 'torch.Tensor'>
4、张量的拼接
1、torch.cat函数的使用
torch.cat函数可以按照指定的维度拼接起来
import torch
def test():
# 随机初始化数据
data1 = torch.randint(0, 10, [2, 3, 2])
data2 = torch.randint(0, 10, [2, 3, 2])
print(data1)
print(data2)
print('*' * 50)
# 1、按照0维度进行拼接
new_data = torch.cat([data1, data2], dim=0)
print(new_data.shape)
print('*' * 50)
# 2、按照1维进行拼接
new_data = torch.cat([data1, data2], dim=1)
print(new_data.shape)
print('*' * 50)
# 3、按照2维进行拼接
new_data = torch.cat([data1, data2], dim=2)
print(new_data.shape)
print(new_data)
if __name__ == '__main__':
test()
tensor([[[7, 7],
[4, 4],
[7, 0]],
[[8, 7],
[8, 7],
[1, 3]]])
tensor([[[8, 8],
[4, 1],
[0, 6]],
[[0, 8],
[3, 7],
[4, 2]]])
**************************************************
torch.Size([4, 3, 2])
**************************************************
torch.Size([2, 6, 2])
**************************************************
torch.Size([2, 3, 4])
tensor([[[7, 7, 8, 8],
[4, 4, 4, 1],
[7, 0, 0, 6]],
[[8, 7, 0, 8],
[8, 7, 3, 7],
[1, 3, 4, 2]]])
2、torch.stack函数的使用
torch.stack 函数可以将两个张量根据指定的维度叠加起来
import torch
def test():
data1 = torch.randint(0, 10, [2, 3])
data2 = torch.randint(0, 10, [2, 3])
print(data1)
print(data2)
new_data = torch.stack([data1, data2], dim=0)
print(new_data)
print('*' * 50)
new_data = torch.stack([data1, data2], dim=1)
print(new_data)
print('*' * 50)
new_data = torch.stack([data1, data2], dim=2)
print(new_data)
if __name__ == '__main__':
test()
tensor([[3, 0, 8],
[4, 4, 9]])
tensor([[4, 7, 8],
[6, 5, 1]])
tensor([[[3, 0, 8],
[4, 4, 9]],
[[4, 7, 8],
[6, 5, 1]]])
**************************************************
tensor([[[3, 0, 8],
[4, 7, 8]],
[[4, 4, 9],
[6, 5, 1]]])
**************************************************
tensor([[[3, 4],
[0, 7],
[8, 8]],
[[4, 6],
[4, 5],
[9, 1]]])
5、张量索引操作
准备数据
import torch
# 准备数据
data = torch.randint(0, 10, [4, 5])
print(data)
print('*' * 50)
tensor([[9, 7, 4, 0, 8],
[8, 1, 9, 9, 0],
[7, 9, 7, 0, 8],
[9, 4, 5, 4, 1]])
**************************************************
1、简单行、列索引
print(data[0]) # 第零行
print(data[:, 0]) # 第零列
print('*' * 50)
tensor([9, 7, 4, 0, 8])
tensor([9, 8, 7, 9])
**************************************************
2、列表索引
# 输出 (0, 1) 、(1, 2) 两个位置的元素
print(data[[0, 1], [1, 2]])
print(data[0, 1], data[1, 2])
# 输出 0、1 行的 1、 2列的四个元素
print(data[[[0], [1]], [1, 2]]) # 注意括号位置
tensor([7, 9])
tensor(7) tensor(9)
tensor([[7, 4],
[1, 9]])
3、范围索引
# 输出前三行前两列的数据
print(data[:3, :2]) # 行为先
# 输出第二行到z前两列的数据
print(data[2, :2])
tensor([[9, 7],
[8, 1],
[7, 9]])
tensor([7, 9])
4、布尔索引
# 输出第三列大于5的行数据
print(data[data[:, 2] > 5]) # data[:, 2] > 5 创建了一个布尔数组(也称为掩码)
# 输出第二行大于5的列数据
#print(data[data[1, :] > 5]) # 注意:这样写违反了 行为先 的规则
print(data[1, data[1] > 5])
tensor([[8, 1, 9, 9, 0],
[7, 9, 7, 0, 8]])
tensor([8, 9, 9])
5、多维数组索引
# 一层一层解析
import torch
data = torch.randint(0, 10, [3, 4, 5])
print(data)
print('*' * 50)
print(data[0, :, :])
print(data[:, 0, :])
print(data[:, :, 0])
tensor([[[5, 5, 9, 7, 9],
[9, 5, 0, 8, 5],
[0, 7, 7, 4, 1],
[3, 8, 5, 5, 3]],
[[5, 3, 8, 4, 8],
[0, 7, 7, 8, 2],
[7, 0, 0, 8, 0],
[7, 5, 7, 7, 3]],
[[6, 7, 5, 6, 4],
[5, 1, 2, 6, 7],
[5, 1, 0, 4, 2],
[3, 0, 0, 7, 7]]])
**************************************************
tensor([[5, 5, 9, 7, 9],
[9, 5, 0, 8, 5],
[0, 7, 7, 4, 1],
[3, 8, 5, 5, 3]])
tensor([[5, 5, 9, 7, 9],
[5, 3, 8, 4, 8],
[6, 7, 5, 6, 4]])
tensor([[5, 9, 0, 3],
[5, 0, 7, 7],
[6, 5, 5, 3]])
6、张量形状操作
1、reshape函数的用法
reshape函数可以保证张量数据不变的情况下提前改变数据的维度,将其转换成指定的形状,在后面的神经网络学习中,会经常使用该函数来调整数据的形状,用来适配不同网络层之间的数据传递。
import torch
import numpy as np
def test():
data = torch.tensor([[10, 20, 30], [40, 50, 60]])
# 1、获取张量的形状
print(data.shape, data.shape[0], data.shape[1])
print(data.size(), data.size(0), data.size(1))
print('*' * 50)
# 2、使用 reshape 函数修改张量的形状
new_data = data.reshape(1, 6)
print(new_data)
if __name__ == '__main__':
test()
torch.Size([2, 3]) 2 3
torch.Size([2, 3]) 2 3
**************************************************
tensor([[10, 20, 30, 40, 50, 60]])
2、transpose和permute函数的使用
transpose函数可以实现交换张量形状的指定维度,例如:一个张量的形状为(2, 3, 4) 通过transpose可以将 3 和 4 进行交换,将张量的形状变为 (2, 4, 3)
而permute函数可以一次交换多维数据
在pytorch中创建张量,是存储在整块内存中的,而使用transpose 和 permute 函数后,返回的张量是由不同的数据块组成了(可以由is_contiguous判断是否由一整内存快组成)
import torch
import numpy as np
def test():
data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print(data.size())
# 交换 1 和 2 的维度
new_data = torch.transpose(data, 1, 2)
print(new_data.shape)
print('*' * 50)
# 将data数据修改为 (4, 5, 3) 维度
new_data = torch.transpose(data, 0, 1)
new_data = torch.transpose(new_data, 1, 2)
print(new_data.shape)
print('*' * 50)
# 使用 permute 直接修改成 (4, 5, 3) |pəˈmjuːt|
new_data = torch.permute(data, [1, 2, 0])
print(new_data.shape)
if __name__ == '__main__':
test()
torch.Size([3, 4, 5])
torch.Size([3, 5, 4])
**************************************************
torch.Size([4, 5, 3])
**************************************************
torch.Size([4, 5, 3])
3、view和contiguous函数的用法
view函数可以用于修改张量的形状,但是用法比较局限,只能
用于存储在整块内存中的张量。在Pytorch中,有一些张量是由不同的数据块组成的,并没有存储在整块的内存中,view函数无法对这样的张量进行变形处理,例如:一个张量经过了 transpose 或者 permute 函数处理后就无法通过view函数进行形状操作。
contiguous可以将非整块内存变成整块内存
import torch
import numpy as np
def test():
data = torch.tensor([[10, 20, 30], [40, 50, 60]])
print(data.size())
print(data.is_contiguous()) # 判断是否用整块内存
print('*' * 50)
# 1、使用 view 函数进行形状修改
new_data = data.view(3, 2)
print(new_data.size())
print(new_data.is_contiguous()) # 判断是否用整块内存
print('*' * 50)
# 2、先使用transpose后用view回报错
new_data = torch.transpose(data, 1, 0)
print(new_data.size())
print(new_data.is_contiguous()) # 判断是否用整块内存
# 报错:
# new_data = new_data.view(2, 3)
print('*' * 50)
# 解决方法:contiguous
new_data = new_data.contiguous().view(2, 3)
print(new_data.size())
print('*' * 50)
if __name__ == '__main__':
test()
torch.Size([2, 3])
True
**************************************************
torch.Size([3, 2])
True
**************************************************
torch.Size([3, 2])
False
**************************************************
torch.Size([2, 3])
**************************************************
4、squeeze和unsqueeze函数用法
squeeze函数用删除shape为1的维度,unsqueeze在每个维度添加1,用来增加数据的形状。
import torch
import numpy as np
def test():
data = torch.tensor(np.random.randint(0, 10, [1, 3, 1, 5]))
print(data.size())
print('*' * 50)
# 1、去掉值为 1 的维度
new_data = data.squeeze()
print(new_data.size())
print('*' * 50)
# 2、去掉指定维度为 1 的维度
new_data = data.squeeze(2)
print(new_data.size())
print('*' * 50)
# 3、在2维度的位置添加一个维度
new_data = data.unsqueeze(-1)
print(new_data.size())
if __name__ == '__main__':
test()
torch.Size([1, 3, 1, 5])
**************************************************
torch.Size([3, 5])
**************************************************
torch.Size([1, 3, 5])
**************************************************
torch.Size([1, 3, 1, 5, 1])
7、张量运算函数
pytorch里面封装了很多计算函数,常用的有:
- 计算均值
- 计算综合
- 计算平方
- 计算平方根
- 指数运算
- 对数运算
- 其他的先用现查
import torch
def test():
# 准备数据
data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
print(data)
# 1、计算均值
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))
# 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([[0., 5., 2.],
[4., 3., 7.]], dtype=torch.float64)
tensor(3.5000, dtype=torch.float64)
tensor([2.0000, 4.0000, 4.5000], dtype=torch.float64)
tensor([2.3333, 4.6667], dtype=torch.float64)
--------------------------------------------------
tensor(21., dtype=torch.float64)
tensor([4., 8., 9.], dtype=torch.float64)
tensor([ 7., 14.], dtype=torch.float64)
--------------------------------------------------
tensor([[ 0., 25., 4.],
[16., 9., 49.]], dtype=torch.float64)
tensor([[0.0000, 2.2361, 1.4142],
[2.0000, 1.7321, 2.6458]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.0000e+00, 1.4841e+02, 7.3891e+00],
[5.4598e+01, 2.0086e+01, 1.0966e+03]], dtype=torch.float64)
--------------------------------------------------
tensor([[ -inf, 1.6094, 0.6931],
[1.3863, 1.0986, 1.9459]], dtype=torch.float64)
tensor([[ -inf, 2.3219, 1.0000],
[2.0000, 1.5850, 2.8074]], dtype=torch.float64)
tensor([[ -inf, 0.6990, 0.3010],
[0.6021, 0.4771, 0.8451]], dtype=torch.float64)