接着caffe2 教程记录四,这个是第五篇
##5.Brewing Models
brew是Caffe2用于构建模型的新API。 CNNModelHelper过去曾担任过这个角色,但由于Caffe2的扩展远远超出了CNN的优势,因此提供更通用的ModelHelper对象是有意义的。 您可能会注意到新的ModelHelper与CNNModelHelper具有许多相同的功能。 brew包装了新的ModelHelper,使得建筑模型比以前更容易。
模型构建和brew Helper 函数
在本概述中,我们将介绍brew,一个轻量级的辅助函数集合,可帮助您构建模型。 我们将首先解释Ops与Helper函数的关键概念。 然后我们将展示brew 用法,以及它如何充当ModelHelper对象的接口,以及arg_scope语法糖。 最后,我们讨论了引入brew的动机。
概念:Ops与Helper函数
在我们深入研究brew之前,我们应该回顾一下Caffe2中的一些约定以及神经网络的层是如何表示的。 Caffe2中的深度学习网络由operators 构建。 通常,这些运算符是用C ++编写的,以获得最佳性能。 Caffe2还提供了一个包装这些C ++运算符的Python API,因此您可以更灵活地进行实验和原型。 在Caffe2中,运算符总是以CamelCase方式呈现,而具有类似名称的Python辅助函数以小写形式呈现。 这样的例子如下。
Ops
我们经常将运算符称为“Op”或运算符集合作为“Ops”。 例如,FC Op代表一个完全连接的运算符,该运算符与前一层中的每个神经元和下一层的每个神经元都有加权连接。 例如,您可以使用以下命令创建FC Op:
model.net.FC([blob_in, weights, bias], blob_out)
或者您可以创建一个Copy Op:
model.net.Copy(blob_in, blob_out)
ModelHelper处理的运算符列表位于本文档的底部。 目前包括最常用的29个Ops。 这是Caffe2在撰写本文时所拥有的400+ Ops的一部分。
还应注意,您还可以创建一个没有注释网络的Operator。 例如,就像我们创建Copy Op的前一个示例一样,我们可以使用以下代码在model.net上创建一个Copy运算符(操作符):
model.Copy(blob_in, blob_out)
Helper 函数:
仅仅使用单个操作员来构建模型/网络可能很费劲,因为您必须自己完成参数初始化,设备/引擎选择(但这也是Caffe2如此之快的原因!)。 例如,要构建一个FC层,您需要使用几行代码来准备权重和偏差,然后将其提供给Op。
这是传统的画时间比较多的,手动创建方式:
model = model_helper.ModelHelper(name="train")
# initialize your weight
weight = model.param_init_net.XavierFill(
[],
blob_out + '_w',
shape=[dim_out, dim_in],
**kwargs, # maybe indicating weight should be on GPU here
)
# initialize your bias
bias = model.param_init_net.ConstantFill(
[],
blob_out + '_b',
shape=[dim_out, ],
**kwargs,
)
# finally building FC
model.net.FC([blob_in, weights, bias], blob_out, **kwargs)
幸运的是,Caffe2辅助函数可以提供帮助。 辅助函数是包装函数,可为模型创建完整的图层。 辅助函数通常用于处理参数初始化,操作符定义和引擎选择。 Caffe2默认Helper函数在Python PEP8函数约定中命名。 例如,使用python / helpers / fc.py,通过辅助函数fc实现FC Op要简单得多:
使用辅助函数简单的创建方法:
fcLayer = fc(model, blob_in, blob_out, **kwargs) # returns a blob reference
一些helper函数会构建超过1个operator。 例如,python/rnn_cell.py 中的LSTM函数可帮助您在网络中构建整个LSTM单元。
查看更多的helper 函数,可点击 repo ,你会看到下面的helper函数。
brew
现在你已经了解了Ops和Helper功能,让我们来介绍一下brew如何使模型构建变得更加容易。 brew是一个智能的辅助函数集合。 只需导入一个brew模块,即可使用所有Caffe2强大的帮助程序功能。 您现在可以使用以下方法添加FC图层:
from caffe2.python import brew
brew.fc(model, blob_in, blob_out, ...)
这与直接使用辅助函数几乎相同,但是一旦模型变得更复杂,brew就会开始发挥它的强大作用了。 以下是从MNIST教程中提取的LeNet模型构建示例。
from caffe2.python import brew
def AddLeNetModel(model, data):
conv1 = brew.conv(model, data, 'conv1', 1, 20, 5)
pool1 = brew.max_pool(model, conv1, 'pool1', kernel=2, stride=2)
conv2 = brew.conv(model, pool1, 'conv2', 20, 50, 5)
pool2 = brew.max_pool(model, conv2, 'pool2', kernel=2, stride=2)
fc3 = brew.fc(model, pool2, 'fc3', 50 * 4 * 4, 500)
fc3 = brew.relu(model, fc3, fc3)
pred = brew.fc(model, fc3, 'pred', 500, 10)
softmax = brew.softmax(model, pred, 'softmax')
每个层都是使用brew创建的,而brew又使用其操作符hooks来实例化每个Op。
arg_scope :
arg_scope是一种语法糖,可以在其上下文中设置默认的辅助函数参数值。 例如,假设您想在ResNet-150训练脚本中尝试不同的权重初始化。 你可以:
# change all weight_init here
brew.conv(model, ..., weight_init=('XavierFill', {}),...)
...
# repeat 150 times
...
brew.conv(model, ..., weight_init=('XavierFill', {}),...)
或者在arg_scope的帮助下,你可以
with brew.arg_scope([brew.conv], weight_init=('XavierFill', {})):
brew.conv(model, ...) # no weight_init needed here!
brew.conv(model, ...)
...
自定义Helper 函数:
当您更频繁地使用brew并且发现需要实现Brew目前不涵盖的Op时,您将需要编写自己的helper函数。 您可以将您的helper 函数注册为brew,以享受统一管理和语法糖。
只需定义新的helper函数,使用.Register函数将其注册到brew,然后使用brew.new_helper_function调用它。
def my_super_layer(model, blob_in, blob_out, **kwargs):
"""
100x faster, awesome code that you'll share one day.
"""
brew.Register(my_super_layer)
brew.my_super_layer(model, blob_in, blob_out)
如果您认为您的helper函数可能对Caffe2社区的其他成员有帮助,请记住共享它,并创建一个pull请求。
Caffe2 默认的 Helper 函数:
要获取有关每个功能的更多详细信息,请访问操作员目录(Operators Catalogue.)。
使用 brew的动机:
感谢您阅读有关brew的全部概述!恭喜你,你终于来了!简而言之,我们希望将模型构建过程和模型存储分开。在我们看来,ModelHelper类应该只包含网络定义和参数信息。 brew模块将具有构建网络和初始化参数的功能。
与之前同时进行模型存储和模型构建的巨型CNNModelHelper相比,模型构建的ModelHelper + brew方式更加模块化,更易于扩展。在命名方面,由于Caffe2系列支持各种网络,包括MLP,RNN和CNN,因此它也更不容易混淆。我们希望本教程能够帮助您更快,更轻松地建立模型,同时更深入地了解Caffe2。python/brew_test.py 中有一个brew使用的详细示例。如果您对brew有任何疑问,请随时与我们联系,并在回购问题中提出问题。再次感谢您拥抱新的brew API。
##6.Regression - Plotting Lines & Random Data
(https://github.com/caffe2/tutorials/blob/master/Toy_Regression.ipynb)
这是一个快速示例,展示了如何使用caff2的基础知识中的概念进行回归。 本教程分为两部分。 第一部分是创建和训练多项式回归模型的更详细的例子,第二部分是一个简洁的线性回归例子。
第一部分:多项式回归
我们正在处理的问题是一个相对简单的问题,涉及一维输入x 和一维输出 y。因为我们寻找二阶多项式作为回归模型,权重向量将包含两个权重(beta_2 和 beta_1)并且会有一个偏差(beta_0 )或拦截。 所需的解决方案形式如下:
在本教程中,我们将生成并格式化具有强二阶多项式关系的任意输入数据集。 然后我们将构建模型,指定训练算法,执行训练,最后查看结果。
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from caffe2.python import workspace, brew, optimizer
from caffe2.python.model_helper import ModelHelper
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import sklearn.datasets
from sklearn.preprocessing import PolynomialFeatures
在此指定回归模型的输入参数包括:输入数据中的样本数,训练迭代次数,SGD算法的学习率以及模型的初始权重
# Number of training sample to generate# Numbe
num_samples = 200
# Learning Rate of SGD algorithm
learning_rate = .05
# Number of iterations to train
training_iters = 100
# Initial model weights
initial_weights = [0.,0.]
创建并准备数据集
现在,我们将创建并准备用于模型的数据集。 注意,我们只是在这里构建numpy数组。 可以使用任何其他数据,只要它在输入到模型之前被正确地整形。
# Create the original observations
orig_X,_ = sklearn.datasets.make_regression(n_samples=num_samples,n_features=1,noise=5)
poly = PolynomialFeatures(degree=2, include_bias=False)
# Transform the features into second order polynomial features
xx_ = poly.fit_transform(orig_X)
# Extract the predictors and the values from the manufactured data
X = [i[0] for i in xx_]
Y_gt = [i[1] for i in xx_]
noise = np.random.uniform(size=(len(Y_gt)))
# Add some noise to the ground truth values
Y_gt += noise
# Shape the ground truth values for use with the model
Y_gt = np.reshape(Y_gt,(-1,1))
# Format the input features. Recall, we accomplish polynomial regression by
# including the original and the polynomial version of the predictors
# as features of the model
X = np.hstack((np.array(X).reshape(-1,1),np.array(X).reshape(-1,1)**2))
# Print a sample of the input data. X is the list of 2-feature input observations
# and Y is the list of ground truth values associated with each observation
print("X Sample:\n{}".format(X[:5]))
print("Y Sample:\n{}".format(Y_gt[:5]))
# Plot the input data
plt.scatter([i[0] for i in X],Y_gt,label="original data",color='b')
plt.xlabel("x")
plt.ylabel("y")
plt.title("Input Training Data")
下面是运行出来的结果:
创建模型
定义模型体系结构
通过创建我们的训练数据和我们的二阶多项式假设,我们现在可以创建一个模型来学习回归线。我们将使用“FC”层作为模型的主要组件。由于我们需要两个权重( beta_2 和 beta_1),我们将输入维度设置为2,并且由于我们只期望单个定量结果,所以我们的输出维度为1,注意,当使用“FC”图层时暗示存在偏差,我们将使用它作为 beta_0。
此外,在继续查看此步骤中创建的protobuf之前。第一个打印输出是'net',包含模型的体系结构。乍看之下,我们看到正如预期的那样,网络中有一个操作符需要输入X,权重和偏差,并输出 y_pred,在打印出'param_init_net'时我们看到这是权重和偏差的初始化存在的地方。这是一个重要的观察结果,可以深入了解如何构建和维护Caffe2中的模型。
# Create the model helper object we will use to create the regression model
regression_model = ModelHelper(name="regression_model")
# Add the FC layer, which is the main component of a linear regression model
y_pred = brew.fc(regression_model,'X','y_pred', dim_in=2, dim_out=1)
# Print the predict and init net to see what protobuf was created for this model
print("************* Predict Net *************")
print(regression_model.net.Proto())
print("\n************* Init Net *************")
print(regression_model.param_init_net.Proto())
运行出来的结果:
添加train operators 并填充到 workspace 工作空间
在这个非常重要的步骤中,我们指定损失函数,设置SGD训练算法,初始化和初始化工作空间,并初始化模型的权重和偏差。
# The loss function is computed by a squared L2 distance,
# and then averaged over all items.
dist = regression_model.SquaredL2Distance(['Y_gt', y_pred], "dist")
loss = regression_model.AveragedLoss(dist, "loss")
# Add the gradient operators and setup the SGD algorithm
regression_model.AddGradientOperators([loss])
optimizer.build_sgd(regression_model, base_learning_rate=learning_rate)
# Prime the workspace with some data
workspace.FeedBlob("Y_gt",Y_gt.astype(np.float32))
workspace.FeedBlob("X",X.astype(np.float32))
# Run the init net to prepare the workspace then create the net
workspace.RunNetOnce(regression_model.param_init_net)
workspace.CreateNet(regression_model.net)
# Inject our desired initial weights and bias
workspace.FeedBlob("y_pred_w",np.array([initial_weights]).astype(np.float32))
workspace.FeedBlob("y_pred_b",np.array([0.]).astype(np.float32))
运行训练:
# Run the training for training_iters
for i in range(training_iters):
workspace.RunNet(regression_model.net)
print("Training Complete")
提取结果
现在我们的模型已经过训练,我们可以提取在名为“y_pred_w”和“y_pred_b”的工作空间中作为blob存在的学习权重和偏差。
# Extract the learned coes and intercept from the workspace
coes = workspace.FetchBlob("y_pred_w")[0]
intercept = workspace.FetchBlob("y_pred_b")
# Calculate the regression line for plotting
x_vals = np.linspace(orig_X.min(), orig_X.max(),100)
regression_result = intercept[0] + coes[0]*x_vals + coes[1]*(x_vals**2)
print("Best Fit Line: {}*x^2 + {}*x + {}".format(round(coes[1],5), round(coes[0],5), round(intercept[0],5)))
# Plot the results of the regression
plt.scatter([i[0] for i in X],Y_gt,label="original data",color='b')
plt.plot(x_vals,regression_result,label="regression result",color='r')
plt.legend()
plt.xlabel("x")
plt.ylabel("y")
plt.title("Polynomial Regression Fit: ${{{}}}x^2 + {{{}}}x + {{{}}}$".format(round(coes[1],5), round(coes[0],5), round(intercept[0],5)))
plt.show()
运行出来的结果:
第二部分:快速线性回归示列
上面的示例显示了如何创建一个易于调整以处理高阶多项式的多项式回归模型。 现在,我们将考虑基线情况,我们需要一个简单的一阶模型,一维输入x,1-D输出 y,已经表单的解决方案:
第二部分的结构与第一部分类似。首先,我们将生成数据集,然后我们将构建模型并指定训练例程,最后我们将训练和提取我们的结果。
#####################################################################
# Initialize data
#####################################################################
X,Y_gt = sklearn.datasets.make_regression(n_samples=100,n_features=1,noise=10)
Y_gt = np.reshape(Y_gt,(-1,1))
Y_gt /= 100.
#####################################################################
# Create and train model
#####################################################################
# Construct model with single FC layer
regression_model = ModelHelper(name="regression_model")
y_pred = brew.fc(regression_model,'X','y_pred', dim_in=1, dim_out=1)
# Specify Loss function
dist = regression_model.SquaredL2Distance(['Y_gt', y_pred], "dist")
loss = regression_model.AveragedLoss(dist, "loss")
# Get gradients for all the computations above.
regression_model.AddGradientOperators([loss])
optimizer.build_sgd(regression_model, base_learning_rate=0.05)
# Prime and prepare workspace for training
workspace.FeedBlob("Y_gt",Y_gt.astype(np.float32))
workspace.FeedBlob("X",X.astype(np.float32))
workspace.RunNetOnce(regression_model.param_init_net)
workspace.CreateNet(regression_model.net)
# Set the initial weight and bias to 0
workspace.FeedBlob("y_pred_w",np.array([[0.]]).astype(np.float32))
workspace.FeedBlob("y_pred_b",np.array([0.]).astype(np.float32))
# Train the model
for i in range(100):
workspace.RunNet(regression_model.net)
#####################################################################
# Collect and format results
#####################################################################
# Grab the learned weight and bias from workspace
coe = workspace.FetchBlob("y_pred_w")[0]
intercept = workspace.FetchBlob("y_pred_b")
# Calculate the regression line for plotting
x_vals = range(-3,4)
regression_result = x_vals*coe + intercept
# Plot the results
plt.scatter(X,Y_gt,label="original data",color='b')
plt.plot(x_vals,regression_result,label="regression result",color='r')
plt.legend()
plt.xlabel("x")
plt.ylabel("y")
plt.title("Regression Line: ${{{}}}x + {{{}}}$".format(round(coe,5), round(intercept[0],5)))
plt.show()
运行出来的结果: