多层神经网络与C++实现

BP理论部分参考:http://blog.csdn.net/itplus/article/details/11022243

参考http://www.cnblogs.com/ronny/p/ann_02.html#!comments,结合BP算法的理论部分,可以真正理解了ANN。

代码部分我加了部分注释

#include<vector>
using namespace std;

//单个连接线
class NNconnection
{
public:
    //两个索引,一个与该结点相连(前一层)的前一层结点的索引,
    //一个对应的权值索引在整个单层网络中权值向量中的索引
    unsigned weightIdx;
    unsigned neuralIdx;
};

//单个神经元,包括一个输出和多个连接线
class NNneural
{
public:
    double output;//输出
    vector<NNconnection> m_connection;
};

//单层网络
class NNlayer
{
public:
    NNlayer *preLayer;//该层网络的前一层
    NNlayer(){ preLayer = NULL; }
    vector<NNneural> m_neurals;//每层网络多个神经元
    vector<double> m_weights;//权值向量
    //加多少个神经元,及经前一层神经元的个数
    void addNeurals(unsigned num, unsigned preNumNeurals);
    //反向传播
    void backPropagate(vector<double>& ,vector<double>&,double);

};

class NeuralNetwork
{
private:
    unsigned nLayer;//网络层数
    vector<unsigned> nodes;//每层的结点数
    vector<double> actualOutput;//每次迭代的输出结果
    double etaLearningRate;//权值学习率
    unsigned iterNum;//迭代次数
public:
    vector<NNlayer*>m_layers;//由多个单层网络组成
    //创建网络,第二个参数为[48,25,30],则表明该网络有三层,每层结点数分别为48,25,30
    void create(unsigned num_layers, unsigned *ar_nodes);
    void initializeNetwork();//初始化网络,包括权值设置等

    void forwardCalculate(vector<double> &invect, vector<double> &outvect);//向前计算

    void backPropagate(vector<double>& tVect, vector<double>& oVect);//反向传播

    void train(vector<vector<double>>& inputVec, vector<vector<double>>& outputVec);//训练

    void classifier(vector<vector<double>>& inputVec, vector<vector<double>>& outputVec);//分类

};

void NeuralNetwork::initializeNetwork()
{
    //初始化网络,创建各层和各层结点,初始化权值
    // i为何如此定义?
    for (vector<NNlayer*>::size_type i = 0; i != nLayer; i++)
    {
        NNlayer *ptrLayer = new NNlayer;
        if (i == 0)
        {
            ptrLayer->addNeurals(nodes[i], 0);//第一层之前的结点数为0
        }
        else
        {
            ptrLayer->preLayer = m_layers[i - 1];
            //每个神经元的初值包括与前一层神经元的连接索引和该层权重索引
            ptrLayer->addNeurals(nodes[i], nodes[i - 1]);
            //连结权重个数
            unsigned num_weights = nodes[i] * (nodes[i - 1] + 1);//+bias
            //初始化权重
            for (vector<NNlayer*>::size_type k = 0; k != num_weights; k++)
            {

                ptrLayer->m_weights.push_back(0.05*rand() / RAND_MAX);//0~0.05
            }
        }
        m_layers.push_back(ptrLayer);
    }
}

void NNlayer::addNeurals(unsigned num, unsigned preNumNeural)
{
    for (vector<NNneural>::size_type i = 0; i != num; i++)
    {
        NNneural sneural;
        sneural.output = 0;
        for (vector<NNconnection>::size_type k = 0; k != preNumNeural; k++)
        {
            NNconnection sconnection;
            //给该神经元加上连接索引和权值索引
            sconnection.weightIdx = i*(preNumNeural + 1) + k;//加1给偏置留一个索引位置
            sconnection.neuralIdx = k;
            sneural.m_connection.push_back(sconnection);
        }
        m_neurals.push_back(sneural);
    }
}
void NeuralNetwork::forwardCalculate(vector<double> &invect, vector<double> &outvect)
{
    actualOutput.clear();
    vector<NNlayer*>::iterator layerIt = m_layers.begin();
    while (layerIt != m_layers.end())
    {
        if (layerIt == m_layers.begin())
        {
            for (vector<NNneural>::size_type k = 0; k != (*layerIt)->m_neurals.size(); k++)
            {
                //对第一层的神经元来说,输出即为输入
                (*layerIt)->m_neurals[k].output = invect[k];
            }
        }
        else
        {
            vector<NNneural>::iterator neuralIt = (*layerIt)->m_neurals.begin();
            int neuralIdx = 0;
            while (neuralIt != (*layerIt)->m_neurals.end())
            {
                //每个神经元的连接线数
                vector<NNconnection>::size_type num_connection = (*neuralIt).m_connection.size();
                //偏置
                double dsum = (*layerIt)->m_weights[num_connection*(neuralIdx + 1) - 1];
                for (vector<NNconnection>::size_type i = 0; i != num_connection; i++)
                {
                    //sum=sum+w*x;
                    unsigned wgtIdx = (*neuralIt).m_connection[i].weightIdx;
                    unsigned neuralIdx = (*neuralIt).m_connection[i].neuralIdx;

                    dsum += (*layerIt)->preLayer->m_neurals[neuralIdx].output*
                        (*layerIt)->m_weights[wgtIdx];
                }
                neuralIt->output = SIGMOID(dsum);

                neuralIt++;//下一个神经元
                neuralIdx++;//每个神经元的偏置不同
            }
        }
        layerIt++;//下一层网络
    }
    //将最后一层的结果保存至输出
    NNlayer * lastLayer = m_layers[m_layers.size() - 1];
    vector<NNneural>::iterator neuralIt = lastLayer->m_neurals.begin();
    while (neuralIt != lastLayer->m_neurals.end())
    {
        outvect.push_back(neuralIt->output);
        neuralIt++;
    }
}

