NLP: когда машины начинают понимать нас (Часть 3)

от автора

1. Введение

В предыдущих статьях мы рассмотрели теоретические основы NLP, включая базовые понятия, такие как токенизация, стемминг, лемматизация и другие. Мы также поработали с библиотеками NLTK и spaCy и выполнили простые задания по обработке текста.

В этой статье мы продолжим изучение NLP и перейдем к более продвинутым темам, которые являются главными для построения современных приложений и моделей в области обработки естественного языка. А также создадим и обучим модели самостоятельно, используя TensorFlow/Keras и PyTorch.

2. Векторные представления слов

Векторные представления слов позволяют нам преобразовывать слова в числовые векторы. Это существенно улучшает качество моделей NLP.

В данной статье мы рассмотрим:

  1. Word2Vec

  2. GloVe

  3. FastText

  4. Библиотеку Gensim

  5. Примеры кода и задания

2.1 Word2Vec

Word2Vec — это группа моделей, которую разработала команда Google. Word2Vec преобразует слова в векторы определённой размерности, где схожие по смыслу слова имеют близкие векторные представления.

Архитектуры Word2Vec

Word2Vec представляет два основных метода обучения:

  • Continuous Bag-of-Words (CBOW): предсказывает текущее слово по контексту (окружающим словам).

  • Skip-Gram: предсказывает окружающие слова по текущему слову.

Особенности

  • Эффективность: Быстро обучается на больших объёмах текстах.

  • Качество: Учитывает семантические отношения между словами.

2.2 GloVe

Glove — это модель, разработанная в Стэнфордском университете. В отличие от Word2Vec, который фокусируется на локальных связях между словами, а GloVe фокусируется на глобальных связях, проверяя, насколько часто два слова встречаются вместе в тексте.

Особенности

  • Глобальная матрица соотношений — это способ увидеть, как слова связаны между собой в целом по всем текстам, проверяя, как часто они появляются вместе.

  • Преимущества: GloVe лучше понимает общую структуру и тематику текста, что особенно полезно, когда важно понять глобальные связи и темы в большом количестве данных.

2.3 FastText

FastText — модель, разработанная Facebook AI Research. Она расширяет Word2Vec, учитывая морфологию слов.

Особенности

  • Символьные n-граммы: Представляет слова как группу буквенных n-грамм.

  • Преимущества: Лучше справляется с редкими словами и опечатками.

2.4 Реализация и применение с помощью Gensim

Gensim — это библиотека Python для для векторных представлений.

Установка Gensim

pip install gensim

2.5 Word2Vec с использованием Gensim

Для начала подготовим наши данные:

import gensim from gensim.models import Word2Vec from gensim.utils import simple_preprocess  texts = [     "Хабр — популярная платформа для IT специалистов",     "На Хабре можно найти статьи по программированию и технологиям",     "Пользователи Хабра делятся своим опытом и знаниями", ]  # Предобработка текстов sentences = [simple_preprocess(text) for text in texts]

Обучим нашу модель Word2Vec:

