前面我们已经了解神经网络的各个组成部分,以及一些基本的层,比如全连接层,ReLU层等,现在是时候开始搭建一个简单的神经网络了!
我们的目标是训练一个神经网络来对二维平面上的点进行分类。
训练数据准备
首先让我们生成一些训练数据,这些训练数据总共有3类,每类100个点,每个点的维度是2(即x,y坐标)。为了把数据作为 caffe2 的网络输入,比较合适的方法是把数据写入到 leveldb 中去,然后使用操作数据库的操作子去读取。生成及写入数据库的代码这里就不赘述,可以参考代码仓库。
这些点在平面上的分布是这样的:
创建神经网络
生成了训练数据库后,可以使用caffe2_cpp_tutoria中的ModelUtil帮助类去创建网络,首先我们尝试看看线性网络能不能正确地把这三类点分开,下面就是我继承这个类设计的一个线性网络(只有一个直连层):
class FCModel : public ModelUtil { |
在实际使用这个类的时候,如果需要训练,那么要对这个网络前面加上数据输入层,以及在后面加上loss层:
void create_net(FCModel& fc_model, bool deploy = false) { |
训练
实际训练用的最多的是 sgd 算法,通常会确定一个初始的Learning rate(这里我设置的是0.1),然后每个iter中让这个初始学习率乘以一个系数,这个系数可以通过stepsize 和 gramma 这两个参数算出:
$$
lr = lr * pow(gramma, floor(iter / stepsize))
$$
在本例中stepsize设为10,gramma设为0.99。
std::string optimizer = "sgd"; |
保存训练结果
通常会每隔一段时间保存一个模型的snapshot,这里就只是把最后一个场景的模型进行保存:
/* |
预测
最后,我们可以加载保存好的模型进行预测:
/* |
当我们把结果绘制出来时,会发现长这个样子:
实际的精度只有54.7%。很显然,不同类之间的边界是直线,可见只具备一个全连接层的网络无法拟合高维的数据分布。
下面我们用两个直连层试试,只需在FCModel这个类的Add函数里面添加一行代码即可:
std::string Add(const std::string input_name, int in_size, int out_size) { |
这里我们相当于添加了一个隐藏层,隐藏层的神经元数据为100。注意这里两个直连层之间多了一个ReLU激活,目的是为了增加神经网络对高维数据分布的拟合效果。当我们使用这个网络的时候,精度竟然达到了99.3%!下面是预测的结果和原来数据分布的对比: