- 一、第一周:Pytorch基础概念
- 3. 张量操作与线性回归
- 3.1 张量的拼接与切分
- example3
- torch.chunk
- example5
- torch.index_select
- example7
- torch.reshape
- example9
- torch.squeeze
- 3.4 张量的数学运算——加减乘除
- example10
- torch.add
- 4. 计算图与动态图机制
- 4.1 计算图
- 4.2 动态图
- 5. 自动求导系统torch.autograd及逻辑回归实现
- 5.1 autograd——自动求导系统
- 5.2 逻辑回归
- 二、第二周:Pytorch数据处理
- 7. 图像数据预处理模块——transforms
- 7.1 transforms运行机制
- 7.2 数据标准化——transforms.Normalize
- 8. transforms图像增强(一)
- 8.1 数据增强(Data Augmentation)
- 8.2 transforms——裁剪(Crop)
- 8.3 transforms——翻转与旋转(Flip and Rotation)
- 9. transforms图像增强(二)
- 9.1 transforms——图像变换(Data Augmentation)
- 9.2 transforms——transforms方法操作(Transforms Operation)
- 9.3 自定义 transforms 方法(User-Defined Transforms)
- 第三周 模型模块
- 11. 模型容器Containers与AlexNet构建
- 11.1 模型容器——Containers
- 11.2 AlexNet构建
- 12. nn中的网络层——卷积层
- 12.1 1d/2d/3d Convolution
- 12.2 卷积 - nn.Conv2d()
- 12.3 转置卷积(Transpose Convlution)
- 13. nn中的网络层——池化层、线性层、激活函数层
- 13.1 池化层(Pooling Layer)
- 13.2 线性层(Linear Layer)
- 13.3 激活函数层
- 第四周
- 15. 损失函数(一)
- 15.1 损失函数概念
- 15.2 交叉熵损失函数
- 15.3 NLL / BCE / BCEWithLogits Loss
- 16. 损失函数(二)—— pytorch 中其余 14 中损失函数
- 16.1 回归中常用的两个损失函数
- 17. 优化器 Optimizer(一)
- 17.1 什么是优化器(Optimizer)
- 17.2 优化器的基本属性
- 17.3 优化器的基本方法
- 18. 常用的优化方法(优化器)
- 18.1 learning rate 学习率
- 18.2 momentum 动量
- 18.3 torch.optim.SGD
- 18.4 Pytorch的十种优化器
- 第五周
- 20. 可视化工具——TensorBoard
- 20.1 TensorBoard简介
- 20.2 TensorBoard安装
- 20.3 TensorBoard运行可视化
- 21. TensorBoard 使用(一)
- 21.1 SummaryWriter
- 提供创建event file的高级接口
- 21.2 模型指标监控
- 22. TensorBoard 使用(二)——TensorBoaed 的图像可视化方法
- 22.1 add_image and torchvision.utils.make_grid
- 22.2 AlexNet 卷积核与特征图可视化
- 22.3 add_graph and torchsummary
- 23. Hook 函数与 CAM 可视化
- 23.1 Hook函数概念
- 23.2 Hook 函数与特征图提取
- 23.3 CAM(Class Activation Map, 类激活图) and Grad-CAM
- 第六周
一、第一周:Pytorch基础概念
2. Pytorch的Tensor(张量)
Content
- Tensor概念
- Tensor创建一:直接创建
- Tensor创建二:依据数值创建
- Tensor创建三:依据概率创建
2.1 张量是什么(What is Tensor?)
张量是一个**多维数组**
,它是标量、向量、矩阵的高维拓展。
2.2 Tensor与Variable
Variable是torch.autograd中的数据类型,主要用于封装Tensor,进行自动求导。
data:被封装的Tensor
grad:data的梯度
grad_fn:创建Tensor的Function,是自动求导的关键
requires_grad:指示是否需要梯度
is_leaf:指示是否是叶子结点(张量)
Pytorch0.4.0版开始,Variable并入Tensor
dtype:张量的数据类型,如torch.FloatTensor、torch.cuda.FloatTensor
shape:张量的形状,如(64,3,224,224)
device:张量所在设备,GPU/CPU,是加速的关键
2.3 张量的创建——(1)直接创建张量
1. torch.tensor()
torch.tensor()
功能:从data创建tensor
- data:数据,可以是list,numpy
- dtype:数据类型,默认与data的一致
- device:所在设备,cuda/cpu
- requires_grad:是否需要梯度
- pin_memory:是否存于锁页内存
torch.tensor(
data,
dtype=None,
device=None,
requires_grad=False,
pin_memory=False)
2. torch.from_numpy(ndarray)
torch.from_numpy(ndarray)
功能:从numpy创建tensor
注意事项:从torch.from_numpy创建的tensor与原ndarray共享内存,当修改其中一个的数据,另一个也将会被修改。
2.4 张量的创建——(2)依据数值创建张量
1. torch.zeros()
torch.zeros()
功能:依size创建全0张量
- size:张量的形状,如(3,3)、(3,244,244)
- out:输出的张量
- layout:内存中布局形式,有strided、sparse_coo等
- device:所在设备,gpu/cpu
- requires_grad:是否需要梯度
torch.zeros(*size,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
2. torch.zeros_like()
torch.zeros_like()
功能:依input形状创建全0张量
- input:创建于input同形状的全0张量
- dtype:数据类型
- layout:内存中布局形式
torch.zeros_like(input,
dtype=None,
layout=None,
device=None,
requires_grad=False)
3. torch.ones()
4. torch.ones_like()
torch.ones()
torch.ones_like()
功能:依input形状创建全1张量
- size:张量的形状,如(3,3)、(3,244,244)
- input:创建于input同形状的全1张量
- dtype:数据类型
- layout:内存中布局形式,有strided、sparse_coo等
- layout:内存中布局形式
- device:所在设备,gpu/cpu
- requires_grad:是否需要梯度
torch.ones(*size,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
torch.ones_like(input,
dtype=None,
layout=None,
device=None,
requires_grad=False)
5. torch.full()
6. torch.full_like()
torch.full()
torch.full_like()
功能:依据input形状创建全值
张量
- size:张量的形状,如(3,3)
- full_value:张量的值
torch.full(size,
full_value,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
7. torch.arange()
torch.arange()
功能:创建等差的1维张量
注意事项:数值区间为[start,end)
- start:数列起始值
- end:数列”结束值”
- step:数列公差,默认为1
torch.arange(start=0,
end,
step=1,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
8. torch.linspace()
torch.linspace()
功能:创建均分的1维张量
注意事项:数值区间为[start,end]
- start:数列起始值
- end:数列结束值
- steps:数列长度
步长为:(end-start)/(steps-1)
torch.linspace(start,
end,
steps=100,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
9. torch.logspace()
torch.logspace()
功能:创建对数均分的1维张量
注意事项:数值区间为[start,end];长度为steps,底数为base
- start:数列起始值
- end:数列结束值
- steps:数列长度
- base:对数函数的底,默认为10
torch.logspace(start,
end,
steps=100,
base=10.0,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
10. torch.eye()
torch.eye()
功能:创建单位对角矩阵(2维张量)
注意事项:默认为方阵
- n:矩阵函数
- m:矩阵列数
torch.linspace(n,
m=None,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
2.5 张量的创建——(3)依概率分布创建张量
1. torch.normal()
torch.normal()
功能:生成正态分布(高斯分布)
- mean:均值
- std:标准差
四种模式:torch.normal(mean,
std,
out=None)
mean为标量,std为标量
mean为标量,std为张量
mean为张量,std为标量
mean为张量,std为张量torch.normal(mean,
std,
size,
out=None)
2. torch.randn()
3. torch.randn_like()
torch.randn()
torch.randn_like()
功能:生成标准正态分布
- size:张量的形状
torch.randn(*size,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
4. torch.rand()
5. torch.rand_like()
torch.rand()
torch.rand_like()
功能:在区间[0,1)上,生成均匀分布
torch.rand(*size,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
6. torch.randint()
7. torch.randint_like()
torch.randint()
torch.randint_like()
功能:在区间[low,high)生成整数均匀分布
- size:张量的形状
torch.randint(low,
high,
size,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
8. torch.randperm()
torch.randperm()
功能:生成从0到n-1的随机排列
- n:张量的长度
torch.randperm(n,
out=None,
dtype=None,
layout=torch.strided,
device=None,
requires_grad=False)
9. torch.bernoulli()
torch.bernoulli()
功能:以input为概率,生成伯努利分布(0-1分布,两点分布)
- input:概率值
torch.bernoulli(input,
*,
generator=None,
out=None)
3. 张量操作与线性回归
Content
- 张量的操作:拼接、切分、索引和变换
- 张量的数学运算
- 线性回归
3.1 张量的拼接与切分
1. torch.cat()
torch.cat()
功能:将张量按维度dim进行拼接
- tensors:张量序列
- dim:要拼接的维度
```python import torchtorch.cat(tensors,
dim=0,
out=None)
t = torch.ones((2, 3)) t0 = torch.cat([t, t], dim=0) t1 = torch.cat([t, t], dim=1)
print(‘t:\n{}\nt.shape:\n{}’.format(t,t.shape)) print(‘t0:\n{}\nt0.shape:\n{}’.format(t0,t0.shape)) print(‘t1:\n{}\nt1.shape:\n{}’.format(t1,t1.shape))
output: t: tensor([[1., 1., 1.], [1., 1., 1.]]) t.shape: torch.Size([2, 3]) t0: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) t0.shape: torch.Size([4, 3]) t1: tensor([[1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.]]) t1.shape: torch.Size([2, 6])
<a name="WMTJT"></a>
#### 2. torch.stack()
torch.stack()<br />功能:在新创建的维度dim上进行拼接
- tensors:张量序列
- dim:要拼接的维度
```python
torch.stack(tensors,
dim=0,
out=None)
# example2
# torch.stack
import torch
t = torch.ones((2, 3))
t0 = torch.stack([t, t], dim=2)
t1 = torch.stack([t, t], dim=0)
# 若dim指定的维度已经存在,则会创建一个新的维度,已存在的依次向后挪一个维度。
print('t:\n{}\nt.shape:\n{}'.format(t, t.shape))
print('t0:\n{}\nt0.shape:\n{}'.format(t0, t0.shape))
print('t1:\n{}\nt1.shape:\n{}'.format(t1, t1.shape))
ouput:
t:
tensor([[1., 1., 1.],
[1., 1., 1.]])
t.shape:
torch.Size([2, 3])
t0:
tensor([[[1., 1.],
[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.],
[1., 1.]]])
t0.shape:
torch.Size([2, 3, 2])
t1:
tensor([[[1., 1., 1.],
[1., 1., 1.]],
[[1., 1., 1.],
[1., 1., 1.]]])
t1.shape:
torch.Size([2, 2, 3])
3. torch.chunk()
torch.chunk()
功能:将张量按维度dim进行平均切分
返回值:张量列表
注意事项:若不能整除,最后一个张量小于其他张量
import torch
a = torch.ones((2, 5))
list_tensors = torch.chunk(a, dim=1, chunks=2) for idx, t in enumerate(list_tensors): print(‘第{}个张量:\n{}\nshape is:\n{}’.format(idx + 1, t, t.shape))
output: 第1个张量: tensor([[1., 1., 1.], [1., 1., 1.]]) shape is: torch.Size([2, 3]) 第2个张量: tensor([[1., 1.], [1., 1.]]) shape is: torch.Size([2, 2])
<a name="MXTua"></a>
#### 4. torch.split()
torch.split()<br />功能:将张量按维度dim进行切分<br />返回值:张量列表
- tensor:要切分的张量
- split_size_or_sections:为int时,表示每一份长度;为list时,按list元素切分
- dim:要切分的维度
注意事项:切分后,tensor指向切分完的最后一个张量
```python
torch.split(tensor,
split_size_or_sections,
dim=0)
# example4
# torch.split
import torch
t = torch.ones((2, 5))
list_tensors = torch.split(t, 2, dim=1)
for idx, t in enumerate(list_tensors):
print('第{}个张量:\n{}\nshape is:\n{}'.format(idx + 1, t, t.shape))
print('t:\n',t)
t = torch.ones((2, 5))
list_tensors = torch.split(t, [2, 1, 2], dim=1)
for idx, t in enumerate(list_tensors):
print('第{}个张量:\n{}\nshape is:\n{}'.format(idx + 1, t, t.shape))
output:
第1个张量:
tensor([[1., 1.],
[1., 1.]])
shape is:
torch.Size([2, 2])
第2个张量:
tensor([[1., 1.],
[1., 1.]])
shape is:
torch.Size([2, 2])
第3个张量:
tensor([[1.],
[1.]])
shape is:
torch.Size([2, 1])
t:
tensor([[1.],
[1.]])
第1个张量:
tensor([[1., 1.],
[1., 1.]])
shape is:
torch.Size([2, 2])
第2个张量:
tensor([[1.],
[1.]])
shape is:
torch.Size([2, 1])
第3个张量:
tensor([[1., 1.],
[1., 1.]])
shape is:
torch.Size([2, 2])
3.2 张量的索引
1. torch.index_select()
torch.index_select()
功能:在维度dim上,按index索引数据
返回值:依index索引数据拼接的张量
- input:要索引的张量
- dim:要索引的维度
- index:要索引数据的序号
```pythontorch.index_select(input,
dim,
index,
out=None)
example5
torch.index_select
import torch
t = torch.randint(0, 9, size=(3, 3)) idx = torch.tensor([0, 2], dtype=torch.long) t_select = torch.index_select(t, dim=0, index=idx) print(‘t:\n{}\nidx:\n{}\nt_select:\n{}’.format(t, idx, t_select))
output: t: tensor([[4, 4, 1], [5, 1, 8], [2, 1, 2]]) idx: tensor([0, 2]) t_select: tensor([[4, 4, 1], [2, 1, 2]])
<a name="NN86p"></a>
#### 2. torch.masked_select()
torch.index_select()<br />功能:按mask中的True进行索引<br />返回值:一维张量
- input:要索引的张量
- mask:与input同形状的布尔类型张量
```python
torch.masked_select(input,
mask,
out=None)
ge:>= gt:> le:<= lt:<
# example6
# torch.masked_select
import torch
t = torch.randint(0, 9, size=(3, 3))
mask = t.ge(5)
t_select = torch.masked_select(t, mask)
print('t:\n{}\nmask:\n{}\nt_select:\n{}'.format(t, mask, t_select))
output:
t:
tensor([[5, 3, 1],
[3, 8, 6],
[1, 5, 4]])
mask:
tensor([[ True, False, False],
[False, True, True],
[False, True, False]])
t_select:
tensor([5, 8, 6, 5])
3.3 张量的变换
1. torch.reshape()
torch.reshape()
功能:变换张量的形状
注意事项:当张量在内存中是连续存在时,新张量与input共享内存
import torch
t = torch.randperm(8) t_reshape = torch.reshape(t, (-1, 4)) print(‘t:\n{}\nt_reshape:\n{}’.format(t, t_reshape))
print(‘t.data 内存地址:{}’.format(id(t.data))) print(‘t_reshape.data 内存地址:{}’.format(id(t_reshape.data))) t[0] = 1024 print(‘t:\n{}\nt_reshape:\n{}’.format(t, t_reshape))
output: t: tensor([5, 7, 0, 2, 6, 1, 4, 3]) t_reshape: tensor([[5, 7, 0, 2], [6, 1, 4, 3]]) t.data 内存地址:1749654845224 t_reshape.data 内存地址:1749654845224 t: tensor([1024, 7, 0, 2, 6, 1, 4, 3]) t_reshape: tensor([[1024, 7, 0, 2], [ 6, 1, 4, 3]])
<a name="KRXRX"></a>
#### 2. torch.transpose()
torch.transpose()<br />功能:交换张量的两个维度
- input:要交换维度的张量
- dim0:要交换的维度
- dim1:要交换的维度
```python
torch.transpose(input,
dim0,
dim1)
# example8
# torch.transpose
import torch
t = torch.rand(2, 3, 4)
t_transpose = torch.transpose(t, dim0=1, dim1=2)
print('t:\n{}\nt_transpose:\n{}'.format(t, t_transpose))
print('t.shape:\n{}\nt_transpose.shape:\n{}'.format(t.shape, t_transpose.shape))
output:
t:
tensor([[[0.9669, 0.3727, 0.8775, 0.5900],
[0.1132, 0.6863, 0.9109, 0.8974],
[0.5809, 0.0539, 0.7609, 0.3699]],
[[0.9527, 0.2056, 0.6411, 0.6718],
[0.6573, 0.9133, 0.6319, 0.2841],
[0.5032, 0.6048, 0.4830, 0.4872]]])
t_transpose:
tensor([[[0.9669, 0.1132, 0.5809],
[0.3727, 0.6863, 0.0539],
[0.8775, 0.9109, 0.7609],
[0.5900, 0.8974, 0.3699]],
[[0.9527, 0.6573, 0.5032],
[0.2056, 0.9133, 0.6048],
[0.6411, 0.6319, 0.4830],
[0.6718, 0.2841, 0.4872]]])
t.shape:
torch.Size([2, 3, 4])
t_transpose.shape:
torch.Size([2, 4, 3])
3. torch.t()
torch.t()
功能:2维张量转置,对矩阵而言,等价于 torch.transpose(input,0,1)
torch.t(input)
4. torch.squeeze()
torch.squeeze()
功能:压缩长度为1的维度(轴)
- dim:若为None,移除所有长度为1的轴;若指定维度,当且仅当该轴长度为1时,可以被移除。
```pythontorch.squeeze(input,
dim=None,
out=None)
example9
torch.squeeze
import torch
t = torch.rand((1, 2, 3, 1)) t_sq = torch.squeeze(t) t0 = torch.squeeze(t, dim=0) t1 = torch.squeeze(t, dim=1)
print(‘t.shape:\n{}\nt_sq.shape:\n{}’.format(t.shape, t_sq.shape)) print(‘t0.shape:\n{}\nt1.shape:\n{}’.format(t0.shape, t1.shape))
output: t.shape: torch.Size([1, 2, 3, 1]) t_sq.shape: torch.Size([2, 3]) t0.shape: torch.Size([2, 3, 1]) t1.shape: torch.Size([1, 2, 3, 1])
<a name="eZVKM"></a>
#### 5. torch.unsqueeze()
torch.unsqueeze()<br />功能:依据dim扩展维度
- dim:扩展的维度
```python
torch.usqueeze(input,
dim,
out=None)
6. torch.flatten()
torch.flatten(input, start_dim=0, end_dim=-1)
功能:展平/推平一个连续范围的维度,输出类型为 Tensor
- input: 一个 tensor,即要被“推平”的 Tensor。
- start_dim: “推平”的起始维度。
- end_dim: “推平”的结束维度。
首先如果按照 start_dim 和 end_dim 的默认值,那么这个函数会把 input 推平成一个 shape 为 [n] 的tensor,其中 n 即 input 中元素个数
当 start_dim = 1 而 end_dim = −1 时,它把第 1 个维度到最后一个维度全部推平合并了。而当 start_dim = 0 而 end_dim = 1 时,它把第 0 个维度到第 1 个维度全部推平合并了。
import torch
t = torch.tensor([[[1, 2, 2, 1],
[3, 4, 4, 3],
[1, 2, 3, 4]],
[[5, 6, 6, 5],
[7, 8, 8, 7],
[5, 6, 7, 8]]])
print('t:\n{}\nt.shape:\n{}'.format(t, t.shape))
x = torch.flatten(t, start_dim=1)
print('x:\n{}\nx.shape:\n{}'.format(x, x.shape))
y = torch.flatten(t, start_dim=0, end_dim=1)
print('y:\n{}\ny.shape:\n{}'.format(y, y.shape))
output:
"""
t:
tensor([[[1, 2, 2, 1],
[3, 4, 4, 3],
[1, 2, 3, 4]],
[[5, 6, 6, 5],
[7, 8, 8, 7],
[5, 6, 7, 8]]])
t.shape:
torch.Size([2, 3, 4])
x:
tensor([[1, 2, 2, 1, 3, 4, 4, 3, 1, 2, 3, 4],
[5, 6, 6, 5, 7, 8, 8, 7, 5, 6, 7, 8]])
x.shape:
torch.Size([2, 12])
y:
tensor([[1, 2, 2, 1],
[3, 4, 4, 3],
[1, 2, 3, 4],
[5, 6, 6, 5],
[7, 8, 8, 7],
[5, 6, 7, 8]])
y.shape:
torch.Size([6, 4])
"""
3.4 张量的数学运算——加减乘除
1. torch.add()
torch.add()
功能:逐元素计算 input+alpha×other
- input:第一个张量
- alpha:乘项因子
- other:第二个张量
```pythontorch.add(input
alpha=1,
other,
out=None)
example10
torch.add
import torch
t0 = torch.randn((3, 3)) t1 = torch.ones_like(t0) t_add = torch.add(t0, 10, t1)
print(‘t0:\n{}\nt1:\n{}\nt_add:\n{}’.format(t0, t1, t_add))
output: t0: tensor([[-0.1253, -0.2573, -2.2917], [ 1.4232, 0.1650, -0.1312], [ 0.7477, 0.1264, -0.6293]]) t1: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) t_add: tensor([[ 9.8747, 9.7427, 7.7083], [11.4232, 10.1650, 9.8688], [10.7477, 10.1264, 9.3707]])
<a name="bhNjB"></a>
#### 2. torch.addcdiv()
torch.addcdiv()<br />功能:逐元素计算 input + value * tensor1 / tensor2
torch.addcmul()<br />功能:input + value * tensor1 * tensor2
```python
torch.addcmul(input,
value=1,
tensor1,
tensor2,
out=None)
torch.sub()
torch.div()
torch.mul()
3.5 张量的数学运算——对指幂函数
torch.log(input,out=None)
torch.log10(input,out=None)
torch.log2(input,out=None)
torch.exp(input,out=None)
torch.pow()
3.6 张量的数学运算——三角函数
torch.abs(input,out=None)
torch.acos(input,out=None)
torch.cosh(input,out=None)
torch.cos(input,out=None)
torch.asin(input,out=None)
torch.atan(input,out=None)
torch.atan2(input,other,out=None)
3.7 线性回归
线性回归是分析一个变量与另外一(多)个变量之间关系的方法
因变量:y 自变量:x 关系:线性
y = wx + b
求解步骤:
- 确定模型 Model:y = wx + b
选择损失函数 MSE:
求解梯度并更新w,b w = LR w.grad b = b - LR b.grad (LR:学习率,步长)
线性回归模型示例:
# -*- coding:utf-8 -*-
"""
@file_name :lesson1.3-Linear_Regression
@author :
@data :2021-05-07
@brief :一元线性回归
"""
import torch
from matplotlib import pyplot as plt
torch.manual_seed(10)
lr = 0.1 # 学习率
# 创建训练数据
x = torch.rand(20, 1) * 10 # x data (tensor),shape=(20,1)
y = 2 * x + (5 + torch.randn(20, 1)) # y data (tensor),shape=(20,1)
# 构建线性回归参数
w = torch.randn((1), requires_grad=True)
b = torch.zeros((1), requires_grad=True)
for iteration in range(1000):
# 前向传播
wx = torch.mul(w, x)
y_pred = torch.add(wx, b)
# 计算 MSE loss
loss = (0.5 * (y - y_pred) ** 2).mean()
# 反向传播
loss.backward()
# 更新参数
b.data.sub_(lr * b.grad)
w.data.sub_(lr * w.grad)
# 绘图
if iteration % 20 == 0:
plt.scatter(x.data.numpy(), y.data.numpy())
plt.plot(x.data.numpy(), y_pred.data.numpy(), 'r-', lw=5)
plt.text(2, 20, 'Loss=%0.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.xlim(1.5, 10)
plt.ylim(8, 28)
plt.title('Iteration:{}\nw:{} b:{}'.format(iteration, w.data.numpy(), b.data.numpy()))
plt.pause(0.5)
if loss.data.numpy() < 1:
break
4. 计算图与动态图机制
Content
- 计算图
- Pytorch的动态图机制
4.1 计算图
- 计算图是用来描述运算的有向无环图
- 计算图两个主要元素:结点Node,边Edge
- 结点表示数据,如向量/矩阵/张量
- 边表示运算,如加/减/乘/除/卷积
用计算图表示:y = (x + w) * (w + 1)
计算图与梯度求导
y = (x + w) (w + 1)
a = x + w b = w + 1
y = a b
叶子结点
叶子节点:用户创建的结点,如上述的 x 和 w 。is_leaf
:指示张量是否为叶子结点
非叶子节点梯度会被释放
grad_fn:记录创建该张量时所用的方法/函数
叶子结点:grad_fn = None
# y.grad_fn = <MulBackward0>
# a.grad_fn = <AddBackward0>
# b.grad_fn = <AddBackward0>
# example1
# 计算图示例
import torch
w = torch.tensor([1., ], requires_grad=True)
x = torch.tensor([2., ], requires_grad=True)
a = torch.add(w, x)
a.retain_grad() # 保存非叶子结点a的梯度
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
print(w.grad) # w=5
# 查看叶子节点
# print("is_leaf:\n", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf) # TTFFF
print('w.is_leaf:', w.is_leaf)
print('x.is_leaf:', x.is_leaf)
print('a.is_leaf:', a.is_leaf)
print('b.is_leaf:', b.is_leaf)
print('y.is_leaf:', y.is_leaf)
# 查看梯度
# print("gradient:\n", w.grad, x.grad, a.grad, b.grad, y.grad)
print('w.grad:', w.grad)
print('x.grad:', x.grad)
print('a.grad:', a.grad)
print('b.grad:', b.grad)
print('y.grad:', y.grad)
# 查看 grad_fn
# print("grad_fn:\n", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn) # NNAAM
print('w.grad_fn:', w.grad_fn)
print('x.grad_fn:', x.grad_fn)
print('a.grad_fn:', a.grad_fn)
print('b.grad_fn:', b.grad_fn)
print('y.grad_fn:', y.grad_fn)
output:
tensor([5.])
w.is_leaf: True
x.is_leaf: True
a.is_leaf: False
b.is_leaf: False
y.is_leaf: False
w.grad: tensor([5.])
x.grad: tensor([2.])
a.grad: tensor([2.])
b.grad: None
y.grad: None
w.grad_fn: None
x.grad_fn: None
a.grad_fn: <AddBackward0 object at 0x000001974E7E15C8>
b.grad_fn: <AddBackward0 object at 0x000001974E7E1608>
y.grad_fn: <MulBackward0 object at 0x000001974E7E15C8>
4.2 动态图
动态图 VS. 静态图
根据计算图搭建方式,可将计算图分为动态图和静态图
5. 自动求导系统torch.autograd及逻辑回归实现
Content
- torch.autograd
- 逻辑回归
5.1 autograd——自动求导系统
1. torch.autograd.backward()
torch.autograd.backward()
功能:自动求取梯度
- tensors:用于求导的张量,如loss
- retain_graph:保存计算图
- create_graph:创建导数计算图,用于高阶求导
- grad_tensors:多梯度权值
torch.autograd.backward(tensors,
grad_tensors=None,
retain_graph=None,
create_graph=Flase)
# example1
# retain_graph
import torch
torch.manual_seed(10)
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([1.], requires_grad=True)
a = torch.add(w, x)
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward(retain_graph=True)
print(w.grad)
y.backward()
print(w.grad)
output:
tensor([4.])
tensor([8.])
# example2
# grad_tensors
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x)
b = torch.add(w, 1)
y0 = torch.mul(a, b) # y0=(x + w)*(w + 1) dy0/dw=5
y1 = torch.add(a, b) # y1=(x + w)+(w + 1) dy1/dw=2
loss = torch.cat([y0, y1], dim=0)
grad_tensors = torch.tensor([1., 2.])
loss.backward(gradient=grad_tensors)
# gradient 传入 torch.autograd.backward()中的grad_tensors
print(w.grad)
output:
tensor([9.])
2. torch.autograd.grad()
torch.autograd.grad()
功能:求取梯度
- outputs:用于求导的张量,如loss
- inputs:需要梯度的张量
- create_graph=Flase:创建导数计算图,用于高阶求导
- retain_graph=None:保存计算图
- grad_tensors=None:多梯度权值
torch.autograd.grad(outputs,
inputs,
grad_tensors=None,
retain_graph=None,
create_graph=Flase)
# example3
# torch.autograd.grad
import torch
x = torch.tensor([3.], requires_grad=True)
y = torch.pow(x, 2)
grad_1 = torch.autograd.grad(y, x, create_graph=True)
print(grad_1)
grad_2 = torch.autograd.grad(grad_1[0], x)
print(grad_2)
output:
(tensor([6.], grad_fn=<MulBackward0>),)
(tensor([2.]),)
autograd小贴士:
梯度不自动清零,使用 w.grad.zero_() 操作清零
依赖于叶子结点的结点,
requires_grad
默认为True叶子结点不可执行
in-place
in-place:原位操作,在原有内存地址上进行操作
# example3
# tips-1
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
for i in range(3):
a = torch.add(w, x)
b = torch.add(w, 1)
y = torch.mul(a, b)
y.backward()
print(w.grad)
if i ==1:
w.grad.zero_()
output:
tensor([5.])
tensor([10.])
tensor([5.])
# example4
# tips-2
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x)
b = torch.add(w, 1)
y = torch.mul(a, b)
print('a.requires_grad:',a.requires_grad)
print('b.requires_grad:',b.requires_grad)
print('y.requires_grad:',y.requires_grad)
output:
a.requires_grad: True
b.requires_grad: True
y.requires_grad: True
5.2 逻辑回归
逻辑回归是线性的二分类模型
模型表达式:
f(x)称为Sigmoid函数,也称为Logistics函数。
逻辑回归与线性回归
线性回归是分析自变量x与因变量y(标量)之间关系的方法
逻辑回归是分析自变量x与因变量y(概率)之间关系的方法
逻辑回归又称对数几率回归
机器学习模型训练步骤:
- 数据:数据采集、数据清洗、数据划分、数据预处理
- 模型:线性模型/神经网络模型
- 损失函数:线性模型中采用的均方差损失函数、分类任务中的交叉熵
- 优化器:更新权值
- 迭代训练:反复迭代训练
逻辑回归模型示例:
# -*- coding:utf-8 -*-
"""
@file_name :lesson1.5-Logistic_Regression
@author :
@data :2021-05-09
@brief :逻辑回归
"""
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
# ========== step 1/5 生成数据 ==========
sample_nums = 100
mean_value = 1.7
bias = 1
n_data = torch.torch.ones(sample_nums, 2)
x0 = torch.normal(mean_value * n_data, 1) + bias # 类别0 数据 shape=(100,2)
y0 = torch.zeros(sample_nums) # 类别0 标签 shape=(100,1)
x1 = torch.normal(-mean_value * n_data, 1) + bias # 类别1 数据 shape=(100,2)
y1 = torch.ones(sample_nums) # 类别1 标签 shape=(100,1)
train_x = torch.cat((x0, x1), 0)
train_y = torch.cat((y0, y1), 0)
# ========== step 2/5 选择数据 ==========
class LR(nn.Module):
def __init__(self):
super(LR, self).__init__()
self.features = nn.Linear(2, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.features(x)
x = self.sigmoid(x)
return x
lr_net = LR() # 实例化逻辑回归模型
# ========== step 3/5 选择损失函数 ==========
loss_fn = nn.BCELoss()
# ========== step 4/5 选择优化器 ==========
lr = 0.01 # 学习率
optimizer = torch.optim.SGD(lr_net.parameters(), lr=lr, momentum=0.9)
# ========== step 5/5 模型训练 ==========
for iteration in range(1000):
# 前向传播
y_pred = lr_net(train_x)
# 计算loss
loss = loss_fn(y_pred.squeeze(), train_y)
# 方向传播
loss.backward()
# 更新参数
optimizer.step()
# 绘图
if iteration % 20 == 0:
mask = y_pred.ge(0.5).float().squeeze() # 以0.5为阀值进行分类
correct = (mask == train_y).sum() # 计算正确预测的样本个数
acc = correct.item() / train_y.size(0) # 计算分类准确率
plt.scatter(x0.data.numpy()[:, 0], x0.data.numpy()[:, 1], c='r', label='class0')
plt.scatter(x1.data.numpy()[:, 0], x1.data.numpy()[:, 1], c='b', label='class1')
w0, w1 = lr_net.features.weight[0]
w0, w1 = float(w0.item()), float(w1.item())
plot_b = float(lr_net.features.bias[0].item())
plot_x = np.arange(-6, 6, 0.1)
plot_y = (-w0 * plot_x - plot_b) / w1
plt.xlim(-5, 7)
plt.ylim(-7, 7)
plt.plot(plot_x, plot_y)
plt.text(-5, 5, 'Loss=%0.4f' % loss.data.numpy(), fontdict={'size': 20, 'color': 'red'})
plt.title('Iteration: {}\nw0:{:.2f} w1:{:.2f} b:{:.2f} accuracy:{:.2f}'.format(iteration, w0, w1, plot_b, acc))
plt.legend()
plt.show()
plt.pause(0.5)
if acc > 0.99:
break
二、第二周:Pytorch数据处理
数据:
- 数据收集:数据又原始样本和标签
- 数据划分:数据会划分为训练集、验证集和测试集(train、valid、test)。
- 训练集->训练模型;
- 验证集->验证模型是否过拟合,也就是挑选模型,挑选那些还没有过拟合的模型;
- 测试集->测试挑选出来的模型的性能
- 数据读取:DataLoader
- Sampler->生成索引
- Dataset->根据索引去读取图片和标签
- 数据预处理:transforms
6. 数据读取模块——DataSet与DataLoader
Content
- 人民币二分类
- DataLoader与Dataset
6.1 DataLoader与Dataset
1. torch.utils.data.DataLoader
torch.utils.data.DataLoader
功能:构建可迭代的数据装载器
- dataset:Dataset类,决定数据从哪里读取
- batch_size:批大小
- num_workers:是否多进程读取数据
- shuffle=False:每个epoch是否乱序
- drop_last=False:当样本数不能被batchsize整除时,是否舍弃最后一批数据
torch.utils.data.DataLoader()
DataLoader(dataset,
batch_size=1,
shuffle=False,
sampler=None,
batch_sampler=None,
num_workers=0,
collate_fn=None,
pin_memory=False,
drop_last=False,
timeout=0,
worker_in it_fn=None,
multiprocessing_context=None)
epoch:所有训练样本都已输入到模型中,称为1个epoch
iteration:一批样本输入到模型中,称为1个iteration
batchsize:批大小,决定了一个epoch有多少个iteration
例如:
样本总数:87,batchsize:8
drop_last = True:1 epoch = 10 iteration # 舍弃掉后七个样本
drop_last = Flase:1 epoch = 11 iteration
2. touch.utils.data.Dataset
touch.utils.data.Dataset
功能:Dataset抽象类,所有自定义的Dataset都需要继承它,并且复写 __getitem__()
(接受一个索引,返回一个样本)
torch.utils.data.Dataset()
class Dataset(object):
def __getitem__(self, index):
# 接收一个索引,返回一个样本
raise NotImplementedError
def __add__(self, other):
return ConcatDataset([self, other])
6.2 人民币二分类
数据读取:
- 读哪些数据? Sampler输出Index
- 从哪读数据? Dataset中的data_dir
- 怎么读数据? Dataset中的getitem
7. 图像数据预处理模块——transforms
Content
- transforms运行机制
- 数据标准化——transforms.normalize
7.1 transforms运行机制
1. 计算机视觉工具包:torchvision
torchvision.transforms:常用的图像预处理方法
torchvision.datasets:常用数据集,如MNIST/CIFAR-10/ImageNet
torchvision.model:常用的模型预训练,如AlexNet/VGG/ResNet/GoogLeNet
2. 常用图像预处理方法:transforms
数据中心化/标准化
缩放/裁剪/旋转/翻转/填充/噪声添加
灰度变换/线性变换/仿射变换/亮度、饱和度及对比度变换
7.2 数据标准化——transforms.Normalize
1. transform.Normalize
transform.Normalize
功能:逐channel(通道)的对图像进行标准化,加快模型的收敛速度
output = (input - mean) / std ==> 得到0均值1标准差的数据分布
- mean:各通道的均值
- std:各通道的标准差
- inplace:是否原地操作
# 对图像的每个通道进行标准化
# output = (input - mean) / std
transforms.Normalize(mean,
std,
inplace=False)
8. transforms图像增强(一)
Content
- 数据增强
- transforms——裁剪
- transforms——翻转和旋转
8.1 数据增强(Data Augmentation)
数据增强又称为数据增广、数据扩增,它是对训练集进行变换,使训练集更丰富,从而让模型更具泛化能力。
原则:使得训练集与测试集更接近
- 空间位置:平移
- 色彩:灰度图,色彩抖动
- 形状:仿射变换
- 上下文场景:遮挡,填充
8.2 transforms——裁剪(Crop)
1. transforms.CenterCrop
transforms.CenterCrop
功能:从图像中心裁剪图片
- size:所需裁剪图片尺寸(也就是裁剪后图片的尺寸)
# 1. 从图像中心裁剪
transforms.CenterCrop(size) # 所需裁剪图片尺寸
2. transforms.RandomCrop
transforms.RandomCrop
功能:从图片中随机裁剪出尺寸为size的图片
- size:所需裁剪图片尺寸
- padding:设置填充大小
- 当为 a 时,上下左右均填充 a 个像素
- 当为 (a,b) 时,上下填充 b 个像素,左右填充 a 个像素
- 当为 (a,b,c,d) 时,左、上、右、下分别填充 a,b,c,d 个像素
- pad_if_need:若图像小于设定的size,则填充
- padding_mode:填充模式,有 4 种模式
- constant:像素值由 fill 设定
- edge:像素值由图像边缘像素决定
- reflect:镜像填充,最后一个像素不镜像,例:[1,2,3,4] — [3,2,1,2,3,4,3,2]
- symmetric:镜像填充,最后一个像素镜像,例:[1,2,3,4] — [2,1,1,2,3,4,4,3]
- fill:constant时,设置填充的像素值
transforms.RandomCrop(size, # 所需裁剪图片的尺寸
padding=None, # 设置填充大小
pad_if_needed=False, # 若图像小于设定size,则填充
fill=0, # 填充模式为constant时,设置填充的像素值,如(R,G,B)/(Gray)
padding_mode='constant') # 填充模式,共有4种
3. transforms.RandomResizedCrop
transforms.RandomResizedCrop
功能:随机大小。长宽比裁剪图片
- size:所需裁剪图片的尺寸
- scale=(0.08, 1.0):随机裁剪面积比例
- ratio=(3/4, 4/3):随即长宽比
- interpolation:插值方法,最近邻/双线性/双三次插值
- PIL.Image.NEAREST 最近邻
- PIL.Image.BILINEAR 双线性
- PIL.Image.BICUBIC 双三次插值
# 3. 随机大小、长宽比裁剪图片
transforms.RandomResizedCrop(size, # 所需裁剪图片的尺寸
scale=(0.08, 1.0), # 随机裁剪面积比例
ratio=(3/4, 4/3) # 随机长宽比
interpolation) # 插值方法,最近邻/双线性/双三次插值
4. transforms.FiveCrop
5. transforms.TenCrop
transforms.FiveCrop
transforms.TenCrop
功能:在图像的上下左右以及中心裁剪出尺寸为size的5张图片,transforms.TenCrop对这 5 张图片进行水平或者垂直镜像获得 10 张图片。
- size:所需裁剪图片尺寸
- vertical_flip:是否垂直翻转
# 4. 从图像上下左右及中心裁剪出尺寸为size的5张图像
transforms.FiveCrop(size) # 返回一个tuple
# 5. 对上述5张图像进行水平或者垂直镜像获得10张图像
transforms.TenCrop(size, # 所需裁剪图片尺寸
vertical_flip=False) # 是否垂直翻转
8.3 transforms——翻转与旋转(Flip and Rotation)
1. RandomHorizontalFlip
2. RandomVerticalFlip
RandomHorizontalFlip
RandomVerticalFlip
功能:依概率水平(左右)或垂直(上下)翻转图片
- p:翻转概率
# 水平翻转
transforms.RandomHorizontalFlip(p=0.5)
# 垂直翻转
transforms.RandomVerticalFlip(p=0.5)
3. transforms.RandomRotation
transforms.RandomRotation
功能:随机旋转图片
- degrees:旋转角度
- 当degrees=a时,在(-a, a)之间选择旋转角度
- 当degrees=(a, b)时,在(a, b)之间选择旋转角度
- resample=False:重采样方法
- expand=False:是否扩大图片,以保持原图信息
- center=None:旋转点设置,默认中心旋转
# 2. 旋转
transforms.RandomRotation(degrees, # 旋转角度
resample=False, # 重采样方法
expand=False, # 是否扩大图片,以保持原图信息
center=None) # 旋转点设置,默认中心旋转
9. transforms图像增强(二)
Content
- transforms——图像变换
- transforms——transforms方法操作
- 自定义 transforms 方法
9.1 transforms——图像变换(Data Augmentation)
1. transforms.Pad
transforms.Pad
功能:对图片的边缘进行填充
- padding:设置填充大小
- 当padding=a时,上下左右均填充a个像素
- 当padding=(a, b)时,上下填充b个像素,左右填充a个像素
- 当padding=(a, b, c, d)时,左/上/右/下分别填充a/b/c/d个像素
- padding_mode:填充模式,有 4 种模式,constant、edge、reflect、symmetric
- fill:填充模式为constant时,设置填充的像素值,如(R,G,B)/(Gray)
# 1. 填充图像边缘
transforms.Pad(padding, # 设置填充大小
# 当padding=a时,上下左右均填充a个像素
# 当padding=(a, b)时,上下填充b个像素,左右填充a个像素
# 当padding=(a, b, c, d)时,左/上/右/下分别填充a/b/c/d个像素
fill=0, # 填充模式为constant时,设置填充的像素值,如(R,G,B)/(Gray)
padding_mode='constant') # 填充模式,constant/edge/reflect/symmetric
2. transforms.ColorJitter
transforms.ColorJitter
功能:调整亮度、对比度、饱和度和色相
- brightness:亮度调整因子
- 当brightness=a时,在[max(0, 1-a), 1+a]之间选择
- 当brightness=(a, b)时,在[a, b]之间选择
- contrast:对比度参数,同brightness
- saturation:饱和度参数,同brightness
- hue=0:色相参数
- 当hue=a时,在[-a, a]之间选择
- 当hue=(a, b)时,在[a, b]之间选择
# 2. 调整图像亮度/对比度/饱和度/色相
transforms.ColorJitter(brightness=0, # 亮度调整因子
# 当brightness=a时,在[max(0, 1-a), 1+a]之间选择
# 当brightness=(a, b)时,在[a, b]之间选择
contrast=0, # 对比度参数,同brightness
saturation=0, # 饱和度参数,同brightness
hue=0) # 色相参数
# 当hue=a时,在[-a, a]之间选择
# 当hue=(a, b)时,在[a, b]之间选择
3. transforms.Grayscale
4. transforms.RandomGrayscale
transforms.Grayscale
transforms.RandomGrayscale
功能:依概率将图片转换为灰度图
- num_ouput_channels:输出通道数
- p:概率值,图像被转换为灰度图的概率
注意事项:transforms.Grayscale 是 transforms.RandomGrayscale p = 1 时的特例
# 3. 灰度图转换
transforms.Grayscale(num_output_channels) # 输出通道数,1或3
transforms.RandomGrayscale(num_output_channels,
p=0.1) # 概率值
5. transforms.RandomAffine
transforms.RandomAffine
功能:对图像进行仿射变换,仿射变换是二维的线性变换,由五种基本原子变换构成,分别是旋转、平移、缩放、错切和翻转。
- degrees:设置旋转角度
- translate:平移区间设置
- 如 translate = (a, b) ,a 设置宽(width),b 设置高(height);
- 图像在宽维度平移的区间为 -img_width a < dx < img_width a
- 图像在高维度平移的区间为 -img_height b < dy < img_height b
- scale:缩放比例(以面积为单位)
- fillcolor:填充颜色设置
- shear:错切角度设置,有水平错切和垂直错切
- 当 shear = a 时,仅在 x 轴错切,错切角度在 (-a, a) 之间
- 当 shear = (a, b) 时,则 x 轴在 (-a, a) 之间随机选择错切角度,y 轴在 (-b, b) 之间随机选择错切角度
- 当shear=(a, b, c, d) 时,则 x 轴在 (a, b) 之间随机选择错切角度,y 轴在 (c, d) 之间随机选择错切角度
- resample:重采样方式,NEAREST、BILINEAR、BICUBIC
# 5. 仿射变换
# 仿射变换是二维的线性变换,由五种基本原子变换构成,旋转/平移/缩放/错切/翻转
transforms.RandomAffine(degrees, # 设置旋转角度
translate=None, # 平移区间设置
# 例:translate=(a, b),a为宽,b为高
# 图像在宽维度平移的区间为 -img_width * a < dx < img_width * a
# 图像在高维度平移的区间为 -img_height * b < dy < img_height * b
scale=None, # 缩放比例
shear=None, # 错切角度设置
# 当shear=a时,仅在x轴错切,错切角度在(-a, a)之间
# 当shear=(a, b)时,则a设置x轴角度,b设置y轴角度
# 当shear=(a, b, c, d)时,则a、b设置x轴角度,c、d设置y轴角
resample=False, # 重采样方式,NEAREST/BILINEAR/BICUBIC
fillcolor=0) # 填充颜色设置
6. transforms.RandomErasing
transforms.RandomErasing
功能:对图像进行随机遮挡
- p:概率值,执行该操作的概率
- scale:遮挡区域面积
- ratio=:遮挡区域长宽比
- value:遮挡区域像素值,如(R,G,B)/(Gray)/‘random’(只要是字符串就会随机)
- inplace
注意事项:transforms.RandomErasing
是对张量进行操作,所以执行 transforms.RandomErasing 之前,一般需要执行 transforms.ToTensor
# 6. 随机遮挡
transforms.ToTensor()
transforms.RandomErasing(p=0.5, # 执行遮挡的概率
scale=(0.02, 0.33), # 遮挡区域面积
ratio=(0.3, 3.3) # 遮挡区域长宽比
value=0, # 遮挡区域像素值,如(R,G,B)/(Gray)/'random'
inplace=False)
7. transforms.ToTensor
transforms.ToTensor
功能:将 PILImage 或者 numpy 的 ndarray 转化成 Tensor ;把一个取值范围是 [0,255] 的 PIL.Image 转换成 Tensor ,shape 为 (H,W,C) 的 numpy.ndarray ,转换成形状为 [C,H,W] ,取值范围是 [0,1.0] 的 Tensor 。
对于 PILImage 转化的 Tensor ,其数据类型是 torch.FloatTensor 对于 ndarray 的数据类型没有限制,但转化成的 Tensor 的数据类型是由 ndarray 的数据类型决定的。
8. transforms.Lambda
transforms.Lambda
功能:用户自定义 lambda 方法
- lambda:lambda 匿名函数
- lambda[arg1 [,arg2,…,argn]] : expression
# 8. 自定义lambda方法
transforms.Lambda(lambd) # 匿名函数 lambda[arg1 [,arg2,...,argn]] : expression
- lambda[arg1 [,arg2,…,argn]] : expression
9.2 transforms——transforms方法操作(Transforms Operation)
1. transforms.RandomChoice
transforms.RandomChoice
功能:从一系列 transforms 方法中随机挑选一个
transforms.RandomChoice([transforms1, transforms2, transforms3])
2. transforms.RandomApply
transforms.RandomApply
功能:依概率执行一组 transforms 操作
transforms.RandomApply([transforms1, transforms2, transforms3], p=0.5)
3. transforms.RandomOrder
transforms.RandomOrder
功能:对一组 transforms 操作打乱顺序
transforms.RandomOrder([transforms1, transforms2, transforms3])
9.3 自定义 transforms 方法(User-Defined Transforms)
自定义 transforms 方法要素:
- 仅接受一个参数,返回一个参数
- 注意上下游的输出与输入
class Compose(object):
def __call__(self, img):
for t in self.transform:
img = t(img)
return img
通过类实现多参数传入:
# 通过类实现多参数传入
class YoutTransforms(object):
def __init__(self, ...):
...
def __call__(self, img):
...
return img
椒盐噪声
椒盐噪声又称为脉冲噪声,是一种随机出现的白点或者黑点,白点成为盐噪声,黑色成为椒噪声
信噪比(Signal-Noise Rate,SNR)是衡量噪声的比例,图像中为图像像素的占比
class AddPepperNoise(object):
def __init__(self, snr, p):
self.snr = snr
self.p = p
def __call__(self, img):
"""
添加椒盐噪声具体实现过程
"""
return img
class Compose(object):
def __call__(self,img):
for t in self.transforms:
img = t(img)
return img
transforms 方法总结
第三周 模型模块
10. 模型创建与 nn.Module
Content
- 网络模型创建步骤
- nn.Module属性
10.1 网络模型创建步骤(Steps of Creating Module)
模型创建两要素:
- 构建子模块(构建网络层) ==> init()
- 拼接子模块(拼接网络层) ==> forward()
10.2 nn.Module属性
1. torch.nn
torch.nn:
nn.Parameter
:张量子类,表示可学习参数,如weight/权重、bias/偏置nn.Module
:所有网络层基类,管理网络属性- nn.functional:函数具体实现,如卷积、池化、激活函数等
- nn.init:参数初始化方法
2. nn.Module
nn.Module
功能:所有网络层基类,管理网络属性
nn.Module有 8 个属性,这个 8 个属性都是有序字典(OrderDict):
paramters
:存储管理nn.Parameter
类modules
:存储管理nn.Module
类- buffers:存储管理缓冲属性,如 BN 层中 running_mean
- *_hooks:存储管理钩子函数
self._parameter = OrderDict()
self._buffers = OrderDict()
self._backward_hooks = OrderDict()
self._forward_hooks = OrderDict()
self._forward_pre_hooks = OrderDict()
self._state_dict_hooks = OrderDict()
self._load_state_pre_dict_hooks = OrderDict()
self._modules = OrderDict()
3. nn.Module总结
nn.Module总结:
- 一个module可以包含多个子module
- 一个module相当于一个运算,必须实现
forward()
函数 - 每个module都有 8 个有序字典(OrderDict)管理它的属性
11. 模型容器Containers与AlexNet构建
Content
- 模型容器(Containers)
- AlexNet构建
11.1 模型容器——Containers
1. Containers
Containers:
nn.Sequetial
:按顺序包装多个网络层nn.ModuleList
:像Python的 list 一样包装多个网络层nn.ModuleDict
:像Python的 dict 一样包装多个网络层
2. nn.Sequetial
nn.Sequetial
功能:nn.Sequetial 是 nn.module 的容器,用于按顺序包装一组网络层
特性:
顺序性
:各网络层之间严格按照顺序构建自带forward()
:自带的 forward 里,通过 for 循环依次执行前向传播运算
注意:nn.Sequetial 中也可以传入字典类型的参数,可以实现给各个网络层命名的效果
实例:
LeNet模块 = features子模块 + classifier子模块
# Conv1-pool1-Conv2-pool2-fc1-fc2-fc3
# ============================ Sequential
class LeNetSequential(nn.Module):
def __init__(self, classes):
super(LeNetSequential, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 6, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2), )
self.classifier = nn.Sequential(
nn.Linear(16 * 5 * 5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, classes), )
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
fake_img = torch.randn((4, 3, 32, 32), dtype=torch.float32)
net = LeNetSequential(classes=2)
output = net(fake_img)
print(net)
print(output)
output:
LeNetSequential(
(features): Sequential(
(0): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(4): ReLU()
(5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(classifier): Sequential(
(0): Linear(in_features=400, out_features=120, bias=True)
(1): ReLU()
(2): Linear(in_features=120, out_features=84, bias=True)
(3): ReLU()
(4): Linear(in_features=84, out_features=2, bias=True)
)
)
tensor([[ 0.0570, -0.0268],
[ 0.0579, -0.0404],
[ 0.0660, -0.0268],
[ 0.0500, -0.0363]], grad_fn=<AddmmBackward>)
通过给 nn.Sequetial 传入字典类型的参数,以实现给各个网络层命名的效果
# Conv1-pool1-Conv2-pool2-fc1-fc2-fc3
# ============================ SequentialOrderDict
class LeNetSequentialOrderDict(nn.Module):
def __init__(self, classes):
super(LeNetSequentialOrderDict, self).__init__()
self.features = nn.Sequential(OrderedDict({
'conv1': nn.Conv2d(3, 6, 5),
'relu1': nn.ReLU(inplace=True),
'pool1': nn.MaxPool2d(kernel_size=2, stride=2),
'conv2': nn.Conv2d(6, 16, 5),
'relu2': nn.ReLU(inplace=True),
'pool2': nn.MaxPool2d(kernel_size=2, stride=2),
}))
self.classifier = nn.Sequential(OrderedDict({
'fc1': nn.Linear(16 * 5 * 5, 120),
'relu3': nn.ReLU(),
'fc2': nn.Linear(120, 84),
'relu4': nn.ReLU(inplace=True),
'fc3': nn.Linear(84, classes),
}))
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
fake_img = torch.randn((4, 3, 32, 32), dtype=torch.float32)
net = LeNetSequentialOrderDict(classes=2)
output = net(fake_img)
print(net)
print(output)
output:
LeNetSequentialOrderDict(
(features): Sequential(
(conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
(relu1): ReLU(inplace=True)
(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(relu2): ReLU(inplace=True)
(pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(classifier): Sequential(
(fc1): Linear(in_features=400, out_features=120, bias=True)
(relu3): ReLU()
(fc2): Linear(in_features=120, out_features=84, bias=True)
(relu4): ReLU(inplace=True)
(fc3): Linear(in_features=84, out_features=2, bias=True)
)
)
tensor([[-0.1304, -0.0338],
[-0.1102, -0.0299],
[-0.0928, -0.0051],
[-0.0988, -0.0417]], grad_fn=<AddmmBackward>)
3. nn.ModuleList
nn.ModuleList
功能:nn.ModuleList 是 nn.module 的容器,用于包装一组网络层,以迭代方式调用网络层
主要方法:
- append():在 ModuleList 后面添加网络层
- extend():拼接两个 ModuleList
- insert():指定在 ModuleList 中位置插入网络层
#20层全连接网络
# ============================ ModuleList
class ModuleList(nn.Module):
def __init__(self):
super(ModuleList, self).__init__()
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])
def forward(self, x):
for i, linear in enumerate(self.linears):
x = linear(x)
return x
net = ModuleList()
print(net)
fake_data = torch.ones((10, 10))
output = net(fake_data)
print(output)
output:
"""
ModuleList(
(linears): ModuleList(
(0): Linear(in_features=10, out_features=10, bias=True)
(1): Linear(in_features=10, out_features=10, bias=True)
(2): Linear(in_features=10, out_features=10, bias=True)
(3): Linear(in_features=10, out_features=10, bias=True)
(4): Linear(in_features=10, out_features=10, bias=True)
(5): Linear(in_features=10, out_features=10, bias=True)
(6): Linear(in_features=10, out_features=10, bias=True)
(7): Linear(in_features=10, out_features=10, bias=True)
(8): Linear(in_features=10, out_features=10, bias=True)
(9): Linear(in_features=10, out_features=10, bias=True)
(10): Linear(in_features=10, out_features=10, bias=True)
(11): Linear(in_features=10, out_features=10, bias=True)
(12): Linear(in_features=10, out_features=10, bias=True)
(13): Linear(in_features=10, out_features=10, bias=True)
(14): Linear(in_features=10, out_features=10, bias=True)
(15): Linear(in_features=10, out_features=10, bias=True)
(16): Linear(in_features=10, out_features=10, bias=True)
(17): Linear(in_features=10, out_features=10, bias=True)
(18): Linear(in_features=10, out_features=10, bias=True)
(19): Linear(in_features=10, out_features=10, bias=True)
)
)
tensor([[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176],
[ 0.1451, 0.1003, 0.0663, 0.1063, -0.2361, -0.1720, 0.1003, -0.1731,
-0.1960, 0.2176]], grad_fn=<AddmmBackward>)
"""
4. nn.ModuleDict
nn.ModuleDict
功能:nn.ModuleDict 是 nn.module 的容器,用于包装一组网络层,以索引方式调用网络层
主要方法:
- clear():清空ModuleDict
- items():返回可迭代的键值对
- keys():返回字典的键
- values():返回字典的值
- pop():返回一对键值,并从字典中删除
# ============================ ModuleDict
class ModuleDict(nn.Module):
def __init__(self):
super(ModuleDict, self).__init__()
self.choices = nn.ModuleDict({
'conv': nn.Conv2d(10, 10, 3),
'pool': nn.MaxPool2d(3)
})
self.activations = nn.ModuleDict({
'relu': nn.ReLU(),
'prelu': nn.PReLU()
})
def forward(self, x, choice, act):
x = self.choices[choice](x)
x = self.activations[act](x)
return x
net = ModuleDict()
fake_img = torch.randn((4, 10, 32, 32))
output = net(fake_img, 'conv', 'relu')
print(output)
5. 容器总结
Containers:
nn.Sequetial
:顺序性,各网络成之间严格按照顺序执行,常用语 block 构建nn.ModuleList
:迭代性,常用于大量重复网络构建,通过 for 循环实现重复构建nn.ModuleDict
:索引性,常用于可选择的网络层
11.2 AlexNet构建
AlexNet:2012 年以高出第二名 10 多个百分点的准确率获得 ImageNet 分类任务冠军,开创了卷积神经网络的新时代
AlexNet特点如下:
- 采用 ReLU:替换饱和激活函数(如,Sigmoid 函数),减轻梯度消失
- 采用 LRN(Local REsponse Normalization):对数据归一化,减轻梯度消失
- Dropout:提高全连接层的鲁棒性,增加网络的泛化能力
- Data Augmentation:TenCrop,色彩修改
12. nn中的网络层——卷积层
Content
- 1d/2d/3d 卷积
- 卷积 - nnConv2d()
- 转置卷积 - nn.ConvTranspose
12.1 1d/2d/3d Convolution
卷积运算:卷积核在输入信号(图像)上滑动,相应位置上进行乘加
卷积核:又称为滤波器、过滤器,可认为是某种模式、某种特征
卷积过程类似于用一个模板去图像上寻找与它相识的区域,与卷积核模式月相似,激活值越高,从而实现特征提取。
AlexNet卷积核可视化,发现卷积核学习到的是边缘、条纹、色彩这一些细节模式
卷积维度:一般情况下,卷积核在几个维度上滑动,就是几维卷积;和卷积核的维度不一定一致,请勿混淆。
12.2 卷积 - nn.Conv2d()
nnConv2d()
功能:对多个二维信号进行二维卷积
主要参数:
- in_channels:输入通道数
- out_channels:输出通道数,等价于卷积核个数
- kernel_size:卷积核尺寸
- stride = 1:步长
- padding = 0:填充个数,保持输入输出的尺寸一致
- dilation = 1:空洞卷积大小
- groups = 1:分组卷积设置
- bias = True:偏置
尺寸计算:
简化版:不带 padding 和 dilation
完整版:
其中, 为输出尺寸; 为输入尺寸; 为内核尺寸; 为步长; 为填充个数; 为空洞卷积大小
12.3 转置卷积(Transpose Convlution)
转置卷积又称为反卷积(Deconvolution)和部分跨越卷积(Fractionally-strided Convolution),用于对图像进行上采样(UpSample)
为什么称为转置卷积?
正常卷积:
假设图像尺寸为:44 ,卷积核为:33 ,padding = 0 ,stride = 1 。
图像:
- 16 ==> 4*4 = 16
卷积核:
- 16 ==> 3*3=9,再根据前面的的形状补 0 得到 16
- 4 ==> 输出特征图的尺寸得到的
输出:
转置卷积:
假设图像尺寸为:22,卷积核为:33,padding = 0,stride = 1
图像:
- 4 ==> 2*2 = 4
卷积核:
- 4 ==> 3*3 = 9,再剔除得到 4;因为能与图像相乘的最多只有 4 个
- 16 ==> 根据输出特征图的尺寸计算得到
输出:
1. nn.ConvTranspose2d
nn.ConvTranspose2d
功能:装置卷积实现上采样
主要参数:
- in_channels:输入通道数
- out_channels:输出通道数,等价于卷积核个数
- kernel_size:卷积核尺寸
- stride=1:步长
- padding=0:填充个数,保持输入输出的尺寸一致
- groups=1:分组卷积设置
- bias=True:偏置
- output_padding=0,
- dilation=1:空洞卷积大小
- padding_mode=’zeros’
# 转置卷积实现上采样
nn.ConvTranspose2d(in_channels, # 输入通道数
out_channels, # 输出通道数,等价于卷积核个数
kernel_size, # 卷积核尺寸
stride=1, # 步长
padding=0, # 填充个数,保持输入输出的尺寸一致
output_padding=0,
groups=1, # 分组卷积设置
bias=True, # 偏置
dilation=1, # 空洞卷积大小
padding_mode='zeros')
尺寸计算:
简化版:
复杂版:
13. nn中的网络层——池化层、线性层、激活函数层
Content
- 池化层——Pooling Layer
- 线性层——Linear Layer
- 激活函数层——Activation Layer
13.1 池化层(Pooling Layer)
池化运算:对信号进行“收集”并“总结”,类似水池收集水资源,因而得名池化层。
- 收集:信号由多变少、尺寸由大变小
- 总结:最大值/平均值
1. nn.MaxPool2d
nn.MaxPool2d
功能:对二维信号(图像)进行最大值池化
主要参数:
- kernel_size:池化核尺寸
- stride = None:步长,通常与kernel_size相同
- padding = 0 :填充个数
- dilation:池化核间隔大小
- ceil_mode = False:尺寸向上/向下取整(尺寸计算中的除法操作是向上/向下取整,默认ceil_mode = False,向下取整。)
- return_indices = False:记录池化像素索引
# 1. 最大池化
nn.MaxPool2d(kernel_size, # 池化核尺寸
stride=None, # 步长,通常与kernel_size相同
padding=0, # 填充个数
dilation=1, # 池化核间隔大小
return_indices=False, # 记录池化像素索引
ceil_mode=False) # 尺寸向上取整
2. nn.AvgPool2d
nn.AvgPool2d
功能:对二维信号(图像)进行平均值池化
主要参数:
- kernel_size:池化核尺寸
- stride = None:步长,通常与kernel_size相同
- padding = 0 :填充个数
- ceil_mode = False:尺寸向上取整
- count_include_pad = True:填充值用于计算
- divisor_override = None :除法因子
注意事项:最大池化后的图片亮度大于平均池化的图片亮度
# 2. 平均池化
nn.AvgPool2d(kernel_size, # 池化核尺寸
stride=None, # 步长,通常与kernel_size相同
padding=0, # 填充个数
ceil_mode=False, # 尺寸向上取整
count_include_pad=True, # 填充值用于计算
divisor_override=None) # 除法因子
3. nn.MaxUnpool2d
nn.MaxUnpool2d
功能:对二维信号(图像)进行最大值池化上采样
主要参数:
- kernel_size:池化核尺寸
- stride = None:步长,通常与kernel_size相同
- padding = 0:填充个数
# 3. 最大值反池化
nn.MaxUnpool2d(kernel_size, # 池化核尺寸
stride=None, # 步长,通常与kernel_size相同
padding=0) # 填充个数
# 在前向传播的过程中要添加反池化的索引值indices
forward(self, input, indices, output_size=None)
13.2 线性层(Linear Layer)
线性层又称全连接层,其,每个神经元与上一层所有神经元相连实现对前一层的线性组合、线性变换。
=
1. nn.Linear
nn.Linear
功能:对一维信号(向量)进行线性组合
主要参数:
- in_features:输入结点数
- out_features:输出结点数
- bias :是否需要偏置
计算公式:
# 对一维信号进行线性组合
nn.Linear(in_features, # 输入结点数
out_features, # 输出结点数
bias=True) # 是否需要偏置
13.3 激活函数层
激活函数对特征进行非线性变换,赋予多层神经网络具有深度的意义
若无激活函数,多个线性层叠加等价于一个线性层
1. nn.Sigmoid
nn.Sigmoid
计算公式:
梯度公式:
特性:
- 输出值在 (0,1) ,符合概率
- 导数范围是 [0, 0.25] ,易导致梯度消失
- 输出为非 0 均值,破坏数据分布
2. nn.tanh
nn.tanh
计算公式:
梯度公式:
特性:
- 输出值在 (-1,1) ,数据符合0均值
- 导数范围是 (0, 1) ,易导致梯度消失
3. nn.ReLU
nn.ReLU
计算公式:
梯度公式:
特性:
- 输出值均为正数,负半轴导致死神经元
- 导数是 1 ,缓解梯度消失,但易引发梯度爆炸
4. 改进的 nn.ReLU 激活函数
nn.LeakyReLU
- negative_slope:负半轴斜率
nn.PReLU
- init:可学习斜率
nn.RReLU
- lower:均匀分布下限
- upper:均匀分布上限
第四周
14. 权值初始化
Content
- 梯度消失与梯度爆炸
- Xavier 方法与 Kaiming 方法
- 常用初始化方法
14.1 梯度消失与梯度爆炸
1. 什么是梯度消失与梯度爆炸
的梯度取决于上一层的输出
梯度消失:
梯度爆炸:
2. 为什么会梯度消失与梯度爆炸
1.2.3 ==>
① ②
由 ①② 可得:
3. 如何解决梯度消失与梯度爆炸
要解决梯度消失与梯度爆炸,就得使网络层的方差保持不变,这就得让网络层的方差为 1 。
==>
==>
当然,这只能在没有激活函数的情况解决梯度消失与梯度爆炸。
在具有激活函数的情况下,还得通过其他方法解决梯度消失有梯度爆炸
14.2 Xavier 初始化
适用激活函数:饱和函数,如 Sigmoid、Tanh
方差一致性:保持数据尺度维持在恰当范围,通常方差为 1
①
②
由①②可得:
通常 Xavier 采用的是均匀分布:
故:
==>
14.3 Kaiming 初始化
适用激活函数:ReLU及其变种
方差一致性:保持数据尺度维持在恰当范围,通常方差为 1
ReLU变种:
(其中, a:负半轴的斜率;ReLU中负半轴斜率为 0 )
14.4 常用初始化方法
1. 常用的初始化方法
常用的初始化方法可以分为四大类:
- Xavier均匀分布
Xavier标准正态分布
Kaiming均匀分布
Kaiming标准正态分布
均匀分布
- 正态分布
常数分布
正交矩阵初始化
- 单位矩阵初始化
- 稀疏矩阵初始化
2. nn.init.calculate_gain
nn.init.calculate_gain
主要功能:计算激活函数的方差变化尺度(输入数据的方差 / 经过激活函数之后输出数据的方差)
主要参数:
- nonlinearity: 激活函数名称
- param: 激活函数的参数,如 Leaky ReLU 的 negative_slop
15. 损失函数(一)
Content
- 损失函数概念
- 交叉熵损失函数
- NLL / BCE / BCEWithLogits Loss
15.1 损失函数概念
损失函数:衡量模型输出与真实标签的差异
损失函数(Loss Function):
计算一个样本:模型输出与真实标签的差异
代价函数(Cost Function):
计算整个训练集 的平均值
目标函数(Objective Function):
对模型进行的一些约束,以防止过拟合
class _Loss(Module):
def __init__(self, size_average=None, reduce=None, reduction='mean'):
super(_Loss, self).__init__()
if size_average is not None or reduce is not None:
self.reduction = _Reduction.legacy_get_string(size_average, reduce)
else:
self.reduction = reduction
注意:
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
15.2 交叉熵损失函数
交叉熵
交叉熵 = 信息熵 + 相对熵
熵:
用来描述不确定性;熵越大,不确定性越大。
熵是自信息的期望
自信息: ( )
相对熵: ( P:真实的分布,Q:模型输出的分布 )
又称为 KL 散度,用来衡量两个分布之间的差异,也就是两个分布之间的距离;但是它不是一个距离的函数,它没有距离函数具有的对称性。
交叉熵:
衡量两个概率分布之间的关系,两个概率分布之间的相似度
相对熵:
交叉熵: ( P:真实的分布,Q:模型输出的分布 )
1. nn.CrossEntropyLoss
nn.CrossEntropyLoss
功能: nn.LogSoftmax() 与 nn.NLLLoss() 结合,进行交叉熵计算
主要参数:
- weight=None:各类别 loss 设置的权值
- ignore_index=-100:忽略某个类别
- reduction=’mean’:计算模式,可为 none/sum/mean
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
计算公式:# 计算交叉熵
nn.CrossEntropyLoss(weight=None, # 各类别loss设置的权值
ignore_index=-100, # 忽略某个类别
reduction='mean') # 计算模式,如none/sum/mean
# none:逐个元素计算
# sum:所有元素求和,返回标题
# mean:加权平均,返回标量
15.3 NLL / BCE / BCEWithLogits Loss
2. nn.NLLoss
nn.NLLoss
功能:实现负对数似然函数中的负号功能
主要参数:
- weigh t:各类别的 loss 设置权值
- ignore_ind e x:忽略某个类别
- reduction :计算模式,可为 none/sum /m e an
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
计算公式:# 1. 实现负对数似然函数中的负号功能
nn.NLLLoss(weight=None, # 各类别的loss设置权值
ignore_index=-100, # 忽略某个类别
reduction='mean') # 计算模式,如none/sum/
3. nn.BCELoss
nn.BCELoss
功能:二分类交叉熵
注意事项:输入值取值在 [0,1]
主要参数:
- weight:各类别的 loss 设置权值
- ignore _index:忽略某个类别
- reduction :计算模式,可为 none/sum /m e an
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
计算公式:# 2. 二分类交叉熵
# 注:输入值为[0,1],可配合sigmoid函数使用
nn.BCELoss(weight=None, # 各类别loss设置的权值
ignore_index=-100, # 忽略某个类别
reduction='mean') # 计算模式,如none/sum/mean
4. nn.BCEWithLogitsLoss
nn.BCEWithLogitsLoss
功能:结合 Sigmoid 与二分类交叉熵
注意事项:网络最后不加 Sigmoid 函数
主要参数:
- pos_weight:正样本的权值
- weight:各类别的 loss 设置权值
- ignore _index:忽略某个类别
- reduction:计算模式,可为 none/sum /mean
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
计算公式:# 3. 结合Sigmoid与二分类交叉熵
# 注:不需要额外加入Sigmoid函数
nn.BCEWithLogitsLoss(weight=None, # 各类别loss设置的权值
ignore_index=-100, # 忽略某个类别
reduction='mean', # 计算模式,如none/sum/mean
pos_weight=None) # 正样本的权值
16. 损失函数(二)—— pytorch 中其余 14 中损失函数
Content
- nn.L1Loss
- nn.MSELoss
- nn.SmoothL1Loss
- nn.PoissonNLLLoss
- nn.KLDivLoss
- nn.MarginRankingLoss
- nn.MultiLabelMarginLoss
- nn.SoftMarginLoss
- nn.MultiLabelSoftMarginLoss
- nn.MultiMarginLoss
- nn.TripletMarginLoss
- nn.HingeEmbeddingLoss
- nn.CosineEmbeddingLoss
- nn.CTCLoss
16.1 回归中常用的两个损失函数
5. nn.L1Loss
nn.L1Loss
功能: 计算 inputs 与 target 之差的绝对值
主要参数:
- reduction :计算模式,可为 none/sum/mean
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
注意:nn.L1Loss(size_average=None,
reduce=None,
reduction='mean’)
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
6. nn.MSELoss
nn.MSELoss
功能: 计算 inputs 与 target 之差的平方
主要参数:
- reduction :计算模式,可为 none/sum/mean
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
注意:nn.MSELoss(size_average=None,
reduce=None,
reduction='mean’)
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
7. SmoothL1Loss
SmoothL1Loss
功能: 平滑的 L1Loss
主要参数:
- reduction :计算模式,可为 none/sum/mean
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
nn.SmoothL1Loss(size_average=None,
reduce=None,
reduction='mean’)
注意:
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
8. PoissonNLLLoss
PoissonNLLLoss
功能:泊松分布的负对数似然损失函数
主要参数:
- log_input :输入是否为对数形式,决定计算公式
- full :计算所有 loss ,默认为 False
- eps :修正项,避免 log(input) 为 nan
注意:nn.PoissonNLLLoss(log_input=True,
full=False,
size_average=None,
eps=1e-08,
reduce=None,
reduction='mean')
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
9. nn.KLDivLoss
nn.KLDivLoss
功能:计算KLD(divergence),KL 散度,相对熵
注意事项:需提前将输入计算 log-probabilities ,如通过 nn.logsoftmax()
主要参数:
- reduction :计算模式,可为 none/sum/mean/batchmean
- batchmean:batchsize 维度求平均值
- none:逐个元素计算
- sum:所有元素求和,返回标量
- mean:加权平均,返回标量
注意:nn.KLDivLoss(size_average=None,
reduce=None,
reduction='mean')
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
公式定义的计算公式:
P:真实标签分布,Q:模型输出分布
nn.KLDivLoss 函数的计算公式如下:
10. nn.MarginRankingLoss
nn.MarginRankingLoss
功能:计算两个向量之间的相似度,用于排序任务
特别说明:该方法计算两组数据之间的差异,返回一个 n*n 的 loss 矩阵
主要参数:
- margin :边界值,x1 与 x2 之间的差异值
- reduction :计算模式,可为 none/sum/mean
nn.MarginRankingLoss(margin=0.0,
size_average=None,
reduce=None,
reduction='mean')
注意:
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
y = 1时, 希望x1比x2大,当 x1 > x2 时,不产生loss
y = -1时,希望x2比x1大,当 x2 > x1 时,不产生loss
11. nn.MultiLabelMarginLoss
nn.MultiLabelMarginLoss
功能:多标签边界损失函数
举例:四分类任务,样本 x 属于 0 类和 3 类,
标签:[0, 3, -1, -1] , 不是 [1, 0, 0, 1]
主要参数:
- reduction :计算模式,可为 none/sum/mean
nn.MultiLabelMarginLoss(size_average=None,
reduce=None,
reduction='mean')
注意:
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
x:模型输出值,y:真实标签。
举例:四分类任务,样本 x 属于 0 类和 3 类,标签 y:[0, 3, -1, -1] , 不是 [1, 0, 0, 1] ;
x 属于第几类,y 中就有几,随后用 -1 将 y 的长度补足到 x 的长度。
上述计算公式中 表示每个真实类别(标签所在)的神经元输出减去其他(非标签所在的)神经元的输出。
x[y[j]] 表示 样本x所属类的输出值,x[i] 表示不等于该类的输出值。
12. nn.SoftMarginLoss
nn.SoftMarginLoss
功能:计算二分类的 logistic 损失
主要参数:
- reduction :计算模式,可为 none/sum/mean
注意:nn.SoftMarginLoss(size_average=None,
reduce=None,
reduction='mean')
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
13. nn.MultiLabelSoftMarginLoss
nn.MultiLabelSoftMarginLoss
功能:SoftMarginLoss 多标签版本
主要参数:
- weight:各类别的 loss 设置权值
- reduction :计算模式,可为 none/sum/mean
注意:nn.MultiLabelSoftMarginLoss(weight=None,
size_average=None,
reduce=None,
reduction='mean')
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
14. nn.MultiMarginLoss
nn.MultiMarginLoss
功能:计算多分类的折页损失
主要参数:
- p :可选 1 或 2
- weight:各类别的 loss 设置权值
- margin :边界值
- reduction :计算模式,可为 none/sum/mean
注意:nn.MultiMarginLoss(p=1,
margin=1.0,
weight=None,
size_average=None,
reduce=None,
reduction='mean')
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
15. nn.TripletMarginLoss
nn.TripletMarginLoss
功能:计算三元组损失,人脸验证中常用
主要参数:
- p :范数的阶,默认为 2
- margin :边界值
- reduction :计算模式,可为 none/sum/mean
三元组:
nn.TripletMarginLoss(margin=1.0,
p=2.0,
eps=1e-06,
swap=False,
size_average=None,
reduce=None,
reduction='mean')
注意:
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
16. nn.HingeEmbeddingLoss
nn.HingeEmbeddingLoss
功能:计算两个输入的相似性,常用于非线性 embedding 和半监督学习
特别注意:输入 x 应为两个输入之差的绝对值
主要参数:
- margin :边界值
- reduction :计算模式,可为 none/sum/mean
注意:nn.HingeEmbeddingLoss(margin=1.0,
size_average=None,
reduce=None,
reduction='mean’)
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
17. nn.CosineEmbeddingLoss
nn.CosineEmbeddingLoss
功能:采用余弦相似度计算两个输入的相似性;关注的是方向上的差异
主要参数:
- margin :可取值 [-1, 1] , 推荐为 [0, 0.5]
- reduction :计算模式,可为 none/sum/mean
注意:nn.CosineEmbeddingLoss(margin=0.0,
size_average=None,
reduce=None,
reduction='mean')
size_average = None 与 reduce = None 这两个参数即将被舍弃,他们的功能在参数 reduction = ‘mean’ 中已经可以完全被实现了。
所以不要再使用 size_average = None 与 reduce = None 这两个参数。
计算公式:
18. nn.CTCLoss
nn.CTCLoss
功能: 计算CTC损失,解决时序类数据的分类(Connectionist Temporal Classification)
主要参数:
- blank :blank label
- zero_infinity :无穷大的值或梯度置 0
- reduction :计算模式,可为 none/sum/mean
# 计算CTC损失,解决时序类数据的分类
nn.CTCLoss(blank=0, # blank label
reduction='mean', # 无穷大的值或梯度置0
zero_infinity=False)
参考文献:
A. Graves et al.: Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks
损失函数总结
17. 优化器 Optimizer(一)
Content
- 什么是优化器
- optimizer 的属性
- optimizer 的方法
17.1 什么是优化器(Optimizer)
- pytorch 的优化器:管理 并 更新 模型中可学习参数的值,使得模型输出更接近真实标签
- 导数:函数在指定坐标轴上的变化率
- 方向导数:函数在指定方向上的变化率
- 梯度:向量,方向为方向导数取得最大值的方向
17.2 优化器的基本属性
基本属性:
- defaults:优化器超参数
- state:参数的缓存,如 momentum 的缓存
- params_groups:管理的参数组
- _step_count:记录更新次数,学习率调整中使用
class Optimizer(object):
def __init__(self, params, defaults):
self.defaults = defaults # 优化器超参数
self.state = defaultdict(dict) # 参数的缓存,如momentum的缓存
self.param_grops = [] # 管理的参数组,包含字典元素的list
# param_groups = [{'params': param_groups}]
# _step_count:记录更新次数,学习率调整中使用
...
17.3 优化器的基本方法
基本方法:
1. zero_grad()
zero_grad():清空管理参数的梯度
注:pytorch中张量梯度不自动清零
class Optimizer(object):
def zero_grad(self):
r"""Clears the gradients of all optimized :class:`torch.Tensor` s."""
for group in self.param_groups:
for p in group['params']:
if p.grad is not None:
p.grad.detach_()
p.grad.zero_()
2. step()
step():执行一步更新
class Optimizer(object):
def __init__(self, params, defaults):
self.defaults = defaults
self.state = defaultdict(dict)
self.param_groups = []
def step(self, closure):
r"""Performs a single optimization step (parameter update).
Arguments:
closure (callable): A closure that reevaluates the model and
returns the loss. Optional for most optimizers.
"""
raise NotImplementedError
3. add_param_group()
add_param_group():添加参数组
class Optimizer(object):
def add_param_group(self, param_group):
for group in self.param_groups:
param_set.update(set(group['params']))
self.param_groups.append(param_group)
4. state_dict()
state_dict():获取优化器当前状态信息字典
class Optimizer(object):
def state_dict(self):
return {
'state': packed_state,
'param_groups': param_groups,
}
5. load_state_dict()
load_state_dict():加载状态信息字典
class Optimizer(object):
def state_dict(self):
return {
'state': packed_state,
'param_groups': param_groups,
}
def load_state_dict(self, state_dict):
18. 常用的优化方法(优化器)
Content
- learning rate 学习率
- momentum 动量
- torch.optim.SGD
- Pytorch的十种优化器
18.1 learning rate 学习率
梯度下降:
例如:
梯度下降:
LR:学习率(learning rate)/步长,控制更新的步伐
18.2 momentum 动量
Momentum(动量,冲量):结合当前梯度与上一次更新信息,用于当前更新
指数加权平均:
例如:
梯度下降:
pytorch中更新公式:
例如:
18.3 torch.optim.SGD
optim.SGD
主要参数:
- params:管理的参数组
- lr:初始学习率
- momentum:动量系数,贝塔
- weight_decay:L2 正则化系数
- nesterov:是否采用 NAG
torch.optim.SGD(params, # 管理的参数组
lr= < object object >, # 初始学习率
momentum=0, # 动量系数beta
dampening=0, # L2正则化系数
weight_decay=0,
nesterov=False) # 是否采用NAG
NAG参考文献:
《On the importance of initialization and momentum in deep learning》
18.4 Pytorch的十种优化器
pytorch中的十种优化器:
- optim.SGD:随机梯度下降法
- optim.Adagrad:自适应学习率梯度下降法
- optim.RMSprop:Adagrad 的改进
- optim.Adadelta:Adagrad 的改进
- optim.Adam:RMSprop 结合 Momentum
- optim.Adamax:Adam 增加学习率上限
- optim.SparseAdam:稀疏版的 Adam
- optim.ASGD:随机平均梯度下降
- optim.Rprop:弹性反向传播
- optim.LBFGS:BFGS 的改进
参考文献:
- optim.SGD: 《On the importance of initialization and momentum in deep learning 》
- optim.Adagrad: 《Adaptive Subgradient Methods for Online Learning and Stochastic Optimization》
- optim.RMSprop:http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf
- optim.Adadelta: 《 AN ADAPTIVE LEARNING RATE METHOD》
- optim.Adam: 《Adam: A Method for Stochastic Optimization》
- optim.Adamax: 《Adam: A Method for Stochastic Optimization》
- optim.SparseAdam
- optim.ASGD:《Accelerating Stochastic Gradient Descent using Predictive Variance Reduction》
- optim.Rprop:《Martin Riedmiller und Heinrich Braun》
- optim.LBFGS:BDGS 的改进
第五周
19. 学习率调整策略
Content
- 为什么要调整学习率?
- pytorch 的六种学习率调整策略
- 学习率调整小结
19.1 为什么要调整学习率?
梯度下降:
LR:学习率(learning rate)/步长,控制更新的步伐
- 学习率大时,loss 下降得快,但到了一定程度不再下降
- 学习率小时,loss 下降得多,但下降的速度慢
- 在训练的过程中调整学习率,先大后小,可以使得训练的效率更高效果更好
19.2 基类——class _LRScheduler
1. class _LRScheduler 的主要属性
class _LRScheduler
主要属性:
- optimizer:关联的优化器
- last_epoch:记录 epoch 数
base_lrs:记录初始学习率
class _LRScheduler(object):
def __init__(self, optimizer, last_epoch=-1):
2. class _LRScheduler 的主要方法
class _LRScheduler
主要方法:
- step():更新下一个 epoch 的学习率
get_lr():虚函数,计算下一个 epoch 的学习率
class _LRScheduler(object):
def __init__(self, optimizer, last_epoch=-1):
def get_lr(self):
raise NotImplementedError
19.3 pytorch 的六种学习率调整策略
1. StepLR
StepLR
功能:等间隔调整学习率
主要参数:
- step_size:调整间隔数
- gamma:调整系数
调整方式:
# 1. StepLR
# 等间隔调整学习率
lr_scheduler.StepLR(optimizer,
step_size, # 调整间隔数
gamma=0.1, # 调整系数
last_epoch=-1)
2. MultiStepLR
MultiStepLR
功能:按给定间隔调整学习率
主要参数:
- milestones:设定调整时刻数
- gamma:调整系数
调整方式:
# 2. MultiStepLR
# 按给定间隔调整学习率
lr_scheduler.MultiStepLR(optimizer,
milestones, # 设定调整时刻数,如[50,70,100]
gamma=0.1, # 调整系数
last_epoch=-1)
3. ExponentialLR
ExponentialLR
功能:按指数衰减调整学习率
主要参数:
- gamma:指数的底
调整方式:
# 3. ExponentialLR
# 按指数衰减调整学习率
lr_scheduler.ExponentialLR(optimizer,
gamma=0.1, # 指数的底
last_epoch=-1)
4. CosineAnnealingLR
CosineAnnealingLR
功能:余弦周期调整学习率
主要参数:
- T_max:下降周期
- eta_min:学习率下限
调整方式:
# 4. CosineAnnealingLR
# 余弦周期调整学习率
lr_scheduler.CosineAnnealingLR(optimizer,
T_max, # 下降周期
eta_min=0, # 学习率下限
last_epoch=-1)
5. ReduceLRonPlateau
ReduceLRonPlateau
功能:监控指标,当指标不再变化则调整学习率
主要参数:
- mode:min/max 两种模式
- mode = min:观察所要监控的指标下降,监控的指标不再下降就调整学习率;一般监控 loss
- mode = max:观察所要监控的指标上升,监控的指标不再上升就调整学习率;一般监控分类准确率
- factor:调整系数
- patience:“耐心 ”,接受连续几次不变化
- cooldown:“冷却时间”,停止监控一段时间
- verbose:是否打印日志
- min_lr:学习率下限
- eps:学习率衰减最小值
# 5. ReduceLRonPlateau
# 监控指标,当指标不再变化时调整学习率
# scheduler_lr.step(要监测的变量)
lr_scheduler.ReduceLROnPlateau(optimizer,
mode='min', # min/max两种模式
factor=0.1, # 调整系数
patience=10, # 接受参数不变化的迭代次数
verbose=False, # 是否打印日志
threshold=0.0001,
threshold_mode='rel',
cooldown=0, # 停止监控的迭代次数
min_lr=0, # 学习率下限
eps=1e-08) # 学习率衰减最小值
6. LambdaLR
LambdaLR
功能:自定义调整策略
主要参数:
- lr_lambda:function or list
# 6. LambdaLR
# 自定义调整策略,可以对不同的参数组使用不同的参数调整策略
lr_scheduler.LambdaLR(optimizer,
lr_lambda, # 函数或元素为函数的列表
last_epoch=-1)
19.4 学习率调整小结
学习率调整小结:
有序调整:Step、MultiStep、Exponential 和 CosineAnnealing
自适应调整:ReduceLROnPleateau
自定义调整:Lambda
19.5 学习率初始化
学习率初始化:
设置较小数:0.01、0.001、0.0001
搜索最大学习率: 《Cyclical Learning Rates for Training Neural Networks》
20. 可视化工具——TensorBoard
Content
- TensorBoard简介
- TensorBoard安装
- TensorBoard运行可视化
20.1 TensorBoard简介
1. TensorBoard简介
TensorBoard:TensorFlow 中强大的可视化工具
支持标量、图像、文本、音频、视频和 Eembedding 等多种数据可视化
2. 运行机制
20.2 TensorBoard安装
安装注意事项:
pip install tensorboard 的时候会报错:
ModuleNotFoundError: No module named ‘past’
通过 pip install future 解决
20.3 TensorBoard运行可视化
tensorboard --logdir=./runs
TensorFlow installation not found - running with reduced feature set.
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.5.0 at http://localhost:6006/ (Press CTRL+C to quit)
21. TensorBoard 使用(一)
Content
- SummaryWriter
- add_scalar and add_histogram
- 模型指标监控
21.1 SummaryWriter
SummaryWriter
功能:提供创建event file的高级接口
主要属性:
- log_dir:event file 输出文件夹;默认 log_dir = None,表示在当前 .py 文件所在文件下创建一个 runs 文件夹
- comment:不指定 log_dir 时,文件夹后缀
- filename_suffix:event file 文件名后缀 ```python
提供创建event file的高级接口
class SummaryWriter(object): def init(self, log_dir=None, # event file 输出文件 comment=’’, # 不指定log_dir时,文件夹后缀 purge_step=None, max_queue=10, flush_secs=120, filename_suffix=’’) # event file文件名后缀
<a name="Donud"></a>
### 21.2 add_scalar and add_histogram
<a name="gfWcQ"></a>
#### 1. add_scalar()
add_scalar()<br />功能:记录标量
主要方法:
- tag:图像的标签名,图的唯一标识
- scalar_value:要记录的标量;可以理解为 y 轴
- **global_step:x 轴**
注意事项:add_scalar() 只能记录一条曲线
```python
# 1. add_scalar
# 记录标量
add_scalar(tag, # 图像的标签名,图的唯一标识
scalar_value, # 要记录的标量
global_step=None, # x轴
walltime=None)
2. add_scalars()
add_scalars()
功能:记录标量;可以记录多条曲线
主要参数:
- main_tag:该图的标签
- tag_scalar_dict:key 是变量的 tag ,value 是变量的值
# 1. add_scalars
# 记录标量,可以记录多条曲线
add_scalars(main_tag, # 该图的标签
tag_scalar_dict, # key是变量的tag,value是变量的值
global_step=None,
walltime=None)
3. add_histogram()
add_histogram()
功能:统计直方图与多分位数折线图
主要参数:
- tag:图像的标签名,图的唯一标识
- values:要统计的参数
- global_step:y 轴
- bins:取直方图的 bins
# 3. add_histogram
# 统计直方图与多分位数折线图
add_histogram(tag, # 图像的标签名,图的唯一标识
values, # 要统计的参数
global_step=None, # y轴
bins='tensorflow', # 取直方图的bins
walltime=None)
21.2 模型指标监控
22. TensorBoard 使用(二)——TensorBoaed 的图像可视化方法
Content
- add_image and torchvision.utils.make_grid
- AlexNet 卷积核与特征图可视化
- add_graph and torchsummary
22.1 add_image and torchvision.utils.make_grid
4. add_image()
add_image()
功能:记录图像
主要参数:
- tag:图像的标签名,图的唯一标识
- img_tensor:图像数据,注意尺度
- global_step:x 轴
- dataformats:数据形式,CHW,HWC,HW
# 4. add_image
# 记录图像
add_image(tag, # 图像的标签名,图的唯一标识
img_tensor, # 图像数据,注意尺度
global_step=None, # x轴
walltime=None,
dataformats='CHW') # 数据形式,CHW/HWC/HW
torchvision.utils.make_grid
torchvision.utils.make_grid
功能:制作网格图像
主要参数:
- tensor:图像数据,BCH*W 形式
- nrow:行数(列数自动计算)
- padding:图像间距(像素单位)
- normalize:是否将像素值标准化
- range:标准化范围
- scale_each:是否单张图维度标准化
- pad_value:padding 的像素值
# torchvision.utils.make_grid
# 制作网格图像
make_grid(tensor, # 图像数据,B*C*H*W形式
nrow=8, # 行数(列数自动计算)
padding=2, # 图像间距(像素单位)
normalize=False, # 是否将像素值标准化
range=None, # 标准化范围
scale_each=False, # 是否单张图维度标准化
pad_value=0) # padding的像素值
22.2 AlexNet 卷积核与特征图可视化
22.3 add_graph and torchsummary
5. add_graph()
add_graph()
功能:可视化模型计算图
主要参数:
- model:模型,必须是 nn.Module
- input_to_model:输出给模型的数据
- verbose:是否打印计算图结构信息
注意事项:在 pytorch 1.2 版本中,add_graph() 有 bug
# 5. add_graph
# 可视化模型计算图
add_graph(model, # 模型,必须是nn.Module
input_to_model=None, # 输出给模型的数据
verbose=False) # 是否打印计算图结构信息
torchsummary
torchsummary
功能:查看模型信息,便于调试
主要参数:
- model:pytorch 模型
- input_size:模型输入 size
- batch_size:batch size
- device:“cuda” or “cpu”
# torchsummary
# 查看模型信息,便于调试
summary(model, # pytorch模型
input_size, # 模型输入size
batch_size=-1, # batch size
device="cuda") # cuda/cpu
github: https://github.com/sksq96/pytorch-summary
23. Hook 函数与 CAM 可视化
Content
- Hook 函数概念
- Hook 函数与特征图提取
- CAM(Class Activation Map, 类激活图)
23.1 Hook函数概念
Hook函数机制:不改变主体,实现额外功能,像一个挂件
Hook函数:
torch.Tensor.register_hook(hook)
torch.nn.Module.register_forward_hook
- torch.nn.Module.register_forward_pre_hook
- torch.nn.Module.register_backward_hook
23.2 Hook 函数与特征图提取
1. torch.Tensor.register_hook(hook)
torch.Tensor.register_hook(hook)
功能:注册一个方向传播 hook 函数
hook 函数仅一个输入参数,为张量的梯度
# 1.
# 注册一个反向传播的hook函数
# hook(grad) -> Tensor or None
torch.Tensor.register_hook(hook)
记录梯度:
# ----------------------------------- 2 tensor hook 2 -----------------------------------
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x)
b = torch.add(w, 1)
y = torch.mul(a, b)
a_grad = list()
def grad_hook(grad):
a_grad.append(grad)
handle = a.register_hook(grad_hook)
y.backward()
# 查看梯度
print("gradient:", w.grad, x.grad, a.grad, b.grad, y.grad)
print("a_grad[0]: ", a_grad[0])
handle.remove()
output:
"""
gradient: tensor([5.]) tensor([2.]) None None None
a_grad[0]: tensor([2.])
"""
修改梯度:
# ----------------------------------- 2 tensor hook 2 -----------------------------------
import torch
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], requires_grad=True)
a = torch.add(w, x)
b = torch.add(w, 1)
y = torch.mul(a, b)
a_grad = list()
def grad_hook(grad):
grad *= 2
return grad * 3
handle = w.register_hook(grad_hook)
y.backward()
# 查看梯度
print("w.grad: ", w.grad)
handle.remove()
output:
"""
w.grad: tensor([30.])
"""
2. Module.register_forward_hook
Module.register_forward_hook
功能:注册 module 的前向传播 hook 函数;
参数:
- module: 当前网络层
- input:当前网络层输入数据
- output:当前网络层输出数据
# 2.
# 注册module的前向传播hook函数
# hook(module, input, output) -> None
# module:当前网络层
# input:当前网路层输入数据
# output:当前网络层输出数据
torch.nn.Module.register_forward_hook(hook)
通常使用 forward_hook 函数获得卷积输出的特征图
# ----------------------------- 3 Module.register_forward_hook-----------------------------
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 2, 3)
self.pool1 = nn.MaxPool2d(2, 2)
def forward(self, x):
x = self.conv1(x)
x = self.pool1(x)
return x
def forward_hook(module, data_input, data_output):
fmap_block.append(data_output)
input_block.append(data_input)
# 初始化网络
net = Net()
net.conv1.weight[0].detach().fill_(1)
net.conv1.weight[1].detach().fill_(2)
net.conv1.bias.data.detach().zero_()
# 注册hook
fmap_block = list()
input_block = list()
net.conv1.register_forward_hook(forward_hook)
# inference
fake_img = torch.ones((1, 1, 4, 4)) # batch size * channel * H * W
output = net(fake_img)
# 观察
print("output shape: {}\noutput value: {}\n".format(output.shape, output))
print("feature maps shape: {}\noutput value: {}\n".format(fmap_block[0].shape, fmap_block[0]))
print("input shape: {}\ninput value: {}".format(input_block[0][0].shape, input_block[0]))
output:
"""
output shape: torch.Size([1, 2, 1, 1])
output value: tensor([[[[ 9.]],
[[18.]]]], grad_fn=<MaxPool2DWithIndicesBackward>)
feature maps shape: torch.Size([1, 2, 2, 2])
output value: tensor([[[[ 9., 9.],
[ 9., 9.]],
[[18., 18.],
[18., 18.]]]], grad_fn=<ThnnConv2DBackward>)
input shape: torch.Size([1, 1, 4, 4])
input value: (tensor([[[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]]]),)
"""
3. Module.register_forward_pre_hook
Module.register_forward_pre_hook
功能:注册 module 前向传播前的 hook函数
参数:
- module: 当前网络层
- input:当前网络层输入数据
# 3.
# 注册module前向传播前的hook函数
# hook(module, input) -> None
# module:当前网络层
# input:当前网路层输入数据
torch.nn.Module.register_forward_pre_hook(hook)
# ------------------------------- 4 Module.register_pre_hook ------------------------------
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 2, 3)
self.pool1 = nn.MaxPool2d(2, 2)
def forward(self, x):
x = self.conv1(x)
x = self.pool1(x)
return x
def forward_pre_hook(module, data_input):
print("forward_pre_hook input:{}".format(data_input))
# 初始化网络
net = Net()
net.conv1.weight[0].detach().fill_(1)
net.conv1.weight[1].detach().fill_(2)
net.conv1.bias.data.detach().zero_()
# 注册hook
fmap_block = list()
input_block = list()
net.conv1.register_forward_pre_hook(forward_pre_hook)
# inference
fake_img = torch.ones((1, 1, 4, 4)) # batch size * channel * H * W
output = net(fake_img)
output:
"""
forward_pre_hook input:(tensor([[[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]]]),)
"""
4. Module.register_backward_hook
Module.register_backward_hook
功能:注册 module 反向传播的 hook 函数
参数:
- module: 当前网络层
- grad_input:当前网络层输入梯度数据
- grad_output:当前网络层输出梯度数据
# 4.
# 注册module反向传播的hook函数
# hook(module, grad_input, grad_output) -> Tensor or None
# module:当前网络层
# grad_input:当前网络层输入梯度数据
# grad_output:当前网络层输出梯度数据
torch.nn.Module.register_backward_hook(hook)
# ---------------------------- 5 Module.register_backward_hook ----------------------------
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 2, 3)
self.pool1 = nn.MaxPool2d(2, 2)
def forward(self, x):
x = self.conv1(x)
x = self.pool1(x)
return x
def backward_hook(module, grad_input, grad_output):
print("backward hook input:{}".format(grad_input))
print("backward hook output:{}".format(grad_output))
# 初始化网络
net = Net()
net.conv1.weight[0].detach().fill_(1)
net.conv1.weight[1].detach().fill_(2)
net.conv1.bias.data.detach().zero_()
# 注册hook
fmap_block = list()
input_block = list()
net.conv1.register_backward_hook(backward_hook)
# inference
fake_img = torch.ones((1, 1, 4, 4)) # batch size * channel * H * W
output = net(fake_img)
loss_fnc = nn.L1Loss()
target = torch.randn_like(output)
loss = loss_fnc(target, output)
loss.backward()
output:
"""
backward hook input:(None, tensor([[[[0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000]]],
[[[0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000]]]]), tensor([0.5000, 0.5000]))
backward hook output:(tensor([[[[0.5000, 0.0000],
[0.0000, 0.0000]],
[[0.5000, 0.0000],
[0.0000, 0.0000]]]]),)
"""
4. 特征图提取
23.3 CAM(Class Activation Map, 类激活图) and Grad-CAM
1. CAM
- CAM:类激活图,Class Activation Map
- Class Activation Map:在最后 Conv 层后、FC 层前增加 GAP 层,得到一组权重,与其对应特征图加权,得到 CAM
Grad-CAM:《Grad-CAM:Visual Explanations from Deep Networks via Gradient-based Localization》
2. Grad-CAM
- Grad-CAM:CAM 的改进,使用梯度作为特征图权重
Grad-CAM:《Grad-CAM:Visual Explanations from Deep Networks via Gradient-based Localization》
3. CAM and Grad-CAM 实例
分析与代码:https://zhuanlan.zhihu.com/p/75894080