【NLP 18、新词发现和TF·IDF】


目录

一、新词发现

1.新词发现的衡量标准 

① 内部稳固 

② 外部多变 

2.步骤

3.代码实现

Ⅰ、初始化

Ⅱ、加载语料数据并统计

Ⅲ、 取词,并记录左邻右邻出现过的字及次数

Ⅳ、计算左右熵

 Ⅴ、统计词长下的词总数

Ⅵ、计算互信息/凝固度

Ⅶ、 根据互信息、左右熵计算分数并排序

Ⅷ、新词发现的衡量标准 

二、挑选重要词

1.何为重要词

2.TF · IDF刻画重要词

例:

​编辑

TF · IDF的其他计算公式:

3.算法特点

4.TF · IDF的计算和使用 —— 代码实现

Ⅰ 统计tf和idf的值

Ⅱ、计算tf·idf

Ⅲ、 分词计算语料库中每个文档的tf·idf值

Ⅳ、按tf·idf值统计排序

Ⅴ、tf·idf的计算和使用

5.TF · IDF应用 —— 搜索引擎

步骤 

代码实现

Ⅰ、计算tf·idf和根据tf·idf值排序

Ⅱ、加载数据

Ⅲ、搜索引擎实现

Ⅳ、基于TF·IDF实现简单搜索引擎 

6.TF · IDF应用 —— 文本摘要

步骤

代码实现

Ⅰ、加载文档数据

Ⅱ、 计算每一篇文章的摘要

Ⅲ、生成所有文章的摘要

Ⅳ、基于TF·IDF实现简单的文本摘要 

7.TF · IDF应用 —— 文本相似度计算

步骤

代码实现

Ⅰ、加载文档数据

Ⅱ、文本向量化

Ⅲ、通过余弦相似度寻找最相似的四篇文本

 Ⅳ、基于TF·IDF实现文本相似度的计算

8.TF·IDF的优势

① 可解释性好

② 计算速度快

③ 对标注数据依赖小

④ 可以与很多算法组合使用

9.TF·IDF的劣势

① 受分词效果影响大

② 词与词之间没有语义相似度

③ 没有语序信息

④ 能力范围有限

⑤ 样本不均衡会对结果有很大影响

⑥ 类内样本间分布不被考虑


死亡不是生命的终点,被遗忘才是

                                               —— 24.12.21

一、新词发现

引言

假设没有词表,如何从文本中发现新词?

随着时间的推移,新词会不断地出现,固有词表会过时

补充词表有利于下游任务

相当于一种固定搭配

1.新词发现的衡量标准 

① 内部稳固 

词的内部应该是稳固的,用内部稳固度 / 互信息衡量

内部稳固度/互信息:词语中几个字的固定搭配出现的次数除以词语中每个字单独出现的概率的乘积

公式:

n:词语中字的个数,词语的长度;

p(W):词语中几个字的固定搭配词语的出现次数;

p(c1)…p(cn):词语中每个字在词表中单独出现的概率


② 外部多变 

词的外部应该是多变的,用左右熵衡量

左右熵: 将词语外部出现的所有字再除以出现的总词频数,得到出现某个字的频率pi,代入公式进行求和后取反,得到词语两边的左右熵,词语的外部两侧出现一个固定字的频率应该较低,换句话说,词的外部应该是多变的,而不是固定的,左右熵的值大小可以衡量词的外部值是否多变,左右熵的值越大,词的外部越多变

公式:

n:词语中字的个数,词语的长度;

pi:词语后出现某个字的频率,词语外部出现的所有字除以出现的总词频数

用两个指标计算候选词的分数,根据分数衡量候选词是否是新词  


2.步骤

① 先选取一个固定长度

② 找到文本中所有固定长度字数的潜在候选词

③ 对每个候选词计算候选词的内部稳固度和左右熵两指标

④ 根据两指标的分数进行排序

⑤ 筛选出认为最有可能是词的候选词


3.代码实现

Ⅰ、初始化

defaultdict():Python 标准库 collections 模块中的一个类,它是内置 dict 类的子类。与普通字典不同的是,defaultdict 可以在键不存在时自动为其提供一个默认值,避免了因访问不存在的键而引发 KeyError 异常。

