深度学习模型基础知识汇总(持续更新)
文章目录
- 一、卷积和转置卷积
- 1、基本概念
- 2、输出尺寸的计算公式
- 3、nn.Conv2d、nn.ConvTranspose2d的参数k,s,p在图形上的含义,卷积运算过程
- 二、模型计算量(FLOPs)和参数量(Params)
- 三、python的argparse库
- 四、数学期望与概率密度函数
- 五、信息熵
- 六、信息熵、KL散度、交叉熵的关系
- 七、hook机制
- 八、top_k和AvgrageMeter函数
一、卷积和转置卷积
1、基本概念
- 卷积(convolution):用来提取图片特征、减小图像尺寸,所以被称为下采样
- 转置卷积(deconvolution(不准确)、transposed convolutionon(推荐称呼)、fractional strided convolution):用来扩大图像尺寸,所以被称为上采样
- 对卷积的理解:(1)数字信号处理中,卷积是指一个函数(如单位响应)在另一个函数(如输入信号)上的加权叠加(即在输入信号的每个位置,叠加一个单位响应,就得到了输出信号) (2)信号与系统中,卷积是指系统某时刻的响应是现在时刻的信号输入的响应以及之前若干时刻信号输入的响应残留影响的一个叠加效果。某时刻函数 f f f与 g g g的卷积指 ∫ R n f ( τ ) g ( t − τ ) d τ \ \int_{\mathbb{R}^n} f(\tau) g(t - \tau)\, d\tau ∫Rnf(τ)g(t−τ)dτ,随着 t t t的不同取值,该卷积构成了新函数 h ( t ) = ( f ∗ g ) ( t ) h(t)=(f * g)(t) h(t)=(f∗g)(t)。例如馒头生产速度 f f f,腐败速度 g g g,求24小时后该时刻馒头总腐败个数: ∫ 0 24 f ( t ) g ( 24 − t ) d t \int_0^{24} {f(t)g(24 - t)dt} ∫024f(t)g(24−t)dt(3)图像处理中,卷积是指让卷积核的中心点与对图像上的某点重合,然后卷积核上的点和图像上对应的点相乘再相加,就得到图像上某点的卷积值
- 非常好的对转置卷积的理解的视频
- 卷积和转置卷积的联系与区别:(1)转置卷积也是卷积,而不是卷积的反过程或逆运算;(2)特征层a经卷积后(参数k,s,p)得到特征层b,特征层b经转置卷积(相同的参数k,s,p)后得到特征层c。注意:特征层c只是与特征层a的shape大小相同,但数值完全不同
2、输出尺寸的计算公式
- I n p u t : ( N , C i n , H i n , W i n ) Input: (N,Cin,Hin,Win) Input:(N,Cin,Hin,Win), O u t p u t : ( N , C o u t , H o u t , W o u t ) Output: (N,Cout,Hout,Wout) Output:(N,Cout,Hout,Wout)
- 卷积:根据i,k,p,s计算输出尺寸:o=⌊(i-k+2p)/s⌋+1,式中,i:输入尺寸;o:输出尺寸;p:padding;k: kernel_size;s: stride。⌊…⌋表示
向下取整
。class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, bias=True) #in_channels输入信号的通道数,out_channels卷积产生的通道数,dilation卷积核元素之间的间距 #常用等尺寸变换配置:(k, s, p) = (1, 1, 0) or (3, 1, 1) or (5, 1, 2) or (7, 1, 3)
- 转置卷积:每一条边输出的尺寸:o=(i-1)*s-2p+k,即与上面卷积计算公式相反(o和i的位置换一下)
class torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, bias=True, dilation=1)
- 例子:
import torch import torch.nn as nnx1=torch.randn(1,3,5,5) temp=nn.Conv2d(in_channels=3,out_channels=256,kernel_size=3,stride=2,padding=1)(x1) #temp.shape:torch.Size([1,256,3,3]),即[(5-3+1*2)/2]+1=3x2=nn.ConvTranspose2d(in_channels=256,out_channels=3,kernel_size=3,stride=2,padding=1)(temp) #x2.shape:torch.Size([1,3,5,5]),即(3-1)*2+3-1*2=5 #即:x1——>nn.Conv2d——>nn.ConvTranspose2d——>x2。其中x1、x2的shape相同,但数值完全不同 print(x1.shape==x2.shape, x1==x2)#True,False
3、nn.Conv2d、nn.ConvTranspose2d的参数k,s,p在图形上的含义,卷积运算过程
- nn.Conv2d:
1、k,s,p在图形上的含义:k(卷积核的尺寸),s(每次移动的步数),p(两边填充0的个数)
2、两个2x2的rgb三通道的卷积核对一张3x3的rgb三通道的图片的卷积运算(对应位置相乘再相加)过程:
3、输出尺寸的计算:o=⌊(i-k+2p)/s⌋+1,如上例子:o=⌊(3-2+2x0)/1⌋+1=2,即输入3x3的图片经卷积后变成2x2的图片 - nn.ConvTranspose2d:
1、与参数k,s,p相关的式子在图形上的含义:
(1)在输入特征图元素间填充s-1行0、s-1列0
(2)在输入特征图四周填充k-p-1行0、k-p-1列0
2、一个3x3单通道卷积核对一张2x2图片的转置卷积运算过程:
(3)继续上面的(2),将卷积核参数上下、左右翻转
(4)做正常卷积运算(填充恒为0,步距恒为1)
3、输出尺寸的计算:由上面的(1)可知输入尺寸已经变成了i+(i-1)(s-1),由(2)可知输入尺寸继续变成i+(i-1)(s-1)+2(k-p-1),由(4)可知p变成0,s变成1,在此基础上做正常的卷积运算,即o=⌊(i-k+2p)/s⌋+1={[i+(i-1)(s-1)+2(k-p-1)]-k+2x0}/1+1=(i-1)xs-2p+k,与nn.ConvTranspose2d官方给的计算公式相同,也与把卷积计算公式的o和i的位置换一下得到的公式相同。上图o=(i-1)xs-2p+k=(2-1)x1-2x0+3=4
二、模型计算量(FLOPs)和参数量(Params)
- 计算量:指模型推断时需要多少浮点计算次数在计算机视觉论文中,常常将一个’乘-加’组合视为一次浮点运算,英文表述为’Multi-Add’,通常是MAC来表示(表征了时间复杂度)。注:MAC(Multiply-Accumulate,乘积累加运算):a+b*c
- 参数量:指模型含有多少参数,直接决定模型文件的大小,也影响模型推断时对内存的占用量(表征了空间复杂度)。假设有a个参数,由于每个requires_grad=True的tensor用浮点数表示占4个字节,所以模型大小a*4(byte)
- (1)标准卷积层:输入图片 ( c i , m i , n i ) (c_i,m_i,n_i) (ci,mi,ni) ,卷积核 ( k m , k n ) (k_m,k_n) (km,kn), 输出图片 ( c o , m o , n o ) (c_o,m_o,n_o) (co,mo,no)
参数量: ( k m × k n × c i + 1 ) × c o (k_m × k_n × c_i + 1) × c_o (km×kn×ci+1)×co
计算量(乘加次数): ( k m × k n × c i ) × m o × n o × c o (k_m × k_n × c_i) × m_o × n_o × c_o (km×kn×ci)×mo×no×co
(2)全连接层:参数量=计算量(乘加次数)= m 输入 × m 输出 m_{输入} × m_{输出} m输入×m输出。 参考 - 计算参数量和计算量的库及使用方法
三、python的argparse库
-
argsparse:是python的命令行解析的标准模块,内置于python。作用:可以让我们直接在命令行中就可以向程序中传入参数并让程序运行。
-
注意事项说明:
a、argsparse模块的作用是通过命令行向程序传参,所以在命令行执行时
不必有default参数(命令行会传入参数) b、在IDE执行时
必须有default参数(只会用默认参数) c、在jupyter notebook执行时
必须有default参数(只会用默认参数),且下面的args = parser.parse_args()改成args = parser.parse_args(args=[])
(1)_0.py文件的代码(参数action的使用链接)import argparse #ArgumentParser类生成一个parser对象, 参数解析器 parser = argparse.ArgumentParser(description="Demo of argparse")#采用对象的add_argument函数来增加参数,default参数表示没有提供参数时将此值当做参数值 parser.add_argument('-n', '--name', type=str, default='Li', help='input name')#--代表可选参数,命令行输入时需要:--name='zhang'。可选参数可输入可不输入。default参数表示没有提供参数时将此值当做默认值. parser.add_argument('--sex', type=str, required=True, help='input male/female')#虽然--为可选参数,但required=True,所以必须要输入。 parser.add_argument('birthday', type=int, nargs='+', help='input birthday')#没有--,代表位置参数,命令行输入时需要:--name='zhang'。位置参数必须输入。 # nargs='*' 表示参数可设置零个或多个 # nargs='+' 表示参数可设置一个或多个 # nargs='?' 表示参数可设置零个或一个(类似正则表达式) #parser.add_argument('--no-cuda', action='store_true', default=False, help='Disable cuda for training') #有了action这一参数,就相当于把--no-cuda参数设成了一个“开关”。在下面命令行再传一个参数--no-cuda,相当于拨了开关,将--no-cuda从设置的默认值False变成True#采用对象的parse_args函数来获取解析的参数,获取的参数是一种类似于python字典的数据类型,可以使用arg.参数名来提取这个参数,如下: args = parser.parse_args() print(args)
(2)命令行传入参数并让程序运行
四、数学期望与概率密度函数
- 数学期望(均值):试验中每次可能结果乘以其结果概率的总和,反映随机变量平均取值的大小。
- 离散的随机变量 X {\displaystyle X} X的数学期望: E ( X ) = ∑ i p i x i {\displaystyle\operatorname{E} (X)=\sum _{i}p_{i}x_{i}} E(X)=i∑pixi;
连续的随机变量 X {\displaystyle X} X的数学期望: E ( X ) = ∫ − ∞ ∞ x f ( x ) d x {\displaystyle \operatorname {E} (X)=\int _{-\infty }^{\infty }xf(x)\,\mathrm {d} x} E(X)=∫−∞∞xf(x)dx,其中 f ( x ) {\displaystyle f(x)} f(x)指概率密度函数
- 离散随机变量的数学期望的例子:求投一次骰子的点数的期望值(期望值并不一定包含于其分布值域,也并不一定等于值域平均值) E ( X ) = 1 ⋅ 1 6 + 2 ⋅ 1 6 + 3 ⋅ 1 6 + 4 ⋅ 1 6 + 5 ⋅ 1 6 + 6 ⋅ 1 6 = 1 + 2 + 3 + 4 + 5 + 6 6 = 3.5 \displaystyle {\begin{aligned}\operatorname {E} (X)&=1\cdot {\frac {1}{6}}+2\cdot {\frac {1}{6}}+3\cdot {\frac {1}{6}}+4\cdot {\frac {1}{6}}+5\cdot {\frac {1}{6}}+6\cdot {\frac {1}{6}}={\frac {1+2+3+4+5+6}{6}}=3.5\end{aligned}} E(X)=1⋅61+2⋅61+3⋅61+4⋅61+5⋅61+6⋅61=61+2+3+4+5+6=3.5
连续随机变量的数学期望的例子:外卖预计会在两小时送达,求期望送达时间: E ( X ) = ∫ 0 120 x ⋅ 1 120 d x = 60 {\displaystyle \operatorname {E} (X)=\int _{0 }^{120}x\cdot{\frac {1}{120}}\,\mathrm {d} x}=60 E(X)=∫0120x⋅1201dx=60分钟 - 连续型随机变量的概率密度函数 f ( x ) {\displaystyle f(x)} f(x):随机变量的输出值在某个确定的取值点附近的可能性的函数,
类似于小球在不同位置的密度变化函数,会直接给出
(参考)。注意:概率密度并不直接表示概率,随机变量的取值落在某个区域内的概率为概率密度函数在这个区域上的积分。例如:随机变量X在 [ a < X ≤ b ] {\left[a<X\leq b\right]} [a<X≤b]上的概率为:
P [ a < X ≤ b ] = F ( b ) − F ( a ) = ∫ − ∞ b f ( x ) d x − ∫ − ∞ a f ( x ) d x = ∫ a b f ( x ) d x P\left[a<X\leq b\right]=F(b)-F(a)=\int _{{-\infty }}^{{b}}f(x)\,dx-\int _{{-\infty }}^{{a}}f(x)\,dx=\int _{{a}}^{{b}}f(x)\,dx P[a<X≤b]=F(b)−F(a)=∫−∞bf(x)dx−∫−∞af(x)dx=∫abf(x)dx,其中 F ( ) F() F()代表累积分布函数
五、信息熵
- 热熵(热力学中)表示分子状态混乱程度的物理量。
- 信息熵(香农将热熵借用到信息论中)表示信息不确定性的物理量。(信息熵越大,信息不确定性越高)参考视频1、2
(1)某个事件有多种可能情况,具体是哪种情况的不确定性叫做信息熵(或熵)
(2)信息熵与信息量的数值相同,但意义相反。某个事件被解除的不定性就是在通信中所要传送的信息量(即:消除信息熵=获取信息量,举例如下)。参考 - 信息熵的基本单位是bit。
抛一次硬币,可能出现正反2种等概率情况,将抛一次硬币的不确定性(信息熵)设为
1bit(标准参照物)。
例(1)某个事件有4种等概率情况,结果是哪种情况的不确定性,即信息熵为:2bit(相当于抛了2次硬币),或 log 2 4 {\log_2}4 log24=2bit。如果有人告诉我们该事件的结果是第一种情况,那么这句话的信息量便是2bit。
例(2)某道题有abcd四个选项且有一半可能是a选项,结果是哪种选项的不确定性,即信息熵为:≠( log 2 4 {\log_2}4 log24=2)bit。因为虽然该事件有4种情况,但不是等概率情况,其中P(a)=1/2,P(b)=P(b)=P(b)=1/6。应该分别测量该事件每种可能情况的信息熵后,乘以它们各自的发生概率,再相加
,即:- 如何确定上面选项是b这一情况的信息熵?(概率为1/6的选项是b这一情况的不确定性相当于抛掷多少个硬币所产生的不确定性呢):相当子事件有6个等概率情况,求该子事件的信息熵,即 log 2 6 {\log_2}6 log26
- 所以例(2)的信息熵为: 1 2 log 2 2 + 1 6 log 2 6 + 1 6 log 2 6 + 1 6 log 2 6 ≈ 1.79 \frac{1}{2}{\log _2}2 + \frac{1}{6}{\log _2}6 + \frac{1}{6}{\log _2}6 + \frac{1}{6}{\log _2}6 \approx 1.79 21log22+61log26+61log26+61log26≈1.79bit。所以有一半可能是a选项这句话的信息量是: log 2 4 − 1.79 {\log_2}4-1.79 log24−1.79= 0.21 0.21 0.21bit。(同理替换成有1/4可能是a选项这句话的信息量是:2-4* 1 2 log 2 2 \frac{1}{2}{\log _2}2 21log22=0bit,相当于废话)
- 信息熵的公式:由上面例题(2)可知,某个事件有多种可能情况用随机变量 X X X= { x 1 , x 2 , . . . , x m } \{ {x_1},{x_2},...,{x_m}\} {x1,x2,...,xm}表示,每种情况发生的概率为 p ( x i ) p(x_i) p(xi),那么该事件(系统)的信息熵公式为: H ( X ) = def ∑ i p ( x i ) log 2 1 p ( x i ) = ∑ i p ( x i ) ( − log 2 p ( x i ) ) = − ∑ i p ( x i ) log 2 p ( x i ) , {\rm H}(X)\stackrel{\text{def}}{=}\sum\limits_i {p(x_i)} {\log _2}\frac{1}{{p(x_i)}}=\sum\limits_i {p(x_i)} (-{\log _2}p(x_i))=-\sum\limits_i {p(x_i)} {\log _2}p(x_i), H(X)=defi∑p(xi)log2p(xi)1=i∑p(xi)(−log2p(xi))=−i∑p(xi)log2p(xi),即:信源的平均不确定性应当为单个符号不确定性 − log 2 p ( x i ) {-\log _2}p(x_i) −log2p(xi)(即上述每种可能情况的信息熵)
的统计平均值(即数学期望)
- 规律总结1:某事件有m种情况,每种情况的概率越接近等概率1/m,则该事件越混乱,该事件的信息熵越大,某句传递了该事件结果(使事件由不确定变确定)的话所包含的信息量越大。
规律总结2:通常默认事件的每种情况是等概率的,
这时概率越小(即情况数m变大),某句使事件由不确定变确定的话所包含的信息量越大。例如:在有16支队伍和有8支队伍参数的情况下,"中国队夺冠"这句话在前面情况下(每种情况的概率为1/16)的信息量(log16=4bit)比后面情况下(每种情况的概率为1/8)的信息量(log8=3bit)要大。
六、信息熵、KL散度、交叉熵的关系
(1) ( 信息 ) 熵: H ( X ) = def − ∑ i p ( x i ) log 2 p ( x i ) > 0 (信息)熵:{\rm H}(X)\stackrel{\text{def}}{=}-\sum_i {p(x_i)} {\log _2}p(x_i)>0 (信息)熵:H(X)=def−∑ip(xi)log2p(xi)>0,
意义:
某事件有m种情况 { x 1 , x 2 , . . . , x m } \{ {x_1},{x_2},...,{x_m}\} {x1,x2,...,xm},该事件具体是哪种情况的不确定性,即该事件的(信息)熵。
(2) K L 散度 ( 相对熵 ) : D K L ( A ∥ B ) = def ∑ i p A ( x i ) [ − log ( p B ( x i ) ) − ( − log ( p A ( x i ) ) ) ] ≠ D K L ( B ∥ A ) KL散度(相对熵):D_{K L}(A \| B)\stackrel{\text{def}}{=}\sum_{i} p_{A}\left(x_{i}\right) [-\log \left(p_{B}\left(x_{i}\right)\right)-(-\log \left(p_{A}\left(x_{i}\right)\right))]\ne D_{K L}(B \| A) KL散度(相对熵):DKL(A∥B)=def∑ipA(xi)[−log(pB(xi))−(−log(pA(xi)))]=DKL(B∥A),
意义:
事件A和事件B各有m种情况,每种情况是一个子事件。KL散度为事件B的子事件的信息熵 − log ( p B ( x i ) ) -\log \left(p_{B}\left(x_{i}\right)\right) −log(pB(xi))与事件A的子事件的信息熵 − log ( p A ( x i ) ) -\log \left(p_{A}\left(x_{i}\right)\right) −log(pA(xi))的差值在以事件A的概率 p A ( x i ) p_{A}\left(x_{i}\right) pA(xi)加权下的统计平均值。即:以事件A为基准,去考虑事件B(相当于神经网络的m个输出)与A(相当于m分类问题中数据集的真实标签)相差有多少。(衡量两个概率分布的差异)
(3) 交叉熵 : H ( A , B ) = def − ∑ i p A ( x i ) log ( p B ( x i ) ) > 0 交叉熵:{\rm H}(A,B)\stackrel{\text{def}}{=}-\sum_{i} p_{A}\left(x_{i}\right) \log \left(p_{B}\left(x_{i}\right)\right)>0 交叉熵:H(A,B)=def−∑ipA(xi)log(pB(xi))>0,
三者的联系:
a、由于KL散度(相对熵): D K L ( A ∥ B ) = def ∑ i p A ( x i ) log ( p A ( x i ) p B ( x i ) ) = ∑ i [ p A ( x i ) log ( p A ( x i ) ) − p A ( x i ) log ( p B ( x i ) ) ] D_{K L}(A \| B)\stackrel{\text{def}}{=}\sum_{i} p_{A}\left(x_{i}\right) \log \left(\frac{p_{A}\left(x_{i}\right)}{p_{B}\left(x_{i}\right)}\right)=\sum_{i} [p_{A}\left(x_{i}\right) \log \left(p_{A}\left(x_{i}\right)\right)-p_{A}\left(x_{i}\right) \log \left(p_{B}\left(x_{i}\right)\right)] DKL(A∥B)=def∑ipA(xi)log(pB(xi)pA(xi))=∑i[pA(xi)log(pA(xi))−pA(xi)log(pB(xi))],即: D K L ( A ∥ B ) = − H ( A ) + H ( A , B ) = − 事件 A 的熵 + 交叉熵 D_{K L}(A \| B)={-\rm H}(A)+{\rm H}(A,B)=-事件A的熵+交叉熵 DKL(A∥B)=−H(A)+H(A,B)=−事件A的熵+交叉熵。
b、同时吉布斯不等式表明: H ( A , B ) ≥ H ( A ) {\rm H}(A,B)\geq {\rm H}(A) H(A,B)≥H(A),且只有事件A和事件B的概率分布相同时取 = = =号,所以KL散度恒大于等于0。
交叉熵的意义:
当A固定不变时(真实标签),最小化KL散度
D K L ( A ∥ B ) D_{K L}(A \| B) DKL(A∥B)等价于最小化交叉熵
H ( A , B ) {\rm H}(A,B) H(A,B),此时B的概率分布(即神经网络的输出)逐渐向A(即基准/真实标签)靠拢,KL散度逐渐接近0。(因此交叉熵和KL散度意义一样,都是衡量两个概率分布的差异,但交叉熵的计算量更小)
七、hook机制
参考链接
1、Pytorch的backward()自动求导机制
会舍弃图计算的中间变量的grad,通过中间变量.register_hook(func)
可以获取该中间变量的grad
,并将该grad传给func函数(自定义功能函数)进行执行
import torch
from torch.autograd import Variable
def print_grad(grad): print('grad is \n',grad)
x = torch.rand(2,1,requires_grad=True)
y = 3*x+3
z = torch.sum(2*y)
y_hook = y.register_hook(print_grad) #对中间变量y做了一个钩子,起名为y_hook(句柄)
z.backward() #自动求导时会启动上面的中间变量y的钩子
y_hook.remove() #若不用了就删掉y_hook这个钩子,减少计算复杂度
#输出:grad is tensor([[2.], [2.]])
#注意:如果print(x.grad,y.grad,z.grad),输出tensor([[6.], [6.]]) None None
2、Pytorch中经模型前向传播
直接得到输出层的输出,通过中间层.register_forward_hook(hook)
可以获取中间层的输出
,并将该输出传给hook函数(自定义功能函数)进行执行(最常用的场景是需要提取模型的某一层的输出特征,但又不希望修改其原有的模型定义文件)
import torch
import torchvision.models as models
net = models.vgg11()
x = torch.randn(2, 3, 32, 32)
features = []
def hook(module, input, output): features.append(output)print(features[0].size())
avgpool_hook = net.avgpool.register_forward_hook(hook)#对中间层avgpool做了一个钩子,起名为avgpool_hook(句柄)
y = net(x)#前向传播会启动上面的钩子,且x对应hook函数的input,net.avgpool层的输出对应hook函数的output
avgpool_hook.remove() #若不用了就删掉avgpool_hook这个钩子,减少计算复杂度
#输出:torch.Size([2, 512, 7, 7])
八、top_k和AvgrageMeter函数
#计算top_k分类准确率的方法
def accuracy(output, target, topk=(1, 5)):maxk = max(topk)batch_size = target.size(0)_, pred = output.topk(maxk, 1, True, True)pred = pred.t()correct = pred.eq(target.view(1, -1).expand_as(pred))res = []for k in topk:correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True)res.append(correct_k.mul_(100.0 / batch_size))return res
#累计多次后求平均
class AvgrageMeter(object):def __init__(self):self.reset()def reset(self):self.avg = 0self.sum = 0self.cnt = 0def update(self, val, n=1):self.sum += val * nself.cnt += nself.avg = self.sum / self.cntobjs = utils.AvgrageMeter() # 用于保存loss的值
top1 = utils.AvgrageMeter() # 用于保存top1准确率
output = torch.randint(low=0, high=6, size=[8, 10])
target = torch.ones(8, dtype=torch.long)
for ···loss = criterion(output, labels)prec1,prec5 = accuracy(output, target)objs.update(loss.data[0], output.size(0))top1.update(prec1.data[0], output.size(0))
print(f'loss = {objs.avg}, acc = {top1.avg}')