参考,
An Intuitive Explanation of Convolutional Neural Networks
http://www.hackcv.com/index.php/archives/104/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
CNN基础
CNN网络主要用于compute vision
对于图片输入而言,是一种极高维度的数据,比如分辨率1000*1000*3的图,可能会产生3 billion的参数,这不太可行
所以我要使用convolutional NN来解决这种问题,
首先看看什么是卷积操作,
如下图,
原图片6*6
中间3*3的矩阵,称为filter或卷积核kernel,它表示你需要匹配的模式,这里detect edge,竖线
最右边4*4的矩阵,就是进行卷积操作后的结果
为何这个filter可以找出竖线,直观理解,filter找出左右存在色彩反差的区域或者找出由明转向暗的区域,如下图
实际中还有很多其他的filter,
但是其实在deep learning中,你不需要去手工设计或者选择filter,因为他们是学习的参数,可以通过模型学习到
可以看出用卷积可以大大减少需要学习的参数,
为什么卷积网络,可以用这么少的参数?
首先是参数共享
通过一个filter可以找出图片上任何位置的特征,如竖线
这样的好处是无论输入图片多大,参数size都是固定的,由filter本身决定
第二,稀疏链接
如果是全连接层,每个输出的每个维度都和输入的每个维度相关,所以需要很多的参数
而在卷积层,一个输出的某个维度如下图,是由输入中的9个feature算出的,而其他特征无关
这样需要的参数大大减少,同时也降低了过拟合
Padding
上面的卷积操作有两个问题,
每次卷积操作都会缩小图片的尺寸
会丢失边缘区域的特征
所以为了解决这些问题,padding就是把图片扩大一圈,用0填充
这样就有两种convolution操作,
valid就是no padding,结果大小的公式,n-f+1
same就是padding让输出结果大小不变,根据公式算,p=2的时候,就可以保证不变
这里描述filter一般都是odd,而不会是偶数,直观上讲,如果f是偶数,那么没法对称的padding,而且f是奇数是filter是有中心点的,这个很方便
所以filter一般都是3*3,5*5,7*7
strided convolution
对于convolution,除了调整filter size,padding,还可以调整stride的大小
可以每次滑动超过1步,那公式会变成如下,
cross-correlation VS convolution
在通信或数据中,convolution操作要先把filter,同时在横轴和竖轴上进行翻转,如图,
这样做的好处,是可以让convolution操作满足结合律
但是在DL中,我们其实没有做这种翻转,这种操作其实应该叫做cross-correlation
但是大家都习惯和默认称为convolution
convolutions on volumes
真实的图片有RBG三层,所以是立体的,这样如果进行convolution
输入数据的层数,称为depth或channel
当输入数据有3个channel,那么filter对应也需要有3个channel
如图,上面这个3*3*3的filter可以detect 红色的竖线
下面这个,可以detect 任意颜色的竖线
在实际使用中,需要同时detect多个feature,所以有多个filter,如下图
输出数据的channel数,取决于filter的数目
one layer of a convolutional network
那把卷积操作放到网络中的一层结构如下,
卷积再加上b,就完成了线性变换的部分,后面跟上非线性变换,就完成了一个神经单元
上图中的各个参数的表示如下,
给出一个完整的CNN的例子,
每层的结构,根据上面的公式很容易算出,卷积层只是做特征的提取和变换,最终输出前,还是需要fully connected,然后用logistics或softmax输出结果
CNN网络一般有三种layer组成,convolution层,pooling层,fully connected层
pooling layers
池化层,利用采样高效的降低维度和减少特征值
一般有最大和平均池化,这层是没有参数的
neural network example
卷积神经网络的结构比较复杂,而且超参数非常多,所以常用的方法是参考经典的网络结构
先看个例子,用于手写体数字识别,
如下图,总结下这个网络的结构,
能学到的规律,
首先,池化层没有参数,而卷积层的参数并不多,参数主要集中在全连接层
然后,CNN的经典结构就是,cov-pool-cov-pool-fc-fc-fc-softmax,几层cov和pool组合,加上几组fc层
再者,activation size是慢慢减少的,如果降低的太快会影响模型的性能
Case Studies
参考,https://zhuanlan.zhihu.com/p/22094600
比较直观看出各个网络的年代和性能差距
LeNet-5
该网络由作者名字LeCun命名,5代表五层模型
该网络,用于手写数字识别,用于灰度图片,所以图片channel为1
网络整体有60k的参数,比较小
用于当时没有padding技术,所以随着网络depth增加,size是不断变小,但是channel是不断变大的
网络结构被后续沿用,若干cov+pool + 若干fc + output
从原始图片1024个像素到最后一层FC的维度84,所以cnn关键就是抽象和提取特征
AlexNet
这个网络也是用作者名字命名的
AlexNet和LeNet其实比较像,就是规模大了许多,参数从60k到60m
只所以可以训练这么大的网络,是因为AlexNet在工程实践上利用的GPU
然后AlexNet,采用了Relu和dropout,最终把compute vision带到一个新世界
VGG
VGG网络是比AlexNet规模更大,更深的网络模型,参数达到138M之多
16表示有参数的layer有16层
VGG的特点是结构规整,工业化的思路,我们不去精巧的设计,依赖网络的规模和数据规模来解决问题
首先他用的Conv和Pool模块是固定的,
Conv是3*3,s=1,same
Pool是2*2,s=1
然后为了便于表示,在图中省略了Conv的参数,Conv 64 *2,表示2层卷积层,每层有64个filter
再者,
由于这里用same conv,所以conv是不会改变图片size的
全靠pooling层改变图片size,并且也很有规律
多个Same Conv后,会接一个Pool,这样picture的size减半,并且每个Pool后,加上的Conv层的channel都会double
所以还是符合size变小,channel变大的规律,只是更规整
Residual Networks(ResNets)
残差网络的本质是要解决网络太深后难以训练的问题,由于梯度消失或梯度爆炸
可以看到之前的网络也就十几层,而残差网络都是上百层,甚至上千层
所以残差网络效果好,不是有什么秘诀,因为更深的网络,更好的性能,这是理所当然的
只是之前的网络模型,在实践中无法训练到那么深
残差网络,是由residual block组成的,
residual block至少两层layer组成,
除了普通的main path,不同就是多了shotcut或skip connection
即,会把第一层的输入a1,叠加到第二层的激活函数前
所以第二层的输出,就从a2 = g(z2),变成a2 = g(z2 + a1)
我们把很多的residual block 堆叠(stack)在一起,就形成residual network,如下图,
如图,把plain network变成ResNet,只需要给每两层加上shortcut,这样就可以解决plain network当layer数过大性能明显下降的问题
为什么残差网络可以帮助网络增加深度?
NG的观点是,因为residual block对网络是无副作用的
如下图,由于residual block的输出是a2 = g(z2 + a1)
那么只要让z2 = w2a1 + b2的参数,w2,b2趋于0(类似正则化),那么a2=g(a1),如果g是relu,那么g(a1)=a1,所以得到a2 = a1
而让参数为0这是很容易学习的
所以说residual block最差的情况就是,原封不动的传递输入,这样当然无论迭代多深都没有关系
但是,如果不是最差情况,能学到些东西,对网络就可以产生正向的帮助
这里residual block有个假设,就是z2 + a2,那么两者的size需要一样
如果不一样了?那这里需要增加一个参数,Ws用于把a2的size转成和z2一样
下图是个实际的例子,如何将一个plain的cnn,转化为一个ResNet网络
注意虚线的链接,表示经过pool,size变化后,需要进行size转换
Inception网络
inception各个版本对比
https://blog.csdn.net/xbinworld/article/details/61674836
inception的命名,是因为在盗梦空间,inception,中有一句台词,“we need to go to deeper”
所以可以看出,inception network或GoogleNet的主要目的,也是让网络更深
首先看下1*1 convolution,
1*1 convolution,其实就是单像素点,在各个channels上的线性组合
它的作用,
主要就是低成本的调节channel数,或depth;在不需要调整长宽的情况下,用3*3,5*5,比较耗费性能
再者,因为有relu输出,附带一层非线性效果
下面就看看啥是inception网络,
普通的convolution网络,你要选择用什么样的filter或是用pool层,选择困难怎么办?
Inception的思路就是,都用上,然后把结果stack在一起,让模型去决定
注意这里要保证输出的size一致,所以这里的pool是same pool,不会压缩尺寸,这是很特殊的pool用法
这个思路最大的问题就是计算量太大,那么这时我们的1*1 convolution就用上了
比如上面的例子,直接对输入做5*5 convolution,那么计算量120 million
用上1*1 convolution,先把depth降下来,再算5*5 convolution,这样计算量只有12 million左右,小十倍
可以看到,两个方法,输入和输出都是一样的,但用上1*1 convolution降低channel后,计算量大大降低
中间这层称为,bottleneck layer,使用恰当的情况下,降维优化并不会损失算法的performance
我个人的理解,由于1*1 convolution是各个channel的线性组合,加上适当数量的filter,并不会丢失太多的信息
Inception module
现在来看inception module就比较清楚了,
如图就是一个inception module,其中1*1 convolution都是用来调节depth的
比如MaxPool后面的,就把depth调节到32,避免pool的结果在最终stack中占太大的空间
而inception网络,就是inception module的堆叠,中间还会加上些红色的pool层来调节size
Object Detection
classification with localization
这个和普通图片分类的区别,就是除了给出类别,还要给出在图中的位置信息
看这个例子,除了要给出图片是哪一类,1,2,3,4,car是2
还要给出car的位置,bx,by,bh,bw
这里假设图片的坐标是,从(0,0)到(1,1)
这样就有5个输出,其他的和原来一样,还是交给模型去训练
具体实现如下,
这里的输出增加一个Pc,表示是否有object,0或1
bx到bw是代表object的位置
c1,c2,c3表示具体的类别
这里给出两个例子,一个是车的,一个是单纯背景无object的
注意,
loss function中,如果Pc=0,表示没有object,那么后面的值就没有意义,所以在算loss function的时候就不需要考虑,只需要算第一项的平方误差
上面是比较简单的位置的例子,还可以检测面部特征或体态特征这样的case,思路都是一样的,关键是定义清楚位置特征作为输出
当然关键是,你需要有相当的训练集
sliding windows object detection
如果一个图里面有多个对象怎么办,这个就是object detection
传统的方法就是,sliding windows object detection,很naive的方法
拿一个框去遍历裁剪图片,对每个裁剪下来的小图,做分类,是否有object,是什么object
这个方法的问题很明显,你如何定义裁剪尺寸,太大就没有意义,太小计算量就会非常大
传统的方法是用线性回归来做分类,性能还能接受,但是用cnn性能就不行了
上面的图,显示如何通过cnn进行分类,14*14*3是裁剪的小图片,通过conv层,pool层,然后FC层,最后softmax
为了优化,我们可以把整个分类的过程,都转化成conv层
比如,第一层FC,5*5*16,全连接到400的节点
我们也可以用400个5*5*16的filter,进行卷积,最终得到1*1*400,同样的效果
这样第二层FC,我们用400个1*1*16的filter来代替
最后softmax,用4个1*1*16的filter来代替
这称为,convolutional implementation
这样的好处,我们可以把遍历裁剪图片分类这个过程,通过一遍cnn计算搞定
比如,输入图片16*16*3,裁剪大小是14*14*3,如果遍历输入图片会产生4个小图片
这里我们不用对每个小图片分别采用conv计算,如果这样做会产生大量的重复计算
而只要整体的做一遍conv计算,如下图,最终得到2*2*4的输出,即一下输出4个裁剪小图片的结果,效率大大提高
但这个方法,我们仍然需要指定裁剪size,
但这样只能判断object是否在小图片中,无法给出更精确的位置信息;并且很有可能object无法完整的被任何裁剪小图包含
Yolo(you only look once)
Yolo的思路,既然上面单纯的分类无法找到精确边界,那我们加上classification and localization的部分
对每个图片同时给出分类和边界数据,
既然我们可以得到精确的边界,所以就没有必要用sliding window,直接用grid把图片切分就可以
对于下面的例子,我们把图片用3*3的grid划分,最终就得到3*3*8的结果
训练这个模型,我们也要按照这个格式给出训练集,比如上图中,给出绿色框和黄色框的数据作为实例
由于这里也用convolutional implementation的方式,使得计算非常高效,
所以训练集中,x表示输入图片,100*100*3,输出就是3*3*8
ok,这里明显的问题是
如果一个grid里面两个objects,怎么办?这个通过用小grid来降低概率,比如用19*19
反之,如果一个object比较大,占好几个grid怎么办?
我们只看,object的中心点,bx,by落在哪个grid,那么就认为object属于这个grid,即Pc为1,其他的grid仍然认为没有object
所以bx,by一定落在grid内,所以bx,by都是小于1的(grid的边界从0,0到1,1)
但是由于object是可能占多个格子的,所以bh,bw可能是大于1的
Intersection over union (IOU)
如何判断算出的bounding是否精确?
很简单,交并比
下图,红框是精确的bounding box,而紫色的算出的bounding box,想判断紫色的bounding box是否足够精确
Non-max suppression
用yolo算法的时候,
在准备训练集的时候,我们可以把一个车根据中心点放到一个grid中
但是在predict的时候,会有不止一个grid认为车在自己的grid中如图,并用Pc来表示概率
那么直接的想法,既然Pc表示概率,我们就选概率最大的,其他的都discard掉,就好了
如果图片中只有一个object,这个方法是可以的
但是如果有多辆车,我怎么知道哪些grid的bounding box是描述的同一辆车?
这里就要用到交并比,如果两个bounding box的IOU大于0.5,我们就认为是同一辆车
所以得到下面的方法,
首先discard掉所以Pc较小的grid
然后找到最大的Pc,然后删掉所有和它的IOU大于0.5的grid,这样一个object就保留了一个Pc最大的grid
如果还有grid剩下,说明有其他的object,再重复上面的过程一一找出
Anchor boxes
前面说了当用很小grid的时候,一个grid中有多个object的概率是很小的
但如果要在一个grid中detect两个object应该怎么做?
直接的想法是,我用8位的输出来detect一个object
那如果要detect两个,就用16位输出,分别表示两个object
比如下图,图片中人和车都在一个grid中,所以我们就用前8位表示人,后8位表示车
这样讲人可以理解,机器不行
所以我们要用机器可以理解的概念来定义人和车,
这里就是用anchor box,anchor box可以人手工设计,也可以用k-means去统计,比如人的平均边界,车的的平均边界
如下图,我们就用anchor box1代表人,anchor box2代表车
那么一个object到底是算哪个box?还是用交并比,和谁的交并比大就算谁
这个方法用起来比较麻烦,detect两个object就用16位输出,如果要detect多个object就要更多位的输出
而且还要指定和设计anchor box
然后如果grid中出现多于假设个数的object,就没办法处理
或者两个object的anchor box类似,也很难处理
Region proposals
除了,Yolo,还有另外一个思路来加速sliding windows object detection
region-cnn,思路就是对于sliding windows object detection,我们要遍历所有的裁剪图片去做分类,但是某些裁剪图片明显是没有任何东西的
所以,region-cnn会对图片做分块,segmentation,如最右图,这样只需要对不同的色块做分类即可
那么执行分类的candidate变少,性能就提高了
Face Recognition
Recognition可以分解成verification的问题,如果解决了verification的问题,那么Recognition只是遍历的问题
这里要注意的是,如果要在Recognition达到一定精度,verification的精度要提高几个量级
因为如果verification的精度99%,但库里面有100张图片,所以每次误差的叠加就会很高
One-shot training
在很多场景下,你不会有很多关于某个人的图片,可能只有一张图片作为训练集,当这个人再次出现时,你要能够认出他
这个用传统的cnn就无法解决,首先训练集太少,无法得到有效的网络
再者如果要识别的对象增加,比如新员工入职,你需要从新训练网络
所以采用的方法是,
Siamese network
具体的做法,就是通过cnn对图片进行encoding,比如下图,一张图片会编码成128位的向量(往往成为embedding)
那么训练目标,就是相同人的图片得到的embedding间的距离比较小,反之
Triplet loss
Triplet,顾名思义,一个训练集中有3个图片,anchor作为baseline,一个positive,和一个negative
很明显,
我们的目的就是,让d(A,P)小于d(A,N),d是距离
光小于不够,要远小于,所以加上margin超参数alpha
形式化,
A和P的选择很简单,
对于N的选择,我们应该尽量选择和A相近的N,这样算法效率会更高,如果我们随机选择N,会让上面的约束很容易达到,会大大降低训练效率
除了triplet loss,也有其他的方法训练,siamese network ,比如下面的方法,把两个图片的embedding,作为logistics回归的输入,变成一个分类问题去训练
Neural sytle transfer
sytle transfer要做的如下图,
给出一张content图片C,一张style图片S,要的效果就是生成G
所以优化目标,J(G),由两部分组成,
J(C,G),表示G和C在content上的相似度
J(S,G),表示G和S在style上的相似度
下面就分别来定义这两个目标函数,
content cost function
判断内容是否相似比较直观,和前面脸部识别一样,我们只要把图片encoding成embedding,然后比较相似度就好
实际做,
找一个pre-trained的ConNet,选一层l的输出作为embedding,通常l不会太浅也不会太深,这样可以比较好的代表图片的内容
剩下的就是计算两个embedding的相似度
style cost function
关键是要找出,style是什么?
这里给出的定义是,对于某个layer,style是各个channel的相关系数
cnn中,某一层的各个channel是由不同的filter生成,filter可以理解代表某一种特征
直观理解,如果两个channel相关性高,说明其filters所代表的特征常常会同时出现,比如下图,中竖线和橘色两种特征
形式化定义,我们用style matrix,G[l],来表示图片在第l层的风格,
G用来表示每个channel之间的关系,所以nc*nc大小的
k层channel和k‘层channel的相关系数,就是两层对应的每个位置数据的乘积和,也称为gram矩阵
直观上简化一下,如果数据只有0,1,如果两层不相干,乘积和会为0,只有相关才会同时出现1,这样相关系数才会大
那么上面就给出在l层上,S和G的style的cost function,其实就是计算S和G的style matrix的差异
最终J(S,G)会考虑所有layer上的风格矩阵的差异