接着caffe2 教程记录五,这个是第六篇
##7.创建自己的数据集
如何创建自己的数据集?
因此Caffe2使用二进制DB格式来存储我们想要训练模型的数据。 Caffe2 DB是键值存储的美化名称,其中键通常是随机的,因此批次大约是i.i.d,但是这些值是真实的:它们包含您希望训练算法摄取的特定数据格式的序列化字符串。因此,存储的数据库看起来(语义上)像这样:
key1 value1 key2 value2 key3 value3 ...
对于DB,它将键和值视为字符串,但您可能需要结构化内容。一种方法是使用TensorProtos协议缓冲区:它基本上包含张量(也称为多维数组)以及张量数据类型和形状信息。然后,可以使用TensorProtosDBInput运算符将数据加载到SGD训练方式中。
在这里,我们将向您展示如何创建自己的数据集的一个示例。为此,我们将使用UCI Iris数据集 - 这是一个非常流行的经典数据集,用于分类虹膜花。它包含4个代表花朵尺寸的实值特征,并将东西分为3种类型的虹膜花。数据集可以在这里(here)下载 。
# First let's import some necessities
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
%matplotlib inline
import urllib2 # for downloading the dataset from the web.
import numpy as np
from matplotlib import pyplot
from StringIO import StringIO
from caffe2.python import core, utils, workspace
from caffe2.proto import caffe2_pb2
f = urllib2.urlopen('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data')
raw_data = f.read()
print('Raw data looks like this:')
print(raw_data[:100] + '...')
输出的结果:
# load the features to a feature matrix.# load
features = np.loadtxt(StringIO(raw_data), dtype=np.float32, delimiter=',', usecols=(0, 1, 2, 3))
# load the labels to a feature matrix
label_converter = lambda s : {'Iris-setosa':0, 'Iris-versicolor':1, 'Iris-virginica':2}[s]
labels = np.loadtxt(StringIO(raw_data), dtype=np.int, delimiter=',', usecols=(4,), converters={4: label_converter})
在我们进行培训之前,通常有益的一件事是将数据集分成培训和测试。 在这种情况下,让我们随机抽取数据,使用前100个数据点进行训练,剩下的50个进行测试。 对于更复杂的方法,您可以使用例如 交叉验证将您的数据集分成多个培训和测试拆分。 阅读更多关于交叉验证的信息。
random_index = np.random.permutation(150)
features = features[random_index]
labels = labels[random_index]
train_features = features[:100]
train_labels = labels[:100]
test_features = features[100:]
test_labels = labels[100:]
# Let's plot the first two features together with the label.
# Remember, while we are plotting the testing feature distribution
# here too, you might not be supposed to do so in real research,
# because one should not peek into the testing data.
legend = ['rx', 'b+', 'go']
pyplot.title("Training data distribution, feature 0 and 1")
for i in range(3):
pyplot.plot(train_features[train_labels==i, 0], train_features[train_labels==i, 1], legend[i])
pyplot.figure()
pyplot.title("Testing data distribution, feature 0 and 1")
for i in range(3):
pyplot.plot(test_features[test_labels==i, 0], test_features[test_labels==i, 1], legend[i])
输出结果:
现在,正如所承诺的那样,让我们把东西放到Caffe2数据库中。 在这个DB中,会发生的是我们将使用“train_xxx”作为键,并使用TensorProtos对象为每个数据点存储两个张量:一个作为特征,一个作为标签。 我们将使用Caffe2的Python DB接口来实现。
# First, let's see how one can construct a TensorProtos protocol buffer from numpy arrays.
feature_and_label = caffe2_pb2.TensorProtos()
feature_and_label.protos.extend([
utils.NumpyArrayToCaffe2Tensor(features[0]),
utils.NumpyArrayToCaffe2Tensor(labels[0])])
print('This is what the tensor proto looks like for a feature and its label:')
print(str(feature_and_label))
print('This is the compact string that gets written into the db:')
print(feature_and_label.SerializeToString())
输出结果:
# Now, actually write the db.
def write_db(db_type, db_name, features, labels):
db = core.C.create_db(db_type, db_name, core.C.Mode.write)
transaction = db.new_transaction()
for i in range(features.shape[0]):
feature_and_label = caffe2_pb2.TensorProtos()
feature_and_label.protos.extend([
utils.NumpyArrayToCaffe2Tensor(features[i]),
utils.NumpyArrayToCaffe2Tensor(labels[i])])
transaction.put(
'train_%03d'.format(i),
feature_and_label.SerializeToString())
# Close the transaction, and then close the db.
del transaction
del db
write_db("minidb", "iris_train.minidb", train_features, train_labels)
write_db("minidb", "iris_test.minidb", test_features, test_labels)
现在,让我们创建一个非常简单的网络,它只包含一个TensorProtosDBInput运算符,以展示我们如何从我们创建的数据库加载数据。 对于培训,您可能希望执行更复杂的操作:创建网络,训练网络,获取模型以及运行预测服务。 为此,您可以查看MNIST教程以获取详细信息。
net_proto = core.Net("example_reader")
dbreader = net_proto.CreateDB([], "dbreader", db="iris_train.minidb", db_type="minidb")
net_proto.TensorProtosDBInput([dbreader], ["X", "Y"], batch_size=16)
print("The net looks like this:")
print(str(net_proto.Proto()))
输出结果:
workspace.CreateNet(net_proto)
输出:true
# Let's run it to get batches of features.
workspace.RunNet(net_proto.Proto().name)
print("The first batch of feature is:")
print(workspace.FetchBlob("X"))
print("The first batch of label is:")
print(workspace.FetchBlob("Y"))
# Let's run again.
workspace.RunNet(net_proto.Proto().name)
print("The second batch of feature is:")
print(workspace.FetchBlob("X"))
print("The second batch of label is:")
print(workspace.FetchBlob("Y"))
输出的结果:
##8.MNIST - 手写识别
在本教程中,我们将向您展示如何训练小型卷积神经网络(CNN)。 我们将在MNIST数据集上训练模型,该数据集由标记的手写数字组成。 来自数据集的每个样本是单个手写数字的28x28像素灰度(1通道)图像,标签是0到9的整数。
对于我们的模型,我们将构建LeNet模型,其中Sigmoid激活被ReLU取代。 本教程还将提供使用简单的多层感知器(MLP)模型的选项,该模型不包括任何卷积运算符。 您可以通过下面的USE_LENET_MODEL标志选择所需的模型。
在开始之前,请注意我们将在本教程中使用ModelHelper类。 这个类可以帮助我们自然地处理参数初始化,并使我们不必分别维护param_init_net和net对象。
在我们开始之前,让我们导入一些必须的依赖。
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
%matplotlib inline
from matplotlib import pyplot
import numpy as np
import os
import shutil
import operator
import caffe2.python.predictor.predictor_exporter as pe
from caffe2.python import (
brew,
core,
model_helper,
net_drawer,
optimizer,
visualize,
workspace,
)
# If you would like to see some really detailed initializations,
# you can change --caffe2_log_level=0 to --caffe2_log_level=-1
core.GlobalInit(['caffe2', '--caffe2_log_level=0'])
print("Necessities imported!")
# If True, use the LeNet CNN model
# If False, a multilayer perceptron model is used
USE_LENET_MODEL = True
数据下载
我们将处理本教程所需的数据下载,并通过在用户主目录中设置名为caffe2_notebooks的顶级本地文件夹来跟踪培训期间的统计信息。 对于数据下载,我们需要设置tutorial_data文件夹,对于训练统计信息,我们设置了tutorial_files文件夹。 如果您已经运行本教程,那么您应该已经拥有这些文件夹。 执行以下单元格时,将确保在tutorial_data文件夹中存在MNIST数据集的训练和测试lmdb数据库。
# This section preps your image and test set in a lmdb database
def DownloadResource(url, path):
'''Downloads resources from s3 by url and unzips them to the provided path'''
import requests, zipfile, StringIO
print("Downloading... {} to {}".format(url, path))
r = requests.get(url, stream=True)
z = zipfile.ZipFile(StringIO.StringIO(r.content))
z.extractall(path)
print("Completed download and extraction.")
# Setup the paths for the necessary directories
current_folder = os.path.join(os.path.expanduser('~'), 'caffe2_notebooks')
data_folder = os.path.join(current_folder, 'tutorial_data', 'mnist')
root_folder = os.path.join(current_folder, 'tutorial_files', 'tutorial_mnist')
db_missing = False
# Check if the data folder already exists
if not os.path.exists(data_folder):
os.makedirs(data_folder)
print("Your data folder was not found!! This was generated: {}".format(data_folder))
# Check if the training lmdb exists in the data folder
if os.path.exists(os.path.join(data_folder,"mnist-train-nchw-lmdb")):
print("lmdb train db found!")
else:
db_missing = True
# Check if the testing lmdb exists in the data folder
if os.path.exists(os.path.join(data_folder,"mnist-test-nchw-lmdb")):
print("lmdb test db found!")
else:
db_missing = True
# Attempt the download of the db if either was missing
if db_missing:
print("one or both of the MNIST lmbd dbs not found!!")
db_url = "http://download.caffe2.ai/databases/mnist-lmdb.zip"
try:
DownloadResource(db_url, data_folder)
except Exception as ex:
print("Failed to download dataset. Please download it manually from {}".format(db_url))
print("Unzip it and place the two database folders here: {}".format(data_folder))
raise ex
# Clean up statistics from any old runs
if os.path.exists(root_folder):
print("Looks like you ran this before, so we need to cleanup those old 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)
运行出现下面结果:
如果在最后一步中找不到数据库,请下载 download the MNIST lmdb database 或查看有关如何从MNIST数据集创建数据库的数据集和数据库笔记本 datasets and databases notebook 。
模型构建
我们将使用ModelHelper类来表示我们的主模型,并使用brew模块以及其他普通的Caffe2运算符来构建我们的模型。 ModelHelper是一个特殊的类,它存储有关参数初始化,网络结构,参数名称以及稍后映射到渐变的大量信息。 我们将在下面的其他地方看到它如何与brew一起使用。
为避免混淆,请注意model.MyOperator是model.net.MyOperator的语法糖,它将相应的MyOperator运算符添加到model.net。
brew 简介
brew是一组helper 函数,旨在简化向我们的模型添加复杂逻辑的过程(有关更多信息,请单击此处)。例如,当我们想要添加参数初始化以及计算步骤时,brew会派上用场。现在,让我们更详细地探讨这个问题。
brew模块具有一组包装函数,可自动将参数初始化和实际计算分成两个网络。在引擎盖下,ModelHelper对象有两个底层网络param_init_net和net,它们分别记录初始化网络和主网络。 model.params还跟踪参数名称。
高级流程
为了模块化,我们将模型的构造分为不同的部分:
(1)数据输入部分(AddInput函数)
(2)主要计算部分(AddModel函数)
(3)训练部分 - 添加梯度算子,优化算法等(AddTrainingOperators函数)
(4)bookkeeping 部分,我们只打印出统计检查。 (AddBookkeepingOperators函数)
添加输入
AddInput将从DB加载数据(特别是上面下载的lmdbs)。加载的MNIST数据以NCHW顺序存储为像素值,因此在批处理后,这将为我们提供形状[batch_size,num_channels,width,height]的数据。更具体地说,由于我们的MNIST数据是尺寸为28x28px的8位(uint8)灰度图像,因此我们作为网络输入的数据形状为[batch_size,1,28,28],我们的标签类型为int with shape [batch_size] 。
由于我们要进行浮点计算,我们将把数据转换为浮点数据类型。为了获得更好的数值稳定性,我们将其缩小为[0,1],而不是在[0,255]范围内表示数据。请注意,我们正在为此运算符进行就地计算,因为我们不需要预先缩放的数据。此外,在计算反向传递时,我们应该指定我们不需要数据准备部分的梯度计算。 StopGradient就是这样做的:在前向传递中它什么都不做,而在后向传递中它所做的就是告诉梯度生成器“渐变不需要通过这里”。
def AddInput(model, batch_size, db, db_type):
### load the data from db - Method 1 using brew
#data_uint8, label = brew.db_input(
# model,
# blobs_out=["data_uint8", "label"],
# batch_size=batch_size,
# db=db,
# db_type=db_type,
#)
### load the data from db - Method 2 using TensorProtosDB
data_uint8, label = model.TensorProtosDBInput(
[], ["data_uint8", "label"], batch_size=batch_size,
db=db, db_type=db_type)
# cast the data to float
data = model.Cast(data_uint8, "data", to=core.DataType.FLOAT)
# scale data from [0,255] down to [0,1]
data = model.Scale(data, data, scale=float(1./256))
# don't need the gradient for the backward pass
data = model.StopGradient(data, data)
return data, label
MLP模型定义
注意:这是标志USE_LENET_MODEL = False时使用的模型
现在我们要构建自己的模型。 输入将是我们的数据blob(X
并且输出将是长度为10的向量,其包含网络对MNIST中的10个可能数字中的每一个的预测。
在这个多层感知器(MLP)架构中,将使用ReLU激活功能,并定义为:
请看上面图片公式。
回想一下,MLP的每一层只是一些输入数据和权重矩阵之间的矩阵乘法(W),然后添加偏见(b)。 在机器学习中,这是通过一个完全连接的层实现的,该层在Caffe2中恰当地命名为FC。 然后将该乘法的结果传递给非线性激活函数(ReLU)。 每个激活函数的输出作为输入数据传递到下一层,并通过各层重复此过程,如下所示:
请看上面的图片公式。
一旦数据在前向传递中通过网络传播,我们将使用Softmax运算符将每个数字的分数转换为概率。 通常,分数总和为1(即p0 + ... + p9 = 1.0 并且没有单个分数大于或等于1(即0 <= pi <= 1.0)。 可以在此处 http://deeplearning.net/tutorial/mlp.html 找到MLP的更详细描述。
在这个功能中,我们将首次使用Brew。 在引擎盖下,当我们调用brew.fc(model,layer,...)时,会发生以下情况:
通过调用model.net.FC([layer,W,b],...)将FC运算符添加到model.net W的初始化 和b被添加到model.param_init_net
希望这使得brew的便利性更加清晰!
# Function to construct a MLP neural network
# The input 'model' is a model helper and 'data' is the input data blob's name
def AddMLPModel(model, data):
size = 28 * 28 * 1
sizes = [size, size * 2, size * 2, 10]
layer = data
for i in range(len(sizes) - 1):
layer = brew.fc(model, layer, 'dense_{}'.format(i), dim_in=sizes[i], dim_out=sizes[i + 1])
layer = brew.relu(model, layer, 'relu_{}'.format(i))
softmax = brew.softmax(model, layer, 'softmax')
return softmax
LeNet模型定义
注意:这是标志USE_LENET_MODEL = True时使用的模型
下面是另一个可能(并且非常强大)的架构,称为LeNet。 与MLP模型的主要区别在于LeNet是卷积神经网络(CNN),因此使用卷积层(Conv),最大池化层 (MaxPool), ReLUs,和完全连接(FC)层。 有关CNN如何工作的完整解释超出了本教程的范围,但这里有一些好的资源供好奇的读者使用:
- Stanford cs231 CNNs for Visual Recognition (Recommended) 斯坦福cs231视听识别CNN(推荐)
- Explanation of Kernels in Image Processing 图像处理中的内核解释
- Convolutional Arithmetic Tutorial 卷积算术教程
注意,此功能也使用Brew。 但是,这次我们添加的不仅仅是FC和Softmax图层。
def AddLeNetModel(model, data):
'''
This part is the standard LeNet model: from data to the softmax prediction.
For each convolutional layer we specify dim_in - number of input channels
and dim_out - number or output channels. Also each Conv and MaxPool layer changes the
image size. For example, kernel of size 5 reduces each side of an image by 4.
While when we have kernel and stride sizes equal 2 in a MaxPool layer, it divides
each side in half.
'''
# Image size: 28 x 28 -> 24 x 24
conv1 = brew.conv(model, data, 'conv1', dim_in=1, dim_out=20, kernel=5)
# Image size: 24 x 24 -> 12 x 12
pool1 = brew.max_pool(model, conv1, 'pool1', kernel=2, stride=2)
# Image size: 12 x 12 -> 8 x 8
conv2 = brew.conv(model, pool1, 'conv2', dim_in=20, dim_out=50, kernel=5)
# Image size: 8 x 8 -> 4 x 4
pool2 = brew.max_pool(model, conv2, 'pool2', kernel=2, stride=2)
# 50 * 4 * 4 stands for dim_out from previous layer multiplied by the image size
# Here, the data is flattened from a tensor of dimension 50x4x4 to a vector of length 50*4*4
fc3 = brew.fc(model, pool2, 'fc3', dim_in=50 * 4 * 4, dim_out=500)
relu3 = brew.relu(model, fc3, 'relu3')
# Last FC Layer
pred = brew.fc(model, relu3, 'pred', dim_in=500, dim_out=10)
# Softmax Layer
softmax = brew.softmax(model, pred, 'softmax')
return softmax
下面的AddModel函数允许我们轻松地从MLP切换到LeNet模型。 只需更改笔记本顶部的USE_LENET_MODEL,然后重新运行整个过程。
def AddModel(model, data):
if USE_LENET_MODEL:
return AddLeNetModel(model, data)
else:
return AddMLPModel(model, data)
准确度层
下面的AddAccuracy函数为模型添加了精度运算符。 它使用softmax分数和输入训练标签(记住这是一种监督学习技术)来报告当前训练批次的准确度分数。 通常不建议在训练期间对准确度分数过度兴奋,因为该值没有给出关于泛化性能的指示,但是训练准确性确实给出了模型是否正在学习的想法。 通过培训过程跟踪准确性也将让我们建立一个很好的情节来总结培训运行。
def AddAccuracy(model, softmax, label):
"""Adds an accuracy op to the model"""
accuracy = brew.accuracy(model, [softmax, label], "accuracy")
return accuracy
添加培训操作员
下一个函数AddTrainingOperators将训练操作符添加到模型中。 请按照内联评论了解所有步骤。 总而言之,这是我们指定模型如何计算损失以及特定优化算法及其超参数的地方。 在本教程中,我们将使用vanilla SGD算法,该算法使用优化器模块中的build_sgd辅助函数构建。 作为此函数的结果,模型对象包含诸如参数名称(model.param)之类的信息以及从参数名称到相应渐变的映射。
def AddTrainingOperators(model, softmax, label):
"""Adds training operators to the model."""
# Compute cross entropy between softmax scores and labels
xent = model.LabelCrossEntropy([softmax, label], 'xent')
# Compute the expected loss
loss = model.AveragedLoss(xent, "loss")
# Track the accuracy of the model
AddAccuracy(model, softmax, label)
# Use the average loss we just computed to add gradient operators to the model
model.AddGradientOperators([loss])
# Specify the optimization algorithm
optimizer.build_sgd(
model,
base_learning_rate=0.1,
policy="step",
stepsize=1,
gamma=0.999,
)
添加Bookkeeping(簿记) Operators(操作符)
以下函数AddBookkeepingOperations添加了一些我们稍后可以检查的簿记操作符。 这些运算符不会影响训练过程:它们仅收集统计信息并将其打印到文件或日志中。 因此,这不是必要的步骤,但记账将使我们能够在培训结束后审查培训结果。
def AddBookkeepingOperators(model):
"""This adds a few bookkeeping operators that we can inspect later.
These operators do not affect the training procedure: they only collect
statistics and prints them to file or to logs.
"""
# Print basically prints out the content of the blob. to_file=1 routes the
# printed output to a file. The file is going to be stored under
# root_folder/[blob name]
model.Print('accuracy', [], to_file=1)
model.Print('loss', [], to_file=1)
# Summarizes the parameters. Different from Print, Summarize gives some
# statistics of the parameter, such as mean, std, min and max.
for param in model.params:
model.Summarize(param, [], to_file=1)
model.Summarize(model.param_to_grad[param], [], to_file=1)
# Now, if we really want to be verbose, we can summarize EVERY blob
# that the model produces; it is probably not a good idea, because that
# is going to take time - summarization do not come for free. For this
# demo, we will only show how to summarize the parameters and their
# gradients.
构建模型
现在我们已经定义了函数,让我们实际创建用于训练,测试和模拟部署的模型。 如果您在下面看到警告消息,请不要惊慌。 我们之前建立的功能现在将被执行。 请记住我们正在做的四个步骤:
(1) data input 数据输入
(2) main computation 主要计算
(3) training 训练
(4) bookkeeping 簿记
#### Train Model
# Specify the data will be input in NCHW order
# (i.e. [batch_size, num_channels, height, width])
arg_scope = {"order": "NCHW"}
# Create the model helper for the train model
train_model = model_helper.ModelHelper(name="mnist_train", arg_scope=arg_scope)
# Specify the input is from the train lmdb
data, label = AddInput(
train_model, batch_size=64,
db=os.path.join(data_folder, 'mnist-train-nchw-lmdb'),
db_type='lmdb')
# Add the model definition (fc layers, conv layers, softmax, etc.)
softmax = AddModel(train_model, data)
# Add training operators, specify loss function and optimization algorithm
AddTrainingOperators(train_model, softmax, label)
# Add bookkeeping operators to save stats from training
AddBookkeepingOperators(train_model)
#### Testing model.
# We will set the batch size to 100, so that the testing
# pass is 100 iterations (10,000 images in total).
# For the testing model, we need the data input part, the main AddModel
# part, and an accuracy part. Note that init_params is set False because
# we will be using the parameters obtained from the train model which will
# already be in the workspace.
test_model = model_helper.ModelHelper(
name="mnist_test", arg_scope=arg_scope, init_params=False)
data, label = AddInput(
test_model, batch_size=100,
db=os.path.join(data_folder, 'mnist-test-nchw-lmdb'),
db_type='lmdb')
softmax = AddModel(test_model, data)
AddAccuracy(test_model, softmax, label)
#### Deployment model.
# We simply need the main AddModel part.
deploy_model = model_helper.ModelHelper(
name="mnist_deploy", arg_scope=arg_scope, init_params=False)
AddModel(deploy_model, "data")
输出:BlobReference("softmax")
Visualize our Progress
现在,让我们看一下使用Caffe2具有的简单图形可视化工具,培训和部署模型的样子。 如果以下命令失败,可能是因为您的计算机没有安装graphviz。 您需要通过您选择的包管理器安装它。
如果图形看起来太小,请右键单击并在新选项卡中打开图像以便更好地检查。
from IPython import display
graph = net_drawer.GetPydotGraph(train_model.net.Proto().op, "mnist", rankdir="LR")
display.Image(graph.create_png(), width=800)
现在,上图显示了培训阶段发生的所有事情:白色节点是blob,绿色矩形节点是正在运行的运算符。 您可能已经注意到像火车轨道这样的大规模平行线:这些是从前向传递中生成的blob到其后向运算符的依赖关系。
让我们通过仅显示必要的依赖关系并仅显示运算符来以更小的方式显示图形。 如果仔细阅读,可以看到图的左半部分是向前传递,图的右半部分是向后传递,右边是一组参数更新和汇总运算符。
graph = net_drawer.GetPydotGraphMinimal(
train_model.net.Proto().op, "mnist", rankdir="LR", minimal_dependency=True)
display.Image(graph.create_png(), width=800)
输出:
重要的是在这里重申ModelHelper类尚未执行任何操作的事实。 到目前为止我们所做的就是声明网络,它基本上创建了协议缓冲区
指定的计算图。 以下是我们如何为训练模型的主网络和参数初始化网络显示部分序列化协议缓冲区的示例。
print("*******train_model.net.Proto()*******\n")
print(str(train_model.net.Proto())[:400] + '\n...')
print("\n*******train_model.param_init_net.Proto()*******\n")
print(str(train_model.param_init_net.Proto())[:400] + '\n...')
输出的结果: *******train_model.net.Proto()******* name: "mnist_train" op { input: "dbreader_/Users/nathaninkawhich/caffe2_notebooks/tutorial_data/mnist/mnist-train-nchw-lmdb" output: "data_uint8" output: "label" name: "" type: "TensorProtosDBInput" arg { name: "batch_size" i: 64 } } op { input: "data_uint8" output: "data" name: "" type: "Cast" arg { name: "to" i: 1 } } op { input: "data" output: "data ... *******train_model.param_init_net.Proto()******* name: "mnist_train_init" op { output: "dbreader_/Users/nathaninkawhich/caffe2_notebooks/tutorial_data/mnist/mnist-train-nchw-lmdb" name: "" type: "CreateDB" arg { name: "db_type" s: "lmdb" } arg { name: "db" s: "/Users/nathaninkawhich/caffe2_notebooks/tutorial_data/mnist/mnist-train-nchw-lmdb" } } op { output: "conv1_w" name: "" type: "XavierFill" arg { na ...
Run Training
建立我们的模型并指定数据源后,我们现在可以运行培训程序。请注意,此过程可能需要一段时间才能运行。密切注意代码块仍在运行的星号(In [*])或其他IPython指示器。
我们通过连续多次执行我们的网络来进行培训,每次都在一批新的输入上进行。请注意,在此过程中,我们可以获取工作空间中任何blob的值,包括准确性和丢失。这使我们能够建立训练图并定期监测训练的准确性和损失。此外,一旦模型看到所有数据一次(1个纪元),训练就不会停止,而是一直持续到达到指定的迭代次数。
使用MLP时,模型精度很大程度上取决于参数的随机初始化和训练迭代次数。如果您的模型停留在大约50%的准确度,请重新运行笔记本,这将从另一个随机种子和新参数初始化开始。
# The parameter initialization network only needs to be run once.
# Now all the parameter blobs are initialized in the workspace.
workspace.RunNetOnce(train_model.param_init_net)
# Creating an actual network as a C++ object in memory.
# We need this as the object is going to be used a lot
# so we avoid creating an object every single time it is used.
# overwrite=True allows you to run this cell several times and avoid errors
workspace.CreateNet(train_model.net, overwrite=True)
# Set the iterations number and track the accuracy & loss
total_iters = 200
accuracy = np.zeros(total_iters)
loss = np.zeros(total_iters)
# MAIN TRAINING LOOP!
# Now, we will manually run the network for 200 iterations.
for i in range(total_iters):
workspace.RunNet(train_model.net)
accuracy[i] = workspace.blobs['accuracy']
loss[i] = workspace.blobs['loss']
# Check the accuracy and loss every so often
if i % 25 == 0:
print("Iter: {}, Loss: {}, Accuracy: {}".format(i,loss[i],accuracy[i]))
# After the execution is done, let's plot the values.
pyplot.plot(loss, 'b')
pyplot.plot(accuracy, 'r')
pyplot.title("Summary of Training Run")
pyplot.xlabel("Iteration")
pyplot.legend(('Loss', 'Accuracy'), loc='upper right')
输出:
346/5000
可视化结果
现在我们的模型已经过培训,我们可以看到我们的工作成果。 但首先,查看上面训练期间打印的内容。 在第一次迭代中,批次0的训练精度为3.1%,但是在第100次迭代中它很快收敛到九十年代中期。 情节也显示了这种趋势。
这是一个好兆头!
### Let's look at some of the training data
pyplot.figure()
pyplot.title("Training Data Sample")
# Grab the most recent data blob (i.e. batch) from the workspace
data = workspace.FetchBlob('data')
# Use visualize module to show the examples from the last batch that was fed to the model
_ = visualize.NCHW.ShowMultiple(data)
### Let's visualize the softmax result
pyplot.figure()
pyplot.title('Softmax Prediction for the first image above')
pyplot.ylabel('Confidence')
pyplot.xlabel('Label')
# Grab and visualize the softmax blob for the batch we just visualized. Since batch size
# is 64, the softmax blob contains 64 vectors, one for each image in the batch. To grab
# the vector for the first image, we can simply index the fetched softmax blob at zero.
softmax = workspace.FetchBlob('softmax')
_ = pyplot.plot(softmax[0], 'ro')
输出:
可视化学习卷积滤波器
注意:这仅适用于使用LeNet模型的情况(USE_LENET_MODEL = True)
对于卷积模型,我们也可以看到它们如何“思考”,即它们提出了哪些特征。 我们不是取出对人类不太有意义的学习权重,而是获取在输入上对这些权重进行卷积的结果。 在这里,我们获取第一个卷积层conv1的输出特征映射,这是将第一层滤波器与最新训练数据批量进行卷积的结果。 请注意,如果在评估阶段后重新运行此代码,则最后一个小批量将更改,因为评估和培训共享相同的工作区。
if USE_LENET_MODEL:
pyplot.figure()
pyplot.title("Conv1 Output Feature Maps for Most Recent Mini-batch")
# Grab the output feature maps of conv1. Change this to conv2 in order to look into the second one.
# Remember, early convolutional layers tend to learn human-interpretable features but later conv
# layers work with highly-abstract representations. For this reason, it may be harder to understand
# features of the later conv layers.
conv = workspace.FetchBlob('conv1')
# We can look into any channel. Think of it as a feature model learned.
# In this case we look into the 5th channel. Play with other channels to see other features
conv = conv[:,[5],:,:]
_ = visualize.NCHW.ShowMultiple(conv)
输出:
测试模型
还记得我们创建了一个测试网吗? 现在我们已经训练了模型,并且我们的工作空间包含训练的模型参数,我们可以对先前看不见的测试数据运行测试传递,以测试精度统计的形式检查我们的泛化性能。 虽然test_model将使用从train_model获得的参数,但仍必须运行test_model.param_init_net来初始化输入数据。
我们只会运行100次迭代,因为我们有10,000个测试图像,而test_model的批量大小为100.此外,我们将报告批量精确度的平均值,而不是报告每个单独图像的准确度分数。
# param_init_net here will only create a data reader
# Other parameters won't be re-created because we selected init_params=False before
workspace.RunNetOnce(test_model.param_init_net)
workspace.CreateNet(test_model.net, overwrite=True)
# Testing Loop
test_accuracy = np.zeros(100)
for i in range(100):
# Run a forward pass of the net on the current batch
workspace.RunNet(test_model.net)
# Collect the batch accuracy from the workspace
test_accuracy[i] = workspace.FetchBlob('accuracy')
# After the execution is done, let's plot the accuracy values.
pyplot.plot(test_accuracy, 'r')
pyplot.title('Accuracy over test batches.')
print('test_accuracy: %f' % test_accuracy.mean())
输出:
部署模型
保存部署模型
让我们使用经过训练的权重和偏差将部署模型保存到文件中。 希望这一步骤的重要性是显而易见的,但这将使我们能够在另一个时间运行模型来运行更多测试,而不是每次都必须从头开始训练模型。 当训练的参数仍在工作空间中时,必须在训练后立即执行此步骤。
# construct the model to be exported
# the inputs/outputs of the model are manually specified.
pe_meta = pe.PredictorExportMeta(
predict_net=deploy_model.net.Proto(),
parameters=[str(b) for b in deploy_model.params],
inputs=["data"],
outputs=["softmax"],
)
# save the model to a file. Use minidb as the file format
pe.save_to_db("minidb", os.path.join(root_folder, "mnist_model.minidb"), pe_meta)
print("Deploy model saved to: " + root_folder + "/mnist_model.minidb")
Deploy model saved to: /Users/nathaninkawhich/caffe2_notebooks/tutorial_files/tutorial_mnist/mnist_model.minidb
加载已保存的模型
注意:为了本教程,我们将在此处执行此操作,但此部分表示我们如何在另一个地方加载经过训练和保存的模型。
让我们从上一步加载已保存的部署模型,然后重新运行我们的测试以进行验证。
# Grab and display the last data batch used before we scratch the workspace. This purely for our convenience...
blob = workspace.FetchBlob("data")
pyplot.figure()
pyplot.title("Batch of Testing Data")
_ = visualize.NCHW.ShowMultiple(blob)
# reset the workspace, to make sure the model is actually loaded
workspace.ResetWorkspace(root_folder)
# verify that all blobs from training are destroyed.
print("The blobs in the workspace after reset: {}".format(workspace.Blobs()))
# load the predict net
predict_net = pe.prepare_prediction_net(os.path.join(root_folder, "mnist_model.minidb"), "minidb")
# verify that blobs are loaded back
print("The blobs in the workspace after loading the model: {}".format(workspace.Blobs()))
# feed the previously saved data to the loaded model
workspace.FeedBlob("data", blob)
# predict
workspace.RunNetOnce(predict_net)
softmax = workspace.FetchBlob("softmax")
print("Shape of softmax: ",softmax.shape)
# Quick way to get the top-1 prediction result
# Squeeze out the unnecessary axis. This returns a 1-D array of length 10
# Get the prediction and the confidence by finding the maximum value and index of maximum value in preds array
curr_pred, curr_conf = max(enumerate(softmax[0]), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
# the first letter should be predicted correctly
pyplot.figure()
pyplot.title('Prediction for the first image')
pyplot.ylabel('Confidence')
pyplot.xlabel('Label')
_ = pyplot.plot(softmax[0], 'ro')
输出:
这是MNIST教程的结束。 我们希望本教程重点介绍Caffe2的一些功能以及创建简单MLP或CNN模型的难易程度。