Обработка естественного языка (NLP) позволяет машинам взаимодействовать с людьми естественным образом. Анализ тональности — это часть НЛП, где машины могут анализировать тональность заданного текста. В основном настроения делятся на положительные, отрицательные и нейтральные. Для простоты в этом случае мы будем классифицировать их как положительные и отрицательные настроения.
Сценарий
Здесь нам предоставлены наборы обучающих данных для некоторых обзоров фильмов (с метками). Цель этого руководства — предсказать настроение для заданного набора тестовых данных. В нем нет меток (настроений), поэтому его можно считать набором данных из реального мира.
Как мы знаем, аналитика данных — это итеративный процесс, и будет много итераций, прежде чем мы получим ожидаемую точность (результат). Здесь мы будем работать над процедурой из 5 шагов, которая включена в каждую итерацию. Мы можем изменять параметры на каждой итерации, чтобы улучшить процесс анализа и, следовательно, результаты (точность). Пять шагов: Очистка данных, Исследовательский анализ данных, Разработка признаков, Моделирование и Прогнозирование.
Очистка данных (предварительная обработка текста)
''' Load Data from source ''' df = pd.read_csv('your_data_path') train_data=df[['document','label']]
Очистка данных — это один из первых шагов в рабочем процессе машинного обучения, который обычно следует за сбором данных. Здесь мы очищаем данные как часть предварительной обработки текста таким образом, чтобы их можно было подготовить для подачи в модель для выполнения дальнейших процессов.
# Cleaning Training Data set lem = WordNetLemmatizer() import re train_data_clean = ( train_data # convert text to lowercase for consistency through out the corpus .assign(clean_document= lambda x:[text.lower() for text in x.document]) # Remove the "<br/>" .assign(clean_document= lambda x: [re.sub(r"^<br />$","",doc) for doc in x.clean_document]) # Remove the "digits " .assign(clean_document= lambda x: [re.sub(r'[~^0-9]',"",doc) for doc in x.clean_document]) # Remove the "br" .assign(clean_document= lambda x: [re.sub(r"br","",doc) for doc in x.clean_document]) #Remove “\n” which represents new line .assign(clean_document= lambda x: [re.sub(r"\n","",doc) for doc in x.clean_document] ) #Remove mentions i.e. any alphanumeric starting with “@” .assign(clean_document= lambda x: [re.sub("\@","",text) for text in x.clean_document]) # Remove punctuation .assign(clean_document= lambda x: [re.sub("[^\w\s]","",text) for text in x.clean_document]) # Tokenize .assign(word_token= lambda x: [nltk.word_tokenize(text) for text in x.clean_document] ) # Remove stop words .assign(word_token= lambda x: [list(set(list_of_words).difference(stop_words)) for list_of_words in x.word_token]) # Lemmatization .assign(word_token= lambda x: [[lem.lemmatize(a_word) for a_word in list_of_words] for list_of_words in x.word_token ]) # join the tokenized word .assign(document_to_sklearn= lambda x: [" ".join(map(str,list_of_words)) for list_of_words in x.word_token ]) )
Изучив данные, мы видим, что есть теги «‹br ›», цифры, «\n», специальные символы и стоп-слова, которые никак не влияют на настроение текста, поэтому мы удаляем эти символы и теги.
Лемматизация приводит слова к их базовой форме, которая является лингвистически правильной леммой. Например, слово «лучше» будет сокращено до «хорошо». В отличие от стемминга, лемматизация учитывает контекст при сокращении, поэтому лемматизация является более сложной, чем стемминг.
Токенизация слов — это процесс разделения текстовых предложений на слова.
Исследовательский анализ данных[EDA]
Для сбора информации, которая может быть полезна для построения моделей и анализа распределения данных, проводится EDA.
''' As a part of exploratory data analysis we are using word cloud to see the most frequent word in the corpus ''' from wordcloud import WordCloud, STOPWORDS def show_wordcloud(data, title = None): wordcloud = WordCloud( background_color='white', max_words=7000, max_font_size=40, scale=3, random_state=1 ).generate(str(data)) fig = plt.figure(1, figsize=(20, 20)) plt.axis('off') if title: fig.suptitle(title, fontsize=20) fig.subplots_adjust(top=2.3) plt.imshow(wordcloud) plt.show() show_wordcloud(train_data_clean['document_to_sklearn'])
Давайте посмотрим на распределение положительных и отрицательных настроений в данном наборе данных.
train_data_clean["label"].value_counts()
''' Visualize the distribution of the sentiments ''' import matplotlib.pyplot as plt import seaborn as sns colors = ["#00FF00", "#DF0101"] sns.countplot('label', data=train_data_clean, palette=colors) plt.title('Sentiment Distributions)', fontsize=14)
Разработка функций
Анализ настроений — это одна из задач классификации текста, и в этой задаче у нас есть текст и соответствующие метки [в данном случае — настроения]. Но мы не можем напрямую использовать этот текст для построения нашей модели, мы должны преобразовать этот текст в некоторое числовое представление. Сумка слов [BOW] и TF-IDF являются одними из распространенных методов.
''' Since the label are given as positive and negative lets change them to 0 and 1 so that it would be easier for machine to learn. 1 if the document has positive sentiment 0 if the document has negative sentiment ''' train_data_clean['label'] = train_data_clean['label'].apply(lambda x: 1 if x == "positive" else "0")
Извлечение признаков можно понимать как механизм преобразования произвольных данных, таких как текст и изображения, в числовые признаки, которые можно использовать для машинного обучения. Теперь нам нужно извлечь функции из набора данных, который мы только что очистили и исследовали. Для извлечения признаков из текстовых данных мы можем использовать различные методы. Здесь мы используем векторизатор TF-IDF.
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer vectorizer = TfidfVectorizer(max_features = 1000) X = vectorizer.fit_transform([i for i in train_data_clean['document_to_sklearn']]) # Use the same vectorizer to extract the features from test data(later) as well df_bow_sklearn = pd.DataFrame(X.toarray(),columns=vectorizer.get_feature_names()) df_bow_sklearn['label'] = train_data_clean['label'] y = train_data_clean['label'] df_bow_sklearn # Here we have extracted 1000 features from the data. The vectorizer would assign the TF-IDF score to the each word and pull out the 1000 features from them.
Моделирование
С Пикарет
Один из самых простых способов моделирования данных — с помощью pycaret. Он прост в использовании и обеспечивает производительность всех алгоритмов с помощью одной строки кода.
s = setup(data = df_bow_sklearn, target='label', numeric_features=vectorizer.get_feature_names(), session_id=123,verbose=False,silent=True) # sort the classifaction algorithms by accuracy. com = compare_models(sort='Accuracy')
Визуализация также проста с pycaret. С помощью evaulate_model мы можем увидеть лучшую производительность модели и другие важные визуализации.
# Lets evaluate the best model according to accuracy evaluate_model(com)
Со Склеарном
Мы уже очистили данные, давайте разделим эти данные на наборы для обучения и проверки и посмотрим, как модель работает в наборе для проверки.
from sklearn.model_selection import train_test_split x_train,x_test,y_train,y_test = train_test_split(train_data_clean['document_to_sklearn'],train_data_clean['label'],random_state=12,test_size=0.2) y_train = y_train.astype(int) y_train
Здесь мы будем использовать векторизатор TF-IDF для извлечения функций и попробуем с 1000 функциями. Мы используем 80% данных для обучения, а остальные — для проверки.
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer vectorizer = TfidfVectorizer(max_features=1000) X_train = vectorizer.fit_transform(x_train) X_train
Давайте предскажем отношение к самим обучающим данным и посмотрим на производительность модели.
from sklearn import svm from sklearn.metrics import accuracy_score, classification_report y_train =y_train.astype('int') svm = svm.SVC(kernel = 'linear', C=1) svm.fit(X_train, y_train) y_pred = svm.predict(X_train) classification_report_= classification_report(y_train, y_pred) print("classification_report_\n",classification_report_) print('\n Accuracy: ', accuracy_score(y_train, y_pred))
Трубопроводы
Чтобы сделать рабочий процесс более читабельным и понятным, давайте воспользуемся конвейером. Кроме того, конвейер помогает сделать работу воспроизводимой.
from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfTransformer from sklearn.naive_bayes import MultinomialNB from sklearn import svm # Make the pipeline. review_clf = Pipeline([ ('vectorizer', CountVectorizer()), ('tfidf', TfidfTransformer()), ('classifier', svm.SVC()), ])
Мы использовали тот же алгоритм классификации в конвейере и будем прогнозировать данные обучения, чтобы увидеть, изменится ли что-нибудь с использованием конвейеров.
# Train using the pipeline review_clf.fit(x_train,y_train) # predict Using the pipeline y_pred = review_clf.predict(x_train) # Lets see how it does on training data which it is trained upon classification_report_= classification_report(y_train, y_pred) print("classification_report_\n",classification_report_) print('\n Accuracy: ', accuracy_score(y_train, y_pred))
Кажется, мы хорошо построили наш трубопровод. Давайте попробуем это на нашем наборе проверочных данных.
y_val_test =review_clf.predict(x_test) classification_report_= classification_report(y_test, y_val_test) print("classification_report_\n",classification_report_) print('\n Accuracy: ', accuracy_score(y_test, y_val_test))
Теперь у нас есть наша модель, давайте используем эту модель для прогнозирования настроений по невидимым данным (набор данных из реального мира).
df_real=pd.read_csv('your_data_path')
Мы будем использовать тот же код для очистки данных и удаления всех ненужных тегов и символов.
# Cleaning Test Data set lem = WordNetLemmatizer() import re real_data_clean = ( df_real # convert text to lowercase for consistency through out the corpus .assign(clean_document= lambda x:[text.lower() for text in x.document]) # Remove the "<br/>" .assign(clean_document= lambda x: [re.sub(r"^<br />$","",doc) for doc in x.clean_document]) # Remove the "digits " .assign(clean_document= lambda x: [re.sub(r'[~^0-9]',"",doc) for doc in x.clean_document]) # Remove the "br" .assign(clean_document= lambda x: [re.sub(r"br","",doc) for doc in x.clean_document]) #Remove "\n" which represents new line .assign(clean_document= lambda x: [re.sub(r"\n","",doc) for doc in x.clean_document] ) #Remove mentions i.e. any alphanumeric starting with "@" .assign(clean_document= lambda x: [re.sub("\@","",text) for text in x.clean_document]) # Remove punctuation .assign(clean_document= lambda x: [re.sub("[^\w\s]","",text) for text in x.clean_document]) # Tokenize .assign(word_token= lambda x: [nltk.word_tokenize(text) for text in x.clean_document] ) # Remove stop words .assign(word_token= lambda x: [list(set(list_of_words).difference(stop_words)) for list_of_words in x.word_token]) # Lemmatization .assign(word_token= lambda x: [[lem.lemmatize(a_word) for a_word in list_of_words] for list_of_words in x.word_token ]) # join the tokenized word .assign(document_to_sklearn= lambda x: [" ".join(map(str,list_of_words)) for list_of_words in x.word_token ]) ) real_data_clean.head()
Поскольку тестовые данные очищены и готовы для подачи в модель. Давайте предскажем настроения, используя нашу модель. Мы уже указали алгоритм извлечения и классификации признаков в нашем пайплайне. Итак, чтобы предсказать настроения, мы будем передавать очищенные данные в конвейер.
Предсказания
x_real = real_data_clean['document_to_sklearn'] x_real.head() #use same model to predict sentiments = review_clf.predict(x_real) sentiments
Преобразуем данные обратно, как они были раньше.
# Convert to series predictions = pd.Series(sentiments) # Name the column as label predictions.name = 'label' #Merge the columns df_final = pd.merge(x_real, predictions, right_index = True, left_index = True) # Remeber we converted it as #1 if the document has positive sentiment #0 if the document has negative sentiment df_final['label'] = df_final['label'].apply(lambda x: "positive" if x == 1 else "negative")df_final
Мы можем использовать to_csv для преобразования нашего фрейма данных в CSV. Вот и все.
Ссылки:
https://towardsdatascience.com/workflow-of-a-machine-learning-project-ec1dba419b94
https://scikit-learn.org/stable/tutorial/text_analytics/working_with_text_data.html
https://www.datacamp.com/tutorial/text-analytics-beginners-nltk
https://medium.com/edureka/scikit-learn-machine-learning-7a2d92e4dd07
Ссылка на GitHub
Ссылки на данные
данные поезда: https://drive.google.com/file/d/1XVw7bpwKY53u_eACblrvaz5xXlW2VeDc/view?usp=sharing
тестовые данные: https://drive.google.com/file/d/1-GjgFI6-S9D2GTtUEnJWULX1KeTZWXqR/view?usp=sharing