Пошаговое руководство по NLP: конструирование признаков текстовых данных

“Хорошие функции не рождаются, их разрабатывают” (технический директор Kaggle и специалист по анализу данных Бен Хамнер).

Конструирование признаков (feature engineering)  —  процесс выбора и создания наиболее релевантных и полезных признаков для ввода в модель машинного обучения. Это важнейший шаг в ходе МО, который может существенно повлиять на производительность, сложность и способность модели обобщать новые данные. Тщательно выбирая и конструируя признаки, используемые в качестве входных данных, можно повысить точность и эффективность модели и избежать ее чрезмерного обучения.

Одним из основных текстовых источников является Twitter. Соцсеть содержит множество информации, которую можно использовать для создания моделей МО, помогающих решать различные задачи, такие как анализ настроений, тематическая классификация и многие другие.

Чтобы обучить модель МО на данных твитов, сначала нужно извлечь из них признаки. Рассмотрим различные типы признаков, которые можно извлечь из твитов, и способы их получения в Python.

1. Текст

Самым важным признаком, который можно извлечь из твита, является текст. Он может быть использован “как есть” или предварительно обработан различными способами в зависимости от задачи, выполняемой моделью. Например, можно проводить стемминг и лемматизацию текста, удалять из него стоп-слова. Вот пример предварительной обработки текста с помощью набора инструментов обработки естественного языка (nltk):

import nltk

def preprocess(text):
# Токенизация текста
tokens = nltk.word_tokenize(text)
# Стемминг токенов
stemmer = nltk.PorterStemmer()
stemmed_tokens = [stemmer.stem(token) for token in tokens]
# Удаление стоп-слов
stop_words = nltk.corpus.stopwords.words('english')
filtered_tokens = [token for token in stemmed_tokens if token not in stop_words]
# Объединение токенов в одну строку
processed_text = ' '.join(filtered_tokens)
return processed_text

2. Хэштеги

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

import re

def extract_hashtags(text):
hashtags = re.findall(r'#\w+', text)
return hashtags

В качестве альтернативы можно создать бинарный признак для каждого уникального хэштега: значение 1 будет индикатором того, что твит содержит хэштег, а значение 0  —  что нет.

3. Упоминания пользователей

Упоминания пользователей (например, “@user”)  —  еще один признак, который можно извлечь из твитов. Извлекаются упоминания пользователей с помощью регулярного выражения, как показано ниже:

import re

def extract_mentions(text):
mentions = re.findall(r'@\w+', text)
return mentions

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

4. Длина твита

Длина твита (в символах)  —  полезный признак, поскольку длинные твиты обычно содержат больше информации, чем короткие. Мы можем извлечь длину твита следующим образом:

def get_length(text):
return len(text)

5. Количество восклицательных знаков

Количество восклицательных знаков в твите может быть полезным признаком, поскольку указывает на яркость и эмоциональность твита. Извлекается количество восклицательных знаков следующим образом:

def get_exclamation_count(text):
return text.count('!')

6. Количество вопросительных знаков

Как и в случае с восклицательными знаками, количество вопросительных знаков в твите служит полезным признаком. Мы можем извлечь количество вопросительных знаков следующим образом:

def get_question_mark_count(text):
return text.count('?')

7. Тональность

Тональность твита (положительная, отрицательная или нейтральная) бывает полезным признаком при выполнении таких задач, как анализ настроений. Мы можем применить инструмент анализа настроений для извлечения тональности твита. Одним из таких инструментов является SentiStrength, который используется в Python следующим образом:

import sentistrength

def get_sentiment(text):
sentiment = sentistrength.analyze(text)[0]
return sentiment

8. Теги частей речи

Теги частей речи могут предоставить информацию о структуре и функции слов в твите. При извлечении тегов частей речи для слов в твите используется средство частеречной разметки. Вот пример с использованием набора инструментов обработки естественного языка (nltk):

import nltk

def get_pos_tags(text):
tokens = nltk.word_tokenize(text)
pos_tags = nltk.pos_tag(tokens)
pos_tags = [tag for word, tag in pos_tags]
return pos_tags

9. Именованные сущности

Именованные сущности  —  это конкретные люди, организации, места и т.д., которые упоминаются в твите. Для извлечения именованных сущностей из твита можно использовать инструмент распознавания именованных сущностей. Вот пример с применением spaCy:

import spacy

# Загрузка модели spaCy
nlp = spacy.load('en_core_web_sm')

def get_named_entities(text):
doc = nlp(text)
named_entities = [X.text for X in doc.ents]
return named_entities

10. Эмодзи

Эмодзи передают эмоции и настроение в твите и поэтому являются полезным признаком. Мы можем использовать библиотеку emoji для извлечения эмодзи из твита следующим образом:

import emoji

