1. 程式人生 > >caffe Python API整理

caffe Python API整理

目錄

1、Packages匯入

2、定義layer

3、生成prototxt檔案

4、生成Solver檔案

5、Model訓練

6、訪問layer輸出值

7、net.params訪問網路引數

8、二進位制均值檔案轉python均值檔案

9、圖片預處理

10、自定義函式:引數/卷積結果視覺化

11、自定義:訓練過程Loss&Accuracy視覺化


1、Packages匯入

import sys
import os
caffe_root = './'  #指定caffe的根目錄 
sys.path.insert(0, caffe_root + 'python')    #將caffe python介面檔案路徑新增到python path中

import caffe
from caffe import layers as L
from caffe import params as P

2、定義layer

使用python介面建立layer層時,輸入引數應該符合caffe.proto中的層定義

  • lmdb/leveldb 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   )     )

# 例項
data, label = L.Data( batch_size=batch_size, 
                      backend=P.Data.LMDB, 
                      source='mnist/mnist_test_lmdb',
                      transform_param=dict(scale=1./255), 
                      ntop=2  #指定返回兩個top blob  )
  • HDF5 Data層定義
layer {
  name: "image"
  type: "HDF5Data"
  top: "image"
  include {
    phase: TRAIN
  }
  hdf5_data_param {
    source: "./training_data_paths.txt"
    batch_size: 64
  }
}


image = L.HDF5Data( hdf5_data_param={ 'source': './training_data_paths.txt',  
                                      'batch_size': 64                         },
                    include={'phase': caffe.TRAIN    }                              )
                     
                     
data, label = L.HDF5Data( batch_size=batch_size, 
                          source=source_path, 
                          ntop=2, # 設定輸出top數量
                          include={'phase': caffe.TRAIN}        ) 

data, label = L.HDF5Data( batch_size=batch_size, 
                          source=source_path,
                          ntop=2, 
                          include={'phase': caffe.TEST}        )
  • ImageData Data層定義

適用於txt檔案一行記錄一張圖片的資料來源

L.ImageData(source=list_path,
            batch_size=batch_size,
            new_width=48,
            new_height=48,
            ntop=2,
            ransform_param=dict(crop_size=40,mirror=True)      )
  • Convloution層定義

L.Convolution(  bottom, 
                kernel_size=ks, 
                stride=stride,
                num_output=nout, 
                pad=pad, 
                group=group                   )


#例項
conv1 = L.Convolution( data_blob, 
                       kernel_size=5, 
                       num_output=20, 
                       weight_filler=dict(type='xavier')        )
  • LRN層定義
L.LRN(  bottom, 
        local_size=5, 
        alpha=1e-4, 
        beta=0.75        )
  • ReLU層定義
L.ReLU( bottom, 
        in_place=True        )

#例項
relu1 = L.ReLU(data_blob, in_place=True)
  • Pooling層定義

L.Pooling( bottom,
            pool=P.Pooling.MAX, 
            kernel_size=ks, 
            stride=stride            )

#例項
 pool1 = L.Pooling(conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
  • FullConnect層定義
L.InnerProduct( bottom, 
                num_output=nout                )
  • Dropout層定義
L.Dropout(  bottom, 
            in_place=True         )
  • Loss層定義
L.SoftmaxWithLoss(  bottom, 
                    label                 )

#example
loss =  L.SoftmaxWithLoss(score_blob, label)
  • Accuracy層定義
L.Accuracy( bottom,
            label            )

3、生成prototxt檔案

caffe.NetSpec 是定義在caffe/net_spec.py 中的類,定義如下:

 """A NetSpec contains a set of Tops (assigned directly as attributes).
    Calling NetSpec.to_proto generates a NetParameter containing all of the
    layers needed to produce all of the assigned Tops, using the assigned
    names.
    NetSpec包括一系列的被賦值成key的Tops,呼叫NetSpec.to_proto生成NetParameter
    NetParameter包括生成所有tops的layer定義,NetParameter使用names做key
    
 """
def __init__(self):
        super(NetSpec, self).__setattr__('tops', OrderedDict())
    
    #方便使用.操作符呼叫
    def __setattr__(self, name, value):
        self.tops[name] = value

    def __getattr__(self, name):
        return self.tops[name]
    
    #方便使用[]訪問字典的值
    def __setitem__(self, key, value):
        self.__setattr__(key, value)

    def __getitem__(self, item):
        return self.__getattr__(item)

    def to_proto(self):
        names = {v: k for k, v in six.iteritems(self.tops)}
        autonames = Counter()
        layers = OrderedDict()
        for name, top in six.iteritems(self.tops):
            top._to_proto(layers, names, autonames)
        net = caffe_pb2.NetParameter()
        net.layer.extend(layers.values())
        return net
from caffe import layers as L, params as P

#引數:lmdb  資料檔案路徑
#      batch_size 批處理大小
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.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'))
    n.loss =  L.SoftmaxWithLoss(n.score, n.label)
    
    #返回proto檔案
    return n.to_proto()

#通說str(proto_object)返回字串格式網路定義    
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)))

