本教程创建一个小的神经网络用于手写字符的识别。我们使用MNIST数据集进行训练和测试。这个数据集的训练集包含60000张来自500个人的手写字符的图像,测试集包含10000张独立于训练集的测试图像。你可以参看本教程的Ipython notebook

本节中,我们使用CNN的模型助手来创建网络并初始化参数。首先import所需要的依赖库。

%matplotlib inline
from matplotlib import pyplot
import numpy as np
import os
import shutil
from caffe2.python import core, cnn, net_drawer, workspace, visualize
# 如果你想更加详细的了解初始化的过程,那么你可以把caffe2_log_level=0 改为-1
core.GlobalInit(['caffe2', '--caffe2_log_level=0'])
caffe2_root = "~/caffe2"
print("Necessities imported!")
数据准备

我们会跟踪训练过程的数据,并保存到一个本地的文件夹。我们需要先设置一个数据文件和根文件夹。在数据文件夹里,放置用于训练和测试的MNIST数据集。如果没有数据集,那么你可以到这里下载MNIST Dataset,然后解压数据集和标签。

./make_mnist_db --channel_first --db leveldb --image_file ~/Downloads/train-images-idx3-ubyte --label_file ~/Downloads/train-labels-idx1-ubyte --output_file ~/caffe2/caffe2/python/tutorials/tutorial_data/mnist/mnist-train-nchw-leveldb

./make_mnist_db --channel_first --db leveldb --image_file ~/Downloads/t10k-images-idx3-ubyte --label_file ~/Downloads/t10k-labels-idx1-ubyte --output_file ~/caffe2/caffe2/python/tutorials/tutorial_data/mnist/mnist-test-nchw-leveldb

这段代码实现和上面的一样的功能

# 这部分将你的图像转换成leveldb
current_folder = os.getcwd()
data_folder = os.path.join(current_folder, 'tutorial_data', 'mnist')
root_folder = os.path.join(current_folder, 'tutorial_files', 'tutorial_mnist')
image_file_train = os.path.join(data_folder, "train-images-idx3-ubyte")
label_file_train = os.path.join(data_folder, "train-labels-idx1-ubyte")
image_file_test = os.path.join(data_folder, "t10k-images-idx3-ubyte")
label_file_test = os.path.join(data_folder, "t10k-labels-idx1-ubyte") def DownloadDataset(url, path):
import requests, zipfile, StringIO
print "Downloading... ", url, " to ", path
r = requests.get(url, stream=True)
z = zipfile.ZipFile(StringIO.StringIO(r.content))
z.extractall(path)
if not os.path.exists(data_folder):
os.makedirs(data_folder)
if not os.path.exists(label_file_train):
DownloadDataset("https://s3.amazonaws.com/caffe2/datasets/mnist/mnist.zip", data_folder) def GenerateDB(image, label, name):
name = os.path.join(data_folder, name)
print 'DB name: ', name
syscall = "/usr/local/binaries/make_mnist_db --channel_first --db leveldb --image_file " + image + " --label_file " + label + " --output_file " + name
print "Creating database with: ", syscall
os.system(syscall) # 生成leveldb
GenerateDB(image_file_train, label_file_train, "mnist-train-nchw-leveldb")
GenerateDB(image_file_test, label_file_test, "mnist-test-nchw-leveldb") if os.path.exists(root_folder):
print("Looks like you ran this before, so we need to cleanup those old workspace files...")
shutil.rmtree(root_folder) os.makedirs(root_folder)
workspace.ResetWorkspace(root_folder) print("training data folder:"+data_folder)
print("workspace root folder:"+root_folder)
模型创建

CNNModelHelper封装了很多函数,它能将参数初始化和真实的计算分成两个网络中实现。底层实现是,CNNModelHelper有两个网络param_init_netnet,这两个网络分别记录着初始化网络和主网络。为了模块化,我们将模型分割成多个不同的部分。

- 数据输入(AddInput 函数)

- 主要的计算部分(AddLeNetModel 函数)

- 训练部分-梯度操作,参数更新等等 (AddTrainingOperators函数)

