我已经尝试解决这个问题几个星期了,现在完全不知道该怎么办。我的模型主要基于Tensorflow的Transformer语言学习教程,并从这篇论文中获得灵感,该论文使用Transformer模型进行图像标注。
我的目标是训练一个模型来为我自己的数据集中的图像生成标注。我已经对几乎所有部分进行了单元测试,似乎一切运作良好,但剩下的唯一问题是模型很快就学会了只预测结束标记。我数据集中所有的标注都结构化为”<start> 这里是标注文本 <end>”,但模型很快学会了预测”<end> <end> <end> <end> … “
损失和准确率分别最小化和最大化,损失接近于零,准确率在不到一个epoch的时间内就达到了>90%(这在图像标注任务中是不可能的),但模型预测的只是结束标记。我测试了自己填充了结束标记的张量的损失和准确率函数,它显示了高损失和0%的准确率,这是正确的,所以我不知道为什么当模型似乎只是在预测结束标记时,准确率会慢慢上升,损失会慢慢下降。
以下是某次运行中第一个epoch的开始部分。我每50个批次打印一次第一个例子的预测(这是沿词汇大小维度的argmax)以获取模型输出的样本和变化情况:
Epoch 1 Batch 0 Loss 9.0129 Accuracy 0.0000Predictions: tf.Tensor([5817 6816 6626 2530 521 7248 3903 4040 2104 7952], shape=(10,), dtype=int64)Epoch 1 Batch 50 Loss 9.0176 Accuracy 0.0001Predictions: tf.Tensor([2904 3546 5805 3328 3021 3028 4687 2457 7491 6794], shape=(10,), dtype=int64)Epoch 1 Batch 100 Loss 9.0173 Accuracy 0.0000Predictions: tf.Tensor([5817 6589 2535 7221 4370 4191 6440 5486 4636 1857], shape=(10,), dtype=int64)Epoch 1 Batch 150 Loss 9.0143 Accuracy 0.0000Predictions: tf.Tensor([5817 6769 6709 955 6709 6284 6709 4732 1027 1027], shape=(10,), dtype=int64)Epoch 1 Batch 200 Loss 9.0093 Accuracy 0.0000Predictions: tf.Tensor([6337 2300 3304 6067 4284 33 6895 2457 6237 6125], shape=(10,), dtype=int64)Epoch 1 Batch 250 Loss 9.0033 Accuracy 0.0001Predictions: tf.Tensor([5817 5503 2889 554 5771 7612 196 1808 6237 6537], shape=(10,), dtype=int64)Epoch 1 Batch 300 Loss 8.9953 Accuracy 0.0002Predictions: tf.Tensor([6067 5 521 5587 3757 2457 3021 2305 6151 584], shape=(10,), dtype=int64)Epoch 1 Batch 350 Loss 8.9855 Accuracy 0.0007Predictions: tf.Tensor([5817 4133 5805 2484 7403 5084 3171 1042 4863 1705], shape=(10,), dtype=int64)Epoch 1 Batch 400 Loss 8.9740 Accuracy 0.0019Predictions: tf.Tensor([5817 1801 5719 1829 4284 4191 6895 6695 4658 4863], shape=(10,), dtype=int64)Epoch 1 Batch 450 Loss 8.9607 Accuracy 0.0047Predictions: tf.Tensor([4133 4284 3822 6895 4425 4663 3 2457 3 3604], shape=(10,), dtype=int64)Epoch 1 Batch 500 Loss 8.9473 Accuracy 0.0090Predictions: tf.Tensor([5216 7 3 521 3 3 3 3 3 3], shape=(10,), dtype=int64)Epoch 1 Batch 550 Loss 8.9329 Accuracy 0.0140Predictions: tf.Tensor([3 7 5 3 3 3 3 3 3 3], shape=(10,), dtype=int64)Epoch 1 Batch 600 Loss 8.9183 Accuracy 0.0186Predictions: tf.Tensor([3 3 7 5 3 3 3 3 3 3], shape=(10,), dtype=int64)Epoch 1 Batch 650 Loss 8.9023 Accuracy 0.0227Predictions: tf.Tensor([3 3 3 3 3 3 3 3 3 3], shape=(10,), dtype=int64)Epoch 1 Batch 700 Loss 8.8860 Accuracy 0.0262Predictions: tf.Tensor([3 3 3 3 3 3 3 3 3 3], shape=(10,), dtype=int64)
为参考,我的分词器中3表示<end>:
>>> roast_tokenizer.index_word[3]<end>
我尝试过的方法:
1. 调整学习率
我使用了正常的Adam优化器,学习率从1e-3到1e-7,我尝试了各种beta衰减,并且实现了Tensorflow教程建议的学习率调度,看起来像这样:
我还尝试了从最大1e-3到1e-7的不同调度。
2. 更小的模型:
我的模型有2个编码器和2个解码器,潜在维度为256。编码器和解码器中的前馈网络有1024个节点。
我尝试了一个只有一个编码器和一个解码器的模型,结果还是一样。
3. 不同的损失函数:
我使用了对logits的稀疏分类交叉熵,这是Tensorflow教程建议使用的,但我还尝试了tensorflow_addons包中的sequence_loss,但问题依然存在。
我现在已经快没有可以测试的东西了。预览掩码和填充掩码似乎工作正常。位置编码似乎工作正常。自注意力和交叉注意力似乎运作良好。可能我的模型中还有某个地方犯了错误,但我确实深入研究了Transformer模型的每个方面的理论,并且在研究了几个星期后,一切似乎都很完美,所以我真的卡在这里了。
我查看了其他关于模型仅预测结束标记的stackoverflow帖子,但我看到的唯一建议是降低学习率或让模型继续训练,我已经这样做了,模型达到了>90%的准确率,然后在第二个epoch中骤降到<20%,然后慢慢回升,但似乎仍然只预测结束标记,所以我不知道那里发生了什么。
为进一步参考,这里是我的损失和准确率方法以及一些显示它们正确工作的测试:
def loss_function(real, pred): mask = tf.math.logical_not(tf.math.equal(real, 0)) loss_ = loss_object(real, pred) mask = tf.cast(mask, dtype=loss_.dtype) loss_ *= mask return tf.reduce_sum(loss_)/tf.reduce_sum(mask)def accuracy_function(real, pred): accuracies = tf.equal(real, tf.cast(tf.argmax(pred, axis=-1), dtype=real.dtype)) mask = tf.math.logical_not(tf.math.equal(real, 0)) accuracies = tf.math.logical_and(mask, accuracies) accuracies = tf.cast(accuracies, dtype=tf.float32) mask = tf.cast(mask, dtype=tf.float32) return tf.reduce_sum(accuracies)/tf.reduce_sum(mask)
real_example = tf.convert_to_tensor([[79,80,50]])logits = tf.one_hot([79,80,50], 8000) * 1pred_example = tf.expand_dims(logits, 0)accuracy_function(real_example, pred_example), loss_function(real_example, pred_example)
(<tf.Tensor: shape=(), dtype=float32, numpy=1.0>, <tf.Tensor: shape=(), dtype=float32, numpy=7.987412>)
这表明如果你预测正确的标记,将获得100%的准确率。我使用了* 1的乘数,因为我尝试将其增加到100或1000,看看损失是否继续下降(这有意义,因为这是logits而不是概率分布)。
logits = tf.one_hot([3,3,3], 8000)print(logits[0][:5])pred_example = tf.expand_dims(logits, 0)accuracy_function(real_example, pred_example), loss_function(real_example, pred_example)
tf.Tensor([0. 0. 0. 1. 0.], shape=(5,), dtype=float32)(<tf.Tensor: shape=(), dtype=float32, numpy=0.0>, <tf.Tensor: shape=(), dtype=float32, numpy=8.9874115>)
在这个例子中,真实标签保持不变,但预测只是结束标记。准确率最终为0%,这是正确的,损失很高。
任何建议将不胜感激,谢谢!
回答:
我决定添加另一个回答而不是修改我之前的回答,因为我确实通过扩大模型解决了一个正确的问题,但没有解决真正的根本问题。
问题以及我为什么获得了荒谬的高准确率的解释是因为我的注意力掩码是倒置的。我遵循的教程定义了自己的多头注意力层,它有一个’忽略’掩码,但自从那个教程创建以来,Keras已经添加了一个官方的MultiHeadAttention层,所以我选择使用那个。我没有多想这个层使用(在我看来更好)的掩码定义,其中1表示应关注某个标记,0表示不应关注该标记。
当时的问题的模型过于简单,所以它学会了只预测结束标记,但一旦我有了合理的模型,这个模型能够学习,但只是读取它应该预测的内容并按此预测。
这就是为什么我的准确率提高到90%,实际上几乎达到了100%的准确率,因为预测你看到的正是 trivial的任务。
自从发现了这一点后,我在教程上添加了一个注释,警告人们这一点,并提供了一个创建正确注意力掩码的注释函数。