DAGNN 是Directed acyclic graph neural network 缩写,也就有向图非循环神经网络。我使用的是由MatConvNet 提供的DAGNN API。选择这套API作为工具的原因有三点,第一:这是matlab的API,相对其他语言我对Matlab比较熟悉;第二:有向图非循环的网络可以实现RPN,Network in Network 等较为复杂的功能,可以随意的引出各层的输入和输出,有利于针对三维视觉任务改造网络结构。MNIST 是手写数字的图片集,也是机器学习网络最简单的试金石。
1、定义层
1 conv_layer1 = dagnn.Conv('size',single([5,5,1,30]),'hasBias',true); 2 relu_layer2 = dagnn.ReLU(); 3 4 conv_layer3 = dagnn.Conv('size',single([5,5,30,16]),'hasBias',true); 5 relu_layer4 = dagnn.ReLU(); 6 pooling_layer5 = dagnn.Pooling('poolSize',[2,2],'stride',[2 2]); 7 8 fullConnet_layer6 = dagnn.Conv('size',single([4,4,16,256]),'hasBias',true); 9 relu_layer7 = dagnn.ReLU(); 10 fullConnet_layer8 = dagnn.Conv('size',single([1,1,256,10]),'hasBias',true); 11 SoftMat_layer9 = dagnn.SoftMax(); 12 Loss_layer = dagnn.Loss();
首先是利用API构造各层网络,定义网络结构类型。所有的Layer 都继承自dagnn.Layer类,子类中定义了输入输出,前向传播,反向传播的行为。
其中包括卷积层,激活层,池化层,Softmax 分类层,以及计算Loss层。值得注意的是全连接层是通过大卷积层来实现的。本质上全连接就是“输入的等尺寸卷积”。全连接层的作用是将卷积层提取的特征进行高度非线性的映射,将其映射到输出空间中。
2、定义网络
1 mynet = dagnn.DagNN(); 2 mynet.addLayer('conv1',conv_layer1,{'input'},{'x2'},{'filters_conv1','bias_conv1'}); 3 mynet.addLayer('relu1',relu_layer2,{'x2'},{'x3'}); 4 mynet.addLayer('pool1',pooling_layer5,{'x3'},{'x4'}); 5 6 mynet.addLayer('conv2',conv_layer3,{'x4'},{'x5'},{'filters_conv2','bias_conv2'}); 7 mynet.addLayer('relu2',relu_layer4,{'x5'},{'x6'}); 8 mynet.addLayer('pool2',pooling_layer5,{'x6'},{'x7'}); 9 10 mynet.addLayer('full1',fullConnet_layer6,{'x7'},{'x8'},{'filters_fc1','bias_fc1'}); 11 mynet.addLayer('relu3',relu_layer7,{'x8'},{'x9'}); 12 mynet.addLayer('full2',fullConnet_layer8,{'x9'},{'x10'},{'filters_fc2','bias_fc2'}); 13 mynet.addLayer('Cls1',SoftMat_layer9,{'x10'},{'pred'}); 14 mynet.addLayer('Loss',Loss_layer,{'pred','label'},{'loss'}); 15 mynet.initParams(); 16 mynet.meta.inputs = {'data',[28,28,1,1]}; 17 mynet.meta.classes.name = {1,2,3,4,5,6,7,8,9,10}; 18 mynet.meta.normalization.imageSize = [28,28,1,1]; 19 mynet.meta.interpolation = 'bicubic';
定义网络调用了addLayer方法,与其他API的网络构建方法不同的是,DAGNN的API需要针对每层定义输入和输出,以及网络中的待求得参数。当然,作为初学者我先实现了链式网络,在下周的工作中会尝试实现Faster R-CNN。
net.addLayer('full1',fullConnet_layer6,{'x7'},{'x8'},{'filters_fc1','bias_fc1'});
以此为例,代表该层的名字是full1 , 该层的结构是fullConnect_layer6,输入为x7、输出x8,参数名为filters_fc1 和 bias_fc1。其中loss 层最为特殊,其具有来自softmax层的pred 和 label (ground truth) 两种输入。
最重要的是一定要initParams()!!!!这会生成初始参数。
3、定义数据输入函数
为了训练网络,我们需要定义一个输入函数。数据量小,可存在内存中,但当数据量大的时候全部存在内存里是不现实的,这就需要一个数据输入函数来对你定义的数据库进行操作。本例中我仅使用5000幅图片进行训练,所以可以把图片放在内存中。getBatch函数如下所示:
1 function inputs = getBatch(imdb, batch) 2 % -------------------------------------------------------------------- 3 images = imdb.images.data(:,:,:,batch) ; 4 labels = imdb.images.labels(1,batch) ; 5 6 % images = gpuArray(images) ; 7 8 inputs = {'input', images, 'label', labels} ;
其中 imdb 是image data base. 其中包括:
imdb.images.data 图片 W*H*C*N 的4-D single Array
imdb.images.label 标签 N*1 的 single Array
imdb.images.data_mean 图片平均值 用于预处理时去中心
imdb.images.set 集合号 N*1 的 single Array, 其中1 代表训练集 2 代表测试集 3 代表验证集
imdb.meta 存放类型名称等和训练关系不太密切的东西
4、开始训练
直接调用 cnn_train_dag 的API 开始对整个集合进行训练,注意getBatch 输入的是函数句柄。
cnn_train_dag(mynet,imdb_sub,@getBatch);
训练了30个epoch,但是learningRate好像给太高了,掉局部最小里了。。。。。。。不过结果不错,在验证集中拿到了4998/5000.