欢迎访问我的个人Blog: zengzeyu.com
导言
源码位置:caffe/examples/pycaffe/caffenet.py
该文件源代码是经典模型AlexNet的Caffe实现,有兴趣的小伙伴去拜读一下论文: ImageNet Classification with Deep Convolutional Neural Networks.
源码解读
1. 导入模块
from __future__ import print_function
from caffe import layers as L, params as P, to_proto
from caffe.proto import caffe_pb2
2. 定义Layer函数
包括: 卷积层(Convolution Layer)、全连接层(Full Connected Layer)和池化层(Pooling Layer)
2.1 卷积层(Convolution Layer)函数
def conv_relu(bottom, ks, nout, stride=1, pad=0, group=1):
conv = L.Convolution(bottom, kernel_size=ks, stride=stride,
num_output=nout, pad=pad, group=group)
return conv, L.ReLU(conv, in_place=True)
1. 函数输入
-
bottom
- 输入节点(blob
)名 -
ks
- 卷积核尺寸(kernel size
) -
nout
- 输出深度尺寸(number output
) -
stride
- 卷积核滑窗距离 -
pad
- 图像边缘添加尺寸,即在图像周围一周添加尺寸为pad
的空白像素 -
group
- 将数据进行分开训练堆数目
2. 调用Caffe卷基层生成函数
conv = L.Convolution(bottom, kernel_size=ks, stride=stride,num_output=nout, pad=pad, group=group)
3. 返回参数
-
conv
- 卷积层配置 -
L.ReLU(conv, in_place=True)
- 卷积后的数据经过ReLU激活函数得到的数据
2.2 全连接层(Full Connected Layer)
def fc_relu(bottom, nout):
fc = L.InnerProduct(bottom, num_output=nout)
return fc, L.ReLU(fc, in_place=True)
1. 调用Caffe内积函数
fc = L.InnerProduct(bottom, num_output=nout)
2. 返回参数
-
fc, L.ReLU(fc, in_place=True)
- 全连接分类之后数据通过ReLU函数
2.3 池化层(Pooling Layer)
def max_pool(bottom, ks, stride=1):
return L.Pooling(bottom, pool=P.Pooling.MAX, kernel_size=ks, stride=stride)
调用Caffe池化层生成函数
L.Pooling)()
-
pool=P.Pooling.MAX
- 池化类型选择MAX,即取模板内最大值输出
3. 定义网络结构
data, label = L.Data(source=lmdb, backend=P.Data.LMDB, batch_size=batch_size, ntop=2,
transform_param=dict(crop_size=227, mean_value=[104, 117, 123], mirror=True))
# the net itself
conv1, relu1 = conv_relu(data, 11, 96, stride=4)
pool1 = max_pool(relu1, 3, stride=2)
norm1 = L.LRN(pool1, local_size=5, alpha=1e-4, beta=0.75)
conv2, relu2 = conv_relu(norm1, 5, 256, pad=2, group=2)
pool2 = max_pool(relu2, 3, stride=2)
norm2 = L.LRN(pool2, local_size=5, alpha=1e-4, beta=0.75)
conv3, relu3 = conv_relu(norm2, 3, 384, pad=1)
conv4, relu4 = conv_relu(relu3, 3, 384, pad=1, group=2)
conv5, relu5 = conv_relu(relu4, 3, 256, pad=1, group=2)
pool5 = max_pool(relu5, 3, stride=2)
fc6, relu6 = fc_relu(pool5, 4096)
drop6 = L.Dropout(relu6, in_place=True)
fc7, relu7 = fc_relu(drop6, 4096)
drop7 = L.Dropout(relu7, in_place=True)
fc8 = L.InnerProduct(drop7, num_output=1000)
loss = L.SoftmaxWithLoss(fc8, label)
if include_acc:
acc = L.Accuracy(fc8, label)
return to_proto(loss, acc)
else:
return to_proto(loss)
1. 函数输入
-
lmdb
- 文件名 -
batch_size
- 每次训练输入样本数目 -
include_acc
- 加速?
2. 调用Caffe数据层输入函数(Data)
L.Data(source=lmdb, backend=P.Data.LMDB, batch_size=batch_size, ntop=2, transform_param=dict(crop_size=227, mean_value=[104, 117, 123], mirror=True))
-
backend
- 数据类型 -
ntop
- 输出blob
数目,因为数据层处理数据输出data和label,所以值为 2 -
transform_param
- 对单个图片处理:crop_size
图片剪裁大小,mean_value
RGB图像需要减去的值(目的是更好突出特征)和mirror
镜像处理。
Layer | Operation | Output |
---|---|---|
Data | crop_size:227, mean_value: [104, 117, 123], mirror: true | data: 227x227x3; label: 227x227x1 |
1 | conv1 -> relu1 -> pool1 -> norm1 | 27x27x96 |
2 | conv2 -> relu2 -> pool2 -> norm2 | 13x13x256 |
3 | conv3 -> relu3 | 11x11x384 |
4 | conv4 -> relu4 | 11x11x384 |
5 | conv5 -> relu5 -> pool5 | 6x6x256 |
6 | fc6 -> relu6 -> drop6 | 4096 |
7 | fc7 -> relu7 -> drop7 | 4096 |
8 | fc8 -> loss | 1000 |
3. 网络结构
此博客绘制了AlexNet网络结构图和数据流动图,方便直观理解网络结构,可移步:深度学习之图像分类模型AlexNet解读
第1-5层为卷积层,如下表所示:
Layer | Operation | Output |
---|---|---|
Data | crop_size:227, mean_value: [104, 117, 123], mirror: true | data: 227x227x3; label: 227x227x1 |
1 | conv1 -> relu1 -> pool1 -> norm1 | 27x27x96 |
2 | conv2 -> relu2 -> pool2 -> norm2 | 13x13x256 |
3 | conv3 -> relu3 | 11x11x384 |
4 | conv4 -> relu4 | 11x11x384 |
5 | conv5 -> relu5 -> pool5 | 6x6x256 |
6 | fc6 -> relu6 -> drop6 | 4096 |
7 | fc7 -> relu7 -> drop7 | 4096 |
8 | fc8 -> loss | 1000 |
以第1层代码为例进行分析:
- 第1层 = 卷积层(conv1+relu1) + 池化层(pool1) + 归一化(norm1)
(1). 第1层 - 卷积层(conv1+relu1)
作用:提取局部特征,使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。
conv1, relu1 = conv_relu(data, 11, 96, stride=4)
- 数据:数据层输出data数据
- 卷积核大小: 11
- 输出节点深度: 96
- 滑窗距离: 4
(2). 第1层 - 池化层(pool1)
作用:提取最大值,避免平均池化的模糊化效果。在AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。
pool1 = max_pool(relu1, 3, stride=2)
- 数据: relu1
- 模板核大小: 3
- 滑窗距离: 2
(3). 第1层 - 局部响应归一化(Local Response Normalize)(norm1)
作用:对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力
norm1 = L.LRN(pool1, local_size=5, alpha=1e-4, beta=0.75)
- 数据: pool1
- 取值模板尺寸: 5
- alpha: 0.0001
- beta: 0.75
4. 输出网络结构文件(.prototxt)
def make_net():
with open('train.prototxt', 'w') as f:
print(caffenet('/path/to/caffe-train-lmdb'), file=f)
with open('test.prototxt', 'w') as f:
print(caffenet('/path/to/caffe-val-lmdb', batch_size=50, include_acc=True), file=f)
5. 运行
if __name__ == '__main__':
make_net()
总结
Caffene.py是入门Caffe较好的源代码,结合原论文看,同时能加深对网络结构的理解,补充理论知识。下面根据这个example形式构建自己的网络结构,其中第一步,也是学习深度学习最重要的一步,编写自己的数据类型接口层程序。
以上。
附:
- AlexNet网络总结
- 深度学习之图像分类模型AlexNet解读