Точность классификации текста Tensorflow / Keras / BERT MultiClass - PullRequest
0 голосов
/ 02 апреля 2020

Я пытаюсь настроить HuggingFace TFBertModel, чтобы можно было классифицировать некоторый текст по одной метке. У меня есть модель и работает, но с самого начала точность крайне низкая. Я ожидаю, что точность будет высокой, учитывая, что в качестве отправной точки используются предварительно обученные веса BERT. Я надеялся получить совет о том, где я ошибаюсь.

Я использую набор данных bb c -text из здесь :

Загрузка данных

df = pd.read_csv(open(<s3 url>),encoding='utf-8', error_bad_lines=False)
df = df.sample(frac=1)
df = df.dropna(how='any')

Значение имеет значение

sport            511
business         510
politics         417
tech             401
entertainment    386
Name: label, dtype: int64

Предварительная обработка

def preprocess_text(sen):
# Convert html entities to normal
sentence = unescape(sen)

# Remove html tags
sentence = remove_tags(sentence)

# Remove newline chars
sentence = remove_newlinechars(sentence)

# Remove punctuations and numbers
sentence = re.sub('[^a-zA-Z]', ' ', sentence)

# Convert to lowercase
sentence = sentence.lower()

return sentence


def remove_newlinechars(text):
    return " ".join(text.splitlines()) 

def remove_tags(text):
    TAG_RE = re.compile(r'<[^>]+>')
    return TAG_RE.sub('', text)

df['text_prepd'] = df['text'].apply(preprocess_text)

Разделение данных

train, val = train_test_split(df, test_size=0.30, shuffle=True, stratify=df['label'])

Кодирование меток

from sklearn.preprocessing import LabelEncoder

label_encoder = LabelEncoder()
y_train = np.asarray(le.fit_transform(train['label']))
y_val = np.asarray(le.fit_transform(val['label']))

Определение функции ввода BERT

# Initialise Bert Tokenizer
bert_tokenizer_transformer = BertTokenizer.from_pretrained('bert-base-cased')

def create_input_array(df, tokenizer, args):
    sentences = df.text_prepd.values

    input_ids = []
    attention_masks = []
    token_type_ids = []

    for sent in tqdm(sentences):
        # `encode_plus` will:
        #   (1) Tokenize the sentence.
        #   (2) Prepend the `[CLS]` token to the start.
        #   (3) Append the `[SEP]` token to the end.
        #   (4) Map tokens to their IDs.
        #   (5) Pad or truncate the sentence to `max_length`
        #   (6) Create attention masks for [PAD] tokens.
        encoded_dict = tokenizer.encode_plus(
            sent,  # Sentence to encode.
            add_special_tokens=True,  # Add '[CLS]' and '[SEP]'
            max_length=args.max_seq_len,  # Pad & truncate all sentences.
                pad_to_max_length=True,
                return_attention_mask=True,  # Construct attn. masks.
                return_tensors='tf',  # Return tf tensors.
            )

        # Add the encoded sentence to the list.
        input_ids.append(encoded_dict['input_ids'])

        # And its attention mask (simply differentiates padding from non-padding).
        attention_masks.append(encoded_dict['attention_mask'])

        token_type_ids.append(encoded_dict['token_type_ids'])

    input_ids = tf.convert_to_tensor(input_ids)
    attention_masks = tf.convert_to_tensor(attention_masks)
    token_type_ids = tf.convert_to_tensor(token_type_ids)

    return input_ids, attention_masks, token_type_ids

Преобразование данных в бертские входы

train_inputs = [create_input_array(train[:], tokenizer=tokenizer, args=args)]
val_inputs = [create_input_array(val[:], tokenizer=tokenizer, args=args)]

Для train_inputs, y_train и val_inputs, y_val Затем я применяю следующую функцию, которая преобразует и преобразует в numpy массивы. Возвращенный список из этой функции затем передается в качестве аргументов методу keras fit. Я понимаю, что это немного излишнее преобразование в tf.tensors, затем в numpy, но я не думаю, что это повлияет Первоначально я пытался использовать набор tf.datasets, но переключился на numpy.

