Узнайте, как подготовить текстовые данные для задач НЛП
Что такое предварительная обработка текста?
Предварительная обработка текста состоит из ряда методов, предназначенных для подготовки текста к задачам обработки естественного языка (NLP). Шум в тексте проявляется в нескольких формах, таких как смайлики, знаки препинания, разные регистры и многое другое. Основная цель очистки текста — уменьшить шум в наборе данных, сохраняя при этом как можно больше релевантной информации.
import pandas as pd import nltk from nltk.stem import PorterStemmer, WordNetLemmatizer import numpy as np import re from sklearn.utils import shuffle import string nltk.download('stopwords') nltk.download('wordnet') nltk.download('omw-1.4')
Важная терминология 📝
Прежде чем мы углубимся, нам нужно определить некоторые термины, которые будут использоваться в этом руководстве.
Документ: отдельная единица текста. Это может быть предложение, абзац или статья.
doc1 = "Hello my name is Gabe" doc2 = "What is your name?"
Корпус: относится к набору текстов или документов.
corpus = [doc1, doc2]
Токены: токенизация — это способ разделения фрагмента текста на более мелкие единицы, называемые токенами. Здесь токенами могут быть как слова, символы, так и подслова (символы n-грамм).
document = "Hex is awesome" tokens = ['Hex', 'is', 'awesome']
Нормализация текста ⚖️
Нормализация текста — это процесс преобразования регистра текста таким образом, чтобы он был одинаковым во всем документе. Это выглядит как преобразование "Hello my name is Gabe"
в "hello my name is gabe"
. Почему этот шаг так важен, спросите вы? Что ж, во многих задачах языкового моделирования модели будут использовать объект, называемый словарем, который представляет собой набор уникальных слов в корпусе документов. Итак, если у меня есть следующее:
corpus = [ 'My cats love dogs', 'Cats are my best friends', ]
Тогда словарный запас будет:
vocab = { "My", "cats", "love", "dogs", "Cats", "are" ,"my", "best", "friends"}
В этом примере одно и то же слово появляется в словаре более одного раза, потому что текст не был нормализован. После нормализации это будет выглядеть примерно так:
vocab = { "my", "cats", "love", "dogs", "are", "best", "friends"}
И теперь мы сократили наш словарный запас на 2 слова, однако в гораздо большем корпусе это может сократить наш словарный запас на сотни слов!
normalize = lambda document: document.lower() sample_text = "This Is some Normalized TEXT" normalize(sample_text)
Удаление нежелательных символов 🙅🏽♀️
Следующим шагом будет удаление всех символов, которые не добавляют ценности или значения нашему документу. Это могут быть знаки препинания, числа, смайлики, даты и т. д. Для нас, людей, знаки препинания могут добавить в текст много полезной информации. Это может быть добавление структуры к языку или указание тона/настроения. Для языковых моделей пунктуация не добавляет столько контекста, как для людей, и в большинстве случаев просто добавляет в наш словарный запас дополнительные символы, которые нам не нужны. Сказав это, есть некоторые случаи, когда вы хотели бы сохранить эти символы в своих данных. Например, в задачах генерации текста может быть полезно сохранить пунктуацию, чтобы ваша модель могла генерировать грамматически правильный текст.
Здесь мы определим функцию, которая удаляет следующее:
- Смайлики 😔
- Пользователь упоминает (@gabeflomo)
- Хэштеги (#Hex)
- URL-адреса
- Пунктуация
# borrowed from stackoverflow https://stackoverflow.com/a/49146722 def remove_emoji(string): emoji_pattern = re.compile("[" u"\U0001F600-\U0001F64F" # emoticons u"\U0001F300-\U0001F5FF" # symbols & pictographs u"\U0001F680-\U0001F6FF" # transport & map symbols u"\U0001F1E0-\U0001F1FF" # flags (iOS) u"\U00002702-\U000027B0" u"\U000024C2-\U0001F251" "]+", flags=re.UNICODE) return emoji_pattern.sub(r' ', string) def remove_unwanted(document): # remove user mentions document = re.sub("@[A-Za-z0-9_]+"," ", document) # remove URLS document = re.sub(r'http\S+', ' ', document) # remove hashtags document = re.sub("#[A-Za-z0-9_]+","", document) # remove emoji's document = remove_emoji(document) # remove punctuation document = re.sub("[^0-9A-Za-z ]", "" , document) # remove double spaces document = document.replace(' ',"") return document.strip() sample = "Hello @gabe_flomo 👋🏾, still want us to hit that new sushi spot??? LMK when you're free cuz I can't go this or next weekend since I'll be swimming!!! #sushiBros #rawFish #🍱" remove_unwanted(sample) # output 'Hello still want us to hit that new sushi spot LMK when youre free cuz I cant go this or next weekend since Ill be swimming'
Удаление стоп-слов ❌
В контексте НЛП стоп-слово — это любое слово, которое не добавляет смысла предложению, такие как и, это, когда и т. д. Удаление этих слов уменьшает размер нашего словаря и нашего набора данных, сохраняя при этом всю соответствующую информацию в этом документе. Как упоминалось ранее, не во всех задачах языкового моделирования полезно удалять стоп-слова, например, при переводе или генерации текста. Это связано с тем, что эти задачи по-прежнему учитывают грамматическую структуру каждого документа, и удаление определенных слов может привести к потере этой структуры. В зависимости от языковой задачи важно помнить, какие стоп-слова удаляются из ваших документов. Мы не будем вдаваться в подробности, но вы можете ознакомиться с этой статьей, в которой еще глубже рассказывается, как с этим справиться.
NLTK — это набор инструментов для работы с НЛП на питоне, который предоставляет нам различные библиотеки обработки текста для типичных задач НЛП. Мы будем использовать стоп-слова из NLTK для фильтрации наших текстовых документов.
def remove_words(tokens): stopwords = nltk.corpus.stopwords.words('english') # also supports german, spanish, portuguese, and others! stopwords = [remove_unwanted(word) for word in stopwords] # remove puntcuation from stopwords cleaned_tokens = [token for token in tokens if token not in stopwords] return cleaned_tokens
Лемматизация ✂️
Лемматизация — распространенная техника НЛП, представляющая собой процесс приведения слова к его корневой форме. Это полезный метод нормализации, поскольку он удаляет все времена из документа и упрощает текст каждого документа.
lemma = WordNetLemmatizer() def lemmatize(tokens): lemmatized_tokens = [lemma.lemmatize(token, pos = 'v') for token in tokens] return lemmatized_tokens lemmatize("Should we go walking or swimming".split()) # output ['Should', 'we', 'go', 'walk', 'or', 'swim']
Стемминг ✂️
Стемминг очень похож на лемматизацию в том смысле, что он направлен на сокращение слов до их корня; разница в том, что алгоритмы определения основы просто отсекают часть слова в конце, чтобы получить основу слова. Это означает, что он полезен для приведения слов к упрощенной форме, но может быть не таким точным, как алгоритм лемминга.
stem = PorterStemmer() def stemmer(tokens): stemmed_tokens = [stem.stem(token) for token in tokens] return stemmed_tokens stemmer("I love cats more than dogs".split()) # output ['i', 'love', 'cat', 'more', 'than', 'dog']
Собираем все вместе 🤝
В этом последнем разделе мы объединим все ранее использовавшиеся методы в одну функцию для простоты использования. Здесь мы будем использовать функцию pipeline
ниже, чтобы очистить все твиты, содержащиеся в этом наборе данных Kaggle.
df = shuffle(pd.read_csv("tweets.csv"), random_state=1111) documents = df.iloc[:20]
def pipeline(document, rule = 'lemmatize'): # first lets normalize the document document = normalize(document) # now lets remove unwanted characters document = remove_unwanted(document) # create tokens tokens = document.split() # remove unwanted words tokens = remove_words(tokens) # lemmatize or stem or if rule == 'lemmatize': tokens = lemmatize(tokens) elif rule == 'stem': tokens = stemmer(tokens) else: print(f"{rule} Is an invalid rule. Choices are 'lemmatize' and 'stem'") return " ".join(tokens) sample = "Hello @gabe_flomo 👋🏾, I still want us to hit that new sushi spot??? LMK when you're free cuz I can't go this or next weekend since I'll be swimming!!! #sushiBros #rawFish #🍱" print(pipeline(sample)) # output "hello still want us hit new sushi spot lmk free cuz cant go next weekend since ill swim"
Давайте сравним неочищенные документы с очищенными!
cleaned = documents['text'].apply(lambda doc: pipeline(doc)) for clean, dirty in zip(cleaned[15:20], documents['text'][15:20]): print(dirty) print(clean, "\n")
Вывод 🔮
Поздравляем 🎉 вы прошли весь урок!
Основываясь на сравнении первых нескольких образцов, мы видим, что наш конвейер очистки текста довольно хорошо справляется с очисткой наших текстовых документов. Он может удалить URL-адреса, хэштеги, смайлики, упоминания и все другие символы/слова, которые мы хотели удалить! Есть несколько сбоев, где он мог бы работать немного лучше, например, объединение слов, в которых нет пробела между знаками препинания, но мы оставим улучшение функции на ваше усмотрение 😊 .