1. Введение
В предыдущих статьях мы рассмотрели теоретические основы NLP, включая базовые понятия, такие как токенизация, стемминг, лемматизация и другие. Мы также поработали с библиотеками NLTK и spaCy и выполнили простые задания по обработке текста.
В этой статье мы продолжим изучение NLP и перейдем к более продвинутым темам, которые являются главными для построения современных приложений и моделей в области обработки естественного языка. А также создадим и обучим модели самостоятельно, используя TensorFlow/Keras и PyTorch.
2. Векторные представления слов
Векторные представления слов позволяют нам преобразовывать слова в числовые векторы. Это существенно улучшает качество моделей NLP.
В данной статье мы рассмотрим:
Word2Vec
GloVe
FastText
Библиотеку Gensim
Примеры кода и задания
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ификации текста:
Сбор данных
Предобработка данных
Преобразование текста в числовой формат
Обучение модели
Оценка модели
Применение модели
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. Вывод
В ходе данной статьи мы подробно изучили основные концепции и методы, используемые в области обработки естественного языка. Рассмотренные темы охватывают большое количество инструментов и технологий, которые позволяют эффективно работать с текстовыми данными и решать различные задачи анализа и понимания естественного языка. В следующих статьях мы займёмся реализацией небольших проектов. Спасибо за внимание!
ENick
"Активирационные функции: " - это функции активации?
https://habr.com/ru/companies/raft/articles/784964/
ceoofmsc Автор
Спасибо за ваш комментарий!
Да, это функции активации. В статье отредактировал, чтоб было понятнее.