神经网络计算简单的加法运算

神经网络

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using ElemType = System.Double; //ElemType 元素的类型//默认是int型//别名
using Status = System.Int32;    //Status 现状;状态;地位

namespace BP
{
    class DPNet
    {

        #region 定义变量
        /// <summary>
        /// 网络层数
        /// </summary>
        protected int layerNum;             
        /// <summary>
        /// 每层节点数
        /// </summary>
        protected int[] nodeNumEachLayer;   
        /// <summary>
        /// 权重 w
        /// </summary>
        protected double[,,] weight;        
        /// <summary>
        /// 权重增量
        /// </summary>
        protected double[,,] deltaWeight;   
        /// <summary>
        /// 节点
        /// </summary>
        protected ElemType[,] node;         
        /// <summary>
        /// 增量
        /// </summary>
        //protected ElemType[,] delta;        
        /// <summary>
        /// 训练数量
        /// </summary>
        protected ElemType[] testNumber;    
        /// <summary>
        /// 输入
        /// </summary>
        protected ElemType[,] input;        
        /// <summary>
        /// 输出
        /// </summary>
        protected ElemType[,] output;       
        /// <summary>
        /// 样本数量
        /// </summary>
        protected int sampleNum;            
        /// <summary>
        /// 学习效率
        /// </summary>
        private double learningRate;        
        /// <summary>
        /// 学习动量
        /// </summary>
        private double learningMomentum;    //学习动量,相当于每次更新的时候,都会考虑上次的更新值,如果方向一样就变得越来越快,如果方向不同,就会相互抵消,以便收敛
        /// <summary>
        /// 各个节点误差
        /// </summary>
        protected ElemType[,] layerErr;//

        //protected ElemType [,]

        /// <summary>
        /// 学习效率
        /// </summary>
        public double LearningRate
        {
            get
            {
                return learningRate;
            }
            set
            {
                if (learningRate <= 1.0 || learningRate > 0)//学习效率要在0-1之间
                {
                    learningRate = value;
                }
                else
                {
                    Console.WriteLine("学习速率为非法值,本次改动无效");
                }
            }
        }

        public double LearningMomentum
        {
            get
            {
                return learningMomentum;
            }
            set
            {
                if (learningRate <= 1.0 || learningRate > 0)
                {
                    learningMomentum = value;
                }
                else
                {
                    Console.WriteLine("学习动量为非法值,本次改动无效");
                }
            }
        }

        #endregion

        #region 网络初始化  DPNet()
        /// <summary>
        /// 网络初始化
        /// </summary>
        public DPNet()
        {
            layerNum = 0;           //网络层数
            learningRate = 0.7;     //学习效率
            learningMomentum = 0.9; //学习动量
            sampleNum = 0;          //样本数量
            nodeNumEachLayer = null;//
            weight = null;
            deltaWeight = null;
           // delta = null;
            testNumber = null;
            node = null;
            input = null;
            output = null;
            layerErr = null;
        }
        #endregion

        #region 创建网络  CreateNet()
        /// <summary>
        /// 创建网络
        /// </summary>
        /// <param name="nums"></param>
        /// <returns></returns>
        public Status CreateNet(params int[] nums)      //params 参数//(3,2,8,1)
        {
            //FreeNet//检查网络层数//这里应该默认至少有一层隐藏层
            if (nums[0] <= 2)
            {
                Console.WriteLine("非法的神经网络层数");
                return -1;
            }

            layerNum = nums[0];
            nodeNumEachLayer = new int[layerNum];//通过一个数组保存每一层的节点数
            int maxNode = 0;            //最大节点数
            for (int i = 0; i < layerNum; i++)
            {
                if (nums[i + 1] >= 1)   // nums[i + 1]括号中的第i+1个元素//最少有一个节点,没有节点也就意味着没有有效的网络层 //(网络层数,输入层,隐藏层1节点,.....,隐藏层n节点,输出层节点)
                {
                    if (maxNode < nums[i + 1])   //获取最大节点数
                    {
                        maxNode = nums[i + 1];
                    }
                    nodeNumEachLayer[i] = nums[i + 1] + 1;      //加了1个偏置项
                }
                else
                {
                    Console.WriteLine("非法的节点数");
                    return -1;
                }
            }

            maxNode++;      //添加一个偏置项
            weight = new double[layerNum, maxNode, maxNode];//权重[网络层数,节点数,节点权重]
            deltaWeight = new double[layerNum, maxNode, maxNode];//权重增量[网络层数,节点数,节点权重增量]
            layerErr = new double[layerNum, maxNode]; //误差= [网络层数,最大节点数]
            node = new ElemType[layerNum, maxNode];         //节点= [网络层数,最大节点数]
            //delta = new ElemType[layerNum, maxNode];       

            Random random = new Random();
            //随机初始化w
            for (int i = 0; i < layerNum - 1; i++)  //-1 是因为w[]和d[]只有总网络层数-1维
            {
                for (int j = 0; j < nodeNumEachLayer[i + 1]; j++)   //下一层的每个节点
                {
                    for (int k = 0; k < nodeNumEachLayer[i]; k++)   //上一层的每个节点
                    {
                        weight[i, j, k] = random.NextDouble();
                        //do                                          
                        //{
                        //    weight[i, j, k] = random.NextDouble() * 2 - 1.0;//每个权重赋值,范围0-1之间
                        //}
                        //while (Math.Abs(weight[i, j, k]) < 1e-6); //如果w[]绝对值小于1x10的-6次方//Math.abs(n):对int、long、float、double类型的数取绝对值
                        // deltaWeight[i, j, k] = 0.0;         //权重增量不变
                    }
                }
            }
            return 0;                                       //return 0 代表正常退出,main函数还给操作系统
        }
        #endregion

