X86和ARM架构分别应用于PC端和低功耗嵌入式设备,X86指令集很复杂,一条很长的指令就可以完成很多功能;而ARM指令集很精简,需要几条精简的短指令完成很多功能。
图像的膨胀和腐蚀是两种基本的形态学运算,主要用来寻找图像中的极大区域和极小区域。
图像边缘是图像最基本的特征,指图像局部特征的不连续性。图像边缘有方向和幅度两种属性。边缘通常可以通过一阶导数和二阶导数检测得到。、
一阶导数:以最大值作为对应的边缘位置,常用的算子:roberts算子,sobel算子和prewitt算子。
二阶导数:以过零点作为对应边缘的位置,常用的算子:laplacian算子,此类算子对噪声敏感。
区别:一阶微分算子获得的边界是比较粗略的边界,反映的边界信息较少(边界很粗),但是所反映的边界比较清晰;二阶微分算子获得的边界是比较细致的边界。反映的边界信息包括了许多的细节信息,但是所反映的边界不是太清晰。
其他边缘算子:前面均是通过微分算子来检测图像边缘,还有一种是canny算子,在满足一定约束条件下推导出来的边缘检测最优化算子。
高频:图像中灰度变化剧烈的点,一般是图像轮廓或者噪声,常用算子:canny,sobel算子,laplacian算子等边缘算子
低频:图像中平坦的,变化不大的点,也就是图像中大部分区域,常用算子:均值滤波,高斯滤波,中值滤波
均值滤波的缺陷:不能很好地保护图像细节,在图像去噪的同时也破坏了图像的细节部分,从而使图像变模糊,不能很好去除噪音点
高斯滤波的原理和操作步骤:高斯滤波是整幅图像进行加权平均的过程,每一个像素点的值都由本身和领域其他像素点经过加权平均后得到。具体操作是用一个高斯核扫描图像中的每一个像素,用模板确定的邻域内像素的加权灰度值去替代模板中心像素点的值。
中值滤波的原理和使用中值滤波的场景:用像素点邻域灰度值的中值来代替该像素点的灰度值,中值滤波在去除脉冲噪声、椒盐噪声的同时又能保留图像的边缘细节。
RGB、RGBA、HSV、HLS、Lab、YCbCr、YUV等
GIL是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍使先后顺序的,并不是同时进行的。GIL存在引起的最直接问题是:在一个解释器进程中通过多线程的方式无法利用多核处理器来实现真正的并行。
多进程中因为每个进程都能被系统分配资源,相当于每个进程都有python解释器,所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大。
python是解释型语言,意味着解释一行,运行一行,它并不清楚代码全局;因此每个线程在调用cpython解释器运行之前,需要先抢到GIL锁,然后才能运行;编译型语言就不存在GIL锁,编译型语言会直接编译所有代码。
ls pwd cd touch rm mkdir tree cp mv cat more grep echo
1.使用trick, 节省显存
2.从反传角度考虑
在训练的时候,CNN主要开销来自于存储用于计算backward的activation,对于一个长度为N的CNN,需要O(N)的内存。在《Training Deep Nets with Sublinear Memory Cost》,每隔sqrt(N)个node存一个activation,需要的时候再算,这样显存就从O(N)降到O(sqrt(N)),pytorch中torch.utils.checkpoint就是实现这样的功能。
3.使用梯度积累
由于显存受限bs只能设置为4,想要通过梯度累积实现bs等于16,这需要4次迭代,每次迭代的loss除以4。
常常将预测出来的结果分成四个部分:TP、FP、TN、FN。
大块白色斜线标记的是TN(预测中真实的背景部分),红色部分标记的是:TN(预测中被预测为背景,但实际上不是背景的部分),蓝色斜线部分是FP(预测分割为某标签的部分,但是其实并不该标签所属的部分),中间黄色块是TP(预测的某标签部分,符合真值)
I O U = target ⋀ prediction t a r g e t ⋃ p r e d i c t i o n IOU = \frac{\text { target } \bigwedge \text { prediction }}{target \bigcup prediction} IOU=target⋃prediction target ⋀ prediction
def compute_ious(pred, label, classes):
'''computes iou for one ground truth mask and predicted mask'''
ious = [] # 记录每一类的iou
for c in classes:
label_c = (label == c) # label_c为true/false矩阵
pred_c = (pred == c)
intersection = np.logical_and(pred_c, label_c).sum()
union = np.logical_or(pred_c, label_c).sum()
if union == 0:
ious.append(float('nan'))
else
ious.append(intersection / union)
return np.nanmean(ious) #返回当前图片里所有类的mean iou
思路:在训练时假设标签可能存在错误,避免过分相信训练样本标签。在训练样本上,我们并不能保证所有样本标签都标注正确,如果某个样本的标注是错误的,那么在训练时,该样本就有可能对训练结果产生负面影响。很自然的想法是我们可以告诉模型,标签不一定正确,那么训练出的模型对于少量样本错误就会有“免疫力”,从而拥有更好地泛化性能。
sigmoid得到的结果是“分到正确类别的概率和未分到正确类别的概率”,softmax得到的是“分到正确类别的概率和分到错误类别的概率”
sigmoid:对于非互斥的多标签分类任务,且我们需要输出多个类别。如一张图我们需要输出是否是男人,是否戴了眼镜,我们可以采用sigmoid来输出最后的结果。例如:[0.01, 0.02, 0.41, 0.62, 0.3, 0.18, 0.5, 0.42, 0.06, 0.81],我们通过设置一个概率阈值,比如0.3,如果概率值大于0.3,则判定类别符合,那么该输入样本则会被判定为类别3、4、5、7和8,即一个样本具有多个标签。
softmax为什么要引入指数形式?
如果使用max函数,虽然能完美的进行分类但函数不可微从而无法进行训练,引入以e为底的指数并加权归一化,一方面指数函数使得结果将分类概率拉开了距离,另一方面函数可微。
softmax:当我们的任务是一个互斥的多类别分类任务,网络只能输出一个正确答案,我们可以用softmax函数处理各个原始的输出值
y i = Softmax ( x i ) = e x i ∑ j = 1 n e x j y_{i}=\operatorname{Softmax}(x_{i})=\frac{e^{x_{i}}}{\sum_{j=1}^{n} e^{x_{j}}} yi=Softmax(xi)=∑j=1nexjexi
import numpy as np
def softmax( f ):
# 为了防止数值溢出,我们将数值进行下处理
# f: 输入值
f -= np.max(f) # f becomes [-666, -333, 0]
return np.exp(f) / np.sum(np.exp(f))
pixel accuracy(标记正确 / 总像素数目),优点:简单。缺点是:图像中大面积是背景,而目标较少,即使整个图片预测为背景也会有很高的PA得分。
MPA(mean pixel accuracy):计算每类各自分类的准确率,再取均值。
MIou:计算两个集合的交集与并集之比,在语义分割中,这两个集合为真实值和预测值。
Mish = x ∗ tanh ( ln ( 1 + e x ) ) \text { Mish }=x * \tanh \left(\ln \left(1+e^{x}\right)\right) Mish =x∗tanh(ln(1+ex))
mish函数:理论上对负值的轻微允许更好的梯度流,而不是像ReLU中那样硬边界。其中最重要的是平滑型激活函数允许更好的信息深入神经网络,从而得到更好的准确性和泛化。pytorch版本的mish比较占显存。
f ( x ) = x ⋅ sigmoid ( β x ) f(x)=x \cdot \operatorname{sigmoid}(\beta x) f(x)=x⋅sigmoid(βx)
understanding the effective receptive field in deep convolutional neural network 一文中提出了有效感受野的理论,论文发现并不是感受野内所有像素对输出向量的贡献相同,在很多情况下感受野区域内像素的影响分布是高斯分布,有效感受野仅占理论感受野的一部分,且高斯分布从中心到边缘快速衰减,下图第二个是训练后CNN的典型有效感受野。
我们看到绿色这个区域,黄色为图像,绿色框扫过时,对于第一例是只扫过一次,也就是参与一次运算,而后面靠近中心区域的列则均是参与了多次计算。因此,最终实际的感受野,是呈现一种高斯分布。
感受野的范围越大表示其接触到的原始图像范围越大,也就意味着它能学习更为全局,语义层次更高的特征,相反,范围越小则表示其所包含的特征越局部和细节
如果将权重初始化全部为0,等价于一个线性模型,将所有权重设为0,对于每个w而言,损失函数的导数都是相同的,因此在随后的迭代过程中所有权重都具有相同的值,这样会使隐藏单元变得对称,并继续运行设置的n次迭代中,会导致网络中同一个神经元的不同权重都是一样的。
如果X很大,w相对较大,会导致Z非常大,这样激活函数是sigmoid,就会导致sigmoid的输出值1或者0,到知道train不动,同时如果初始化均值为0,方差为1的高斯分布(训练过程参数分布会向两侧跑),当神经网络层数增多时,会发现越往后的层激活函数的输出值几乎为0,极易出现梯度消失。
上面两种方法初始化权重极易出现梯度消失的问题,而xavier初始化可以解决上面的问题,其思想就是尽可能地让输入和输出服从相同地分布,这样就能避免后面层地激活函数地输出值趋向于0, xavier权重初始化比较适用于tanh和sigmoid激活函数,而对relu这种非对称性地激活函数还是容易出现梯度消失。权值采样(0, 1/N)的高斯分布。
主要解决relu层将负数映射到0,影响整体方差,kaiming初始化解决这个问题。权值采样为(0,2/N)的高斯分布。
优化算法采用的原理是梯度下降法,即最小化目标函数,最优化的求解过程,首先求解目标函数的梯度,然后将参数向负梯度方向更新,学习率表面梯度更新的步伐大小,最优化的过程依赖的算法称为优化器,深度学习优化器的两个核心是梯度与学习率,前者决定参数更新的方向,后者决定参数的更新程度。
v1——GoogLeNet: inception结构的主要贡献有两个:一是使用1 x 1的卷积来升降维;二是在多个尺度上同时进行卷积再聚合。
**v2和v3——BN-Inception:**v2在v1的基础上加入BN层,v3提出了四种inception模块,并引入辅助分类器,加速深度网络收敛,此外在网络中使用RMSProp优化器和标签平滑,并在辅助分类器中使用BN,进一步加速网络收敛。
主要核心思想:
**v4:**研究了Inception模块与残差连接的结合。ResNet结构大大加深了网络深度,还极大的提升了训练速度,同时性能也有提升。v4主要利用残差连接来改进v3结构。
**Xception:**谷歌在2017年在v3的基础上提出的,主要有以下亮点:1. 作者从v3的假设出发,解耦通道相关性和空间相关性,进行简化网络,推导出深度可分离。2. 提出了一个新的xception 网络。 xception提出的深度可分离卷积和 mobilenet中是有差异的。(这里先1x1 pw,然后 3x3 dw, 效果差不多)
v1: 主要亮点有两个,第一个 采用depthwise convolution和 pointwise convolution 来大大减少计算量和参数量 , 第二个增加超参数α和β来控制网络模型。
v2:针对V1中出现的DW部分卷积核容易失效,即卷积核参数大部分为零的问题,google团队在 2018年提出v2,相比于v1,准确率更高,模型更小。主要有以下两个亮点:使用inverted residual(倒残差结构 1x1升维 —> dw ---->pw, dw在高维空间效果可能更佳),使用了linear bottleneck(解决:卷积核参数大部分为0,主要是由于relu造成的。低维特征图做relu6运算,很容易造成信息的丢失,就将inverted 中最后一个relu6换成 线性激活函数)。
**v3:**在v2网络的基础上,主要有四个亮点
s w i s h ( x ) = x ⋅ s i g m o i d ( β x ) swish(x)=x·sigmoid(\beta x) swish(x)=x⋅sigmoid(βx)
h − s w i s h ( x ) = x ⋅ R e L U 6 ( x + 3 ) 6 h-swish(x)=x·\frac{ReLU6(x+3)}{6} h−swish(x)=x⋅6ReLU6(x+3)
**v1:**旷视于2017年提出轻量级网络,主要有两个亮点:1. 提出pointwise group convolution 来降低PW卷积的计算复杂度, 2. 提出channel shuffle来改善跨特征通道的信息流动。
group convolution 的缺点:由于组和组之间没有交互,导致组与组之间信息流动阻塞,模型表达能力的弱化。这也是shuffleNet提出 channel shuffle的原因。 channel shuffle实现:假定输入层分为g组, 总通道数为g*n, 首先将通道那个维度拆分为(g,n)两个维度,然后将这个两个维度转置变成(n,g),重新reshape成一个维度g x n,仅需要简单的维度操作和转置就可以实现均匀的shuffle。
第一步进行reshape:
第二步对得到的矩阵进行矩阵转置操作:
最后一步再进行reshape操作:
def shuffle_channels(x, groups):
"""shuffle channels of a 4-D Tensor"""
batch_size, channels, height, width = x.size()
assert channels % groups == 0
channels_per_group = channels // groups
# split into groups
x = x.view(batch_size, groups, channels_per_group,
height, width)
# transpose 1, 2 axis
x = x.transpose(1, 2).contiguous()
# reshape into orignal
x = x.view(batch_size, channels, height, width)
return x
**v2:**同等复杂度下,比v1和mobilenet v2 更准确,主要有以下两个亮点:1. 提出四个高效网络设计指南 2. 针对v1的两种unit 做了升级。