從零開始,一步一步學習caffe的使用,期間貫穿深度學習和調參的相關知識!
生成net文件
from caffe import layers as L, params as P
def lenet(lmdb, batch_size):
# 以Lenet-5網絡搭建爲例
n = caffe.NetSpec() # 繼承自NetSpec
# 創建數據層。數據層類型選用LMDB文件,向上傳遞兩類數據(ntop=2):圖片數據和對應的標籤,並歸一化到[0,1]
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.fc1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
#創建激活函數層
n.relu1 = L.ReLU(n.fc1, in_place=True)
n.score = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
#創建loss函數
n.loss = L.SoftmaxWithLoss(n.score, n.label)
return n.to_proto()
with open('mnist/lenet_auto_train.prototxt', 'w') as f:
f.write(str(lenet('mnist/mnist_train_lmdb', 64)))
with open('mnist/lenet_auto_test.prototxt', 'w') as f:
f.write(str(lenet('mnist/mnist_test_lmdb', 100)))
生成solver文件
from caffe.proto import caffe_pb2
def gen_solver(solver_file, net_file, test_net_file=None):
s = caffe_pb2.SolverParameter() #繼承自SolverParameter
s.train_net = net_file
if not test_net_file:
s.test_net.append(net_file)
else:
s.test_net.append(test_net_file)
s.test_interval = 500 # 每訓練500次,執行一次測試
s.test_iter.append(100) # 測試迭代次數,假設測試數據有8000個,那batch size=80
s.max_iter = 20000 # 最大迭代次數
s.base_lr = 0.001 # 基礎學習率
s.momentum = 0.9 # momentum係數
s.weight_decay = 5e-4 # 正則化權值衰減因子,防止過擬合
s.lr_policy = 'step' # 學習率衰減方法
s.stepsize=1000 # 只對step方法有效, base_lr*gamma^floor(iter/stepsize)
s.gamma = 0.1
s.display = 500 # 輸出日誌間隔迭代次數
s.snapshot = 10000 # 在指定迭代次數時保存模型
s.snapshot_prefix = 'shapshot'
s.type = 'SGD' # 迭代算法類型, ADADELTA, ADAM, ADAGRAD, RMSPROP, NESTEROV
s.solver_mode = caffe_pb2.SolverParameter.GPU #採用GPU訓練 或單獨設置
with open(solver_file, 'w') as f:
f.write(str(s))
solver = caffe.SGDSolver(solver_file)
return solver
訓練 && 測試
solver.net.forward()
solver.net.forward()
是將batch_size
個圖片送到網絡中去,只有前向傳播(Forward Propagation
,FP),作用於訓練集
#訓練數據作爲輸入,進行一次前向傳播:
solver.net.forward()
#假如有300個數據,我們的batch_size的大小爲100,那麼:
solver.net.forward() #數據爲1-100;
solver.net.forward() #數據爲101-200
solver.net.forward() #數據爲201-300
solver.net.forward() #數據爲1-100
#另外,我們可以設置forward開始的地方,如下面所示:
solver.net.forward(start ='conv1') #表示從conv1開始,這樣的話,data層這不用傳用新的數據了。
solver.test_nets[i].forward()
solver.test_nets[i].forward()
也是將batch_size
個圖片送到網絡中去,只有前向傳播(Forward Propagation
,FP),作用於測試集。其中i
表示第幾個測試網絡,從0
開始。例如,我們就一個測試網絡的話,我們就寫爲:solver.test_nets[0].forward()
# 測試數據作爲輸入,進行一次前向傳播:
solver.test_nets[0].forward() #第一個測試網絡
solver.test_nets[1].forward() #第二個測試網絡
#假如有300個數據,我們的batch_size的大小爲100,那麼:
solver.test_nets[0].forward() #數據爲1-100;
solver.test_nets[0].forward() #數據爲101-200
solver.test_nets[0].forward() #數據爲201-300
solver.test_nets[0].forward() #數據爲1-100
solver.net.backward()
solver.net.backward()
是將batch_size
個圖片送到網絡中去,只有反向傳播(Back Propagation
,BP),作用於訓練集
#訓練數據作爲輸入,進行一次反向傳播:
solver.net.backward()
solver.step(n)
solver.step(1)
也是將batch_size
個圖片送到網絡中去,不過solver.step(1)
不僅有正向傳播FP
,而且還有反向傳播!這樣就可以更新整個網絡的權值與偏置,同時得到該batch
的loss
值。
#訓練網絡進行一次正向與反向傳播,並進行更新權值與偏置;
sover.step(1)#表示進行1次訓練。
sover.step(n)#表示進行n次訓練。
solver.solve()
根據solver
文件中設置進行完整model
訓練。
# 根據solver文件中設置進行完整model訓練
solver.solve()
訓練設置
訓練過程中選擇使用GPU
或CPU
,若有多塊GPU
可以指定使用哪一塊。
# 使用GPU
caffe.set_device(gpu_id) # 若不設置,默認爲0
caffe.set_mode_gpu()
# 使用CPU
caffe.set_mode_cpu()
數據與參數的保存及調用
Blob
#solver.net.blobs爲一個字典的數據類型,裏面的key值爲各個layer的名字,value爲caffe的blob塊;
solver.net.blobs
#輸出:
rderedDict([('data', <caffe._caffe.Blob at 0x7f7bde968398>),
('label', <caffe._caffe.Blob at 0x7f7bde968488>),
('conv1', <caffe._caffe.Blob at 0x7f7bde968578>),
('pool1', <caffe._caffe.Blob at 0x7f7bde968e60>),
('conv2', <caffe._caffe.Blob at 0x7f7bde9686e0>),
('pool2', <caffe._caffe.Blob at 0x7f7bde968cf8>),
('ip1', <caffe._caffe.Blob at 0x7f7bde968c80>),
('ip2', <caffe._caffe.Blob at 0x7f7bde968c08>),
('loss', <caffe._caffe.Blob at 0x7f7bde968b90>)])
#我們可以訪問Blob塊裏的內容了,通過看Blob塊的源碼你會發現裏面有data, diff,count等內容的。
#我們以conv1層爲例子,我們訪問conv1的輸出的數據,可以通過下面的語句:
solver.net.blobs['conv1'].data
solver.net.blobs['conv1'].diff
#如果想看它們的數據結構,可以通過下面的語句得到:
solver.net.blobs['conv1'].data.shape
solver.net.blobs['conv1'].diff.shape
#另外,還可以通過reshape()transpose()等操作對它們變形,應該是對數組的操作之類的吧。
Params
#solver.net.params爲一個字典的數據類型,key值爲layer的名字,value爲caffe的blob塊的容器;
solver.net.params()
#輸出爲:
solver.net.params
orderedDict([ ('conv1', <caffe._caffe.BlobVec at 0x7f7bffd68578>),
('conv2', <caffe._caffe.BlobVec at 0x7f7bde9ff6e0>),
('ip1', <caffe._caffe.BlobVec at 0x7f7bde968f80>),
('ip2', <caffe._caffe.BlobVec at 0x7f7bde968408>)])
#下面,我們可以訪問params塊裏的內容了。我們以conv1層爲例子,具體如下:
#sover.net.params['conv1'][0]裏面放是與連接權值相關的數據、梯度;可以通過下面方式訪問:
solver.net.params['conv1'][0].data #數據值
solver.net.params['conv1'][0].diff #梯度值
#solver.net.params['conv1'][1]裏面放的是與偏置相關的的數據、梯度;可以通過下面方式訪問:
solver.net.params['conv1'][1].data #數據值
solver.net.params['conv1'][1].diff #梯度值
#同樣,我們可以還可以通過它們進行 shape()、reshape()、transpose()等操作
參考
caffe中python接口的使用(https://www.cnblogs.com/yinheyi/p/6062488.html)