4、生成Solver檔案

from caffe.proto import caffe_pb2

# 建立solver_proto物件
s = caffe_pb2.SolverParameter()

s.train_net = 'train.prototxt'         # 訓練配置檔案
s.test_net.append('val.prototxt')      # 測試配置檔案,可以有多個測試proto txt
s.test_interval = 782                   # 測試間隔
s.test_iter.append(313)                 # 測試迭代次數
s.max_iter = 78200                      # 最大迭代次數

s.base_lr = 0.001                       # 基礎學習率
s.momentum = 0.9                        # momentum係數
s.weight_decay = 5e-4                   # 權值衰減係數
s.lr_policy = 'step'                    # 學習率衰減方法
s.stepsize=26067                        # 此值僅對step方法有效
s.gamma = 0.1                           # 學習率衰減指數
s.display = 782                         # 螢幕日誌顯示間隔
s.snapshot = 7820
s.snapshot_prefix = 'shapshot'
s.type = “SGD”                          # 優化演算法
s.solver_mode = caffe_pb2.SolverParameter.GPU

#指定solver檔案儲存路徑
path='/home/xxx/data/'
solver_file=path+'solver.prototxt'     
with open(solver_file, 'w') as f:
    f.write(str(s))

5、Model訓練

# 訓練設定
caffe.set_device(gpu_id) # 若不設定,預設為0
caffe.set_mode_gpu()    # 使用GPU
caffe.set_mode_cpu()    # 使用CPU

# 載入Solver,有兩種常用方法
# 1. 無論模型中Slover型別是什麼統一設定為SGD
solver = caffe.SGDSolver('/home/xxx/data/solver.prototxt') 
# 2. 根據solver的prototxt中solver_type讀取,預設為SGD
solver = caffe.get_solver('/home/xxx/data/solver.prototxt')

# 訓練模型
# 1.1 前向傳播
solver.net.forward()  # train net
# 1.2 反向傳播,計算梯度
solver.net.backward()
# 1.3 進行一輪測試
solver.test_nets[0].forward()  # test net (there can be more than one)
# 1.4 儲存引數
solver.net.save('mymodel.caffemodel')

# 2. 進行一次完整的訓練,包括前向傳播+反向傳播+根據梯度更新引數
solver.step(1)
# 3. 根據solver檔案中設定進行完整model訓練
solver.solve()

6、訪問layer輸出值

每層的輸出值儲存在OrderedDict型別的物件中,net.blobs,其典型shape是(batch_size, channel, height, width)

# 訪問blob時使用blob名字做key值
# 返回值為numpy陣列,shape(batch_size, Channels, H, W)
feat = net.blobs['conv1'].data[0, :36]

# 遍歷網路的每一層,顯示輸出的shape
for layer_name, blob  in net.blobs.iteritems():
    print layer_name, '\t', str(blob.data.shape)