void NeuralNetwork::backPropagate(vector<double>& tVect, vector<double>& oVect)
{
    //首先取得最后一层迭代器
    vector<NNlayer *>::iterator lit = m_layers.end() - 1;
    //用于保存最后一层所有结点误差
    vector<double> dErrWrtDxLast((*lit)->m_neurals.size());
    for (vector<NNneural>::size_type i = 0; i != (*lit)->m_neurals.size(); i++)
    {
        dErrWrtDxLast[i]=oVect[i] - tVect[i];
    }
    //所有层的误差
    vector<vector<double>> diffVect(nLayer);
    diffVect[nLayer - 1] = dErrWrtDxLast;

    //先将其他层误差设为0
    for (unsigned int i = 0; i < nLayer - 1; i++)
    {
        //每层误差的个数要与神经元相等
        diffVect[i].resize(m_layers[i]->m_neurals.size(), 0.0);
    }

    vector<NNlayer>::size_type i = m_layers.size() - 1;
    //对每一层调用BP算法,第一个参数为第i层输出误差
    //第二个参数可作为下次调用的返回值
    for (lit; lit>m_layers.begin(); lit--)
    {
        (*lit)->backPropagate(diffVect[i], diffVect[i - 1], etaLearningRate);
        i--;
    }
    diffVect.clear();
}

void NNlayer::backPropagate(vector<double>& dErrWrtDxn, vector<double>& dErrWrtDxnm, double eta)
{
    //三个参数分别代表第i层的误差,第i-1层的误差,学习速率
    //计算每个神经元的误差
    double output;
    vector<double> dErrWrtDyn(dErrWrtDxn.size());//每个神经元的残差
    for (vector<NNneural>::size_type i = 0; i != m_neurals.size(); i++)
    {
        output = m_neurals[i].output;
        //计算第i层的残差,对于输出层,dErrWrtDxn表示误差,对于
        //其他层,dErrWrtDxn表示w*(i+1层残差)
        dErrWrtDyn[i] = DSIGMOD(output)*dErrWrtDxn[i];
    }
    //计算每个w的偏导数
    unsigned ii(0);
    vector<NNneural>::iterator nit = m_neurals.begin();
    vector<double> dErrWrtDwn(m_weights.size(), 0);

    while (nit != m_neurals.end())
    {
        //对于每个神经元
        for (vector<NNconnection>::size_type k = 0; k != (*nit).m_connection.size(); k++)
        {
            //对于每个权重连接
            if (k == (*nit).m_connection.size() - 1)
                output = 1;//如果是偏置,则为1
            else//与该权重相连的前一层神经元的输出
                output = preLayer->m_neurals[(*nit).m_connection[k].neuralIdx].output;
            //计算该权重的偏导数值(随着迭代的进行,偏导也是逐渐累加的)
            dErrWrtDwn[((*nit).m_connection[k].weightIdx)] += output*dErrWrtDyn[ii];
        }
        nit++;
        ii++;
    }

    //dErrWrtDxnm作为下一层的dErrWrtDxn,用于计算残差
    unsigned j(0);
    nit = m_neurals.begin();
    while (nit != m_neurals.end())
    {
        for (vector<NNconnection>::size_type k = 0; k != (*nit).m_connection.size()-1; k++)
        {
            dErrWrtDxnm[(*nit).m_connection[k].neuralIdx] += dErrWrtDyn[j] *
                m_weights[(*nit).m_connection[k].weightIdx];
        }
        j++;
        nit++;
    }

    for (vector<double>::size_type i = 0; i != m_weights.size(); i++)
    {
        m_weights[i] -= eta*dErrWrtDwn[i];
    }
}

时间: 2024-10-14 10:09:48

多层神经网络与C++实现的相关文章

模式分类 学习笔记 第6章 多层神经网络

6.1引言 线性分类在解决很多问题是取得的最小误差率还显得不够! 一个精确选择的非线性函数,可以得到任意判决边界.但主要的困难是如何选择非线性函数.一个完备的基函数可能得到较好的效果,但可能会有太多的参数需要估计,而训练样本总是有限的! 再或者我们可能有先验知识引导我们选择非线性函数,但如果缺少这些信息,就无法自动选择非线性函数. 多层神经网络是一种在训练线性判别函数的同时学习其非线性程度的方法???决定非线性映射的参数的学习与控制线性判别函数的参数的学习同时进行的??? 多层神经网络基本上的执

ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程)

ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程) ufldl出了新教程,感觉比之前的好,从基础讲起,系统清晰,又有编程实践. 在deep learning高质量群里面听一些前辈说,不必深究其他机器学习的算法,可以直接来学dl. 于是最近就开始搞这个了,教程加上matlab编程,就是完美啊. 新教程的地址是:http://ufldl.stanford.edu/tutorial/ 本节学习地址:http://ufldl.stanfor