def get_emojis(text):
emojis = [emoji.emojize(word) for word in text.split()]
emojis = [emoji for emoji in emojis if emoji != word]
return emojis

11. URL

Содержащийся в твите URL  —  полезный признак при выполнении таких задач, как классификация тем и веб-скрейпинг. Мы можем извлечь URL из твита с помощью регулярного выражения следующим образом:

import re

def get_url(text):
url = re.findall(r'https?://\S+', text)
return url

12. Количество слов

Количество слов в твите  —  полезный признак, поскольку дает представление о сложности и длине твита. Мы можем получить количество слов следующим образом:

def get_word_count(text):
return len(text.split())

13. Средняя длина слов

Средняя длина слов в твите также служит полезным признаком. Мы можем вычислить среднюю длину слов следующим образом:

def get_avg_word_length(text):
words = text.split()
word_count = len(words)
avg_word_length = sum(len(word) for word in words) / word_count
return avg_word_length

14. Слова, написанные заглавными буквами

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

def get_capitalized_words(text):
capitalized_words = [word for word in text.split() if word.isupper()]
return capitalized_words

15. Встраивания в твитах

Наконец, мы можем использовать предварительно обученную языковую модель для генерации встраиваний для твитов. Эти встраивания могут передать значение и контекст слов в твите и часто используются в качестве признаков для различных задач, таких как текстовая классификация и сопоставление по сходству. Вот пример с использованием BERT:

import transformers

# Загрузка модели BERT
model = transformers.BertModel.from_pretrained('bert-base-uncased')
def get_tweet_embedding(text):
# Токенизация текста и конвертация его в тензор PyTorch
tokens = transformers.BertTokenizer.from_pretrained('bert-base-uncased').tokenize(text)
tokens = torch.tensor([transformers.BertTokenizer.from_pretrained('bert-base-uncased').convert_tokens_to_ids(tokens)])
# Проводка тензора через модель BERT для получения встраивания
embedding = model(tokens)[0][0]
return embedding

Примечание: у вас есть возможность выбора из таких встраиваний, как Bag of Words, TF-IDF, Word2Vec, GloVe, FastText и др.

Подведем итоги

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

import re
import nltk
import spacy
import emoji
import sentistrength
import transformers
import torch

# Загрузка модели spaCy
nlp = spacy.load('en_core_web_sm')
# Загрузка модели BERT
model = transformers.BertModel.from_pretrained('bert-base-uncased')

def get_features(tweets):
features = []
for tweet in tweets:
feature_vector = []
# Текст
feature_vector.append(tweet)
# Хэштеги
hashtags = [hashtag for hashtag in re.findall(r'#\w+', tweet)]
feature_vector.extend(hashtags)
# Упоминания пользователя
user_mentions = [mention for mention in re.findall(r'@\w+', tweet)]
feature_vector.extend(user_mentions)
# Длина твита
feature_vector.append(len(tweet))
# Количество восклицательных знаков
feature_vector.append(tweet.count('!'))
# Количество вопросительных знаков
feature_vector.append(tweet.count('?'))
# Тональность
sentiment = sentistrength.analyze(tweet)[0]
feature_vector.append(sentiment)
# Теги частей речи
pos_tags = nltk.pos_tag(nltk.word_tokenize(tweet))
pos_tags = [tag for word, tag in pos_tags]
feature_vector.extend(pos_tags)
# Именованные сущности
doc = nlp(tweet)
named_entities = [X.text for X in doc.ents]
feature_vector.extend(named_entities)
# Эмодзи
emojis = [emoji.emojize(word) for word in tweet.split()]
emojis = [emoji for emoji in emojis if emoji != word]
feature_vector.extend(emojis)
# URL
url = re.findall(r'https?://\S+', tweet)
feature_vector.extend(url)
# Количество слов
word_count = len(tweet.split())
feature_vector.append(word_count)
# Средняя длина слов
avg_word_length = sum(len(word) for word in tweet.split()) / word_count
feature_vector.append(avg_word_length)
# Слова, написанные заглавными буквами
capitalized_words = [word for word in tweet.split() if word.isupper()]
feature_vector.extend(capitalized_words)
# Встраивания для твитов
tokens = transformers.BertTokenizer.from_pretrained('bert-base-uncased').tokenize(tweet)
tokens = torch.tensor([transformers.BertTokenizer.from_pretrained('bert-base-uncased').convert_tokens_to_ids(tokens)])
embedding = model(tokens)[0][0]
feature_vector.extend(embedding)
features.append(feature_vector)
return features

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

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Harshmeet Singh Chandhok: A Step-to-Step Guide for Feature Engineering on Textual Data- NLP

Предыдущая статьяПрофессиональная обработка ошибок в TypeScript
Следующая статьяКак работает маршрутизация Express