参数名 参数类型 默认值 是否必填 描述
default_factory 可调用对象(如函数、类)或 None None 当访问不存在的键时,用于生成默认值的可调用对象。如果传入 None,其行为与普通字典相同,访问不存在的键会引发 KeyError。常见的传入类型有 intlistset 等,分别会在键不存在时返回默认的整数 0、空列表 []、空集合 set()
**kwargs 关键字参数 用于初始化字典的键值对,用法和普通字典的初始化相同。
    def __init__(self, corpus_path):
        # 设置词语最高长度 1 - 5 四个字的词语
        self.max_word_length = 5
        self.word_count = defaultdict(int)
        self.left_neighbor = defaultdict(dict)
        self.right_neighbor = defaultdict(dict)
        self.load_corpus(corpus_path)
        self.calc_pmi()
        self.calc_entropy()
        self.calc_word_values()

Ⅱ、加载语料数据并统计

open():Python 的内置函数,用于打开文件并返回一个文件对象。通过这个文件对象,你可以对文件进行读取、写入、追加等操作。

参数名 参数类型 默认值 是否必填 描述
file str 或 pathlib.Path 对象 要打开的文件的路径,可以是绝对路径或相对路径。
mode str 'r' 文件打开模式,常见的模式有: - 'r':只读模式,用于读取文件内容。 - 'w':写入模式,若文件存在则清空内容,不存在则创建新文件。 - 'a':追加模式,在文件末尾追加内容,不存在则创建新文件。 - 'x':独占创建模式,创建新文件,若文件已存在则报错。 - 'b':二进制模式,可与其他模式组合,如 'rb' 表示二进制只读。 - 't':文本模式,默认值,可与其他模式组合,如 'wt' 表示文本写入。 - '+':更新模式,可与其他模式组合,如 'r+' 表示读写模式。
buffering int -1 缓冲策略: - -1:使用系统默认缓冲策略。 - 0:不使用缓冲(仅适用于二进制模式)。 - 1:行缓冲(仅适用于文本模式)。 - 大于 1 的整数:指定缓冲区大小(字节)。
encoding str 取决于系统 文件的编码方式,如 'utf-8''gbk' 等。处理非 ASCII 字符的文本文件时需指定。
errors str None 编码错误处理方式,如 'ignore' 忽略非法字符,'replace'? 替换,'strict' 遇到非法字符抛出异常。
newline str None 控制换行符处理,None 表示根据系统自动处理,'' 不进行转换,还可指定 '\n''\r' 等。
closefd bool True 是否在关闭文件对象时同时关闭文件描述符,一般保持默认。
opener 可调用对象 None 自定义文件打开器,高级用法,一般不用。

strip():Python 字符串对象的方法,用于移除字符串首尾指定的字符序列(默认为空格、制表符、换行符等空白字符)。

参数名 参数类型 默认值 是否必填 描述
chars str None 要移除的字符序列。如果不提供该参数,则默认移除字符串首尾的空白字符。
    #加载语料数据,并进行统计
    def load_corpus(self, path):
        with open(path, encoding="utf8") as f:
            for line in f:
                sentence = line.strip()
                for word_length in range(1, self.max_word_length):
                    self.ngram_count(sentence, word_length)
        return

Ⅲ、 取词,并记录左邻右邻出现过的字及次数

按照指定的窗口长度(word_length)从输入的句子(sentence)中提取词语,并统计这些词语的出现次数,同时记录每个词语的左邻字符和右邻字符及其出现的次数。

# 按照窗口长度取词,并记录左邻右邻出现过的字及次数
def ngram_count(self, sentence, word_length):
    # 遍历句子,提取长度为 word_length 的词语
    for i in range(len(sentence) - word_length + 1):
        # 从句子中截取长度为 word_length 的词语
        word = sentence[i:i + word_length]
        # 统计该词语的出现次数,若词语不在 word_count 字典中,初始值为 0
        self.word_count[word] += 1

        # 检查当前词语是否有左邻字符
        if i - 1 >= 0:
            # 获取左邻字符
            char = sentence[i - 1]
            # 统计左邻字符的出现次数,若左邻字符不在对应词语的 left_neighbor 字典中,初始值为 0
            self.left_neighbor[word][char] = self.left_neighbor[word].get(char, 0) + 1

        # 检查当前词语是否有右邻字符
        if i + word_length < len(sentence):
            # 获取右邻字符
            char = sentence[i + word_length]
            # 统计右邻字符的出现次数,若右邻字符不在对应词语的 right_neighbor 字典中,初始值为 0
            self.right_neighbor[word][char] = self.right_neighbor[word].get(char, 0) + 1

    return

Ⅳ、计算左右熵

values():Python 字典(dict)对象的一个方法,用于返回一个包含字典中所有值的视图对象。这个视图对象会动态反映字典的变化,即当字典中的值被修改、添加或删除时,视图对象也会相应地更新。

items():Python 字典对象的一个方法,用于返回一个包含字典中所有键值对的视图对象。每个键值对以元组的形式表示,元组的第一个元素是键,第二个元素是对应的值。同样,这个视图对象也是动态的,会随着字典的变化而更新。

    #计算熵
    def calc_entropy_by_word_count_dict(self, word_count_dict):
        total = sum(word_count_dict.values())
        entropy = sum([-(c / total) * math.log((c / total), 10) for c in word_count_dict.values()])
        return entropy

    #计算左右熵
    def calc_entropy(self):
        self.word_left_entropy = {}
        self.word_right_entropy = {}
        for word, count_dict in self.left_neighbor.items():
            self.word_left_entropy[word] = self.calc_entropy_by_word_count_dict(count_dict)
        for word, count_dict in self.right_neighbor.items():
            self.word_right_entropy[word] = self.calc_entropy_by_word_count_dict(count_dict)

 Ⅴ、统计词长下的词总数

defaultdict():Python 标准库 collections 模块中的一个类,它继承自内置的 dict 类。defaultdict 的特殊之处在于,当访问一个不存在的键时,它会自动为该键创建一个默认值,而不是像普通字典那样抛出 KeyError 异常。

参数名 参数类型 默认值 是否必填 描述
default_factory 可调用对象(如函数、类)或 None None 当访问字典中不存在的键时,会调用该可调用对象来生成默认值。若为 None,行为同普通字典,访问不存在的键会抛出 KeyError。常见传入类型有 int(默认值 0)、list(默认值 [])、set(默认值 set())等。
**kwargs 关键字参数 用于初始化字典的键值对,和普通字典初始化时传入键值对的用法相同。

items():字典(包括 defaultdict)对象的一个方法,用于返回一个包含字典中所有键值对的视图对象。每个键值对以元组的形式表示,元组的第一个元素是键,第二个元素是对应的值。这个视图对象是动态的,意味着当字典内容发生变化时,视图对象也会相应更新。

    #统计每种词长下的词总数
    def calc_total_count_by_length(self):
        self.word_count_by_length = defaultdict(int)
        for word, count in self.word_count.items():
            self.word_count_by_length[len(word)] += count
        return

Ⅵ、计算互信息/凝固度

    #计算互信息(pointwise mutual information 凝固度)
    def calc_pmi(self):
        self.calc_total_count_by_length()
        self.pmi = {}
        for word, count in self.word_count.items():
            p_word = count / self.word_count_by_length[len(word)]
            p_chars = 1
            for char in word:
                p_chars *= self.word_count[char] / self.word_count_by_length[1]
            self.pmi[word] = math.log(p_word / p_chars, 10) / len(word)
        return

Ⅶ、 根据互信息、左右熵计算分数并排序

get():字典(dict)的 get() 函数用于获取字典中指定键对应的值。与使用方括号 [] 直接访问键值不同的是,当指定的键不存在于字典中时,get() 函数不会抛出 KeyError 异常,而是返回一个默认值,这样能让代码更具健壮性。

参数名 参数类型 默认值 是否必填 描述
key 任意不可变类型(如 intstrtuple 等) 要在字典中查找的键。因为字典使用哈希表存储键值对,而不可变类型能保证哈希值稳定,所以键必须是不可变类型。
default 任意类型 None 当指定的 key 不在字典中时,get() 函数返回的默认值。用户可根据需求自定义该值,例如传入空列表 []、数字 0 等。
    def calc_word_values(self):
        self.word_values = {}
        for word in self.pmi:
            if len(word) < 2 or "," in word:
                continue
            pmi = self.pmi.get(word, 1e-3)
            le = self.word_left_entropy.get(word, 1e-3)
            re = self.word_right_entropy.get(word, 1e-3)
            # 通过三个指标综合评估词的价值
            # self.word_values[word] = pmi + le + re
            # self.word_values[word] = pmi * min(le, re)
            self.word_values[word] = pmi * max(le, re)
            # self.word_values[word] = pmi + le * re
            # self.word_values[word] = pmi * le * re

