歡迎訪問我的個人博客: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
鏡像處理。
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. 第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形式構建自己的網絡結構,其中第一步,也是學習深度學習最重要的一步,編寫自己的數據類型接口層程序。
以上。
附:
1. AlexNet網絡總結
2. 深度學習之圖像分類模型AlexNet解讀