Caffe实战之Python接口系列 终章总结

引言

对官方例程中所用到的Python接口的用法做一个归纳,方便查询(并不是所有,只是例程中出现过的)。

目录

  • 引言
  • 目录
    • 往期传送门
    • 常用的模块
    • 1. 网络的初始化
      • 1.1. 网络模式设置
      • 1.2. 用模型prototxt加载网络(net = caffe.Net版,常用于部署)
      • 1.3. 用求解器prototxt加载网络(solver = caffe.get_solver/SGDSolver版,常用于训练)
      • 1.3. 用Caffe自带的工具加载输入图像并进行格式转换
      • 1.4. 关于subtract mean的说明
    • 2. 定义网络模型和求解器
      • 2.1. 定义网络模型
      • 2.2. 定义求解器
    • 3. 读写网络模型的数据(net = caffe.Net版,常用于部署)
      • 3.1. 各层数据/梯度
      • 3.2. 各层权重/偏置
      • 3.3 数据的基本操作
    • 4. 读写网络模型数据(solver = caffe.get_solver/SGDSolver版,常用于训练)
      • 4.1. 各层数据
      • 4.2. 各层权重和偏置
      • 4.3 数据基本操作
    • 5. 网络推理/评估(net = caffe.Net版,常用于部署)
    • 6. 网络训练/测试(solver = caffe.get_solver/SGDSolver版,常用于训练)

往期传送门

  1. Caffe实战之Python接口系列(一)Classification_Visualization
  2. Caffe实战之Python接口系列(二)Learning-LeNet
  3. Caffe实战之Python接口系列(三)Fine-tuning a Pretrained Network
  4. Caffe实战之Python接口系列(四)Brewing Logistic Regression then Going Deeper
  5. Caffe实战之Python接口系列(五)Multilabel classification on PASCAL using python data-layers
  6. Caffe实战之Python接口系列(六)Net Surgery(Editing model parameters)
  7. Caffe实战之Python接口系列(七)R-CNN detection
  8. Caffe实战之Python接口系列(八)Siamese Network Tutoria

常用的模块

import sys # 用于管理sys.path,也就是python的搜索路径,如:sys.path.append()或者.insert()都是达到临时添加的效果,不过.insert()可以设置优先级(列表的前后位置)
import os # 用于路径目录方面的操作,如os.path.isfile(),os.chdir()改变当前工作目录
import os.path as osp  # 简写模式
import tempfile # 管理临时文件

import pandas as pd # 用于数据分析,如pd.read_hdf(),pd.DataFrame(),pd.Series()
import numpy as np
import matplotlib.pyplot as plt # 绘图,常见参数设置有'figure.figsize'、'image.cmap'
from pylab import *   # 结合pylot和numpy到单个命名空间,这对于交互式工作很方便,但是对于编程,建议将命名空间保持独立,也就是上面两行。

import caffe # 常用于加载已有网络模型
from caffe import layers as L, params as P # 用于访问和设置各种层类型
from caffe.proto import caffe_pb2  # 用于定义求解器

1. 网络的初始化

1.1. 网络模式设置

# 使用CPU模式
caffe.set_mode_cpu()
# 使用GPU模式
caffe.set_device(0) # 用于指定GPU设备
caffe.set_mode_gpu()

caffe.set_random_seed() # 固定随机种子用于复现

1.2. 用模型prototxt加载网络(net = caffe.Net版,常用于部署)

net = caffe.Net(model_def,      # 定义模型架构的prototxt文件
                model_weights,  # 训练好的权重参数
                caffe.TEST)     # 使用模型的TEST模式

1.3. 用求解器prototxt加载网络(solver = caffe.get_solver/SGDSolver版,常用于训练)

solver = caffe.SGDSolver('solver.prototxt') # 仅用于加载SGD求解器
solver = caffe.get_solver(solver_config_path) # 允许加载其他求解器类型
solver.net.copy_from(weights) # 从指定文件中加载权重
solver.test_nets[0].share_with(solver.net)  # 共享权重 

1.3. 用Caffe自带的工具加载输入图像并进行格式转换

分为四步:实例化转换对象、设置转换参数、加载图像、调用对象转换格式

