本文是读深度学习的开山之作《ImageNet Classification with Deep Convolutional Neural Networks》所作的笔记。

论文笔记

1.解决了什么问题

大规模图像分类的问题。

2.使用的方法

搭建深度卷积神经网络进行图像分类,被称为AlexNet
在GPU上实现了高度优化的卷积神经网络的训练过程
使用了ReLU激活函数,局部响应归一化,重叠池化等trick。
使用了数据增强、Dropout防止过拟合。

3.实验结果

该模型让深度学习成为最主流的机器学习算法
在ImageNet LSVRC-2010数据集上达到state of the art,模型的top5错误率为17.0%
在ILSVRC-2012比赛中获得冠军,测试集top5错误率降低到15.3%

4.待解决的问题

由于当时计算资源的缺乏,该网络不够深,不够宽,因此准确率仍有很大的提升空间。

论文细节

AlexNet:这个神经网络拥有6000w个参数和65w的神经元,由5层卷积池化层组成,后面接3层全连接层,输出层有1000个神经元。

模型架构:

如上图所示,模型由5个卷积层和3个全连接层组成,其中最后一个全连接层是softmax层,在ImageNet-1k数据集上训练,故输出1000个分类,使用分类交叉熵作为损失函数。

可以看到模型分为两部分,因为该模型在两个GPU上训练,其中按照图像数据的通道切分为两部分,两部分在特定的层通信。卷积层的第二、第四、第五层直接和同一个GPU的前一层相连,第三层则与两个GPU上的第二层相连。全连接层都和上一层的所有神经元相连。

卷积层的第一层使用96个卷积核,size=(11,11),stride=4。第二卷积层使用256个卷积核,size=(5,5)。第三卷积层使用3x3的384个卷积核,第四卷积层使用3x3的384个卷积核,第五卷积层使用3x3的256个卷积核。全连接有两层有4096个神经元。

局部响应归一化应用在第一和第二卷积层之后,所有层的激活函数是ReLU,Dropout应用在全连接层。

在模型中用到的技术:

1.ReLU非线性激活函数

这篇论文之前激活函数一般选择饱和非线性函数f(x)=tanh(x)或者sigmoid函数:f(x)=(1+e^(-x))^(-1),与之相比,非饱和非线性的ReLU f(x)=max(0,x)使用梯度下降训练的速度要快几倍。

2.在多GPUs上面训练

Alex模型在两个GPU上并行训练,加快了训练速度,两部分只在特定的层通信。

3.局部响应归一化(LRN,Local Response Normalization)

因为使用了ReLU,则不需要对输入进行标准化防止过拟合。对ReLU输出后的数据进行LRN的公式如下:

ai是像素坐标为(x,y),第i通道的输出值,bi是将a归一化后的值,N是通道的总数,k、alpha、beta、n都是参数。公式叠加通道方向的值,n表示叠加的范围,示意图如下:

LRN的原理 :局部归一的动机:在神经生物学有-一个概念叫做侧抑制(lateral inhibitio) ,指的是被激活的神经元抑制相邻神经元。归一化(normalization)的目的是“抑制”,局部响应归一化就是借鉴侧抑制的思想来实现局部抑制,尤其当我们使用ReLU的时候这种“侧抑制”很管用。LRN的优点:增加范化能力。LRN层模仿生物神经系统的侧抑制机制,对局部神经元的活动创建竞争机制,使得响应比较大的值相对更大,提高模型范化能力。

论文使用的超参数为k=2,n=5,alpha=10^-4,beta=0.75

4.重叠池化(Overlapping Pooling)

重叠池化即池化步长<池化窗口,重叠池化可以减小过拟合。这里使用最大池化步长=2,池化窗口大小=3。

防止过拟合的方法

1.数据增强

使用两种方式进行数据增强:

第一种方法是图像平移和水平翻转和裁切。裁切过程先将图片的短边缩放至256,然后在中间裁切出256x256的图片。256x256的图片在四个角和中间裁切出5张224x224的图片,就是我们的训练数据。

第二种方法是改变图片RGB通道的亮度。在ImageNet数据集上面执行PCA,对每个训练图片,将找到的主成分的倍数相加,与特征值成比例的模量乘以一个随机变量,随机变量来自均值为0标准差为0.1的高斯分布。对于每个像素Ixy=[Ixy_R, Ixy_G, Ixy_B],公式如下:

pi是特征向量,lambdai是特征值,pi和lambdai都来源于RGB像素值的3x3的协方差矩阵的,alphai是随机变量。

2.使用Dropout

在两个全连接层使用了0.5比率的dropout,这种技术减少了神经元之间复杂的相互适应。因为神经元不能依赖于其他神经元的存在,它被迫学习与其他神经元的许多不同随机子集结合使用的更健壮的特征。

模型训练

AlexNet在ImageNet LSVRC-2010上面训练,该数据集有1000个分类,120w的数据。用GTX580 3GB GPU跑了5、6天。模型的输入为224x224大小,对于各种分辨率的图片,先将图片的短边缩放至224,再从中间进行裁剪中224x224的图片。对于图片的每个像素,减去训练集上的平均像素值。

模型使用SGD训练,batch_size=128,动量是0.9,权重衰减因子为0.0005,权重更新的公式如下:

v是动量变量。权重初始化采用高斯随机数,均值为0标准差为0.01,偏置初始化为0,学习是手动调整的,初始学习率为0.01当验证错误率不再提高时学习减小10倍。

效果:在ImageNet LSVRC-2010数据集上达到state of the art,模型的top5错误率为17.0% ,ILSVRC-2012比赛中获得冠军,测试集top5错误率降低到15.3%。ps:在ImageNet竞赛中,通常用top-1和top-5错误率表示模型的性能,top-5错误率表示正确的标签不在最可能的5个标签的比率。

代码实现

  1. 数据集和数据预处理

鉴于我的计算资源十分缺乏,因此我不打算在论文作者使用的数据集上跑一边。刚好我有一个mini的ImageNet数据集,包含100个分类总共6w个样本,勉强满足测试要求。数据集链接:https://pan.baidu.com/s/17mX1c_Xteaw7B9dr9elhng 提取码:qkmh

首先把训练图片缩放裁剪至224x224,并按分类保存到不同的文件夹便于调用Keras的数据增强API,代码如下:

import os
import cv2
import tqdmIMG_SIZE=224def ReadAndCutPicture(r_path,w_path):img = cv2.imread(r_path) #读到图片为BGRh=img.shape[0]w=img.shape[1]if(h<=w):new_h=IMG_SIZEnew_w=int(w/h*IMG_SIZE)img=cv2.resize(img,(new_w,new_h))  #输出图片尺寸为(宽,高)cut_w=int((new_w-new_h)/2)img=img[:,cut_w:cut_w+IMG_SIZE,:]else:new_w=IMG_SIZEnew_h=int(h/w*IMG_SIZE)img=cv2.resize(img,(new_w,new_h))cut_h=int((new_h-new_w)/2)img=img[cut_h:cut_h+IMG_SIZE,:,:]cv2.imwrite(w_path, img)normal_path=r'F:BaiduNetdiskDownloadmini-imagenetimages_normal' #裁剪后图片所在的位置
base_path=r'F:BaiduNetdiskDownloadmini-imagenetimages' #数据集所在位置for name in tqdm.tqdm(os.listdir(base_path)):r_path=os.path.join(base_path,name)w_dir=os.path.join(normal_path,name[:9])w_path=os.path.join(w_dir,name)if(os.path.exists(w_dir)==False):os.makedirs(w_dir)ReadAndCutPicture(r_path,w_path)

2.搭建网络

Keras官方没有给出LRN层的实现,我在github上找了一个现成的LRN实现,但是放上去就报错了,这个代码是这样的:

from keras.layers.core import Layer
from keras import backend as K
import tensorflow as tf
import numpy as np
class LRN(Layer):def __init__(self, alpha=0.0001,k=1,beta=0.75,n=5, **kwargs):self.alpha = alphaself.k = kself.beta = betaself.n = nsuper(LRN, self).__init__(**kwargs)def call(self, x, mask=None):b, ch, r, c = x.shapehalf_n = self.n // 2 # half the local region# orig keras code#input_sqr = T.sqr(x)  # square the inputinput_sqr = K.square(x) # square the input# orig keras code#extra_channels = T.alloc(0., b, ch + 2 * half_n, r,c)  # make an empty tensor with zero pads along channel dimension#input_sqr = T.set_subtensor(extra_channels[:, half_n:half_n+ch, :, :],input_sqr) # set the center to be the squared inputextra_channels = K.zeros((b, int(ch) + 2 * half_n, r, c))input_sqr = K.concatenate([extra_channels[:, :half_n, :, :],input_sqr, extra_channels[:, half_n + int(ch):, :, :]],axis = 1)scale = self.k # offset for the scalenorm_alpha = self.alpha / self.n # normalized alphafor i in range(self.n):scale += norm_alpha * input_sqr[:, i:i+int(ch), :, :]scale = scale ** self.betax = x / scalereturn xdef get_config(self):config = {"alpha": self.alpha,"k": self.k,"beta": self.beta,"n": self.n}base_config = super(LRN, self).get_config()return dict(list(base_config.items()) + list(config.items()))
class PoolHelper(Layer):def __init__(self, **kwargs):super(PoolHelper, self).__init__(**kwargs)def call(self, x, mask=None):return x[:,:,1:,1:]def get_config(self):config = {}base_config = super(PoolHelper, self).get_config()return dict(list(base_config.items()) + list(config.items()))

