這是caffe官方文檔Notebook Examples中的第二個例子,鏈接地址:http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/01-learning-lenet.ipynb
這個例子使用LeNet對手寫數字分類。LeNet的結構可以參考:http://blog.csdn.net/thystar/article/details/50470325
1. 改變工作目錄:
import os
caffe_root = '/home/sindyz/caffe-master/'
os.chdir(caffe_root)
2. 導入相應的包
import sys
sys.path.insert(0, './python')
import caffe
from pylab import *
%matplotlib inline
3. 獲取數據
如果已經下載過,這步可以不要
# Download and prepare data
!data/mnist/get_mnist.sh
!examples/mnist/create_mnist.sh
4. 寫LeNet網絡結構,分別命名爲lenet_auto_train.prototxt和lenet_auto_test.prototxt
from caffe import layers as L
from caffe import params as P
def lenet(lmdb, batch_size):
# our version of LeNet: a series of linear and simple nonlinear transformations
n = caffe.NetSpec()
n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb,
transform_param=dict(scale=1./255), ntop=2)
n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier'))
n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier'))
n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
n.ip1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
n.relu1 = L.ReLU(n.ip1, in_place=True)
n.ip2 = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
n.loss = L.SoftmaxWithLoss(n.ip2, n.label)
return n.to_proto()
with open('examples/mnist/lenet_auto_train.prototxt', 'w') as f:
f.write(str(lenet('examples/mnist/mnist_train_lmdb', 64)))
with open('examples/mnist/lenet_auto_test.prototxt', 'w') as f:
f.write(str(lenet('examples/mnist/mnist_test_lmdb', 100)))
5. 查看網絡結構
!cat examples/mnist/lenet_auto_train.prototxt
輸出:
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
transform_param {
scale: 0.00392156862745
}
data_param {
source: "examples/mnist/mnist_train_lmdb"
batch_size: 64
backend: LMDB
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
convolution_param {
num_output: 20
kernel_size: 5
weight_filler {
type: "xavier"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
convolution_param {
num_output: 50
kernel_size: 5
weight_filler {
type: "xavier"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "ip1"
type: "InnerProduct"
bottom: "pool2"
top: "ip1"
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
6. 查看參數文件,同樣是prototxt文件,用帶動量的SGD(隨機梯度下降),權重衰減,以及特定的學習率:
!cat examples/mnist/lenet_auto_solver.prototxt
輸出:
# The train/test net protocol buffer definition
train_net: "examples/mnist/lenet_auto_train.prototxt"
test_net: "examples/mnist/lenet_auto_test.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 100
# Carry out testing every 500 training iterations.
test_interval: 500
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "inv"
gamma: 0.0001
power: 0.75
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
7. 使用GPU,載入solver,這裏用SGD,Adagrad和Nesterov加速梯度也是可行的:
這裏需要說明一下,caffe的優化函數是非凸的,沒有解析解,需要通過優化方法來求解,
caffe封裝了三種優化方法:
- Stochastic Gradient Descent (SGD), 隨機梯度下降
- AdaptiveGradient (ADAGRAD), 自適應梯度下降
- Nesterov’s Accelerated Gradient (NAG)。Nesterov加速梯度下降法
caffe.set_device(0)
caffe.set_mode_gpu()
solver = caffe.SGDSolver(caffe_root+'/examples/mnist/lenet_auto_solver.prototxt')
8. 查看中間特徵(blobs)和參數(params)的維數:
[(k,v.data.shape) for k,v in solver.net.blobs.items()]
輸出[('data', (64, 1, 28, 28)),
('label', (64,)),
('conv1', (64, 20, 24, 24)),
('pool1', (64, 20, 12, 12)),
('conv2', (64, 50, 8, 8)),
('pool2', (64, 50, 4, 4)),
('ip1', (64, 500)),
('ip2', (64, 10)),
('loss', ())]
[(k,v[0].data.shape) for k,v in solver.net.params.items()]
輸出:
[('conv1', (20, 1, 5, 5)),
('conv2', (50, 20, 5, 5)),
('ip1', (500, 800)),
('ip2', (10, 500))]
9. 在測試集和訓練集上執行一個前向的過程
solver.net.forward()
solver.test_nets[0].forward()
輸出:
{'loss': array(2.2941734790802, dtype=float32)}
10. 顯示訓練集8個數據的圖像和他們的標籤,
imshow(solver.net.blobs['data'].data[:8, 0].transpose(1, 0, 2).reshape(28, 8*28), cmap='gray')
print solver.net.blobs['label'].data[:8]
[ 5. 0. 4. 1. 9. 2. 1. 3.]
11. 顯示測試集中的8個圖像和他們的標籤
imshow(solver.test_nets[0].blobs['data'].data[:8, 0].transpose(1, 0, 2).reshape(28, 8*28), cmap='gray')
print solver.test_nets[0].blobs['label'].data[:8]
[ 7. 2. 1. 0. 4. 1. 4. 9.]
11. 執行無誤,則執行一步SGB, 查看權值的變化:
第一層權值的變化如下圖:20個5x5規模的濾波器
solver.step(1)
imshow(solver.net.params['conv1'][0].diff[:, 0].reshape(4, 5, 5, 5)
...: .transpose(0, 2, 1, 3).reshape(4*5, 5*5), cmap='gray')
運行網絡,這個過程和通過caffe的binary訓練是一樣的,
12. 控制循環
因爲可以控制python中的循環,因此可以做一些其他的事情,例如自定義停止的標準,通過循環更新網絡來改變求解過程:
%%time
niter = 200
test_interval = 25
# losses will also be stored in the log
train_loss = zeros(niter)
test_acc = zeros(int(np.ceil(niter / test_interval)))
output = zeros((niter, 8, 10))
# the main solver loop
for it in range(niter):
solver.step(1) # SGD by Caffe
# store the train loss
train_loss[it] = solver.net.blobs['loss'].data
# store the output on the first test batch
# (start the forward pass at conv1 to avoid loading new data)
solver.test_nets[0].forward(start='conv1')
output[it] = solver.test_nets[0].blobs['ip2'].data[:8]
# run a full test every so often
# (Caffe can also do this for us and write to a log, but we show here
# how to do it directly in Python, where more complicated things are easier.)
if it % test_interval == 0:
print 'Iteration', it, 'testing...'
correct = 0
for test_it in range(100):
solver.test_nets[0].forward()
correct += sum(solver.test_nets[0].blobs['ip2'].data.argmax(1)
== solver.test_nets[0].blobs['label'].data)
test_acc[it // test_interval] = correct / 1e4
Iteration 0 testing...
Iteration 25 testing...
Iteration 50 testing...
Iteration 75 testing...
Iteration 100 testing...
Iteration 125 testing...
Iteration 150 testing...
Iteration 175 testing...
CPU times: user 19 s, sys: 6.2 s, total: 25.2 s
Wall time: 24.4 s
13. 畫出訓練樣本損失和測試樣本正確率
_, ax1 = subplots()
ax2 = ax1.twinx()
ax1.plot(arange(niter), train_loss)
ax2.plot(test_interval * arange(len(test_acc)), test_acc, 'r')
ax1.set_xlabel('iteration')
ax1.set_ylabel('train loss')
ax2.set_ylabel('test accuracy')
14. 畫出分類結果:
for i in range(8):
figure(figsize=(2, 2))
imshow(solver.test_nets[0].blobs['data'].data[i, 0], cmap='gray')
figure(figsize=(10, 2))
imshow(output[:50, i].T, interpolation='nearest', cmap='gray')
xlabel('iteration')
ylabel('label')
圖像省略,在網站上有的
15. 使用softmax分類
for i in range(8):
figure(figsize=(2, 2))
imshow(solver.test_nets[0].blobs['data'].data[i, 0], cmap='gray')
figure(figsize=(10, 2))
imshow(exp(output[:50, i].T) / exp(output[:50, i].T).sum(0), interpolation='nearest', cmap='gray')
xlabel('iteration')
ylabel('label')
參考網站:
http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/01-learning-lenet.ipynb