相比TensorFlow的静态图开发,Pytorch的动态图特性使得开发起来更加人性化
数据类型
标量/张量 torch.tensor(1.)
类型:通用/float/double/int/long/byte型
Tensor/torch.FloatTensor/torch.DoubleTensor/torch.IntTensor/torch.LongTensor/torch.ByteTensor
GUP下 torch.cuda.FloatTensor/…..
索引 a=torch.rang(2,3) a[0]
切片 a[0,::2] 第一行三列
shap()/size() 张量形状
dim()维数
numel()张量
type()获取张量类型
cuda() 类型转换 x=torch.IntTensor(1)
x=x.cuda()
判断是否使用GPU torch.cuda.is_available()
获取gpu数量 torch.cuda.device_count()
使用编号为0的GPU torch.cuda.set_device(0)
GPU转CPU a.cpu()
view()/reshape() 修改张量形状
t() 装置
pow() 对张量进行幂运算,也可以用**
代替
sqrt()/ rsqrt() 对张量取平方根/平方根的倒数
exp() 取e
为底的幂次方
log() 取log
以e为底的对数
round()/floor()/ceil() 四舍五入、向下取整和向上取整
trunc()/frac() 取整数/小数部分
max()/min()/median()/mean() 取最大值、最小值、中位数和平均值
argmax()/argmin() 返回最大/小值的索引
argsort()/argsort(descending=True) 从小到大索引/相反
topk()返回前几大的值
norm()张量的范数,默认是l2范数(即欧氏距离)
transpose()将指定维度对调
>>> a = torch.rand(2, 3, 1)
>>> a
tensor([[[0.8327],
[0.7932],
[0.7497]],
[[0.2347],
[0.7611],
[0.5529]]])
>>> a.transpose(0, 2)
tensor([[[0.8327, 0.2347],
[0.7932, 0.7611],
[0.7497, 0.5529]]])
# 将第1维和第3维对调
>>> a.transpose(0, 2).shape
torch.Size([1, 3, 2])
permute()和transpose
类似也是对调维度
>>> a = torch.rand(2, 3, 1)
>>> a
tensor([[[0.6857],
[0.4819],
[0.3992]],
[[0.7477],
[0.8073],
[0.1939]]])
>>> a.permute(2, 0, 1)
tensor([[[0.6857, 0.4819, 0.3992],
[0.7477, 0.8073, 0.1939]]])
# 将a修改成原来第3个维度放在第1个维度,第1个维度放在第2个维度,第2个维度放在第3个维度
>>> a.permute(2, 0, 1).shape
torch.Size([1, 2, 3])
squeeze()/unsqueeze() 在指定索引位置删减维度/在指定索引位置增加维度
expand() 扩展数据,但仅限于维度是1的地方
repeat() 复制数据
split() 根据长度切分数据
chunk() 根据数量切分数据,也就是自定义要切成多少份
常用方法
数组转张量 torch.from_numpy(a)
张量转数组 data.numpy()
基本运算
张量torch.add()/torch.sub()/torch.mul()/torch.div()/
矩阵torch.matmul()
逻辑操作
torch.equal/torch.all与/torch.any或
torch.where
>>> a = torch.tensor([[1., 2.], [3., 4.]])
>>> b = torch.tensor([[5., 6.], [7., 8.]])
>>> c = torch.rand(2, 2)
>>> c
tensor([[0.3821, 0.6138],
[0.2323, 0.2675]])
>>> torch.where(c>0.3, a, b)
tensor([[1., 2.],
[7., 8.]])
数据生成
torch.zeros/torch.ones/torch.eye/torch.full()生成固定尺寸的值全为指定值的张量
torch.arange生成指定等差数列的张量
torch.linspace生成等差数列的张量
torch.linspace(-1, 1, steps=21)
torch.rand随机生成一个固定尺寸的张量,并且数值范围都在0~1之间
torch.randn随机生成固定尺寸的张量,数值符合正态分布
torch.randint随机生成一个固定尺寸的张量,并且数值为自定义范围的整数
数据处理
torch.masked_select 取出指定条件数据
torch.masked_select(a, a>0.5)
torch.cat在指定维度合并数据
>>> a = torch.rand(1,2,3)
>>> b = torch.rand(2,2,3)
>>> torch.cat((a,b))
tensor([[[0.0132, 0.4118, 0.5814],
[0.8034, 0.8765, 0.8404]],
[[0.7860, 0.6115, 0.4745],
[0.0846, 0.4158, 0.3805]],
[[0.9454, 0.3390, 0.3802],
[0.6526, 0.0319, 0.7155]]])
>>> torch.cat((a,b)).shape
torch.Size([3, 2, 3])
torch.stack在指定维度创建一个新维度合并两个数据
>>> a = torch.rand(1,2,3)
>>> b = torch.rand(1,2,3)
>>> a
tensor([[[0.5239, 0.0540, 0.0213],
[0.9713, 0.5983, 0.1413]]])
>>> b
tensor([[[0.3397, 0.0976, 0.3744],
[0.5080, 0.7520, 0.1759]]])
>>> torch.stack((a, b))
tensor([[[[0.5239, 0.0540, 0.0213],
[0.9713, 0.5983, 0.1413]]],
[[[0.3397, 0.0976, 0.3744],
[0.5080, 0.7520, 0.1759]]]])
one-hot编码
>>> label = torch.tensor([[0], [1], [2], [3]])
# 标签内容
>>> label_number = len(label)
# 标签长度
>>> label_range = 10
# 标签总数
>>> torch.zeros(label_number, label_range).scatter_(1, label, 1)
tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]])
工具集 torch.utils下面
通过torch.utils.data.random_split()
方法随机切分数据集,然后通过torch.utils.data.DataLoader
来载入数据
函数API和类API
神经网络层、激活函数、损失函数等都提供了两种API调用方式,分别是函数式API和类API,前者基本都在torch.nn.functional
下,后者基本都在torch.nn
下,前者一般直接调用即可,适合函数式编程;后者一般是先实例化,然后通过其内置的方法进行调用,适合面向对象编程。
网络层
全连接 函数式API:torch.nn.functional.linear
,类API:torch.nn.Linear
Dropout
随机选取一部分节点使用,忽略一部分节点,函数式API:torch.nn.functional.dropout
,类API:torch.nn.Dropout
批标准化层
函数式API:torch.nn.functional.batch_norm
,类API:torch.nn.BatchNorm2d
卷积层
函数式API:torch.nn.functional.conv2d
,类API:torch.nn.Conv2d
layer = torch.nn.Conv2d(1, 3, kernel_size=3, stride=1, padding=0)
>>> x = torch.rand(1, 1, 28, 28)
>>> w = torch.rand(3, 1, 3, 3)
# 输出3通道,输入1通道,尺寸3x3
>>> b = torch.rand(3)
# 偏置长度要和通道数一样
>>> layer = torch.nn.functional.conv2d(x, w, b, stride=1, padding=1)
池化层
函数式API:torch.nn.functional.max_pool2d
,类API:torch.nn.MaxPool2d
>>> x = torch.rand(1, 1, 28, 28)
>>> layer = torch.nn.MaxPool2d(3, stride=2)
还有个avgpool(函数式API:torch.nn.functional.avg_pool2d
,类API:torch.nn.AvgPool2d
)
>>> torch.nn.functional.avg_pool2d(x, 3, stride=2)
flatten将张量转成一维
>>> torch.flatten(torch.rand(2,2))
向上取样
将图片放大/缩小成原来的几倍,函数式API:torch.nn.functional.interpolate
还有如Upsampling
、UpsamplingNearest2d
也是向上采样,现在已经逐渐被interpolate
给取代。上面interpolate
示例参数中mode='nearest'
时,相当于该UpsamplingNearest2d
,函数式API:torch.nn.functional.upsample_nearest
,函数式API:torch.nn.UpsamplingNearest2d
,类API举例:
>>> x = torch.rand(1, 1, 2, 2)
>>> x
tensor([[[[0.9977, 0.9778],
[0.4167, 0.6936]]]])
>>> torch.nn.functional.upsample_nearest(x, scale_factor=2).shape
torch.Size([1, 1, 4, 4])
>>> torch.nn.functional.upsample_nearest(x, scale_factor=2)
嵌入层
常用于定义词向量
函数式API:torch.nn.functional.embedding
,类API:torch.nn.Embedding
RNN层
类API:torch.nn.RNN
,会返回计算后总体的输出,以及最后一个时间戳上的输出
LSTM层
类API:torch.nn.LSTM
,因为LSTM是基于RNN并添加了门控制,因此返回的时候比RNN要多返回一个cell单元,格式和hidden一样
序列化模型
定义神经网络模型(每一层都要是继承于torch.nn
下的网络),类API:torch.nn.Sequential
>>> net = torch.nn.Sequential(
torch.nn.Linear(100, 10),
torch.nn.Dropout(0.7),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
>>> net
Sequential(
(0): Linear(in_features=100, out_features=10, bias=True)
(1): Dropout(p=0.7)
(2): ReLU()
(3): Linear(in_features=10, out_features=1, bias=True)
# 可以直接查看网络结构
序列化模型修改
序列化模型可以理解成一个列表,里面按顺序存放了所有的网络层,官方也提供了添加往序列化模型里添加网络层的方法add_module(name, layer)
,而修改则可以索引到对应的层直接修改,删除可以通过del
关键字删除
>>> seq.add_module("tanh", nn.Tanh())
修改网络参数
对于网络层中的参数,其是一个Parameter
类型,因此如果我们需要手动修改其参数时,可以通过定义一个该类的数据来赋值修改
自定义神经网络
自己定义神经网络层的时候,首先需要继承于torch.nn.Module
,并在初始化时调用父类的初始化方法,同时也要在forward
方法里实现数据的前向传播
import torch
class Dense(torch.nn.Module):
# 实现一个自定义全连接+relu层,继承torch.nn.Module
def __init__(self, input_shape, output_shape):
super(Dense, self).__init__()
# 首先初始化时执行父类的初始化,这句话可以看
# 在父类初始化中会初始化很多变量
self.w = torch.nn.Parameter(torch.randn(output_shape, input_shape))
# 初始化权重和偏置参数
# 使用Parameter其会自动将参数设置为需要梯度信息,并且可以通过内置的parameters方法返回这些参数
self.b = torch.nn.Parameter(torch.rand(output_shape))
self.relu = torch.nn.ReLU()
# 初始化relu层
def forward(self, x):
# 定义前向传播方法
x = x @ self.w.t() + self.b
# 全连接层的功能就是矩阵相乘计算
x = self.relu(x)
# 进行relu层计算
return x
def __call__(self, x):
# 调用该类对象执行时,调用前向传播方法
# 这个可以不写,直接通过调用forward方法也一样
return self.forward(x)
layer = Dense(10, 1)
x = torch.rand(2, 10)
output = layer(x)
print(output)
# 输出结果:
# tensor([[0.1780],
# [0.0000]], grad_fn=<ThresholdBackward0>)
冻结网络层
对某些网络层的权重不进行训练的话,可以设置该层的权重、偏差等属性为False
保存和载入网络
对于所有继承自torch.nn.Module
下的网络,保存时首先通过内置的方法state_dict()
返回当前模型的所有参数,然后通过torch.save()
方法保存成文件,载入时通过torch.load()
方法载入文件,并通过内置的load_state_dict()
方法载入所有的参数
优化器
torch.optim定义了各种优化器,将优化器实例化后(传入需要求导的参数和学习率),通过step()
方法进行梯度下降
对于实例化的优化器,其参数都将存放到一个属性param_groups[0]
里
optim = torch.optim.Adam(model.parameters(), lr=0.01)
print(optim)
# 结果:
# Adam (
# Parameter Group 0
# amsgrad: False
# betas: (0.9, 0.999)
# eps: 1e-08
# lr: 0.01
# weight_decay: 0
# )
激活函数
函数式API:torch.nn.functional.sigmoid
,类API:torch.nn.Sigmoid
,pytorch自带:torch.sigmoid
函数式API:torch.nn.functional.relu
,类API:torch.nn.ReLU
损失函数
均方差:计算的y与实际y之差的平方取平均值,函数式API:torch.nn.functional.mse_loss
,类API:torch.nn.MSELoss
,第一个参数是y,第二个参数是y对应的公式
交叉熵:通过含有的信息量大小来进行判断,一般用于分类,函数式API:torch.nn.functional.cross_entropy
,类API:torch.nn.CrossEntropyLoss
求导机制
自动机制torch.autograd.grad
定义了自动求导,传入第一个参数是对应的公式,第二个参数是一个列表,里面存放所有要求导的变量,并且在求导前的变量需要通过require_grad()
方法来声明该公式的某个变量是需要求导的(或者在定义时就设置requires_grad
参数为True
),返回一个元组,里面是对应每个变量的求导信息
>>> x = torch.tensor(1.)
>>> w = torch.tensor(2.)
>>> b = torch.tensor(0.)
>>> y = torch.tensor(1.)
>>> mse = torch.nn.functional.mse_loss(y, w*x+b)
# 模拟一个y=wx+b的函数
>>> mse
tensor(1.)
反向传播
torch.backward反向传播,也能实现求导,通过设置该tensor允许求导,那么该方法会对计算过程中所有声明了求导信息的变量进行求导,然后在对应的变量上通过grad
属性获取求导结果
>>> x = torch.tensor(1.)
>>> b = torch.tensor(0.)
>>> w = torch.tensor(2., requires_grad=True)
>>> y = torch.tensor(1.)
>>> mse = torch.nn.functional.mse_loss(y, w*x+b)
>>> mse.backward()
# 对mse公式里需要求导的变量都进行求导
>>> w.grad
tensor(2.)
# w的求导结果为2
>>> x.grad
# 因为x没有声明需要求导,所以为空
可视化
在TensorFlow里有tensorboard
可以进行训练过程的可视化,而在Pytorch里也提供了tensorboardX
几乎和tensorboard
一样
visdom pytorch提供的另一个可视化
深度学习程序实现如下功能:
- 模型定义
- 数据处理和加载
- 训练模型(Train&Validate)
- 训练过程的可视化
- 测试(Test/Inference)