Наивный байесовский анализ настроений и прогнозирование 250 лучших обзоров фильмов Дубана

Python

предисловие

В этой статье используется алгоритм наивного Байеса для проведения анализа настроений и прогнозирования рецензий на фильмы Douban Top 250.

В последнее время я узнаю об обработке положительных и отрицательных эмоций на естественном языке, но большинство практик, которые можно найти, — это анализ настроений в обзорах фильмов IMDB на Kggle.

Итак, здесь я использую самый простой алгоритм Наивного Байеса для анализа и прогнозирования обзоров фильмов Дубана.

Здесь я имею в видуGitHub.com/AE бомба и это зло/IM…,Большое спасибо.

Наивный байесовский классификатор

Байесовская классификация — это общий термин для класса алгоритмов классификации, основанных на теореме Байеса, поэтому они вместе называются байесовской классификацией.

Этот алгоритм часто используется для классификации статей, спама и спам-комментариев.Наивный Байес имеет хороший эффект и низкую стоимость.

Зная некоторую условную вероятность, как получить вероятность после обмена двумя событиями, то есть как получить P(B|A), когда известно P(A|B).

P(B|A) представляет собой вероятность того, что событие B произойдет при условии, что произошло событие A, которая называется условной вероятностью события B при наступлении события A.

Формула наивного Байеса

P(B|A) = \frac{P(A|B)P(B)}{P(A)}

Простой в использовании видеоурок

Youtube Woohoo.YouTube.com/watch?V=AQ О…

привести неуместный пример

Мы хотим узнать взаимосвязь между тем, чтобы быть программистом, и тем, чтобы быть лысым, и мы можем использовать формулу Наивного Байеса, чтобы вычислить ее.

мы хотим сейчасP (лысый | быть программистом)вероятность, то естьВероятность быть программистом будет лысой

Я никогда в жизни не буду лысой(((о(゚▽゚)о))) ! ! !

Замените формулу Наивного Байеса

P(秃头|做程序员) = \frac{P(做程序员|秃头)P(秃头)}{P(做程序员)}

Известные данные таковы

Имя Профессия ты лысый?
Кратос Марс да
Убийца 47 убийца да
Сайтама супермен да
Танос Директор офиса планирования семьи да
Джейсон Стэтхэм крутой парень да
такой-то 996 программист программист да
я программист нет

Основываясь на формуле Наивного Байеса, мы можем найти из приведенной выше таблицы:

P(秃头|做程序员) = \frac{\frac16 * \frac67}{\frac27} = \frac{21}{42} = \frac{1}{2}

Приведенный выше пример просто описывает основное использование формулы наивного Байеса.

Далее я буду использовать обзоры фильмов Douban Top250, чтобы использовать Наивный Байес для обучения и предсказания хороших и плохих отзывов.

Анализ настроений 250 лучших обзоров фильмов Douban

Прежде всего, мне нужен корпус обзоров фильмов Douban Top 250. Я использовал Scrapy, чтобы получить корпус 5w для обучения и проверки.

Сканер обзоров фильмов DoubanGitHub.com/3inch time/…

После того, как у нас есть корпус, мы можем приступить к фактической разработке.

Здесь рекомендуется использовать jupyter для разработки операций.

Следующие коды доступны на моем Github, и предложения приветствуются.

GitHub.com/3inch time/…

Сначала загрузите корпус

# -*- coding: utf-8 -*-
import random
import numpy as np
import csv
import jieba


file_path = './data/review.csv'
jieba.load_userdict('./data/userdict.txt')

# 读取保存为csv格式的语料
def load_corpus(corpus_path):
    with open(corpus_path, 'r') as f:
        reader = csv.reader(f)
        rows = [row for row in reader]

        
    review_data = np.array(rows).tolist()
    random.shuffle(review_data)

    review_list = []
    sentiment_list = []
    for words in review_data:
        review_list.append(words[1])
        sentiment_list.append(words[0])

    return review_list, sentiment_list

Перед обучением набор данных обычно перемешивается, порядок между данными нарушается, а данные рандомизируются, что позволяет избежать переобучения. так что используйтеrandom.shuffle()метод перемешивания данных.

jieba.load_userdict('./data/userdict.txt')Здесь я сам сделал словарь, чтобы предотвратить неточность сегментации некоторых заикающихся слов, что может повысить точность примерно на 1%.

