读代码学编程、读代码学理论、读代码学技术、读代码学专业英语


样本数据

蓝莓

草莓(好想吃啊)

训练神经网络-四步

InitializeData();//初始化训练集和测试集InitializeLayers();//初始化神经网络RunNetwork(1250, false);//运行神经网络TestNetwork(1000);//测试神经网络

初始化训练集和测试集

//蓝莓图片目录string BlueberryDirectory = @"D:\test/Image Data/Blueberries";//草莓图片目录string StrawberryDirectory = @"D:\test/Image Data/Strawberries";List<Data> STImageData = new List<Data>();//草莓图片映射的数据列表List<Data> BLImageData = new List<Data>();//蓝莓图片映射的数据列表List<Data> NewTrainingImageData = new List<Data>();//新训练图像数据列表List<Data> NewTestingImageData = new List<Data>();//新测试图像数据列表foreach (string BLimage in Directory.GetFiles(BlueberryDirectory))//遍历所有蓝莓图像{List<float> PixelValues = new List<float>(); //归一化的像素的R通道值列表//从指定的现有图像初始化Bitmap,缩放到指定的大小。Bitmap image = new Bitmap(Image.FromFile(BLimage), newSize: new Size(ImageResolution, ImageResolution));Data newData = new Data();//创建图像映射数据//获取所有for (int i = 0; i < ImageResolution; i++){for (int j = 0; j < ImageResolution; j++){float ColorValue = image.GetPixel(i, j).R;//读取像素的R通道值  红色PixelValues.Add(Remap(ColorValue, 0, 255, 0, 1));//将R值映射到【0,1】区间}}newData.IsBlueBerry = 1;//蓝莓设置为1newData.Values = PixelValues.ToArray();//归一化的R通道值数组BLImageData.Add(newData);//添加到  图像映射数据 列表}foreach (string STimage in Directory.GetFiles(StrawberryDirectory))//遍历所有草莓图像{List<float> PixelValues = new List<float>();//创建像素值列表//  Bitmap image = new Bitmap(STimage);//读取草莓图像 225x225像素。这里应该需要缩放到统一大小  //分辨率16x16 :  网络精度 92.8%  90.6%    分辨率225x225 :Bitmap image = new Bitmap(Image.FromFile(STimage), newSize: new Size(ImageResolution, ImageResolution));//78.7%  73.9%Data newData = new Data();for (int i = 0; i < ImageResolution; i++){for (int j = 0; j < ImageResolution; j++){float ColorValue = image.GetPixel(i, j).R;PixelValues.Add(Remap(ColorValue, 0, 255, 0, 1));//获取草莓 归一化的R通道像素值列表}}newData.IsBlueBerry = 0;//草莓设置为0newData.Values = PixelValues.ToArray();//草莓图像归一化的R通道像素值数组STImageData.Add(newData);//添加到 草莓图像映射数据列表}int BLSeparationNumber = Convert.ToInt32((float)BLImageData.Count * 0.26f);//分割蓝莓样本数据:训练集  测试集int STSeparationNumber = Convert.ToInt32((float)STImageData.Count * 0.26f);//分割草莓样本数据:26%测试集 for (int i = 0; i < STImageData.Count; i++){if (i < STSeparationNumber){NewTestingImageData.Add(STImageData[i]);//草莓测试集添加到总的测试集}else{NewTrainingImageData.Add(STImageData[i]);//添加其余的到训练集}}for (int i = 0; i < BLImageData.Count; i++){if (i < BLSeparationNumber){NewTestingImageData.Add(BLImageData[i]);//蓝莓测试集添加到总的测试集}else{NewTrainingImageData.Add(BLImageData[i]);// 其余添加到训练集}}TrainingData = NewTrainingImageData.ToArray();//所有训练集TestingData = NewTestingImageData.ToArray();//所有测试集RandomizeTrainingData();//打乱顺序RandomizeTestingData();

初始化神经网络

