Huggingface TFBertForSequenceClassification всегда предсказывает одну и ту же метку

TL; DR: Моя модель всегда предсказывает одни и те же ярлыки, и я не знаю почему. Ниже приведен весь мой код для тонкой настройки в надежде, что кто-то укажет мне, в чем я ошибаюсь.

Я использую TFBertForSequenceClassification Huggingface для задачи классификации последовательностей, чтобы предсказать 4 метки предложений в немецком тексте.

Я использую модель bert-base-german-cased, поскольку я не использую только строчные буквы (поскольку немецкий более чувствителен к регистру, чем английский).

Я получаю данные из файла csv, который я создаю из аннотированного корпуса, который я получил. Вот пример этого:

0       Hier kommen wir ins Spiel Die App Cognitive At...
1       Doch wenn Athlet Lebron James jede einzelne Mu...
2       Wie kann ein Gehirn auf Hochleistung getrimmt ...
3       Wie schafft es Warren Buffett knapp 1000 Wörte...
4       Entfalte dein mentales Potenzial und werde ein...
Name: sentence_clean, Length: 3094, dtype: object

И это мои ярлыки из того же файла csv:

0       e_1
1       e_4
2       e_4
3       e_4
4       e_4

Различными ярлыками являются: e_1, e_2, e_3 и e_4.

Это код, который я использую для точной настройки моей модели:

import pandas as pd
import numpy as np
import os
    
# read in data
# sentences_df = pd.read_csv('path/file.csv')


X = sentences_df.sentence_clean
Y = sentences_df.classId

# =============================================================================
# One hot encode labels
# =============================================================================

# integer encode labels
from numpy import array
from numpy import argmax
from sklearn.preprocessing import LabelEncoder


label_encoder = LabelEncoder()
Y_integer_encoded = label_encoder.fit_transform(list(Y))


# one hot encode labels
from sklearn.preprocessing import OneHotEncoder

onehot_encoder = OneHotEncoder(sparse=False)
Y_integer_encoded_reshaped = Y_integer_encoded.reshape(len(Y_integer_encoded), 1)
Y_one_hot_encoded = onehot_encoder.fit_transform(Y_integer_encoded_reshaped)

# train test split
from sklearn.model_selection import train_test_split


X_train_raw, X_test_raw, y_train, y_test = train_test_split(X, Y_one_hot_encoded, test_size=0.20, random_state=42)


# =============================================================================
# Perpare datasets for finetuning
# =============================================================================
import tensorflow as tf
physical_devices = tf.config.list_physical_devices('GPU') 
tf.config.experimental.set_memory_growth(physical_devices[0], True)

from transformers import BertTokenizer, TFBertForSequenceClassification


tokenizer = BertTokenizer.from_pretrained('bert-base-german-cased') # initialize tokenizer


# tokenize trai and test sets
max_seq_length = 128

X_train_tokens = tokenizer(list(X_train_raw),
                            truncation=True,
                            padding=True)

X_test_tokens = tokenizer(list(X_test_raw),
                            truncation=True,
                            padding=True)


# create TF datasets as input for BERT model
bert_train_ds = tf.data.Dataset.from_tensor_slices((
    dict(X_train_tokens),
    y_train
))

bert_test_ds = tf.data.Dataset.from_tensor_slices((
    dict(X_test_tokens),
    y_test
))

# =============================================================================
# setup model and finetune
# =============================================================================

# define hyperparams
num_labels = 4
learninge_rate = 2e-5
epochs = 3
batch_size = 16

# create BERT model
bert_categorical_partial = TFBertForSequenceClassification.from_pretrained('bert-base-german-cased', num_labels=num_labels)

optimizer = tf.keras.optimizers.Adam(learning_rate=learninge_rate)
bert_categorical_partial.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])

history = bert_categorical_partial.fit(bert_train_ds.shuffle(100).batch(batch_size),
          epochs=epochs,
          # batch_size=batch_size,
          validation_data=bert_test_ds.shuffle(100).batch(batch_size))

А вот результат тонкой настройки:

Epoch 1/3
155/155 [==============================] - 31s 198ms/step - loss: 8.3038 - accuracy: 0.2990 - val_loss: 8.7751 - val_accuracy: 0.2811
Epoch 2/3
155/155 [==============================] - 30s 196ms/step - loss: 8.2451 - accuracy: 0.2913 - val_loss: 8.9314 - val_accuracy: 0.2779
Epoch 3/3
155/155 [==============================] - 30s 196ms/step - loss: 8.3101 - accuracy: 0.2913 - val_loss: 9.0355 - val_accuracy: 0.2746

Наконец, я пытаюсь предсказать метки тестового набора и проверить результаты с помощью матрицы путаницы:

X_test_tokens_new = {'input_ids': np.asarray(X_test_tokens['input_ids']),
                     'token_type_ids': np.asarray(X_test_tokens['token_type_ids']),
                     'attention_mask': np.asarray(X_test_tokens['attention_mask']),
                     }

pred_raw = bert_categorical_partial.predict(X_test_tokens_new)
pred_proba = tf.nn.softmax(pred_raw).numpy()
pred = pred_proba[0].argmax(axis = 1)
y_true = y_test.argmax(axis = 1)

cm = confusion_matrix(y_true, pred)

Выход отпечатка (см):

array([[  0,   0,   0,  41],
       [  2,   0,   0, 253],
       [  2,   0,   0, 219],
       [  6,   0,   0,  96]], dtype=int64)

