Сводка:
Я хочу настроить BERT для классификации предложений в пользовательском наборе данных. Я следил за некоторыми найденными мною примерами, например, этот , что было очень полезно. Я также просмотрел эту суть .
Проблема, с которой я столкнулся, заключается в том, что при выполнении вывода для некоторых образцов результат имеет другие размеры, чем я ожидал. получить кортеж с массивом размеров numpy (1472, 42), где 42 - количество классов. Я бы ожидал размеров (23, 42).
Код и другие детали:
Я выполняю вывод на обученной модели с помощью Keras следующим образом:
preds = model.predict(features)
Где функции токенизируются и преобразуются в набор данных:
for sample, ground_truth in tests:
test_examples.append(InputExample(text=sample, category_index=ground_truth))
features = convert_examples_to_tf_dataset(test_examples, tokenizer)
Где sample
может быть, например, "A test sentence I want classified"
, а ground_truth
может быть, например, 12
это закодированная метка. Поскольку я делаю вывод, то, что я предлагаю как основную истину, конечно, не должно иметь значения. *
def convert_examples_to_tf_dataset(
examples: List[Tuple[str, int]],
tokenizer,
max_length=64,
):
"""
Loads data into a tf.data.Dataset for finetuning a given model.
Args:
examples: List of tuples representing the examples to be fed
tokenizer: Instance of a tokenizer that will tokenize the examples
max_length: Maximum string length
Returns:
a ``tf.data.Dataset`` containing the condensed features of the provided sentences
"""
features = [] # -> will hold InputFeatures to be converted later
for e in examples:
# Documentation is really strong for this method, so please take a look at it
input_dict = tokenizer.encode_plus(
e.text,
add_special_tokens=True,
max_length=max_length, # truncates if len(s) > max_length
return_token_type_ids=True,
return_attention_mask=True,
pad_to_max_length=True, # pads to the right by default
)
# input ids = token indices in the tokenizer's internal dict
# token_type_ids = binary mask identifying different sequences in the model
# attention_mask = binary mask indicating the positions of padded tokens so the model does not attend to them
input_ids, token_type_ids, attention_mask = (input_dict["input_ids"],
input_dict["token_type_ids"], input_dict['attention_mask'])
features.append(
InputFeatures(
input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, label=e.category_index
)
)
def gen():
for f in features:
yield (
{
"input_ids": f.input_ids,
"attention_mask": f.attention_mask,
"token_type_ids": f.token_type_ids,
},
f.label,
)
return tf.data.Dataset.from_generator(
gen,
({"input_ids": tf.int32, "attention_mask": tf.int32, "token_type_ids": tf.int32}, tf.int64),
(
{
"input_ids": tf.TensorShape([None]),
"attention_mask": tf.TensorShape([None]),
"token_type_ids": tf.TensorShape([None]),
},
tf.TensorShape([]),
),
)
with tf.device('/cpu:0'):
train_data = convert_examples_to_tf_dataset(train_examples, tokenizer)
train_data = train_data.shuffle(buffer_size=len(train_examples), reshuffle_each_iteration=True) \
.batch(BATCH_SIZE) \
.repeat(-1)
val_data = convert_examples_to_tf_dataset(val_examples, tokenizer)
val_data = val_data.shuffle(buffer_size=len(val_examples), reshuffle_each_iteration=True) \
.batch(BATCH_SIZE) \
.repeat(-1)
Он работает так, как я ожидал, и запуск print(list(features.as_numpy_iterator())[1])
дает следующее:
({'input_ids': array([ 101, 11639, 19962, 23288, 13264, 35372, 10410, 102, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0], dtype=int32), 'attention_mask': array([1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
dtype=int32), 'token_type_ids': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
dtype=int32)}, 6705)
Пока все выглядит так, как я ожидал. И похоже, что токенизатор работает как надо; 3 массива длиной 64 (что соответствует моей максимальной длине) и метка в виде целого числа.
Модель была обучена следующим образом:
config = BertConfig.from_pretrained(
'bert-base-multilingual-cased',
num_labels=len(label_encoder.classes_),
output_hidden_states=False,
output_attentions=False
)
model = TFBertForSequenceClassification.from_pretrained('bert-base-multilingual-cased', config=config)
# train_data is then a tf.data.Dataset we can pass to model.fit()
optimizer = tf.keras.optimizers.Adam(learning_rate=3e-05, epsilon=1e-08)
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
metric = tf.keras.metrics.SparseCategoricalAccuracy(name='accuracy')
model.compile(optimizer=optimizer,
loss=loss,
metrics=[metric])
model.summary()
history = model.fit(train_data,
epochs=EPOCHS,
steps_per_epoch=train_steps,
validation_data=val_data,
validation_steps=val_steps,
shuffle=True,
)
Результаты
Проблема теперь в том, что при выполнении прогноза preds = model.predict(features)
выходные размеры не соответствуют тому, что написано в документации : logits (Numpy array or tf.Tensor of shape (batch_size, config.num_labels)):
. Я получаю кортеж, содержащий массив numpy с размерами: (1472,42).
42 имеет смысл, поскольку это мое количество классов. Я отправил 23 образца для теста, и 23 x 64 = 1472. 64 - моя максимальная длина предложения, так что это звучит знакомо. Этот вывод неверен? Как я могу преобразовать этот вывод в фактическое предсказание класса для каждой входной выборки? Я получаю 1472 прогноза, хотя ожидал 23.
Пожалуйста, дайте мне знать, могу ли я предоставить более подробную информацию, которая могла бы помочь решить эту проблему.