1. PyTorch基础
1.1. 导入PyTorch包
import torch
1.2. 张量的初始化
(1) 创建并初始化张量
# 创建一个任意张量 tensor = torch.tensor([5.5, 3, 0]) ''' tensor([5.5000, 3.0000, 0.0000]) ''' # 创建一个数列张量,从1开始,到10结束,间隔为2 tensor_arange = torch.arange(1, 10, 2) ''' tensor([1, 3, 5, 7, 9]) ''' # 创建一个空张量,大小为 5*3 tensor_empty = torch.empty((5, 3)) ''' tensor([[7.1819e+22, 4.0666e+21, 1.0999e+27], [2.6277e+17, 7.2368e+25, 2.7516e+17], [4.4721e+21, 1.8497e+31, 2.6845e+17], [4.4724e+21, 1.8497e+31, 5.4214e-11], [9.1720e-04, 1.9086e-41, 0.0000e+00]]) ''' # 创建一个0张量,大小为 5*3 tensor_zeros = torch.zeros((5, 3)) ''' tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) ''' # 创建一个1张量,大小为 5*3 tensor_ones = torch.ones((5, 3)) ''' tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) ''' # 创建一个任意值张量,大小为 5*3,填充值为99 tensor_full = torch.full([5, 3], 99.) ''' tensor([[99., 99., 99.], [99., 99., 99.], [99., 99., 99.], [99., 99., 99.], [99., 99., 99.]]) ''' # 创建一个在[0,1)内均匀分布的随机张量,大小为 5*3 tensor_rand = torch.rand((5, 3)) ''' tensor([[0.4991, 0.5687, 0.9626], [0.3185, 0.2407, 0.6076], [0.2600, 0.0289, 0.1632], [0.4269, 0.9201, 0.4774], [0.7576, 0.9349, 0.5644]]) ''' # 创建一个服从标准正态分布的随机张量,大小为 5*3 tensor_randn = torch.randn((5, 3)) ''' tensor([[-0.6753, -1.6975, -1.1010], [-1.9736, 0.6005, 0.5844], [ 0.3015, -0.5700, 1.1196], [ 0.3156, -0.4194, -0.8618], [-1.1506, 0.5248, 0.0401]]) ''' # 创建一个服从均值为mean、标准差为std正态分布的随机张量,大小为 1*10 # 更多关于随机正态分布张量的内容:https://pytorch.org/docs/stable/generated/torch.normal.html#torch-normal tensor_normal = torch.normal(mean=torch.arange(0., 1.), std=torch.arange(0, 10, 1)) ''' tensor([ 0.0000, -0.9662, -0.2848, 5.5105, 0.9495, 3.0639, -3.4798, 6.9719, -11.6149, -6.0056]) '''
(2) 从已有张量创建大小一致的张量
# 根据已有的张量创建大小一致的0张量 tensor_zeros = torch.zeros_like(tensor_empty) ''' tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) ''' # 根据已有的张量创建大小一致的1张量 tensor_ones = torch.ones_like(tensor_empty) ''' tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) ''' # 根据已有的张量创建大小一致在[0,1)内的均匀分布的随机张量 tensor_rand = torch.rand_like(tensor_empty) ''' tensor([[0.0437, 0.9776, 0.1685], [0.5190, 0.6811, 0.3768], [0.4864, 0.7169, 0.4813], [0.4596, 0.4703, 0.2497], [0.9778, 0.9880, 0.9152]]) '''
(3) 指定张量的数据类型 dtype
# 创建long数据类型的张量 tensor_long = torch.ones((5, 3), dtype=torch.long) ''' tensor([[1, 1, 1, 1, 1]]) ''' # 查看张量的数据类型 print(tensor_long.dtype) ''' torch.int64 ''' # 改变张量的数据类型 print(tensor_long.float()) ''' torch.float32 ''' # 张量的数据类型总结: # 8位无符号整型:torch.uint8 # 8位整型:torch.int8 # 16位整型:torch.int16 | torch.short # 32位整型:torch.int32 | torch.int # 64位整型:torch.int64 | torch.long # 16位浮点型:torch.float16 | torch.half # 32位浮点型:torch.float32 | torch.float # 64位浮点型:torch.float64 | torch.double
1.3. 张量的基本操作
(1) 查看张量的大小
tensor_empty = torch.empty((5, 3)) print(tensor_empty.size()) ''' torch.Size([5, 3]) ''' print(tensor_empty.shape) ''' torch.Size([5, 3]) '''
(2) 张量堆叠
# 创建一个空张量,大小为 5*3 tensor_empty = torch.tensor([[1,2,3]]) ''' tensor([[1, 2, 3]]) ''' # 张量按维度0堆叠,即添加行 tensor_cat0 = torch.cat((tensor_empty,tensor_empty,tensor_empty), 0) ''' tensor([[1, 2, 3], [1, 2, 3], [1, 2, 3]]) ''' # 张量按维度0堆叠,即添加列 tensor_cat1 = torch.cat((tensor_empty,tensor_empty,tensor_empty), 1) ''' tensor([[1, 2, 3, 1, 2, 3, 1, 2, 3]]) '''
(3) 张量增减维度
tensor_zeros = torch.zeros(3, 2, 4, 1, 2, 1) print(tensor_zeros.shape) ''' torch.Size([3, 2, 4, 1, 2, 1]) ''' # 在张量的第dim维增添一个维数为1的维度 tensor_unsqueeze = torch.unsqueeze(tensor_zeros, dim=0) print(tensor_unsqueeze.shape) ''' torch.Size([1, 3, 2, 4, 1, 2, 1]) ''' # 删去张量中维数为1的维度,如果未指定dim则删去全部维数为1的维度 tensor_squeeze = torch.squeeze(tensor_zeros, dim=3) print(tensor_squeeze.shape) ''' torch.Size([3, 2, 4, 2, 1]) '''
(4) 张量分割
# 创建一个数列张量 tensor_arange = torch.arange(10) ''' tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ''' # 把张量均匀分割。4表示分割成4份;dim=0表示按行分割,dim=1表示按列分割 tensor_chunk = torch.chunk(tensor_arange, 4, dim=0) ''' (tensor([0, 1, 2]), tensor([3, 4, 5]), tensor([6, 7, 8]), tensor([9])) ''' # 把张量按指定方案分割。[1,2,3,4]表示分割成4份,每份分别有1、2、3、4个元素;dim=0表示按行分割,dim=1表示按列分割 tensor_split = torch.split(tensor_arange, [1,2,3,4], dim=0) ''' (tensor([0]), tensor([1, 2]), tensor([3, 4, 5]), tensor([6, 7, 8, 9])) '''
(5) 选择张量中的元素
# 创建一个随机张量 tensor_randn = torch.randn((5, 3)) ''' tensor([[ 0.3567, 1.3366, 0.1045], [ 0.7795, -0.2843, -0.2252], [-0.4905, -0.2118, -1.5967], [ 0.8375, 0.3417, -0.7932], [-0.5923, -2.4375, -0.9357]]) ''' # 取张量的(0,0)元素 print(tensor_randn[0, 0]) ''' tensor(0.3567) ''' # 取张量的第一列元素 print(tensor_randn[:, 0]) ''' tensor([ 0.3567, 0.7795, -0.4905, 0.8375, -0.5923]) ''' # 选择张量中行index为0、2、4的元素 print(torch.index_select(tensor_randn, dim=0, index=torch.tensor([0, 2, 4]))) ''' tensor([[ 0.3567, 1.3366, 0.1045], [-0.4905, -0.2118, -1.5967], [-0.5923, -2.4375, -0.9357]]) ''' # 选择张量中>=0的元素,mask为条件表达式 print(torch.masked_select(tensor_randn, mask=tensor_randn.ge(0))) ''' tensor([0.3567, 1.3366, 0.1045, 0.7795, 0.8375, 0.3417]) '''
(6) 改变张量形状
# 创建一个随机张量 tensor_randn = torch.randn((5, 3)) ''' tensor([[ 0.3478, -2.1573, 0.7693], [-0.3915, -1.2390, -0.7590], [-0.3685, 0.1261, 0.1424], [ 0.5710, 0.0483, -1.2647], [-0.8762, 0.9870, -0.0174]]) ''' # 将tensor_randn从(5,3)转变为(3,5) tensor_reshape = torch.reshape(tensor_randn, (3,5)) ''' tensor([[ 0.3478, -2.1573, 0.7693, -0.3915, -1.2390], [-0.7590, -0.3685, 0.1261, 0.1424, 0.5710], [ 0.0483, -1.2647, -0.8762, 0.9870, -0.0174]]) ''' # 将tensor_randn从(5,3)转变为(3,5) tensor_view = tensor_randn.view((-1,5)) #当某一维是-1时,会根据另一维自动计算它的大小 ''' tensor([[ 0.3478, -2.1573, 0.7693, -0.3915, -1.2390], [-0.7590, -0.3685, 0.1261, 0.1424, 0.5710], [ 0.0483, -1.2647, -0.8762, 0.9870, -0.0174]]) '''
(7) 张量运算
# 创建一个数列张量 tensor_arange1 = torch.arange(10, 20, 2) tensor_arange2 = torch.arange(0, -10, -2) ''' tensor([10., 12., 14., 16., 18.]) tensor([-1., -3., -5., -7., -9.]) ''' # 张量元素相加 + 两种方法 tensor_add = torch.add(tensor_arange1,tensor_arange2, out=None) # torch.add(tensor_arange1,tensor_arange2, out=tensor_arange1) # 此时操作结果直接存储在tensor_arange1中 # tensor_arange2.add_(tensor_arange1) # 此时操作结果直接存储在tensor_arange2中 ''' tensor([9., 9., 9., 9., 9.]) ''' # 张量元素相乘 × tensor_mul = torch.mul(tensor_arange1, tensor_arange2, out=None) ''' tensor([ -10., -36., -70., -112., -162.]) ''' # 张量元素相除 ÷ 这里要注意除法的结果一般为浮点数,所以张量类型也应当为浮点数类型 tensor_div = torch.div(tensor_arange1, tensor_arange2, out=None) ''' tensor([-10.0000, -4.0000, -2.8000, -2.2857, -2.0000]) ''' # 张量元素求幂 ^ tensor_pow = torch.pow(tensor_arange1, tensor_arange2, out=None) ''' tensor([1.0000e-01, 5.7870e-04, 1.8593e-06, 3.7253e-09, 5.0414e-12]) ''' # 张量元素开平方根 √ tensor_sqrt = torch.sqrt(tensor_arange1, out=None) ''' tensor([3.1623, 3.4641, 3.7417, 4.0000, 4.2426]) ''' # 张量元素四舍五入 tensor_round = torch.round(tensor_arange1, out=None) ''' tensor([10., 12., 14., 16., 18.]) ''' # 张量元素取绝对值 tensor_abs = torch.abs(tensor_arange2, out=None) ''' tensor([1., 3., 5., 7., 9.]) ''' # 张量元素向上取整,等于向下取整+1 tensor_ceil = torch.ceil(tensor_arange2, out=None) ''' tensor([-1., -3., -5., -7., -9.]) ''' # 张量元素限定值域,把输入数据规范在min-max区间,超过范围的用min、max代替 tensor_clamp = torch.clamp(tensor_arange1, min=12, max=16, out=None) ''' tensor([12., 12., 14., 16., 16.]) ''' # 张量取最大值的下标,dim=1时取出每一列中的最大值的下标,dim=0时取出每一行中的最大值的下标 tensor_argmax = torch.argmax(tensor_arange1, dim=0, keepdim=False) ''' tensor(4) ''' # 张量元素输入sigmoid函数进行处理 tensor_sigmoid = torch.sigmoid(tensor_arange1, out=None) ''' tensor([1.0000, 1.0000, 1.0000, 1.0000, 1.0000]) ''' # 张量元素输入tanh函数进行处理 tensor_tanh = torch.tanh(tensor_arange1, out=None) ''' tensor([1., 1., 1., 1., 1.]) '''
1.4. Tensor与Numpy、List的互相转换
(1) Tensor 转 Numpy 再转 List
# 创建一个1张量 tensor_ones = torch.ones((2,4)) ''' tensor([[1., 1., 1., 1.], [1., 1., 1., 1.]]) ''' # 将tensor张量转换为array数组 # 转换后,numpy的变量和原来的tensor会共用底层内存地址,所以如果原来的tensor改变了,numpy变量也会随之改变 tensor_numpy = tensor_ones.numpy() ''' [[1. 1. 1. 1.] [1. 1. 1. 1.]] ''' tensor_list = tensor_numpy.tolist() ''' [[1., 1., 1., 1.], [1., 1., 1., 1.]] '''
(2) List 转 Numpy 再转 Tensor
# 创建一个1列表 list_ones = [1 for i in range(0, 5)] ''' [1, 1, 1, 1, 1] ''' # 将list转换为array array_ones = numpy.array(list_ones) ''' [1 1 1 1 1] ''' # 将array数组转换为tensor张量 # 转换后,tensor和原来的numpy的变量会共用底层内存地址,所以如果原来的tensor改变了,numpy变量也会随之改变 tensor_form_numpy = torch.from_numpy(array_ones) ''' tensor([1., 1., 1., 1., 1.], dtype=torch.int) ''' # 改变tensor的数据类型 tensor_form_numpy = tensor_form_numpy.float() ''' tensor([1., 1., 1., 1., 1.], dtype=torch.float32) '''
1.5. GPU加速
在Pytorch使用GPU进行加速之前,需要先安装机器显卡对应的 CUDA ,然后即可利用GPU进行运算
(1) 将数据从CPU迁入GPU
# 一般而言,我们将网络模型、损失函数、张量三种数据迁入GPU中进行加速 # 将网络模型和损失函数迁入GPU net = Net() loss_func = torch.nn.CrossEntropyLoss() if(torch.cuda.is_available()): net = net.cuda() loss_func = loss_func.cuda() # 将张量迁入GPU,实际上只需要将输入网络的张量数据迁入GPU即可,因为根据这些数据产生的中间数据以及预测结果均在GPU中,所以只要把握住源头数据,后面的数据就不需要再显式地迁入GPU # 有些旧版的Pytorch需要将张量封装成Variable类型才可以迁入GPU,不过这已经是过去式了 if (torch.cuda.is_available()): tensor_x, tensor_y = tensor_x.cuda(), tensor_y.cuda() # tensor_x, tensor_y = Variable(tensor_x).cuda(), Variable(tensor_y).cuda() # 旧版Pytorch
(2) 将数据从GPU迁出到CPU
# 将网络模型预测的结果迁出GPU,一般是最终损失值、精确度,也可以是其他迁入了GPU的数据 if(torch.cuda.is_available()): loss = loss.cpu() accurate = accurate.cpu()
2. 自动微分
2.1. 开启自动微分
# 创建一个张量并在初始化时设置requires_grad=True开启自动微分功能 tensor_ones = torch.ones(2, 2, requires_grad=True) ''' tensor([[1., 1.], [1., 1.]], requires_grad=True) ''' # 创建一个张量,初始化时不开启自动微分 tensor_randn = torch.randn(2, 2) tensor_randn = (tensor_randn * 3) / (tensor_randn - 1) print(tensor_randn.requires_grad) # 设置该张量为自动微分 tensor_randn.requires_grad_(True) print(tensor_randn.requires_grad) ''' False True '''
2.2. 梯度计算
(1) 查看梯度方程 grad_fn
# 创建一个1张量,并查看其梯度方程grad_fn tensor_ones = torch.ones(2, 2, requires_grad=True) print(tensor_ones) print(tensor_ones.grad_fn) # 可以看到,单独的张量是没有梯度方程的 ''' tensor([[1., 1.], [1., 1.]], requires_grad=True) None ''' # 构建一个张量方程,并查看其梯度方程grad_fn tensor_add = tensor_ones + 2 print(tensor_add) print(tensor_add.grad_fn) # 该张量方程是由加法构建的张量方程,因此梯度方程是AddBackward类型的 ''' tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward0>) <AddBackward0 object at 0x00000245B1025CA0> ''' # 构建一个张量方程,并查看其梯度方程grad_fn tensor_mul = tensor_ones * tensor_ones * 2 print(tensor_mul) print(tensor_mul.grad_fn) # 该张量方程是由乘法构建的张量方程,因此梯度方程是MulBackward类型的 ''' tensor([[2., 2.], [2., 2.]], grad_fn=<MulBackward0>) <MulBackward0 object at 0x000001A309525CA0> '''
(2) 反向传播计算标量梯度
# 创建一个1张量 tensor_x = torch.ones(2, 2, requires_grad=True) tensor_y = tensor_x + 2 tensor_z = tensor_y * tensor_y * 3 out = tensor_z.mean() print(out) ''' tensor(27., grad_fn=<MeanBackward0>) ''' # 反向传播计算梯度 # out是标量,因此不需要为backward()函数指定参数,相当于out.backward(torch.tensor(1)) out.backward() print(tensor_x.grad) # 输出梯度 ''' tensor([[4.5000, 4.5000], [4.5000, 4.5000]]) '''
(3) 反向传播计算矢量梯度
# 创建一个随机张量 tensor_x = torch.randn((1,3), requires_grad=True) print(tensor_x) ''' tensor([[0.2427, 0.8484, 0.7631]], requires_grad=True) ''' # 求 y^(2^(n))>=1000 的最小值 tensor_y = tensor_x * 2 while tensor_y.data.norm() < 1000: tensor_y = tensor_y * 2 print(tensor_y) ''' tensor([[248.5333, 868.7134, 781.4405]], grad_fn=<MulBackward0>) ''' gradients = torch.tensor([[0.1, 1.0, 0.0001]], dtype=torch.float) # 梯度参数 tensor_y.backward(gradients) # 对矢量进行梯度计算需要指定梯度,tensor_x[0,0]与tensor_y[0,0]对应梯度0.1... print(tensor_x.grad) ''' tensor([[102.40, 1024.0, 0.10240]]) '''
(4) 不进行梯度计算
# 创建一个随机张量 tensor_x = torch.randn((1,3), requires_grad=True) print(tensor_x.requires_grad) ''' True ''' # 进行梯度计算 tensor_y = tensor_x ** 2 print(tensor_y.requires_grad) ''' True ''' # 将变量包裹在no_grad()中,可以不进行梯度计算 with torch.no_grad(): tensor_y = tensor_x ** 2 print(tensor_y.requires_grad) ''' False '''
(5) 切断梯度计算
# 创建一个随机张量 tensor_x = torch.randn(3, requires_grad=True) tensor_y = tensor_x ** 2 print(tensor_x.requires_grad) print(tensor_y.requires_grad) ''' True True ''' # 切断tensor_y的梯度计算 tensor_y = tensor_y.detach() print(tensor_y.requires_grad) ''' False '''