- 记录数据部分,比如需要展示训练过程的相关数据(AddBookkeepingOperators 函数)

  1. AddInput会从一个DB中载入数据。我们将MNIST保存为像素值,并且我们用浮点数进行计算,所以我们的数据也必须是Float类型。为了数值稳定性,我们将图像数据归一化到[0,1]而不是[0,255]。注意,我们做的事in-place操作,会覆盖原来的数据,因为我们不需要归一化前的数据。准备数据这个操作,在后向传播时,不需要进行梯度计算。所以我们使用StopGradient来告诉梯度生成器:“不用将梯度传递给我。”
def AddInput(model, batch_size, db, db_type):
# 载入数据和标签
data_uint8, label = model.TensorProtosDBInput(
[], ["data_uint8", "label"], batch_size=batch_size,
db=db, db_type=db_type)
# 转化为 float
data = model.Cast(data_uint8, "data", to=core.DataType.FLOAT)
#归一化到 [0,1]
data = model.Scale(data, data, scale=float(1./256))
# 后向传播不需要梯度
data = model.StopGradient(data, data)
return data, label
print("Input function created.")

输出

Input function created.
  1. AddLeNetModel输出softmax.
def AddLeNetModel(model, data):
conv1 = model.Conv(data, 'conv1', 1, 20, 5)
pool1 = model.MaxPool(conv1, 'pool1', kernel=2, stride=2)
conv2 = model.Conv(pool1, 'conv2', 20, 50, 5)
pool2 = model.MaxPool(conv2, 'pool2', kernel=2, stride=2)
fc3 = model.FC(pool2, 'fc3', 50 * 4 * 4, 500)
fc3 = model.Relu(fc3, fc3)
pred = model.FC(fc3, 'pred', 500, 10)
softmax = model.Softmax(pred, 'softmax')
return softmax
print("Model function created.")
Model function created.
  1. AddTrainingOperators函数函数用于添加训练操作。

    AddAccuracy函数输出模型的准确率,我们会在下一个函数使用它来跟踪准确率。
def AddAccuracy(model, softmax, label):
accuracy = model.Accuracy([softmax, label], "accuracy")
return accuracy
print("Accuracy function created.")
Accuracy function created.

首先添加一个op:LabelCrossEntropy,用于计算输入和lebel的交叉熵。这个操作在得到softmax后和计算loss前。输入是[softmax, label],输出交叉熵用xent表示。

xent = model.LabelCrossEntropy([softmax, label], 'xent')

AveragedLoss将交叉熵作为输入,并计算出平均损失loss

loss = model.AveragedLoss(xent, "loss")

AddAccuracy为了记录训练过程,我们使用AddAccuracy 函数来计算。

AddAccuracy(model, softmax, label)

接下来这步至关重要:我们把所有梯度计算添加到模型上。梯度是根据我们前面的loss计算得到的。

model.AddGradientOperators([loss])

然后进入迭代

ITER = model.Iter("iter")

更新学习率使用策略是lr = base_lr * (t ^ gamma) ,注意我们是在最小化,所以基础学率是负数,这样我们才能向山下走。

LR = model.LearningRate(ITER, "LR", base_lr=-0.1, policy="step", stepsize=1, gamma=0.999 )
#ONE是一个在梯度更新阶段用的常量。只需要创建一次,并放在param_init_net中。
ONE = model.param_init_net.ConstantFill([], "ONE", shape=[1], value=1.0)

现在对于每一和参数,我们做梯度更新。注意我们如何获取每个参数的梯度——CNNModelHelper保持跟踪这些信息。更新的方式很简单,是简单的相加: param = param + param_grad * LR

for param in model.params:
param_grad = model.param_to_grad[param]
model.WeightedSum([param, ONE, param_grad, LR], param)

我们需要每隔一段时间检查参数。这可以通过Checkpoint 操作。这个操作有一个参数every表示每多少次迭代进行一次这个操作,防止太频繁去检查。这里,我们每20次迭代进行一次检查。

model.Checkpoint([ITER] + model.params, [],
db="mnist_lenet_checkpoint_%05d.leveldb",
db_type="leveldb", every=20)

然后我们得到整个AddTrainingOperators函数如下:

