admin管理员组

文章数量:1629929

'''创建数据集和类标签'''
def loadDataSet():
    docList = [];classList = [] # 文档列表、类别列表
    dirlist = ['C3-Art','C4-Literature','C5-Education','C6-Philosophy','C7-History']
    for j in range(5):
        for i in range(1, 11): # 总共10个文档
            # 切分,解析数据,并归类为 1 类别
            wordList = textParse(open('./fudan/%s/%d.txt' % (dirlist[j],i),encoding='UTF-8').read())
            docList.append(wordList)
            classList.append(j)
            # print(i,'\t','./fudan/%s/%d.txt' % (dirlist[j],i),'\t',j)
    return docList,classList

''' 利用jieba对文本进行分词,返回切词后的list '''
def textParse(str_doc):
    # 正则过滤掉特殊符号、标点、英文、数字等。
    import re
    r1 = '[a-zA-Z0-9’!"#$%&\'()*+,-./:;<=>?@,。?★、…【】《》?“”‘’![\\]^_`{|}~]+'
    str_doc=re.sub(r1, '', str_doc)

    # 创建停用词列表
    stwlist = set([line.strip() for line in open('./stopwords.txt', 'r', encoding='utf-8').readlines()])
    sent_list = str_doc.split('\n')
    # word_2dlist = [rm_tokens(jieba.cut(part), stwlist) for part in sent_list]  # 分词并去停用词
    word_2dlist = [rm_tokens([word+"/"+flag+" " for word, flag in pseg.cut(part) if flag in ['n','v','a','ns','nr','nt']], stwlist) for part in sent_list] # 带词性分词并去停用词
    word_list = list(itertools.chain(*word_2dlist)) # 合并列表
    return word_list



''' 去掉一些停用词、数字、特殊符号 '''
def rm_tokens(words, stwlist):
    words_list = list(words)
    for i in range(words_list.__len__())[::-1]:
        word = words_list[i]
        if word in stwlist:  # 去除停用词
            words_list.pop(i)
        elif len(word) == 1:  # 去除单个字符
            words_list.pop(i)
        elif word == " ":  # 去除空字符
            words_list.pop(i)
    return words_list
'''获取所有文档单词的集合'''
def createVocabList(dataSet):
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)  # 操作符 | 用于求两个集合的并集
    # print(len(vocabSet),len(set(vocabSet)))
    return list(vocabSet)
'''文档词袋模型,创建矩阵数据'''
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec
'''朴素贝叶斯模型训练数据优化'''
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix) # 总文件数
    numWords = len(trainMatrix[0]) # 总单词数

    p1Num=p2Num=p3Num=p4Num=p5Num = ones(numWords) # 各类为1的矩阵
    p1Denom=p2Denom=p3Denom=p4Denom=p5Denom = 2.0 # 各类特征和
    num1=num2=num3=num4=num5 = 0 # 各类文档数目

    pNumlist=[p1Num,p2Num,p3Num,p4Num,p5Num]
    pDenomlist =[p1Denom,p2Denom,p3Denom,p4Denom,p5Denom]
    Numlist = [num1,num2,num3,num4,num5]

    for i in range(numTrainDocs): # 遍历每篇训练文档
        for j in range(5): # 遍历每个类别
            if trainCategory[i] == j: # 如果在类别下的文档
                pNumlist[j] += trainMatrix[i] # 增加词条计数值
                pDenomlist[j] += sum(trainMatrix[i]) # 增加该类下所有词条计数值
                Numlist[j] +=1 # 该类文档数目加1

    pVect,pi = [],[]
    for index in range(5):
        pVect.append(log(pNumlist[index] / pDenomlist[index]))
        pi.append(Numlist[index] / float(numTrainDocs))
    return pVect, pi
'''朴素贝叶斯分类函数,将乘法转换为加法'''
def classifyNB(vec2Classify, pVect,pi):
    # 计算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
    bnpi = [] # 文档分类到各类的概率值列表
    for x in range(5):
        bnpi.append(sum(vec2Classify * pVect[x]) + log(pi[x]))
    # print([bnp for bnp in bnpi])
    # 分类集合
    reslist = ['Art','Literature','Education','Philosophy','History']
    # 根据最大概率,选择索引值
    index = [bnpi.index(res) for res in bnpi if res==max(bnpi)]
    return reslist[index[0]] # 返回分类值
'''朴素贝叶斯新闻分类应用'''
def testingNB():
    # 1. 加载数据集
    dataSet,Classlabels = loadDataSet()
    # 2. 创建单词集合
    myVocabList = createVocabList(dataSet)

    # 3. 计算单词是否出现并创建数据矩阵
    trainMat = []
    for postinDoc in dataSet:
        trainMat.append(bagOfWords2VecMN(myVocabList, postinDoc))
    with open('./word-bag.txt','w') as f:
        for i in trainMat:
            f.write(str(i)+'\r\n')
    # 4. 训练数据
    pVect,pi= trainNB0(array(trainMat), array(Classlabels))
    # 5. 测试数据
    testEntry = textParse(open('./fudan/test/C5-1.txt',encoding='UTF-8').read())
    thisDoc = array(bagOfWords2VecMN(myVocabList, testEntry))
    print(testEntry[:10], '分类结果是: ', classifyNB(thisDoc, pVect,pi))

Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.892 seconds.
Prefix dict has been built succesfully.
['全国/n ', '举办/v ', '电影/n ', '新华社/nt ', '北京/ns ', '国家教委/nt ', '广播电影电视部/nt ', '文化部/n ', '联合/v ', '决定/v '] 分类结果是:  Literature
耗时:29.4882 s
 

 

结果分析:我们运行分类器得出结果易知,预测结果是文化类,且运行时间为29s。首先分析为什么预测错误,这里面主要是训练集样本比较少和特征选择的原因。运行时间是由于将特征矩阵存储本地后,后面直接读取文本,相当于加载缓存,大大缩短运行时间。但是这里还有值得优化的地方,比如每次运行都会加载训练模型,大大消耗时间,我们能不能训练模型加载一次,多次调用呢?当然是可以的,这个问题下文继续优化。我们重点关注下特征选择问题

特征选择问题讨论

  • 做文本分类的时候,遇到特征矩阵1.5w。在测试篇幅小的文章总是分类错误?这个时候如何做特征选择?是不是说去掉特征集中频率极高和极低的一部分,对结果有所提升?
    答:你说的这个情况是很普遍的现象,篇幅小的文章,特征小,所以模型更容易判断出错!去掉高频和低频通常是可以使得训练的模型泛化能力变强

  • 比如:艺术,文化,历史,教育。界限本来就不明显,比如测试数据“我爱艺术,艺术是我的全部”。结果会分类为文化。其实这个里面还有就是不同特征词的权重问题,采用tf-idf优化下应该会好一些?

    答:我个人觉得做文本特征提取,还是需要自己去分析文本本身内容的文字特点,你可以把每一类的文本的实体提取出来,然后统计一下每个词在每一类上的数量,看看数量分布,也许可以发现一些数据特点

  • 我就是按照这个思路做的,还有改进时候的停用词,其实可以分析特征文本,针对不同业务,使用自定义的停用词要比通用的好
    还有提前各类见最具表征性的词汇加权,凸显本类的权重是吧?
    答:比如,艺术类文章中,哪些词出现较多,哪些词出现少,再观察这些词的词性主要是哪些,这样可能会对你制定提取特征规则方式的时候提供一定的思路参考,我可以告诉你的是,有些词绝对会某一类文章出出现多,然后在其他类文章出现很少,这一类的词就是文章的特征词

  • 那样的思路可以是:对某类文章单独构建类内的词汇表再进行选择。最后对类间词汇表叠加就ok了。
    答:词汇表有个缺点就是,不能很好的适应新词
  • 改进思路呢
    答:我给你一个改进思路:你只提取每个文本中的名词、动词、形容词、地名,用这些词的作为文本的特征来训练试一试,用文本分类用主题模型(LDA)来向量化文本,再训练模型试一试。如果效果还是不够好,再将文本向量用PCA进行一次特征降维,然后再训练模型试一试,按常理来说,效果应该会有提高
  • 还有我之前个人写的程序分类效果不理想,后来改用sklearn内置BN运行依旧不理想。适当改进了特征提取,还是不理想。估计每类10篇文章的训练数据太少了
    答:文本本身特征提取就相对难一些,再加上训练数据少,训练出来的模型效果可想而已,正常的

本文标签: 中文朴素文本代码贝叶斯