为啥带有 Dropout 的 DNN 总是预测一个?

Posted

技术标签:

【中文标题】为啥带有 Dropout 的 DNN 总是预测一个?【英文标题】:Why DNN with Dropout always predict one?为什么带有 Dropout 的 DNN 总是预测一个? 【发布时间】:2018-08-24 15:43:10 【问题描述】:

我已经实现了一个非常简单的深度神经网络来执行多标签分类。该模型的概述是(为了简单的可视化,省略了偏差):

即以 ReLU 单元和 Sigmoid 为输出单元的 3 层深度神经网络。

损失函数是 Sigmoid Cross Entropy,使用的优化器是 Adam。

当我训练这个 NN 没有 Dropout 时,我得到以下结果:

    #Placeholders
    x = tf.placeholder(tf.float32,[None,num_features],name='x')
    y = tf.placeholder(tf.float32,[None,num_classes],name='y')

    keep_prob = tf.placeholder(tf.float32,name='keep_prob')

    #Layer1
    WRelu1 = tf.Variable(tf.truncated_normal([num_features,num_features],stddev=1.0),dtype=tf.float32,name='wrelu1')
    bRelu1 = tf.Variable(tf.zeros([num_features]),dtype=tf.float32,name='brelu1')
    layer1 = tf.add(tf.matmul(x,WRelu1),bRelu1,name='layer1')
    relu1 = tf.nn.relu(layer1,name='relu1')

    #Layer2
    WRelu2 = tf.Variable(tf.truncated_normal([num_features,num_features],stddev=1.0),dtype=tf.float32,name='wrelu2')
    bRelu2 = tf.Variable(tf.zeros([num_features]),dtype=tf.float32,name='brelu2')
    layer2 = tf.add(tf.matmul(relu1,WRelu2),bRelu2,name='layer2')
    relu2 = tf.nn.relu(layer2,name='relu2')

    #Layer3
    WRelu3 = tf.Variable(tf.truncated_normal([num_features,num_features],stddev=1.0),dtype=tf.float32,name='wrelu3')
    bRelu3 = tf.Variable(tf.zeros([num_features]),dtype=tf.float32,name='brelu3')
    layer3 = tf.add(tf.matmul(relu2,WRelu3),bRelu3,name='layer3')
    relu3 = tf.nn.relu(tf.matmul(relu2,WRelu3) + bRelu3,name='relu3')

    #Out layer
    Wout = tf.Variable(tf.truncated_normal([num_features,num_classes],stddev=1.0),dtype=tf.float32,name='wout')
    bout = tf.Variable(tf.zeros([num_classes]),dtype=tf.float32,name='bout')
    logits = tf.add(tf.matmul(relu3,Wout),bout,name='logits')

    #Predictions
    logits_sigmoid = tf.nn.sigmoid(logits,name='logits_sigmoid')

    #Cost & Optimizer
    cost = tf.losses.sigmoid_cross_entropy(y,logits)
    optimizer = tf.train.AdamOptimizer(LEARNING_RATE).minimize(cost)

测试数据的评估结果:

ROC AUC - micro average: 0.6474180196222774
ROC AUC - macro average: 0.6261438437099212

Precision - micro average: 0.5112489722699753
Precision - macro average: 0.48922193879411413
Precision - weighted average: 0.5131092162035961

Recall - micro average: 0.584640369246549
Recall - macro average: 0.55746897003228
Recall - weighted average: 0.584640369246549

