C++ 卷积网络滤波器总是负的

C++ 卷积网络滤波器总是负的,c++,machine-learning,neural-network,logistic-regression,backpropagation,C++,Machine Learning,Neural Network,Logistic Regression,Backpropagation,我问了一个关于我一直在构建的网络的问题,我反复讨论了一些建议,这些建议导致我发现了一些问题。我回到这个项目,解决了所有的问题,在这个过程中,我学到了更多关于CNN的知识。现在我陷入了一个问题:我所有的权重都变成了巨大的负值,再加上输出图像中的RELU端总是完全黑色(这使得分类器无法完成它的工作) 在两个标记的图像上: 这些被传递到一个两层网络,一个分类器(它自己获得100%)和一个单过滤器3*3卷积层 在第一次迭代中,conv层的输出如下所示(图像顺序与上面相同): 由于图像为RGB,因

我问了一个关于我一直在构建的网络的问题,我反复讨论了一些建议,这些建议导致我发现了一些问题。我回到这个项目,解决了所有的问题,在这个过程中,我学到了更多关于CNN的知识。现在我陷入了一个问题:我所有的权重都变成了巨大的负值,再加上输出图像中的RELU端总是完全黑色(这使得分类器无法完成它的工作)

在两个标记的图像上:

这些被传递到一个两层网络,一个分类器(它自己获得100%)和一个单过滤器3*3卷积层

在第一次迭代中,conv层的输出如下所示(图像顺序与上面相同):

由于图像为RGB,因此过滤器为3*3*3。权重均为0.0f-1.0f之间的随机数。在下一次迭代中,图像是完全黑色的,打印过滤器显示它们现在的范围是-49678.5f(我能看到的最高值)和-61932.3f

这一问题又是由于从逻辑回归/线性层传回的梯度对于交叉来说非常高(标签0,预测0)。对于圆(标签1,预测0),值大约在-12和-5之间,但对于十字,它们都在正高1000到正高2000范围内

发送这些信息的代码如下所示(某些部分省略):

void LinearClassifier::序列(浮点*x,浮点输出,浮点y)
{
浮点h=输出-y;
浮动平均值=0.0f;
对于(int i=1;i
这将传递回单个卷积层:

// This code is in three nested for loops (for layer,for outWidth, for outHeight)
float gradient = 0.0f;
// ReLu Derivative
if ( m_pOutputBuffer[outputIndex] > 0.0f) 
{
    gradient = outputGradients[outputIndex];
}

for (int z = 0; z < m_InputDepth; ++z)
{
    for ( int u = 0; u < m_FilterSize; ++u)
    {
        for ( int v = 0; v < m_FilterSize; ++v)
        {
            int x = outX + u - 1;
            int y = outY + v - 1;

            int inputIndex = x + y*m_OutputWidth + z*m_OutputWidth*m_OutputHeight;

            int kernelIndex = u + v*m_FilterSize + z*m_FilterSize*m_FilterSize;

            m_pGradients[inputIndex] += m_Filters[layer][kernelIndex]*gradient;
            m_GradientSum[layer][kernelIndex] += input[inputIndex]*gradient;
        }
    }
}
//此代码在三个嵌套for循环中(对于层、对于向外宽度、对于向外高度)
浮动梯度=0.0f;
//瑞鲁导数
如果(m_pOutputBuffer[outputIndex]>0.0f)
{
梯度=输出梯度[输出指数];
}
对于(intz=0;z

通过以一次一个的方式传递每个图像来迭代此代码。梯度显然朝着正确的方向发展,但是我如何阻止巨大的梯度抛出预测函数呢

我通过缩小CNN层的梯度来解决这个问题,但现在我不明白为什么需要这样做,所以如果有人能凭直觉知道为什么这样做会很好

我通过缩小CNN层的梯度来解决这个问题,但现在我不明白为什么需要这样做,所以如果有人能凭直觉知道为什么这样做会很好

RELU激活就是因为这样做而臭名昭著的。你通常必须使用低学习率。这背后的原因是,当RELU返回正数时,它可以继续自由学习,但当一个单位返回一个非常低的数时,它可能会成为一个“死亡”神经元,永远不会再次激活。如果您有一个系统,其中正数可以自由更改,但低于某个阈值的负数有可能“卡住”,那么如果可以达到该最小阈值,系统最终将完全卡住

此外,使用RELU神经元初始化你的体重是非常微妙的。您似乎正在初始化到范围0-1,这会产生巨大的偏差。这里有两个提示-使用以0为中心的范围和更小的范围。类似于(-0.1)-(0.1)


另一件事是,根据我的经验,RELU单元似乎在困难的任务(如Imagenet分类)中更有用。让TanH单位试试这个。初始化权重和学习速率的方式并没有那么微妙。

RELU激活就是因为这样做而臭名昭著的。你通常必须使用低学习率。这背后的原因是,当RELU返回正数时,它可以继续自由学习,但当一个单位返回一个非常低的数时,它可能会成为一个“死亡”神经元,永远不会再次激活。如果您有一个系统,其中正数可以自由更改,但低于某个阈值的负数有可能“卡住”,那么如果可以达到该最小阈值,系统最终将完全卡住

此外,使用RELU神经元初始化你的体重是非常微妙的。您似乎正在初始化到范围0-1,这会产生巨大的偏差。这里有两个提示-使用以0为中心的范围和更小的范围。类似于(-0.1)-(0.1)

另一件事是,根据我的经验,RELU单元似乎在困难的任务(如Imagenet分类)中更有用。让TanH单位试试这个。你初始化权重和学习速度的方式并不那么微妙

// This code is in three nested for loops (for layer,for outWidth, for outHeight)
float gradient = 0.0f;
// ReLu Derivative
if ( m_pOutputBuffer[outputIndex] > 0.0f) 
{
    gradient = outputGradients[outputIndex];
}

for (int z = 0; z < m_InputDepth; ++z)
{
    for ( int u = 0; u < m_FilterSize; ++u)
    {
        for ( int v = 0; v < m_FilterSize; ++v)
        {
            int x = outX + u - 1;
            int y = outY + v - 1;

            int inputIndex = x + y*m_OutputWidth + z*m_OutputWidth*m_OutputHeight;

            int kernelIndex = u + v*m_FilterSize + z*m_FilterSize*m_FilterSize;

            m_pGradients[inputIndex] += m_Filters[layer][kernelIndex]*gradient;
            m_GradientSum[layer][kernelIndex] += input[inputIndex]*gradient;
        }
    }
}