私は、より大きなプロジェクトの一部として、プログラミング言語検出器、つまりコードスニペットの分類子を構築しています。私のベースラインモデルはかなり単純です:入力をトークン化し、スニペットをbag-of-wordsまたはこの場合はbag-ofとしてエンコードします-tokens、そしてこれらの機能の上に単純なNNを作成します。
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でした。すべてが完璧に見えました。
しかし、私は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がバグである場合はどうでしょうか?
しばらく遊んだ後、両方のモデルの実際の予測分布を視覚化することにしました。
predicted_distribution = tf.nn.softmax(logits, name='distribution')
以下は、分布のヒストグラムとそれらが時間とともにどのように進化したかです。
ReLuで(間違ったモデル)
ReLuなし(正しいモデル)
最初のヒストグラムは理にかなっており、ほとんどの確率は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.14286
は1/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_k
とk=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))