Как стать автором
Обновить

Обработка естественного языка (NLP). Личный опыт — мой первый запуск BERT

Время на прочтение4 мин
Количество просмотров3.5K

BERT — Bidirectional Encoder Representations from Transformers

Здесь не будет рассказываться о том, что такое BERT, как это работает и для чего применяется — в сети об этом достаточно информации.

Эта статья про личный опыт — как конкретно у меня получилось запустить BERT с чистого Colab по конкретным описаниям.

Исходники

Изучение материала основывалось на следующих статьях:

Визуализируя нейронный машинный перевод (seq2seq модели с механизмом внимания)
Transformer в картинках
BERT, ELMO и Ко в картинках (как в NLP пришло трансферное обучение)
Ваш первый BERT: иллюстрированное руководство
A Visual Notebook to Using BERT for the First TIme.ipynb

Применяемые фрагменты кода

Код в целом без подробностей и комментариев, так как все подробно изложено в приведенных статьях.

# устанавливаем трансформеры
!pip install transformers 
# устанавливаем библиотеки

import numpy as np
import pandas as pd
import torch
import transformers as ppb

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score

import warnings
warnings.filterwarnings('ignore')
# скачиваем dataset
df = pd.read_csv('https://github.com/clairett/pytorch-sentiment-classification/raw/master/data/SST2/train.tsv', delimiter='\t', header=None)
# выбираем фрагмент для ускорения и экономии времени
batch_1 = df[:2000]
# проверяем, что с данными все ок
print(batch_1[:5])
# Загружаем предобученную модель и токенизаторы 
model_class, tokenizer_class, pretrained_weights = (ppb.DistilBertModel, ppb.DistilBertTokenizer, 'distilbert-base-uncased')
tokenizer = tokenizer_class.from_pretrained(pretrained_weights)
model = model_class.from_pretrained(pretrained_weights)
# токенизируем 
tokenized = batch_1[0].apply((lambda x: tokenizer.encode(x, add_special_tokens=True)))
# делаем из списков массив, чтобы была одинаковая длина

max_len = 0
for i in tokenized.values:
    if len(i) > max_len:
        max_len = len(i)

padded = np.array([i + [0]*(max_len-len(i)) for i in tokenized.values])
# маскируем сделанные добавления
attention_mask = np.where(padded != 0, 1, 0)
# создаем входной вектор из матрицы токенов

input_ids = torch.tensor(padded)  
attention_mask = torch.tensor(attention_mask)

with torch.no_grad():
    last_hidden_states = model(input_ids, attention_mask=attention_mask)
# разделяем данные на обучающую и тестовую выборки
labels = batch_1[1]
train_features, test_features, train_labels, test_labels = train_test_split(features, labels)
# обучаем модель логистической регрессии на обучающей выборке
lr_clf = LogisticRegression()
lr_clf.fit(train_features, train_labels)

На данном этапе работы по обучению модели, описанные в статьях, закончены.

Ссылка на блокнот Colab

Проверяем корректность

Прежде всего посмотрим показатели точности:

# модель обучена, смотрим метрики 
print('# train:',lr_clf.score(train_features, train_labels))
print('# test:',lr_clf.score(test_features, test_labels))

# train: 0.906
# test: 0.844

Теперь проверим «визуально», что у нас в принципе получилось обучить модель. Для этого возьмем размеченные фрагменты из датасета, посчитаем их сами с помощью полученных коэффициентов и сравним результаты.

# распечатаем фрагмент из датасета
print(batch_1[:5])

0 a stirring , funny and finally transporting re... 1
1 apparently reassembled from the cutting room f... 0
2 they presume their audience wo n't sit still f... 0
3 this is a visually stunning rumination on love... 1
4 jonathan parker 's bartleby should have been t... 1

# посчитаем сами и сравним

def LR(x): return (1 if x > 0 else 0)
def correct(LR, label): return True if LR - label == 0 else False

sum_false = 0
N = 5

for i in range (N):
  if i and i % 100 == 0: print(i)
  array = np.array(tokenized[i])
  array = np.pad(array, (0, max_len - len(array)), 'constant')
  attention_mask_this = np.where(array != 0, 1, 0)
  input_ids_this = torch.tensor([array])  
  attention_mask_this = torch.tensor(attention_mask_this)
  with torch.no_grad():
      last_hidden_states_this = model(input_ids_this, attention_mask=attention_mask_this)
  features_this = last_hidden_states_this[0][:,0,:].numpy()

  sum = np.dot(features_this,lr_clf.coef_[0]) 

  if N < 20: print(i, labels[i], LR(sum), correct(LR(sum), labels[i]), sum)  
  if correct(LR(sum), labels[i]) == True: sum_false += 1

print()
print(sum_false/(i+1))

0 1 1 True [3.85857511]
1 0 0 True [-6.51904703]
2 0 0 True [-2.21694384]
3 1 1 True [3.65850942]
4 1 0 False [-0.32528463]

0.8

Видим, что 1 ошибка из 5, то есть совпадает с ожидаемой точностью.
Можно сравнивать и больше строк, меняя N, и видно, что вывод модели совпадает с заданной разметкой в параметрах заявленной точности, то есть считаем, что модель понята и «перенесена» корректно.

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

Теперь проанализируем свои фрагменты текста, то есть зададим новые фрагменты и выведем оценку с помощью полученных коэффициентов.

# задаем свои фрагменты

texts = [
    'All is good', 
    'it is so bad', 
    'nice to meet you',
    'it is so rainy',
    'he is a stupid',
    'I like my car',
    'have a nice day'    
    ]
# Классифицируем свои фрагменты

for text in texts:
  array = tokenizer.encode(text, add_special_tokens=True)
  array = np.pad(array, (0, max_len - len(array)), 'constant')
  attention_mask_this = np.where(array != 0, 1, 0)
  input_ids_this = torch.tensor([array])  
  attention_mask_this = torch.tensor(attention_mask_this)
  with torch.no_grad():
      last_hidden_states_this = model(input_ids_this, attention_mask=attention_mask_this)
  features_this = last_hidden_states_this[0][:,0,:].numpy()

  sum = np.dot(features_this,lr_clf.coef_[0]) 

  print(text, ':', sum, ':', LR(sum)) 

All is good : [1.90097556] : 1
it is so bad : [-1.86999172] : 0
nice to meet you : [4.96587936] : 1
it is so rainy : [-2.53429642] : 0
he is a stupid : [-2.4318853] : 0
I like my car : [0.2118134] : 1
have a nice day : [2.30864912] : 1

1 - позитивно
0 - негативно

Видим, что модель корректно классифицирует представленные фрагменты.

Результат

В результате модель корректно классифицирует фрагменты текста на позитивный/негативный.

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


Ссылка на блокнот Colab

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+3
Комментарии5

Публикации

Истории

Работа

Python разработчик
142 вакансии
Data Scientist
63 вакансии

Ближайшие события