情感分析snownlp包部分核心代碼理解


 

snownlps是用Python寫的個中文情感分析的包,自帶了中文正負情感的訓練集,主要是評論的語料庫。使用的是朴素貝葉斯原理來訓練和預測數據。主要看了一下這個包的幾個主要的核心代碼,看的過程作了一些注釋,記錄一下免得以后再忘了。

1. sentiment文件夾下的__init__.py,主要是集成了前面寫的幾個模塊的功能,進行打包。

 1 # -*- coding: utf-8 -*-
 2 from __future__ import unicode_literals
 3 
 4 import os
 5 import codecs
 6 
 7 from .. import normal
 8 from .. import seg
 9 from ..classification.bayes import Bayes
10 
11 data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
12                          'sentiment.marshal')
13 
14 
15 class Sentiment(object):
16 
17     def __init__(self):# 實例化Bayes()類作為屬性,下面的很多方法都是調用的Bayes()的方法完成的
18         self.classifier = Bayes()
19 
20     def save(self, fname, iszip=True):# 保存最終的模型
21         self.classifier.save(fname, iszip)
22 
23     def load(self, fname=data_path, iszip=True):
24         self.classifier.load(fname, iszip)# 加載貝葉斯模型
25 
26     # 分詞以及去停用詞的操作
27     def handle(self, doc):
28         words = seg.seg(doc)# 分詞
29         words = normal.filter_stop(words)# 去停用詞
30         return words# 返回分詞后的結果,是一個list列表
31 
32     def train(self, neg_docs, pos_docs):
33         data = []
34         for sent in neg_docs:# 讀入負樣本
35             data.append([self.handle(sent), 'neg'])
36             # 所以可以看出進入bayes()的訓練的數據data格式是[[[第一行分詞],類別],
37             #                                             [[第二行分詞], 類別],
38             #                                             [[第n行分詞],類別]
39             #                                                              ]
40         for sent in pos_docs: # 讀入正樣本
41             data.append([self.handle(sent), 'pos'])
42         self.classifier.train(data)  # 調用的是Bayes模型的訓練方法train()
43 
44     def classify(self, sent):
45         ret, prob = self.classifier.classify(self.handle(sent))#得到分類結果和概率
46         if ret == 'pos':#默認返回的是pos('正面'),否則就是負面
47             return prob
48         return 1-prob
49 
50 
51 classifier = Sentiment()#實例化Sentiment()對象
52 classifier.load()
53 
54 
55 def train(neg_file, pos_file):
56     #讀取正負語料庫文本
57     neg_docs = codecs.open(neg_file, 'r', 'utf-8').readlines()
58     pos_docs = codecs.open(pos_file, 'r', 'utf-8').readlines()
59     global classifier#聲明classifier為全局變量,下面重新賦值,雖然值仍然是Sentiment()函數
60     classifier = Sentiment()
61     classifier.train(neg_docs, pos_docs)#調用Sentment()模塊里的train()方法
62 
63 
64 def save(fname, iszip=True):
65     classifier.save(fname, iszip)
66 
67 
68 def load(fname, iszip=True):
69     classifier.load(fname, iszip)
70 
71 
72 def classify(sent):
73     return classifier.classify(sent)

2.使用的朴素貝葉斯原理及公式變形

推薦一篇解釋得很好的文章:情感分析——深入snownlp原理和實踐

 

classification文件夾下的Bayes.py模塊主要包含兩個方法train(data)和classify(X),訓練和預測方法

 1     # 訓練數據集
 2     # 訓練的數據data格式是[[['分詞1','分詞2','分詞x'],類別],
 3     #                     [[第二行分詞], 類別],
 4     #                     [[第n行分詞],類別]
 5     #                                      ]
 6     def train(self, data):#訓練后得到的self.d={'neg':AddOneProb,'pos':AddOneProb},AddOneProb包含重要的分詞信息,他里面也有一個self.d={'分詞1':v1,'分詞2':v2,'分詞3':v3,...}包含分詞和相應的分詞個數。
 7 
 8         # 遍歷數據集
 9         for d in data:
