caffe框架学习二——Learning LeNet

英文版原文 https://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/01-learning-lenet.ipynb

教程代码

from caffe.proto import caffe_pb2
from caffe import Layers as L, params as P
import os
import caffe
from pylab import *
import sys

caffe_root = '../'
sys.path.insert(0, caffe_root + 'python')

os.chdir(caffe_root)
os.system('data/mnist/get_mnist.sh')  # 下载数据
os.system('examples/mnist/create_mnist.sh')  # 准备数据
os.chdir('examples')


def lenet(lmdb, batch_size):
    n = caffe.NetSpec()  # 获取Caffe的一个net

    n.data, n.label = L.Data(
        batch_size=batch_size,
        backend=P.Data.LMDB,  # 选择使用 LEVELDB 还是 LMDB
        source=lmdb,  # 数据库文件的路径
        # 特征归一化系数,将[0,255]归一化
        transform_param=dict(scale=1. / 255),
        ntop=2)  # 2个输出,分别是data和label

    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)

    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)))

caffe.set_mode_gpu()
caffe.set_device(0)  # 指定GPU的ID号

solver = None
solver = caffe.SGDSolver('mnist/lenet_auto_solver.prototxt')  # 表示求解接口

# 打印出(层名, (batch size, 特征维度, 空间维度))
print([(k, v.data.shape) for k, v in solver.net.blobs.items()])

# 打印出(层名, (权重参数维度))
print([(k, v[0].data.shape) for k, v in solver.net.params.items()])

# 前向传播训练网络
solver.net.forward()
solver.test_nets[0].forward()

# 展示训练结果及原图
imshow(
    solver.net.blobs['data'].data[:8, 0].transpose(1, 0, 2).reshape(
        28, 8 * 28),
    cmap='gray')
axis('off')
print('train labels:', solver.net.blobs['label']).data[:8]

# 展示测试结果及原图
imshow(
    solver.test_nets[0].blobs['data'].data[:8, 0].tranpsose(1, 0, 2).reshape(
        28, 8 * 28),
    cmap='gray')
print('test labels:', solver.test_nets[0].blobs['label'].data[:8])
axis('off')

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')
axis('off')

niter = 200  # 总迭代次数
test_interval = 25   # 每25次测试一次
train_loss = zeros(niter)  # 训练误差
test_acc = zeros(int(np.ceil(niter / test_interval)))  # 测试正确率
output = zeros((niter, 8, 10))

# 主迭代循环
for it in range(niter):
    solver.step(1)  # 迭代一步 Caffe使用SGD

    train_loss[it] = solver.net.blobs['loss'].data  # 训练误差

    solver.test_nets[0].forward(start='conv1')  # 从conv1开始前向传播
    output[it] = solver.test_nets[0].blobs['score'].data[:8]  # 保存测试概率结果

    if it % test_interval == 0:  # 到达该测试的轮数
        print('Iteration' + str(it) + 'testing...')
        correct = 0
        for test_it in range(100):
            solver.test_nets[0].forward()  # 从头开始前向传播
            correct += sum(solver.test_nets[0].blobs['score'].data.argmax(1) ==
                           solver.test_nets[0].blobs['label'].data)  # 统计正确的总个数
        test_acc[it // test_interval] = correct / 1e4   # 正确个数除以总数10000

_, ax1 = subplots()
ax2 = ax1.twinx()  # 双y轴
ax1.plot(arange(niter), train_loss)  # x 迭代次数 y 训练误差
ax2.plot(test_interval * arange(len(test_acc)),
         test_acc, 'r')  #  x 固定测试节点*一轮样本个数 y 测试误差
ax1.set_xlabel('iteration')
ax1.set_ylabel('train loss')
ax2.set_ylabel('test accuarcy')
ax2.set_title('Test Accuracy: {:,2f}'.format(test_acc[-1]))

# 展示一张图片属于10个数字的原始概率分布
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')

# 展示一张图片属于10个数字的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',
        camp='gray')  # 渐变打印颜色条,越白可能性越高
    xlabel('iteration')
    ylabel('label')

模板代码

from caffe.proto import caffe_pb2
from caffe import Layers as L, params as P
import os
import caffe
from pylab import *
import sys

caffe_root = '../'
sys.path.insert(0, caffe_root + 'python')

train_net_path = 'mnist/custom_auto_train.prototxt'  # 训练网络定义
test_net_path = 'mnist/custom_auto_test.prototxt'  # 测试网络定义
solver_config_path = 'mnist/custom_auto_solver.prototxt'  # 学习参数定义


