【问题标题】:Label tokenizer not working, loss and accuracy cannot be calculated标签标记器不起作用,无法计算损失和准确性
【发布时间】:2020-07-31 16:30:42
【问题描述】:

我正在使用 Keras Tensorflow 进行 NLP,我目前正在处理 imdb 评论数据集。我想使用 hub.KerasLayer。我想直接传递实际的 x 和 y 值。因此,在我的 model.fit 语句中,句子为 x,标签为 y。我的代码:

import csv
import tensorflow as tf
import tensorflow_datasets as tfds
import numpy as np
import tensorflow_hub as hub
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

imdb, info = tfds.load("imdb_reviews", with_info=True, as_supervised=True)

imdb_train=imdb['train']
imdb_test=imdb['test']

training_sentences=[]
training_labels=[]

test_sentences=[]
test_labels=[]

for a,b in imdb_train:
  training_sentences.append(a.numpy().decode("utf8"))
  training_labels.append(b.numpy())

for a,b in imdb_test:
  test_sentences.append(a.numpy().decode("utf8"))
  test_labels.append(b.numpy())

model = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1"
hub_layer = hub.KerasLayer(model, output_shape=[20], input_shape=[], 
                           dtype=tf.string, trainable=True)

model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1))

model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),optimizer='adam', metrics=[tf.metrics.BinaryAccuracy(threshold=0.0, name='accuracy')])

尝试

history = model.fit(x=training_sentences,
                      y=training_labels,
                      validation_data=(test_sentences, test_labels),
                      epochs=2)

不起作用,因为 training_labels 的形状/格式不正确。我现在的方法是再次使用标记器,因为然后我会以正确的格式/形状获得结果(来自 texts_to_sequences)。为此,我必须首先将其转换为是/否(或 a/b 等)字符串。

training_labels_test=[]
for i in training_labels:
   if i==0: training_labels_test.append("no")
   if i==1: training_labels_test.append("yes")
  
testtokenizer=Tokenizer()
testtokenizer.fit_on_texts(training_labels_test)
test_labels_pad=testtokenizer.texts_to_sequences(training_labels_test)

val_labels_test=[]
for i in test_labels:
   if i==0: val_labels_test.append("no")
   if i==1: val_labels_test.append("yes")

testtokenizer.fit_on_texts(val_labels_test)
val_labels_pad=testtokenizer.texts_to_sequences(val_labels_test)

因为我现在有 1 和 2 作为标签,所以我需要更新我的模型:

model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(2))

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])

然后我尝试适应它:

history = model.fit(x=training_sentences,
                      y=test_labels_pad,
                      validation_data=(test_sentences, val_labels_pad),
                      epochs=2)

问题是loss是nan,准确率计算不正确。

错在哪里?

请不要说我的问题实际上是关于这种特定方式以及为什么这个标记器不起作用。我知道还有其他可行的方法。

【问题讨论】:

    标签: python tensorflow keras nlp tokenize


    【解决方案1】:

    问题似乎有两个方面。

    首先,二进制目标应始终为[0, 1] 而不是[1, 2]。所以,我从你的目标中减去了一个。 Tokenizer() 不是用来编码标签的,你应该使用 tfds.features.ClassLabel()。目前,我只是在fit() 调用中减去了 1。

    history = model.fit(x=training_sentences,
                          y=list(map(lambda x: x[0] - 1, test_labels_pad)),
                          validation_data=(test_sentences, 
                                           list(map(lambda x: x[0] - 1, val_labels_pad))),
                          epochs=1)
    

    其次,由于某种原因,您的输入层只返回了nan。在预训练模型的page 上,他们说:

    google/tf2-preview/gnews-swivel-20dim-with-oov/1 - 与 google/tf2-preview/gnews-swivel-20dim/1 相同,但有 2.5% 的词汇转换为 OOV 存储桶。 如果任务的词汇和模型的词汇没有完全重叠,这会有所帮助

    因此您应该使用第二个,因为您的数据集与训练时的数据没有完全重叠。然后,您的模型将开始学习。

    model = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim-with-oov/1"
    hub_layer = hub.KerasLayer(model, output_shape=[20], input_shape=[],
                               dtype=tf.string, trainable=True)
    

    完整运行代码:

    import csv
    import tensorflow as tf
    import tensorflow_datasets as tfds
    import numpy as np
    import tensorflow_hub as hub
    from tensorflow.keras.preprocessing.text import Tokenizer
    from tensorflow.keras.preprocessing.sequence import pad_sequences
    
    imdb, info = tfds.load("imdb_reviews", with_info=True, as_supervised=True)
    
    imdb_train=imdb['train']
    imdb_test=imdb['test']
    
    training_sentences=[]
    training_labels=[]
    
    test_sentences=[]
    test_labels=[]
    
    for a,b in imdb_train:
      training_sentences.append(a.numpy().decode("utf8"))
      training_labels.append(b.numpy())
    
    for a,b in imdb_test:
      test_sentences.append(a.numpy().decode("utf8"))
      test_labels.append(b.numpy())
    
    training_labels_test = []
    for i in training_labels:
        if i == 0: training_labels_test.append("no")
        if i == 1: training_labels_test.append("yes")
    
    testtokenizer = Tokenizer()
    testtokenizer.fit_on_texts(training_labels_test)
    test_labels_pad = testtokenizer.texts_to_sequences(training_labels_test)
    
    val_labels_test = []
    for i in test_labels:
        if i == 0: val_labels_test.append("no")
        if i == 1: val_labels_test.append("yes")
    
    testtokenizer.fit_on_texts(val_labels_test)
    val_labels_pad = testtokenizer.texts_to_sequences(val_labels_test)
    
    model = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim-with-oov/1"
    hub_layer = hub.KerasLayer(model, output_shape=[20], input_shape=[],
                               dtype=tf.string, trainable=True)
    
    model = tf.keras.Sequential()
    model.add(hub_layer)
    model.add(tf.keras.layers.Dense(16, activation='relu'))
    model.add(tf.keras.layers.Dense(2))
    
    model.compile(optimizer='adam',
                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=[tf.keras.metrics.SparseCategoricalAccuracy()])
    
    history = model.fit(x=training_sentences,
                          y=list(map(lambda x: x[0] - 1, test_labels_pad)),
                          validation_data=(test_sentences, 
                          list(map(lambda x: x[0] - 1, val_labels_pad))),
                          epochs=1)
    
    model.predict(training_sentences)
    
    24896/25000 [==================>.] - ETA: 0s - loss: 0.5482 - sparse_cat_acc: 0.7312
    
    array([[-0.94201976, -1.3173063 ],
           [-3.7894788 , -3.0269182 ],
           [-3.0404441 , -3.4826043 ],
           ...,
           [-2.8379505 , -1.2451388 ],
           [-0.7685702 , -3.1836908 ],
           [-1.7252465 , -3.8163807 ]], dtype=float32)
    

    看看如果你有 3 个类别会发生什么,并使用 [1, 2, 3] 而不是 [0, 1, 2]

    y_true = tf.constant([1, 2, 3])
    y_pred = tf.constant([[0.05, 0.95, 0], [0.1, 0.8, 0.1], [.2, .4, .4]])
    scce = tf.keras.losses.SparseCategoricalCrossentropy()
    scce(y_true, y_pred).numpy()
    
    nan
    

    但它适用于[0, 1, 2]:

    y_true = tf.constant([0, 1, 2])
    y_pred = tf.constant([[0.05, 0.95, 0], [0.1, 0.8, 0.1], [.2, .4, .4]])
    scce = tf.keras.losses.SparseCategoricalCrossentropy()
    scce(y_true, y_pred).numpy()
    
    1.3783889
    

    【讨论】:

    • 感谢您的回答并加 1,但是您为什么还要使用 model.add(tf.keras.layers.Dense(2))?你确实从标签中减去了 1,所以我们有 0 和 1,所以它应该是最后一层中的 1 个单位,损失中的 BinaryCrossentropy 和指标中的 Binary Accuracy?
    • 此外:为什么我不能使用 SparseCategorical 执行 [1] 和 [2]?在 Coursera Tensorflow 官方课程中,他们确实在标签上使用了标记器:github.com/lmoroney/dlaicourse/blob/master/…,所以他们有 5 个类(1、2、3、4、5),而不仅仅是 2 个(但他们也缺少 0)。那么为什么在两个班级的情况下这是“禁止的”呢?而且我更困惑,因为您确实在最后一层使用了 SparseCategorical 和 2 个单位。
    • sparse_ 对于损失函数或指标意味着 输入 不是 one-hot 编码。这一切都在这里解释:tensorflow.org/api_docs/python/tf/keras/losses/…。对于你的第二个问题,我必须说我很困惑。我倾向于说他们犯了一个错误。看看当您将 [0, 1, 2] 以外的其他内容放入 3 类损失函数时会发生什么(请参阅我的答案的底部)
    猜你喜欢
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 2017-09-13
    • 1970-01-01
    • 2015-07-18
    相关资源
    最近更新 更多