# 实例化
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
# 设置转换参数
transformer.set_transpose('data', (2,0,1))  # 维度转换,如HxWxC to CxHxW
transformer.set_mean('data', mu)            # 每个通道减去数据集的均值
transformer.set_raw_scale('data', 255)      # 图像预处理时用在减去均值之前的缩放系数,如[0, 1]缩放到[0, 255]  
transformer.set_input_scale('data', 0.017)  # 图像预处理时用在减去均值之后的缩放系数
transformer.set_channel_swap('data', (2,1,0))  # 交换通道,如 RGB to BGR
# 加载输入图像
image = caffe.io.load_image('img_path')  # 加载进来的图像像素值在[0,1]区间,RGB or grayscale
# 调用对象转换格式 resize->transpose->channel_swap->raw_scale->mean->input_scale
transformed_image = transformer.preprocess('data', image) 
# 调用对象逆向转回格式 与上面预处理的操作顺序相反
image = transformer.deprocess('data', net.blobs['data'].data[0]) 

1.4. 关于subtract mean的说明

  • 减去均值的操作接受两种输入:
    1. 像素级的均值,shape和输入图像一致,每个像素位置的平均;
    2. 通道级的均值,每个颜色通道对应一个均值
  • prototxt中对应transform_parammean_valuemean_file(*.binaryproto),也就是说对pixs-wise 均值的各通道再求平均就能得到channel-wise 均值。
  • C++接口接受*.binaryproto文件输入,pycaffe接受*.npy文件输入,下附转换的关键代码:
import caffe
import numpy as np
import sys

blob = caffe.proto.caffe_pb2.BlobProto()
data = open( sys.argv[1] , 'rb' ).read()
blob.ParseFromString(data)
arr = np.array( caffe.io.blobproto_to_array(blob) )
out = arr[0]
np.save( 'mean.npy' , out )

2. 定义网络模型和求解器

2.1. 定义网络模型

