Обработка естественного языка (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://github.com/aadarshachapagain/sentinment-analysis/blob/main/Sentimental_Analysis_Fundamentals.ipynb

Ссылки на данные

данные поезда: https://drive.google.com/file/d/1XVw7bpwKY53u_eACblrvaz5xXlW2VeDc/view?usp=sharing

тестовые данные: https://drive.google.com/file/d/1-GjgFI6-S9D2GTtUEnJWULX1KeTZWXqR/view?usp=sharing