最近在做一個商品評論分類的需求,主要是將商品的差評根據(jù)主題進行多次二分類,例如評論的內(nèi)容是不是質(zhì)量問題,物流問題等。
由于對機器學習屬于小白水平,所以先從最簡單的樸素貝葉斯算法下手。
優(yōu)點:易懂易實現(xiàn),文本容易向量化? ??
缺點:分類效果一般
樸素貝葉斯算法是用統(tǒng)計學的方法來對數(shù)據(jù)進行二分類,其主要思想就是貝葉斯公式,這個公式大學本科就學過,所以它的原理很容易理解,
? ? ? ? ? ? ? ? ? ? ? P(B|A)=P(A|B)P(B)/P(A)
即 事件A在事件B發(fā)生的前提下發(fā)生的概率=事件A在事件B發(fā)生的前提下發(fā)生的概率*事件B發(fā)生的概率/事件A發(fā)生的概率。
我們這里的事件主要是兩類:
?1)這個評論是A分類的概率是多大,記做P(A)
?2)某個詞Bi在A分類下出現(xiàn)的概率是多大,記做P(Bi|A),其中i為某個詞?
通過大數(shù)定律可知,當樣本的數(shù)量足夠大,某件事情發(fā)生的概率是收斂于這件事發(fā)生的期望的。所以當訓練樣本數(shù)量足夠多時,我們就可以把某件事發(fā)生的概率近似作為期望。而上述兩類事情的概率,都是可以通過訓練集統(tǒng)計到的。然后對于一個新的文本,可以根據(jù)它的詞向量來判斷各種分類的概率,然后取最大概率的分類即可。
一.文本分詞
使用python的結巴中文分詞庫對文本進行處理,由于評論主要是用戶的主觀感受,文本異常字符較多,所以這里需要對文本進行清洗,例如特殊字符,標點符號,停用詞等;而對于某些領域,需要自己添加分詞詞典,方便提取有用的關鍵詞(這里不考慮詞頻,所以分詞的最終結果都做去重處理)。
訓練集分詞前:

訓練集分詞后:

二.構建分詞向量
首先創(chuàng)建一個所有評論中不重復詞的列表,然后向量化所有的評論,函數(shù)代碼如下:
def setOfWords2Vec(vocabList,inputSet):
? ? ? returnVec=[0]*len(vocabList)
? ? ? for word in inputSet:
? ? ? ? ? if word in vocabList:
? ? ? ? ? ? ? returnVec[vocabList.index(word)]=1
? ? ? ? ? else:
? ? ? ? ? ? ? pass
? ? ? ? ? ? ? # print "the word:%s is not in myVocabulary!" % word
? ? ? return returnVec
其中 vocabList為所有訓練集中不重復詞的列表,可以選擇集合作為它的數(shù)據(jù)類型,由于集合中每個元素都是唯一的,所以不需要考慮去重的問題。inputSet為評論分詞后的結果,這樣就將一個評論轉化為一個向量,對于測試集或測試數(shù)據(jù)中的詞如果在vocabList不存在則不予考慮。這個向量的維數(shù)就是vocabList的長度,如果某個詞存在,則這個詞的位數(shù)為1,否則為0.
選中20條評論作為訓練數(shù)據(jù),一共分出了129個詞,生成的詞向量組成的矩陣為20*129

三.訓練??
根據(jù)上文的描述,假設 P(A)表示這個評論是A分類的概率是多大,P(Bi|A)表示在A分類的前提下詞Bi出現(xiàn)的概率,則向量B在A分類下的概率為P(B|A)=P(b0,b1,b2...bn|A),假設所有的詞都互相獨立,則上面的表達式也可寫為P(b0|A)P(b1|A)P(b2|A)...P(bn|A)。根據(jù)訓練數(shù)據(jù),可以求出P(A),P(bi|A)。
若要判斷一個向量C是屬于A1還是A2,則求出向量C是A1和A2分類的概率并比較大小,哪個類別的概率大,則將其歸為哪個類。其中:
P(A1|C)=P(C|A1)*P(A1)/P(C)
P(A2|C)=P(C|A2)*P(A2)/P(C)
要比較P(A1|C)和P(A2|C)的大小,只需要比較P(C|A1)*P(A1)和P(C|A2)*P(A2)的大小即可。
#樸素貝葉斯分類器訓練函數(shù),trainCategory為分類數(shù)組,trainMatrix為所有的評論向量
def trainNB0(trainMatrix,trainCategory):
? ? numTrainDocs=len(trainMatrix)
? ? numWords=len(trainMatrix[0])
? ? #pAbusive表示為所有評論中出現(xiàn),是該分類的概率
? ? pAbusive=sum(trainCategory)/float(numTrainDocs)
? ? p0Num=zeros(numWords);p1Num=zeros(numWords) #p0Num和p1Num為兩個全0數(shù)組,長度為numWords,
? ? # 這兩個向量用來統(tǒng)計P0分類中每個詞的個數(shù)和P1中每個詞的個數(shù)
? ? p0Denom=0.0;p1Denom=0.0#初始化
? ? #for循環(huán)掃描每一個評論生成的向量
? ? for i in range(numTrainDocs):
? ? ? ? # 如果向量判定為1,p1Num加上每個詞的個數(shù)
? ? ? ? if trainCategory[i]==1:
? ? ? ? ? ? p1Num =p1Num+trainMatrix[i]
? ? ? ? ? ? #p1Denom是詞向量中關鍵詞的個數(shù)
? ? ? ? ? ? p1Denom =p1Denom+sum(trainMatrix[i])
? ? ? ? # 如果向量判定為0,p1Num加上每個詞的個數(shù)
? ? ? ? else:
? ? ? ? ? ? p0Num =p0Num+ trainMatrix[i]
? ? ? ? ? ? p0Denom =p0Denom+ sum(trainMatrix[i])
? ? p1Vect=p1Num/p1Denom
? ? p0Vect=p0Num/p0Denom
? ? return p0Vect,p1Vect,pAbusive
p0Vect:P0分類下每個詞的概率
p1Vect:P1分類下每個詞的概率
pAbusive:P1分類的概率。
上面的算法有兩個問題:
1)P(b0|A)P(b1|A)P(b2|A)...P(bn|A),如果其中一個概率值為0,那么最后的乘積也為0,所以我們將所有詞的出現(xiàn)次數(shù)初始化為1,將出現(xiàn)的總次數(shù)初始化為2
?p0Num=ones(numWords);p1Num=ones(numWords)
?p0Denom=2.0;p1Denom=2.0
2)P(b0|A)P(b1|A)P(b2|A)...P(bn|A)大部分因子都很小,所以結果很容易四舍五入得到0,為了降低這種影響,我們對其取對數(shù)則,雖然函數(shù)并不一樣,但是對他們比較大小的結果并不影響,所以
?p1Vect=log(p1Num/p1Denom)
?p0Vect=log(p0Num/p0Denom)
四.分類
傳入一個新的向量,判斷它是那種分類,即比較P(A1|C)和P(A2|C)的大小
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
? ? p1=sum(vec2Classify*p1Vec)+log(pClass1)
? ? p0=sum(vec2Classify*p0Vec)+log(1-pClass1)
? ? if p1>p0:
? ? ? ? return 1
? ? else:
? ? ? ? return 0
其中vec2Classify為需要判定的向量,p0Vec表示每個詞在分類0中的概率的對數(shù)向量,p1Vec表示每個詞在分類1中的對數(shù)向量,pClass1為分類為1的概率,由于是二分類問題,可知pClass0=1-pClass1
由于p0Vec和p1Vec是概率的對數(shù),所以對概率的乘積大小比較和對其對數(shù)求和的大小比較,結果是一致的。
五.測試正確率
將測試數(shù)據(jù)進行判定,將返回的結果值和人工判定值進行對比,如果結果一致則表示分類成功。
def testing(testVec,predictVec):
? ? vecLen=len(testVec)
? ? count=0.0
? ? for i in range(vecLen):
? ? ? ? if testVec[i]-predictVec[i]==0:
? ? ? ? ? ? count=count+1
? ? return count/vecLen
testVec表示人工判定的分類向量,predictVec表示算法判定的分類向量,即可算出正確率。
#分類效果不能令人滿意,需要繼續(xù)嘗試其他方法。