def AddTrainingOperators(model, softmax, label):
# 计算交叉熵
xent = model.LabelCrossEntropy([softmax, label], 'xent')
# 计算loss
loss = model.AveragedLoss(xent, "loss")
#跟踪模型的准确率
AddAccuracy(model, softmax, label)
#添加梯度操作
model.AddGradientOperators([loss])
# 梯度下降
ITER = model.Iter("iter")
# 学习率
LR = model.LearningRate(
ITER, "LR", base_lr=-0.1, policy="step", stepsize=1, gamma=0.999 )
ONE = model.param_init_net.ConstantFill([], "ONE", shape=[1], value=1.0)
# 梯度更新
for param in model.params:
param_grad = model.param_to_grad[param]
model.WeightedSum([param, ONE, param_grad, LR], param)
# 每迭代20次检查一次
# you may need to delete tutorial_files/tutorial-mnist to re-run the tutorial
model.Checkpoint([ITER] + model.params, [],
db="mnist_lenet_checkpoint_%05d.leveldb",
db_type="leveldb", every=20)
print("Training function created.")
Training function created.
  1. **AddBookkeepingOperators **添加一些记录操作,这些操作不会影响训练过程。他们只是收集数据和打印出来或者写到log里面去。
def AddBookkeepingOperators(model):
# 输出 blob的内容. to_file=1 表示输出到文件,文件保存的路径是 root_folder/[blob name]
model.Print('accuracy', [], to_file=1)
model.Print('loss', [], to_file=1)
# Summarizes 给出一些参数比如均值,方差,最大值,最小值
for param in model.params:
model.Summarize(param, [], to_file=1)
model.Summarize(model.param_to_grad[param], [], to_file=1)
print("Bookkeeping function created")
  1. 定义网络

    现在让我们将真正创建模型。前面写的函数将真正被执行。回忆我们四步。
-data input
-main computation
-training
-bookkeeping

在我们读进数据前,我们需要定义我们训练模型。我们将使用到前面定义的所有东西。我们将在MNIST数据集上使用NCHW的储存顺序。

train_model = cnn.CNNModelHelper(order="NCHW", name="mnist_train")
data, label = AddInput(train_model, batch_size=64,
db=os.path.join(data_folder, 'mnist-train-nchw-leveldb'), db_type='leveldb')
softmax = AddLeNetModel(train_model, data)
AddTrainingOperators(train_model, softmax, label)
AddBookkeepingOperators(train_model)
# Testing model. 我们设置batch=100,这样迭代100次就能覆盖10000张测试图像
# 对于测试模型,我们需要数据输入 ,LeNetModel,和准确率三部分
#注意到init_params 设置为False,是因为我们从训练网络获取参数。
test_model = cnn.CNNModelHelper(order="NCHW", name="mnist_test", init_params=False)
data, label = AddInput(test_model, batch_size=100,
db=os.path.join(data_folder, 'mnist-test-nchw-leveldb'), db_type='leveldb')
softmax = AddLeNetModel(test_model, data)
AddAccuracy(test_model, softmax, label) # Deployment model. 我们仅需要LeNetModel 部分
deploy_model = cnn.CNNModelHelper(order="NCHW", name="mnist_deploy", init_params=False)
AddLeNetModel(deploy_model, "data")
#你可能好奇deploy_model的param_init_net 发生了什么,在这节中,我们没有使用它,
#因为在deployment 阶段,我们不会随机初始化参数,而是从本地载入。
print('Created training and deploy models.')

现在让我们用caffe2的可视化工具看看Training和Deploy模型是什么样子的。如果下面的命令运行失败,那可能是因为你的机器没有安装graphviz。可以用如下命令安装:

sudo yum install graphviz #ubuntu 用户sudo apt-get install graphviz

图看起来可能很小,右键点击在新的窗口打开就能看清。

from IPython import display
graph = net_drawer.GetPydotGraph(train_model.net.Proto().op, "mnist", rankdir="LR")
display.Image(graph.create_png(), width=800)



现在上图展示了训练阶段的一切东西。白色的节点是blobs,绿色的矩形节点是operators.你可能留意到大规模的像火车轨道一样的平行线。这些依赖关系从前前向传播的blobs指向到后向传播的操作。

让我们仅仅展示必要的依赖关系和操作。如果你细心看,你会发现,左半图式前向传播,右半图式后向传播,在最右边是一系列参数更新操作和summarization .

graph = net_drawer.GetPydotGraphMinimal(
train_model.net.Proto().op, "mnist", rankdir="LR", minimal_dependency=True)
display.Image(graph.create_png(), width=800)



现在我们可以通过Python来跑起网络,记住,当我们跑起网络时,我们随时可以从网络中拿出blob数据,下面先来展示下如何进行这个操作。

