cnn,是卷积核神经网络,前面讲的没有卷积核,所以这里,我们在前面三层bpnet网络的基础上,增加卷积核试一试!
所有的启发来自,28*28变14*14的那个常用的高斯卷积核,我们前面的网络实际是:28*28->3*3卷积后,隔行隔列抠图(相当于池化)->14*14->128->10
下面,我们就是仿照上面,我们不再使用高斯核,而是让他自己产生四个卷积核(看看是什么鬼!):
即14*14->4*10*10->4*5*5->80->10,结论是:
改成14*14*4-10*10*4-5*5*4-80-10,学习6万次,均分大于90,小于95,我们先看结果:
我们训练学习了12万次,达到95分,用鼠标画了个2,识别出来了,得分99,不错,说明四个卷积核有了,我们把mnist原图的2,用学到的卷积核处理后的四个图像也有了,太神奇!
下面是c#实现源码,请对比前面三层bpnet网络看:
先初始化了四个卷积核:
double[,] w1cnn = new double[25, 4]; double[,] w12cnn = new double[100, 80];
double[,] w2cnn = new double[80, 10];
//for cnn202409181004
for (int i = 0; i <25; i++)
for (int j = 0; j < 4; j++)
{
w1cnn[i, j] = ran.Next(-100, 100) / 100f;
}
for (int i = 0; i <100; i++)
for (int j = 0; j < 80; j++)
{
w12cnn[i, j] = ran.Next(-100, 100) / 100f;
}
for (int i = 0; i < 80; i++)
for (int j = 0; j < 10; j++)
{
w2cnn[i, j] = ran.Next(-100, 100) / 100f;
}
然后,你对比前头的三层网络:forward()函数中增加如下:
forward第一步:14*14的图像用5*5的四个卷积核操作,变成4*10*10
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)//
{
int l = (i) * 10 + j;
for (int m = 0; m < 5; m++)
for (int n = 0; n < 5; n++)
{
int k = (i + m) * 14 + j + n;
hIcnntemp[l] += xI[k] * w1cnn[m * 5 + n, 0];
hIcnntemp1[l] += xI[k] * w1cnn[m * 5 + n, 1];
hIcnntemp2[l] += xI[k] * w1cnn[m * 5 + n, 2];
hIcnntemp3[l] += xI[k] * w1cnn[m * 5 + n, 3];
}
}
//forward第二步,10*10-》5*5
jilumn.Clear();//forward执行一次,就是换一张图片
jilumn1na.Clear();
jilumn2na.Clear(); jilumn3na.Clear();
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)//
{
int l = (i) * 5 + j;
double tempb = 0;
Point tempPt = new Point();
for (int m = 0; m < 2; m++)
for (int n = 0; n < 2; n++)//需要记住m,n和序号L,202409181018
{
int k = (i * 2 + m) * 10 + j * 2 + n;
//取最大值
if (hIcnntemp[k] > tempb)
{
tempb = hIcnntemp[k];
tempPt = new Point(n, m);//m是h,n是w,这个要小心用错
}
}
jilumn.Add(tempPt);//25个数据,就有25组m,n,序号一一对应
hIcnn[l] = tempb;//25个数据,通过这个关系,就能找到14*14matrix中去。202409181038
}
上面这段代码要重复三次,一共处理四副图像,即4@10*10-》4@5*5
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)//
{
int l = (i) * 5 + j;
double tempb = 0;
Point tempPt = new Point();
for (int m = 0; m < 2; m++)
for (int n = 0; n < 2; n++)//需要记住m,n和序号L,202409181018
{
int k = (i * 2 + m) * 10 + j * 2 + n;
// tempb += B1[k] * Wb2c[m * 2 + n, l];//取均值?还是取最大值?
if (hIcnntemp1[k] > tempb)
{
tempb = hIcnntemp1[k];//如果是最大值,核就变得没有意义,back怎么办?202409170727
tempPt = new Point(n, m);//m是h,n是w,这个要小心用错
}
}
// C1[l] = (byte)(tempb/4f);
jilumn1na.Add(tempPt);//25个数据,就有25组m,n,序号一一对应
hIcnn1na[l] = tempb;//25个数据,通过这个关系,就能找到14*14matrix中去。202409181038
}
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)//
{
int l = (i) * 5 + j;
double tempb = 0;
Point tempPt = new Point();
for (int m = 0; m < 2; m++)
for (int n = 0; n < 2; n++)//需要记住m,n和序号L,202409181018
{
int k = (i * 2 + m) * 10 + j * 2 + n;
// tempb += B1[k] * Wb2c[m * 2 + n, l];//取均值?还是取最大值?
if (hIcnntemp2[k] > tempb)
{
tempb = hIcnntemp2[k];//如果是最大值,核就变得没有意义,back怎么办?202409170727
tempPt = new Point(n, m);//m是h,n是w,这个要小心用错
}
}
// C1[l] = (byte)(tempb/4f);
jilumn2na.Add(tempPt);//25个数据,就有25组m,n,序号一一对应
hIcnn2na[l] = tempb;//25个数据,通过这个关系,就能找到14*14matrix中去。202409181038
}
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)//
{
int l = (i) * 5 + j;
double tempb = 0;
Point tempPt = new Point();
for (int m = 0; m < 2; m++)
for (int n = 0; n < 2; n++)//需要记住m,n和序号L,202409181018
{
int k = (i * 2 + m) * 10 + j * 2 + n;
// tempb += B1[k] * Wb2c[m * 2 + n, l];//取均值?还是取最大值?
if (hIcnntemp3[k] > tempb)
{
tempb = hIcnntemp3[k];//如果是最大值,核就变得没有意义,back怎么办?202409170727
tempPt = new Point(n, m);//m是h,n是w,这个要小心用错
}
}
// C1[l] = (byte)(tempb/4f);
jilumn3na.Add(tempPt);//25个数据,就有25组m,n,序号一一对应
hIcnn3na[l] = tempb;//25个数据,通过这个关系,就能找到14*14matrix中去。202409181038
}
第三步关于forward,把处理后的图像合并起来,4@5*5=100
hocnnhebing = new double[100];
for (int i = 0; i < 25; i++)
{
hocnnhebing[i] = hocnn[i] = sigmoid(hIcnn[i]);
hocnnhebing[i+25] = hocnn1na[i] = sigmoid(hIcnn1na[i]);
hocnnhebing[i + 50] = sigmoid(hIcnn2na[i]);
hocnnhebing[i + 75] = sigmoid(hIcnn3na[i]);
}
forward第四步,全连接:
//下面开始全连接202409181044
hI2 = new double[80];//
for (int i = 0; i < 100; i++)//
for (int j = 0; j < 80; j++)//
hI2[j] += (hocnnhebing[i]) * w12cnn[i, j];//
//通过激活函数对隐藏层进行计算
for (int i = 0; i < 80; i++)
hO2[i] = sigmoid(hI2[i]);
yicnn = new double[10];
//通过w2计算隐藏层-输出层
for (int i = 0; i < 80; i++)
for (int j = 0; j < 10; j++)
yicnn[j] += hO2[i] * w2cnn[i, j];
//通过激活函数求yo
for (int i = 0; i <10; i++)
yOcnn[i] = sigmoid(yicnn[i]);
上面就完成了forward()函数。下面再看backward()函数:
for (int i = 0; i < 80; i++)//
for (int j = 0; j < 10; j++)//10
{
double delta = (yOcnn[j] - d[j]) * dsigmoid(yOcnn[j]) * hO2[i];
w2cnn[i, j] -= delta * learnRate;
}
double[] W3 = new double[80];//
for (int j = 0; j < 80; j++)//
for (int k = 0; k < 10; k++)//10
W3[j] += (yOcnn[k] - d[k]) * dsigmoid(yOcnn[k]) * w2cnn[j, k];
for (int i = 0; i <100; i++)//
for (int j = 0; j < 80; j++)//
{
double delta = dsigmoid(hO2[j]) * (hocnnhebing[i]) * W3[j];//这里要的是系数
w12cnn[i, j] -= delta * learnRate;
}
//对比三层bpnet,上面比较好理解,下面是对四个卷积核的backward:
//backward中第一个卷积核5*5的处理:
double[] deltacnn = new double[25];//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 0; i < 25; i++)//25
for (int j = 0; j < 80; j++)//
{
deltacnn[i] = dsigmoid(hO2[j]) * W3[j] * w12cnn[i, j] * dsigmoid(hocnnhebing[i]);
}//全连接还是好处理202409200708
for (int i= 0; i< 25; i++)
{
Point temppt= 求二维(i, jilumn[i].Y, jilumn[i].X);
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * 14 + (temppt.X + z);
double delta = xI[newIndex] * deltacnn[i];//一共25个
w1cnn[k * 5 + z, 0] -= delta * learnRate;//只用一个卷积核,核forward就对应上了202409181430
}
}
//第二个卷积核202409191112
double[] deltacnn1 = new double[25];//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 25; i < 50; i++)//25
for (int j = 0; j < 80; j++)//
{
deltacnn1[i - 25] = dsigmoid(hO2[j]) * W3[j] * w12cnn[i, j] * dsigmoid(hocnnhebing[i]);
}
for (int i = 0; i < 25; i++)
{
Point temppt = 求二维(i, jilumn1na[i].Y, jilumn1na[i].X);
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * 14 + (temppt.X + z);
double delta = xI[newIndex] * deltacnn1[i];//一共25个
w1cnn[k * 5 + z, 1] -= delta * learnRate;//只用一个卷积核,核forward就对应上了202409181430
}
}
//第三个卷积核202409191112
double[] deltacnn2 = new double[25];//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 50; i < 75; i++)//25
for (int j = 0; j < 80; j++)//
{
deltacnn2[i - 50] = dsigmoid(hO2[j]) * W3[j] * w12cnn[i, j] * dsigmoid(hocnnhebing[i]);
}
for (int i = 0; i < 25; i++)
{
Point temppt = 求二维(i, jilumn2na[i].Y, jilumn2na[i].X);
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * 14 + (temppt.X + z);
double delta = xI[newIndex] * deltacnn2[i];//一共25个
w1cnn[k * 5 + z, 2] -= delta * learnRate;//只用一个卷积核,核forward就对应上了202409181430
}
}
//第四个卷积核202409191112
double[] deltacnn3 = new double[25];//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 75; i < 100; i++)//25
for (int j = 0; j <80; j++)//
{
deltacnn3[i - 75] = dsigmoid(hO2[j]) * W3[j] * w12cnn[i, j] * dsigmoid(hocnnhebing[i]);
}
for (int i = 0; i < 25; i++)
{
Point temppt = 求二维(i, jilumn3na[i].Y, jilumn3na[i].X);
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * 14 + (temppt.X + z);
double delta = xI[newIndex] * deltacnn3[i];//一共25个
w1cnn[k * 5 + z, 3] -= delta * learnRate;//只用一个卷积核,核forward就对应上了202409181430
}
}
以上就处理完了。哦,还有一个函数:
public Point 求二维(int index, int mm, int nn)
{
Point rePt = new Point();
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
{
int n = i * 5 + j;
if (index == n)
{
int h = i * 2 + mm;
int w = j * 2 + nn;
rePt.X = w;
rePt.Y = h;
i = 5;
j = 5;//end 2 for loop;
}
}
return rePt;
}
或许,我这个cnn与你想的高大上的不同,但刨根问底,就是一回事!
也就是,你自学懂了,想怎么实现就怎么实现!
这几个核,在训练达到95分后,竟然自己生成了,太神奇!
想一想我们在图像处理里,用了那么多的卷积核,重来没有说是让计算机自己生成的,震惊到了吧!虽然这几个核,不那么好看,但的的确确,是计算机自己学习到的!
这是什么,这就是人工智能,这就是有学习能力的机器,太厉害了!所以人能做到的高斯核,机器在不断改进和进化中,一定会生成,更震惊的,他可能生成的卷积核,你都没见过,但他确实效果好,所以,他得到的结果,还有可能超过你!
本程序中,我们只生成了4个卷积核,那么使用8个卷积核,是不是效果更好?留作思考。
本节到此结束。