Обзор
На прошлой неделе вышло два больших фильма, которые очень ждали за последний год — Varisu и Thunivu. Мы пытаемся измерить успех фильмов, но это не ежу понятно — оба фильма собрали 150 Cr + в течение недели. Вокруг фильмов так много шумихи, но есть ли показатель качества фильмов. ? Мы здесь, чтобы узнать. В этом проекте мы пытаемся сравнить отношение рецензентов к этим фильмам. Для выполнения этой задачи мы будем использовать методы обработки естественного языка и модели машинного обучения.
Шаги в целом делятся на:
Получение данных отзывов с веб-сайта IMDB Очистка данных Обучение модели подготовки с использованием уже существующего набора данных IMDB из 50 000 отзывов Прогнозирование модели
Веб-скрейпинг для получения данных отзывов с веб-сайта IMDB
Selenium — это библиотека Python, которая используется для парсинга веб-страниц. Широко используются две библиотеки Python парсинга веб-страниц: BeautifulSoup и Selenium. Здесь мы используем Selenium, потому что IMDB — это веб-сайт с бесконечным циклом. Если веб-сайт статичен, то есть нет никаких кнопок «загрузить еще» или других подобных кнопок, которые нужно нажимать для получения дополнительных данных, BeautifulSoup проще в использовании. Здесь мы хотим собирать данные о постоянном нажатии на кнопку «Загрузить еще». Лучше всего с этим справляется библиотека selenium.
# install selenium through pip
from selenium import webdriver
import time
driver = webdriver.Chrome("C:/Users/windows/OneDrive/Desktop/Pathsetter/Movie Sentiment Analysis/chromedriver.exe")
# I have installed chromedriver in the path above.I am accessing it using webdriver.chrome() function.
driver.get('https://www.imdb.com/title/tt15163652/reviews/?ref_=tt_ql_urv')
# The url from which we want our data. Here, this url is the url of imdb's Thunivu movie review page.
driver.implicitly_wait(10)
# We are waiting 10 seconds for the website to load fully.
reviews = driver.find_elements_by_class_name('review-container')
# Inspect element in the imdb website, it can be seen that the reviews are placed in a class named 'review-container'
Мы хотим, чтобы водитель прокручивал документ и нажимал кнопку «Далее» в течение определенного периода времени. Мы делаем это, используя этот фрагмент кода.
i=0 while i<20: # Here, 20 is the number of times the load more button is presses, this can be changed to more or less depending on how much data is required. driver.execute_script("window.scrollTo(0,document.body.scrollHeight);") # we ask the driver to scroll to the bottom of everytime i=i+1 try: buu=driver.find_elements_by_tag_name("button") # we ask driver to find the button x=[btn for btn in buu if btn.text=="Load More"] for btn in x: driver.execute_script("arguments[0].click();", btn) # click on button when and if found time.sleep(3) except: pass time.sleep(4)
titles=[] contents=[] ratings=[] review=[] # Initialize arrays
После инициализации просим веб-драйвер добавить в эти массивы тексты на основе тегов на сайте.
from selenium.webdriver.common.by import By
reviews = driver.find_elements_by_class_name('review-container')
for review in reviews:
title = review.find_element(By.CLASS_NAME, 'title').text
titles.append(title)
content = review.find_element(By.CLASS_NAME,'content').text
contents.append(content)
rating = review.find_element(By.TAG_NAME,'span').text[:-3]
ratings.append(rating)
DataFrames — очень полезная структура данных для работы, когда у нас есть данные в формате таблицы. Создание данных, которые мы получили, в DataFrame позволяет нам манипулировать нашими значениями во время очистки данных, если это необходимо.
len(titles)
96
import pandas as pd # Make the data into a dataframe so that it is easier to clean and preprocess data. Thunivu = pd.DataFrame(data=zip(titles,contents,ratings),columns=['Title','Content','Rating'])
Thunivu
Thunivu.describe()
Thunivu.to_excel("Thunivu.xlsx") # Moving the data to excel so that data can accessed from the excel sheet directly. Webscraping each time we reload the notebook is time consuming.
import pandas as pd Thunivu= pd.read_excel("Thunivu.xlsx")
Очистка данных
Можно заметить, что рецензенты наполнили свое сердце большим количеством смайликов. К сожалению для нас, смайлики не воспринимаются машинами. Итак, давайте создадим функцию, которая может удалять смайлики из текста.
import re import sys # Text needs to be cleaned once, emojis are not comprehended by python. 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)
Thunivu["Content"]=Thunivu["Content"].apply(str) for i in range(len(Thunivu["Content"])): y = remove_emoji(Thunivu["Content"][i]).replace("Was this review helpful? Sign in to vote.\nPermalink",'') # Cleaning out extra text that is unnecessary as much as possible y1 = y.translate({ord("\n"): None}) y1 = y1.replace('.',' ') y1 = re.sub(r"(@\[A-Za-z0-9]+)|([^0-9A-Za-z \t])|(\w+:\/\/\S+)|^rt|http.+?", "", y1) Thunivu["Content"][i] = y1
C:\Users\windows\AppData\Local\Temp\ipykernel_10872\2162188665.py:8: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy Thunivu["Content"][i] = y1
Thunivu["Content"] # checking if the data is clean enough now to work with
0 The movie has a racy first half provided by co... 1 Thunivu Well crafted heist thriller with a roo... 2 The screenplay was very fast which is satisfyi... 3 Watched FDFS at 230 am Had very high hopes ... 4 Back bone of the movie was well written story ... ... 91 nan 92 Well written script screenplay awesome Ajith ... 93 The movie begins as a heist film The screenpl... 94 Overall must watch movie Characterization so ... 95 Thunivu is a decent heist thriller movie The ... Name: Content, Length: 96, dtype: object
Далее мы хотим посмотреть, требуют ли Рейтинги какой-либо очистки.
Thunivu["Rating"].unique() # Data preprocessing
array(['10', '8', '9', '7', '1', '4', '5', '6', '3', 'akash', '2'], dtype=object)
Обратите внимание на мошеннический рейтинг «Акаш». Это нужно очистить для дальнейшего анализа.
lst = [] for i in range(len(Thunivu["Rating"])): if Thunivu["Rating"][i] in ['akash']: print(Thunivu["Title"][i]) lst.append(i) Thunivu["Rating"][i] = 0 lst
disater cringe movie C:\Users\windows\AppData\Local\Temp\ipykernel_10872\4080376033.py:7: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy Thunivu["Rating"][i] = 0 [61]
Thunivu["Rating"] = Thunivu["Rating"].astype(int) # coverting rest of the rating values to function so that we can perform a classification function on them. Thunivu["Rating"].describe()
count 96.000000 mean 8.229167 std 2.870830 min 0.000000 25% 7.000000 50% 10.000000 75% 10.000000 max 10.000000 Name: Rating, dtype: float64
# lst contains row numbers of these ratings and in these rows rating is changed to the median. for i in lst: Thunivu["Rating"][i] = Thunivu["Rating"].median()
C:\Users\windows\AppData\Local\Temp\ipykernel_10872\3219620520.py:3: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy Thunivu["Rating"][i] = Thunivu["Rating"].median()
Thunivu["Rating"] = Thunivu["Rating"].astype(int) Thunivu["Rating"].unique() # checking if rating data is uniform and cleaned.
array([10, 8, 9, 7, 1, 4, 5, 6, 3, 2])
import seaborn as sns sns.countplot(Thunivu["Rating"],palette = 'rainbow')
C:\Users\windows\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( <AxesSubplot:xlabel='Rating', ylabel='count'>
График представляет собой визуальное изображение прямого рейтинга, данного отзывами. Когда мы попытаемся проанализировать настроения, давайте посмотрим, соответствуют ли настроения отзывов этим рейтингам.
Мы повторяем тот же процесс для отзывов Varisu.
driver = webdriver.Chrome("C:/Users/windows/OneDrive/Desktop/Pathsetter/Movie Sentiment Analysis/chromedriver.exe") # I have installed chromedriver in the path above.I am accessing it using webdriver.chrome() function. driver.get('https://www.imdb.com/title/tt11998558/reviews/?ref_=tt_ql_urv') # The url from which we want our data. Here, this url is the url of imdb's varisu movie review page. driver.implicitly_wait(10) # We are waiting 10 seconds for the website to load fully. reviews = driver.find_elements_by_class_name('review-container') # Inspect element in the imdb website, it can be seen that the reviews are placed in a class named 'review-container'
i=0 while i<20: # Here, 20 is the number of times the load more button is presses, this can be changed to more or less depending on how much data is required. driver.execute_script("window.scrollTo(0,document.body.scrollHeight);") # we ask the driver to scroll to the bottom of everytime i=i+1 try: buu=driver.find_elements_by_tag_name("button") # we ask driver to find the button x=[btn for btn in buu if btn.text=="Load More"] for btn in x: driver.execute_script("arguments[0].click();", btn) # click on button when and if found time.sleep(3) except: pass time.sleep(4)
titles=[] contents=[] ratings=[] review=[] # Initialize arrays
reviews = driver.find_elements_by_class_name('review-container') for review in reviews: title = review.find_element(By.CLASS_NAME, 'title').text titles.append(title) content = review.find_element(By.CLASS_NAME,'content').text contents.append(content) rating = review.find_element(By.TAG_NAME,'span').text[:-3] ratings.append(rating)
len(titles)
118
Varisu = pd.DataFrame(data=zip(titles,contents,ratings),columns=['Title','Content','Rating'])
Varisu
import pandas as pd
Varisu.to_excel("Varisu.xlsx")
Varisu= pd.read_excel("Varisu.xlsx")
Varisu
Varisu["Content"]=Varisu["Content"].apply(str) for i in range(len(Varisu["Content"])): y = remove_emoji(Varisu["Content"][i]).replace("Was this review helpful? Sign in to vote.\nPermalink",'') y1 = y.translate({ord("\n"): None}) y1 = y1.replace('.',' ') y1 = re.sub(r"(@\[A-Za-z0-9]+)|([^0-9A-Za-z \t])|(\w+:\/\/\S+)|^rt|http.+?", "", y1) Varisu["Content"][i] = y1
C:\Users\windows\AppData\Local\Temp\ipykernel_10872\539550371.py:7: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy Varisu["Content"][i] = y1
Varisu["Rating"].unique()
array([10, 7, 4, 9, 5, 6, 8, 2, 1, 3], dtype=int64)
Varisu['Rating'] = Varisu['Rating'].astype(int) Varisu['Rating'].dtype
dtype('int32')
import seaborn as sns sns.countplot(Varisu["Rating"],palette = 'rainbow')
C:\Users\windows\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( <AxesSubplot:xlabel='Rating', ylabel='count'>
Нам нужно обучить модель с большим количеством положительных и отрицательных данных, чтобы модель знала, что ассоциировать с положительным настроением, а что — с отрицательным.
Следовательно, мы используем набор данных Kaggle IMDB, который является бесплатным и доступным для всех для обучения модели.
import pandas as pd
Data = pd.read_csv("IMDB Dataset.csv")
Импорт библиотек из библиотеки nltk, которая является библиотекой обработки естественного языка, необходим, поскольку мы имеем дело с обзорами.
Стоп-слово содержит все общие стоп-слова, movie_review — это модуль НЛП, специфичный для movie_review, токенизация используется для токенизации предложений. Нам нужно избавиться от стоп-слов, чтобы модель могла лучше понимать данные.
import nltk.classify.util
from nltk.corpus import movie_reviews
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.corpus import wordnet
from nltk.stem import PorterStemmer
Давайте посмотрим, сколько есть хороших и плохих отзывов. Обратите внимание, что количество хороших и плохих отзывов одинаково, что делает этот набор данных идеальным для обучения.
Data['sentiment'].value_counts()
positive 25000 negative 25000 Name: sentiment, dtype: int64
Подготовка данных для построения модели
from sklearn.preprocessing import LabelEncoder
# label encoder is used for converting the labels into a numeric form so as to convert them into the machine-readable form.
label = LabelEncoder()
Data["sentiment"] = label.fit_transform(Data['sentiment'])
Указание независимой переменной и зависимой переменной. Независимая переменная — это переменная, зависящая от другого атрибута. Здесь переменная настроения зависит от обзора, что делает настроение независимой переменной.
X = Data["review"] # independent variable Y = Data["sentiment"] # dependent variable
X
0 One of the other reviewers has mentioned that ... 1 A wonderful little production. <br /><br />The... 2 I thought this was a wonderful way to spend ti... 3 Basically there's a family where a little boy ... 4 Petter Mattei's "Love in the Time of Money" is... ... 49995 I thought this movie did a down right good job... 49996 Bad plot, bad dialogue, bad acting, idiotic di... 49997 I am a Catholic taught in parochial elementary... 49998 I'm going to have to disagree with the previou... 49999 No one expects the Star Trek movies to be high... Name: review, Length: 50000, dtype: object
Модель
Проблема с использованием слов для машинного обучения заключается в том, что
А) Машины не понимают слов
B) Есть много слов, таких как in, on, the и т. д., которые не добавляют ценности при попытке понять настроение обзора, на самом деле они могут даже разбавить чувства в обзоре.
Решением этой проблемы является векторизация текста. Для этого мы используем библиотеку tf-idf. Создается огромная матрица в виде таблицы со счетчиком для каждого слова и нескольких пар слов. Парам слов и слов присваивается вес в зависимости от их частоты в каждом обзоре и в общих обзорах.
Еще одна задача, которую выполняет библиотека tf-idf, — минимизация эффектов стоп-слов. Эти слова обычно отфильтровываются перед обработкой естественного языка. Это наиболее распространенные слова в любом языке (такие как артикли, предлоги, местоимения, союзы и т. д.), и они не добавляют много информации к тексту.
Логистическая регрессия — это метод машинного обучения, который мы здесь используем. Существует множество продвинутых моделей, таких как модель BERT, модели LSTM и т. д., которые также можно использовать. Цель проекта состояла в том, чтобы быть дружелюбным к новичкам, поэтому мы использовали модель, которую легко понять. Логистическая регрессия — это модель, которую можно использовать как классификационную функцию, и она выглядит следующим образом:
Выход классифицируется как 1 или 0 с использованием параметров, определенных в модели. Определяется пороговое значение, обычно 0,5, и все выходные данные выше 0,5 классифицируются как 1, а ниже классифицируются как 0. Здесь мы будем использовать слова в отзыв, чтобы классифицировать отзыв как положительный и отрицательный.
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression # the words and the frequancy of words are computed with the TfidfVectorizer library tvec = TfidfVectorizer() clf2 = LogisticRegression(solver = "lbfgs") # we are using a Logistic Regression Model that sklearn has. To understand how this model works and its paramters visit https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
from sklearn.pipeline import Pipeline model = Pipeline([('vectorizer',tvec),('classifier',clf2)]) # pipeline assemble several steps that can be cross-validated together while setting different parameters. # it sets the parameters for the TfidfVectorizer and the logistic Regression Model at the same time in one line of code model.fit(X, Y)
Pipeline(steps=[('vectorizer', TfidfVectorizer()), ('classifier', LogisticRegression())])
model.fit() создает модель с помощью обучающих данных. Теперь эту модель можно использовать для понимания настроения новых отзывов.
Предсказание модели
Теперь, когда модель обучена, пришло время применить эту модель к нашим обзорам Thunivu и Varisu.
Thunivu_reviews = Thunivu["Content"] model.predict(Thunivu_reviews)
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
sns.countplot(model.predict(Thunivu_reviews))
C:\Users\windows\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( <AxesSubplot:ylabel='count'>
Varisu_reviews = Varisu["Content"] model.predict(Varisu_reviews)
array([1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1])
sns.countplot(model.predict(Varisu_reviews))
C:\Users\windows\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( <AxesSubplot:ylabel='count'>
from matplotlib import pyplot as plt f, axes = plt.subplots(1, 2) sns.countplot(model.predict(Thunivu_reviews), x='Thunivu_reviews', color="red", ax=axes[0]) sns.countplot(model.predict(Varisu_reviews), x="Varisu_reviews", color="blue", ax=axes[1])
C:\Users\windows\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( C:\Users\windows\anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variable as a keyword arg: x. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( <AxesSubplot:ylabel='count'>
Графики выше показывают, что отношение к Thunivu немного лучше, чем к Varisu. Можно заметить, что количество положительных отзывов примерно одинаковое. Тем не менее, это контраст в количестве негативных отзывов. Отрицательных отзывов о Варису в два раза больше, чем о Туниву (вставить гифку вздоха?)
С точки зрения кассовых сборов, оба этих суперзвездных фильма обречены на успех. Однако в большинстве случаев качество фильма зависит от того, сколько денег он зарабатывает. Мы можем сказать, что «Варису» и «Туниву» стали хитом, но, учитывая, что они вышли в один день, их трудно не сравнивать. .
Мы выбрали IMDB, потому что у них есть уже существующий набор данных, размещенный на kaggle, который мы можем использовать для обучения модели. У них также было больше всего обзоров сразу после выхода фильма. Мы также считаем, что он менее предвзят, чем Твиттер, потому что Твиттер, как правило, является мерой того, насколько шумиха вокруг фильма, у обоих этих фильмов было СТОЛЬКО много шумихи. Хотя у IMDB есть предвзятость рецензентов , это, вероятно, меньше для платформы социальных сетей.
Мы использовали библиотеки векторизации текста и модель логистической регрессии для создания классификатора обзора. Есть много вариантов, которые можно сделать из этой модели, можно использовать разные векторизаторы, модели ML. Суть в том, чтобы сделать черный ящик, который может классифицировать отзыв как положительный и отрицательный. Этот черный ящик хорошо справился с классификацией обзоров Thunivu и Varisu, он может сделать это и для других обзоров.
Дайте нам знать ваши мысли в комментариях ниже!