作者主页:苏州程序大白
作者简介:CSDN人工智能域优质创作者,苏州市凯捷智能科技有限公司创始之一,目前合作公司富士康、歌尔等几家新能源公司
如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)和C#、Halcon、python+opencv、VUE、各大公司面试等一些订阅专栏哦
有任何问题欢迎私信,看到会及时回复
关注苏州程序大白,分享粉丝福利
NLP(Nature Language Processing,自然语言处理)
是计算机及人工智能领域的一个重要的子项目,它研究计算机如何处理、理解及应用人类语言。是人类在漫长的进化过程中形成的计算机语言复杂的符号等系统(类似C/Java的符号等系统)。以下是关于自然处理的常见定义:
语言处理是科学与自然语言中关于计算机与人类语言转换的领域。
语言处理是人工智能领域中一个重要的方向。它研究实现人与计算机之间用自然语言进行有效运作的各种理论和方法。
语言处理研究这些方法在交际条件和交际条件下与人交际的一门中及人与计算机网络中的语言问题的语言问题。不断完善这些语言模型,并根据系统的实用性,以及对系统的实用性评测技术。
自然语言处理还有其他一些名称,例如:自然语言(Natural Language Understanding)
、计算机语言学(Computational Linguistics)
、人类语言技术(Human Language Technology)
等等。
NLP 可以分成两类,是生成主要基于新文本或语料的分析,另一种文本
或语料
。
该任务将文本或语言对日料分隔成更多语言特征单元(单词)。对于拉丁系,词之间有重要的空格等文字,对于中文语言,分词就是例如的,分词直接影响文本的理解。
文本:苏州市姑苏区超市
分词1:苏州市/姑苏区/超市
分词2:苏州/市长/零食/店
例如,在“The dog barked at the mailman”(狗对邮递递员吠叫)
和正确的树皮“用作药物”(树皮有时用作药物)
中,对于不同的含义。词义消歧类诸如此类的任务。
NER尝试从给定的文本或文本语料库中提取实体(例如,人物、位置和组织)。例如,句子:
周一,约翰在学校给了玛丽两个苹果
PoS 常用的两种称呼分别是名词、动词、形容词、词、词、词等、也可以是词性的词、词、词、动词、动词等。
例如文本分类有许多应用场景,垃圾邮件检测、新闻文章分类(例如,政治、科技和运动)和产品评论评分(即正向或负向)。我们可以使用数据标记(即人工对评论标上正面)或者是负面的标签)训练一个分类模型来实现这个任务。
可以利用 NLP 来生成新的文本或材料,编写机器天气预报(天气预报、新闻、例如唐诗等),生成文本是一段机器合成的“下面的诗”:
向塞向芶芶临扇,猛牒来惊。向面炎交好
,若隚。
何人改,松仙绕绮霞。偶笑寒栖咽,长闻暖顶时。
失个亦垂谏,守身丈韦鸿。忆及他年事,应愁一故名。
坐忆山高道,为随夏郭间。到乱唯无己,千方得命赊。
QA 技术具有广泛的商业价值,这些技术是聊天机器人和 VA(例如,Google Assistant 和 Apple Siri)的支持。许多公司已经采用对话机器人来提供客户。以下是一段与聊天机器人的:
机器翻译,MT)指将文本由一种语言翻译成另一种语言,是根据一个序列(机器翻译最接近的另一种语言生成)。
NLP的发展发展为:基于规则→其基于统计→基于深入学习,发展大致经历了4个阶段:1956年的萌芽期;1957年1970年的快速期;1971年1993年的低速期;1994年现在的复苏融合期。
1956年前
)1946年
:第一台电子计算机诞生148 年
香农 把 离散 商业 年: 描述 于 马尔 可 的 机 器 。156 年
:Chomsky 又提出了当时的自然语言,并将其运用到处理中。一个时期是在不同的处理领域中存在的自然规律和基于两种不同的处理方式。基于这种方法的符号(象征性的)另一派(每个派别)。期间,方法派的研究取得了从60年代开始到长足的发展。乔姆斯基为代表的象征派学者开始了语言理论和生成句法的研究,60年代进行了叶逻辑形式的研究。也取得了很大的进步。
1997 年 TDAP 期重要的美国语言研究成果,美国的语言系统的建立与等。知联系起来了。
语言研究的结果由于人们看到基于自然语言处理的应用并不能在地段中不断涌现解决,而一连的新问题又出现了,于是,很多人对随着语言处理的研究失去了自然的信心。 70 年代开始,自然语言处理在自然低谷时期。
尽管如此,一些研究人员仍然继续进入了他们的研究。由于他们的出色工作,自然语言处理这一低谷时期同样取得了一些成果。70当年,基于隐马尔可夫模型,HMM的马尔可夫模型(Hidden Markov Model,HMM的马尔可夫模型)
统计了最初在语音领域取得重大进展,话语分析(话语分析)也取得了。过去的研究方法进行了反思,有限状态模型和经验主义研究也开始复苏。
90年代以后,有两台计算机从根本上发展到促进语言的自然复苏与研究。一件事是90年代以来,计算机的速度和测量量自然增加,为语言处理改善了物质基础,处理事件的事件; 19 可能化是互联网化和网络化 2000 年的另一种商业活动 4 年基于语言的信息和语言的信息和成为自然语言的 00 话题的热门话题。之后 NLP 领域的里程碑事件:
2001年:神经语言模型
2008年:多任务学习
2013年:词嵌入
2013年:NLP的神经网络
2014年:序列到序列模型
2015年:纪律机制
2015年:根据记忆的神经网络
2018年:预训练语言模型
例:自动化研究所取得的成就
一:自动化/研究/所/取得/的/成就
成就二:自动化/研究所/取得/的/成就
动物保护警察
“保护” 理解成动词、名词,不一样。
喜欢乡下的孩子
关于鲁迅的文章
节假日期间,所有博物馆全部(不)开放
双减
元 宇宙
绝绝子
平躺
甲:你这人真正的英文?甲:没有英文,英文
?那我就不好意思了。
中国队最有悬念的是全世界有一个女人也有她和她,他们一个谁也有谁干过球,不过,另一个人打
如果希拉里干过球,只是因为美国总统和美国总统的努力,克林顿也将成为全世界唯一一个干过美国总统和干过美国总统的男人
什么是语料库
语料库是指语言材料(材料库的我们)。现代的语料是指放在以原始语料库里的语料或文字标注的文字文本。 ,语言反应单位的使用和意义,基本以知识的表现形态——语言的原貌。
语料库的特征
语料库中存放的是真实中出现过的材料。
语料库是计算机为承重语言知识的资源,但语言知识的资源。
真实语料需要经过分析、处理和加工,才能成为有用的资源。
语料库的作用
支持语言学研究和语言教学研究。
支持 NLP 系统的开发。
北京大学计算机语言所语料库(中文),地址: https://opendata.pku.edu.cn/dataverse/icl
London-Lund 英语口语语料库,地址:http /www.helsinki.fi/varieng/CoRD/copora.LLC/
腾讯中文语每包含800个万个单词。其中每一个词都满足2200个维的库,包括最新的一期新词。采用了一个更先进的工具预测的数据和更好的。
维基百科是编辑和最常用的开放网络数据集之一,作为最常用的材料、内容、格式的文本语言,各种语言的维基百科在 NLP 中广泛应用。
中文分词是具有重要意义的基本任务,对文本分词统计有重要的直接影响,有分词规则的理解。分词主要是基于的分词和分词。基于分词主要是通过维护语句,在切切时,将语句的每个子字符串与表中的词进行匹配则切分,缺少则不切分;根据统计找到的分词,是根据统计规则和语言模型,输出一个特色的最大分词(由于需要的知识尚未讲解,暂且不讨论);混合分词就是各种分词方式混合使用,从而提高分词准确率。下面根据介绍规则的分词顺序。
正向最大匹配法(Forward Maximum Matching,FMM)
是按照从前到后的顺序对语句进行切分,其步骤为:
从左向右取字待分汉语句的m个作为匹配字段,m为词典中词的最长长度;
查找进行词典匹配;
若成功,则作为匹配词切分出去;
若不成功,则将派出一个字,进行剩余的字作为新的匹配;
重复上述过程,直到切分所有词为止。
逆向最大匹配法(Reverse Maximum Matching,RMM)
基本原理与FMM基本相同,不同分的方向开始与是相反。RMM是从待分词的右开始,也就是从向左匹配扫描句子,这时句子取m个字作为匹配的地方,找不到匹配的地方,则把前面的一个字,继续匹配。
进行最大的法匹配,Bi-MM(Bi-MM)
的方向正向匹配最大匹配法向结果的逆向匹配最匹配的规则是:
如果正,相反则取分词结果数不同的词数少的那个;
分词结果结果没有异义任意任意一个;分词中,返回一个不同的字,返回一个不同的主题,
正向最大匹配分词法
# 正向最大匹配分词示例
class MM ( object ): def __init__ ( self ): self.window_size = 3
def cut ( self, text ):
result [文本分词结果
= 0 # 文本长度 = 文本位置
( text_len =) # 长度
dic = [ "苏州" , "苏州市" , "姑苏" , "苏锦" , "零食" , "超市" ]
while text_len start: for size in range (self.window_size + start, start, - 1 )
#取最大长度,逐步比较
piece = text[start:size]
#ifpiece in dic:
#在字典中
result.append(piece)
# 添加到列表
start += len (piece) break else :
#没在字典中,什么都不做
if len (piece) == 1 :
result.append(piece)
# 结果字成词 开始
+= len(片)
break
result.reverse()
返回结果
if __name__ == "__main__":
text = "苏州市苏锦超市"
tk = MM() # 实例化对象
result = tk.cut(text)
print(result)
执行结果:
[ '苏州市', '苏锦', '超市']
逆向最大匹配分词法
# 逆向最大匹配分词示例
class RMM(object):
def __init__(self):
self.window_size = 3
def cut(self, text):
result = [] # 分词结果
start = len(text) # 起始位置
text_len = len(text) # 文本长度
dic = ["苏州", "苏州市", "姑苏", "苏锦", "零食", "超市"]
while start > 0:
for size in range(self.window_size, 0, -1):
piece = text[start-size:start] # 切片
if piece in dic: # 在字典中
result.append(piece) # 添加到列表
start -= len(piece)
break
else: # 没在字典中
if len(piece) == 1:
result.append(piece) # 单个字成词
start -= len(piece)
break
result.reverse()
return result
if __name__ == "__main__":
text = "苏州市姑苏超市"
tk = RMM() # 实例化对象
result = tk.cut(text)
print(result)
执行结果:
[ '苏州', '苏锦', '超市']
Jieba 是一款很简单的功能、功能、使用的中文分词工具库,它提供了分词模式:
句子合理文本模式:将词最合理,适合分析地。
全文:把但中词的所有词组划分出来,速度快,有重复词模式和歧义。
搜索引擎模式:在合理模式基础上,对长词再次切分,提高成绩,适合用于搜索引擎分词。
使用 Jieba 库之前,需要进行安装:
pip install jieba== 0.42 . 1
分词代码如下:
# jieba分词示例
import jieba
text = "吉林市长春药店"
# 全模式
seg_list = jieba.cut(text, cut_all=True)
for word in seg_list:
print(word, end="/")
print()
# 精确模式
seg_list = jieba.cut(text, cut_all=False)
for word in seg_list:
print(word, end="/")
print()
# 搜索引擎模式
seg_list = jieba.cut_for_search(text)
for word in seg_list:
print(word, end="/")
print()
执行结果:
苏州/苏州市/姑苏/苏锦/零食/超市/
苏州市/姑苏/超市/
苏州/苏州市/姑苏/超市/
文本高频支出
# 通过tf-idf提取高频词汇
import glob
import random
import jieba
# 读取文件内容
def get_content(path):
with open(path, "r", encoding="gbk", errors="ignore") as f:
content = ""
for line in f.readlines():
line = line.strip()
content += line
return content
# 统计词频,返回最高前10位词频列表
def get_tf(words, topk=10):
tf_dict = {}
for w in words:
if w not in tf_dict.items():
tf_dict[w] = tf_dict.get(w, 0) + 1 # 获取词频并加1
# 倒序排列
new_list = sorted(tf_dict.items(), key=lambda x: x[1], reverse=True)
return new_list[:topk]
# 去除停用词
def get_stop_words(path):
with open(path, encoding="utf8") as f:
return [line.strip() for line in f.readlines()]
if __name__ == "__main__":
# 样本文件
fname = "d:\\NLP_DATA\\chap_3\\news\\C000008\\11.txt"
# 读取文件内容
corpus = get_content(fname)
# 分词
tmp_list = list(jieba.cut(corpus))
# 去除停用词
stop_words = get_stop_words("d:\\NLP_DATA\\chap_3\\stop_words.utf8")
split_words = []
for tmp in tmp_list:
if tmp not in stop_words:
split_words.append(tmp)
# print("样本:\n", corpus)
print("\n 分词结果: \n" + "/".join(split_words))
# 统计高频词
tf_list = get_tf(split_words)
print("\n top10词 \n:", str(tf_list))
执行结果:
分词结果:
焦点/个股/苏宁/电器/002024/该股/早市/涨停/开盘/其后/获利盘/抛/压下/略有/回落/强大/买盘/推动/下该/股/已经/再次/封于/涨停/主力/资金/积极/拉升/意愿/相当/强烈/盘面/解析/技术/层面/早市/指数/小幅/探低/迅速/回升/中石化/强势/上扬/带动/指数/已经/成功/翻红/多头/实力/之强/令人/瞠目结舌/市场/高度/繁荣/情形/投资者/需谨慎/操作/必竟/持续/上攻/已经/消耗/大量/多头/动能/盘中/热点/来看/相比/周二/略有/退温/依然/看到/目前/热点/效应/外扩散/迹象/相当/明显/高度/活跌/板块/已经/前期/有色金属/金融/地产股/向外/扩大/军工/概念/航天航空/操作/思路/短线/依然/需/规避/一下/技术性/回调/风险/盘中/切记/不可/追高
top10词:
[('已经', 4), ('早市', 2), ('涨停', 2), ('略有', 2), ('相当', 2), ('指数', 2), ('多头', 2), ('高度', 2), ('操作', 2), ('盘中', 2)]
top10词:
[( '已经' , 4 ), ( '指数' , 2 ), ( '涨停' , 2 ) , ('突然' , 2 ), ( '相当' , 2 ), ( '指数' , 2 ), ( '多头' , 2 ), ( '高度' , 2 ), ( '操作' , 2 ), ( '盘中' , 2 )]
什么是词性标注
词性是词性的语法,通常也被称为词类。词性标注是识别给定文本中各种词性的性质。在词性中不同环境中不同的词性,也就是词性的基本特征,也就是词性标注的基本词性。来性很大的困难。
词性标注的原理
词性标注生成方式,将其输入的相同分词序列作为一个序列生成来处理。
词性标注
有一定的标注规范,如将名词、标注、动词表示为“n”、“adj”、“v”等词性。以下是北大词性标注部分词性表示:
Jieba 库提供了词性标注功能,采用结合规则和统计的方式,具体为在词性标注的过程中,词典匹配和 HMM 共同作用。词性标注流程如下:
第一步:根据正则表达式判断文本是否为汉字;
第二步:如果判断为汉字,构建 HMM 模型计算最大概率,在词典中查找分出的词性,若在词典中未找到,则标记为 “未知”;
第三步:若不如何上面的正则表达式,则继续通过正则表达式进行判断,分别赋予 “未知”、” 数词 “或 “英文”。
Jieba 库实现词性标注代码实现
import jieba.posseg as psg
def pos(text):
results = psg.cut(text)
for w, t in results:
print("%s/%s" % (w, t), end=" ")
print("")
text = "呼伦贝尔大草原"
pos(text)
text = "梅兰芳大剧院里星期六晚上有演出"
pos(text)
运行结果:
呼伦贝尔/nr 大/a 草原/n
梅兰芳/nr 大/a 剧院/n 里/f 星期六/t 晚上/t 有/v 演出/v
命名实体识别(Named Entities Recognition,NER)
也是自然语言处理的一个基础任务,是信息抽取
、信息检索
、机器翻译
、问答系统
等多种自然语言处理技术必不可少的组成部分。其目的是识别语料中人名、地名、组织机构名等命名实体,实体类型包括 3 大类(实体类、时间类和数字类)和 7 小类(人名、地名、组织机构名、时间、日期、货币和百分比)。中文命名实体识别主要有以下难点:
1、各类命名实体的数量众多。
2、命名实体的构成规律复杂。
3、嵌套情况复杂。
4、长度不确定。
命名实体识别方法有:
1、基于规则的命名实体识别。规则加词典是早期命名实体识别中最行之有效的方式。其依赖手工规则的系统,结合命名实体库,对每条规则进行权重赋值,然后通过实体与规则的相符情况来进行类型判断。这种方式可移植性差、更新维护困难等问题。
2、基于统计的命名实体识别。基于统计的命名实体识别方法有:隐马尔可夫模型、最大熵模型、条件随机场等。其主要思想是基于人工标注的语料,将命名实体识别任务作为序列标注问题来解决。基于统计的方法对语料库的依赖比较大,而可以用来建设和评估命名实体识别系统的大规模通用语料库又比较少,这是该方法的一大制约。
3、基于深度学习的方法。利用深度学习模型,预测词(或字)是否为命名实体,并预测出起始、结束位置。
4、混合方法。将前面介绍的方法混合使用。
命名实体识别在深度学习部分有专门案例进行探讨和演示。
关键词提取是提取出代表文章重要内容的一组词,对文本聚类
、分类
、自动摘要
起到重要作用。此外,关键词提取还能使人们便捷地浏览和获取信息。现实中大量文本不包含关键词,自动提取关检测技术具有重要意义和价值。关键词提取包括有监督学习、无监督学习方法两类。
有监督关键词提取
。该方法主要通过分类方式进行,通过构建一个较为丰富完整的词表,然后通过判断每个文档与词表中每个词的匹配程度,以类似打标签的方式,达到关键词提取的效果。该方法能获取较高的精度,但需要对大量样本进行标注,人工成本过高。另外,现在每天都有大量新的信息出现,固定词表很难将新信息内容表达出来,但人工实时维护词表成本过高。所以,有监督学习关键词提取方法有较明显的缺陷。
无监督关键词提取
。相对于有监督关键词提取,无监督方法对数据要求低得多,既不需要人工维护词表,也不需要人工标注语料辅助训练。因此,在实际应用中更受青睐。这里主要介绍无监督关键词提取算法,包括 TF-IDF 算法,TextRank 算法和主题模型算法。
TF-IDF(Term Frequency-Inverse Document Frequency,词频 - 逆文档频率)
是一种基于传统的统计计算方法,常用于评估一个文档集中一个词对某份文档的重要程度。其基本思想是:一个词语在文档中出现的次数越多、出现的文档越少,语义贡献度越大(对文档区分能力越强)。TF-IDF 表达式由两部分构成,词频
、逆文档频率
。词频定义为:
T F i j = n j i ∑ k n k j TF_{ij} = \frac{n_{ji}}{\sum_k n_{kj}} TFij=∑knkjnji
其中, n i j n_{ij} nij表示词语 i 在文档 j 中出现的次数,分母 ∑ k n k j \sum_k n_{kj} ∑knkj表示所有文档总次数。逆文档频率定义为:
I D F i = l o g ( ∣ D ∣ ∣ D i ∣ + 1 ) IDF_i = log(\frac{|D|}{|D_i| + 1}) IDFi=log(∣Di∣+1∣D∣)
其中, ∣ D ∣ |D| ∣D∣为文档总数, D i D_i Di为文档中出现词 i 的文档数量,分母加 1 是避免分母为 0 的情况(称为拉普拉斯平滑),TF-IDF 算法是将 TF 和 IDF 综合使用,表达式为:
T F − I D F = T F i j × I D F i = n j i ∑ k n k j × l o g ( ∣ D ∣ ∣ D i ∣ + 1 ) TF-IDF = TF_{ij} \times IDF_i =\frac{n_{ji}}{\sum_k n_{kj}} \times log(\frac{|D|}{|D_i| + 1}) TF−IDF=TFij×IDFi=∑knkjnji×log(∣Di∣+1∣D∣)
由公式可知,词频越大,该值越大;出现的文档数越多(说明该词越通用),逆文档频率越接近 0,语义贡献度越低。例如有以下文本:
世界献血日,学校团体、献血服务志愿者等可到血液中心参观检验加工过程,我们会对检验结果进行公示,同时血液的价格也将进行公示。
以上文本词语总数为 30,计算几个词的词频:
假设出现献血、血液、进行、公示文档数量分别为 10、15、100、50,根据 TF-IDF 计算公式,得:
“献血”、“血液” 的 TF-IDF 值最高,所以为最适合这篇文档的关键词。
与 TF-IDF
不一样,TextRank 算法
可以脱离于语料库,仅对单篇文档进行分析就可以提取该文档的关键词,这也是 TextRank 算法
的一个重要特点。TextRank 算法
最早用于文档的自动摘要,基于句子维度的分析,利用算法对每个句子进行打分,挑选出分数最高的 n 个句子作为文档的关键句,以达到自动摘要的效果。
TextRank 算法
的基本思想来源于Google
的 PageRank 算法
,该算法是 Google 创始人拉里・佩奇和希尔盖・布林于 1997 年构建早期的搜索系统原型时提出的链接分析法,用于评价搜索系统各覆盖网页重要性的一种方法。随着 Google 的成功,该算法也称为其它搜索引擎和学术界十分关注的计算模型。
PageRank 基本思想有两条:
链接数量。一个网页被越多的其它网页链接,说明这个网页越重要。
链接质量。一个网页被一个越高权值的网页链接,也能表名这个网页越重要。
基于上述思想,一个网页的 PageRank 计算公式可以表示为:
S ( V i ) = ∑ j ∈ I n ( V i ) ( 1 O u t ( V j ) × S ( V j ) ) S(V_i) = \sum_{j \in In(V_i)} \Bigg( \frac{1}{Out(V_j)} \times S(V_j) \Bigg) S(Vi)=∑j∈In(Vi)(Out(Vj)1×S(Vj))
其中, I n ( V i ) In(V_i) In(Vi) 为 V i V_i Vi的入链集合, O u t ( V j ) Out(V_j) Out(Vj) 为 V j V_j Vj的出链集合, ∣ O u t ( V j ) ∣ |Out(V_j)| ∣Out(Vj)∣为出链的数量。因为每个网页要将它自身的分数平均贡献给每个出链,则 ( 1 O u t ( V j ) × S ( V j ) ) \Bigg( \frac{1}{Out(V_j)} \times S(V_j) \Bigg) (Out(Vj)1×S(Vj)) 即为 V i V_i Vi贡献给 V j V_j Vj的分数。将所有入链贡献给它的分数全部加起来,就是 V i V_i Vi自身的得分。算法开始时,将所有页面的得分均初始化为 1。
对于一些孤立页面,可能链入、链出的页面数量为 0,为了避免这种情况,对公式进行了改造,加入了一个阻尼系数 d d d,这样,即使孤立页面也有一个得分。改造后的公式如下:
S ( V i ) = ( 1 − d ) + d × ∑ j ∈ I n ( V i ) ( 1 O u t ( V j ) × S ( V j ) ) S(V_i) = (1 - d) + d \times \sum_{j \in In(V_i)} \Bigg( \frac{1}{Out(V_j)} \times S(V_j) \Bigg) S(Vi)=(1−d)+d×∑j∈In(Vi)(Out(Vj)1×S(Vj))
以上就是 PageRank 的理论,也是 TextRank 的理论基础,不同于的是 TextRank 不需要与文档中的所有词进行链接,而是采用一个窗口大小,在窗口中的词互相都有链接关系。例如对下面的文本进行窗口划分:
世界献血日,学校团体、献血服务志愿者等可到血液中心参观检验加工过程,我们会对检验结果进行公示,同时血液的价格也将进行公示。
如果将窗口大小设置为 5,则可得到如下计算窗口:
[世界,献血,日,学校,团体]
[献血,日,学校,团体,献血]
[日,学校,团体,献血,服务]
[学校,团体,献血,服务,志愿者]
……
每个窗口内所有词之间都有链接关系,如 [世界] 和 [献血,日,学校,团体] 之间有链接关系。得到了链接关系,就可以套用 TextRank 公式,计算每个词的得分,最后选择得分最高的 N 个词作为文档的关键词。
关键词提取示例
本案例演示了通过自定义 TF-IDF
、调用TextRank API
实现关键字提取:
# -*- coding: utf-8 -*-
import math
import jieba
import jieba.posseg as psg
from gensim import corpora, models
from jieba import analyse
import functools
import numpy as np
# 停用词表加载方法
def get_stopword_list():
# 停用词表存储路径,每一行为一个词,按行读取进行加载
# 进行编码转换确保匹配准确率
stop_word_path = '../data/stopword.txt'
with open(stop_word_path, "r", encoding="utf-8") as f:
lines = f.readlines()
stopword_list = [sw.replace('\n', '') for sw in lines]
return stopword_list
# 去除停用词
def word_filter(seg_list):
filter_list = []
for word in seg_list:
# 过滤停用词表中的词,以及长度为<2的词
if not word in stopword_list and len(word) > 1:
filter_list.append(word)
return filter_list
# 数据加载,pos为是否词性标注的参数,corpus_path为数据集路径
def load_data(corpus_path):
# 调用上面方式对数据集进行处理,处理后的每条数据仅保留非干扰词
doc_list = []
for line in open(corpus_path, 'r', encoding='utf-8'): # 循环读取一行(一行即一个文档)
content = line.strip() # 去空格
seg_list = jieba.cut(content) # 分词
filter_list = word_filter(seg_list) # 去除停用词
doc_list.append(filter_list) # 将分词后的内容添加到列表
return doc_list
# idf值统计方法
def train_idf(doc_list):
idf_dic = {}
tt_count = len(doc_list) # 总文档数
# 每个词出现的文档数
for doc in doc_list:
doc_set = set(doc) # 将词推入集合去重
for word in doc_set: # 词语在文档中
idf_dic[word] = idf_dic.get(word, 0.0) + 1.0 # 文档数加1
# 按公式转换为idf值,分母加1进行平滑处理
for word, doc_cnt in idf_dic.items():
idf_dic[word] = math.log(tt_count / (1.0 + doc_cnt))
# 对于没有在字典中的词,默认其仅在一个文档出现,得到默认idf值
default_idf = math.log(tt_count / (1.0))
return idf_dic, default_idf
# TF-IDF类
class TfIdf(object):
def __init__(self, idf_dic, default_idf, word_list, keyword_num):
"""
TfIdf类构造方法
:param idf_dic: 训练好的idf字典
:param default_idf: 默认idf值
:param word_list: 待提取文本
:param keyword_num: 关键词数量
"""
self.word_list = word_list
self.idf_dic, self.default_idf = idf_dic, default_idf # 逆文档频率
self.tf_dic = self.get_tf_dic() # 词频
self.keyword_num = keyword_num
# 统计tf值
def get_tf_dic(self):
tf_dic = {} # 词频字典
for word in self.word_list:
tf_dic[word] = tf_dic.get(word, 0.0) + 1.0
total = len(self.word_list) # 词语总数
for word, word_cnt in tf_dic.items():
tf_dic[word] = float(word_cnt) / total
return tf_dic
# 按公式计算tf-idf
def get_tfidf(self):
tfidf_dic = {}
for word in self.word_list:
idf = self.idf_dic.get(word, self.default_idf)
tf = self.tf_dic.get(word, 0)
tfidf = tf * idf # 计算TF-IDF
tfidf_dic[word] = tfidf
# 根据tf-idf排序,去排名前keyword_num的词作为关键词
s_list = sorted(tfidf_dic.items(), key=lambda x: x[1], reverse=True)
# print(s_list)
top_list = s_list[:self.keyword_num] # 切出前N个
for k, v in top_list:
print(k + ", ", end='')
print()
def tfidf_extract(word_list, keyword_num=20):
doc_list = load_data('../data/corpus.txt') # 读取文件内容
# print(doc_list)
idf_dic, default_idf = train_idf(doc_list) # 计算逆文档频率
tfidf_model = TfIdf(idf_dic, default_idf, word_list, keyword_num)
tfidf_model.get_tfidf()
def textrank_extract(text, keyword_num=20):
keywords = analyse.textrank(text, keyword_num)
# 输出抽取出的关键词
for keyword in keywords:
print(keyword + ", ", end='')
print()
if __name__ == '__main__':
global stopword_list
text = """在中国共产党百年华诞的重要时刻,在“两个一百年”奋斗目标历史交汇关键节点,
党的十九届六中全会的召开具有重大历史意义。全会审议通过的《决议》全面系统总结了党的百年奋斗
重大成就和历史经验,特别是着重阐释了党的十八大以来党和国家事业取得的历史性成就、发生的历史性变革,
充分彰显了中国共产党的历史自觉与历史自信。"""
stopword_list = get_stopword_list()
seg_list = jieba.cut(text) # 分词
filter_list = word_filter(seg_list)
# TF-IDF提取关键词
print('TF-IDF模型结果:')
tfidf_extract(filter_list)
# TextRank提取关键词
print('TextRank模型结果:')
textrank_extract(text)
运行结果:
TF-IDF模型结果:
历史, 中国共产党, 百年, 历史性, 华诞, 一百年, 奋斗目标, 交汇, 节点, 十九, 六中全会, 全会, 奋斗, 重大成就, 着重, 阐释, 十八, 党和国家, 成就, 变革,
TextRank模型结果:
历史, 历史性, 意义, 成就, 决议, 审议, 发生, 系统, 总结, 全面, 节点, 关键, 交汇, 召开, 具有, 全会, 取得, 事业, 自信, 变革,
数据集介绍:包含 5000 份正常邮件、5001 份垃圾邮件的样本
文本特征处理方式:采用 TF-IDF 作为文本特征值
模型选择:朴素贝叶斯、支持向量机模型
基本流程:读取数据 → 去除停用词和特殊符号 → 计算 TF-IDF 特征值 → 模型训练 → 预测 → 打印结果
# -*- coding: utf-8 -*-
# 利用TF-IDF特征、朴素贝叶斯/支持向量机实现垃圾邮件分类
import numpy as np
import re
import string
import sklearn.model_selection as ms
from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import SGDClassifier
from sklearn import metrics
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
label_name_map = ["垃圾邮件", "正常邮件"]
# 分词
def tokenize_text(text):
tokens = jieba.cut(text) # 分词
tokens = [token.strip() for token in tokens] # 去空格
return tokens
def remove_special_characters(text):
tokens = tokenize_text(text)
# escape函数对字符进行转义处理
# compile函数用于编译正则表达式,生成一个 Pattern 对象
pattern = re.compile('[{}]'.format(re.escape(string.punctuation)))
# filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表
# sub函数进行正则匹配字符串替换
filtered_tokens = filter(None, [pattern.sub('', token) for token in tokens])
filtered_text = ' '.join(filtered_tokens)
return filtered_text
# 去除停用词
def remove_stopwords(text):
tokens = tokenize_text(text) # 分词、去空格
filtered_tokens = [token for token in tokens if token not in stopword_list] # 去除停用词
filtered_text = ''.join(filtered_tokens)
return filtered_text
# 规范化处理
def normalize_corpus(corpus):
result = [] # 处理结果
for text in corpus: # 遍历每个词汇
text = remove_special_characters(text) # 去除标点符号
text = remove_stopwords(text) # 去除停用词
result.append(text)
return result
def tfidf_extractor(corpus):
vectorizer = TfidfVectorizer(min_df=1,
norm='l2',
smooth_idf=True,
use_idf=True)
features = vectorizer.fit_transform(corpus)
return vectorizer, features
def get_data():
'''
获取数据
:return: 文本数据,对应的labels
'''
corpus = [] # 邮件内容
labels = [] # 标签(0-垃圾邮件 1-正常邮件)
# 正常邮件
with open("data/ham_data.txt", encoding="utf8") as f:
for line in f.readlines():
corpus.append(line)
labels.append(1)
# 垃圾邮件
with open("data/spam_data.txt", encoding="utf8") as f:
for line in f.readlines():
corpus.append(line)
labels.append(0)
return corpus, labels
# 过滤空文档
def remove_empty_docs(corpus, labels):
filtered_corpus = []
filtered_labels = []
for doc, label in zip(corpus, labels):
if doc.strip():
filtered_corpus.append(doc)
filtered_labels.append(label)
return filtered_corpus, filtered_labels
# 计算并打印分类指标
def print_metrics(true_labels, predicted_labels):
# Accuracy
accuracy = metrics.accuracy_score(true_labels, predicted_labels)
# Precision
precision = metrics.precision_score(true_labels,
predicted_labels,
average='weighted')
# Recall
recall = metrics.recall_score(true_labels,
predicted_labels,
average='weighted')
# F1
f1 = metrics.f1_score(true_labels,
predicted_labels,
average='weighted')
print("正确率: %.2f, 查准率: %.2f, 召回率: %.2f, F1: %.2f" % (accuracy, precision, recall, f1))
if __name__ == "__main__":
global stopword_list
# 读取停用词
with open("dict/stop_words.utf8", encoding="utf8") as f:
stopword_list = f.readlines()
corpus, labels = get_data() # 加载数据
corpus, labels = remove_empty_docs(corpus, labels)
print("总的数据量:", len(labels))
# 打印前N个样本
for i in range(10):
print("label:", labels[i], " 邮件内容:", corpus[i])
# 对数据进行划分
train_corpus, test_corpus, train_labels, test_labels = \
ms.train_test_split(corpus,
labels,
test_size=0.10,
random_state=36)
# 规范化处理
norm_train_corpus = normalize_corpus(train_corpus)
norm_test_corpus = normalize_corpus(test_corpus)
# tfidf 特征
## 先计算tf-idf
tfidf_vectorizer, tfidf_train_features = tfidf_extractor(norm_train_corpus)
## 再用刚刚训练的tf-idf模型计算测试集tf-idf
tfidf_test_features = tfidf_vectorizer.transform(norm_test_corpus)
# print(tfidf_test_features)
# print(tfidf_test_features)
# 基于tfidf的多项式朴素贝叶斯模型
print("基于tfidf的贝叶斯模型")
nb_model = MultinomialNB() # 多分类朴素贝叶斯模型
nb_model.fit(tfidf_train_features, train_labels) # 训练
mnb_pred = nb_model.predict(tfidf_test_features) # 预测
print_metrics(true_labels=test_labels, predicted_labels=mnb_pred) # 打印测试集下的分类指标
print("")
# 基于tfidf的支持向量机模型
print("基于tfidf的支持向量机模型")
svm_model = SGDClassifier()
svm_model.fit(tfidf_train_features, train_labels) # 训练
svm_pred = svm_model.predict(tfidf_test_features) # 预测
print_metrics(true_labels=test_labels, predicted_labels=svm_pred) # 打印测试集下的分类指标
print("")
# 打印测试结果
num = 0
for text, label, pred_lbl in zip(test_corpus, test_labels, svm_pred):
print('真实类别:', label_name_map[int(label)], ' 预测结果:', label_name_map[int(pred_lbl)])
print('邮件内容【', text.replace("\n", ""), '】')
print("")
num += 1
if num == 10:
break
运行结果:
基于tfidf的贝叶斯模型
正确率: 0.97, 查准率: 0.97, 召回率: 0.97, F1: 0.97
基于tfidf的支持向量机模型
正确率: 0.98, 查准率: 0.98, 召回率: 0.98, F1: 0.98
真实类别: 正常邮件 预测结果: 正常邮件
邮件内容【 分专业吧,也分导师吧 标 题: Re: 问一个:有人觉得自己博士能混毕业吗 当然很好混毕业了 : 博士读到快中期了,始终感觉什么都不会,文章也没发几篇好的,论文的架构也没有, : 一切跟刚上的时候没有区别。但是事实上我也很辛苦的找资料,做实验,还进公司实习过, : 现在感觉好失败,内心已经放弃了,打算混毕业,不知道过来人有什么高招,请指点一二。 -- 】
真实类别: 垃圾邮件 预测结果: 垃圾邮件
邮件内容【 您好! 我公司有多余的发票可以向外代开!(国税、地税、运输、广告、海关缴款书)。 如果贵公司(厂)有需要请来电洽谈、咨询! 联系电话: 01521025**** 大白 谢谢 顺祝商祺! 】
1、One-hot
One-hot(独热)编码是一种最简单的文本表示方式。如果有一个大小为 V 的词表,对于第 i 个词 w i w_i wi,可以用一个长度为 V 的向量来表示,其中第 i 个元素为 1,其它为 0. 例如:
减肥:[1, 0, 0, 0, 0]
瘦身:[0, 1, 0, 0, 0]
增重:[0, 0, 1, 0, 0]
One-hot 词向量构建简单,但也存在明显的弱点:
维度过高。如果词数量较多,每个词需要使用更长的向量表示,造成维度灾难;
稀疏矩阵。每个词向量,其中只有一位为 1,其它位均为零;
语义鸿沟。词语之间的相似度、相关程度无法度量。
词袋模型 (Bag-of-words model,BOW),BOW 模型假定对于一个文档,忽略它的单词顺序和语法、句法等要素,将其仅仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的,不依赖于其它单词是否出现。例如:
我把他揍了一顿,揍得鼻青眼肿
他把我走了一顿,揍得鼻青眼肿
构建一个词典:
{"我":0, "把":1, "他":2, "揍":3, "了":4 "一顿":5, "鼻青眼肿":6, "得":7}
再将句子向量化,维数和字典大小一致,第 i 维上的数值代表 ID 为 i 的词在句子里出现的频次,两个句子可以表示为:
[1, 1, 1, 2, 1, 1, 1, 1]
[1, 1, 1, 2, 1, 1, 1, 1]
词袋模型表示简单,但也存在较为明显的缺点:
丢失了顺序和语义。顺序是极其重要的语义信息,词袋模型只统计词语出现的频率,忽略了词语的顺序。例如上述两个句子意思相反,但词袋模型表示却完全一致;
高维度和稀疏性。当语料增加时,词袋模型维度也会增加,需要更长的向量来表示。但大多数词语不会出现在一个文本中,所以导致矩阵稀疏。
TF-IDF
TF-IDF(Term Frequency-Inverse Document Frequency,词频 - 逆文档频率)
是一种基于传统的统计计算方法,常用于评估一个文档集中一个词对某份文档的重要程度。其基本思想是:一个词语在文档中出现的次数越多、出现的文档越少,语义贡献度越大(对文档区分能力越强)。其表达式为:
T F − I D F = T F i j × I D F i = n j i ∑ k n k j × l o g ( ∣ D ∣ ∣ D i ∣ + 1 ) TF-IDF = TF_{ij} \times IDF_i =\frac{n_{ji}}{\sum_k n_{kj}} \times log(\frac{|D|}{|D_i| + 1}) TF−IDF=TFij×IDFi=∑knkjnji×log(∣Di∣+1∣D∣)
该指标依然无法保留词语在文本中的位置关系。该指标前面有过详细讨论,此处不再赘述。
共现矩阵
共现(co-occurrence)矩阵
指通过统计一个事先指定大小的窗口内的词语共现次数,以词语周边的共现词的次数做为当前词语的向量。具体来说,我们通过从大量的语料文本中构建一个共现矩阵来表示词语。例如,有语料如下:
I like deep learning.
I like NLP.
I enjoy flying.
则共现矩阵表示为:
矩阵定义的词向量在一定程度上缓解了 one-hot 向量相似度为 0 的问题,但没有解决数据稀疏性和维度灾难的问题。
这里有各种学习资料还有有有趣好玩的编程项目,更有难寻的各种资源。