def convert_inputs_to_tf_dataset(inputs,y, args):
    # args.max_seq_len = 256
    ids = inputs[0][1]
    masks = inputs[0][1]
    token_types = inputs[0][2]

    ids = tf.reshape(ids, (-1, args.max_seq_len))
    print("Input ids shape: ", ids.shape)
    masks = tf.reshape(masks, (-1, args.max_seq_len))
    print("Input Masks shape: ", masks.shape)
    token_types = tf.reshape(token_types, (-1, args.max_seq_len))
    print("Token type ids shape: ", token_types.shape)

    ids=ids.numpy()
    masks = masks.numpy()
    token_types = token_types.numpy()

    return [ids, masks, token_types, y]

Модель Keras

# args.max_seq_len = 256
# n_classes = 6
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased', trainable=True, num_labels=n_classes)

input_ids_layer = Input(shape=(args.max_seq_len, ), dtype=np.int32)
input_mask_layer = Input(shape=(args.max_seq_len, ), dtype=np.int32)
input_token_type_layer = Input(shape=(args.max_seq_len,), dtype=np.int32)

bert_layer = model([input_ids_layer, input_mask_layer, input_token_type_layer])[0]
flat_layer = Flatten()(bert_layer)
dropout= Dropout(0.3)(flat_layer)
dense_output = Dense(n_classes, activation='softmax')(dropout)

model_ = Model(inputs=[input_ids_layer, input_mask_layer, input_token_type_layer], outputs=dense_output)

Компиляция и сборка

loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
metric = tf.keras.metrics.SparseCategoricalAccuracy('accuracy')
model.compile(optimizer='adam', loss=loss, metrics=[metric])
model.fit(inputs=..., outputs=..., validation_data=..., epochs=50, batch_size = 32, metrics=metric, verbose=1)


Epoch 32/50
1401/1401 [==============================] - 42s 30ms/sample - loss: 1.6103 - accuracy: 0.2327 - val_loss: 1.6042 -
 val_accuracy: 0.2308

Поскольку я использую BERT, необходимо всего несколько эпох, поэтому я ожидал чего-то намного более 23% после 32 эпох.

Ответы [ 2 ]

1 голос
/ 23 апреля 2020

Основная проблема в этой строке: ids = inputs[0][1]. На самом деле, идентификаторы являются первым элементом inputs[0]; таким образом, это должно быть ids = inputs[0][0].

Но есть и другая проблема, которая может привести к непоследовательной точности проверки: вы должны соответствовать LabelEncoder только один раз, чтобы построить отображение метки; поэтому вы должны использовать метод transform вместо fit_transform для проверки меток.

Другой момент заключается в том, что вам может потребоваться использовать более низкую скорость обучения для оптимизатора. Скорость обучения по умолчанию для оптимизатора Adam составляет 1e-3, что может быть слишком высоким, учитывая, что вы настраиваете предварительно обученную модель . Попробуйте более низкую скорость обучения, скажем, 1e-4 или 1e-5; например tf.keras.optimizers.Adam(learning_rate=1e-4). Высокая скорость обучения для тонкой настройки предварительно обученной модели может разрушить изученные веса и нарушить процесс тонкой настройки (из-за больших значений градиента, генерируемых, особенно в начале процесса тонкой настройки).

0 голосов
/ 22 апреля 2020

Я не уверен, что понял все ваши шаги, особенно с токенизатором. Я не знаю, где проблема может быть, но это можно сделать, это гораздо проще. Преобразователь Higgungface предлагает вам простое решение для классификации текста:

model = TFBertForSequenceClassification.from_pretrained('bert-base-cased')

есть также функция для перевода данных в формат, ожидаемый моделью:

glue_convert_examples_to_features

Вам понадобится чтобы сделать глубокое погружение в do c, чтобы увидеть все параметры, которые вы можете установить, такие как количество классов, тип склеиваемой задачи для предварительной обработки ...

Ниже приведен пример: https://pypi.org/project/transformers/

...