model = Word2Vec(     sentences=sentences,     vector_size=100,  # размерность текстов     window=5,         # размер контекстного окна     min_count=1,      # минимальную частоту слова     workers=4,        # количество потоков     sg=0              # использование Continuous Bag-of-Words )

Получение вектора слова:

vector = model.wv['хабр'] print("Вектор слова 'хабр':\n", vector)

Вывод:

Вектор слова 'хабр': [ 0.00973555 -0.00978038 -0.00649949 0.00278379 0.00643199 -0.00536737 0.00275249 0.00912131 -0.00681542 -0.00609991 -0.00498964 -0.00367641 0.00184972 0.00968263 0.00643778 0.00039709 0.00247077 0.00844049 0.00912898 0.00562875 0.00594626 -0.00762069 -0.00382767 -0.00568033 0.00618177 -0.00225645 -0.00877944 0.00761912 0.00839968 -0.00332024 0.00911666 -0.00073836 -0.00362652 -0.00038469 0.00019443 -0.0035049 0.00281324 0.00572971 0.00686901 -0.00890347 -0.00219273 -0.0054818 0.0075211 0.0065017 -0.00436072 0.00232683 -0.00595366 0.0002365 0.00946176 -0.00260984 -0.00518772 -0.00739721 -0.00291194 -0.00086431 0.00352786 0.00974189 -0.00338928 0.00190177 0.00968101 0.00153159 0.0009865 0.00980237 0.00929546 0.00770807 -0.00617053 0.00998399 0.00584899 0.00907267 -0.0019952 0.00334994 0.00683356 -0.00389376 0.00664285 0.00256286 0.00931373 -0.0030358 -0.00310937 0.00621539 -0.00907825 -0.00725399 -0.00650003 -0.00074907 -0.00236302 0.00681552 0.00923659 -0.00090976 0.00141282 0.00202036 -0.0020198 -0.00803434 0.00744105 -0.0042979 0.00457652 0.0090897 0.00304322 0.00313879 0.00406183 -0.00270122 0.00382477 0.00033762]

Поиск наиболее похожих слов:

similar_words = model.wv.most_similar('хабр') print("Слова, похожие на 'хабр':") for word, similarity in similar_words:     print(f"{word}: {similarity}")

Вывод:

Слова, похожие на 'хабр': можно: 0.15923377871513367 популярная: 0.1528114527463913 технологиям: 0.14256368577480316 статьи: 0.1326463520526886 своим: 0.1193675547838211 на: 0.07913301885128021 специалистов: 0.07480262219905853 делятся: 0.059599656611680984 по: 0.03546776622533798 для: 0.03307188302278519

2.5 FastText с использованием Gensim

Обучение модели:

from gensim.models import FastText  ft_model = FastText(     vector_size=100,     window=5,     min_count=1,     workers=4 )  ft_model.build_vocab(corpus_iterable=sentences)  ft_model.train(      corpus_iterable=sentences,     total_examples=len(sentences),     epochs=10 )

Поиск похожих слов:

similar_words_ft = ft_model.wv.most_similar('хабр') print("Слова, похожие на 'хабр':") for word, similarity in similar_words_ft:     print(f"{word}: {similarity:.3f}")

Вывод:

Слова, похожие на 'хабр': хабра: 0.514 хабре: 0.421 пользователи: 0.046 найти: 0.045 технологиям: 0.041 специалистов: 0.019 по: 0.016 делятся: 0.010 на: -0.002 статьи: -0.034

3. Классификация текста с помощью scikit-learn

Основные шаги класcификации текста:

  1. Сбор данных

  2. Предобработка данных

  3. Преобразование текста в числовой формат

  4. Обучение модели

  5. Оценка модели

  6. Применение модели

3.1 Работа с размеченными данными

Размеченные данные — это данные, где каждому экземпляру соответствует метка или категория.

Источники размеченных данных

  • Многие наборы данных доступны в библиотеке sklearn.datasets или nltk.

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

3.2 Оценка качества моделей

Метрики оценки

  • Точность (Accuracy): Показывает, какую долю всех предсказаний модель сделала правильно.

  • Матрица ошибок (Confusion Matrix): Таблица, которая показывает, сколько раз модель правильно или неправильно предсказала каждый класс.

  • Precision: Определяет сколько из всех примеров, которые модель предсказала как положительные, на самом деле положительные.

  • Recall: Определяет, сколько из всех реальных положительных примеров, модель обнаружила.

  • F1-score: Среднее значение между Precision и Recall.

Валидация моделей

  • Тренировочные и тестовые данные: Разделение данных на обучающую и тестовую выборки.

  • Кросс-валидация: Разделение данных на K подвыборок и обучение модели K раз.

3.3 Примеры кода

Классификация текстов новостей из 20 Newsgroups датасета

Этот набор данных представляет собой коллекцию документов группы новостей. 

Импорт библиотек:

import numpy as np from sklearn.datasets import fetch_20newsgroups from sklearn.model_selection import train_test_split from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import classification_report, confusion_matrix, accuracy_score

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

categories = ['rec.sport.baseball', 'rec.sport.hockey', 'talk.politics.mideast', 'sci.space'] newsgroups = fetch_20newsgroups(subset='all', categories=categories)   X = newsgroups.data #тексты y = newsgroups.target #метки

Разделение на обучающую и тестовую выборки:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Преобразование текста в числовой формат:

tfidf_vectorizer = TfidfVectorizer(stop_words='english', lowercase=True) X_train_tfidf = tfidf_vectorizer.fit_transform(X_train) X_test_tfidf = tfidf_vectorizer.transform(X_test)

Обучение модели с использованием байесовского классификатора:

model = MultinomialNB() model.fit(X_train_tfidf, y_train)

Прогнозирование и оценка модели:

y_pred = model.predict(X_test_tfidf) # Прогнозирование на тестовой выборке  print("Accuracy:", accuracy_score(y_test, y_pred)) # Оценка точности  print(classification_report(y_test, y_pred, target_names=newsgroups.target_names))

Вывод:

Подробный отчет

Подробный отчет

Построение матрицы ошибок:

import matplotlib.pyplot as plt import seaborn as sns  cm = confusion_matrix(y_test, y_pred) sns.heatmap(cm, annot=True, fmt='d',             xticklabels=newsgroups.target_names,             yticklabels=newsgroups.target_names) plt.ylabel('Истинный класс') plt.xlabel('Предсказанный класс') plt.show()

Вывод:

Матрица ошибок

Матрица ошибок

4. Нейронные сети для NLP

Нейронные сети — это мощный инструмент для решения сложных задач в различных сферах. Они постоянно готовы адаптироваться и обучаться, что делает их очень полезными для работы с большими данными.

Основные компоненты

  • Входной слой: Получает данные.

  • Скрытые слои: Обрабатывают эти данные, выявляя в них важные особенности.

  • Выходной слой: Выдаёт результат — ответ или предсказание.

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

  • Функция потерь: Показывает, насколько хороша модель, сравнивая предсказания с реальными данными.

  • Оптимизаторы: Это алгоритмы, которые помогают модели учиться на своих ошибках, корректируя параметры для улучшения точности.

Процесс обучения

  • Прямое распространение (forward propagation): Процесс, при котором входные данные проходят через нейронную сеть от входного слоя к выходному, чтобы сделать предсказание.

  • Обратное распространение (backpropagation): Алгоритм обучения нейронной сети, при котором сеть корректирует свои веса, чтобы уменьшить ошибку между предсказанием и реальным ответом.

4.2 Рекуррентные нейронные сети (RNN)

Рекуррентные нейронные сети — это модели, которые могут помнить предыдущие данные при обработке последовательностей. Они используют информацию из прошлого для улучшения понимания настоящего и будущего элементов.

Проблемы стандартных RNN

  • Ванишинг градиент (исчезающий градиент): при обучении на длинных последовательностях градиенты могут становиться слишком малыми, затрудняя обучение.

  • Эффективность на коротких последовательностях: стандартные RNN хорошо работают с короткими последовательностями, но плохо на длинных.

4.3 LSTM и GRU

LSTM (Long Short-Term Memory) — это разновидность RNN, разработанная для решения проблемы исчезающего градиента.

Компоненты LSTM

  • Входные ворота: Контролируют, какую новую информацию добавить в текущее состояние памяти нейрона в LSTM-сети.

  • Забывающие ворота: Контролируют, какую информацию удалить из состояния памяти.

  • Выходные ворота: Контролируют, какую информацию из состояния памяти использовать для текущего вывода.

GRU (Gated Recurrent Unit) — упрощенная версия LSTM с меньшим количеством ворот, которая зачастую показывает схожие результаты.

Компоненты GRU

  • Ворот обновления: Контролирует, какую часть информации из памяти сохранить, а какую обновить с новой информацией.

  • Ворот сброса: Контролирует, какую часть предыдущей информации забыть при вычислении нового состояния.

4.4 Трансформеры (BERT, GPT)

Трансформеры — это мощные модели, которые используют механизм внимания для эффективной обработки последовательностей данных. Они способны фокусироваться на важных частях входной информации, независимо от их позиции, что позволяет им лучше понимать контекст и сложные взаимосвязи.

Архитектура модели -трансформера

Ключевые компоненты

  • Механизм внимания (Attention Mechanism): Позволяет модели определять, на какие части входных данных обратить больше внимания при обработке.

  • Многоголовое внимание (Multi-Head Attention): Это расширение механизма внимания, которое позволяет модели фокусироваться на разных факторах последовательности одновременно.

  • Фидфорвард слои: Это обычные полносвязные нейронные слои, которые применяются к каждому элементу последовательности отдельно.

Преимущества трансформеров

  • Параллельная обработка: позволяет обучать модели быстрее при использовании GPU.

  • Эффективная работа с длинными зависимостями: механизм внимания эффективно обрабатывает связи между отдаленными элементами.

BERT (Bidirectional Encoder Representations from Transformers)

  • BERT — модель на основе трансформеров, обученная на большом объеме текстов.

  • Двухнаправленная: учитывает контекст как слева, так и справа от целевого слова.

  • Применение: задачи NLP, такие как классификация текста, ответы на вопросы и т.д.

GPT (Generative Pre-trained Transformer)

  • GPT — серия моделей от OpenAI, использующих архитектуру трансформеров.

  • Однонаправленная: предсказывает следующее слово на основе предыдущих.

  • GPT o1, GPT 4, GPT 3: мощные модели, способные генерировать связный и осмысленный текст.

5. Реализация простых моделей с использованием TensorFlow/Keras

Установка библиотек:

pip install tensorflow

Импорт библиотек:

import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import SimpleRNN, Dense, Embedding from tensorflow.keras.preprocessing.sequence import pad_sequences from tensorflow.keras.preprocessing.text import Tokenizer

Подготовка данных:

texts = [     "Хабр — популярная платформа для IT-специалистов",       "На Хабре можно найти статьи по программированию и технологиям",       "Пользователи Хабра делятся своим опытом и знаниями",       "Хабр помогает разработчикам узнавать о новых технологиях",       "Не все статьи на Хабре полезны",      "Иногда на Хабре сложно найти нужную информацию",       "Хабр может быть сложен для понимания новичками",       "Некоторые статьи на Хабре слишком технически сложны" ]  labels = [1, 1, 1, 1, 0, 0, 0, 0] # Метки (0 - негативный, 1 - позитивный)  labels = np.array(labels, dtype='float32') # Преобразуем метки в numpy.ndarray

Токенизация текста:

tokenizer = Tokenizer() tokenizer.fit_on_texts(texts) sequences = tokenizer.texts_to_sequences(texts)  word_index = tokenizer.word_index # Словарь print("Размер словаря:", len(word_index))

Вывод:

Размер словаря: 28

Паддинг последовательностей:

maxlen = 10 data = pad_sequences(sequences, maxlen=maxlen)

Создание модели:

model = Sequential() model.add(Embedding(input_dim=len(word_index) + 1, output_dim=32)) model.add(SimpleRNN(32)) model.add(Dense(1, activation='sigmoid'))

Компиляция модели:

model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

Обучение модели:

model.fit(data, labels, epochs=10)

Вывод:

Точность обучения

Точность обучения

Давайте проверим, как работает обученная модель на новых данных.

Подготовим новые тексты для проверки:

test_texts = [     "Я нашёл полезную статью на Хабре о машинном обучении",            "На Хабре слишком много сложной информации для меня",               "Статьи на Хабре помогают мне развиваться как программисту",        "Иногда Хабр бывает непонятен новичкам в IT",                       "Обожаю читать обзоры новых технологий на Хабре",                   "На Хабре мало информации по интересующей меня теме",               "Хабр - отличный ресурс для IT-специалистов",                      "Сложно разобраться в статьях на Хабре без подготовки",         ]

Преобразуем новые тексты так же, как и обучающие данные:

test_sequences = tokenizer.texts_to_sequences(test_texts) # Преобразуем тексты в последовательности test_data = pad_sequences(test_sequences, maxlen=maxlen)  print("Преобразованные тестовые последовательности:") print(test_data) 

Вывод:

Сделаем предсказания на новых данных:

predictions = model.predict(test_data)

Вывод:

Выведем результаты предсказаний:

threshold = 0.5 # Порог классификации  for i, text in enumerate(test_texts):     prediction = predictions[i][0]     predicted_label = "Позитивный" if prediction >= threshold else "Негативный"     print(f"Текст: {text}")     print(f"Предсказание: {prediction:.4f}")     print(f"Класс: {predicted_label}")     print("-" * 50) 

Вывод:

6. Теперь рассмотрим реализацию LSTM c использованием PyTorch

Установка библиотек:

pip install torch

Импорт библиотек:

import numpy as np import torch import torch.nn as nn from torch.utils.data import DataLoader, Dataset from tensorflow.keras.preprocessing.text import Tokenizer

Подготовка данных:

texts = [     "Хабр — популярная платформа для IT-специалистов",     "На Хабре можно найти статьи по программированию и технологиям",     "Пользователи Хабра делятся своим опытом и знаниями",     "Хабр помогает разработчикам узнавать о новых технологиях",     "Не все статьи на Хабре полезны",     "Иногда на Хабре сложно найти нужную информацию",     "Хабр может быть сложен для понимания новичками",     "Некоторые статьи на Хабре слишком технически сложны" ]  labels = [1, 1, 1, 1, 0, 0, 0, 0] labels = np.array(labels, dtype='float32') # Преобразуем метки в массив NumPy maxlen = 10 # Параметр максимальной длины последовательности  tokenizer = Tokenizer() # Токенизация текста tokenizer.fit_on_texts(texts) word_index = tokenizer.word_index print("Размер словаря:", len(word_index))

Определение класса Dataset:

class TextDataset(Dataset):     def __init__(self, texts, labels, tokenizer, maxlen):         self.texts = texts         self.labels = labels         self.tokenizer = tokenizer         self.maxlen = maxlen      def __len__(self):         return len(self.texts)      def __getitem__(self, idx):            text = self.texts[idx] #Получаем текст         label = self.labels[idx] #Получаем метку          sequence = self.tokenizer.texts_to_sequences([text])[0] # Преобразуем текст в последовательность токенов          if len(sequence) < self.maxlen: # Применяем паддинг или обрезку последовательности             sequence = np.pad(sequence, (0, self.maxlen - len(sequence)), 'constant')         else:             sequence = sequence[:self.maxlen]          sequence = torch.tensor(sequence, dtype=torch.long)  # Преобразуем в тензоры PyTorch         label = torch.tensor(label, dtype=torch.float32)          return sequence, label 

Определение модели LSTM:

class LSTMModel(nn.Module):     def __init__(self, vocab_size, embedding_dim, hidden_dim):         super(LSTMModel, self).__init__()         self.embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim)         self.lstm = nn.LSTM(input_size=embedding_dim, hidden_size=hidden_dim, batch_first=True)         self.linear = nn.Linear(hidden_dim, 1)         self.sigmoid = nn.Sigmoid()      def forward(self, x): # x имеет размерность (batch_size, maxlen)         embeds = self.embedding(x)         # embeds имеет размерность (batch_size, maxlen, embedding_dim)         lstm_out, (hn, cn) = self.lstm(embeds)         out = self.linear(hn[-1]) #Используем скрытый слой         out = self.sigmoid(out)         return out 