当我训练这个 NN 添加 Dropout 层时,我得到以下结果:

    #Placeholders
    x = tf.placeholder(tf.float32,[None,num_features],name='x')
    y = tf.placeholder(tf.float32,[None,num_classes],name='y')

    keep_prob = tf.placeholder(tf.float32,name='keep_prob')

    #Layer1
    WRelu1 = tf.Variable(tf.truncated_normal([num_features,num_features],stddev=1.0),dtype=tf.float32,name='wrelu1')
    bRelu1 = tf.Variable(tf.zeros([num_features]),dtype=tf.float32,name='brelu1')
    layer1 = tf.add(tf.matmul(x,WRelu1),bRelu1,name='layer1')
    relu1 = tf.nn.relu(layer1,name='relu1')

    #DROPOUT
    relu1 = tf.nn.dropout(relu1,keep_prob=keep_prob,name='relu1drop')

    #Layer2
    WRelu2 = tf.Variable(tf.truncated_normal([num_features,num_features],stddev=1.0),dtype=tf.float32,name='wrelu2')
    bRelu2 = tf.Variable(tf.zeros([num_features]),dtype=tf.float32,name='brelu2')
    layer2 = tf.add(tf.matmul(relu1,WRelu2),bRelu2,name='layer2')
    relu2 = tf.nn.relu(layer2,name='relu2')

    #DROPOUT
    relu2 = tf.nn.dropout(relu2,keep_prob=keep_prob,name='relu2drop')

    #Layer3
    WRelu3 = tf.Variable(tf.truncated_normal([num_features,num_features],stddev=1.0),dtype=tf.float32,name='wrelu3')
    bRelu3 = tf.Variable(tf.zeros([num_features]),dtype=tf.float32,name='brelu3')
    layer3 = tf.add(tf.matmul(relu2,WRelu3),bRelu3,name='layer3')
    relu3 = tf.nn.relu(tf.matmul(relu2,WRelu3) + bRelu3,name='relu3')


    #DROPOUT
    relu3 = tf.nn.dropout(relu3,keep_prob=keep_prob,name='relu3drop')

    #Out layer
    Wout = tf.Variable(tf.truncated_normal([num_features,num_classes],stddev=1.0),dtype=tf.float32,name='wout')
    bout = tf.Variable(tf.zeros([num_classes]),dtype=tf.float32,name='bout')
    logits = tf.add(tf.matmul(relu3,Wout),bout,name='logits')

    #Predictions
    logits_sigmoid = tf.nn.sigmoid(logits,name='logits_sigmoid')


    #Cost & Optimizer
    cost = tf.losses.sigmoid_cross_entropy(y,logits)
    optimizer = tf.train.AdamOptimizer(LEARNING_RATE).minimize(cost)

测试数据的评估结果:

ROC AUC - micro average: 0.5
ROC AUC - macro average: 0.5

Precision - micro average: 0.34146163499985405
Precision - macro average: 0.34146163499985405
Precision - weighted average: 0.3712475781926326

Recall - micro average: 1.0
Recall - macro average: 1.0
Recall - weighted average: 1.0

从 Dropout 版本中的 Recall 值可以看出,NN 输出始终为 1,对于每个样本的每个类始终为正类。

确实这不是一个简单的问题,但在应用 Dropout 后,我​​预计至少会得到与没有 Dropout 相似的结果,而不是更差的结果,当然也不是这种饱和的输出。

为什么会发生这种情况?我怎样才能避免这种行为?您是否在代码中看到了一些奇怪或糟糕的事情?

超参数:

辍学率:0.5@训练/1.0@推理

时代:500

学习率:0.0001

数据集信息:

实例数:+22.000

班级数:6

谢谢!

【问题讨论】:

尝试删除 logits_sigmoid 并直接从 logits 中获取您的预测,而不是通过四舍五入,而是通过 tf.argmax 谢谢@mikkola,但事实上我没有使用 pred 操作,所以把它放在那里是我的错。我已经删除了它,我直接从 logits_sigmoid 得到我的预测,我只得到概率。 什么是训练/评估期间的“保持概率”? 【参考方案1】:

最后我通过更多的实验设法解决了我自己的问题,所以这就是我想出来的。

我导出了 Tensorboad 图和权重、偏差和激活数据,以便在 TB 上探索它们。

然后我意识到配重有些问题。

如您所见,权重根本没有变化。换句话说,该层“没有学习”任何东西。

但随后解释就在我眼前。权重的分布太广泛了。看看那个直方图范围,从 [-2,2] 这太多了。

然后我意识到我正在初始化权重矩阵

truncated_normal(mean=0.0, std=1.0)

对于正确的 init,这是一个非常高的 std.dev。显而易见的技巧是使用更正确的初始化来初始化权重。然后,我选择了“Xavier Glorot Initialization”,然后权重变为:

并且预测不再是正面的,而是再次变成混合的预测。当然,由于 Dropout,在测试集上的性能更好。

总之,没有 Dropout 的网络能够通过过于宽泛的初始化来学习一些东西,但有 Dropout 的网络则不能,并且需要更好的初始化以避免卡住。

感谢所有阅读帖子并发表评论的人。

【讨论】:

以上是关于为啥带有 Dropout 的 DNN 总是预测一个?的主要内容,如果未能解决你的问题,请参考以下文章

预测模型基于WMMSE的DNN算法实现数据预测

预测模型基于WMMSE的DNN算法实现数据预测

Python,为啥我的概率神经网络(PNN)总是预测零?

为啥RNN总是输出1

Keras DNN 预测模型准确率没有提高

神经网络dropout