web-dev-qa-db-ja.com

ニューラルネットワーク:謎のReLu

私は、より大きなプロジェクトの一部として、プログラミング言語検出器、つまりコードスニペットの分類子を構築しています。私のベースラインモデルはかなり単純です:入力をトークン化し、スニペットをbag-of-wordsまたはこの場合はbag-ofとしてエンコードします-tokens、そしてこれらの機能の上に単純なNNを作成します。

boxes

NNへの入力は、コーパスから自動的に抽出される"def""self""function""->""const""#include"などの最も特徴的なトークンのカウンターの固定長配列です。アイデアは、これらのトークンはプログラミング言語にかなり独特であるため、この素朴なアプローチでさえ、高い精度のスコアが得られるはずです。

Input:
  def   1
  for   2
  in    2
  True  1
  ):    3
  ,:    1

  ...

Output: python

セットアップ

99%の精度がすぐに得られ、それが期待どおりに機能することを示しています。これがモデルです(完全な実行可能なスクリプトは here です):

# Placeholders
x = tf.placeholder(shape=[None, vocab_size], dtype=tf.float32, name='x')
y = tf.placeholder(shape=[None], dtype=tf.int32, name='y')
training = tf.placeholder_with_default(False, shape=[], name='training')

# One hidden layer with dropout
reg = tf.contrib.layers.l2_regularizer(0.01)
hidden1 = tf.layers.dense(x, units=96, kernel_regularizer=reg, 
                          activation=tf.nn.elu, name='hidden1')
dropout1 = tf.layers.dropout(hidden1, rate=0.2, training=training, name='dropout1')

# Output layer
logits = tf.layers.dense(dropout1, units=classes, kernel_regularizer=reg,
                         activation=tf.nn.relu, name='logits')

# Cross-entropy loss
loss = tf.reduce_mean(
    tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, abels=y))

# Misc reports: accuracy, correct/misclassified samples, etc.
correct_predicted = tf.nn.in_top_k(logits, y, 1, name='in-top-k')
prediction = tf.argmax(logits, axis=1)
wrong_predicted = tf.logical_not(correct_predicted, name='not-in-top-k')
x_misclassified = tf.boolean_mask(x, wrong_predicted, name='misclassified')
accuracy = tf.reduce_mean(tf.cast(correct_predicted, tf.float32), name='accuracy')

出力はかなり励みになります:

iteration=5  loss=2.580  train-acc=0.34277
iteration=10  loss=2.029  train-acc=0.69434
iteration=15  loss=2.054  train-acc=0.92383
iteration=20  loss=1.934  train-acc=0.98926
iteration=25  loss=1.942  train-acc=0.99609
Files.VAL mean accuracy = 0.99121             <-- After just 1 Epoch!

iteration=30  loss=1.943  train-acc=0.99414
iteration=35  loss=1.947  train-acc=0.99512
iteration=40  loss=1.946  train-acc=0.99707
iteration=45  loss=1.946  train-acc=0.99609
iteration=50  loss=1.944  train-acc=0.99902
iteration=55  loss=1.946  train-acc=0.99902
Files.VAL mean accuracy = 0.99414

テストの精度も約1.0でした。すべてが完璧に見えました。

神秘的なReLu

しかし、私はactivation=tf.nn.reluを最終的な高密度レイヤー(logits)に挿入していることに気付きました。これは明らかにバグです:前に負のスコアを破棄する必要はありませんsoftmax、なぜならそれらは低い確率でクラスを示すからです。しきい値がゼロの場合、これらのクラスは人為的に確率が高くなるだけで、これは間違いです。それを取り除くことは、モデルをより堅牢にし、正しいクラスで自信を持たせるだけです。

私もそう思っていました。そこで、それをactivation=Noneに置き換え、モデルを再度実行すると、驚くべきことが起こりました。パフォーマンスは向上しませんでした。全然。実際、それは大幅に低下しました

iteration=5  loss=5.236  train-acc=0.16602
iteration=10  loss=4.068  train-acc=0.18750
iteration=15  loss=3.110  train-acc=0.37402
iteration=20  loss=5.149  train-acc=0.14844
iteration=25  loss=2.880  train-acc=0.18262
Files.VAL mean accuracy = 0.28711

iteration=30  loss=3.136  train-acc=0.25781
iteration=35  loss=2.916  train-acc=0.22852
iteration=40  loss=2.156  train-acc=0.39062
iteration=45  loss=1.777  train-acc=0.45312
iteration=50  loss=2.726  train-acc=0.33105
Files.VAL mean accuracy = 0.29362