//将每一层附加到前一层,然后随机化这些层static void InitializeLayers(){Console.WriteLine("\n\nInitializing Layers...");//初始化所有层:偏置,权重数组if (Layers.Length >= 3)//4层{for (int i = 1; i < Layers.Length; i++){Layers[i].AppendToLayer(Layers[i - 1]);//初始化所有神经元权重数组}for (int i = 0; i < Layers.Length; i++){Layers[i].Randomize();//将所有神经元偏置 和 权重数组里的值 设置为随机浮点数}}}

前馈神经网络算法

//将数据输入网络以获得输出   Feed Data into the Network to get an outputstatic void FeedForward()//前馈{Inputs.SetValues(data.Values);//输入层:映射数据。测试时需要给全局变量data赋值for (int i = 1; i < Layers.Length; i++)//遍历非输入层的所有层{Layer layer = Layers[i]; // 第i层,下一层int index = 0;foreach (Neuron neuron in layer.Neurons)//遍历下一层的神经元,计算神经元的输出值{float newValue = 0;//foreach (Neuron prevNeuron in Layers[i - 1].Neurons)//遍历前一层神经元{newValue += prevNeuron.Value * prevNeuron.Weights[index];//前一层神经元值的加权和if (Double.IsNaN(Sigmoid((newValue + neuron.Bias))))//判断加权和的非线性变换   是否不为数字{throw new Exception("NaN value was found in network.");// LearnRate 0.5, BiasLeranRate 0.1 = 1:20s : NaN; LearnRate 0.005, BiasLearningRate 0.001 = 4:31s : 61.8% no ch;}}newValue += neuron.Bias;//加权和+偏置neuron.Value = Sigmoid(newValue);//非线性变换后得到神经元的输出值index++; //计算下一神经元的输出}}}

反向传播算法

//调整偏置和权重--反向传播   Adjust the biases and weightsstatic void BackPropagate(){float LearningRate = 0.005f;//学习率float BiasLearningRate = 0.001f;//偏置学习率float[] expectedAnswer;//期望输出数组if (data.IsBlueBerry == 1)expectedAnswer = new float[] { 1, 0 };//蓝莓elseexpectedAnswer = new float[] { 0, 1 };//草莓//Begin with changing the Weights connected to the output layer//首先更改连接到输出层的权重//foreach Neuron in Outputs  遍历输出层的神经元,计算输出层神经元的gamma,调整上一层神经元的权重和偏置。for (int NeuronNum = 0; NeuronNum < Outputs.Length; NeuronNum++) //遍历输出层的神经元{Neuron neuron = Outputs[NeuronNum];//取输出层的一个神经元//   Gamma= (实际输出-期望输出)*激活函数的导数     可以把导数理解为该神经元对残差的贡献比例 。也可理解为连续激活的概率。float Gamma = (neuron.Value - expectedAnswer[NeuronNum]) * (1 - (neuron.Value) * (neuron.Value));//残差*该神经元sigmoid导数neuron.Gamma = Gamma;//分到输出层神经元的锅,//遍历倒数第二层神经元   foreach Neuron in Previous Layerfor (int PrevLayerNum = 0; PrevLayerNum < Layers[Layers.Length - 2].Length; PrevLayerNum++){//计算要减去的新值 Calculate a New Value to Subtractfloat Delta = Gamma * Layers[Layers.Length - 2][PrevLayerNum].Value;//为降低代价函数 计算每一神经元调整量//应用新值来调整指向神经元的权重   Apply the New Value to Adjust the Weight thats pointing to the neuronLayers[Layers.Length - 2][PrevLayerNum].Weights[NeuronNum] -= Delta * LearningRate;//调整权重     减去所需调整量*学习率//应用新值来调整先前权重的偏差 Apply the New Value to Adjust the Bias of the Previous WeightLayers[Layers.Length - 2][PrevLayerNum].Bias -= Delta * BiasLearningRate;//调整偏置   }}for (int LayerNum = Layers.Length - 2; LayerNum > 0; LayerNum--)//反向遍历所有层  ,从倒数第二层开始计算。{Layer layer = Layers[LayerNum];//取一层神经元,第一次执行为倒数第二层for (int NeuronNum = 0; NeuronNum < layer.Length; NeuronNum++)//遍历该层所有神经元{Neuron neuron = layer[NeuronNum];//取一个神经元float Gamma = 0;//初始化神经元改分的锅   第一次执行为倒数第二层的神经元for (int NeuronNum2 = 0; NeuronNum2 < Layers[LayerNum + 1].Length; NeuronNum2++)//遍历后一层神经元-  相对的输出层。第一次执行是最后一层即输出层。{Gamma += Layers[LayerNum + 1][NeuronNum2].Gamma * Layers[LayerNum][NeuronNum].Weights[NeuronNum2];//后一层的NeuronNum2神经元的gamma * 该神经元的NeuronNum2权重 再求和。加权残差 }Gamma *= (1 - neuron.Value * neuron.Value);//加权残差 * sigmoid导数neuron.Gamma = Gamma;//神经元分到的锅。第一次执行为倒数第二层//遍历前一层神经元   计算其新的权重和偏置      foreach Neuron in Previous Layerfor (int PrevLayerNum = 0; PrevLayerNum < Layers[LayerNum - 1].Length; PrevLayerNum++){//计算要减去的新值float Delta = Gamma * Layers[LayerNum - 1][PrevLayerNum].Value;//应用新值来调整指向神经元的权重Layers[LayerNum - 1][PrevLayerNum].Weights[NeuronNum] -= Delta * LearningRate;//应用新值来调整先前权重的偏置Layers[LayerNum - 1][PrevLayerNum].Bias -= Delta * BiasLearningRate;}}}}

运行神经网络

static void RunNetwork(int Iterations, bool log){Console.WriteLine("\n\nRunning Network...\n\n");for (int i = 0; i < Iterations; i++){print($"Iteration {i + 1} / {Iterations} Completed\n");GetNextTrainingData();//获取打乱顺序的训练数据集FeedForward();//前馈BackPropagate();//反向传播}void print(string str){if (log){Console.WriteLine(str);//控制台输出  准确率百分比}}}

测试神经网络

static void TestNetwork(int Iterations){int CorrectAnswers = 0;//正确答案数for (int i = 0; i < Iterations; i++)//反复测试不同图像  Iterations{GetNextTestingData();//获取下一 测试数据,修改全局变量dataFeedForward();//前馈:输入图像映射数据 计算神经网络输出Console.WriteLine("\n\nImage: " + data.IsBlueBerry);//标签Console.WriteLine("0: " + Outputs[0].Value);// 是蓝莓的答案。1是,0否Console.WriteLine("1: " + Outputs[1].Value);//是草莓的答案。1是,0否//判断神经网络输出是否与标签一致if ((data.IsBlueBerry == 1 && Outputs[0].Value > Outputs[1].Value) || (data.IsBlueBerry == 0 && Outputs[1].Value > Outputs[0].Value)){Console.WriteLine("\nCorrect");//输出与标签一致,打印正确 CorrectAnswers++;//正确数量}else{Console.WriteLine("\nIncorrect");}}Console.WriteLine($"\n\nNetwork Accuracy: {(float)CorrectAnswers / (float)Iterations * 100f}%");//打印正确率  92.8}

该BP神经网络仅把输入图像缩放后的像素R通道归一化后的值作为特征向量(图像映射数据),随着缩放尺寸不同,训练后的神经网络准确度也不同,缩放尺寸越大,训练时间越长,识别精度越差。加上样本数量有限,试验了几次识别率最高不到93% 。图像目标识别还得用卷积神经网络。

运行效果


笔记:

Sigmoid函数

Sigmoid函数导数最大值为0.25,因链式法则需要连乘,故进行反向传播时容易导致梯度消失。前边神经元的权重可能得不到更改。

BP神经网络

卷积神经网络

下面我们来看一下卷积神经网络中神经元所代表的实质含义。下面的gif图展示了size为3X3 ,strides为2的filter (分别有蓝绿两个filters)对一个 5X5 的输入图像做卷积的情形。

在该示例中,蓝色和绿色的 3X3  filter就是神经元。仍然套用上面所提到的3个步骤来分析这个卷积过程:

1.输入:接收前一层的输入数据,即 5X5X3 的图像。可以看出,此时的输入数据已经不是传统机器学习中所定义的高维向量了,而是一个多维的张量(Tensor)。

2. 操作

1.   注意到,神经元内的参数个数(本例中为 3X3X3 )通常小于输入数据的个数(本例中为 5x5x3 ),神经元会和在输入数据上进行滑动计算,直到扫过输入数据中的每个部分,并求出多个值,最终产生一个二维矩阵,通常也被称为feature map。(补充一下:三维张量经过卷积后变成了二维矩阵是因为原始尺寸只有长宽不同,而深度是相同的。假如filter和输入数据的尺寸在深度上也不同,那么输出的结果还是一个三维张量)

2.   在上述大框架的基础上,我们还需要引入bias和非线性变换。关于bias,仍然是一个神经元配备一个bias。以绿色神经元为例,对于其4个输出值,均加上相同的bias值 b0 。在加完bias之后,再对这4个输出值apply非线性变换,通常会是ReLU(Rectified Linear Unit)整流线性单元

3. 输出:经过卷积、加bias、非线性变换操作之后的feature map(特征映射).

通过对比BP神经元和卷积神经元的异同,我们似乎可以得出以下总结:

·       神经元的存在形式是一系列参数,并且是可学习的参数。

·       神经元可以接受不同尺寸的输入,也可以将输入与内部参数进行任意运算/操作(但操作方式应予输入尺寸相匹配)。

·       神经元的输出大小取决于输入数据的大小和神经元操作方式

·       神经元在输出前需要apply非线性函数

·       一个神经元配备一个bias

另一个思考的角度是:经过神经元的处理,输出通常都会比输入数据的尺寸要小,这其实是一种信息整合的体现。具体而言:

·       BP的神经元一次性就能够看见前一层传递过来的所有信息(正因此被称为全连接),因此操作时能将全部信息整合在一起,产生的输出为一个数。

·       而卷积神经元一次只能看到前一层传递过来信息的一个局部,需要通过滑动来将信息cover全,因此其产生的输出不止一个数,并且输出的信息凝聚程度也不如全链接。尽管如此,CNN还是更适合用来处理图像的,因为它保留了图像的二维结构,且所需神经元参数个数相对少、计算也相对少。和BP相比,相同的网络参数个数情形下,CNN可以更深,也就意味着非线性变换可以更多。


深入理解神经网络

1.   MP神经元

2.   感知机

来源:《机器学习》——周志华

3.   多层前馈神经网络

来源:《机器学习》——周志华

4.   误差逆传播算法(BP算法)

参考:

1>https://zhuanlan.zhihu.com/p/37773135 反向传播之二:sigmoid函数 - 知乎 (zhihu.com)

2>https://zhuanlan.zhihu.com/p/28735794 人工神经网络中的神经元 - 知乎 (zhihu.com)

3>https://wasedamagina.github.io/archives/  归档 | Magina的个人小站 (wasedamagina.github.io)

4>https://zhuanlan.zhihu.com/p/35407734 深入理解神经网络 - 知乎 (zhihu.com)

5> https://www.cnblogs.com/maybe2030/p/5597716.html [Deep Learning] 神经网络基础 - Poll的笔记 - 博客园 (cnblogs.com)

6> https://blog.csdn.net/fsfjdtpzus/article/details/106256925 机器学习笔记丨神经网络的反向传播原理及过程(图文并茂+浅显易懂)_アルゴノゥト的博客-CSDN博客_神经网络反向传播原理

【神经网络-C#】蓝莓、草莓神经网络分类器demo源码学习-理解神经网络相关推荐

  1. Api demo源码学习(8)--App/Activity/QuickContactsDemo --获取系统联系人信息

    本节通过Content Provider机制获取系统中的联系人信息,注意这个Anctivity直接继承的是ListActivity,所以不再需要setContentView函数来加载布局文件了(我自己 ...

  2. Api demo源码学习(4)--App/Activity/Dialog --Activity以Dialog形式呈现

    这一节实际上比 Api demo源码学习(2)--App/Activity/Custom Dialog 自定义Activity样式  还要简单一些,在源码学习(2)里,也是让Activity以Dial ...

  3. Neo4j【环境部署 01】图形数据库(简介+下载地址+安装+配置+demo源码+学习教程地址)

    1.简介 Neo4j是一个高性能的.NOSQL图形数据库,它将结构化数据存储在网络上而不是表中.它是一个嵌入式的.基于磁盘的.具备完全的事务特性的Java持久化引擎,但是它将结构化数据存储在网络(从数 ...

  4. 卷积神经网络(三):卷积神经网络CNN的简单实现(部分Python源码)

    转载自: 卷积神经网络(三):卷积神经网络CNN的简单实现(部分Python源码) - xuanyuansen的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/xu ...

  5. winserver的consul部署实践与.net core客户端使用(附demo源码)

    前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是"拆",把臃肿的单块应用,拆分成多个轻量级的服务,每个服务可以在短周期内重构.迭代.交付.随着微服务的数量增多,因量变 ...

  6. vscode插件开发实践与demo源码

    vscode插件开发实践与demo源码 写在前面 工欲善其事必先利其器.vscode作为优秀的开发工具,给我的日常开发工作提供了极大的便利.其拓展机制更是如此. 但是,最近在做年度专业线任务时,有需要 ...

  7. C# Windows系统音量调节Demo源码

    基于.NET Framework 4.0开发,包含滑块设置音量,键盘F1.F2调节音量. 通过滑块调节系统音量核心代码 /// <summary>/// 滑块设置音量/// </su ...

  8. GEF教程及demo源码

    GEF教程及demo源码下载http://download.csdn.net/download/sidihuo/9695103 GEF教程及demo源码

  9. GreenDao的简单学习(附带demo源码)

    先看效果图: 添加实体: 按条件查询实体: GreenDao 概述:     适用于Android的轻量级快速ORM框架,可将对象映射到SQLite数据库中,并且针对Andriod进行了高度的优化,g ...

最新文章

  1. Android学习笔记(一) - 如果我们来设计Android
  2. Exception in thread main java.lang.IncompatibleClassChangeError: net/sf/cglib/core/DebuggingClassW
  3. XCode修改工程名注意
  4. 【数学与算法】PCA主成分分析(降维)的通俗理解
  5. 创建一个纯色的背景图
  6. 技术实践 | Web 端实现 RTC 视频特效的解决方案
  7. 【SSL】java keytool工具操作PCKS12证书库
  8. mysql 链接慢_mysql连接非常慢的觖决办法及其它常见问题解决办法
  9. 机器学习物语(3):回归问题
  10. Elastic Stack 安装
  11. http库cookiejar模块
  12. mac/unix系统:C++实现一个端口扫描器
  13. oracle去除检查非空,oracle 主键,非空,检查,唯一,默认,外键约束
  14. OpenCV :(-5:Bad argument) Matrix operand is an empty matrix. in function ‘checkOperandsExist‘
  15. java带参数的方法笔记_Java学习笔记十一:Java中的方法
  16. readelf ELF 文件格式分析
  17. 网站被挂黑链是什么原因,如何解决挂黑链问题!
  18. 计算机扫描服务开启,扫描系统(window怎么开启扫描服务)
  19. 悲剧!广电总局12月11日将封闭的网站目录!!!
  20. 伦敦金行情走势k线图

热门文章

  1. 山东工业技师学院孙洪文计算机,山东工业技师学院召开山东省集训选拔赛项目总结会议...
  2. 国土防线2计算机内存不足,我安装国土防线后打开就出现这个 怎么回事啊
  3. 怎样增加混凝土粘聚性_如何有效改善混凝土的粘聚性和保水性
  4. MyBatis代码生成器Easy Code
  5. 中国大学MOOC翁恺C语言PTA入门练习(更新中)
  6. C语言趣味小游戏——三子棋
  7. iTunes空间不足无法备份iphone的问题
  8. 二叉排序树的删除,全网最详解析
  9. 【最简单最快的方式】虚拟机-CentOS7创建共享文件夹——Centos笔记-Day1
  10. python制作ppt改变颜色标题_摆脱千篇一律的PPT字体和颜色,自定义设置