caffe詳解之Python接口


從零開始,一步一步學習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,而且還有反向傳播!這樣就可以更新整個網絡的權值與偏置,同時得到該batchloss

#訓練網絡進行一次正向與反向傳播,並進行更新權值與偏置;
sover.step(1)#表示進行1次訓練。

sover.step(n)#表示進行n次訓練。

solver.solve()

根據solver文件中設置進行完整model訓練。

# 根據solver文件中設置進行完整model訓練
solver.solve()

訓練設置

訓練過程中選擇使用GPUCPU,若有多塊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)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章