我们前面已经推导了极简的cnn数学流程,这里编程测试一下,从极简bpnet改过来,c#版本!
我们用九宫格"X",来训练1万次,然后测试,看看效果:


测个:工

然后,全1

测个,0做的x,不错,识别到了!有意思!

我们再测试一个:0(1做的圈)

再测:x

效果还不错!
完整代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace 识别x第二版//及其简单的bpnet
{
public partial class Form1 : Form
{
//九宫格x,即
//1 0 1
//0 1 0
//1 0 1
double 常数a = 0.02;
float a1 = 1;
float a2 = 0;
float a3 = 1;
float a4 = 0;
float a5 = 1;
float a6 = 0;
float a7 = 1;
float a8 = 0;
float a9 = 1;
public double sigmoid(double x)
{
return 1 / (1.0 + Math.Exp(-x));
}
public double dsigmoid(double x)
{
return x * (1 - x);
}
public Form1()
{
InitializeComponent();
for (int xx = 0; xx < 2; xx++)
wcnn[xx] = new double[4];
for (int xx = 0; xx < 2; xx++)
wcnn的偏差[xx] = new double[4];
a0[0]= a1;
a0[1]= a2;
a0[2]= a3;
a0[3]= a4;
a0[4]= a5;
a0[5]= a6;
a0[6]= a7;
a0[7]= a8;
a0[8] = a9;
}
double[] a0 = new double[9];
double[] aI = new double[4];
double aII = 0;
double[] z1 = new double[4];
double z2 = 0;
double[][] wcnn = new double[2][];// wcnn[0]第一层卷积核, wcnn[1]第二层卷积核
double[][] wcnn的偏差 = new double[2][];
double o = 0;
public void forward()
{
//1,第一层卷积
z1[0] = a0[0] * wcnn[0][0] + a0[1] * wcnn[0][1] + a0[3]* wcnn[0][2] + a0[4]* wcnn[0][3];//1,2,4,5
z1[1] = a0[1] * wcnn[0][0] + a0[2] * wcnn[0][1] + a0[4] * wcnn[0][2] + a0[5] * wcnn[0][3];//2,3,5,6
z1[2] = a0[3] * wcnn[0][0] + a0[4] * wcnn[0][1] + a0[6] * wcnn[0][2] + a0[7] * wcnn[0][3];//4,5,7,8
z1[3] = a0[4] * wcnn[0][0] + a0[5] * wcnn[0][1] + a0[7] * wcnn[0][2] + a0[8] * wcnn[0][3];//5,6,8,9
//2, z1->aI,第二层输入
aI[0] = sigmoid(z1[0]);
aI[1] = sigmoid(z1[1]);
aI[2] = sigmoid(z1[2]);
aI[3] = sigmoid(z1[3]);
//3,第三层
z2 = aI[0] * wcnn[1][0] + aI[1] * wcnn[1][1] + aI[2]* wcnn[1][2] + aI[3] * wcnn[1][3] ;
aII = sigmoid(z2);
o = aII;
//4
double 偏差 = (1 - o) * (1 - o) * 1 / 2.0;
}
double E偏差zII = 0;
public void back()
{
//1,计算偏差,e/a2的偏导,a/z2偏导
E偏差zII = -(1 - o) * dsigmoid(o);//delta
//2,计算w【1】的偏导,第二层卷积核
wcnn的偏差[1][0] = E偏差zII * aI[0];//e偏差z2*zII偏导w2【11】
wcnn的偏差[1][1] = E偏差zII * aI[1];//e偏差z2*zII偏导w2【12】
wcnn的偏差[1][2] = E偏差zII * aI[2];//e偏差z2*zII偏导w2【21】
wcnn的偏差[1][3] = E偏差zII * aI[3];//e偏差z2*zII偏导w2【22】
//e对z1的偏导=e对a1的偏导*a1对z1的偏导
double delta11 = E偏差zII * wcnn[1][0]*dsigmoid(aI[0]);
double delta12 = E偏差zII * wcnn[1][1]*dsigmoid(aI[1]);
double delta21 = E偏差zII * wcnn[1][2]*dsigmoid(aI[2]);
double delta22 = E偏差zII * wcnn[1][3] * dsigmoid(aI[3]);
//3,计算w【0】的偏导,第一层卷积核
wcnn的偏差[0][0] = delta11*a0[0] + delta12*a0[1] + delta21*a0[3] + delta22*a0[4];//w1的偏差
wcnn的偏差[0][1] = delta11*a0[1] + delta12*a0[2] + delta21*a0[4] + delta22*a0[5];//w2的偏差
wcnn的偏差[0][2] = delta11*a0[3] + delta12*a0[4] + delta21*a0[6] + delta22*a0[7];//w3的偏差
wcnn的偏差[0][3] = delta11*a0[4] + delta12*a0[5] + delta21*a0[7] + delta22*a0[8];//w4的偏差
//更新
//w1,第二层的一个卷积核
for (int i = 0; i < 4; i++)
{
wcnn[1][i] = wcnn[1][i] - 常数a * wcnn的偏差[1][i];
}
//w0,第一层的一个卷积核
for (int i = 0; i < 4; i++)
{
wcnn[0][i] = wcnn[0][i] - 常数a * wcnn的偏差[0][i];
}
}
private void button1_Click(object sender, EventArgs e)//训练
{
for (int i = 0; i < 10000; i++)
{
forward();
back();
}
double last偏差 = (1 - o) * (1 - o) * 1 / 2.0;
textBox10.Text = last偏差.ToString();
button1.Enabled = false;
MessageBox.Show("请换图测试!");
}
Random ran = new Random();
public double randomNormalDistribution()
{
double u = 0.0, v = 0.0, w = 0.0, c = 0.0;
do
{
//获得两个(-1,1)的独立随机变量
u = ran.NextDouble() * 2 - 1.0;
v = ran.NextDouble() * 2 - 1.0;
w = u * u + v * v;
} while (w == 0.0 || w >= 1.0);
//这里就是 Box-Muller转换
c = Math.Sqrt((-2 * Math.Log(w)) / w);
//返回2个标准正态分布的随机数,封装进一个数组返回
//当然,因为这个函数运行较快,也可以扔掉一个
//return [u*c,v*c];
//double u1 = ran.NextDouble();
//double u2 = ran.NextDouble();
//double z = Math.Sqrt(-2 * Math.Log(u1)) * Math.Sin(2 * Math.PI * u2);
//return z;
return u * c;
}
private void button2init_Click(object sender, EventArgs e)
{
a1 =(float)Convert.ToDouble(textBox1.Text);
a2 = (float)Convert.ToDouble(textBox2.Text);
a3 = (float)Convert.ToDouble(textBox3.Text);
a4 = (float)Convert.ToDouble(textBox4.Text);
a5 = (float)Convert.ToDouble(textBox5.Text);
a6 = (float)Convert.ToDouble(textBox6.Text);
a7 = (float)Convert.ToDouble(textBox7.Text);
a8 = (float)Convert.ToDouble(textBox8.Text);
a9 = (float)Convert.ToDouble(textBox9.Text);
a0[0] = a1;
a0[1] = a2;
a0[2] = a3;
a0[3] = a4;
a0[4] = a5;
a0[5] = a6;
a0[6] = a7;
a0[7] = a8;
a0[8] = a9;
// layer.map[i].bias = randomNormalDistribution() / Math.Sqrt(9 / 2f);
for (int xx = 0; xx < 4; xx++)
{
wcnn的偏差[0][xx] = randomNormalDistribution() / Math.Sqrt(9 / 2f);
wcnn的偏差[1][xx] = randomNormalDistribution() / Math.Sqrt(4 / 2f);
}
button2init.Enabled = false;
}
private void button2Test_Click(object sender, EventArgs e)
{
a1 = (float)Convert.ToDouble(textBox1.Text);
a2 = (float)Convert.ToDouble(textBox2.Text);
a3 = (float)Convert.ToDouble(textBox3.Text);
a4 = (float)Convert.ToDouble(textBox4.Text);
a5 = (float)Convert.ToDouble(textBox5.Text);
a6 = (float)Convert.ToDouble(textBox6.Text);
a7 = (float)Convert.ToDouble(textBox7.Text);
a8 = (float)Convert.ToDouble(textBox8.Text);
a9 = (float)Convert.ToDouble(textBox9.Text);
a0[0] = a1;
a0[1] = a2;
a0[2] = a3;
a0[3] = a4;
a0[4] = a5;
a0[5] = a6;
a0[6] = a7;
a0[7] = a8;
a0[8] = a9;
forward();
double last偏差 = (1 - o) * (1 - o) * 1 / 2.0;
MessageBox.Show(last偏差.ToString());
}
}
}