PyTorch API
PyTorch数据类型
- Torch.FloatTensor
- Torch.IntTensor
- Torch.ByteTensor
- Torch.CharTensor
- Torch.LongTensor
- 当tensor的维度为(1, ), 用来表示标量, 对应到tensor.size()/tensor.shape中是tensor.Size([num]), 如果tensor是这种情况, 可以直接使用tensor.item()返回对应的python内置的浮点数
PyTorch创建Tensor
- Torch.tensor(data)
- Torch.FloatTensor(dim1, dim2, …): 生成(dim1, dim2, …)维度的随机数tensor
- Torch.Tensor(dim1, dim2, …): 和 torch.FloatTensor 一样, torch.set_default_tensor_type(torch.XxxTensor)指定默认类型
- Torch.randn(dim1, dim2, …): 生成(dim1, dim2, …)维度的高斯随机数
- Torch.normal(u, sigma): 高斯分布,但是指定了均值和方差, 具体使用需要看API文档
- Torch.randint(low, high, size): 生成size维度的数据, 每个数据在[low, high)之间
- Torch.rand(dim1, dim2, …): 生成(dim1, dim2, …)维度的均匀随机数
- Torch.fill(size, value): 生成size维度的数据,每个元素的值为value
- Torch.arange(start, end, step)
- Torch.linspace(start, end, steps表示分成几份)
- Torch.ones(dim1, dim2, …)
- Torch.zeros(dim1, dim2, …)
- Torch.eye(dim1, dim2, …)
- Torch.randperm(len): [0, len) 之间的数随机排序
- Torch.masked_select(mask): mask 中为1的选出来, 放在一维的tensor中
维度
- tensor.shape == tensor.size(), torch.shape和torch.size()返回的类型为torch.Size(), list(torch.Size类型的数据)可以转为python内置的list
- tensor.shape[0] == tensor.size(0)
- Tensor.dim() 返回 tensor.shape 的长度
- Tensor.numel(): 返回tensor.shape中所有维度的乘积
维度变换
- Torch.view() == torch.reshape(),常用于将Size([32, 512, 32, 32])变为Size([32, 512 * 32 * 32])等,工作原理是先将tensor展开成一维,再按照参数reshape,只修改表达方式,不修改内容
- Sequeeze(dim): 如果没有参数,则将维度为1的都挤压掉,否则仅仅挤压dim(如果dim对应的size为1);unsequeeze(dim);squeeze和unsqueeze为维度元组增加1或者减少1的操作,比如将Size([512])扩展为Size(1, 512, 1, 1])
- Expand()和repeat(): 原理类似,但是接口不一样,目的都是对Size([1, 512, 1, 1])中为1的进行扩张, 比如要将Size([1, 512, 1, 1])变为Size([32, 512, 32, 32])就需要expand()或者repeat(), tensor.expand(32, 512, 32, 32)或者使用tensor.repeat(32, 1, 32, 32)
- 广播:a与b先维度按照右对齐,再expand/repeat,而在tf中repeat对应的是tf.tile
- 转置:
- Tensor.t(): 只针对2D
- Transpose(): 一次只能交换两个维度
- Permute(): 一次交换所有
- 拼接
- Tensor.cat([a, b], dim=0), 在0维度上将a与b进行拼接, 如果a为(32, 512, 64, 64)维度, b为(20, 512, 64, 64)维度, 则结果为(52, 512, 64, 64)维度, 注意a与b除了要拼接的维度可以不一样外, 其他都要一样
- Tensor.stack([a, b], dim=0), 在0维度上将a与b进行拼接,但是与Tensor.cat不同的是, 并不会将a和b合并在一起, 而是添加一个新的维度将a和b拼接在一起, 以a为(32, 512, 64, 64)维度, b为(32, 512, 64, 64)维度为例子, 结果为(2, 32, 512, 64, 64)维度, 当取[0, …]时为a, 当取[1, …]为b, 注意这里a与b的维度要一样
- 拆分
- Tensor.split(a, size, dim=0): 如果size为标量,表示将a在dim维度每size个拆分出来; 如果size为列表,表示将a在dim维度上第一个拆出来的维度个数为size[0], 第二个是size[1], 总和要等与a在dim维度的数值; 比如a的维度是(32, 512, 64, 64), 则tensor.split(a, 16, dim=0) 得到的结果的维度为(16, 512, 64, 64)和(16, 512, 64 ,64); 如果tensor.split(a, [2, 30], dim=0), 得到的结果的维度为(2, 512, 64, 64)和(20, 512, 64, 64)
- Tensor.chunk(a, num, dim=0): 将a在dim维度分成num份
- tensor矩阵乘法
- *表示element-wise乘法
- @对应tensor.matmul, @适用于任意维度的矩阵乘法
- 高维矩阵乘法的规则,只关注最后两个维度,如a的维度为(32, 512, 64, 32), b的维度为(32, 1, 32, 64), b先广播成(32, 512, 32, 64), 如果b的第二个维度不是1,则不能进行矩阵乘法,因为无法将b广播到与a在除了倒数两个维度上的维度数相同, 再进行矩阵乘法, 得到的结果维度是(32, 512, 64, 64)
- Tensor.mm只适用于2-D矩阵的乘法
- tensor的统计
- Tensor.norm(num): 计算范数
- Tensor.max, min, mean, argmin, argmax, topk, kthvalue, prod(累乘)
- 对于argmin和argmax,如果传入dim,则计算时会将tensor进行flatten,返回flatten对应元素的下标
- max和min除了返回最大值和最小值,还会返回索引(也就是argmax和argmin的功能)
- 默认的情况下tensor的统计函数会对计算的维度进行消除, 比如a的维度是(32, 512, 128, 64), a.norm(1, dim=2)之后, 结果的维度是(32, 512, 64), 有时候为了保留这个维度, 再调用统计函数时, 使用keepdim=True, 得到的结果便是(32, 512, 1, 64)
- 不过topk是没有keepdim的,返回的结果原来上述为1的维度变成了k
- 复杂操作
- Torch.where(cond, x, y): cond为True,用x对应的值,否则用y对应的值
- Torch.gather(input, dim, index): index根据input进行转换, 一般用于对index的进行映射
- 梯度
- Tensor.requires_grad_(): 将requires_grad设置为True
- Torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=False): outputs 一般为标量, inputs 为输入的x值
- tensorScalar.backward()
- 交叉熵
- F.cross_entropy:将输入的pred进行softmax,再进行log,再进行nll_loss
- F.softmax: 计算softmax
- F.nll_loss: 计算softmax和log之后,两个分布的交叉熵,输入为向量,target为标量,没有转为one-hot编码
- 参数初始化
- Torch.nn.init模块中
- Kaiming_normal
- 学习率策略
- Torch.optim.lr_scheduler.ReduceLROnPlateau
- StepLR
- MultiStepLR
- 划分数据
- Train_data, test_data = Torch.utils.data.random_split(database, [train_num, test_num])
- 交叉验证
- 插值放大
- `F.interpolate`: 可以选择linear,nearest等插值算法
- `F.upsample`: 注意和`F.interpolate`的区别,官网建议使用`F.interpolate`
- 网络模块
- BatchNorm2D: 输入的是channel数
- 参数affine表示是否学习alpha和gamma缩放参数,一般设置为affine为True,如果affine为False,则alpha为1,gamma为0,并且不会学习
- batchnorm的作用
- 可以使用更大的学习率,对于,如果没有使用batchnorm,则x的值可能会比较大,那么得到的梯度就会很大,稍微增加一点学习率,可能就不会收敛到全局最优点
- Dropout: 现在不能所以使用,因为版权
- Net.train()与Net.eval()只针对BatchNorm和DropOut
- 保存和加载模型
- Net.load_state_dict(torch.load('model.pkl'))
- Torch.save(net.state_dict(), 'model.pkl')
- 数据增强
- T.RandomHorizontalFlip()
- T.RandomVerticalFlip()
- T.RandomRotation()
- T.Resize()
- T.RandomCrop()
- T.CenterCrop()
- 常见搭配
- T.Resize先对图像进行尺度变换,一般先变换得大一些
- 再使用T.RandomRotation对图像进行选择,这个时候会出现黑色边框,这不是我们需要得
- 再使用T.CenterCrop去掉边框
- 池化
- Maxpool
- Avgpool
- Adaptive_avg_pool
- 自定义数据集
- RNN(在NLP中,数据的维度为(seq_len, batch, feature_len),其中seq_len也是时间戳)容易导致梯度爆炸和梯度消失,RNN的梯度消失表示为RNN的记忆力短
- nn.RNN(input_size, hidden_size, num_layers): input_size是一个单词词嵌入的维度,hidden_size也就是memory_size,表示的是一个词嵌入经过RNN输出的维度,num_layers是RNNCell在空间上的个数
- `rnn = nn.RNN(100, 20, 10)`的前向传播, `x = torch.randn(30, 10, 100)`表示有10个句子,每个句子单词有30个,每个单词是100维的词向量, `h0=torch.randn(10, 10, 20)`表示初始记忆,第一个10表示10个空间上的RNNCell,每个RNNCell都有一个向右传播的h(构成一个网格),第二个10表示batch,第三个20表示memory的维度, `out, h = rnn(x, h0)`其中out是所有ht的stack,h是最后序列的输出, 维度为[10, 10, 20]
- 与RNNCell不同,RNN输入的x就是[seq_len, batch, feature_len],但是在人工推导的时候,是一个[batch, feature_len]也就是时间戳设定的,为了和这个思路相一致,就诞生了RNNCell
- RNNCell
- RNNCell表示一个RNN块,输入的x是[batch, feature_len], 需要人工保存中间的h,并输入到下一个RNNCell中,一般使用for训练迭代[seq_len, batch, feature_len],按照时间戳迭代,依次输入一个[batch, feature_len]
- RNNCell(input_size, hidden_size),没有num_layers, 因此空间上RNNCell的展开也需要手动编写
- LSTM(用于解决RNN的梯度消失现象)
导致损失函数不再下降的原因
- 出现了梯度消失现象,与之对一个的是梯度爆炸,pytorch通过torch.nn.utils.clip_grad_norm(param, threshold)解决梯度爆炸问题,本质上是将grad方向不变,大小缩小,梯度爆炸的表现为loss现在比较小,突然loss变大
- 陷入了局部最优解
- 选择其他优化器,比如SGD或者Adam,通过动量冲出局部最优解,在pytorch中通过优化器构造器的mometum设置动量参数beta,不过有些优化器,比如Adam是没有monmetum参数的,Adam内部的实现机制就是构建在momentum之上的
- 选择不同的参数初始化,使得网络在另外一个起点进行学习
解决过拟合问题
- 提供更多的数据
- 采用正则化(PyTorch中通过优化器的weight_decay进行l2正则化,它的值为lamda超参数)
- L1-regularization:在原始的损失函数之上添加
- L2-regularization:在原始的损失函数之上添加
- 模型在最小化损失函数的时候,也就最小化模型的参数,这样做是为了降低模型的复杂度,假如一个问题,只需要使用2次模型即可,但是因为实现我们并不知道,而采用了5次模型来学习,,如果通过正则化将参数取小,最终d,e和f的值会趋向于0,这样预测函数就变成2次的了
激活函数以及其用途
- tanh:常用于RNN
- ReLU:常用于卷积
- LeakyReLU
- SELU:ReLU + LeakyReLU
- softplus:是ReLU函数在0点平滑函数
- Sigmoid
数据可视化
- Tensorboardx
- Visdom
- Visom.line绘制曲线
- Visom.image绘制图像