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))

提升网络准确度

  1. SE网络用于大model中,可以加强通道之间的特征关系提取
  2. 把LeRU换成H-Swish

batch大小选择

一般来说往大的选

过大时,因为模型的下降方向基本确定,过大或许造成收敛变慢,过小会因为震荡过大难以收敛

因此,当设备限制导致不能选取过大的batch时,应该将学习率降低

resize_size = crop_size * 0.875。

分类与回归区别

回归:线性回归输出一个标量的函数公式wx+b,它可以根据输入的值给出预测值

分类:分类体现在逻辑回归中,具体做法是将以上的线性回归的预测值经过例如sigmoid的函数映射到(0,1)之间,然后对这个概率值进行分类判断,输出一个类

分类网络

  1. Alexnet

开创了深度的卷积神经网络能自己捕获图片的特征

运用了DP和Relu,LRN对局部的特征进行归一化(不是BN)

  1. VGG-16

全局用了标准的3x3卷积核,用小卷积替代了大卷积,更省资源

3.Inception(googlenet) 系列

相比以前的卷积神经网络,进行了宽度的延申

用1x1,3x3,5x5的卷积核使得感受野增大,并且通过设定stride=1,只调整pad=0、1、2使得输出特征方班对齐

(xception)

  1. ResNet 解决了 deep NN 的两大问题:
  2. deep NN 的梯度弥散和爆炸问题;
  3. deep NN 的精度随着模型的加深,会逐渐饱和不再上升,甚至会大幅度下降。

激活函数的作用,如果没有激活函数会怎样?

激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。

激活函数的作用:激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。

这个变换要满足什么条件?

  1. 非线性,这样增加网络的深度才有意义
  2. 可导的,不然怎么做梯度下降
  3. 易于计算的
  4. 输出空间最好是有限的,这条好像也不是必须的,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:

https://blog.csdn.net/pengchengliu/article/details/113878358?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link


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

  1. 通过q格式量化成8bit位
  2. 将全连接层与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
如果对你有帮助就太好了)))