由于使用的数据集跟原文的并不相同,因此这里对AlexNet做了微调,网络如下:

from keras.models import *
from keras.layers import *
from keras import models
from keras import initializers
from keras.utils import plot_model
normal_init=initializers.RandomNormal(mean=0.0, stddev=0.01, seed=None)
X=Input(shape=(224,224,3))
conv1=Conv2D(96,(11,11),strides=(4,4),padding='same',activation='relu',kernel_initializer=normal_init,bias_initializer='zeros',name='conv1')(X)
maxpooling1=MaxPooling2D(pool_size=(3,3),strides=2,name='maxpooling1')(conv1)
#lrn1=LRN(alpha=0.0001,k=2,beta=0.75,n=5)(maxpooling1)
conv2=Conv2D(256,(5,5),strides=(1,1),padding='same',activation='relu',kernel_initializer=normal_init,bias_initializer='zeros',name='conv2')(maxpooling1)
maxpooling2=MaxPooling2D(pool_size=(3,3),strides=2,name='maxpooling2')(conv2)
#lrn2=LRN(alpha=0.0001,k=2,beta=0.75,n=5)(maxpooling2)
conv3=Conv2D(384,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer=normal_init,bias_initializer='zeros',name='conv3')(maxpooling2)
conv4=Conv2D(384,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer=normal_init,bias_initializer='zeros',name='conv4')(conv3)
conv5=Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer=normal_init,bias_initializer='zeros',name='conv5')(conv4)
maxpooling3=MaxPooling2D(pool_size=(3,3),strides=2,name='maxpooling3')(conv5)
flatten=Flatten(name='flatten')(maxpooling3)
dense1=Dense(512,activation='relu',kernel_initializer=normal_init,name='dense1')(flatten)
dropout1=Dropout(0.5)(dense1)
dense2=Dense(512,activation='relu', kernel_initializer=normal_init,name='dense2')(dropout1)
dropout2=Dropout(0.5)(dense2)
outputs=Dense(100,activation='sigmoid',kernel_initializer=normal_init,name='output')(dropout2)
model = Model(X, outputs)
model.summary()
plot_model(model,to_file='AlexNet.png',show_shapes=True)

计算图:

3.训练网络

这里使用Keras官方带数据增强的数据生成器,代码如下:

