20180110
keywords: CNTK C# CNN MNIST
1、CNN使用需明白的参数
对CNN不太明白的童鞋参见这篇,很生动cnn动态展示
卷积网络的基本运算过程如下,z = wx + b,w是权重矩阵,b是偏差矩阵
看了上面的连接应该明白下面这三个参数的意思了,创建CNN网络时要用到,在此不多说了。
filter、stride、pad
2、CNN网络创建
var scaledInput = CNTKLib.ElementTimes(Constant.Scalar(0.00390625f, device), input);
double convWScale = 0.26;// parameter initialization hyper parameter
var m1 = new Parameter(new int[] { 5, 5, 1, 4 }, DataType.Float, CNTKLib.GlorotUniformInitializer(convWScale, -1, 2), device);
var c1 = CNTKLib.ReLU(CNTKLib.Convolution(m1, scaledInput, new int[] { 1, 1, 1 }));
var p1 = CNTKLib.Pooling(c1, PoolingType.Max, new int[] { 4, 4 }, new int[] { 4, 4 }, new bool[] { true });
var m2 = new Parameter(new int[] { 4, 4, 4, 8 }, DataType.Float, CNTKLib.GlorotUniformInitializer(convWScale, -1, 2), device);
var c2 = CNTKLib.ReLU(CNTKLib.Convolution(m2, p1, new int[] { 1, 1, 4 }));
var p2 = CNTKLib.Pooling(c2, PoolingType.Max, new int[] { 3, 3 }, new int[] { 3, 3 }, new bool[] { true });
var m3 = new Parameter(new int[] { 3, 3, 8, 16 }, DataType.Float, CNTKLib.GlorotUniformInitializer(convWScale, -1, 2), device);
var c3 = CNTKLib.ReLU(CNTKLib.Convolution(m3, p2, new int[] { 1, 1, 8 }));
var p3 = CNTKLib.Pooling(c3, PoolingType.Max, new int[] { 2, 2 }, new int[] { 2, 2 }, new bool[] { true });
dout = TestHelper.Dense(p3, numClasses, device, Activation.None, classifierName);
上面的程序创建了3个卷积层(c1、c2、c3)、3个pooling层(p1、p2、p3)和一个输出层(dout),下面单看一个卷积层和一个pooling层如何创建
var m1 = new Parameter(new int[] { 5, 5, 1, 4 }, DataType.Float, CNTKLib.GlorotUniformInitializer(convWScale, -1, 2), device);
var c1 = CNTKLib.ReLU(CNTKLib.Convolution(m1, scaledInput, new int[] { 1, 1, 1 }));
var p1 = CNTKLib.Pooling(c1, PoolingType.Max, new int[] { 4, 4 }, new int[] { 4, 4 }, new bool[] { true });
{ 5, 5, 1, 4 }表示卷积核filter是5*5,输入1通道,输出4通道
{ 1, 1, 1 }表示卷积stride是1、1;最后一个1表示输入通道
前一个 { 4, 4 }表示Pooling filter是4*4,后一个 { 4, 4 }表示Pooling的stride是4、4,也就是没有重复的区域
3、几点注意事项
1)卷积层的stride,{ 1, 1, 1 }中最后一个1表示输入通道,如果填错或不填,程序会莫名其妙的飞掉
var c1 = CNTKLib.ReLU(CNTKLib.Convolution(cp1, scaledInput, new int[] { 1, 1, 1 }));
2)使用下面这句正确率会降低1个点(不使用pad),由此看出 1、使用pad会提高正确率;2、创建卷积时缺省pad为true
var c1 = CNTKLib.ReLU(CNTKLib.Convolution(cp1, scaledInput, new int[] { 1, 1, 1 }, new bool[] { true }, new bool[] { false }));
3)前面的Pooling层filter比后面的Pooling层大,会提高正确率
4、测试结论
1)卷积网络会比MLP提高2个点的正确率
2)对于mnist,增加卷积层并没有提高正确率
3)我测试的结果只能达到98.86,为啥没有超过99呢?
4)单纯使用卷积层,不使用Pooling层,计算时间会增加几倍,结果也会下降2个点。可见Pooling层的抽象提取作用还是有效的。
下节讨论LSTM长短记忆模型