网上有cnn的源码(lenet),也有原理!(cpu版本的,很原始的那种,但还是比我的自研cnn好!)
所以改he凯明初始化,softmax,批处理,vgg,残差都实验成功了!
以及cudnn下也实现了bn和残差,自然也实现了并发!
但是自己的初心还是放不下,mnist下,应该有一个自己安心的版本!毕竟是自己原创的!
基于bpnet,怎样达成cnn呢?
其实就一句话:不放弃,磕到底,最终你会意识到权重和卷积核是一回事,这件事想通了,就基本成功了!
这次修改,其实我已经透彻了cnn的数学原理,也就是读过人家的paper了!
前头极简cnn几篇博客已经分析透彻了cnn的数学原理,也做了达成!
这次是审视和修改自己的初心,肯定还是比极简cnn复杂的多!
毕竟是训练mnist数据集!
虽然batch=10,但还是cpu版本的!c#达成!使用了he初始化和softmax,以及leaky relu!
卷积时的bias不好处理,也删除了,只保留了全连接中的bias!
训练一轮6万次,上了90分,训练时间降到1分07秒,没有batch要1分37秒!learnrate=0.015
第二轮,第三轮,讲的学习率到0.01,上96没问题,继续降低学习率,成绩还能上!
lr=0.005,超过97分!其实成绩我并不关注了,手写了十个数字,正常,基本全能正确识别!
只是在正确的cnn理论下约束自己的初心,使其数学化,达成安心版本!
其实我的版本里,有一些东西不在cnn理论中,而且训练成绩不降反升!这也是我为什么做记录的原因,人工智能困扰的地方,也可能时处女地!
好,先说在cnn理论下,自己初心达成的安心版本!

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;
using System.IO;
using System.Drawing.Imaging;
namespace 自己的20核
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
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);
return u * c;
}
double[,] w576cnn = new double[25, 4];
double[,] w1cnn = new double[25, 4];
double[,] w1cnn2 = new double[25, 4];
double[,] w1cnn3 = new double[25, 4];
double[,] w1cnn4 = new double[25, 4];
double[,] delw1cnn = new double[25, 4];
double[,] delw1cnn2 = new double[25, 4];
double[,] delw1cnn3 = new double[25, 4];
double[,] delw1cnn4 = new double[25, 4];
double[,] w12cnn = new double[16 * 16, 80];
double[,] w2cnn = new double[80, 10];
//double[] bIOcnn = new double[256];//不用B和deltaB,降低难度
//double[] bI2Ocnn = new double[80];
//double[] bycnn = new double[10];
void init()
{
//lenet-1
for (int i = 0; i < 25; i++)
for (int j = 0; j < 4; j++)
{
w576cnn[i, j] = randomNormalDistribution() / Math.Sqrt(25*batch_size / 2f);
}
for (int i = 0; i < 25; i++)
for (int j = 0; j < 4; j++)
{
w1cnn[i, j] = randomNormalDistribution() / Math.Sqrt(25 * batch_size / 2f);
w1cnn2[i, j] = randomNormalDistribution() / Math.Sqrt(25 * batch_size / 2f);
w1cnn4[i, j] = randomNormalDistribution() / Math.Sqrt(25 * batch_size / 2f);
w1cnn3[i, j] = randomNormalDistribution() / Math.Sqrt(25 * batch_size / 2f);
}
for (int i = 0; i < 16 * 16; i++)
for (int j = 0; j < 80; j++)
{
w12cnn[i, j] = randomNormalDistribution() / Math.Sqrt(256 * batch_size / 2f);
}
for (int i = 0; i < 80; i++)
for (int j = 0; j < 10; j++)
{
w2cnn[i, j] = randomNormalDistribution() / Math.Sqrt(80 * batch_size / 2f);
}
//for (int i = 0; i < 256; i++)
//{ bIOcnn[i] = randomNormalDistribution() / Math.Sqrt(256 / 2f); }
//for (int i = 0; i < 80; i++)
//{ bI2Ocnn[i] = randomNormalDistribution() / Math.Sqrt(80 / 2f); }
//for (int i = 0; i < 10; i++)
//{ bycnn[i] = randomNormalDistribution() / Math.Sqrt(10 / 2f); }
}
List<byte[]> alltraindataorg = new List<byte[]>();
double[][] hello28 = new double[60000][];
List<int> labels = new List<int>();
void ReadMnistImages(string filePath)
{
alltraindataorg.Clear();
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(fileStream))
{
// 跳过文件头的前16个字节
reader.ReadBytes(16);
while (fileStream.Position < fileStream.Length)
{
byte[] image = reader.ReadBytes(28 * 28); // MNIST图像大小为28x28
alltraindataorg.Add(image);
}
}
}
void ReadMnistLabels(string filePath)
{
labels.Clear();
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(fileStream))
{
// 跳过文件头的前8个字节
reader.ReadBytes(8);
while (fileStream.Position < fileStream.Length)
{
byte label = reader.ReadByte(); // 读取标签
// 处理标签数据
// ...
labels.Add(label);
}
}
}
double learnRate = 0.2f; //学习率
private void button29_Click(object sender, EventArgs e)
{
init();
////////////duqu mnist shujuji ,DuQu Mnist ShuJuJi//2024090111004
string imagesFilePath = "c:\\train-images.idx3-ubyte";
string labelsFilePath = "c:\\train-labels.idx1-ubyte";
ReadMnistImages(imagesFilePath);
ReadMnistLabels(labelsFilePath);
for (int xx = 0; xx < 60000; xx++)
{
hello28[xx] = new double[28 * 28];
for (int i = 0; i < 28 * 28; i++)
{
hello28[xx][i] = alltraindataorg[xx][i] / 255f;
}
}//上头的初始化应该独立出去
//learnRate = 0.015f;//不错,上96了202502272143
learnRate = 0.015f;
showbuffer2pictmod4(new Byte[28 * 28], 28, 28, pictureBox20);
}
void showbuffer2pictmod4(byte[] buffer, int ww, int hh, PictureBox destImg)
{
int mod = ww % 4;//解决四位对齐问题20150716
int temproiw = ww + (4 - mod) % 4;//其实这都是和显示相关,处理图像其实不必考虑,
//顯示
byte[] cutvalues = new byte[temproiw * hh * 3];
int bytes = temproiw * hh * 3;
Bitmap cutPic24 = new Bitmap(temproiw, hh, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
BitmapData _cutPic = cutPic24.LockBits(new Rectangle(0, 0, temproiw, hh), ImageLockMode.ReadWrite,
cutPic24.PixelFormat);
IntPtr ptr = _cutPic.Scan0;//得到首地址
for (int i = 0; i < hh; i++)
{
for (int j = 0; j < ww; j++)
{
int n = i * ww + j;
//int m = 3 * n;
int m = 3 * (i * temproiw + j);
cutvalues[m] = buffer[n];
cutvalues[m + 1] = buffer[n];
cutvalues[m + 2] = buffer[n];
}
}
System.Runtime.InteropServices.Marshal.Copy(cutvalues, 0, ptr, bytes);
cutPic24.UnlockBits(_cutPic);
destImg.Image = cutPic24;
}
int[] last = new int[60000];
int batch_size = 10;
public void updatesumresult()
{
//最后更新d
for (int j = 0; j < 10; j++)//10
for (int i = 0; i < 80; i++)//15
{
w2cnn[i, j] -= learnRate * 临时dzw2[i, j] / batch_size;
}
for (int j = 0; j < 80; j++)//15
for (int i = 0; i < 16 * 16; i++)//256
{
w12cnn[i, j] -= learnRate * 临时dzw12[i, j] / batch_size;
}
//for (int i = 0; i < 10; i++)
//{
// bycnn[i] -= learnRate * 临时记录dby[i];
//}
//for (int i = 0; i < 80; i++)
//{
// bI2Ocnn[i] -= learnRate * 临时记录dbhI2O[i];
//}
//for (int i = 0; i < 256; i++)
//{
// bIOcnn[i] -= learnRate * 临时记录dbhIO[i];
//}
//卷积核[k * 5 + z, (tidai) % 4] -= delta * learnRate;
for (int i = 0; i < 25; i++)
for(int j=0;j<4;j++)
{
w1cnn[i, j] -= delw1cnn[i, j] * learnRate/batch_size;
w1cnn2[i, j] -= delw1cnn2[i, j] * learnRate / batch_size;
w1cnn3[i, j] -= delw1cnn3[i, j] * learnRate / batch_size;
w1cnn4[i, j] -= delw1cnn4[i, j] * learnRate / batch_size;
}
//图像变了10次了,怎么会对?
for (int i = 0; i < 25; i++)
{
w576cnn[i, 0] -= (delta576w03[i] + delta576w02[i] + delta576w01[i] + delta576w00[i]) * learnRate / batch_size;
}
for (int i = 0; i < 25; i++)
{
w576cnn[i, 1] -= (delta576w13[i] + delta576w12[i] + delta576w11[i] + delta576w10[i]) * learnRate / batch_size;
}
for (int i = 0; i < 25; i++)
{
w576cnn[i, 2] -= (delta576w23[i] + delta576w22[i] + delta576w21[i] + delta576w20[i]) * learnRate / batch_size;
}
for (int i = 0; i < 25; i++)
{
w576cnn[i, 3] -= (delta576w33[i] + delta576w32[i] + delta576w31[i] + delta576w30[i]) * learnRate / batch_size;
}
}
public void reset_update_all()
{
for (int j = 0; j < 10; j++)//10
{
for (int i = 0; i < 80; i++)//15
{
临时dzw2[i, j] = 0;
}
}
for (int j = 0; j < 80; j++)//
{
for (int i = 0; i < 16 * 16; i++)//256
{
临时dzw12[i, j] = 0;
}
}
//for (int i = 0; i < 16; i++)
//{
// deltacnn[i] = 0;
// deltacnn1[i] = 0;
// deltacnn2[i] = 0;
// deltacnn3[i] = 0;
// deltacnn5[i] = 0;
// deltacnn6[i] = 0;
// deltacnn7[i] = 0;
// deltacnn8[i] = 0;
// deltacnn9[i] = 0;
// deltacnn10[i] = 0;
// deltacnn11[i] = 0;
// deltacnn12[i] = 0;
// deltacnn13[i] = 0;
// deltacnn14[i] = 0;
// deltacnn15[i] = 0;
// deltacnn16[i] = 0;
//}
for (int i = 0; i < 25; i++)
{
delw1cnn[i, 0] = 0; delw1cnn[i, 1] = 0; delw1cnn[i, 2] = 0; delw1cnn[i, 3] = 0;
delw1cnn2[i, 0] = 0; delw1cnn2[i, 1] = 0; delw1cnn2[i, 2] = 0; delw1cnn2[i, 3] = 0;
delw1cnn3[i, 0] = 0; delw1cnn3[i, 1] = 0; delw1cnn3[i, 2] = 0; delw1cnn3[i, 3] = 0;
delw1cnn4[i, 0] = 0; delw1cnn4[i, 1] = 0; delw1cnn4[i, 2] = 0; delw1cnn4[i, 3] = 0;
delta576w00[i] = 0;
delta576w01[i] = 0;
delta576w02[i] = 0;
delta576w03[i] = 0;
delta576w10[i] = 0;
delta576w11[i] = 0;
delta576w12[i] = 0;
delta576w13[i] = 0;
delta576w20[i] = 0;
delta576w21[i] = 0;
delta576w22[i] = 0;
delta576w23[i] = 0;
delta576w30[i] = 0;
delta576w31[i] = 0;
delta576w32[i] = 0;
delta576w33[i] = 0;
}
}
private void button30cnn_Click(object sender, EventArgs e)
{
last=new int[60000];
DateTime dt = DateTime.Now;
for (int cas = 0; cas < 6000; cas++)
{
reset_update_all();
for (int j = 0;j < 10; j++)
{
int n = cas * batch_size + j;
traincnn(n);//改minibatch,要把更新单独出来,10次一更新,要累加,就要改变初始化值202507130503
}
updatesumresult();//10次一更新
}
int P = 100;
int he = 59100;
float junzhi = 0;
for (int n = 0; n < 9; n++)
{
int hh = 0;
for (int i = 0; i < P; i++)
{
hh += last[he + i];
}
textBox47.Text += he.ToString() + "," + hh.ToString() + "\r\n";
he += 100;
junzhi += hh;
}
junzhi = junzhi / 9f;
TimeSpan sp = DateTime.Now - dt;
textBox47.Text += "均值:" + junzhi.ToString() + "\r\n";
textBox48.Text = sp.ToString();
}
double[] hIcnntemp1 = new double[576]; double[] hIcnntemp = new double[576];
double[] hIcnntemp2 = new double[576]; double[] hIcnntemp3 = new double[576];
List<Point> jilumn1na = new List<Point>(); List<Point> jilumnna = new List<Point>();
List<Point> jilumn2na = new List<Point>(); List<Point> jilumn3na = new List<Point>();
double[] hIcnn1na = new double[144]; double[] hIcnnna = new double[144];
double[] hIcnn2na = new double[144]; double[] hIcnn3na = new double[144];
double[] hI遗忘1 = new double[144]; double[] hI遗忘 = new double[144];
double[] hI遗忘2 = new double[144]; double[] hI遗忘3 = new double[144];
public double NDrelu(double x)
{
return x > 0 ? x : 0.01f * x;//=max(0.1x,x)
}
public double dNDrelu(double x)
{
// return x * (1 - x);
return x > 0 ? 1f : 0.01f;
}
List<double[]> hebingbackImg = new List<double[]>();
List<List<Point>> hebingpos = new List<List<Point>>();
double[] hocnnhebing = new double[16 * 16];
void maxpooling(int p后h, int p后w,int 前图h,int 前图w,double[] 前图,double[] p后图,ref List<Point> maxP相对位置)//24*24->12*12,2*2取最大
{
for (int i = 0; i < p后h; i++)
for (int j = 0; j < p后w; j++)//
{
int l = (i) * p后w + 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) * 前图w+ j * 2 + n;
if (前图[k] > tempb)
{
tempb = 前图[k];//如果是最大值,核就变得没有意义,back怎么办?202409170727
tempPt = new Point(n, m);//m是h,n是w,这个要小心用错
}
}
maxP相对位置.Add(tempPt);//25个数据,就有25组m,n,序号一一对应
p后图[l] = tempb;//25个数据,通过这个关系,就能找到14*14matrix中去。202409181038
}
}
public void cnnforward()//202409180943使用4层网络,抄过来(这里还是三层网络),14*14->5*5->15->10
{
hIcnntemp = new double[576];
hIcnntemp1 = new double[576]; hIcnntemp2 = new double[576]; hIcnntemp3 = new double[576];
hIcnn1na = new double[144]; hIcnn2na = new double[144]; hIcnn3na = new double[144];
hIcnnna = new double[144];//每一幅图片都要重置
hI遗忘 = new double[144]; hI遗忘2 = new double[144]; hI遗忘3 = new double[144];
hI遗忘1 = new double[144];//每一幅图片都要重置
jilumnna.Clear();//forwardcnn执行一次,就是换一张图片
jilumn1na.Clear();
jilumn2na.Clear(); jilumn3na.Clear();
for (int i = 0; i < 24; i++)
for (int j = 0; j < 24; j++)//
{
int l = (i) * 24 + j;
for (int m = 0; m < 5; m++)
for (int n = 0; n < 5; n++)
{
int k = (i + m) * 28 + j + n;
hIcnntemp[l] += xI[k] * w576cnn[m * 5 + n, 0];//始终用零组,其他三个组不用
//为了方便,如此设计202409181006
hIcnntemp1[l] += xI[k] * w576cnn[m * 5 + n, 1];
hIcnntemp2[l] += xI[k] * w576cnn[m * 5 + n, 2];
hIcnntemp3[l] += xI[k] * w576cnn[m * 5 + n, 3];
}
}
////第二步,24*24》12*12
maxpooling(12, 12, 24, 24, hIcnntemp, hIcnnna, ref jilumnna);
maxpooling(12, 12, 24, 24, hIcnntemp1, hIcnn1na, ref jilumn1na);
maxpooling(12, 12, 24, 24, hIcnntemp2, hIcnn2na, ref jilumn2na);
maxpooling(12, 12, 24, 24, hIcnntemp3, hIcnn3na, ref jilumn3na);
//4*12*12 //第一次maxpooling返回位置
for (int i = 0; i < 144; i++)//12*12时才做sigmod,挺有意思202507120618
{
hI遗忘[i] = NDrelu(hIcnnna[i]);
hI遗忘1[i] = NDrelu(hIcnn1na[i]);
hI遗忘2[i] = NDrelu(hIcnn2na[i]);
hI遗忘3[i] = NDrelu(hIcnn3na[i]);
}
/////////
// 上面已经达成12*12的四张特征图202409211852,下面每张特征图再卷积成8*8,下采样,生成4张4*4
hebingbackImg = new List<double[]>(); //16*4*4
hebingpos = new List<List<Point>>();//第二次maxpooling返回位置
List<double[]> tempbackImg = new List<double[]>();
List<List<Point>>
temp = chongfu(hI遗忘, w1cnn, ref tempbackImg);//重复一次,生成4张4*4特征图和16个位置记录要返回,4次就是,16张图,64个位置记录
for (int i = 0; i < 4; i++)
{
hebingpos.Add(temp[i]);
hebingbackImg.Add(tempbackImg[i]);
}
tempbackImg.Clear();
temp.Clear();
temp = chongfu(hI遗忘1, w1cnn2, ref tempbackImg);//重复一次,生成4张4*4特征图和16个位置记录要返回,4次就是,16张图,64个位置记录
for (int i = 0; i < 4; i++)
{
hebingpos.Add(temp[i]);
hebingbackImg.Add(tempbackImg[i]);
}
tempbackImg.Clear();
temp.Clear();
temp = chongfu(hI遗忘2, w1cnn3, ref tempbackImg);//重复一次,生成4张4*4特征图和16个位置记录要返回,4次就是,16张图,64个位置记录
for (int i = 0; i < 4; i++)
{
hebingpos.Add(temp[i]);
hebingbackImg.Add(tempbackImg[i]);
}
tempbackImg.Clear();
temp.Clear();
temp = chongfu(hI遗忘3, w1cnn4, ref tempbackImg);//重复一次,生成4张4*4特征图和16个位置记录要返回,4次就是,16张图,64个位置记录
for (int i = 0; i < 4; i++)
{
hebingpos.Add(temp[i]);
hebingbackImg.Add(tempbackImg[i]);
}
hocnnhebing = new double[16 * 16];
for (int i = 0; i < 16; i++)
for (int j = 0; j < 16; j++)//4*4
{
int n = i * 16 + j;
// hocnnhebing[n] = NDrelu(hebingbackImg[i][j] + bIOcnn[n]);//4*4时才做sigmod,挺有意思202507120618
hocnnhebing[n] = NDrelu(hebingbackImg[i][j] );
}
//下面开始全连接202409181044
hI2cnn = new double[80];//15gai 25
for (int i = 0; i < 16 * 16; i++)//128
for (int j = 0; j < 80; j++)//60
hI2cnn[j] += (hocnnhebing[i]) * w12cnn[i, j];//75,25
//通过激活函数对隐藏层进行计算
for (int i = 0; i < 80; i++)
// hO2cnn[i] = NDrelu(hI2cnn[i] + bI2Ocnn[i]);
hO2cnn[i] = NDrelu(hI2cnn[i] );
yicnn = new double[10];
//通过w2计算隐藏层-输出层
for (int i = 0; i < 80; i++)
for (int j = 0; j < 10; j++)
yicnn[j] += hO2cnn[i] * w2cnn[i, j];
//通过激活函数求yo
for (int i = 0; i < 10; i++)
// yOcnn[i] = NDrelu(yicnn[i] + bycnn[i]);
yOcnn[i] = NDrelu(yicnn[i] );
////////////////////////////
//下面是softmax,//yo相当与zi
double maxtemp = yOcnn[0];
for (int i = 1; i < 10; i++)
{
if (yOcnn[i] > maxtemp)
maxtemp = yOcnn[i];
}
double sum = 0;
// 首先找出yo中最大,然后softmax
for (int i = 0; i < 10; i++)
{
sum += Math.Exp(yOcnn[i] - maxtemp);
}
for (int i = 0; i < 10; i++)
{
softm[i] = Math.Exp(yOcnn[i] - maxtemp) / sum;//概率计算完毕
}
}
double[] hI2cnn = new double[80];
double[] hO2cnn = new double[80];
double[] yicnn = new double[10];
double[] yOcnn = new double[10];
double[] ThIcnntemp = new double[64];
double[] ThIcnntemp1 = new double[64]; double[] ThIcnntemp2 = new double[64]; double[] ThIcnntemp3 = new double[64];
double[] ThIcnn1na = new double[16]; double[] ThIcnn2na = new double[16]; double[] ThIcnn3na = new double[16];
double[] ThIcnnna = new double[16];//每一幅图片都要重置
List<Point> Tjilumn1na = new List<Point>(); List<Point> Tjilumnna = new List<Point>();
List<Point> Tjilumn2na = new List<Point>(); List<Point> Tjilumn3na = new List<Point>();
public List<List<Point>> chongfu(double[] 输入图像, double[,] 卷积核,ref List<double[]> fanhuituxiang)
{
List<List<Point>> templlp = new List<List<Point>>();
// 上面已经达成12*12的四张特征图202409211852,下面每张特征图再卷积成8*8,下采样,生成4张4*4
ThIcnntemp = new double[64];
ThIcnntemp1 = new double[64]; ThIcnntemp2 = new double[64]; ThIcnntemp3 = new double[64];
ThIcnn1na = new double[16]; ThIcnn2na = new double[16]; ThIcnn3na = new double[16];
ThIcnnna = new double[16];//每一幅图片都要重置
Tjilumnna.Clear();//forwardcnn执行一次,就是换一张图片
Tjilumn1na.Clear();
Tjilumn2na.Clear(); Tjilumn3na.Clear();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)//
{
int l = (i) * 8 + j;
for (int m = 0; m < 5; m++)
for (int n = 0; n < 5; n++)
{
int k = (i + m) * 12 + j + n;
ThIcnntemp[l] += 输入图像[k] * 卷积核[m * 5 + n, 0];//始终用零组,其他三个组不用
//为了方便,如此设计202409181006
ThIcnntemp1[l] += 输入图像[k] * 卷积核[m * 5 + n, 1];
ThIcnntemp2[l] += 输入图像[k] * 卷积核[m * 5 + n, 2];
ThIcnntemp3[l] += 输入图像[k] * 卷积核[m * 5 + n, 3];//一个图像用四个卷积核生成4个特征图*8*8
}
}
//第二步,8*8-》4*4
maxpooling(4, 4, 8, 8, ThIcnntemp, ThIcnnna, ref Tjilumnna);
templlp.Add(Tjilumnna); fanhuituxiang.Add(ThIcnnna);
maxpooling(4, 4, 8, 8, ThIcnntemp1, ThIcnn1na, ref Tjilumn1na);
templlp.Add(Tjilumn1na); fanhuituxiang.Add(ThIcnn1na);
maxpooling(4, 4, 8, 8, ThIcnntemp2, ThIcnn2na, ref Tjilumn2na);
templlp.Add(Tjilumn2na); fanhuituxiang.Add(ThIcnn2na);
maxpooling(4, 4, 8, 8, ThIcnntemp3, ThIcnn3na, ref Tjilumn3na); //程序中的0初始化值,已经核实过,是ok的20250712
templlp.Add(Tjilumn3na); fanhuituxiang.Add(ThIcnn3na);
return templlp;
}
double[] softm = new double[10];
double[] xI = new double[28 * 28];
int[] d = new int[10];
void traincnn(int cas)
{
//是否需要重置deltaB?deltaW?在minibatch中?要
xI = hello28[cas];//28*28
int num = labels[cas];
d = new int[10];
d[num] = 1;
cnnforward();
int ans = getAnsSoftmaxcnn();
if (num == ans)
last[cas] = 1;
softm[num] = softm[num] - 1;
backcnn();
}
public double[] 更新卷积核(double[] 中间值, int 卷积核序号, double[] 图像, double[,] 卷积核)//=4,=5,卷积核序号从1开始
{
//第四个卷积核202409191112
double[] deltacnn3 = new double[16];//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
int kk = 0;
int tidai = 卷积核序号 - 1;
int temp = (tidai) * 16;
for (int i = temp; i < temp + 16; i++)
{
for (int j = 0; j < 80; j++)//15
{
// deltacnn3[kk] = 中间值[j] * w12cnn[i, j] * dsigmoid(hocnnhebing[i]);
// 临时记录dbhIO[i] =?这个怎么办?202502271112
deltacnn3[kk] += 中间值[j] * w12cnn[i, j] * dNDrelu(hocnnhebing[i]);
}
// deltacnn3[kk] /= 80;
kk++;
}
//for (int i = 0; i < 16; i++)
//{
// Point temppt = 求二维(i, hebingpos[tidai][i].Y, hebingpos[tidai][i].X);//3代表第si波16个
// for (int k = 0; k < 5; k++)
// for (int z = 0; z < 5; z++)
// {
// int newIndex = (temppt.Y + k) * 12 + (temppt.X + z);
// double delta = 图像[newIndex] * deltacnn3[i];//一共25个
// // w1cnn1[k * 5 + z, 卷积核序号 - 1] -= delta * learnRate;
// 卷积核[k * 5 + z, (tidai) % 4] -= delta * learnRate;
// }
// }
return deltacnn3;
}
public void last更新卷积核minibatch(double[] 更新卷积核ret, int 卷积核序号, double[] 图像, double[,] 卷积核)//=4,=5,卷积核序号从1开始
{
int tidai = 卷积核序号 - 1;
for (int i = 0; i < 16; i++)
{
Point temppt = 求二维(i, hebingpos[tidai][i].Y, hebingpos[tidai][i].X);//3代表第si波16个
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * 12 + (temppt.X + z);
// 更新卷积核ret[i] += 图像[newIndex] * 更新卷积核ret[i];//一共25个
卷积核[k * 5 + z, (tidai) % 4] += 图像[newIndex] * 更新卷积核ret[i];
//// w1cnn1[k * 5 + z, 卷积核序号 - 1] -= delta * learnRate;
//卷积核[k * 5 + z, (tidai) % 4] -= delta * learnRate;
}
}
//// 更新卷积核ret[i] = 更新卷积核ret[i] / 25f;
// for (int k = 0; k < 25; k++)
// { 卷积核[k, (tidai) % 4] /= 16; }
}
public void last更新卷积核(double[] 更新卷积核ret, int 卷积核序号, double[] 图像, double[,] 卷积核)//=4,=5,卷积核序号从1开始
{
int tidai = 卷积核序号 - 1;
for (int i = 0; i < 16; i++)
{
Point temppt = 求二维(i, hebingpos[tidai][i].Y, hebingpos[tidai][i].X);//3代表第si波16个
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * 12 + (temppt.X + z);
double delta = 图像[newIndex] * 更新卷积核ret[i];//一共25个
// w1cnn1[k * 5 + z, 卷积核序号 - 1] -= delta * learnRate;
卷积核[k * 5 + z, (tidai) % 4] -= delta * learnRate;
}
}
}
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;
}
void 求deltaW576( double[] deltacnnx, double[,] w1cnnx,int 指定核, List<Point> 记录mn,double[] deltaW576, double[] hI遗忘图,int pool1W=12,int pool1前原图W=28)
{
double[] deltacnnXx = new double[16];
for (int i = 0; i < 16; i++)//16
{
for (int j = 0; j < 25; j++)//25
{
deltacnnXx[i] += deltacnnx[i] * w1cnnx[j, 指定核];
}
// deltacnnXx[i] /= 25;//图像中有25个位置导致
}
// double[] delta576w0 = new double[25];
for (int i = 0; i < 16; i++)
{
Point temppt = 求二维(i, 记录mn[i].Y, 记录mn[i].X);
for (int k = 0; k < 5; k++)
for (int z = 0; z < 5; z++)
{
int newIndex = (temppt.Y + k) * pool1前原图W + (temppt.X + z);
int biasIndex = (temppt.Y / 2 + k) * pool1W + temppt.X / 2 + z;
//double delta = deltacnnX[i] * dNDrelu(hI遗忘[biasIndex]) * learnRate;//一共25个 ,12*12=hI遗忘
//w576cnn[k * 5 + z, 0] -= xI[newIndex] * delta;//28*28,更新四个卷积核,先不更新,想用minibatch,10次运算,更新一次
double delta = deltacnnXx[i] * dNDrelu(hI遗忘图[biasIndex]);//一共25个 ,12*12=hI遗忘
deltaW576[k * 5 + z] += xI[newIndex] * delta;//28*28,更新四个卷积核,先不更新,想用minibatch,10次运算,更新一次
//deltaW576[k * 5 + z] = xI[newIndex] * delta;//28*28,更新四个卷积核,先不更新,想用minibatch,10次运算,更新一次
}
}
//for (int k = 0; k < 25; k++)
//{
// deltaW576[k] /= 16f;
//}
}
//反向传播函数
double[,] 临时dzw2 = new double[80, 10];
double[,] 临时dzw12 = new double[16 * 4 * 4, 80];
double[] delta576w00 = new double[25];
double[] delta576w01 = new double[25];
double[] delta576w02 = new double[25];
double[] delta576w03 = new double[25];
double[] delta576w10 = new double[25];
double[] delta576w11 = new double[25];
double[] delta576w12 = new double[25];
double[] delta576w13 = new double[25];
double[] delta576w20 = new double[25];
double[] delta576w21 = new double[25];
double[] delta576w22 = new double[25];
double[] delta576w23 = new double[25];
double[] delta576w30 = new double[25];
double[] delta576w31 = new double[25];
double[] delta576w32 = new double[25];
double[] delta576w33 = new double[25];
void backcnn()//应该引入结局b的更新,这样可以加快收敛速度202409140818
{
double[] 替代 = new double[10];
//double[,] 临时dzw2 = new double[80, 10];
//double[,] 临时dzw12 = new double[16*4*4, 80];
////更新delta w1cnn【25,4】《--来自临时dzw12【0-63,80】,第一4*4*4
////更新delta w2cnn【25,4】《--来自临时dzw12【64-127,80】,第二4*4*4
////更新delta w3cnn【25,4】《--来自临时dzw12【128-191,80】,第三4*4*4
////更新delta w4cnn【25,4】《--来自临时dzw12【191-255,80】,第四4*4*4
//double[] 临时记录dby = new double[10];
//double[] 临时记录dbhI2O = new double[80];
//double[] 临时记录dbhIO = new double[256];
//////double[] 临时记录db = new double[144];
//////double[] 临时记录db1 = new double[144];
//////double[] 临时记录db2 = new double[144];
//////double[] 临时记录db3 = new double[144];
double[] deltacnn = new double[16];
double[] deltacnn1 = new double[16];
double[] deltacnn2 = new double[16];
double[] deltacnn3 = new double[16];
double[] deltacnn5 = new double[16];
double[] deltacnn6 = new double[16];
double[] deltacnn7 = new double[16];
double[] deltacnn8 = new double[16];
double[] deltacnn9 = new double[16];
double[] deltacnn10 = new double[16];
double[] deltacnn11 = new double[16];
double[] deltacnn12 = new double[16];
double[] deltacnn13 = new double[16];//卷积核w1cnn4[25,0],5*5的卷积核
double[] deltacnn14 = new double[16];//卷积核w1cnn4[25,1]
double[] deltacnn15 = new double[16];//卷积核w1cnn4[25,2]
double[] deltacnn16 = new double[16];//卷积核w1cnn4[25,3]
for (int j = 0; j < 10; j++)//10
{
// 临时记录dby[j] =
替代[j] = softm[j] * dNDrelu(yOcnn[j]);
for (int i = 0; i < 80; i++)//15
{
临时dzw2[i, j] += 替代[j] * hO2cnn[i];//这里出现+的原因是batch导致的20260401
}
}
double[] W3 = new double[80];//
for (int j = 0; j < 80; j++)//
for (int k = 0; k < 10; k++)//10
W3[j] += 替代[k] * w2cnn[j, k];
double[] 替代2 = new double[80];
for (int j = 0; j < 80; j++)//
{
// 临时记录dbhI2O[j] =
替代2[j] = dNDrelu(hO2cnn[j]) * W3[j];
for (int i = 0; i < 16 * 16; i++)//256
{
临时dzw12[i, j] += 替代2[j] * (hocnnhebing[i]); //这里出现+的原因是batch导致的20260401
}
}
//上头是全连接
//下头是卷积核,处理变得不一样,头4个与与w1cnn有关,
//即第一4*4*4=64,更新deltaw1cnn【25,0】,更新deltaw1cnn【25,1】,
//更新deltaw1cnn【25,2】,更新deltaw1cnn【25,3】
//卷积核中一个元素,在图像中可以找到很多位置,这个问题怎么办?202507121726
//取均值,有80,求和后,除80,如果有25,求和后,除以25,试试看,公式中没给出表达,办法比问题多。
for (int i = 0; i < 16; i++)//25
{
for (int j = 0; j < 80; j++)//
{
// 临时记录dbhIO[i] =
deltacnn[i] += 替代2[j] * w12cnn[i, j] * dNDrelu(hocnnhebing[i]);//与w1cnn有关
}//全连接还是好处理202409200708
// deltacnn[i]/= 80;//80次,取个均值
}
//另一个卷积核202409191112
//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 16; i < 32; i++)//25
{
for (int j = 0; j < 80; j++)//
{
// 临时记录dbhIO[i] =
deltacnn1[i - 16] += 替代2[j] * w12cnn[i, j] * dNDrelu(hocnnhebing[i]);//与w1cnn有关
}
// deltacnn1[i - 16] /= 80;//80次,取个均值
}
//第三个卷积核202409191112
//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 32; i < 48; i++)//25
{
for (int j = 0; j < 80; j++)//
{
// 临时记录dbhIO[i] =
deltacnn2[i - 32] += 替代2[j] * w12cnn[i, j] * dNDrelu(hocnnhebing[i]);//与w1cnn有关
}
// deltacnn2[i - 32] /= 80;//80次,取个均值
}
//第四个卷积核202409191112
//每一个deltacnn,都对应25个14*14中的数据元素,以及一个5*5的卷积核
for (int i = 48; i < 64; i++)//25
{
for (int j = 0; j < 80; j++)//15
{
// 临时记录dbhIO[i] =
deltacnn3[i - 48] += 替代2[j] * w12cnn[i, j] * dNDrelu(hocnnhebing[i]);//与w1cnn有关
}
// deltacnn3[i - 48] /= 80;//80次,取个均值
}
//4个卷积核已经更新完,下面第5个到16
deltacnn5 = 更新卷积核(替代2, 5, hIcnn1na, w1cnn2);//5->0,w1cnn2[25,(5 - 1) % 4]->w1cnn2[25,(6 - 1) % 4]->w1cnn2[25,(7 - 1) % 4]->w1cnn2[25,(8 - 1) % 4]
deltacnn6 = 更新卷积核(替代2, 6, hIcnn1na, w1cnn2);
deltacnn7 = 更新卷积核(替代2, 7, hIcnn1na, w1cnn2);
deltacnn8 = 更新卷积核(替代2, 8, hIcnn1na, w1cnn2);
deltacnn9 = 更新卷积核(替代2, 9, hIcnn2na, w1cnn3);//9->0,w1cnn3[25,(9- 1) % 4]->w1cnn3[25,(10 - 1) % 4]->w1cnn3[25,(11- 1) % 4]->w1cnn3[25,(12 - 1) % 4]
deltacnn10 = 更新卷积核(替代2, 10, hIcnn2na, w1cnn3);
deltacnn11 = 更新卷积核(替代2, 11, hIcnn2na, w1cnn3);
deltacnn12 = 更新卷积核(替代2, 12, hIcnn2na, w1cnn3);
deltacnn13 = 更新卷积核(替代2, 13, hIcnn3na, w1cnn4);//卷积核w1cnn4[25,0],5*5的卷积核
deltacnn14 = 更新卷积核(替代2, 14, hIcnn3na, w1cnn4);//卷积核w1cnn4[25,1]
deltacnn15 = 更新卷积核(替代2, 15, hIcnn3na, w1cnn4);//卷积核w1cnn4[25,2]
deltacnn16 = 更新卷积核(替代2, 16, hIcnn3na, w1cnn4);//卷积核w1cnn4[25,3]//hIcnn3na是12*12的图像《-8**《-4*4,backup
//for (int i = 0; i < 16; i++)//四个没有更新,发现一个delta|B错误202507130520,为改正,屏蔽
//{
// 临时记录dbhIO[64 + i] = deltacnn5[i];
// 临时记录dbhIO[64 + 16 + i] = deltacnn6[i];
// 临时记录dbhIO[64 + 16 * 2 + i] = deltacnn7[i];
// 临时记录dbhIO[64 + 16 * 3 + i] = deltacnn8[i];
// 临时记录dbhIO[64 + 16 * 4 + i] = deltacnn9[i];
// 临时记录dbhIO[64 + 16 * 5 + i] = deltacnn10[i];
// 临时记录dbhIO[64 + 16 * 6 + i] = deltacnn11[i];
// 临时记录dbhIO[64 + 16 * 7 + i] = deltacnn12[i];
// 临时记录dbhIO[64 + 16 * 8 + i] = deltacnn13[i];
// 临时记录dbhIO[64 + 16 * 9 + i] = deltacnn14[i];
// 临时记录dbhIO[64 + 16 * 10 + i] = deltacnn15[i];
// 临时记录dbhIO[64 + 16 * 11 + i] = deltacnn16[i];
//}
////继续向上,四个卷积核刚好对应一副原始图像,即四个卷积核向上对应一个卷积核28*28-》24*24-》12*12
////卷积核w1cnn[25,4]对应xI[k] * w576cnn[m * 5 + n, 0]
////卷积核w1cnn1[25,4]对应xI[k] * w576cnn[m * 5 + n, 1]
// double[] deltacnnX = new double[16];//每一个deltacnnx,都对应25个28*28中的数据元素,以及一个5*5的卷积核
求deltaW576( deltacnn, w1cnn, 0, jilumnna, delta576w00, hI遗忘);
求deltaW576( deltacnn1, w1cnn, 1, jilumnna, delta576w01, hI遗忘);
求deltaW576( deltacnn2, w1cnn, 2, jilumnna, delta576w02, hI遗忘);
求deltaW576( deltacnn3, w1cnn, 3, jilumnna, delta576w03, hI遗忘);
//第二轮开
求deltaW576( deltacnn5, w1cnn2, 0, jilumn1na, delta576w10, hI遗忘1);
求deltaW576( deltacnn6, w1cnn2, 1, jilumn1na, delta576w11, hI遗忘1);
求deltaW576( deltacnn7, w1cnn2, 2, jilumn1na, delta576w12, hI遗忘1);
求deltaW576( deltacnn8, w1cnn2, 3, jilumn1na, delta576w13, hI遗忘1);
//第san轮开始
求deltaW576( deltacnn9, w1cnn3, 0, jilumn2na, delta576w20, hI遗忘2);
求deltaW576( deltacnn10, w1cnn3, 1, jilumn2na, delta576w21, hI遗忘2);
求deltaW576( deltacnn11, w1cnn3, 2, jilumn2na, delta576w22, hI遗忘2);
求deltaW576( deltacnn12, w1cnn3, 3, jilumn2na, delta576w23, hI遗忘2);
////最后yilun
求deltaW576( deltacnn13, w1cnn4, 0, jilumn3na, delta576w30, hI遗忘3);
求deltaW576( deltacnn14, w1cnn4, 1, jilumn3na, delta576w31, hI遗忘3);
求deltaW576( deltacnn15, w1cnn4, 2, jilumn3na, delta576w32, hI遗忘3);
求deltaW576( deltacnn16, w1cnn4, 3, jilumn3na, delta576w33, hI遗忘3);
////最后更新d
//for (int j = 0; j < 10; j++)//10
// for (int i = 0; i < 80; i++)//15
// {
// w2cnn[i, j] -= learnRate * 临时dzw2[i, j];
// }
//for (int j = 0; j < 80; j++)//15
// for (int i = 0; i < 16 * 16; i++)//256
// {
// w12cnn[i, j] -= learnRate * 临时dzw12[i, j];
// }
////for (int i = 0; i < 10; i++)
////{
//// bycnn[i] -= learnRate * 临时记录dby[i];
////}
////for (int i = 0; i < 80; i++)
////{
//// bI2Ocnn[i] -= learnRate * 临时记录dbhI2O[i];
////}
////for (int i = 0; i < 256; i++)
////{
//// bIOcnn[i] -= learnRate * 临时记录dbhIO[i];
////}
//last更新卷积核(deltacnn, 1, hIcnnna, w1cnn);
//last更新卷积核(deltacnn1, 2, hIcnnna, w1cnn);
//last更新卷积核(deltacnn2, 3, hIcnnna, w1cnn);
//last更新卷积核(deltacnn3, 4, hIcnnna, w1cnn);
//last更新卷积核(deltacnn5, 5, hIcnn1na, w1cnn2);
//last更新卷积核(deltacnn6, 6, hIcnn1na, w1cnn2);
//last更新卷积核(deltacnn7, 7, hIcnn1na, w1cnn2);
//last更新卷积核(deltacnn8, 8, hIcnn1na, w1cnn2);
//last更新卷积核(deltacnn9, 9, hIcnn2na, w1cnn3);
//last更新卷积核(deltacnn10, 10, hIcnn2na, w1cnn3);
//last更新卷积核(deltacnn11, 11, hIcnn2na, w1cnn3);
//last更新卷积核(deltacnn12, 12, hIcnn2na, w1cnn3);
//last更新卷积核(deltacnn13, 13, hIcnn3na, w1cnn4);
//last更新卷积核(deltacnn14, 14, hIcnn3na, w1cnn4);
//last更新卷积核(deltacnn15, 15, hIcnn3na, w1cnn4);
//last更新卷积核(deltacnn16, 16, hIcnn3na, w1cnn4);
//for (int i = 0; i < 25; i++)
//{
// w576cnn[i, 0] -= (delta576w03[i] + delta576w02[i] + delta576w01[i] + delta576w00[i]) / 4 * learnRate;
//}
//for (int i = 0; i < 25; i++)
//{
// w576cnn[i, 1] -= (delta576w13[i] + delta576w12[i] + delta576w11[i] + delta576w10[i]) / 4 * learnRate;
//}
//for (int i = 0; i < 25; i++)
//{
// w576cnn[i, 2] -= (delta576w23[i] + delta576w22[i] + delta576w21[i] + delta576w20[i]) / 4 * learnRate;
//}
//for (int i = 0; i < 25; i++)
//{
// w576cnn[i, 3] -= (delta576w33[i] + delta576w32[i] + delta576w31[i] + delta576w30[i]) / 4 * learnRate;
//}
last更新卷积核minibatch(deltacnn, 1, hIcnnna, delw1cnn);
last更新卷积核minibatch(deltacnn1, 2, hIcnnna, delw1cnn);
last更新卷积核minibatch(deltacnn2, 3, hIcnnna, delw1cnn);
last更新卷积核minibatch(deltacnn3, 4, hIcnnna, delw1cnn);
last更新卷积核minibatch(deltacnn5, 5, hIcnn1na, delw1cnn2);
last更新卷积核minibatch(deltacnn6, 6, hIcnn1na, delw1cnn2);
last更新卷积核minibatch(deltacnn7, 7, hIcnn1na, delw1cnn2);
last更新卷积核minibatch(deltacnn8, 8, hIcnn1na, delw1cnn2);
last更新卷积核minibatch(deltacnn9, 9, hIcnn2na, delw1cnn3);
last更新卷积核minibatch(deltacnn10, 10, hIcnn2na, delw1cnn3);
last更新卷积核minibatch(deltacnn11, 11, hIcnn2na, delw1cnn3);
last更新卷积核minibatch(deltacnn12, 12, hIcnn2na, delw1cnn3);
last更新卷积核minibatch(deltacnn13, 13, hIcnn3na, delw1cnn4);
last更新卷积核minibatch(deltacnn14, 14, hIcnn3na, delw1cnn4);
last更新卷积核minibatch(deltacnn15, 15, hIcnn3na, delw1cnn4);
last更新卷积核minibatch(deltacnn16, 16, hIcnn3na, delw1cnn4);
}
int getAnsSoftmaxcnn()
{
int ans = 0;
for (int i = 1; i <10; i++)
if (softm[i] > softm[ans]) ans = i;
return ans;
}
private void button31_Click(object sender, EventArgs e)
{
Bitmap curBitmap = (Bitmap)pictureBox20.Image;
Rectangle rc = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);
System.Drawing.Imaging.BitmapData bmpdata = curBitmap.LockBits(rc,
System.Drawing.Imaging.ImageLockMode.ReadWrite,
curBitmap.PixelFormat);
IntPtr imageptr = bmpdata.Scan0;
int ww = curBitmap.Width;
int hh = curBitmap.Height;
int bytes = 0;
if (curBitmap.PixelFormat == PixelFormat.Format24bppRgb)
{
bytes = ww * hh * 3;//此处针对的是24位位图
}
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(imageptr, rgbValues, 0, bytes);
curBitmap.UnlockBits(bmpdata);
// showbuffer2pictmod4(showoutput14, 14, 14, pictureBox10);
byte[] buffer8 = new byte[28 * 28];
double[] buffer8d = new double[28 * 28];
if (curBitmap.PixelFormat == PixelFormat.Format24bppRgb)
{
for (int ii = 0; ii < 28; ii++)
{
for (int j = 0; j < 28; j++)
{
int n = ii * 28 + j;
int k = (ii) * 28 + j;
// int m = 3 * k;
buffer8[k] = rgbValues[n * 3];//这里都是灰度图像20221209
// buffer8[k] = (byte)(0.3 * rgbValues[n * 3 + 2] + 0.59 * rgbValues[n * 3 + 1] + 0.11 * rgbValues[n * 3]);//这里是1024*768图像
buffer8d[k] = rgbValues[n * 3] / 255f;//这里都是灰度图像20221209
}
}
}
showbuffer2pictmod4(buffer8, 28, 28, pictureBox20);
textBox51.Text = "";
xI = buffer8d;
cnnforward();
// int ans = getAnscnn();
int ans = getAnsSoftmaxcnn();
for (int i = 0; i < 10; i++)
textBox51.Text += ((float)softm[i]).ToString() + "\r\n";
textBox49.Text = ans.ToString() + ";" + ((float)yOcnn[ans]).ToString();
}
private void button30_Click(object sender, EventArgs e)
{
showbuffer2pictmod4(new byte[28 * 28], 28, 28, pictureBox20);
}
Bitmap weitu28;
float bilix28 = 0; float biliy28 = 0;
private void pictureBox20_MouseMove(object sender, MouseEventArgs e)
{
if (huax28)
{
Graphics g = pictureBox20.CreateGraphics();
g.DrawLine(new Pen(Color.White, 12f), qidianx28, new Point(e.X, e.Y));//huabi gao kuai yidian
// Bitmap curBitmap = (Bitmap)pictureBox9.Image;
//curBitmap.SetPixel(e.X, e.Y, Color.White);
//weitu28.SetPixel((int)(e.X * bilix28), (int)(e.Y * biliy28), Color.White); ;
try
{
weitu28.SetPixel((int)(qidianx28.X * bilix28), (int)(qidianx28.Y * biliy28 - 1), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(qidianx28.X * bilix28), (int)(qidianx28.Y * biliy28 + 1), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(qidianx28.X * bilix28), (int)(qidianx28.Y * biliy28), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(qidianx28.X * bilix28 - 1), (int)(qidianx28.Y * biliy28), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(qidianx28.X * bilix28 + 1), (int)(qidianx28.Y * biliy28), Color.FromArgb(255, 255, 255));
qidianx28 = new Point(e.X, e.Y);
weitu28.SetPixel((int)(e.X * bilix28 - 1), (int)(e.Y * biliy28 - 1), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(e.X * bilix28 + 1), (int)(e.Y * biliy28 + 1), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(e.X * bilix28), (int)(e.Y * biliy28), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(e.X * bilix28 - 1), (int)(e.Y * biliy28 + 1), Color.FromArgb(255, 255, 255));
weitu28.SetPixel((int)(e.X * bilix28 + 1), (int)(e.Y * biliy28 - 1), Color.FromArgb(255, 255, 255));
}
catch (Exception ee)
{ }
}
}
private void pictureBox20_MouseDown(object sender, MouseEventArgs e)
{
weitu28 = (Bitmap)pictureBox20.Image;
bilix28 = weitu28.Width * 1f / pictureBox20.Width;
biliy28 = weitu28.Height * 1f / pictureBox20.Height;
if (MouseButtons.Left == e.Button)
{
qidianx28.X = e.X;
qidianx28.Y = e.Y;
// e.Button
huax28 = true;
}
}
Point qidianx28 = new Point();
bool huax28 = false;
private void pictureBox20_MouseUp(object sender, MouseEventArgs e)
{
huax28 = false;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
this.learnRate = Convert.ToDouble(textBox1.Text);
}
}
}