Как видите, моя точность очень плохая, и когда я смотрю на сантиметры, я вижу, что моя модель в значительной степени просто предсказывает одну единственную метку. Я пробовал все и запускал модель несколько раз, но всегда получаю одни и те же результаты. Я знаю, что данные, с которыми я работаю, не очень хороши, и я тренируюсь только на двух тысячах предложений с ярлыками. Но я чувствую, что точность все равно должна быть выше и, что более важно, модель не должна предсказывать только одну единственную метку в 98% случаев, верно?

Я разместил все, что использую для запуска модели, в надежде, что кто-то сможет указать мне, где я ошибаюсь. Заранее большое спасибо за вашу помощь!


person alxgal    schedule 12.01.2021    source источник
comment
Почему вы выполняете LabelEncoding, а затем OneHotEncoding на Y данных?   -  person yudhiesh    schedule 12.01.2021
comment
как распределялись ваши лейблы (сколько в ваших данных e1, e2, e3, e4)? Вы тренировались всего на 465 шагов из 2000 предложений. Я думаю, что количество е4 составляет 30%, поэтому модели легче прогнозировать только эти метки. Попробуйте потренироваться 50 эпох.   -  person Andrey    schedule 12.01.2021
comment
@yudhiesh Мне нужно было преобразовать метки в числовой формат, чтобы OneHotEncoder работал в этом случае, поэтому я сделал промежуточный шаг. Тем не менее, один горячий вектор должен быть правильным, я дважды проверил выходы, и они совпадают с метками (так что e_1 находится в позиции 0, e_2 в позиции 1 и т. Д.)   -  person alxgal    schedule 12.01.2021
comment
@Andrey, весь набор данных содержит около 6,3% e_1, 38,8% e_2, 37,6% e_3 и 17,4% e_4 меток, так что, как вы думаете, это близко. Если честно, я не совсем понимаю шаги. Когда я тренируюсь по 150 шагов в каждую эпоху, значит ли это, что я тренируюсь только по 450 наблюдениям? Как я могу это отрегулировать? Я пробовал тренироваться до 10 эпох, но точность застряла после третьей эпохи. Я попробую пробежать 50 и посмотрю, что будет. Однако я думал, что BERT настолько мощный инструмент, потому что для достижения хороших результатов не нужно столько эпох, не так ли? Разве это не часть цели трансферного обучения?   -  person alxgal    schedule 12.01.2021
comment
@alxgal вы тренируете все данные каждую эпоху. Но вы делаете это поэтапно и на каждом шагу тренируете одну партию. Количество эпох зависит от количества обучаемых переменных и объема обучающих данных. Если ваших данных мало - нужно тренировать больше эпох. Сколько обучаемых переменных в вашей модели? Вы правы насчет цели Трансферного обучения. Если вы обучили свою модель с самого начала - на это ушли бы месяцы. При использовании трансферного обучения это может занять несколько часов. Но точно не через пару минут даже ТПУ даже на 1 млн проб.   -  person Andrey    schedule 12.01.2021
comment
@alxgal Я бы посоветовал уменьшить скорость обучения, чтобы ваша точность повышалась после каждой эпохи (для первых 10 эпох)   -  person Andrey    schedule 12.01.2021
comment
Я уменьшил скорость обучения до 1e-6 и тренировался в течение 10 эпох: на самом деле результаты намного лучше, и модель предсказывает разные метки. Я сейчас готовлюсь к 50 эпохам, как вы изначально предлагали, и буду держать вас в курсе!   -  person alxgal    schedule 12.01.2021
comment
Позвольте нам продолжить это обсуждение в чате.   -  person alxgal    schedule 12.01.2021


Ответы (1)


Вы тренировались пару минут. Этого недостаточно даже для предварительно обученного BERT.

Постарайтесь снизить скорость обучения, чтобы ваша точность повышалась после каждой эпохи (в течение первых 10 эпох). И тренируйтесь для большего количества эпох (пока вы не увидите, что точность проверки снижается на 10 эпох).

person Andrey    schedule 12.01.2021
comment
Так что я сделал именно это в соответствии с вашим комментарием. Я тренировался 50 эпох со скоростью обучения 1e-6. Я получил точность 41,2% val в эпоху 25, а затем она начала снижаться до 5,01% в эпоху 50. Значит ли это, что тогда я должен просто использовать 25 эпох для обучения модели? - person alxgal; 12.01.2021
comment
@alxgal да, ваши данные слишком малы, и модель переоснащается через 25 эпох. Но со скоростью обучения можно поиграться и дальше. - person Andrey; 12.01.2021
comment
Это имеет смысл, однако у меня есть еще один вопрос к вам: когда вы говорите, что моя модель превышает данные, разве это не означает, что точность моего поезда будет очень высокой (например, 99%)? Он также уменьшается примерно до 5%, поэтому точность обучения и проверки всегда составляет около 2% друг от друга. - person alxgal; 12.01.2021
comment
@ да, обычно точность вашей тренировки должна повыситься. Не до 99%, но не должно уменьшаться (должно оставаться стабильным через некоторое количество эпох). Но NN оптимизирует потери при обучении, а не точность. Теоретически она может уменьшиться, но потеря тренировки не должна уменьшаться однозначно. Как ведет себя потеря тренировки? - person Andrey; 12.01.2021
comment
Потери, как правило, довольно высоки, что, на мой взгляд, имеет смысл, учитывая небольшой объем данных в наборе данных и низкую производительность. Потеря начинается с ~ 5, затем колеблется между 6 и 7,5, пока не достигнет 7,34 в течение последней эпохи. Да, это 7,34, а не 0,734. - person alxgal; 12.01.2021
comment
@alxgal Значение потерь не имеет значения (это не вероятность). Но он должен уменьшиться. Попробуйте еще больше снизить скорость обучения - person Andrey; 12.01.2021