我们重申一下,CNNModelHelper 类目前没有执行任何东西。他目前做的仅仅是声明网络,只是简单的创建了protocol buffers.例如我们可以展示网络一部分序列化的protobuf。

print(str(train_model.param_init_net.Proto())[:400] + '\n...')

当然,我们也可以把protobuf写到本地磁盘中去,这样可以方便的查看。你会发现这些protobuf和以前的Caffe网络定义很相似。

with open(os.path.join(root_folder, "train_net.pbtxt"), 'w') as fid:
fid.write(str(train_model.net.Proto()))
with open(os.path.join(root_folder, "train_init_net.pbtxt"), 'w') as fid:
fid.write(str(train_model.param_init_net.Proto()))
with open(os.path.join(root_folder, "test_net.pbtxt"), 'w') as fid:
fid.write(str(test_model.net.Proto()))
with open(os.path.join(root_folder, "test_init_net.pbtxt"), 'w') as fid:
fid.write(str(test_model.param_init_net.Proto()))
with open(os.path.join(root_folder, "deploy_net.pbtxt"), 'w') as fid:
fid.write(str(deploy_model.net.Proto()))
print("Protocol buffers files have been created in your root folder: "+root_folder)

现在,让我们进入训练过程。我们使用Python来训练。当然也可以使用C++接口来训练。这留在另一个教程讨论。

训练网络

首先,初始化网络是必须的

workspace.RunNetOnce(train_model.param_init_net)

接着我们创建训练网络并,加载到workspace中去。

workspace.CreateNet(train_model.net)

然后设置迭代200次,并把准确率和loss保存到两个np矩阵中去

total_iters = 200
accuracy = np.zeros(total_iters)
loss = np.zeros(total_iters)

网络和跟踪准确loss都配置好后,我们循环调用workspace.RunNet200次,需要传入的参数是train_model.net.Proto().name.每一次迭代,我们计算准确率和loss。

for i in range(total_iters):
workspace.RunNet(train_model.net.Proto().name)
accuracy[i] = workspace.FetchBlob('accuracy')
loss[i] = workspace.FetchBlob('loss')

最后我们可以用pyplot画出结果。

# 参数初始化只需跑一次
workspace.RunNetOnce(train_model.param_init_net)
# 创建网络
workspace.CreateNet(train_model.net)
#设置迭代数和跟踪accuracy & loss
total_iters = 200
accuracy = np.zeros(total_iters)
loss = np.zeros(total_iters)
# 我们迭代200次
for i in range(total_iters):
workspace.RunNet(train_model.net.Proto().name)
accuracy[i] = workspace.FetchBlob('accuracy')
loss[i] = workspace.FetchBlob('loss')
# 迭代完画出结果
pyplot.plot(loss, 'b')
pyplot.plot(accuracy, 'r')
pyplot.legend(('Loss', 'Accuracy'), loc='upper right')



现我们可以进行抽取数据和预测了

#数据可视化
pyplot.figure()
data = workspace.FetchBlob('data')
_ = visualize.NCHW.ShowMultiple(data)
pyplot.figure()
softmax = workspace.FetchBlob('softmax')
_ = pyplot.plot(softmax[0], 'ro')
pyplot.title('Prediction for the first image')





还记得我们创建的test net吗?我们将跑一遍test net测试准确率。**注意,虽然test_model的参数来自train_model,但是仍然需要初始化test_model.param_init_net **。这次,我们只需要追踪准确率,并且只迭代100次。

workspace.RunNetOnce(test_model.param_init_net)
workspace.CreateNet(test_model.net)
test_accuracy = np.zeros(100)
for i in range(100):
workspace.RunNet(test_model.net.Proto().name)
test_accuracy[i] = workspace.FetchBlob('accuracy')
pyplot.plot(test_accuracy, 'r')
pyplot.title('Acuracy over test batches.')
print('test_accuracy: %f' % test_accuracy.mean())



译者注:这里译者不是很明白,test_model是如何从train_model获取参数的?有明白的小伙伴希望能在评论区分享一下。

MNIST教程就此结束。希望本教程能向你展示一些Caffe2的特征。

转载请注明出处:http://www.jianshu.com/c/cf07b31bb5f2