'''
輸出
    data	(50, 3, 227, 227)
    conv1	(50, 96, 55, 55)
    pool1	(50, 96, 27, 27)
    norm1	(50, 96, 27, 27)
    conv2	(50, 256, 27, 27)
    pool2	(50, 256, 13, 13)
    norm2	(50, 256, 13, 13)
    conv3	(50, 384, 13, 13)
    conv4	(50, 384, 13, 13)
    conv5	(50, 256, 13, 13)
    pool5	(50, 256, 6, 6)
    fc6		(50, 4096)
    fc7		(50, 4096)
    fc8		(50, 1000)
    prob	(50, 1000)
'''

 7、net.params訪問網路引數

      parameter值儲存在OrderedDict型別物件中,net.params, weight用0索引,biase用1索引。weight的典型shape是(output_channels, input_channels, filter_height,filter_width)。biases的典型shape是(output_channels,)

'''
 the parameters are a list of [weights, biases]
 訪問引數時,使用的layer層的名字做key值
 返回值為列表,0為權重,shape為(channels, h, w)
              1為偏置, shape為(channels)
'''
filters = net.params['conv1'][0].data

# 遍歷網路引數
for layer_name, param in net.params.iteritems():
    print layer_name + '\t' + str(param[0].data.shape), str(param[1].data.shape)

'''
輸出:
    conv1	(96, 3, 11, 11) (96,)
    conv2	(256, 48, 5, 5) (256,)
    conv3	(384, 256, 3, 3) (384,)
    conv4	(384, 192, 3, 3) (384,)
    conv5	(256, 192, 3, 3) (256,)
    fc6	(4096, 9216) (4096,)
    fc7	(4096, 4096) (4096,)
    fc8	(1000, 4096) (1000,)
'''

8、二進位制均值檔案轉python均值檔案

# 編寫一個函式,將二進位制的均值轉換為python的均值
def convert_mean(binMean,npyMean):
    #建立一個BlobProto物件
    blob = caffe.proto.caffe_pb2.BlobProto()
    bin_mean = open(binMean, 'rb' ).read()
    #將二進位制資料解析為BlobProto格式
    blob.ParseFromString(bin_mean)
    #blobProto物件轉換為np陣列
    arr = np.array( caffe.io.blobproto_to_array(blob) )
    npy_mean = arr[0]
    np.save(npyMean, npy_mean)

# 呼叫函式轉換均值
binMean='examples/cifar10/mean.binaryproto'
npyMean='examples/cifar10/mean.npy'
convert_mean(binMean,npyMean)

9、圖片預處理

# 建立一個名字叫‘data’圖片轉換器
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) 
# transformer會將channels變成最外面的維度, 即 (H,W,C) 變成(C, W, C)
transformer.set_transpose('data', (2,0,1))  
transformer.set_mean('data', mu)            # 每個通道上減去均值
transformer.set_raw_scale('data', 255)      # 從[0, 1]的範圍放大到[0, 255]
transformer.set_channel_swap('data', (2,1,0))  #修改通道順序,從RGB變成BG

# 讀取圖片
# caffe.io.load_image讀取圖片值的範圍是0-1,cv2.imread讀取圖片值的範圍是0-255
image = caffe.io.load_image(caffe_root + 'examples/images/cat.jpg')
# transformer進行圖片預處理,包括圖片值轉換到0-255
transformed_image = transformer.preprocess('data', image)

10、自定義函式:引數/卷積結果視覺化

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import caffe
%matplotlib inline

plt.rcParams['figure.figsize'] = (8, 8)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

