前言
这一节将讲述如何使用Caffe2的特征进行简单的线性回归学习。主要分为以下几步:
- 生成随机数据作为模型的输入 - 用这些数据创建网络 - 自动训练模型 - 查看梯度递减的结果和学习过程中网络参数的变化 ipython notebook教程请看译者注:如果图片看不清,可以保存到本地查看。这是一个快速的例子,展示如何使用前面的基础教程进行快速的尝试用CNN进行回归。我们要解决的问题非常简单,输入是二维的x,输出是一维的y,权重w=[2.0,1.5],偏置b=0.5。所以生成ground truth的等式是y=wx+b
。
在这个教程中,我们将会使用Caffe2的op生成训练数据。注意,这和你日常训练工作不同:在真实的训练中,训练数据一般从外部源载入,比如Caffe的DB数据库,或者Hive表。我们将会在MNIST的例程中讲到。
这个例程中,每一个Caffe2 的op将会写得非常详细,所以会显得太多繁杂。但是在MNIST例程中,我们将使用CNN模型助手来构建CNN模型。
from caffe2.python import core, cnn, net_drawer, workspace, visualizeimport numpy as npfrom IPython import displayfrom matplotlib import pyplot
声明计算图
这里,我们声明两个图:一个用于初始化计算中将会用到的变量参数和常量,另外一个作为主图将会用于跑起梯度下降,也就是训练。(译者注:不明白为啥叫做计算图(computation graphs),其实看代码和前一个教程的一样,就是创建两个net,一个用于初始化参数,一个用于训练。)
首先,初始化网络:网络的名字不重要。我们基本上把初始化代码放在一个net中,这样,我们就可以调用RunNetOnce()
函数来执行。我们分离init_net
的原因是,这些操作在整个训练的过程中只需要执行一次。
init_net = core.Net("init")# ground truth 参数.W_gt = init_net.GivenTensorFill( [], "W_gt", shape=[1, 2], values=[2.0, 1.5])B_gt = init_net.GivenTensorFill([], "B_gt", shape=[1], values=[0.5])# Constant value ONE is used in weighted sum when updating parameters.ONE = init_net.ConstantFill([], "ONE", shape=[1], value=1.)# ITER是迭代的次数.ITER = init_net.ConstantFill([], "ITER", shape=[1], value=0, dtype=core.DataType.INT32)# 随机初始化权重,范围在[-1,1],初始化偏置为0W = init_net.UniformFill([], "W", shape=[1, 2], min=-1., max=1.)B = init_net.ConstantFill([], "B", shape=[1], value=0.0)print('Created init net.')
上面代码创建并初始化了init_net
网络。主训练网络如下,我们展示了创建的的每一步。
train_net = core.Net("train")# First, 生成随机的样本X和创建ground truth.X = train_net.GaussianFill([], "X", shape=[64, 2], mean=0.0, std=1.0, run_once=0)Y_gt = X.FC([W_gt, B_gt], "Y_gt")# 往ground truth添加高斯噪声noise = train_net.GaussianFill([], "noise", shape=[64, 1], mean=0.0, std=1.0, run_once=0)Y_noise = Y_gt.Add(noise, "Y_noise")#注意到不需要讲梯度信息传播到 Y_noise层,#所以使用StopGradient 函数告诉偏微分算法不需要做这一步Y_noise = Y_noise.StopGradient([], "Y_noise")# 线性回归预测Y_pred = X.FC([W, B], "Y_pred")# 使用欧拉损失并对batch进行平均dist = train_net.SquaredL2Distance([Y_noise, Y_pred], "dist")loss = dist.AveragedLoss([], ["loss"])
现在让我们看看网络是什么样子的。从下面的图可以看到,主要包含四部分。
- 随机生成X - 使用W_gt
,B_gt
和FC
操作生成grond truth Y_gt
- 使用当前的参数W和B进行预测 - 比较输出和计算损失 graph = net_drawer.GetPydotGraph(train_net.Proto().op, "train", rankdir="LR")display.Image(graph.create_png(), width=800)现在,和其他框架相似,Caffe2允许我们自动地生成梯度操作,让我们试一下,并看看计算图有什么变化。
# Get gradients for all the computations above.gradient_map = train_net.AddGradientOperators([loss])graph = net_drawer.GetPydotGraph(train_net.Proto().op, "train", rankdir="LR")display.Image(graph.create_png(), width=800)一旦我们获得参数的梯度,我们就可以将进行SGD操作:获得当前step的学习率,更参数。在这个例子中,我们没有做任何复杂的操作,只是简单的SGD。
# 迭代数增加1.train_net.Iter(ITER, ITER)# 根据迭代数计算学习率.LR = train_net.LearningRate(ITER, "LR", base_lr=-0.1, policy="step", stepsize=20, gamma=0.9)# 权重求和train_net.WeightedSum([W, ONE, gradient_map[W], LR], W)train_net.WeightedSum([B, ONE, gradient_map[B], LR], B)graph = net_drawer.GetPydotGraph(train_net.Proto().op, "train", rankdir="LR")display.Image(graph.create_png(), width=800)
再次展示计算图
既然我们创建了网络,那么跑起来workspace.RunNetOnce(init_net)workspace.CreateNet(train_net)
在我们开始训练之前,先来看看参数:
print("Before training, W is: {}".format(workspace.FetchBlob("W")))print("Before training, B is: {}".format(workspace.FetchBlob("B")))
参数初始化如下
Before training, W is: [[-0.77634162 -0.88467366]]Before training, B is: [ 0.]
训练:
for i in range(100): workspace.RunNet(train_net.Proto().name)
迭代100次后,查看参数:
print("After training, W is: {}".format(workspace.FetchBlob("W")))print("After training, B is: {}".format(workspace.FetchBlob("B")))print("Ground truth W is: {}".format(workspace.FetchBlob("W_gt")))print("Ground truth B is: {}".format(workspace.FetchBlob("B_gt")))
参数如下:
After training, W is: [[ 1.95769441 1.47348857]]After training, B is: [ 0.45236012]Ground truth W is: [[ 2. 1.5]]Ground truth B is: [ 0.5]
看起来相当简单是不是?让我们再近距离看看训练过程中参数的更新过程。为此,我们重新初始化参数,看看每次迭代参数的变化。记住,我们可以在任何时候从workspace中取出我们的blobs。
workspace.RunNetOnce(init_net)w_history = []b_history = []for i in range(50): workspace.RunNet(train_net.Proto().name) w_history.append(workspace.FetchBlob("W")) b_history.append(workspace.FetchBlob("B"))w_history = np.vstack(w_history)b_history = np.vstack(b_history)pyplot.plot(w_history[:, 0], w_history[:, 1], 'r')pyplot.axis('equal')pyplot.xlabel('w_0')pyplot.ylabel('w_1')pyplot.grid(True)pyplot.figure()pyplot.plot(b_history)pyplot.xlabel('iter')pyplot.ylabel('b')pyplot.grid(True)你可以发现非常典型的批梯度下降表现:由于噪声的影响,训练过程中存在波动。在Ipython notebook中跑多几次这个案例,你将会看到不同的初始化和噪声的影响。 当然,这只是一个玩玩的例子,在MNIST例程中,我们将会看到一个更加真实的CNN训练的例子。
译者注: 转载请注明出处: