朴素贝叶斯算法实现 豆瓣Top250电影评价的情感分析与预测。
前言
本文使用樸素貝葉斯算法實(shí)現(xiàn) 豆瓣Top250電影評(píng)價(jià)的情感分析與預(yù)測(cè)。
最近在學(xué)習(xí)自然語(yǔ)言正負(fù)面情感的處理問(wèn)題,但是絕大部分能搜索到的實(shí)踐都是Kggle上IMDB影評(píng)的情感分析。
所以在這里我就用最基礎(chǔ)的樸素貝葉斯算法來(lái)對(duì)豆瓣的影評(píng)進(jìn)行情感分析與預(yù)測(cè)。
在這里我參考了?https://github.com/aeternae/IMDb_Review,萬(wàn)分感謝。
樸素貝葉斯分類(lèi)器
貝葉斯分類(lèi)是一類(lèi)分類(lèi)算法的總稱(chēng),這類(lèi)算法均以貝葉斯定理為基礎(chǔ),故統(tǒng)稱(chēng)為貝葉斯分類(lèi)。
這種算法常用來(lái)做文章分類(lèi),垃圾郵、件垃圾評(píng)論分類(lèi),樸素貝葉斯的效果不錯(cuò)并且成本很低。
已知某條件概率,如何得到兩個(gè)事件交換后的概率,也就是在已知P(A|B)的情況下如何求得P(B|A)。
P(B|A)表示事件A已經(jīng)發(fā)生的前提下,事件B發(fā)生的概率,叫做事件A發(fā)生下事件B的條件概率。
樸素貝葉斯的公式
P ( B ∣ A ) = P ( A ∣ B ) P ( B ) P ( A ) P(B|A) = \frac{P(A|B)P(B)}{P(A)}P(B∣A)=P(A)P(A∣B)P(B)?
一個(gè)通俗易懂的視頻教程
Youtube?https://www.youtube.com/watch?v=AqonCeZUcC4
舉個(gè)不太恰當(dāng)?shù)睦?/strong>
我們想知道做程序員與禿頭之間的關(guān)系,我們就可以用樸素貝葉斯公式來(lái)進(jìn)行計(jì)算。
我們現(xiàn)在想求?P(禿頭|做程序員)?的概率, 也就是做程序員就會(huì)禿頭的概率
我這輩子都不會(huì)禿頭 (((o(゚▽゚)o))) !!!
代入樸素貝葉斯公式
P ( 禿 頭 ∣ 做 程 序 員 ) = P ( 做 程 序 員 ∣ 禿 頭 ) P ( 禿 頭 ) P ( 做 程 序 員 ) P(禿頭|做程序員) = \frac{P(做程序員|禿頭)P(禿頭)}{P(做程序員)}P(禿頭∣做程序員)=P(做程序員)P(做程序員∣禿頭)P(禿頭)?
已知數(shù)據(jù)如下表
| 奎托斯 | 戰(zhàn)神 | 是 |
| 殺手47號(hào) | 殺手 | 是 |
| 埼玉 | 超人 | 是 |
| 滅霸 | 計(jì)生辦主任 | 是 |
| 杰森 斯坦森 | 硬漢 | 是 |
| 某某996程序員 | 程序員 | 是 |
| 我 | 程序員 | 否 |
基于樸素貝葉斯公式,由以上這張表我們可以求出:
P ( 禿 頭 ∣ 做 程 序 員 ) = 1 6 ? 6 7 2 7 = 21 42 = 1 2 P(禿頭|做程序員) = \frac{\frac16 * \frac67}{\frac27} = \frac{21}{42} = \frac{1}{2}P(禿頭∣做程序員)=72?61??76??=4221?=21?
上面這個(gè)例子就簡(jiǎn)單的描述了樸素貝葉斯公式的基本用法。
接下來(lái)我就使用豆瓣Top250排行榜的影評(píng)來(lái)使用樸素貝葉斯進(jìn)行好評(píng)與差評(píng)的訓(xùn)練與預(yù)測(cè)。
豆瓣Top250影評(píng)情感分析
首先需要豆瓣Top250影評(píng)的語(yǔ)料,我用Scrapy抓取了5w份語(yǔ)料,用于訓(xùn)練與驗(yàn)證。
豆瓣影評(píng)爬蟲(chóng)?GitHub - 3inchtime/douban_movie_review: 豆瓣Top250影評(píng)爬蟲(chóng)(用于情感分析語(yǔ)料)
有了語(yǔ)料之后我們就可以開(kāi)始實(shí)際開(kāi)發(fā)了。
這里建議使用jupyter來(lái)開(kāi)發(fā)操作。
以下代碼全部在我的Github上可以看到,歡迎大家提出建議。
GitHub - 3inchtime/douban_sentiment_analysis: 基于樸素貝葉斯實(shí)現(xiàn)的豆瓣影評(píng)情感分析
首先加載語(yǔ)料
# -*- coding: utf-8 -*- import random import numpy as np import csv import jiebafile_path = './data/review.csv' jieba.load_userdict('./data/userdict.txt')# 讀取保存為csv格式的語(yǔ)料 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在訓(xùn)練之前,一般均會(huì)對(duì)數(shù)據(jù)集做shuffle,打亂數(shù)據(jù)之間的順序,讓數(shù)據(jù)隨機(jī)化,這樣可以避免過(guò)擬合。所以使用random.shuffle()方法打亂數(shù)據(jù)。
jieba.load_userdict('./data/userdict.txt')這里我自己做了一個(gè)詞典,防止部分結(jié)巴分詞的不準(zhǔn)確,可以提高約1%左右的準(zhǔn)確率。
比如不是很喜歡這句,jieba會(huì)分成’不是‘,’很喜歡‘兩個(gè)詞,這樣導(dǎo)致這句話(huà)很大概率會(huì)被預(yù)測(cè)為好評(píng)。
所以這里我在自定義的詞典中分好了很多類(lèi)似這樣的詞,提高了一點(diǎn)點(diǎn)準(zhǔn)確率。
然后將全部的語(yǔ)料按1:4分為測(cè)試集與訓(xùn)練集
n = len(review_list) // 5train_review_list, train_sentiment_list = review_list[n:], sentiment_list[n:] test_review_list, test_sentiment_list = review_list[:n], sentiment_list[:n]分詞
使用jieba分詞,將語(yǔ)料進(jìn)行分詞,并且去除stopwords。
import re import jiebastopword_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_wordsdef 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# 用于訓(xùn)練的評(píng)論 review_train = [' '.join(review_to_text(review)) for review in train_review_list] # 對(duì)于訓(xùn)練評(píng)論對(duì)應(yīng)的好評(píng)/差評(píng) sentiment_train = train_sentiment_list# 用于測(cè)試的評(píng)論 review_test = [' '.join(review_to_text(review)) for review in test_review_list] # 對(duì)于測(cè)試評(píng)論對(duì)應(yīng)的好評(píng)/差評(píng) sentiment_test = test_sentiment_listTF*IDF與詞頻向量化
TF-IDF(是一種常用于信息處理和數(shù)據(jù)挖掘的加權(quán)技術(shù)。根據(jù)詞語(yǔ)的在文本中出現(xiàn)的次數(shù)和在整個(gè)語(yǔ)料中出現(xiàn)的文檔頻率來(lái)計(jì)算一個(gè)詞語(yǔ)在整個(gè)語(yǔ)料中的重要程度。
它的優(yōu)點(diǎn)是能過(guò)濾掉一些常見(jiàn)的卻無(wú)關(guān)緊要本的詞語(yǔ),同時(shí)保留影響整個(gè)文本的重要字詞。
使用Countvectorizer()將一個(gè)文檔轉(zhuǎn)換為向量,計(jì)算詞匯在文本中出現(xiàn)的頻率。
CountVectorizer類(lèi)會(huì)將文本中的詞語(yǔ)轉(zhuǎn)換為詞頻矩陣,例如矩陣中包含一個(gè)元素a[i] [j],它表示j詞在i類(lèi)文本下的詞頻。它通過(guò)fit_transform函數(shù)計(jì)算各個(gè)詞語(yǔ)出現(xiàn)的次數(shù)。
TfidfTransformer用于統(tǒng)計(jì)vectorizer中每個(gè)詞語(yǔ)的TF-IDF值。
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer from sklearn.pipeline import Pipeline from sklearn.naive_bayes import MultinomialNBcount_vec = CountVectorizer(max_df=0.8, min_df=3)tfidf_vec = TfidfVectorizer()# 定義Pipeline對(duì)全部步驟的流式化封裝和管理,可以很方便地使參數(shù)集在新數(shù)據(jù)集(比如測(cè)試集)上被重復(fù)使用。 def MNB_Classifier():return Pipeline([('count_vec', CountVectorizer()),('mnb', MultinomialNB())])max_df?這個(gè)參數(shù)的作用是作為一個(gè)閾值,當(dāng)構(gòu)造語(yǔ)料庫(kù)的關(guān)鍵詞集的時(shí)候,如果某個(gè)詞的詞頻大于max_df,這個(gè)詞不會(huì)被當(dāng)作關(guān)鍵詞。
如果這個(gè)參數(shù)是float,則表示詞出現(xiàn)的次數(shù)與語(yǔ)料庫(kù)文檔數(shù)的百分比,如果是int,則表示詞出現(xiàn)的次數(shù)。
min_df類(lèi)似于max_df,不同之處在于如果某個(gè)詞的詞頻小于min_df,則這個(gè)詞不會(huì)被當(dāng)作關(guān)鍵詞
這樣我們就成功的構(gòu)造出了用于訓(xùn)練以及測(cè)試的Pipeline
然后用?Pipeline.fit()對(duì)訓(xùn)練集進(jìn)行訓(xùn)練
再直接用?Pipeline.score()?對(duì)測(cè)試集進(jìn)行預(yù)測(cè)并評(píng)分
mnbc_clf = MNB_Classifier()# 進(jìn)行訓(xùn)練 mnbc_clf.fit(review_train, sentiment_train)# 測(cè)試集準(zhǔn)確率 print('測(cè)試集準(zhǔn)確率: {}'.format(mnbc_clf.score(review_test, sentiment_test)))這樣我們就完成了整個(gè)從訓(xùn)練到測(cè)試的全部流程。
基本上測(cè)試集的正確率在79%-80%左右。
因?yàn)殡娪霸u(píng)論中有很大一部分好評(píng)中會(huì)有負(fù)面情感的詞語(yǔ),例如在紀(jì)錄片《海豚灣》中
我覺(jué)得大部分看本片會(huì)有感的人,都不知道,中國(guó)的白暨豚已經(jīng)滅絕8年了,也不會(huì)知道,長(zhǎng)江里的江豚也僅剩1000左右了。與其感慨,咒罵日本人如何捕殺海豚,不如做些實(shí)際的事情,保護(hù)一下長(zhǎng)江里的江豚吧,沒(méi)幾年,也將絕跡了。中國(guó)人做出來(lái)的事情,也不會(huì)比小日本好到哪兒去。
所以說(shuō)如果將這種類(lèi)似的好評(píng)去除,則可以提高準(zhǔn)確率。
保存訓(xùn)練好的模型
# 先轉(zhuǎn)換成詞頻矩陣,再計(jì)算TFIDF值 tfidf = tfidftransformer.fit_transform(vectorizer.fit_transform(review_train)) # 樸素貝葉斯中的多項(xiàng)式分類(lèi)器 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)使用訓(xùn)練好的模型進(jìn)行影評(píng)情感預(yù)測(cè)
這里我直接貼上全部的源代碼,代碼非常簡(jiǎn)單,我將整個(gè)處理邏輯封裝為一個(gè)類(lèi),這樣就非常方便使用了。
有需要直接可以在我的Github上clone
# -*- coding: utf-8 -*- import re import pickleimport numpy as np import jiebaclass SentimentAnalyzer(object):def __init__(self, model_path, userdict_path, stopword_path):self.clf = Noneself.vectorizer = Noneself.tfidftransformer = Noneself.model_path = model_pathself.stopword_path = stopword_pathself.userdict_path = userdict_pathself.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)# 過(guò)濾文字中的英文與無(wú)關(guān)文字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# 情感分析計(jì)算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 resultdef analyze(self, text):text_corpus = self.replace_text(text)result = self.predict_score(text_corpus)neg = result[0][0]pos = result[0][1]print('差評(píng): {} 好評(píng): {}'.format(neg, pos))使用時(shí)只要實(shí)例化這個(gè)分析器,并使用analyze()方法就可以了。
# -*- coding: utf-8 -*- from native_bayes_sentiment_analyzer import SentimentAnalyzermodel_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 = '倍感失望的一部諾蘭的電影,感覺(jué)更像是盜夢(mèng)幫的一場(chǎng)大雜燴。雖然看之前就知道肯定是一部無(wú)法超越前傳2的蝙蝠狹,但真心沒(méi)想到能差到這個(gè)地步。節(jié)奏的把控的失誤和角色的定位模糊絕對(duì)是整部影片的硬傷。' analyzer.analyze(text=text)總結(jié)
以上是生活随笔為你收集整理的朴素贝叶斯算法实现 豆瓣Top250电影评价的情感分析与预测。的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 刷脸支付现骗局,创业投资需谨慎
- 下一篇: 常用代码参考模板