利用朴素贝叶斯分类算法对搜狐新闻进行分类(python)

数据来源  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

猜你喜欢

转载自www.cnblogs.com/Micang/p/10348240.html