Напримерне очень нравитсяВ этом предложении jieba будет делиться на два слова: «не» и «очень нравится», что приводит к высокой вероятности того, что это предложение будет предсказуемо как положительный комментарий.

Итак, здесь я разделил много таких слов в пользовательском словаре, что немного повысило точность.

Затем весь корпус делится на тестовую и обучающую выборки в соотношении 1:4.

n = len(review_list) // 5

train_review_list, train_sentiment_list = review_list[n:], sentiment_list[n:]
test_review_list, test_sentiment_list = review_list[:n], sentiment_list[:n]

Причастие

Используйте сегментацию слов jieba, чтобы сегментировать корпус и удалить стоп-слова.

import re
import jieba


stopword_path = './data/stopwords.txt'


def load_stopwords(file_path):
    stop_words = []
    with open(file_path, encoding='UTF-8') as words:
       stop_words.extend([i.strip() for i in words.readlines()])
    return stop_words


def review_to_text(review):
    stop_words = load_stopwords(stopword_path)
    # 去除英文
    review = re.sub("[^\u4e00-\u9fa5^a-z^A-Z]", '', review)
    review = jieba.cut(review)
    # 去掉停用词
    if stop_words:
        all_stop_words = set(stop_words)
        words = [w for w in review if w not in all_stop_words]

    return words

# 用于训练的评论
review_train = [' '.join(review_to_text(review)) for review in train_review_list]
# 对于训练评论对应的好评/差评
sentiment_train = train_sentiment_list

# 用于测试的评论
review_test = [' '.join(review_to_text(review)) for review in test_review_list]
# 对于测试评论对应的好评/差评
sentiment_test = test_sentiment_list

TF*IDF и векторизация частоты слов