        #region 加载样本数据文件 LoadSimplesFromFile()
        /// <summary>
        /// 加载样本数据文件
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public Status LoadSimplesFromFile(String fileName)
        {
            //Free
            string fileContent = File.ReadAllText(fileName);
            if (fileContent == null)
            {
                Console.WriteLine("未能成功打开样例文件");
                return -1;
            }
            string[] integerStrings = fileContent.Split     //通过分割获取样本数据
                (new char[] { ' ', '	', '
', '
' }, StringSplitOptions.RemoveEmptyEntries);

            sampleNum = int.Parse(integerStrings[0]);           //数组一个元素为样本数量
            int inputNodeNum = int.Parse(integerStrings[1]);    //第二个元素为输入节点数
            int outputNodeNum = int.Parse(integerStrings[2]);   //第三个元素为输出节点数

            input = new ElemType[sampleNum, inputNodeNum];
            output = new ElemType[sampleNum, outputNodeNum];

            if (input == null || output == null)
            {
                Console.WriteLine("创建input output数组失败");
                return -1;
            }
            int numIndex = 3;//为后面索引从第4个元素开始
            for (int i = 0; i < sampleNum; i++)
            {
                for (int j = 0; j < inputNodeNum; j++)//第i个样本的输入数据为
                {
                    input[i, j] = ElemType.Parse(integerStrings[numIndex]);
                    numIndex++;
                }
                for (int k = 0; k < outputNodeNum; k++)//第i个样本的输出数据为
                {
                    output[i, k] = ElemType.Parse(integerStrings[numIndex]);
                    numIndex++;
                }
            }
            return 0;
        }
        #endregion

        #region 训练单个样本 TrainSingleSample()
        /// <summary>
        /// 训练单个样本
        /// </summary>
        /// <param name="sampleIndex">样本在数组中的位置</param>
        /// <returns></returns>
        public double TrainSingleSample(int sampleIndex)
        {
            //赋值
            int i;
            for (i = 0; i < nodeNumEachLayer[0] - 1; i++)//第一层网络,输入训练数据赋值
            {
                node[0, i] = input[sampleIndex, i];
            }
            node[0, nodeNumEachLayer[0] - 1] = 1;//偏置项为1

            //前(正)向计算
            for (i = 1; i < layerNum; i++)
            {
                int j;
                for (j = 0; j < nodeNumEachLayer[i] - 1; j++)
                {
                    node[i, j] = 0;
                    for (int k = 0; k < nodeNumEachLayer[i - 1]; k++)
                    {
                        node[i, j] += node[i - 1, k] * weight[i - 1, j, k];//net1=x1*w1+x2*w2
                    }
                   node[i, j] /= (nodeNumEachLayer[i - 1]);//平均分配????
                    node[i, j] = 1.0 / (1.0 + Math.Exp(-node[i, j]));       //sigmoid激活函数,正向传播
                }
                node[i, j] = 1;//每层最后都有一个偏置项
            }

            //反向计算,根据误差修改权重w
            i = layerNum - 1;
            for (int j = 0; j < nodeNumEachLayer[i] - 1; j++)
            {
                layerErr [i, j] = node[i, j] * (1 - node[i, j]) * (node[i, j] - output[sampleIndex, j]);//o1*(1-01)*(01 - y)
            }

            int avoidThreshold = 1;
            for (i = layerNum - 2; i > 0; i--)
            {
                for (int j = 0; j < nodeNumEachLayer[i]; j++)
                {
                    layerErr [i, j] = 0;
                    int k = 0;
                    for (k = 0; k < nodeNumEachLayer[i + 1] - avoidThreshold; k++)
                    {
                        layerErr[i, j] += (weight[i, k, j] * layerErr [i + 1, k]);//上一层的误差= 
                    }
                    layerErr [i, j] *= (node[i, k] * (1.0 - node[i, k]));//记录误差
                }
                avoidThreshold = 0;//避免阈值
            }

            for (i = 0; i < layerNum - 1; i++)
            {
                if (i == layerNum - 2)
                    avoidThreshold = 1;
                for (int j = 0; j < nodeNumEachLayer[i + 1] - avoidThreshold; j++)
                {
                    for (int k = 0; k < nodeNumEachLayer[i]; k++)   //更新权重   i 网络层数;j该层网络的节点数,k 各个节点的权重w
                    {
                        weight[i, j, k] += learningMomentum * deltaWeight[i, j, k];
                        weight[i, j, k] -= learningRate * layerErr [i + 1, j] * node[i, k];//调整隐藏层权重
                        deltaWeight[i, j, k] = learningMomentum * deltaWeight[i, j, k] - learningRate * layerErr [i + 1, j] * node[i, k];//更新后的权重//隐含层动量调整//学习动量x变换权重-学习效率
                    }
                }
            }
            ElemType error = 0;  
            for (i = 0; i < nodeNumEachLayer[layerNum - 1] - 1; i++)
            {
                error += Math.Pow((node[layerNum - 1, i] - output[sampleIndex, i]), 2.0);//Loss损失函数
            }
            error /= 2.0;
            return error;
        }
        #endregion

        #region 训练 Train()
        /// <summary>
        /// 训练
        /// </summary>
        /// <param name="maxTurn">最大训练次数</param>
        /// <param name="allowedError"></param>
        /// <returns></returns>
        public Status Train(int maxTurn, ElemType allowedError)
        {

            for (int i = 0; i < maxTurn; i++)
            {
                Console.WriteLine("正在进行第" + (i + 1) + "次训练");
                ElemType error = 0;
                for (int j = 0; j < sampleNum; j++)
                {
                    error += TrainSingleSample(j);  //误差累积
                }
                error /= sampleNum;     
                Console.WriteLine("本轮误差" + error);

                if (error < allowedError)
                {
                    Console.WriteLine("已达到允许误差值");
                    return 0;
                }
            }
            Console.WriteLine("已达到最大训练数量");
            return 0;
        }
        #endregion

        #region 测试 Test()
        /// <summary>
        /// 测试
        /// </summary>
        /// <param name="testInput"></param>
        /// <param name="testOutput"></param>
        /// <returns></returns>
        public Status Test(ElemType[] testInput, ElemType[] testOutput)
        {
            int i;
            for (i = 0; i < nodeNumEachLayer[0] - 1; i++)//-1,偏置项
            {
                node[0, i] = testInput[i];
            }

            node[0, nodeNumEachLayer[0] - 1] = 1;//偏置项

            for (i = 1; i < layerNum; i++)//计算每层的输出值
            {
                int j;
                for (j = 0; j < nodeNumEachLayer[i] - 1; j++)
                {
                    node[i, j] = 0;
                    for (int k = 0; k < nodeNumEachLayer[i - 1]; k++)//
                    {
                        node[i, j] += node[i - 1, k] * weight[i - 1, j, k];
                    }
                    node[i, j] /= (nodeNumEachLayer[i - 1]); 
                    node[i, j] = 1.0 / (1.0 + Math.Exp(-node[i, j]));
                }
                node[i, j] = 1;
            }
            for (i = 0; i < nodeNumEachLayer[layerNum - 1] - 1; i++)//给testOutput赋值
            {
                testOutput[i] = node[layerNum - 1, i];
            }
            return 0;
        }
        #endregion

    }
}

程序主入口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ElemType = System.Double;

namespace BP
{
    class BPTest
    {
        static void Main(string[] args)
        {
            DPNet dpNet = new DPNet();
            String filename = "E:\test\BPNet-simple-plus--master\sample.txt";//样本数据文件路径
            dpNet.LoadSimplesFromFile(filename);            //加载样本数据文件
            dpNet.CreateNet(3, 2, 8, 1);                    //(网络层数,输入层,隐藏层1节点,.....,隐藏层n节点,输出层节点)
            dpNet.Train(19999, 1e-6);                        //训练次数9999,精度为1x10的负5次方
            dpNet.LearningMomentum=0.5;
            ElemType[] input = new ElemType[2];//两个输入数据
            ElemType[] output = new ElemType[1];//一个输出数据
            //ElemType[] forecastInput = new ElemType[2];
            //double forecastOutput = 0;
            while (true)
            {
                //读取数据
                string str = Console.ReadLine();
                string[] sGroup = str.Split(' ');
                input[0] = double.Parse(sGroup[0]);
                input[1] = double.Parse(sGroup[1]);

                dpNet.Test(input, output);
                Console.WriteLine(output[0] + "
");
            }
            //Console.WriteLine("请输入您想要的第一个预测数据(三位有效数字):");
            //forecastInput [0] = Convert.ToDouble(Console.ReadLine());
            //Console.WriteLine("请输入您想要的第二个预测数据(三位有效数字):");
            //forecastInput[1] = Convert.ToDouble(Console.ReadLine());
            //dpNet.Forecast (forecastInput);
            //Console.WriteLine(output);
            //Console.ReadKey();
        }
    }
}
原文地址:https://www.cnblogs.com/jiang2020/p/12606782.html