Подготовка DataLoader и модели:

from torch.utils.data import DataLoader  dataset = TextDataset(texts, labels, tokenizer, maxlen) # Создаём датасет dataloader = DataLoader(dataset, batch_size=2, shuffle=True) # Создаём загрузчик данных  vocab_size = len(word_index) + 1   embedding_dim = 32 hidden_dim = 32  model = LSTMModel(vocab_size, embedding_dim, hidden_dim) # Инициализируем модель criterion = nn.BCELoss() # Инициализируем функцию потерь optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # Инициализируем оптимизатор

Обучение модели:

model.train()  num_epochs = 10  for epoch in range(num_epochs):     total_loss = 0     for sequences, labels_batch in dataloader:         optimizer.zero_grad()         outputs = model(sequences) # Прямой проход         outputs = outputs.squeeze()         loss = criterion(outputs, labels_batch) # Вычисляем потерю         loss.backward() # Обратный проходоптимизация         optimizer.step() # Оптимизация         total_loss += loss.item()     avg_loss = total_loss / len(dataloader)     print(f"Эпоха {epoch+1}, Потеря: {avg_loss:.4f}") 

Вывод:

Проверим модель на новых данных:

test_texts = [     "Хабр предоставляет отличные возможности для обучения",       "Некоторые статьи на Хабре трудно понять",                ]  test_sequences = [] # Преобразуем тексты в последовательности for text in test_texts:     sequence = tokenizer.texts_to_sequences([text])[0]     if len(sequence) < maxlen:         sequence = np.pad(sequence, (0, maxlen - len(sequence)), 'constant')     else:         sequence = sequence[:maxlen]     test_sequences.append(sequence)  test_sequences = torch.tensor(test_sequences, dtype=torch.long) # Преобразуем в тензор  model.eval() # Переводим модель в режим оценки  with torch.no_grad():     outputs = model(test_sequences)     predictions = outputs.squeeze().numpy()  threshold = 0.5  for i, text in enumerate(test_texts):     prediction = predictions[i]     predicted_label = "Позитивный" if prediction >= threshold else "Негативный"     print(f"Текст: {text}")     print(f"Предсказание: {prediction:.4f}")     print(f"Класс: {predicted_label}")     print("-" * 50) 

Вывод:

7. Вывод

В ходе данной статьи мы подробно изучили основные концепции и методы, используемые в области обработки естественного языка. Рассмотренные темы охватывают большое количество инструментов и технологий, которые позволяют эффективно работать с текстовыми данными и решать различные задачи анализа и понимания естественного языка. В следующих статьях мы займёмся реализацией небольших проектов. Спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/864912/