1. 程式人生 > >利用樸素貝葉斯分類算法對搜狐新聞進行分類(python)

利用樸素貝葉斯分類算法對搜狐新聞進行分類(python)

cno new 資產 jieba分詞 寶寶 .cn 官網 info targe

數據來源 https://www.sogou.com/labs/resource/cs.php
介紹:來自搜狐新聞2012年6月—7月期間國內,國際,體育,社會,娛樂等18個頻道的新聞數據,提供URL和正文信息
格式說明:
<doc>
<url>頁面URL</url>
<docno>頁面ID</docno>
<contenttitle>頁面標題</contenttitle>
<content>頁面內容</content>
</doc>
註意:content字段去除了HTML標簽,保存的是新聞正文文本

一、數據預處理

  (1)數據中為上述doc標簽的集合,並不是標準的xml文件,首先將數據開頭和結尾分別加上‘<data>‘和</data>根標簽。

 1 #-*-coding:utf-8-*-
 2 # 修復xml格式
 3 filePath = news_sohusite_xml.dat #語料路徑
 4 fileSeqWordDonePath = sougou.xml.parse.txt# 分詞後生成路徑
 5 fw=open(fileSeqWordDonePath, w, encoding=utf-8)
 6 fw.write(<data>)
7 with open(filePath, r, encoding=gb18030 ) as fileTrainRaw: #python3 8 for line in fileTrainRaw: 9 fw.write(line.replace(&,&amp;)) #去除非法字符 10 fw.write(</data>) 11 fw.close()

(2)提取數據中的分類信息並統計,按照二級域名分析相應類別

import xml.etree.ElementTree as ET
tree = ET.parse(fileSeqWordDonePath)
root 
= tree.getroot() # print(root.tag) classfiy={} for child in root: #print(child.tag, child.text) url = child.find(url) url_split=url.text.replace(http://,‘‘).split(.) sohu_index=url_split.index(sohu) if url_split[sohu_index-1] not in classfiy.keys(): classfiy[url_split[sohu_index-1]]=1 else: classfiy[url_split[sohu_index-1]]+=1 sorted_classfiy=sorted(classfiy.items(),key=operator.itemgetter(1), reverse=True)#排序 for c in sorted_classfiy: print(c[0],c[1])

輸出結果並分析對應分類:

roll 720957————滾動新聞
it 199871————科技
auto 138576————汽車
news 86052————新聞
stock 52930————股票
yule 50138————娛樂
sports 44536————體育
business 27489————財經
health 23409————健康
learning 13012————教育
money 10616————理財
s 8678————體育視頻
book 6532————圖書
women 5882————女性
fund 5015————基金
baobao 2693————母嬰
travel 2179————旅遊
cul 1924————文化
gd 1843————廣東
tv 1643
sh 1298————上海
goabroad 1106
men 1094
2008 842
media 669
2010 558
chihe 532
green 521
astro 360
club 349
gongyi 239
bschool 230
korea 109
games 42
2012 31
dm 29
v 8
campus 2
tuan 1
expo2010 1

選取以下分類進bayes行分類:
it 199871————科技
auto 138576————汽車
stock 52930————股票
yule 50138————娛樂
sports 44536————體育
business 27489————財經
health 23409————健康
learning 13012————教育
money 10616————理財
s 8678————體育視頻
book 6532————圖書
women 5882————女性
fund 5015————基金
baobao 2693————母嬰
travel 2179————旅遊

(3)計算互信息,抽取各類中的特征詞(對分類具有指示作用的詞——增加信息量),統計各分類數量

一個常用的方法是計算文檔中的詞項t與文檔類別c的互信息MI,MI度量的是詞的存在與否給類別c帶來的信息量。
信息量的計算參考這裏(https://www.cnblogs.com/fengfenggirl/p/text_feature_selection.html)
計算每個詞與與每個分類的互信息,每個類別下,互信息排名前200個單詞被保留,將他們加入特征詞集合。
根據這裏的代碼做修改,使用jieba分詞,並去除停用詞,將新聞數據重新組織,僅保留分類和新聞標題及正文

def get_stopword_set(filename=stop_word.txt):
    a=[]
    with open(filename, encoding=utf-8) as trainText:
        for line in trainText:
            a.append(line.strip())
    return sorted(set(a))

lables = [it,auto,stock,yule,sports,business,health,learning,money,s,book,women,fund,baobao,travel]
def lable2id(lable):
    for i in range(len(lables)):
        if lable == lables[i]:
            return i
    raise Exception(Error lable %s % (lable))

def doc_dict():
    ‘‘‘
        構造和類別數等長的0向量
    ‘‘‘
    return [0]*len(lables)

def mutual_info(N,Nij,Ni_,N_j):
    ‘‘‘
        計算互信息,這裏log的底取為2
    ‘‘‘
    return Nij * 1.0 / N * math.log(N * (Nij+1)*1.0/(Ni_*N_j))/ math.log(2)
    
def count_for_cates(trainText, featureFile):
    ‘‘‘
        遍歷文件,統計每個詞在每個類別出現的次數,和每類的文檔數
        並寫入結果特征文件
    ‘‘‘
    docCount = [0] * len(lables)
    wordCount = collections.defaultdict(doc_dict)
    stopword=get_stopword_set()
    #掃描文件和計數  
    for line in trainText:
        if not line.strip():continue
        lable,text = line.strip().split(^_^,1)
        #print lable,text
        index = lable2id(lable) #類別索引
        #print index
        words = text.split(    )
        for word in words:
            if word in stopword :continue
            elif word ==  :continue
            elif word ==     :continue
            wordCount[word][index] += 1  #每個單詞在每個類別中的計數
            docCount[index] += 1         #各類別單詞計數
    #計算互信息值
    print(u"計算互信息,提取關鍵/特征詞中,請稍後...")
    miDict = collections.defaultdict(doc_dict)
    N = sum(docCount)
    for k,vs in wordCount.items(): #遍歷每個單詞
        #print ‘k,vs‘, k,vs
        #break
        for i in range(len(vs)):  #遍歷每個分類,計算詞項k與文檔類別i的互信息MI
            N11 = vs[i]   #類別i下單詞k的數量
            N10 = sum(vs) - N11  #非類別i下單詞k的數量
            N01 = docCount[i] - N11  #類別i下其他單詞數量
            N00 = N - N11 - N10 - N01 #其他類別中非k單詞數目
            #print N11,N10,N01,N00
            mi = mutual_info(N,N11,N10+N11,N01+N11) + mutual_info(N,N10,N10+N11,N00+N10)+ mutual_info(N,N01,N01+N11,N01+N00)+ mutual_info(N,N00,N00+N10,N00+N01)
            miDict[k][i] = mi
    fWords = set()
    for i in range(len(docCount)): #遍歷每個單詞
        keyf = lambda x:x[1][i]
        sortedDict = sorted(miDict.items(),key=keyf,reverse=True)
        t=,.join([w[0] for w in sortedDict[:20]])
        print(lables[i] ,:,t)
        for j in range(1000):
            fWords.add(sortedDict[j][0])
    out = open(featureFile, w, encoding=utf-8)
    #輸出各個類的文檔數目
    out.write(str(docCount)+"\n")
    #輸出互信息最高的詞作為特征詞
    for fword in fWords:
        out.write(fword+"\n")
    print(u"特征詞寫入完畢...")
    out.close()

if __name__=="__main__":
    # print(get_stopword_set())
    _trainText=[]
    with open(sohu_train.txt, encoding=utf-8) as trainText:
        for line in trainText:
            id,catgre,body= line.split(^_^)
            #print id,catgre
            _trainText.append(catgre+^_^+body)
    
    #數據打亂分成兩份,4/5用來訓練,1/5用來測試
    random.shuffle(_trainText)
    num = len(_trainText)
    _testText = _trainText[4*num//5:]
    _trainText = _trainText[:4*num//5]
    
    # #計算互信息,提取關鍵字
    count_for_cates(_trainText, featureFile)

打印每個類別中排名前20的特征詞:

PS D:\學習\樸素貝葉斯-文本分類\news_sohusite_xml.full> python3 .\bayes_classfify.py
計算互信息,提取關鍵/特征詞中,請稍後...
it : 系列,CPU,華碩,英寸,尺寸,主頻,屏幕,硬盤容量,內存容量,芯片,顯卡,Intel,參數,操作系統,型號,產品,Windows,酷睿,NVIDIA
auto : 座椅,mm,調節,系列,CPU,華碩,電動,發動機,L,元,後排,汽車,方向盤,英寸,系統,車型,主頻,車,屏幕
stock : 萬股,公司,系列,CPU,華碩,公告,股份,證券,英寸,股,尺寸,A股,參數,主頻,屏幕,硬盤容量,內存容量,顯卡,芯片
yule : 娛樂,訊,電影,系列,CPU,導演,華碩,觀眾,演員,主演,英寸,產品,拍攝,尺寸,飾演,搜狐,參數,主頻,屏幕
sports : 比賽,球隊,球員,系列,CPU,華碩,開出,賽季,號碼,期,聯賽,冠軍,英寸,尺寸,產品,球迷,對手,主場,參數
business : 系列,CPU,華碩,經濟,市場,英寸,尺寸,主頻,參數,屏幕,硬盤容量,內存容量,顯卡,芯片,公司,Intel,企業,座椅,型號
health : 本品,治療,患者,主任醫師,處方,看病,大夫,醫院,服用,下夜,疾病,劑量,醫生,用量,過程,就醫,國藥準字,應,禁用
learning : 類,專業,考生,第二批,經濟學,第一批,錄取,高考,學校,學生,招生,教育,第三批,平均分,高校,批次,誌願,科學,電子信息
money : 銀行,信托,收益率,投資,理財產品,系列,終止,CPU,華碩,理財,美元,提前,收益,保障,預期,英寸,黃金,尺寸,資產
s : 視頻,體育,天下,時間,北京,CBA,集錦,廣播節目,收看,輪,西甲,搜狐,破門,VS,賽季,日,系列,聯賽,比賽
book : 說,看著,幣,系列,原創,想,走,CPU,華碩,淩天,黎涵,道,女人,裏,七妹,產品,英寸,尺寸,馥薰
women : 肌膚,膚質,功效,庫,制造商,品牌,所屬,保濕,官網,商品,精華,男人,CPU,華碩,女人,美白,皮膚,介紹,滋潤
fund : 基金,0.00,投資,證券,申購,代銷,凈值,起始,債券,開放式,代碼,指數,費率,收益,股票,有限公司,管理,日常,機構,贖回
baobao : 寶寶,孩子,媽媽,懷孕,胎兒,吃,孕婦,準媽媽,系列,嬰兒,發育,父母,幼兒園,CPU,家長,華碩,食物,兒童,營養
travel : 旅遊,概述,攻略,位於,景區,簡介,溫泉,平方公裏,系列,米,公裏,公園,景點,面積,CPU,48262083,華碩,河南,版友

二、訓練模型,計算先驗概率,即每個類別下各個單詞出現的概率

def load_feature_words(featureFile):
    ‘‘‘
        從特征文件導入特征詞
    ‘‘‘
    f = open(featureFile, encoding=utf-8)
    #各個類的文檔數目
    docCounts = eval(f.readline())
    features = set()
    #讀取特征詞
    for line in f:
        features.add(line.strip())
    f.close()
    return docCounts,features
            
def train_bayes(featureFile, textFile, modelFile):
    ‘‘‘
        訓練貝葉斯模型,實際上計算每個類中特征詞的出現次數
    ‘‘‘
    print(u"使用樸素貝葉斯訓練中...")
    docCounts,features = load_feature_words(featureFile) #讀取詞頻統計和特征詞
    wordCount = collections.defaultdict(doc_dict)
    # #每類文檔特征詞出現的次數
    tCount = [0]*len(docCounts)
    # for line in open(textFile).readlines():
    for line in textFile: #遍歷每個文檔
        lable,text = line.strip().split(^_^,1)
        index = lable2id(lable)        
        words = text.strip().split(    )
        for word in words: #遍歷文檔中每個詞
            if word in features and word !=‘‘:
                tCount[index] += 1      #類別index中單詞總數計數
                wordCount[word][index] += 1  #類別index中單詞word的計數
    outModel = open(modelFile, w, encoding=utf-8)
    #拉普拉斯平滑
    print(u"訓練完畢,寫入模型...")
    for k,v in wordCount.items():   #遍歷每個單詞
        scores = [(v[i]+1) * 1.0 / (tCount[i]+len(wordCount)) for i in range(len(v))]  #遍歷每個類別i,計算該類別下單詞的出現概率(頻率)
        outModel.write(k+"\t"+str(scores)+"\n")  #保存模型,記錄類別i下單詞k的出現概率(頻率)
    outModel.close()
if __name__=="__main__":
    # print(get_stopword_set())
    _trainText=[]
    with open(sohu_train.txt, encoding=utf-8) as trainText:
        for line in trainText:
            id,catgre,body= line.split(^_^)
            #print id,catgre
            _trainText.append(catgre+^_^+body)
    
    #數據打亂分成兩份,4/5用來訓練,1/5用來測試
    random.shuffle(_trainText)
    num = len(_trainText)
    _testText = _trainText[4*num//5:]
    _trainText = _trainText[:4*num//5]
    
    # #計算互信息,提取關鍵字
    count_for_cates(_trainText, featureFile)
    
    #訓練,計算先驗概率
    train_bayes(./featureFile, _trainText, ./modelFile)

技術分享圖片

三、模型測試,通過貝葉斯公式預測分類,並評價分類精度

def load_model(modelFile):
    ‘‘‘
        從模型文件中導入計算好的貝葉斯模型
    ‘‘‘
    print(u"加載模型中...")
    f = open(modelFile, encoding=utf-8)
    scores = {}
    for line in f:
        word,counts = line.strip().rsplit(\t,1)    
        scores[word] = eval(counts)
    f.close()
    return scores
    
def predict(featureFile, modelFile, testText):
    ‘‘‘
        預測文檔的類標,標準輸入每一行為一個文檔
    ‘‘‘
    docCounts,features = load_feature_words(featureFile)   #讀取詞頻統計和特征詞 
    docScores = [math.log(count * 1.0 /sum(docCounts)) for count in docCounts] #每個類別出現的概率
    scores = load_model(modelFile)   #加載模型,每個單詞在類別中出現的概率
    rCount = [0]*len(lables)
    docCount = [0]*len(lables)
    print(u"正在使用測試數據驗證模型效果...")
    for line in testText:
        lable,text = line.strip().split(^_^,1)
        index = lable2id(lable)        
        words = text.split(    )
        preValues = list(docScores)
        for word in words:
            if word in features and word !=‘‘:                
                for i in range(len(preValues)):
                    preValues[i]+=math.log(scores[word][i])  #利用貝葉斯公式計算對數概率,後半部分為每個類別中單詞word的出現概率
        m = max(preValues)          #取出最大值
        pIndex = preValues.index(m) #取出最大值類別的索引
        if pIndex == index:         #預測分類正確
            rCount[index] += 1
        #print lable,lables[pIndex],text
        docCount[index] += 1
    for r in range(len(lables)):
        print(u"類別:%s,測試文本量: %d , 預測正確的類別量: %d, 樸素貝葉斯分類器準確度:%f" %(lables[r],rCount[r],docCount[r],rCount[r] * 1.0 / docCount[r]))
    print(u"總共測試文本量: %d , 預測正確的類別量: %d, 樸素貝葉斯分類器準確度:%f" %(sum(rCount),sum(docCount),sum(rCount) * 1.0 / sum(docCount)))       
        
        
if __name__=="__main__":
    # print(get_stopword_set())
    _trainText=[]
    with open(sohu_train.txt, encoding=utf-8) as trainText:
        for line in trainText:
            id,catgre,body= line.split(^_^)
            #print id,catgre
            _trainText.append(catgre+^_^+body)
    
    #數據打亂分成兩份,4/5用來訓練,1/5用來測試
    random.shuffle(_trainText)
    num = len(_trainText)
    _testText = _trainText[4*num//5:]
    _trainText = _trainText[:4*num//5]
    
    # #計算互信息,提取關鍵字
    # count_for_cates(_trainText, ‘featureFile‘)
    
    #訓練,計算先驗概率
    # train_bayes(‘./featureFile‘, _trainText, ‘./modelFile‘)
    
    #預測
    predict(./featureFile, ./modelFile, _testText)

打印各類的分類精度和總體精度:

正在使用測試數據驗證模型效果...
類別:it,測試文本量: 35385 , 預測正確的類別量: 39647, 樸素貝葉斯分類器準確度:0.892501
類別:auto,測試文本量: 23320 , 預測正確的類別量: 27658, 樸素貝葉斯分類器準確度:0.843156
類別:stock,測試文本量: 6335 , 預測正確的類別量: 10769, 樸素貝葉斯分類器準確度:0.588263
類別:yule,測試文本量: 9591 , 預測正確的類別量: 10091, 樸素貝葉斯分類器準確度:0.950451
類別:sports,測試文本量: 8299 , 預測正確的類別量: 9007, 樸素貝葉斯分類器準確度:0.921394
類別:business,測試文本量: 3025 , 預測正確的類別量: 5493, 樸素貝葉斯分類器準確度:0.550701
類別:health,測試文本量: 4461 , 預測正確的類別量: 4662, 樸素貝葉斯分類器準確度:0.956885
類別:learning,測試文本量: 2340 , 預測正確的類別量: 2569, 樸素貝葉斯分類器準確度:0.910860
類別:money,測試文本量: 1440 , 預測正確的類別量: 2070, 樸素貝葉斯分類器準確度:0.695652
類別:s,測試文本量: 1414 , 預測正確的類別量: 1786, 樸素貝葉斯分類器準確度:0.791713
類別:book,測試文本量: 844 , 預測正確的類別量: 1328, 樸素貝葉斯分類器準確度:0.635542
類別:women,測試文本量: 848 , 預測正確的類別量: 1196, 樸素貝葉斯分類器準確度:0.709030
類別:fund,測試文本量: 942 , 預測正確的類別量: 1039, 樸素貝葉斯分類器準確度:0.906641
類別:baobao,測試文本量: 429 , 預測正確的類別量: 542, 樸素貝葉斯分類器準確度:0.791513
類別:travel,測試文本量: 410 , 預測正確的類別量: 455, 樸素貝葉斯分類器準確度:0.901099
總共測試文本量: 99083 , 預測正確的類別量: 118312, 樸素貝葉斯分類器準確度:0.837472

利用樸素貝葉斯分類算法對搜狐新聞進行分類(python)