精度はトレーニングによって向上しましたが、91〜92%を超えることはありませんでした。さまざまなパラメーター(レイヤーサイズ、ドロップアウト、レギュラライザー、追加レイヤーなど)を変えて、何度もアクティベーションを前後に変更し、常に同じ結果を得ました:「間違った」モデルは99%ヒットしましたすぐに、「正しい」モデルは50のエポックの後にやっと90%を達成しましたテンソルボードによると、重量分布に大きな違いはありませんでした。勾配は消えず、両方のモデルが正常に学習しました。

これはどのようにして可能ですか?最終的なReLuはどのようにしてモデルを非常に優れたものにすることができますか?特にこのReLuがバグである場合はどうでしょうか?

4
Maxim

予測分布

しばらく遊んだ後、両方のモデルの実際の予測分布を視覚化することにしました。

predicted_distribution = tf.nn.softmax(logits, name='distribution')

以下は、分布のヒストグラムとそれらが時間とともにどのように進化したかです。

ReLuで(間違ったモデル)

predicted-distribution

ReLuなし(正しいモデル)

predicted-distribution

最初のヒストグラムは理にかなっており、ほとんどの確率は0に近いです。しかし、ReLuモデルのヒストグラムはsuspiciousです。値は、数回の反復の後、0.15に集中するようです。実際の予測を印刷すると、このアイデアが確認されました。

[0.14286 0.14286 0.14286 0.14286 0.14286 0.14286 0.14286]
[0.14286 0.14286 0.14286 0.14286 0.14286 0.14286 0.14286]

私は7つのクラス(現時点では7つの異なる言語用)を持っていて、0.142861/7です。結局のところ、「完全な」モデルは0ロジットを出力することを学習し、それが次に均一な予測に変換されました。

しかし、どのようにthis分布を99%正確であると報告できますか?

tf.nn.in_top_k

tf.nn.in_top_k に入る前に、精度を計算する別の方法を確認しました。

true_correct = tf.equal(tf.argmax(logits, 1), tf.cast(y, tf.int64))
alternative_accuracy = tf.reduce_mean(tf.cast(true_correct, tf.float32))

最高予測クラスとグラウンドトゥルースの正直な比較を行う...結果はこれです:

iteration=2  loss=3.992  train-acc=0.13086  train-alt-acc=0.13086
iteration=4  loss=3.590  train-acc=0.13086  train-alt-acc=0.12207
iteration=6  loss=2.871  train-acc=0.21777  train-alt-acc=0.13672
iteration=8  loss=2.466  train-acc=0.37695  train-alt-acc=0.16211
iteration=10  loss=2.099  train-acc=0.62305  train-alt-acc=0.10742
iteration=12  loss=2.066  train-acc=0.79980  train-alt-acc=0.17090
iteration=14  loss=2.016  train-acc=0.84277  train-alt-acc=0.17285
iteration=16  loss=1.954  train-acc=0.91309  train-alt-acc=0.13574
iteration=18  loss=1.956  train-acc=0.95508  train-alt-acc=0.06445
iteration=20  loss=1.923  train-acc=0.97754  train-alt-acc=0.11328

確かに、tf.nn.in_top_kk=1は正しい精度からすぐに逸脱し、空想的な99%の値を報告し始めました。では、実際には何をするのでしょうか?これが ドキュメント が言うことです:

ターゲットが上位Kの予測にあるかどうかを示します。

これはbatch_size bool配列を出力し、ターゲットクラスの予測がすべての予測の中で上位のk予測に含まれる場合、エントリout[i]はtrueになります。 InTopKの動作はtieの処理においてTopK opとは異なることに注意してください。複数のクラスが同じ予測値を持ち、上位kの境界にまたがる場合、これらのクラスはすべて上位kにあると見なされます。

それはそれが何であるかです。確率が均一である場合(実際には「わからない」を意味します)、それらはすべて正しいです。ロジット分布がほとんど均一である場合、softmaxはそれをexactly均一分布に変換できるため、状況はさらに悪化します。この単純な例で見られる:

x = tf.constant([0, 1e-8, 1e-8, 1e-9])
tf.nn.softmax(x).eval()
# >>> array([0.25, 0.25, 0.25, 0.25], dtype=float32)

...つまり、ほぼ均一なすべての予測が、tf.nn.in_top_k仕様に従って「正しい」と見なされる可能性があることを意味します。

結論

tf.nn.in_top_kは、誤った予測を黙って飲み込んで「正しい」と報告する可能性があるため、テンソルフローでの精度測定の危険な選択です。代わりに、常にこの長く信頼できる式を使用する必要があります。

accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(logits, 1), tf.cast(y, tf.int64)), tf.float32))
8
Maxim