np一些操作:
a = [::-1] #最后一个轴倒序输出
有序字典:
import collections
print "Regular dictionary"
d={}
d['a']='A'
d['b']='B'
d['c']='C'
for k,v in d.items():
print k,v
print "\nOrder dictionary"
d1 = collections.OrderedDict()
d1['a'] = 'A'
d1['b'] = 'B'
d1['c'] = 'C'
d1['1'] = '1'
d1['2'] = '2'
for k,v in d1.items():
print k,v
输出:
Regular dictionary
a A
c C
b B
Order dictionary
a A
b B
c C
1 1
2 2
torch一些操作:
- IntermediateLayerGetter()提取网络到某位置
Examples::
>>> m = torchvision.models.resnet18(pretrained=True)
>>> # extract layer1 and layer3, giving as names `feat1` and feat2`
>>> new_m = torchvision.models._utils.IntermediateLayerGetter(m,
>>> {'layer1': 'feat1', 'layer3': 'feat2'})
>>> out = new_m(torch.rand(1, 3, 224, 224))
>>> print([(k, v.shape) for k, v in out.items()])
>>> [('feat1', torch.Size([1, 64, 56, 56])),
>>> ('feat2', torch.Size([1, 256, 14, 14]))]
- tensor.permute()
相当于np.transpose()
narrow()`, `view()`, `expand()`, `transpose()`,`permute()
# 都不会改变数据的内存位置
- tensor.contiguous()
张量复制
学习率退火
余弦退火衰减法,学习率会先上升再下降,这是退火优化法的思想。
上升的时候使用线性上升,下降的时候模拟cos函数下降。执行多次。
lr_scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=5, eta_min=1e-5)
模型可视化
from torchsummary import summary
...
summary(model, (1, 24, 28, 28))
提升网络准确度
- SE网络用于大model中,可以加强通道之间的特征关系提取
- 把LeRU换成H-Swish
batch大小选择
一般来说往大的选
过大时,因为模型的下降方向基本确定,过大或许造成收敛变慢,过小会因为震荡过大难以收敛
因此,当设备限制导致不能选取过大的batch时,应该将学习率降低
resize_size = crop_size * 0.875。
分类与回归区别
回归:线性回归输出一个标量的函数公式wx+b,它可以根据输入的值给出预测值
分类:分类体现在逻辑回归中,具体做法是将以上的线性回归的预测值经过例如sigmoid的函数映射到(0,1)之间,然后对这个概率值进行分类判断,输出一个类
分类网络
- Alexnet
开创了深度的卷积神经网络能自己捕获图片的特征
运用了DP和Relu,LRN对局部的特征进行归一化(不是BN)
- VGG-16
全局用了标准的3x3卷积核,用小卷积替代了大卷积,更省资源
3.Inception(googlenet) 系列
相比以前的卷积神经网络,进行了宽度的延申
用1x1,3x3,5x5的卷积核使得感受野增大,并且通过设定stride=1,只调整pad=0、1、2使得输出特征方班对齐
(xception)
- ResNet 解决了 deep NN 的两大问题:
- deep NN 的梯度弥散和爆炸问题;
- deep NN 的精度随着模型的加深,会逐渐饱和不再上升,甚至会大幅度下降。
激活函数的作用,如果没有激活函数会怎样?
激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。
激活函数的作用:激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。
这个变换要满足什么条件?
- 非线性,这样增加网络的深度才有意义
- 可导的,不然怎么做梯度下降
- 易于计算的
- 输出空间最好是有限的,这条好像也不是必须的,Relu就没有遵循这条
防止过拟合的方法
1.BN非线性的数据
2.DP层
BN层
BN应作用在非线性映射前
梯度弥散和爆炸
relu存在消失和爆炸
sigmoid只存在弥散
爆炸:随着 网络层数变深, activations倾向于越大和越小的方向前进, 往大走梯度爆炸,往小梯度消失(1以下越乘越小)
弥散:输入激活函数的数字过大或过小造成激活函数输出的梯度值很小
如何更改预训练模型?
继承后用model.children()提取出模型放到nn.Sequential中,根据需求提取固定的位置再重新写到 forward中
反卷积又称为转置卷积的原因
\data\矩阵实现卷积.png)
卷积方法可以其实可以通过矩阵乘法实现,只要知道对应位置并且后面重新reshape就可以:
$$ Y=WX $$
torch.matmul(W, X.reshape(-1)).reshape(2, 2)
那如果反过来知道Y和w,想还原X呢?
$$ W^TY=X $$
torch.matmul(W.T, Y.reshape(-1)).reshape(3, 3)
故成为转置卷积
python中的 *parameter 是用来接受任意多个参数并将其放在一个元组中
感受野和featuremap(特征图)大小的计算方法
感受野:该层一个像素对应输入图的大小
(从输入到输出)
$$ \begin{gathered} S_n=S_{n-1}*stride \\ RF_n= RF_{n-1}+(k-1)*S_n \end{gathered} $$
$S_n$代表到目前位置积累的步数,$k$代变核的体积(mlp算作1),输入的第一层RF=1(相当于1像素对1像素)
featuremap:
该图输入后输出的大小
以w宽为例子
$$ W_{out}=\frac{W_{in}+2Padding-k}{Stride}+1 $$
验证集精度高于训练集精度的原因
1.预训练效果太好
2.正则化或者DP过大,或者数据增强过多导致数据原本特征发生改变
3.训练是每个batch后平均出来的,对比起验证集的有滞后性
模型评估
Params参数量计算
卷积层:
$k_w*k_h*C_{in}*C_{out}$
算入bias则为$k_w*k_h*C_{in}*C_{out}+C_{out}$
MLP:
$N_{in}*N_{out}$,其中$N_{in}=H_{in}*W_{in}*C_{in}$
如果要算上bias则为$N_{in}*N_{out}+N_{out}$
Flops浮点计算量:
在计算FLOPS时,我们通常将加,减,乘,除,求幂,平方根等计为单个FLOP。
但是,实际上,我们通常不计这些操作,因为它们只占总时间的一小部分。通常只计算矩阵乘法和点积(dot product),忽略激活函数的计算量。
卷积层:
$k_w*k_h*C_{in}*C_{out}*H_{out}*W_{out}$
MLP:
参数量=计算量=$N_{in}*N_{out}$
MACC(也叫MADD):
multiply-accumulate operations:先乘起来再加起来的运算次数。
卷积层:
$k_w*k_h*C_{in}*C_{out}*H_{out}*W_{out}$
MLP:
参数量=计算量=$N_{in}*N_{out}$
(如果忽略激活和乘法比加法多1的话,两个方法计数方式一致)
model ensemble
待补充...
nn.Conv2d和nn.ConvTranspose2d
nn.Conv2d
是卷积操作
nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,padding=0,dilation=1,groups=1,bias=True)
经过卷积后的图像大小计算为:
$$ M=\frac{W_{in}-F+2P}{S}+1 $$
nn.ConvTranspose2d
是反卷积操作
nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1)
padding(int or tuple, optional) - 输入的每一条边补充0的层数,高宽都增加2*padding
output_padding(int or tuple, optional) - 输出边补充0的层数,高宽都增加padding
经过反卷积的计算公式为:
$$ output = (input-1)stride+outputpadding -2padding+kernelsize $$
pytorch中的hook
hook种类分为两种:
Tensor级别
register_hook(hook) ->为Tensor注册一个backward hook,用来获取变量的梯度;
hook必须遵循如下的格式:hook(grad) -> Tensor or None
其实就是要定义一个这样的函数来接收
import torch
from torch.autograd import Variable
def print_grad(grad):
print('grad is \n',grad)
x = Variable(torch.randn(2, 1), requires_grad=True)
## x = torch.rand(2,1,requires_grad=True) # 等效
print('x value is \n',x)
y = x+3
print('y value is \n',y)
z = torch.mean(torch.pow(y, 1/2))
lr = 1e-3
y.register_hook(print_grad)
z.backward() # 梯度求解
x.data -= lr*x.grad.data
print('new x is\n',x)
------
output:
x value is
tensor([[ 2.5474],
[-1.1597]], requires_grad=True)
y value is
tensor([[5.5474],
[1.8403]], grad_fn=<AddBackward0>)
grad is
tensor([[0.1061],
[0.1843]])
new x is
tensor([[ 2.5473],
[-1.1599]], requires_grad=True)
注意,grad值在z.backward()
后产生,但是要在计算梯度值之前声明
nn.Module级别
作用对象可为一个已经定义的对象,并实例化的model
以register_forward_hook(hook)
为例子做说明
import torch
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module): # 一个网络结构
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
out = F.relu(self.conv1(x)) #1
out = F.max_pool2d(out, 2) #2
out = F.relu(self.conv2(out)) #3
out = F.max_pool2d(out, 2)
out = out.view(out.size(0), -1)
out = F.relu(self.fc1(out))
out = F.relu(self.fc2(out))
out = self.fc3(out)
return out
features = []
def hook(module, input, output):
# module: model.conv2
# input :in forward function [#2]
# output:is [#3 self.conv2(out)]
features.append(output.clone().detach())
# output is saved in a list
net = LeNet() ## 模型实例化
x = torch.randn(2, 3, 32, 32) ## input
## 获取整个Lenet模型 forward中所有有关于conv2的中间结果
handle = net.conv2.register_forward_hook(hook)
y = net(x)
print(features[0].size()) # 即 [#3 self.conv2(out)]
handle.remove() ## hook删除
register_forward_hook(hook)
和register_backward_hook(hook)
两种方法,分别对应前向传播和反向传播的hook函数。
其中register_forward_hook(hook)
中的hook需要满足
hook(module, input, output) -> None or modified output
register_backward_hook(hook)
则要满足
hook(module, grad_input, grad_output) -> tuple(Tensor) or None
将来register_backward_hook(hook)
将会变成register_full_backward_hook(hook)
使用完毕后,记得将hook后的值进行handle.remove(),不然计算耗费资源
其中register_forward_hook(hook)
可用在U-net中,层级跳跃中的output的传递:
rn = nn.Sequential(*layers)
class UnetBlock(nn.Module):
def __init__(self, up_in, x_in, n_out):
super().__init__()
up_out = x_out = n_out//2
self.x_conv = nn.Conv2d(x_in, x_out, 1)
self.tr_conv = nn.ConvTranspose2d(up_in, up_out, 2, stride=2)
self.bn = nn.BatchNorm2d(n_out)
def forward(self, up_p, x_p):
up_p = self.tr_conv(up_p)
x_p = self.x_conv(x_p)
cat_p = torch.cat([up_p,x_p], dim=1)
return self.bn(F.relu(cat_p))
class SaveFeatures():
features=None
def __init__(self, m): self.hook = m.register_forward_hook(self.hook_fn)
def hook_fn(self, module, input, output): self.features = output
def remove(self): self.hook.remove()
class Unet34(nn.Module):
def __init__(self, rn):
super().__init__()
self.rn = rn
self.sfs = [SaveFeatures(rn[i]) for i in [2,4,5,6]]
self.up1 = UnetBlock(512,256,256)
self.up2 = UnetBlock(256,128,256)
self.up3 = UnetBlock(256,64,256)
self.up4 = UnetBlock(256,64,256)
self.up5 = nn.ConvTranspose2d(256, 1, 2, stride=2)
def forward(self,x):
x = F.relu(self.rn(x))
x = self.up1(x, self.sfs[3].features)
x = self.up2(x, self.sfs[2].features)
x = self.up3(x, self.sfs[1].features)
x = self.up4(x, self.sfs[0].features)
x = self.up5(x)
return x[:,0]
def close(self):
for sf in self.sfs: sf.remove()
nn.Module的children()方法与modules()方法的区别
前者modules()会递归地遍历所有子模块, 包括子模块的子模块、子模块的子模块的子模块,等等依此类推. 后者children()只会遍历所有直接子模块,不再递归下去, 即子模块的子模块不会被遍历到.
ref:
k-mean
k为人为规定的类别,簇划分为$(C_1,C_2,…C_k)$,则我们的目标是最小化平方误差E:
$$ E=\sum^k_{i=1}\sum_{x\in C_i}||x-\mu_i||^2_2 $$
其中$μ_i$是簇Ci的均值向量,有时也称为质心,表达式为:
$$ \mu_i=\frac{1}{|C_i|}\sum_{x \in C_i}x $$
Transforms
torchvision.transforms.ToTensor
对于一个图片img,调用ToTensor转化成张量的形式,发生的不是将图片的RGB三维信道矩阵变成tensor图片在内存中以bytes的形式存储,转化过程的步骤是:
img.tobytes() 将图片转化成内存中的存储格式
torch.BytesStorage.frombuffer(img.tobytes() ) 将字节以流的形式输入,转化成一维的张量
对张量进行reshape
对张量进行permute(2,0,1)
将当前张量的每个元素除以255
输出张量torchvision.transforms.ToPILImage
对于一个Tensor的转化过程是:将张量的每个元素乘上255
将张量的数据类型有FloatTensor转化成Uint8
将张量转化成numpy的ndarray类型
对ndarray对象做permute (1, 2, 0)的操作
利用Image下的fromarray函数,将ndarray对象转化成PILImage形式
输出PILImage
————————————————
版权声明:本文为CSDN博主「Adversity-sl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40821163/article/details/109382111
PS
#OpenCV读进来的图像,通道顺序为BGR, 而matplotlib的顺序为RGB,因此需要转换
import cv2
import numpy as np
from matplotlib import pyplot as plt
import torch.nn
img = cv2.imread('./deep_sort_yolov3_pytorch/result.png')
B, G, R = cv2.split(img)
#BGR转RGB,方法1
img_rgb1 = cv2.merge([R, G, B])
#BGR转RGB,方法2
img_rgb2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#BGR转RGB,方法3
img_rgb3 = img[:,:,::-1]
pass
cv2.imwrite('1.png',img, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])
cv2.imwrite('2.png',img_rgb1,[int(cv2.IMWRITE_PNG_COMPRESSION),3])
深度学习中模型的量化
https://www.cnblogs.com/talkaudiodev/p/14219321.html
- 通过q格式量化成8bit位
- 将全连接层与bn层合并
CBAM(Convolutional Block Attention Module):卷积注意力机制模块
比起Senet只注意通道的注意力机制更好,关注了空间和通道两个层面:
可见当特征经过通道注意力模块后,将输出结果与原输入feature进行点乘,同理经过空间注意力机制后再进行点乘,强调数值更大的值,这就是注意力机制的原理。
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes)
self.ca = ChannelAttention(planes)
self.sa = SpatialAttention()
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.ca(out) * out # 广播机制
out = self.sa(out) * out # 广播机制
if self.downsample is not None:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
Channel Attention Module:
实现:
class ChannelAttention(nn.Module):
def __init__(self, in_planes, rotio=16):
super(ChannelAttention, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
self.sharedMLP = nn.Sequential(
nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False), nn.ReLU(),
nn.Conv2d(in_planes // rotio, in_planes, 1, bias=False))
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avgout = self.sharedMLP(self.avg_pool(x))
maxout = self.sharedMLP(self.max_pool(x))
return self.sigmoid(avgout + maxout)
为了实现共享shared MLP,这里使用了ConV2d,in_planes代表输入通道数
Spatial Attention Module:
class SpatialAttention(nn.Module):
def __init__(self, kernel_size=7):
super(SpatialAttention, self).__init__()
assert kernel_size in (3,7), "kernel size must be 3 or 7"
padding = 3 if kernel_size == 7 else 1
self.conv = nn.Conv2d(2,1,kernel_size, padding=padding, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
avgout = torch.mean(x, dim=1, keepdim=True)
maxout, _ = torch.max(x, dim=1, keepdim=True)
x = torch.cat([avgout, maxout], dim=1)
x = self.conv(x)
return self.sigmoid(x)
SEnet中,作者在resnet中放入的位置:(是不是cbam也同理?)
计划补充:
- [ ] SLAM
- [ ] DPM
- [ ] BEV Net + Transformer
- [ ] stylegan
- [ ] REtainNET
- [ ] XGB
- [ ] 红黑树拆分
- [ ] model ensemble
- [X] ~nn.Conv2d和nn.ConvTranspose2d~
- [ ] AI的落地
- [ ] 异步计算
- [ ] BN的mom和eps怎么确定?
- [ ] 集成学习:bagging,boosting和stacking