C++ 神经网络中的反向传播算法

C++ 神经网络中的反向传播算法,c++,machine-learning,neural-network,C++,Machine Learning,Neural Network,我在神经网络中实现反向传播时遇到了一些问题。这个实现使用了Andrew Ng的Coursera机器学习课程幻灯片中的想法(这里是链接)。我想我已经理解了算法,但是代码中有一些微妙的错误 我使用的网络有一个输入层、一个隐藏层和一个输出层。它们分别有2+1、2+1、1个神经元(+1表示偏倚)。 当我尝试实现逻辑AND和逻辑OR时,一切都很好,网络学会了给出正确的值。但后来我尝试实现XNOR(XNOR b=NOT(XOR b)) 我用了4个例子: 01 0110 100 11 但是突然,在这个函数

我在神经网络中实现反向传播时遇到了一些问题。这个实现使用了Andrew Ng的Coursera机器学习课程幻灯片中的想法(这里是链接)。我想我已经理解了算法,但是代码中有一些微妙的错误

我使用的网络有一个输入层、一个隐藏层和一个输出层。它们分别有2+1、2+1、1个神经元(+1表示偏倚)。 当我尝试实现逻辑AND和逻辑OR时,一切都很好,网络学会了给出正确的值。但后来我尝试实现XNOR(XNOR b=NOT(XOR b))

我用了4个例子:

  • 01
  • 0110
  • 100
  • 11
但是突然,在这个函数上,梯度什么地方都去不了。在开始时,我使用随机小数字(从-0.01到0.01)初始化权重。输出接近0.5。然后我做梯度下降。在任何输入上,输出始终接近0.5

我想知道如何解决这个问题

代码如下:

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

// contains matrix and Vector classes.
// Vector is just like std::valarray, but is compatible with my matrix.
#include "matrix.hpp" 

size_t L;
std::vector< Vector<double> > layers;
std::vector< matrix<double> > theta;

struct Example
{
    Vector<double> x;
    Vector<double> y;
};

using TrainingSet = std::vector<Example>;

TrainingSet examples;

double g(double x)
{
    return 1 / (1 + exp(-x));
}

void forwardPropagate(Vector<double> x)
{
    for ( size_t i = 1; i < layers[0].size(); ++i )
        layers[0][i] = x[i - 1];

    for ( size_t i = 0; i < L - 1; ++i )
    {
        auto z = theta[i] * layers[i];
        for ( size_t j = 1; j < layers[i + 1].size(); ++j )
            layers[i + 1][j] = g(z[j - 1]);
    }
}

void backwardPropagate(Vector<double> y, std::vector< matrix<double> >& delta)
{
    auto err = layers.back().slice(1) - y;

    for ( int i = L - 2; i >= 0; --i )
    {   
        delta[i] += asMatrix(err) * asMatrix(layers[i]).transpose();

        auto gdz = layers[i] * (Vector<double>(layers[i].size(), 1.0) - layers[i]);
        auto tmp = theta[i].transpose() * err * gdz;
        err = tmp.slice(1);
    }
}

double costFunction(const TrainingSet& examples)
{
    double result = 0.0;

    for ( const auto& example : examples )
    {
        std::cout << layers.back()[1] << '\n';

        forwardPropagate(example.x);
        for ( size_t k = 1; k < layers.back().size(); ++k )
        {
            auto h = layers.back()[k];
            auto y = example.y[k - 1];
            result += y * log(h) + (1 - y) * log(1 - h);
        }
    }

    return (-result) / examples.size();
}

void computeGradient(std::vector< matrix<double> >& delta, const TrainingSet& examples)
{
    for ( auto& m : delta )
        m.fillWith(0);

    for ( auto example : examples )
    {
        forwardPropagate(example.x);
        backwardPropagate(example.y, delta);
    }

    for ( auto& m : delta )
        m /= examples.size();
}

void gradientDescentStep(const std::vector< matrix<double> >& gradient)
{
    const double alpha = 0.01;

    for ( size_t i = 0; i < L - 1; ++i )
        theta[i] -= alpha / examples.size() * gradient[i];
}

double gradientDescent(const TrainingSet& examples)
{
    const double eps = 0.0000001;

    double prev, cur;
    cur = costFunction(examples);

    size_t iterations = 0;
    const size_t max_iterations = 200000000;

    std::vector< matrix<double> > delta;
    delta.reserve(L - 1);
    for ( size_t i = 0; i < L - 1; ++i )
        delta.emplace_back(theta[i].rows(), theta[i].cols());

    do
    {
        prev = cur;
        computeGradient(delta, examples);
        gradientDescentStep(delta);
        cur = costFunction(examples);

    } while ( fabs(cur - prev) >= eps && iterations++ < max_iterations );

    std::cout << "Made " << iterations << " iterations\n";

    return cur;
}

int main()
{
    std::ifstream fin("input.txt");    
    std::istream& in = fin;    

    std::cout.sync_with_stdio(false);

    in >> L;
    std::vector<size_t> architecture(L);

    for ( size_t i = 0; i < L; ++i )
        in >> architecture[i];

    layers.reserve(L);
    for ( size_t i = 0; i < L; ++i )
    {
        layers.emplace_back(1 + architecture[i]);
        layers.back()[0] = 1;
    }

    const double eps = 0.01;    

    theta.reserve(L - 1);
    for ( size_t i = 0; i < L - 1; ++i )
    {
        theta.emplace_back(layers[i + 1].size() - 1, layers[i].size());
        theta[i].randomInitialize(eps);
    }

    size_t number_of_examples;
    in >> number_of_examples;

    examples.reserve(number_of_examples);
    for ( size_t i = 0; i < number_of_examples; ++i )
    {
        auto x = Vector<double>(architecture.front());
        auto y = Vector<double>(architecture.back());

        for ( size_t j = 0; j < architecture.front(); ++j )
            in >> x[j];

        for ( size_t j = 0; j < architecture.back(); ++j )
            in >> y[j];

        examples.emplace_back(Example{x, y});
    }

    for ( auto example : examples )
    {
        forwardPropagate(example.x);
        std::cout << layers.back()[1] << '\n';
    }

    for ( size_t i = 0; i < theta.size(); ++i )
        std::cout << "θ[" << i << "] = " << theta[i];

    gradientDescent(examples);

    for ( size_t i = 0; i < theta.size(); ++i )
        std::cout << "θ[" << i << "] = " << theta[i];

    std::cout << "\n\n\n";

    for ( auto example : examples )
    {
        forwardPropagate(example.x);
        std::cout << layers.back()[1] << '\n';
    }

    return 0;
}
#包括
#包括
#包括
#包括
//包含矩阵和向量类。
//Vector与std::valarray类似,但与我的矩阵兼容。
#包括“matrix.hpp”
大小;
std::vector层;
std::向量<矩阵>θ;
结构示例
{
向量x;
向量y;
};
使用TrainingSet=std::vector;
培训集示例;
双g(双x)
{
返回1/(1+exp(-x));
}
void(向量x)
{
对于(大小i=1;i<层[0]。大小();++i)
层[0][i]=x[i-1];
对于(尺寸i=0;i&增量)
{
自动错误=layers.back().slice(1)-y;
对于(int i=L-2;i>=0;--i)
{   
delta[i]+=asMatrix(err)*asMatrix(layers[i]).transpose();
auto-gdz=layers[i]*(向量(layers[i].size(),1.0)-layers[i]);
自动tmp=theta[i].transpose()*err*gdz;
err=tmp.slice(1);
}
}
双成本函数(常数训练集和示例)
{
双结果=0.0;
例如(常量自动&示例:示例)
{
标准:坡度和坡度)
{
常数双α=0.01;
对于(尺寸i=0;i增量;
三角洲储量(L-1);
对于(尺寸i=0;i=eps&&iterations++architecture[i];
3.保护区(L);
对于(尺寸i=0;i>中,示例的数量;
示例。储量(示例的数量);
对于(大小i=0;i<示例数;++i)
{
auto x=Vector(architecture.front());
auto y=向量(architecture.back());
用于(尺寸j=0;j>x[j];
对于(大小j=0;j>y[j];
示例。将_向后放置(示例{x,y});
}
例如(自动示例:示例)
{
前向传播(示例x);

std::cout最后我意识到了问题所在。问题不在代码本身。问题是,在使用XOR的这种网络配置上,代价函数具有局部最小值。因此,我来到那里并被卡住了


解决方案是在随机方向上迈出一步,直到你从局部最小值中跳出来。这可以让你很快地到达全局最小值。

一般来说,随机初始化权重在~[-3,3]范围内。初始较大的误差量(通过较大的权重)有助于“跳跃”是的,XOR问题有局部极小值,但是你的网络应该能够很容易地收敛到只有几个隐藏节点的正确答案

你不应该“需要”跳出局部极小值,它应该很容易收敛到正确的最优值。网络结构为[2,2,1]的XOR问题的权重非常小,你可以相对快速地“随机初始化”你的权重到正确的最优值。(因为搜索空间很小)


注意/编辑如果您的网络只有2个隐藏节点,那么这是一个很好的更改,它将停留在局部极小值。即使网络大小为~[2,7,1]如果没有随机步骤,就可以收敛。

我不太理解C++,可以读取代码。你是否已经通过调试器完成了这个步骤,并监视了权重的变化?我希望隐藏层将学习<代码>和<代码>和NoD> < /Cord>函数。它们是重复的例子?@Igenchris我监测了权重的变化,实际上,它们变化很小,大约10^(-6)。如果我将初始值设置为间隔(-ε,ε)ε=0.5,那么我的梯度下降会进行大约2000次迭代。如果我设置ε=0.01,那么它根本不会进行任何迭代。即使重复,我也没有尝试过更多的数据,但我即将进行d