#定义数据生成器
from keras.preprocessing import image
from keras import optimizers
train_gen=image.ImageDataGenerator(featurewise_center=True,#输入数据数据减去数据集均值width_shift_range=0.2,#水平平移height_shift_range=0.2,#垂直平移horizontal_flip=True,#水平翻转brightness_range=[-0.1,0.1] #亮度变化范围
)
#编译模型
model.compile(loss='categorical_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])
#训练图片路径
train_dir=r'F:BaiduNetdiskDownloadmini-imagenetimages_normal'
tg=train_gen.flow_from_directory(train_dir,target_size=(224,224),batch_size=128,class_mode='categorical'
)
tg.class_indices

开始训练。。。

history=model.fit_generator(tg,#训练数据生成器steps_per_epoch=10,#从生成器中抽取100个批次,epochs=5,#进行30轮训练shuffle=True,
)

因为是使用数据放在磁盘上,训练实在是太慢了,所以意思意思就得了。

参考文献

[1]Alex Krizhevsky,Ilya Sutskever,Geoffrey E. Hinton.ImageNet Classification with Deep Convolutional

Neural Networks.2012

[2] yangdashi888. 深度学习的局部响应归一化LRN(Local Response Normalization)理解. https://blog.csdn.net/yangdashi888/article/details/77918311. 2017-09-09

[3] Microstrong0305. 在AlexNet中LRN 局部响应归一化的理解. https://blog.csdn.net/program_developer/article/details/79430119. 2018-03-03

高斯拟合原理_AlexNet原理和实现相关推荐

  1. 20181210-es6(letconst解构模版字符串原理 展开运算符、剩余运算符运用 深拷贝原理 reduce原理 箭头函数)...

    变量声明 var 特点: 1.可以重复声明 2.不能定义常量 3.不支持块级作用域 复制代码 let //1.不存在预解释 变量提升 //2.暂时性死区 //3.具备块级,同一块内不能重复声明;let ...

  2. MySQL主从复制原理(原理+实操)

    1.MySQL主从复制原理(原理+实操) 主从复制简介 在实际的生产中,为了解决Mysql的单点故障已经提高MySQL的整体服务性能,一般都会采用「主从复制」. 比如:在复杂的业务系统中,有一句sql ...

  3. 使用Origin进行非线性高斯拟合

    文章目录 1.导入数据 2.高亮显示名为Amplitude的列,并绘制散点图. 3.返回工作簿,选中名为Error的列,然后右键单击并 从上下文菜单中选择"设置为:Y Error" ...

  4. matlab 半高斯拟合,高斯曲线拟合求半宽高

    请问下我想用matlab求拉曼光谱的拟合曲线的半宽高,用origin的高斯拟合可以直接求出一系列的数值,如下图: QQ图片20180626113405.png (83.35 KB, 下载次数: 1) ...

  5. 双高斯拟合,差距啊,继续努力吧!

    继续用PEAKFIT对数据做的双高斯拟合: 看第一个图多漂亮,这个是真实的数据,不是模拟的,而下面我的数据,汗~~,看到差距了,慢慢研究吧,只有静下心来,才能做好研究!别顾虑太多,一步一步的走,PAP ...

  6. Python 高斯拟合

    Python 高斯拟合 通常我们进行高斯拟合的办法是导入scipy的curve_fit 包,不过这需要自己手写一个高斯分布的函数表达式,不是很方便,astropy提供了一个写好的高斯拟合包 1. 调包 ...

  7. Docker实现原理/容器原理(LXC,Cgroups,Docker)

    Docker实现原理/容器原理 Docker实现原理/容器原理 什么是容器(Container) 容器 传统架构问题 容器是什么 容器如何实现 Cgroups Cgroups是什么 Cgroups解决 ...

  8. Spring原理/SpringMVC原理/IOC/AOP原理

    Spring原理/SpringMVC原理/IOC/AOP原理 我的微型IOC框架实现 我的微型IOC框架实现 当你打开这本书的时候我要告诉你的第一句话就是认真把前言读完,因为前言概括的本书的大纲以及思 ...

  9. python直方图拟合曲线_关于matplotlib:Python高斯拟合颜色与直方图条形相同

    我使用pyplot中的函数plot()和hist()(没有任何颜色定义)来生成以下图形: 将包括更多的数据集. 这就是为什么我要对拟合曲线和相关的直方图使用相同的颜色,以使其具有一定的可区分性. 我找 ...

最新文章

  1. 机器学习笔记:牛顿方法
  2. 经常收到信用卡邀请短信,但为什么总是办不下来?
  3. Webstorm设置开发模板
  4. 取消Win7关机时的补丁更新
  5. 总结开发Silverlight项目准则 [转]
  6. 一个关于指针+记录数据类型的使用技巧
  7. vue npm run dev 报错 semver\semver.js:312 throw new TypeError('Invalid Version: ' + version)
  8. Razor的主版页面框架
  9. slxrom+v.21+原生android+4.2,小米4移动联通版 魔趣OS 安卓10 MagiskV21版 完美ROOT 纯净完美 原生极简 纯净推荐...
  10. 怎样用计算机做ppt,电脑怎么做ppt(教你几个小技巧做出高大上的ppt)
  11. 题解:100元买100只鸡,公鸡4元一只,母鸡3元一只,小鸡1元3只,问公鸡,母鸡,小鸡各买了多少只?
  12. 去水印小程序源码,全新界面无加密,平台支持微信小程序和QQ小程序。支持解析抖音、快手、皮皮虾和微视等平台。带PHP下载接口。支持微信QQ流量主
  13. wandb报错:Exception: The wandb backend process has shutdown
  14. Excle常用快捷键
  15. LiteMes系统中对于文件系统的文件删除使用
  16. 07.第八章、质量管理
  17. 【网络篇】第三篇——源端口号和目的端口号
  18. c# 调用c++ lib静态库
  19. jstree Api 中文翻译文档
  20. 财务系统软件c语言,用vc++6.0编写一个简单的财务应用程序来计算职工所得的实际工资...

热门文章

  1. [gic]-ARM gicv3/gicv2的总结和介绍-PPT
  2. Boost Part III. 函数对象与高级编程 Library 10. Lambda 用法 switch_statement
  3. (58)模拟线程切换——添加挂起、恢复线程功能
  4. 2020-11-12(JNI开发常见错误)
  5. 【安全漏洞】SRC另类思路分享:不受限制的资源调用
  6. 如何使用ThreadStackSpoofer隐藏Shellcode的内存分配行为
  7. Intel VT学习笔记(九)—— EPT应用示例
  8. windbg基本简单步骤
  9. 事件,信号量,互斥量
  10. 1.18 StringBuffer替换特殊字符