n = caffe.NetSpec() # 实例化网络模型
# 构建网络
    n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb,

    n.data, n.label = L.HDF5Data(batch_size=batch_size, source=hdf5, ntop=2)

    n.data, n.label = L.ImageData(
        transform_param=dict(mirror='',crop_size,mean_file/mean_value,shuffle,scale),
        source=list_path,
        batch_size=50, new_height=256, new_width=256, ntop=2,root_folder='')

    n.data = L.DummyData(shape=dict(dim=[1, 3, 227, 227])) # 虚拟数据层,常用于调试阶段

    # 使用自定义的Python层
    n.data, n.label = L.Python(module = 'layers_class_name',       # 该Python层的类名
                                ntop = 2,                          # 顶层数量
                                param_str=str(layer_params))  # 层参数

    n.conv = L.Convolution(bottom, kernel_size=ks, stride=stride,
                         num_output=nout, pad=pad, group=group,
                         param=param, weight_filler=weight_filler,

    n.pool = L.Pooling(bottom, pool=P.Pooling.MAX, kernel_size=ks, stride=stride)
    ......
    n.fc =   L.InnerProduct(bottom, num_output=nout, param=param,
                        weight_filler=weight_filler,
                        bias_filler=bias_filler)
    n.relu = L.ReLU(bottom, in_place=True)
    n.drop = L.Dropout(bottom, in_place=True)
    n.norm = L.LRN(bottom, local_size=5, alpha=1e-4, beta=0.75)
    n.loss =  L.SoftmaxWithLoss(bottom, n.label)   # 多类别损失
    n.loss = L.SigmoidCrossEntropyLoss(n.score, n.label)  # 用于多标签损失
    n.acc = L.Accuracy(bottom, n.label)
    n.probs = L.Softmax(bottom)

    n.__setattr__(new_name, old_layer_name) # 用于改名
# 发布为prototxt格式
n.to_proto()    # 注:写入文件时要使用str()

2.2. 定义求解器

from caffe.proto import caffe_pb2
s = caffe_pb2.SolverParameter()

# 为重现试验设置随机种子
# 控制训练过程的随机
s.random_seed = 0xCAFFE

s.train_net = train_net_path        # 指定网络训练配置文件的位置
s.test_net.append(test_net_path)    # 指定网络测试配置文件的位置,可以添加多个测试网络
s.test_interval = 500               # 每训练500迭代,测试一次
s.test_iter.append(100)             # 每次测试时都要测试100个batch取平均
s.iter_size = 1                     # 处理batch_size*iter_size个数据后,更新一次参数
s.max_iter = 10000                  # 最大迭代次数

# 编辑这里来尝试不同的求解器
# 求解器类型包括 "SGD", "Adam", "Nesterov" 等
s.type = "SGD"

# 设置基础学习率
s.base_lr = 0.01   

# 设置momentum来加速学习,通过将当前和之前的updates进行加权平均
s.momentum = 0.9
# 设置权重衰减系数来正则化避免过拟合
s.weight_decay = 5e-4

# 设置`lr_policy`来定义学习率在训练期间如何变化。

s.lr_policy = 'inv'
s.gamma = 0.0001
s.power = 0.75

# 保持学习率不变(与自适应方法相对立)
# s.lr_policy = 'fixed'

# 每迭代stepsize次,学习率乘以gamma
s.lr_policy = 'step'
s.gamma = 0.1
s.stepsize = 20000

# 每迭代1000次显示当前训练损失和精度
s.display = 1000

# 每5K次迭代保存一次快照
s.snapshot = 5000
s.snapshot_prefix = 'mnist/custom_net'

# 设置在CPU还是GPU上训练
s.solver_mode = caffe_pb2.SolverParameter.GPU 或 caffe_pb2.SolverParameter.CPU

3. 读写网络模型的数据(net = caffe.Net版,常用于部署)

3.1. 各层数据/梯度

各层的输入/输出数据均存储在net.blobs['layer_name']中,一个blob存储了两块数据‘data’‘diff’,分别代表普通数据梯度

# 每层的blobs形如
net.blobs['layer_name'](N,        # 一个batch的大小,每层都一样
                        C,        # 通道数,取决于卷积核数目(数据层取决于输入)
                        H, W)     # 图像的高宽,根据公式计算
# 迭代打印出各层输出的shape
for layer_name, blob in net.blobs.iteritems():
    print layer_name + '\t' + str(blob.data.shape)

3.2. 各层权重/偏置

各层的权重/偏置均存储在net.params['layer_name']net.params['layer_name'][0]为权重,net.params['layer_name'][1]为偏置

# 各层的params形如
net.params['layer_name'][0](output_channels,      # 输出通道数
                            input_channels,       # 输入通道数
                            filter_height, filter_width)    # 卷积核高宽
net.params['layer_name'][1](output_channels,)     # 输出通道数
# 迭代打印出各层参数的shape
for layer_name, param in net.params.iteritems():
    print layer_name + '\t' + str(param[0].data.shape), str(param[1].data.shape)

net.name # 网络的名称

3.3 数据的基本操作

下面列举出一些读/写数据的examples,各操作存在共性

# reshape
net.blobs['data'].reshape(n, c, h, w)
# 访问各层数据
net.blobs['data'].data[...] = transformed_image # 给输入数据层赋值
net.blobs['data'].data.copy() # copy数据
feat = net.blobs['conv1'].data[0, :36] # 取conv1层batch中的第1张图所对应的前36个通道
output_prob = output['prob'][0] # 读取一个batch中第一幅图的最后输出层'prob'的概率
net.blobs['conv'].data.min(), net.blobs['conv'].data.max() # 取大,取小
filters = net.params['conv1'][0].data # 读取卷积核权重数据 

4. 读写网络模型数据(solver = caffe.get_solver/SGDSolver版,常用于训练)

4.1. 各层数据

# 迭代打印出各层输出的shape
[(k, v.data.shape) for k, v in solver.net.blobs.items()]

4.2. 各层权重和偏置

# 迭代打印出各层参数的shape
[(k, v[0].data.shape) for k, v in solver.net.params.items()]

4.3 数据基本操作

solver.net.blobs['label'].data[:10] # 取训练网络‘label’层前10个数据
solver.test_nets[0].blobs['data'].data[:8, 0] # 取测试网络‘data’层一个batch的前8个数据,第一个通道
solver.test_nets[0].blobs['data'].num # 获取数据个数(这里为batch_size)

solver.net.params['conv1'][0].diff[:, 0] # 取conv1层权重的梯度
solver.net.params['conv1'][0].data.copy() # 数据深拷贝

solver.net.layers[0]
solver.iter # 迭代次数

5. 网络推理/评估(net = caffe.Net版,常用于部署)

output = net.forward() # 做一次前向传播
net.forward(start='conv1') # 拿上一个batch做一次前向传播(不加载新的batch)
output = net.forward_all(data=data) # 批量前向传播,一次装载所有数据
net.save('weights_name.caffemodel')

6. 网络训练/测试(solver = caffe.get_solver/SGDSolver版,常用于训练)

solver.net.forward()  # 训练网络,做一次前向传播
solver.net.backward() # 训练网络,做一次反向传播
solver.test_nets[0].forward()  # 测试网络(可能会多于一个)
solver.test_nets[0].forward(start='conv1') # 拿上一个batch测试网络(不加载新的batch)
solver.step(1) # 对一个batch的数据迭代一次,包括forward、backward、update
solver.solve() # 根据solver文件中最大迭代次数的限制,训练完整的model
solver.net.save(weights_name) # 保存权重参数

你可能感兴趣的:(Caffe)