Caffe2 手写字符识别(MNIST - Create a CNN from Scratch)[8]的更多相关文章

  1. 仅用200个样本就能得到当前最佳结果:手写字符识别新模型TextCaps

    由于深度学习近期取得的进展,手写字符识别任务对一些主流语言来说已然不是什么难题了.但是对于一些训练样本较少的非主流语言来说,这仍是一个挑战性问题.为此,本文提出新模型TextCaps,它每类仅用200 ...

  2. TensorFlow 入门之手写识别(MNIST) softmax算法

    TensorFlow 入门之手写识别(MNIST) softmax算法 MNIST flyu6 softmax回归 softmax回归算法 TensorFlow实现softmax softmax回归算 ...

  3. TensorFlow 入门之手写识别(MNIST) softmax算法 二

    TensorFlow 入门之手写识别(MNIST) softmax算法 二 MNIST Fly softmax回归 softmax回归算法 TensorFlow实现softmax softmax回归算 ...

  4. TensorFlow 入门之手写识别(MNIST) 数据处理 一

    TensorFlow 入门之手写识别(MNIST) 数据处理 一 MNIST Fly softmax回归 准备数据 解压 与 重构 手写识别入门 MNIST手写数据集 图片以及标签的数据格式处理 准备 ...

  5. 利用Tensorflow实现手写字符识别

    模式识别领域应用机器学习的场景非常多,手写识别就是其中一种,最简单的数字识别是一个多类分类问题,我们借这个多类分类问题来介绍一下google最新开源的tensorflow框架,后面深度学习的内容都会基 ...

  6. 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST

    目录 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST 下载数据集 加载数据集 构建神经网络 反向传播(BP)算法 进行预测 F1验证 总结 参考 数据挖掘入门系 ...

  7. 利用CNN神经网络实现手写数字mnist分类

    题目: 1)In the first step, apply the Convolution Neural Network method to perform the training on one ...

  8. Tensorflow学习笔记3:卷积神经网络实现手写字符识别

    # -*- coding:utf-8 -*- import tensorflow as tf from tensorflow.examples.tutorials.mnist import input ...

  9. OpenCV OpenGL手写字符识别

    另外一篇文章地址:这个比较详细,但是程序略显简单,现在这个程序是比较复杂的 http://blog.csdn.net/wangyaninglm/article/details/17091901 整个项 ...

随机推荐

  1. [洛谷P4463] calc (生成函数)

    首先注意到题目中 \(a\) 数组是有序的,那我们只用算有序的方案乘上 \(n!\) 即可. 而此时的答案显然 \[Ans=[x^n](1+x)(1+2x)\dots (1+Ax)=\prod_{i= ...

  2. 深入delphi编程理解之消息(一)WINDOWS原生窗口编写及消息处理过程

    通过以sdk方式编制windows窗口程序,对理解windows消息驱动机制和delphi消息编程有很大的帮助. sdk编制windows窗口程序的步骤: 1.对TWndClass对象进行赋值; 2. ...

  3. django+vue基础框架:django one对one格式

    创建app:python manage.py startapp  app01(这里的app01是指名字,可以是a或b等等) 生成迁移文件:python manage.py makemigrations ...

  4. IDEA类和方法注释自动生成

    一.生成类注释 1.打开Preferences-->Editor-->File and Code Templates,右侧选择Filestab页,找到Class.Interface,可以看 ...

  5. 超好用的免费Redis客户端

    Redis这款基于内存的键值对(key-vlaue)数据库,自带了一个基于命令行式的管理工具redis-cli,但是使用起来并不方便.虽然现在有了许多的图形化管理工具,有些需要收费,有些不好用.最终还 ...

  6. linux配置放火墙开放端口

    vi /etc/sysconfig/iptables -A INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEPT(允许80端口通过防火 ...

  7. 深入浅出Oracle:DBA入门、进阶与诊断案例 PDF 下载

    网盘地址: 链接:https://pan.baidu.com/s/1tMFoNSUW7ICKOtmSQ5ZazA 提取码:dbnc

  8. $.isEmptyObject() 判断对象是否为空

    $.isEmptyObject(obj):为空 返回true不为空 返回 false: isEmptyObject: function( obj ) { var name; for ( name in ...

  9. 【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第56章       STM32H7的DMA2D应用之刷色块, ...

  10. Net Core解决ZipFile解压中文出现乱码

    一.在main方法中添加 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 二.解压添加 //sourceArchiveFi ...