def show_data(data, padsize=1, padval=0):
"""Take an array of shape (n, height, width) or (n, height, width, 3)
       and visualize each (height, width) thing in a grid of size approx. sqrt(n) by sqrt(n)"""
    # data歸一化
    data -= data.min()
    data /= data.max()
    
    # 根據data中圖片數量data.shape[0],計算最後輸出時每行每列圖片數n
    n = int(np.ceil(np.sqrt(data.shape[0])))
    # padding = ((圖片個數維度的padding),(圖片高的padding), (圖片寬的padding), ....)
    padding = ((0, n ** 2 - data.shape[0]), (0, padsize), (0, padsize)) + ((0, 0),) * (data.ndim - 3)
    data = np.pad(data, padding, mode='constant', constant_values=(padval, padval))
    
    # 先將padding後的data分成n*n張影象
    data = data.reshape((n, n) + data.shape[1:]).transpose((0, 2, 1, 3) + tuple(range(4, data.ndim + 1)))
    # 再將(n, W, n, H)變換成(n*w, n*H)
    data = data.reshape((n * data.shape[1], n * data.shape[3]) + data.shape[4:])
    plt.figure()
    plt.imshow(data,cmap='gray')
    plt.axis('off')

# 示例:顯示第一個卷積層的輸出資料和權值(filter)
print net.blobs['conv1'].data[0].shape
show_data(net.blobs['conv1'].data[0])
print net.params['conv1'][0].data.shape
show_data(net.params['conv1'][0].data.reshape(32*3,5,5))

11、自定義:訓練過程Loss&Accuracy視覺化

import matplotlib.pyplot as plt  
import caffe   
caffe.set_device(0)  
caffe.set_mode_gpu()   
# 使用SGDSolver,即隨機梯度下降演算法  
solver = caffe.SGDSolver('/home/xxx/mnist/solver.prototxt')  
  
# 等價於solver檔案中的max_iter,即最大解算次數  
niter = 10000 

# 每隔100次收集一次loss資料  
display= 100  
  
# 每次測試進行100次解算 
test_iter = 100

# 每500次訓練進行一次測試
test_interval =500
  
#初始化 
train_loss = zeros(ceil(niter * 1.0 / display))   
test_loss = zeros(ceil(niter * 1.0 / test_interval))  
test_acc = zeros(ceil(niter * 1.0 / test_interval))  
  
# 輔助變數  
_train_loss = 0; _test_loss = 0; _accuracy = 0  
# 進行解算  
for it in range(niter):  
    # 進行一次解算  
    solver.step(1)  
    # 統計train loss  
    _train_loss += solver.net.blobs['SoftmaxWithLoss1'].data  
    if it % display == 0:  
        # 計算平均train loss  
        train_loss[it // display] = _train_loss / display  
        _train_loss = 0  
  
    if it % test_interval == 0:  
        for test_it in range(test_iter):  
            # 進行一次測試  
            solver.test_nets[0].forward()  
            # 計算test loss  
            _test_loss += solver.test_nets[0].blobs['SoftmaxWithLoss1'].data  
            # 計算test accuracy  
            _accuracy += solver.test_nets[0].blobs['Accuracy1'].data  
        # 計算平均test loss  
        test_loss[it / test_interval] = _test_loss / test_iter  
        # 計算平均test accuracy  
        test_acc[it / test_interval] = _accuracy / test_iter  
        _test_loss = 0  
        _accuracy = 0  
  
# 繪製train loss、test loss和accuracy曲線  
print '\nplot the train loss and test accuracy\n'  
_, ax1 = plt.subplots()  
ax2 = ax1.twinx()  
  
# train loss -> 綠色  
ax1.plot(display * arange(len(train_loss)), train_loss, 'g')  
# test loss -> 黃色  
ax1.plot(test_interval * arange(len(test_loss)), test_loss, 'y')  
# test accuracy -> 紅色  
ax2.plot(test_interval * arange(len(test_acc)), test_acc, 'r')  
  
ax1.set_xlabel('iteration')  
ax1.set_ylabel('loss')  
ax2.set_ylabel('accuracy')  
plt.show()

參考文件:

1、https://blog.csdn.net/langb2014/article/details/53082704

2、http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb

3、http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/net_surgery.ipynb

4、http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/01-learning-lenet.ipynb