# 定义网络
def custom_net(lmdb, batch_size):
    # 开始定义网络
    n = caffe.NetSpec()

    # 对于所有的网络保持这一data层的定义
    n.data, n.label = L.Data(
        batch_size=batch_size,
        backend=P.Data.LMDB,  # 选择使用 LEVELDB 还是 LMDB
        source=lmdb,  # 数据库文件的路径
        # 特征归一化系数,将[0,255]归一化
        transform_param=dict(scale=1. / 255),
        ntop=2)  # 2个输出,分别是data和label

    # 在这里编辑以定义不同的网络结构
    # 默认的网络结构是一个简单的现行分类器
    # 本质上是定义了一个多变量现行回归
    n.score = L.InnerProduct(
        n.data, num_output=10, weight_filler=dict(type='xavier'))

    # 这里定义我们已经尝试过的LeNet网络
    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')))

    # 可以在这里使用L.ELU或者L.Sigmoid尝试非线性
    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(train_net_path, 'w') as f:
    f.write(str(custom_net('mnist/mnist_train_lmdb', 64)))

# 保存测试网络定义
with open(test_net_path, 'w') as f:
    f.write(str(custom_net('mnist/mnist_test_lmdb', 100)))

# 定义solver
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个样本

s.max_iter = 10000  # 总的训练迭代次数

# 这里可以尝试不同的solver,比如"SGD", "Adam", 和 "Nesterov"等等
s.type = "SGD"

# 设置SGD的初始学习率
s.base_lr = 0.01  # 不同的学习率
s.momentum = 0.9  # 设置动量参数,参考现在和之前的更新来加速学习速度
s.weight_decay = 5e-4  # 设置衰减率来正则化以防止过拟合

# 设置'lr_policy'参数来确定训练过程中的学习率修改,我们使用默认的LeNet网络参数
# 其他的lr_policy选择参考 https://www.cnblogs.com/laowangxieboke/p/10282096.html
s.lr_policy = 'inv'
s.gamma = 0.0001
s.power = 0.75

# 这里可以设置固定的学习率
# s.lr_policy = 'fixed'

# 每1000次迭代后统计预测结果并计算训练误差
s.display = 1000

# 对中途的训练网络通过快照的方式进行保存
s.snapshot = 5000  # 5000次保存一次,在总训练次数10000中保存两次
s.snapshot_prefix = 'mnist/custom_net'

# 在GPU上进行训练
s.solver_mode = caffe_pb2.SolverParameter.GPU

# 在临时文件中写入solver并返回它的文件名
with open(solver_config_path, 'w') as f:
    f.write(str(s))

# 加载solver,创建训练过程,测试网络
# 忽略掉lmdb数据的解决方案(无法在同一数据上实例化两个solver)
solver = None
solver = caffe.get_solver(solver_config_path)

# 定义solve
niter = 250  # 定义总的训练次数
test_interval = niter / 10  # 定义总的测试次数
train_loss = zeros(niter)  # 在日志中保存loss
test_acc = zeros(int(np.ceil(niter / test_interval)))

# 主训练迭代
for it in range(niter):
    solver.step(1)  # Caffe使用SGD随机梯度下降

    train_loss[it] = solver.net.blobs['loss'].data  # 保存训练误差

    if it % test_interval == 0:  # 到达该测试的轮数
        print('Iteration' + str(it) + 'testing...')
        correct = 0
        for test_it in range(100):
            solver.test_nets[0].forward()  # 从头开始前向传播
            correct += sum(solver.test_nets[0].blobs['score'].data.argmax(1) ==
                           solver.test_nets[0].blobs['label'].data)  # 统计正确的总个数
        test_acc[it // test_interval] = correct / 1e4   # 正确个数除以总数10000

_, ax1 = subplots()
ax2 = ax1.twinx()  # 双y轴
ax1.plot(arange(niter), train_loss)  # x 迭代次数 y 训练误差
ax2.plot(test_interval * arange(len(test_acc)),
         test_acc, 'r')  #  x 固定测试节点*一轮样本个数 y 测试误差
ax1.set_xlabel('iteration')
ax1.set_ylabel('train loss')
ax2.set_ylabel('test accuarcy')
ax2.set_title('Test Accuracy: {:,2f}'.format(test_acc[-1]))

猜你喜欢

转载自blog.csdn.net/Zjhao666/article/details/88076062