Обработка естественного языка (NLP) - действительно очень интересная и обширная область искусственного интеллекта. Здесь я собираюсь использовать его для обработки текстовых записей и дам вам ускоренный курс по парсингу и анализу настроений. Для парсинга я использовал Selenium и tweepy, а для анализа настроений я использовал классы и методы NLTK и наивную байесовскую модель. Я изо всех сил старался охватить большинство шагов, которые следует выполнять при работе с набором текстовых данных, и позвольте мне убедиться, что это будет стоить вашего времени.
Так что же такое парсинг и анализ настроений?
Очистка - процесс получения небольших фрагментов чего-либо. В нашем случае это веб-скрапинг, поэтому здесь мы берем фрагменты информации, доступной на веб-сайте.
Анализ настроений. Из самого термина можно сделать вывод, что это процесс анализа взглядов или мнений людей по любому вопросу. Теперь темой может быть что угодно: продукт, фильм, политическая или социальная проблема, технология, любое событие или какой-либо тренд.
Люди обычно предпочитают социальные сети для выражения своих взглядов или мнений, это может быть Facebook, Twitter, Quora или любой другой блог-сайт. В этом уроке я собираюсь рассматривать Твиттер как источник информации, и тему, которую я хотел бы выбрать, - «ИИ и глубокое обучение», хотя код, которым я поделюсь, будет полностью общим, так что вы можете выбрать любой и другая интересная тема.
Из заголовка и приведенного выше описания вы, должно быть, определили, что текстовые данные, необходимые для выполнения анализа настроений, необходимо удалить из Twitter. Итак, ниже приведены основные операции, которые я собираюсь выполнить:
1. Очистка твитов
2. Определение настроений
3. Предварительная обработка текста
4. Извлечение признаков
5. Построение модели
- Очистка твитов
Если вы раньше выполняли парсинг на Python, значит, вы, должно быть, использовали «Запросы» и «Красивый суп»; для тех, кто не слышал об этом раньше, Request - это HTTP-библиотека Python для отправки HTTP-запросов, а Beautiful Soup - HTML-парсер для синтаксического анализа DOM и получения желаемой информации. из этого. Но мы не можем использовать эти библиотеки для удаления твитов из твиттера из-за динамической и прогрессивной генерации твитов. Осталось два варианта:
а). Селен
б). тонкая библиотека Python
Я покажу вам реализацию обоих. Между прочим, Selenium - это инструмент имитации браузера, обычно используемый для тестирования веб-страниц, и tweepy, как я уже упоминал, библиотека python, которая предоставляет доступ к различным API твиттера.
а). Утилизация с использованием селена:
Предположим, вы уже импортировали numpy и pandas. Ниже представлен класс SeleniumClient, который будет выполнять парсинг:
from selenium import webdriver from selenium.webdriver.common.keys import Keys class SeleniumClient(object): def __init__(self): #Initialization method. self.chrome_options = webdriver.ChromeOptions() self.chrome_options.add_argument('--headless') self.chrome_options.add_argument('--no-sandbox') self.chrome_options.add_argument('--disable-setuid-sandbox') # you need to provide the path of chromdriver in your system self.browser = webdriver.Chrome('D:/chromedriver_win32/chromedriver', options=self.chrome_options) self.base_url = 'https://twitter.com/search?q=' def get_tweets(self, query): ''' Function to fetch tweets. ''' try: self.browser.get(self.base_url+query) time.sleep(2) body = self.browser.find_element_by_tag_name('body') for _ in range(3000): body.send_keys(Keys.PAGE_DOWN) time.sleep(0.3) timeline = self.browser.find_element_by_id('timeline') tweet_nodes = timeline.find_elements_by_css_selector('.tweet-text') return pd.DataFrame({'tweets': [tweet_node.text for tweet_node in tweet_nodes]}) except: print("Selenium - An error occured while fetching tweets.")
В приведенном выше коде вам нужно указать путь к веб-драйверу желаемого браузера, или мы можем просто установить переменную среды и не передавать какие-либо параметры внутри webdriver.Chrome ().
Вы можете использовать этот класс:
selenium_client = SeleniumClient() tweets_df = selenium_client.get_tweets('AI and Deep learning')
В tweets_df вы получите фрейм данных, содержащий все отмененные твиты.
б). Получайте твиты с помощью tweepy:
Мы можем создать класс TwitterClient:
import tweepy from tweepy import OAuthHandler class TwitterClient(object): def __init__(self): # Access Credentials consumer_key = 'XXXX' consumer_secret = 'XXXX' access_token = 'XXXX' access_token_secret = 'XXXX' try: # OAuthHandler object auth = OAuthHandler(consumer_key, consumer_secret) # set access token and secret auth.set_access_token(access_token, access_token_secret) # create tweepy API object to fetch tweets self.api = tweepy.API(auth, wait_on_rate_limit=True, wait_on_rate_limit_notify=True) except tweepy.TweepError as e: print(f"Error: Twitter Authentication Failed - \n{str(e)}") # Function to fetch tweets def get_tweets(self, query, maxTweets = 1000): # empty list to store parsed tweets tweets = [] sinceId = None max_id = -1 tweetCount = 0 tweetsPerQry = 100 while tweetCount < maxTweets: try: if (max_id <= 0): if (not sinceId): new_tweets = self.api.search(q=query, count=tweetsPerQry) else: new_tweets = self.api.search(q=query, count=tweetsPerQry, since_id=sinceId) else: if (not sinceId): new_tweets = self.api.search(q=query, count=tweetsPerQry, max_id=str(max_id - 1)) else: new_tweets = self.api.search(q=query, count=tweetsPerQry, max_id=str(max_id - 1), since_id=sinceId) if not new_tweets: print("No more tweets found") break for tweet in new_tweets: parsed_tweet = {} parsed_tweet['tweets'] = tweet.text # appending parsed tweet to tweets list if tweet.retweet_count > 0: # if tweet has retweets, ensure that it is appended only once if parsed_tweet not in tweets: tweets.append(parsed_tweet) else: tweets.append(parsed_tweet) tweetCount += len(new_tweets) print("Downloaded {0} tweets".format(tweetCount)) max_id = new_tweets[-1].id except tweepy.TweepError as e: print("Tweepy error : " + str(e)) break return pd.DataFrame(tweets)
В приведенном выше коде нам нужны «Учетные данные для доступа» для выполнения вызовов API, их можно получить из консоли разработчика Twitter, вам просто нужно зарегистрировать свое приложение и указать все веские причины для получения доступа. Этот вызов можно использовать так же, как мы использовали SeleniumClient. В ответ мы получим фрейм данных, содержащий все выбранные твиты.
Какой из них использовать?
Да, это очевидный вопрос. Ответ - крутой, потому что он быстрее и надежнее. Однако, если у вас нет учетных данных для доступа к Twiter API и вы не хотите ждать одобрения Twitter, вы можете использовать SeleniumClient. Всегда полезно знать различные подходы к выполнению любой задачи.
2. Определение типа настроения
Итак, тип настроения - это не что иное, как общая реакция, она может быть положительной, отрицательной или нейтральной. В нашем случае мы будем рассматривать только положительные (включая нейтральные) и отрицательные.
В. Почему мы должны определять тип настроения?
Потому что в конечном итоге мы будем обучать модель, которая должна уметь классифицировать негативные и позитивные настроения в твитах. Для этой классификации мы будем использовать некоторую модель обучения с учителем, поэтому нам нужна целевая переменная. Тип настроения будет нашей целевой переменной.
Я выделил два способа идентифицировать:
а. Использование NLTK's SentimentIntensityAnalyzer (мы будем называть SIA)
b. Использование TextBlob
import nltk from nltk.sentiment.vader import SentimentIntensityAnalyzer from textblob import TextBlob def fetch_sentiment_using_SIA(text): sid = SentimentIntensityAnalyzer() polarity_scores = sid.polarity_scores(text) if polarity_scores['neg'] > polarity_scores['pos']: return 'negative' else: return 'positive' def fetch_sentiment_using_textblob(text): analysis = TextBlob(text) # set sentiment if analysis.sentiment.polarity >= 0: return 'positive' else: return 'negative'
Мы можем выбрать любой из них, я лично предпочитаю TextBlob, это дает лучшую категоризацию.
3. Предварительная обработка текста
Текст, полученный из твитов, недостаточно чистый, чтобы его можно было использовать для обучения модели, поэтому его необходимо предварительно обработать. Возможно, мы не сможем полностью очистить его, но мы должны постараться сделать все возможное, чтобы предварительно обработать как можно больше.
а. Удаление ‘@names’:
Все «@anyname» бесполезны, поскольку не передают никакого значения.
def remove_pattern(text, pattern_regex): r = re.findall(pattern_regex, text) for i in r: text = re.sub(i, '', text) return text # We are keeping cleaned tweets in a new column called 'tidy_tweets' tweets_df['tidy_tweets'] = np.vectorize(remove_pattern)(tweets_df['tweets'], "@[\w]*: | *RT*")
б. Удаление ссылок (http | https)
Ссылки в тексте бесполезны, потому что они также не несут никакой полезной информации.
cleaned_tweets = [] for index, row in tweets_df.iterrows(): # Here we are filtering out all the words that contains link words_without_links = [word for word in row.tidy_tweets.split() if 'http' not in word] cleaned_tweets.append(' '.join(words_without_links)) tweets_df['tidy_tweets'] = cleaned_tweets
c. Удаление повторяющихся строк
У нас могут быть повторяющиеся твиты в нашем фрейме данных, об этом нужно позаботиться:
tweets_df.drop_duplicates(subset=['tidy_tweets'], keep=False)
d. Удаление знаков препинания, цифр и специальных символов
tweets_df['absolute_tidy_tweets'] = tweets_df['tidy_tweets'].str.replace("[^a-zA-Z# ]", "")
Этот шаг не следует выполнять, если мы также хотим провести анализ тональности __key phrases__, потому что в предложении должно присутствовать семантическое значение. Итак, здесь мы создадим один дополнительный столбец «absolute_tidy_tweets», который будет содержать абсолютные аккуратные слова, которые в дальнейшем можно будет использовать для анализа тональности __key words__.
е. Удаление стоп-слов
Стоп-слова - это слова, которые используются только для правильного построения предложений. Они не имеют никакого значения для полной информации. Поэтому его нужно удалить, чтобы сделать нашу текстовую запись чище.
from nltk.corpus import stopwords nltk.download('stopwords') stopwords_set = set(stopwords.words("english")) cleaned_tweets = [] for index, row in tweets_df.iterrows(): # filerting out all the stopwords words_without_stopwords = [word for word in row.absolute_tidy_tweets.split() if not word in stopwords_set] # finally creating tweets list of tuples containing stopwords(list) and sentimentType cleaned_tweets.append(' '.join(words_without_stopwords)) tweets_df['absolute_tidy_tweets'] = cleaned_tweets
е. Токенизация и лемматизация:
from nltk.stem import WordNetLemmatizer # Tokenization tokenized_tweet = tweets_df['absolute_tidy_tweets'].apply(lambda x: x.split()) # Finding Lemma for each word word_lemmatizer = WordNetLemmatizer() tokenized_tweet = tokenized_tweet.apply(lambda x: [word_lemmatizer.lemmatize(i) for i in x]) #joining words into sentences (from where they came from) for i, tokens in enumerate(tokenized_tweet): tokenized_tweet[i] = ' '.join(tokens) tweets_df['absolute_tidy_tweets'] = tokenized_tweet
4. Извлечение функций
Нам нужно преобразовать текстовое представление в виде числовых функций. У нас есть два популярных метода извлечения признаков:
- Пакет слов (простая векторизация)
- TF-IDF (частота термина - обратная частота документа)
Мы будем использовать извлеченные функции из обоих по очереди для анализа настроений и, наконец, сравним результат.
Ознакомьтесь с приведенным ниже ядром, чтобы правильно понять интуицию методов извлечения функций с примерами:
https://www.kaggle.com/amar09/text-pre-processing-and-feature-extraction
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer # BOW features bow_word_vectorizer = CountVectorizer(max_df=0.90, min_df=2, stop_words='english') # bag-of-words feature matrix bow_word_feature = bow_word_vectorizer.fit_transform(tweets_df['absolute_tidy_tweets']) # TF-IDF features tfidf_word_vectorizer = TfidfVectorizer(max_df=0.90, min_df=2, stop_words='english') # TF-IDF feature matrix tfidf_word_feature = tfidf_word_vectorizer.fit_transform(tweets_df['absolute_tidy_tweets'])
5. Построение модели
Давайте сначала сопоставим целевую переменную с {0,1}.
target_variable = tweets_df['sentiment'].apply(lambda x: 0 if x=='negative' else 1 )
Мы собираемся использовать наивную байесовскую модель для классификации настроений, потому что я также пробовал SVM, логистическую регрессию и дерево решений, но получил наилучшие результаты, используя только наивный байесовский метод.
from sklearn.naive_bayes import GaussianNB from sklearn.model_selection import train_test_split from sklearn.metrics import f1_score def naive_model(X_train, X_test, y_train, y_test): naive_classifier = GaussianNB() naive_classifier.fit(X_train.toarray(), y_train) # predictions over test set predictions = naive_classifier.predict(X_test.toarray()) # calculating f1 score print(f'F1 Score - {f1_score(y_test, predictions)}')
Обучение функциям, извлеченным с помощью пакета слов:
X_train, X_test, y_train, y_test = train_test_split(bow_word_feature, target_variable, test_size=0.3, random_state=870) naive_model(X_train, X_test, y_train, y_test)
Он дает показатель F1 - 0,9387254901960784
Теперь давайте обучимся функциям, извлеченным из TF-IDF:
X_train, X_test, y_train, y_test = train_test_split(tfidf_word_feature, target_variable, test_size=0.3, random_state=870) naive_model(X_train, X_test, y_train, y_test)
Я получил результат F1 - 0,9400244798041616
Функции TF-IDF явно дают лучший результат.
Вывод. Здесь для анализа настроений мы использовали только «ключевые слова», мы также можем использовать «ключевые фразы». Есть много других шагов, которые мы должны выполнить, чтобы узнать о них подробно, ознакомьтесь с моим полным ядром.
В этом посте я предположил, что у вас уже есть базовые знания об обработке текста с помощью NLTK. Если у вас его нет, я предлагаю вам просмотреть мое ядро ниже, которое содержит объяснение всех основных текстовых операций и подробное объяснение извлечения функций с использованием BOW и TF-IDF.
Прокомментируйте, если вам нужны дополнительные объяснения по чему-либо. Все предложения и отзывы всегда приветствуются.
Спасибо за чтение, удачного обучения;)