Ⅷ、新词发现的衡量标准 

import math
from collections import defaultdict

class NewWordDetect:
    def __init__(self, corpus_path):
        # 设置词语最高长度 1 - 5 四个字的词语
        self.max_word_length = 5
        self.word_count = defaultdict(int)
        self.left_neighbor = defaultdict(dict)
        self.right_neighbor = defaultdict(dict)
        self.load_corpus(corpus_path)
        self.calc_pmi()
        self.calc_entropy()
        self.calc_word_values()


    #加载语料数据,并进行统计
    def load_corpus(self, path):
        with open(path, encoding="utf8") as f:
            for line in f:
                sentence = line.strip()
                for word_length in range(1, self.max_word_length):
                    self.ngram_count(sentence, word_length)
        return

    #按照窗口长度取词,并记录左邻右邻出现过的字及次数
    def ngram_count(self, sentence, word_length):
        for i in range(len(sentence) - word_length + 1):
            word = sentence[i:i + word_length]
            self.word_count[word] += 1
            if i - 1 >= 0:
                char = sentence[i - 1]
                self.left_neighbor[word][char] = self.left_neighbor[word].get(char, 0) + 1
            if i + word_length < len(sentence):
                char = sentence[i +word_length]
                self.right_neighbor[word][char] = self.right_neighbor[word].get(char, 0) + 1
        return

    #计算熵
    def calc_entropy_by_word_count_dict(self, word_count_dict):
        total = sum(word_count_dict.values())
        entropy = sum([-(c / total) * math.log((c / total), 10) for c in word_count_dict.values()])
        return entropy

    #计算左右熵
    def calc_entropy(self):
        self.word_left_entropy = {}
        self.word_right_entropy = {}
        for word, count_dict in self.left_neighbor.items():
            self.word_left_entropy[word] = self.calc_entropy_by_word_count_dict(count_dict)
        for word, count_dict in self.right_neighbor.items():
            self.word_right_entropy[word] = self.calc_entropy_by_word_count_dict(count_dict)


    #统计每种词长下的词总数
    def calc_total_count_by_length(self):
        self.word_count_by_length = defaultdict(int)
        for word, count in self.word_count.items():
            self.word_count_by_length[len(word)] += count
        return

    #计算互信息(pointwise mutual information 凝固度)
    def calc_pmi(self):
        self.calc_total_count_by_length()
        self.pmi = {}
        for word, count in self.word_count.items():
            p_word = count / self.word_count_by_length[len(word)]
            p_chars = 1
            for char in word:
                p_chars *= self.word_count[char] / self.word_count_by_length[1]
            self.pmi[word] = math.log(p_word / p_chars, 10) / len(word)
        return

    def calc_word_values(self):
        self.word_values = {}
        for word in self.pmi:
            if len(word) < 2 or "," in word:
                continue
            pmi = self.pmi.get(word, 1e-3)
            le = self.word_left_entropy.get(word, 1e-3)
            re = self.word_right_entropy.get(word, 1e-3)
            # 通过三个指标综合评估词的价值
            # self.word_values[word] = pmi + le + re
            # self.word_values[word] = pmi * min(le, re)
            self.word_values[word] = pmi * max(le, re)
            # self.word_values[word] = pmi + le * re
            # self.word_values[word] = pmi * le * re

if __name__ == "__main__":
    nwd = NewWordDetect("sample_corpus.txt")
    value_sort = sorted([(word, count) for word, count in nwd.word_values.items()], key=lambda x:x[1], reverse=True)
    print([x for x, c in value_sort if len(x) == 2][:10])
    print([x for x, c in value_sort if len(x) == 3][:10])
    print([x for x, c in value_sort if len(x) == 4][:10])


二、挑选重要词

从词到理解 

有了分词能力后,需要利用词来完成对文本的理解

首先可以想到的,就是从文章中挑选重要词

1.何为重要词

假如一个词在某类文本(假设为A类)中出现次数很多在其他类别文本(非A类)出现很少,那么这个词是A类文本的重要词(高权重词 

例:恒星、黑洞 ——> 天文 

反之,如果一个词出现在很多领域,则其对于任意类别的重要性都很差

例:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值