TF-IDF (это метод взвешивания, обычно используемый при обработке информации и интеллектуальном анализе данных. Вычисляет важность слова во всем корпусе в соответствии с тем, сколько раз это слово появляется в тексте, и частотой появления документа во всем корпусе.

Его преимущество заключается в том, что он отфильтровывает некоторые общие, но нерелевантные слова, сохраняя при этом важные слова, влияющие на весь текст.

использоватьCountvectorizer()Преобразуйте документ в вектор и подсчитайте, как часто слова появляются в тексте.

CountVectorizerКласс преобразует слова в тексте в матрицу частоты слов, например, матрица содержит элемент a[i][j], который представляет частоту слова j в тексте класса i. Он подсчитывает количество вхождений каждого слова с помощью функции fit_transform.

TfidfTransformer используется для подсчета значения TF-IDF каждого слова в векторизаторе.

from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.pipeline import Pipeline
from sklearn.naive_bayes import MultinomialNB

count_vec = CountVectorizer(max_df=0.8, min_df=3)

tfidf_vec = TfidfVectorizer()

# 定义Pipeline对全部步骤的流式化封装和管理,可以很方便地使参数集在新数据集(比如测试集)上被重复使用。
def MNB_Classifier():
    return Pipeline([
        ('count_vec', CountVectorizer()),
        ('mnb', MultinomialNB())
    ])

max_dfФункция этого параметра является пороговой.При построении набора ключевых слов корпуса, если частотность слова больше, чемmax_df, это слово не будет рассматриваться как ключевое слово.

Если этот параметр имеет значение float, он указывает процентное соотношение количества вхождений слова к количеству документов корпуса, если он имеет значение int, он указывает количество вхождений слова.

min_dfпохожий наmax_df, разница в том, что если частота слова слова меньше, чемmin_df, слово не будет рассматриваться как ключевое слово

Таким образом, мы успешно построили конвейер для обучения и тестирования.

затем используйтеPipeline.fit() тренироваться на тренировочном наборе

использовать напрямуюPipeline.score()Предсказать и оценить на тестовом наборе

mnbc_clf = MNB_Classifier()

# 进行训练
mnbc_clf.fit(review_train, sentiment_train)

# 测试集准确率
print('测试集准确率: {}'.format(mnbc_clf.score(review_test, sentiment_test)))

Таким образом, мы завершили весь процесс от обучения до тестирования.

В основном, правильная скорость тестового набора составляет около 79%-80%.

Потому что в большом проценте обзоров фильмов есть негативные сентиментальные слова, например, в документальном фильме « Бухта дельфинов».

Я думаю, что большинство людей, которые смотрят этот фильм, не будут знать, что морская свинья байцзи в Китае вымерла уже 8 лет, и они не будут знать, что в реке Янцзы осталось всего около 1000 бесперых морских свиней. Вместо того, чтобы вздыхать и проклинать, как японцы охотятся и убивают дельфинов, лучше заняться чем-то практическим и защитить беспёрых морских свиней в реке Янцзы, которые вымрут через несколько лет. То, что делают китайцы, ничем не лучше маленькой Японии.

Поэтому, если такие подобные похвалы убрать, точность можно повысить.

Сохраните обученную модель

# 先转换成词频矩阵,再计算TFIDF值
tfidf = tfidftransformer.fit_transform(vectorizer.fit_transform(review_train))
# 朴素贝叶斯中的多项式分类器
clf = MultinomialNB().fit(tfidf, sentiment_train)

with open(model_export_path, 'wb') as file:
    d = {
        "clf": clf,
        "vectorizer": vectorizer,
        "tfidftransformer": tfidftransformer,
    }
    pickle.dump(d, file)

Прогнозирование тональности обзоров фильмов с использованием обученной модели

Здесь я напрямую вставляю весь исходный код, код очень простой, всю логику обработки я инкапсулирую в класс, поэтому очень удобно пользоваться.

Если вам это нужно, вы можете клонировать его прямо на моем Github.

# -*- coding: utf-8 -*-
import re
import pickle

import numpy as np
import jieba


class SentimentAnalyzer(object):
    def __init__(self, model_path, userdict_path, stopword_path):
        self.clf = None
        self.vectorizer = None
        self.tfidftransformer = None
        self.model_path = model_path
        self.stopword_path = stopword_path
        self.userdict_path = userdict_path
        self.stop_words = []
        self.tokenizer = jieba.Tokenizer()
        self.initialize()

    # 加载模型
    def initialize(self):
        with open(self.stopword_path, encoding='UTF-8') as words:
            self.stop_words = [i.strip() for i in words.readlines()]

        with open(self.model_path, 'rb') as file:
            model = pickle.load(file)
            self.clf = model['clf']
            self.vectorizer = model['vectorizer']
            self.tfidftransformer = model['tfidftransformer']
        if self.userdict_path:
            self.tokenizer.load_userdict(self.userdict_path)

    # 过滤文字中的英文与无关文字
    def replace_text(self, text):
        text = re.sub('((https?|ftp|file)://)?[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|].(com|cn)', '', text)
        text = text.replace('\u3000', '').replace('\xa0', '').replace('”', '').replace('"', '')
        text = text.replace(' ', '').replace('↵', '').replace('\n', '').replace('\r', '').replace('\t', '').replace(')', '')
        text_corpus = re.split('[!。?;……;]', text)
        return text_corpus

    # 情感分析计算
    def predict_score(self, text_corpus):
        # 分词
        docs = [self.__cut_word(sentence) for sentence in text_corpus]
        new_tfidf = self.tfidftransformer.transform(self.vectorizer.transform(docs))
        predicted = self.clf.predict_proba(new_tfidf)
        # 四舍五入,保留三位
        result = np.around(predicted, decimals=3)
        return result

    # jieba分词
    def __cut_word(self, sentence):
        words = [i for i in self.tokenizer.cut(sentence) if i not in self.stop_words]
        result = ' '.join(words)
        return result

    def analyze(self, text):
        text_corpus = self.replace_text(text)
        result = self.predict_score(text_corpus)

        neg = result[0][0]
        pos = result[0][1]

        print('差评: {} 好评: {}'.format(neg, pos))

Чтобы использовать, просто создайте экземпляр этого анализатора и используйтеanalyze()метод подойдет.

# -*- coding: utf-8 -*-
from native_bayes_sentiment_analyzer import SentimentAnalyzer


model_path = './data/bayes.pkl'
userdict_path = './data/userdict.txt'
stopword_path = './data/stopwords.txt'
corpus_path = './data/review.csv'


analyzer = SentimentAnalyzer(model_path=model_path, stopword_path=stopword_path, userdict_path=userdict_path)
text = '倍感失望的一部诺兰的电影,感觉更像是盗梦帮的一场大杂烩。虽然看之前就知道肯定是一部无法超越前传2的蝙蝠狭,但真心没想到能差到这个地步。节奏的把控的失误和角色的定位模糊绝对是整部影片的硬伤。'
analyzer.analyze(text=text)

GitHub.com/3inch time/…

Все приведенные выше коды были отправлены на мой Github, и предложения приветствуются.