2017/7/20 朱兴全教授学术讲座观点与总结第三讲:多层神经网络

一.多层神经网络(为什么可以解决多种问题) 多层神经网络:在输入和输出层上有隐含层,可以克服单层神经网络的限制处理非线性分离问题 多层有更大的区分度,多条线去拟合 第三个图中,每一个方块对应第二个图中神经网络,即有两个隐含层. 二.Feedforward Neural Networks 1.FF NN模型 选择sigmoid函数作为激活函数的原因是其处处可导. 多层神经网络的误差,输出误差(期望输出与真实输出的差).单个样本网络误差(单个样本对应所有输出与期望输出的误差).所有样本的网络误差.

多层神经网络BP算法 原理及推导

首先什么是人工神经网络?简单来说就是将单个感知器作为一个神经网络节点,然后用此类节点组成一个层次网络结构,我们称此网络即为人工神经网络(本人自己的理解).当网络的层次大于等于3层(输入层+隐藏层(大于等于1)+输出层)时,我们称之为多层人工神经网络. 1.神经单元的选择 那么我们应该使用什么样的感知器来作为神经网络节点呢?在上一篇文章我们介绍过感知器算法,但是直接使用的话会存在以下问题: 1)感知器训练法则中的输出 由于sign函数时非连续函数,这使得它不可微,因而不能使用上面的梯度下降算法来最

多层神经网络的总结和理解

前言:今天从早上开始写吴恩达多层神经网络的题目.反复运算,出现一个天坑,一个存储矩阵的字典parameters的W1和b1在传入函数updata_parameters前维数还是(7, 12288), 传进去后变成了(3, 4)导致不能和grads["dW1"]的维数(7, 12288)进行广播出错.调了半天也不知道到底哪里出错了.最后重新写了一遍没有出错. 神经网络训练过程: 1.初始化参数 2.进行前向传播(其中分两部分,一部分是前向传播的计算部分,另一部分是前向传播的激活部分) 3

第5章 实现多层神经网络BP算法

前言 神经网络是一种很特别的解决问题的方法.本书将用最简单易懂的方式与读者一起从最简单开始,一步一步深入了解神经网络的基础算法.本书将尽量避开让人望而生畏的名词和数学概念,通过构造可以运行的Java程序来实践相关算法. 关注微信号"javaresearcher"来获取本书的更多信息. 上一章我们讨论了神经网络的表达能力的数学原理,这一章我们就来实现一个神经网络以及训练算法. 我们今天讨论类似下面的全连接多层单向神经网络: 我们把输入也看作一层,上图中一共有三层.输入和输出层是由问题的输

MXNET:多层神经网络

多层感知机(multilayer perceptron,简称MLP)是最基础的深度学习模型. 多层感知机在单层神经网络的基础上引入了一到多个隐藏层(hidden layer).隐藏层位于输入层和输出层之间.隐藏层中的神经元和输入层中各个输入完全连接,输出层中的神经元和隐藏层中的各个神经元也完全连接.因此,多层感知机中的隐藏层和输出层都是全连接层. 仿射变换 在描述隐藏层的计算之前,我们看看多层感知机输出层是怎么计算的.输出层的输入是隐藏层的输出,通常我们将隐藏层的输出称为隐藏层变量或隐藏变量.

学习笔记TF011:多层神经网络

线性回归.对数几率回归模型,本质上是单个神经元.计算输入特征加权和.偏置视为每个样本输入特征为1权重,计算特征线性组合.激活(传递)函数 计算输出.线性回归,恒等式(值不变).对数几率回归,sigmoid.输入->权重->求和->传递->输出.softmax分类含C个神经元,每个神经元对应一个输出类别. XOR异或运算,无法通过线性模型解决.sigmoido类型神经元要求数据线性可分.2D数据存在直线,高维数据存在超平面,把不同类别样本分隔. 在神经网络输入和输出之间插入更多神经元

keras做多层神经网络

一. 背景与目的 背景:配置好了theano,弄了gpu, 要学dnn方法. 目的:本篇学习keras基本用法, 学习怎么用keras写mlp,学keras搞文本的基本要点. 二. 准备 工具包: theano.numpy.keras等工具包 数据集: 如果下不来, 可以用迅雷下,弄到~/.keras/datasets/下面即可 代码位置:examples/reuters_mlp.py 三. 代码赏析 '''Trains and evaluate a simple MLP on the Reut

【UFLDL】多层神经网络

原英文教程地址见:http://ufldl.stanford.edu/tutorial/supervised/MultiLayerNeuralNetworks 本文是在学习该教程时记得笔记,供参考.周末的时候利用空闲时间用python实现了一下,但是训练结果总是不对,原因尚未查清楚,如果公式推导有误请指出,谢谢!