10             # d[1]標簽-->分類類別
11             c = d[1]
12             # 判斷數據字典中是否有當前的標簽
13             if c not in self.d:
14                 # 如果沒有該標簽,加入標簽,值是一個AddOneProb對象。其實就是為每個分詞建一個AddOnePro對象來計數
15                 self.d[c] = AddOneProb()
16             # d[0]是評論的分詞list,遍歷分詞list
17             for word in d[0]:
18                 # 調用AddOneProb中的add方法,添加單詞
19                 self.d[c].add(word, 1)#self.d[c]是AddOneProb對象,調用AddOneProb的add()函數來對詞word計數,重點看frequency.py中的幾個類
20         # 計算總詞數
21         self.total = sum(map(lambda x: self.d[x].getsum(), self.d.keys()))#self.d[x].getsum()是調用AddOneProb對象的getsum()函數計算詞
22 
23     #對句子x分類,而x是被分過詞的列表,(將句子分詞的步驟會在Sentiment類中的分類函數classify()執行,這里不要管,只要知道x是分詞后的的列表)
24     def classify(self, x):
25         tmp = {}
26         # 遍歷每個分類標簽,本案例中只有兩個類別
27         for k in self.d:
28             tmp[k] = log(self.d[k].getsum()) - log(self.total)#計算先驗概率,即p(neg)和p(pos)兩個類別的概率
29             for word in x:
30                 tmp[k] += log(self.d[k].freq(word))#計算后驗概率,即每個類別條件下某個分詞的概率p('詞A'|neg)和p('詞A'|pos),# 詞頻,詞word不在字典里的話就為0
31         ret, prob = 0, 0
32         for k in self.d:#遍歷兩個類
33             now = 0#預測值賦初值為0
34             try:
35                 for otherk in self.d:#當類相同時now=1,類不同時now累加exp(tmp[otherk]-tmp[k]),最終計算now為變形后的朴素貝葉斯預測值的分母
36                     now += exp(tmp[otherk]-tmp[k])#朴素貝葉斯變形式可見博客
37                 now = 1/now#求倒數為得到的這個類的預測值
38             except OverflowError:
39                 now = 0
40             if now > prob:#比較兩個類別的概率誰大,大的就是這個文本的類別。注意:初始prob等於0,經過遍歷后悔更新prob並且prob等於相應類別的朴素貝葉斯概率
41                 ret, prob = k, now
42         return (ret, prob)
43         #這里用朴素貝葉斯方法計算

注意:classify()方法中的朴素貝葉斯變形方法的編寫,下面兩個for循環,先是遍歷兩個類,比例第一個類,now表示對這個類計算的貝葉斯預測值,賦初值為0,第二個循環遍歷第一個類(otherk=k),

exp(tmp[otherk]-tmp[k])=exp(0)=1,則now=1,再遍歷第二個類得到上面變形公式中的第二部分值,這兩個相加得到的new就是

最后再倒數就得到了

   

 

再附加一個上面的AddOneProb()方法的源代碼解析,他就是一個對輸入的分詞計數的函數,將語料庫的詞進行分類計數,為了訓練做得到先后驗概率准備:

 1 '''對詞計算頻數'''
 2 class BaseProb(object):
 3 
 4     def __init__(self):
 5         self.d = {}#用來存儲分詞和分詞的個數,鍵是分詞,值是分詞的個數
 6         self.total = 0.0#計數總共的詞個數
 7         self.none = 0
 8 
 9     def exists(self, key):#判斷字典self.d中是否存在這個詞key
10         return key in self.d
11 
12     def getsum(self):#返回語self.d中存儲的詞的總數
13         return self.total
14 
15     def get(self, key):#判斷字典中是否存在這個詞key,並且返回這分詞的詞個數
16         if not self.exists(key):
17             return False, self.none
18         return True, self.d[key]
19 
20     def freq(self, key):#計算詞key的頻率
21         return float(self.get(key)[1])/self.total
22 
23     def samples(self):#返回字典的鍵,其實就是返回所有的分詞,以列表形式
24         return self.d.keys()
25 
26 
27 class NormalProb(BaseProb):
28 
29     def add(self, key, value):
30         if not self.exists(key):
31             self.d[key] = 0
32         self.d[key] += value
33         self.total += value
34 
35 
36 '''對詞計數'''
37 class AddOneProb(BaseProb):#繼承BaseProb類,所以BaseProb類中的屬性和函數都能用。
38 
39     def __init__(self):
40         self.d = {}
41         self.total = 0.0
42         self.none = 1
43 
44     def add(self, key, value):
45         self.total += value#計算總詞數
46         if not self.exists(key):#如果這個詞key不在self.d中的話,那么在字典中加上這個詞,即鍵為此,並且給這個詞計數1,同時總的詞數量total加1.
47             self.d[key] = 1
48             self.total += 1#感覺不應該再加1了,上面都已經計算過總數了????說是后面預測要用到,可能是要平滑
49         self.d[key] += value#如果字典已經有這個詞了的話,那么給這個詞數量加1

 

對於局進行分詞的方法Handle()感覺也很重要,也需要看一看。

再碼個帖子:snownlp情感分析源碼解析snownlp的Github代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM