自然语言处理——金庸小说的信息熵计算

自然语言处理——金庸小说的信息熵计算

1.实验目的

通过计算所提供的数据库中的金庸小说的信息熵,熟悉自然语言处理的方法与过程。

2.实验内容

2.1信息熵

1948年,为了解决信息量化的问题,香农从热力学中借用了热熵的概念提出了“信息熵”的概念,把信息中排除了冗余后的平均信息量称为“信息熵”,并给出了计算信息熵的数学表达式。
H ( X ) = ∑ x ∈ χ p ( x ) l o g p ( x ) H(X)=\sum_{x∈χ} p(x)logp(x) H(X)=xχp(x)logp(x)
信息论之父克劳德·香农给出的信息熵的三个性质[1]:

  1. 单调性,发生概率越高的事件,其携带的信息量越低;
  2. 非负性,信息熵可以看作为一种广度量,非负性是一种合理的必然;
  3. 累加性,即多随机事件同时发生存在的总不确定性的量度是可以表示为各事件不确定性的量度的和,这也是广度量的一种体现。

2.2 jieba分词

支持三种分词模式:

  • 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;
  • 精确模式,试图将句子最精确地切开,适合文本分析;
  • 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。

主要功能有:

2.2.1分词
  • jieba.cut 方法接受三个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型
  • jieba.cut_for_search 方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细
  • 待分词的字符串可以是 unicode 或 UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8
  • jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用
  • jieba.lcut 以及 jieba.lcut_for_search 直接返回 list
  • jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定义分词器,可用于同时使用不同词典。jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射
2.2.2添加自定义词典

载入辞典

  • 开发者可以指定自己自定义的词典,以便包含 jieba 词库里没有的词。虽然 jieba 有新词识别能力,但是自行添加新词可以保证更高的正确率
  • 用法: jieba.load_userdict(file_name) # file_name 为文件类对象或自定义词典的路径
  • 词典格式和 dict.txt 一样,一个词占一行;每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。file_name 若为路径或二进制方式打开的文件,则文件必须为 UTF-8 编码。
  • 词频省略时使用自动计算的能保证分出该词的词频。

调整词典

  • 使用 add_word(word, freq=None, tag=None)del_word(word) 可在程序中动态修改词典。
  • 使用 suggest_freq(segment, tune=True) 可调节单个词语的词频,使其能(或不能)被分出来。
  • 注意:自动计算的词频在使用 HMM 新词发现功能时可能无效。
2.2.3. 关键词提取

基于 TF-IDF 算法的关键词抽取

import jieba.analyse

  • jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
    • sentence 为待提取的文本
    • topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20
    • withWeight 为是否一并返回关键词权重值,默认值为 False
    • allowPOS 仅包括指定词性的词,默认值为空,即不筛选
  • jieba.analyse.TFIDF(idf_path=None) 新建 TFIDF 实例,idf_path 为 IDF 频率文件

3.实验结果

数据集大小,size: 5457833
信息熵:12.144266718132455

分词结果:

‘只得’, ‘罢了’, ‘好’, ‘在’, ‘双儿’, ‘为’, ‘人’, ‘温柔’, ‘谦和’, ‘和’, ‘六位’, ‘夫人’, ‘个个’, ‘情谊’, ‘甚好’, ‘大家’, ‘也’, ‘不’, ‘妒嫉’, ‘于’, ‘她’, ‘只’, ‘建宁’, ‘建宁公主’, ‘宁公主’, ‘公主’, ‘自忖’, ‘以’, ‘皇上’, ‘御’, ‘妹’, ‘的’, ‘身分’, ‘分金’, ‘金枝’, ‘金枝玉叶’, ‘玉叶’, ‘居然’, ‘还’, ‘及’, ‘不’, ‘上’, ‘一个’, ‘出身’, ‘微贱’, ‘的’, ‘小丫头’, ‘丫头’, ‘心中’, ‘着实’, ‘气恼’, ‘不过’, ‘七位’, ‘夫人’, ‘人平’, ‘平时’, ‘若’, ‘有’, ‘纷争’, ‘其余’, ‘六’, ‘人’, ‘一定’, ‘联盟’, ‘对付’, ‘公主’, ‘建宁’, ‘建宁公主’, ‘宁公主’, ‘公主’, ‘主人’, ‘孤’, ‘势’, ‘单’, ‘韦小’, ‘韦小宝’, ‘小宝’, ‘又’, ‘不对’, ‘她’, ‘回护’, ‘近年’, ‘近年来’, ‘年来’, ‘来气’, ‘气焰’, ‘已’, ‘大为’, ‘收敛’, ‘轻易’, ‘不敢’, ‘启’, ‘次日’, ‘韦小’, ‘韦小宝’, ‘小宝’, ‘命’, ‘双儿’, ‘取出’, ‘郑克’, ‘当年’, ‘在’, ‘通吃岛’, ‘岛上’, ‘血书’, ‘的’, ‘借据’, ‘请’, ‘了’, ‘多隆’, ‘来’, ‘交给’, ‘了’, ‘他’, ‘多隆’, ‘大喜’, ‘说道’, ‘既有’, ‘亲笔’, ‘借据’, ‘咱们’, ‘石头’, ‘头里’, ‘也’, ‘要’, ‘榨出’, ‘他’, ‘油’, ‘来’,

上面随机节选了部分分词结果,整体效果还好,但也有诸如“人平”、“来气”、“通吃岛”、“韦小”等效果很差的分词。

下面是计算词频(TF)、逆文本频率指数(IDF)的结果

白马 8.844901865223905
啸 8.924944572897441
西风 9.618091753457387
碧血 9.618091753457387
碧血剑 10.311238934017332
飞狐 19.236183506914774
外传 8.70180102158323
连城 9.330409681005605
连城诀 10.311238934017332
鹿鼎记 10.02355686156555
三十 8.519479464789276
三十三 9.01195594988707
十三 8.576637878629224
三剑客 10.311238934017332
剑客 9.330409681005605
图 8.63726250044566
射雕 9.463941073630128
英雄 8.519479464789276
英雄传 10.02355686156555
传神 10.02355686156555
神雕侠侣 9.618091753457387
侠侣 9.618091753457387
书剑 9.618091753457387
恩仇 9.01195594988707
恩仇录 9.800413310251342
天龙 9.330409681005605
天龙八部 10.02355686156555
八部 9.800413310251342
侠客 9.330409681005605
侠客行 10.311238934017332
笑傲 9.618091753457387
笑傲江湖 10.02355686156555
傲江湖 10.02355686156555
江湖 8.576637878629224
雪山 8.844901865223905
雪山飞狐 10.02355686156555
倚天 9.618091753457387
天屠龙 9.800413310251342
屠龙 9.463941073630128
屠龙记 9.800413310251342
鸳鸯 8.770793893070183
鸳鸯刀 9.800413310251342
越女剑 9.463941073630128

4.附录

`



import os
import time

import jieba
import re
from math import log

# 1、递归读取文件
# 2、读取到文件,文件内容转换成文本,使用jieba进行分词

class TraversalFun():


    # 1 初始化
    def __init__(self, rootDir):
        self.rootDir = rootDir

    def TraversalDir(self):
        return TraversalFun.getCorpus(self, self.rootDir)

    def getCorpus(self, rootDir):
        corpus = []
        r1 = u'[a-zA-Z0-9’!"#$%&\'()*+,-./::;<=>?@,。?★、…【】《》?“”‘’![\\]^_`{|}~]+'  # 用户也可以在此进行自定义过滤字符
        listdir = os.listdir(rootDir)
        for file in listdir:
            path  = os.path.join(rootDir, file)
            if os.path.isfile(path):
                # 打印文件地址
                #print(os.path.abspath(path))
                # 获取文章内容,fromfile主要用来处理数组 pass
                # filecontext = np.fromfile(os.path.abspath(path))
                with open(os.path.abspath(path), "r", encoding='ANSI') as file:
                    filecontext = file.read();
                    #print(filecontext)
                    # 分词 去掉符号
                    filecontext = re.sub(r1, '', filecontext)
                    filecontext = filecontext.replace("\n", '')
                    filecontext = filecontext.replace(" ", '')
                    seg_list = jieba.cut(filecontext, cut_all=True)
                    corpus += seg_list
                    #print(seg_list)
                    #print("[精确模式]:" + "/".join(seg_list))
            elif os.path.isdir(path):
                TraversalFun.AllFiles(self, path)
        return corpus


#构造词典,统计每个词的频率,并计算信息熵
def calc_tf(corpus):
    #   统计每个词出现的频率
    word_freq_dict = dict()
    for word in corpus:
        if word not in word_freq_dict:
            word_freq_dict[word] = 1
        word_freq_dict[word] += 1
    # 将这个词典中的词,按照出现次数排序,出现次数越高,排序越靠前
    word_freq_dict = sorted(word_freq_dict.items(), key=lambda x:x[1], reverse=True)
    # 计算TF概率
    word_tf = dict()
    # 信息熵
    shannoEnt = 0.0
    # 按照频率,从高到低,开始遍历,并未每个词构造一个id
    for word, freq in word_freq_dict:
        # 计算p(xi)
        prob = freq / len(corpus)
        word_tf[word] = prob
        shannoEnt -= prob*log(prob, 2)
    return word_tf, shannoEnt



if __name__ == "__main__":
    startTime = time.time()
    tra = TraversalFun("./2")
    corpus = tra.TraversalDir()
    print("数据集大小,size: " + str(len(corpus)))
    print(corpus)
    word_tf, shannoEnt = calc_tf(corpus)
    print("信息熵:" + str(shannoEnt))
    #print("TF: ")
    #print(word_tf)
    endTime = time.time()
    print("\ntotal cost time(s)", (endTime-startTime), 's')

`

你可能感兴趣的:(python,自然语言处理)