NLP入门——基于TF-IDF算法的应用

从json格式数据中抽出句子和标签

首先查看json格式的数据文件:

:~/nlp/tnews/src$ less train.json

在这里插入图片描述
可以看到json字符串表示一个对象,我们利用json.loads() 函数会将其转换为一个 Python 字典。docs python json

#ext.py
#encoding: utf-8import sys
from json import loadsdef handle(srcf, rsf, rstf):#打开三个文件,将句子写入rsf,将标签写入rstfens = "\n".encode("utf-8") with open(srcf,"rb") as frd, open(rsf,"wb") as fwrts, open(rstf, "wb") as fwrtt:for line in frd:tmp = line.strip()if tmp:tmp = loads(tmp.decode("utf-8"))#将json对象取出转换成字典src, tgt = tmp.get("sentence",""), tmp.get("label_desc", "")#python dict的get方法,取出sentence标签的值,若不存在则返回空串if src and tgt: #若非空fwrts.write(src.encode("utf-8"))fwrts.write(ens)fwrtt.write(tgt.encode("utf-8"))fwrtt.write(ens)if __name__=="__main__":handle(*sys.argv[1:])

在命令行输入:

:~/nlp/tnews/src$ python ext.py train.json src.train.txt tgt.train.txt 
:~/nlp/tnews/src$ python ext.py dev.json src.dev.txt tgt.dev.txt 

得到的分别为原数据中训练集以及验证集中的句子和标签,src.文件中每个句子占一行,tgt.文件中每个句子的类别占一行。

对训练集和验证集做数据的预处理

随后我们对这四个提取出来的数据文件做数据预处理的编码规范化以及繁体转简体,得到四个预处理后的文件:

:~/nlp/tnews$ python ~/bin/text-tools/data/normu8.py src.train.txt - | python ~/bin/text-tools/data/zht2s.py - src.train.s.txt &
:~/nlp/tnews$ python ~/bin/text-tools/data/normu8.py src.dev.txt - | python ~/bin/text-tools/data/zht2s.py - src.dev.s.txt &
:~/nlp/tnews$ python ~/bin/text-tools/data/normu8.py tgt.dev.txt - | python ~/bin/text-tools/data/zht2s.py - tgt.dev.s.txt &
:~/nlp/tnews$ python ~/bin/text-tools/data/normu8.py tgt.train.txt - | python ~/bin/text-tools/data/zht2s.py - tgt.train.s.txt &
def handle(srcf, rsf, d="t2s"):with sys.stdin.buffer if srcf == "-" else open(srcf, "rb") as frd, sys.stdout.buffer if rsf == "-" else open(rsf, "wb") as fwrt:

normu8.pyzht2s.py中添加以上代码,如果输入是字符‘-’则从输入缓冲区中读文件,如果输出文件是字符‘-’则将文件写入输出缓冲区中。
这样做的好处是可以将文件在命令行中串起来,避免生成中间我们不需要的文件。例如:

python normu8.py tgt.train.txt - | python zht2s.py - tgt.train.s.txt &

顺序执行命令行,normu8.py输入文件是tgt.train.txt,输出文件是-,那么就把经过处理的输入文件输出到sys.stdout.buffer缓冲区中,利用管道线将前半部分的输出给后半部分的输入,执行zht2s.py输入是-,则从sys.stdin.buffer缓冲区中读入,输出文件是tgt.train.s.txt,则写入到这个文件中。
缓冲区在内存中,在缓冲区操作比文件操作要快很多。

执行预处理后查看他们的行号,发现行号正常:

:~/nlp/tnews/src$ wc -l *.s.txt10000 src.dev.s.txt53360 src.train.s.txt10000 tgt.dev.s.txt53360 tgt.train.s.txt126720 总计

接着,对编码规范化后的文件分词并查看分词后的词表大小:

 :~/nlp/tnews/src$ python ~/nlp/token/seg.py src.train.s.txt src.train.c.txt &:~/nlp/tnews/src$ python ~/nlp/token/seg.py src.dev.s.txt src.dev.c.txt:~/nlp/tnews/src$ python ~/nlp/token/vcb.py src.train.c.txt 
67733

对训练集和验证集做子词切分

:~/nlp/tnews/src$ subword-nmt learn-bpe -s 8000 < src.train.c.txt > src.cds

由于文件词表本身的长度较小,因此合并操作不能太多,我们设置学习8000个词,随后利用得到的词表进行子词切分,并得到每个子词的频率src.bvcb

:~/nlp/tnews/src$ subword-nmt apply-bpe -c src.cds < src.train.c.txt | subword-nmt get-vocab > src.bvcb

最后我们设置阈值8,利用学习到的src.bvcb子词频率表来对整个子词表进行过滤,得到最终完整的经过bpe算法处理后的文件:

:~/nlp/tnews/src$ subword-nmt apply-bpe -c src.cds --vocabulary src.bvcb --vocabulary-threshold  8 < src.train.c.txt > src.train.bpe.txt
:~/nlp/tnews/src$ subword-nmt apply-bpe -c src.cds --vocabulary src.bvcb --vocabulary-threshold  8 < src.dev.c.txt > src.dev.bpe.txt

我们可以查看bpe算法后的分词效果:

less src.train.bpe.txt

在这里插入图片描述

less src.dev.bpe.txt

在这里插入图片描述

统计词表中每个子词在该类别出现的频率

#p.train.py
#encoding: utf-8import sys
from json import dump
from math import logdef count(srcf, tgtf):#{class: {word: freq}} #model为字典嵌套,外层为每个类别,内层统计每个类别出现的子词频率model = {}with open(srcf,"rb") as fsrc, open(tgtf,"rb") as ftgt:for sline, tline in zip(fsrc, ftgt):_s, _t = sline.strip(), tline.strip()if _s and _t:_s, _class = _s.decode("utf-8"), _t.decode("utf-8")if _class not in model:#如果model中没有这个类别model[_class] = {}#为model中这个类别作初始化_ = model[_class]#取出model中_class的类别的字典for word in _s.split():#遍历一行里面空格隔开的每个分词_[word] = _.get(word,0) + 1 #出现一次则将分词的词频+1return model              def log_normalize(modin):#将子词出现次数转化成频率rs = {}for _class, v in modin.items():#遍历整个词典;键为modin.keys(),值为modin.values()_ = float(sum(v.values()))#统计这个类别中所有子词出现的总个数,转化成浮点数rs[_class] = {word: log(freq / _) for word, freq in v.items()}#遍历所有的子词,用每个子词出现的频率除以总数#为了防止溢出,将频率取对数return rs#得到的rs字典即为每个子词在这个类别中出现的频率def save(modin, frs):with open(frs, "w") as f:dump(modin, f) #用dump方法向文件写strif __name__=="__main__":save(log_normalize(count(*sys.argv[1:3])),sys.argv[3])#第一和第二个文件传给count,之后将返回model字典传给log_normalize,再将第三个写入文件传给save保存

为了防止因为频率太低导致的精度丢失,我们将频率取对数后存储成json格式文件:

:~/nlp/tnews/src$ python p.train.py src.train.bpe.txt  tgt.train.s.txt logp.model.txt

查看logp.model.txt文件,由于log(a)+log(b)=log(ab),我们可以采用对取log后的频率相加得到组合子词的概率对数:
在这里插入图片描述

对每行每句话的类别作预测、比较准确率

#pnorm.predict.py
#encoding: utf-8import sys
from json import loads
from math import infdef load(fname):with open(fname, "r") as f:model = loads(f.read())#从json对象取出转化成字典return model#model: {class: {word,logp}}    
def predict_instance(lin, model):_max_score, _max_class = -inf, Nonefor _class, _cd in model.items():#取出 类别 和 {子词 : log频率}#_s = sum([_cd.get(word, 0.0) for word in lin])_s = 0.0    #记录总分数_n = 0      #记录总词数for word in lin:        #遍历每行的分词if word in _cd:     #如果分词在分词表里面_s += _cd[word] #累加总频率_n += 1         #总词数+1if _s > 0.0:            #排除n=0的情况_s = _s / _n        #求平均分数if _s > _max_score: #更新每行出现词标签频率最高的logp以及类别_max_class = _class_max_score = _sif _max_class is None:_max_class = _classreturn _max_class, _max_score#返回封装成一个tuple:(_max_class,_max_score)def predict(fsrc, fmodel, frs):model = load(fmodel)ens = '\n'.encode("utf-8")with open(fsrc, "rb") as frd, open(frs, "wb") as fwrt:for line in frd:_ = line.strip() if _: #将每行数据传入predict_instance()函数_ =predict_instance( _.decode("utf-8").split(), model)[0]#取出类别_max_classfwrt.write(_.encode("utf-8")) #将类别写入文件,作为每行(每句话)的预测类别fwrt.write(ens) if __name__=="__main__":predict(*sys.argv[1:])

我们对整个model文件进行遍历,统计每句话的分词出现的频率,在遍历完整个model后,会得到一句话中分词出现频率最高的情况,将这个分词属于的类别标记为这句话的类别。

在命令行执行输入以下代码,src.dev.bpe.txt是分词后的验证集;模型文件是logp.model.txt,是json格式的文件;最后pred.dev.txt 是预测的标签写入的文件。

:~/nlp/tnews$ python pnorm.predict.py src.dev.bpe.txt logp.model.txt pred.dev.txt 

查看pred.dev.txt文件:
在这里插入图片描述
可以看到,预测是股票类的句子最多。下面我们统计对比预测的准确率:

#acc.py
#encoding: utf-8
import sysdef handle(predf, ref):t = c = 0 #t是总标签数,c是预测正确的标签数with open(predf, "rb") as fp, open(ref, "rb") as fr:for lp, lr in zip(fp, fr):_p, _r = lp.strip(), lr.strip()if _p and _r:_p, _r = _p.decode("utf-8"), _r.decode("utf-8")if _p == _r:c += 1 #如果预测正确,累加ct += 1 #统计总标签数return float(c) / t *100.0 #返回预测的准确率if __name__ == "__main__":print(handle(*sys.argv[1:]))

第一个输入文件是预测的标签文件,第二个是真实的验证集标签:

:~/nlp/tnews$ python acc.py pred.dev.txt  tgt.dev.s.txt 
0.44999999999999996

预测的准确率大约是是0.45%,这个准确率是很低的。

准确率分析

将模型加载到内存中,我们查看模型文件的全部类别:

>>> from predict import load
>>> model=load("logp.model.txt")
>>> model.keys()
dict_keys(['news_edu', 'news_finance', 'news_house', 'news_travel', 'news_tech', 'news_sports', 'news_game', 'news_culture', 'news_car', 'news_story', 'news_entertainment', 'news_military', 'news_agriculture', 'news_world', 'news_stock'])

接着,我们查看逗号这个分词在不同类别中的频率:

>>> model["news_edu"][',']
-3.3306962741282318
>>> model["news_finance"][',']
-3.507571369597401
>>> model["news_house"][',']
-3.1864756857742638

可以看到,由于log函数的单调性,在以上三个类别中逗号在news_house标签中的频率最高,因此对最终句子标签的判定产生影响。
因为 ',' 实际上并不具有标签的意义,只是作为标点符号存在,但在我们的模型以及判定方法中,对结果产生了不良影响。又由于逗号大量的存在,故使得准确率大大降低。

TF-IDF算法

TF(Term Frequency)即词频,表示某一词在某类文档中出现的频率,我们上文所求的logp中的p即为TF,公式是:
T F = 某类词中某个词出现的次数 这类词的总词数 TF=\frac {某类词中某个词出现的次数}{这类词的总词数} TF=这类词的总词数某类词中某个词出现的次数
上面的例子中,逗号在news_house标签中的频率最高,TF值最大,因此TF认为逗号是此类别的单词。

IDF(Inverse Document Frequency)即逆向文件频率,表示一个词在整个语料库中的普遍性。IDF值越高,说明其频率越低,越能代表不同的文档。公式为:
I D F ( t ) = log ⁡ ( N d t ) IDF(t)= \log(\frac{N}{d_t}) IDF(t)=log(dtN)
N为文档总数,d_t为包含词语的文档数。上面的例子中,逗号广泛存在于几乎所有的类别文本中。因此,他的IDF值会极低,甚至接近于0.

TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。 T F − I D F = T F ∗ I D F TF-IDF=TF*IDF TFIDF=TFIDF
这样,对逗号的TF值乘以IDF值作为总体的值,则最终值会受到IDF值的影响而变得极小。则逗号就会大大减弱用其来分类文本的作用。

我们修改p.train.py,来求每个子词的 TF-IDF值,我们将类别的粒度设置在class,也就是说N为15(总标签数),d_t为每个子词拥有的标签数。

#tfidf.train.py
#encoding: utf-8import sys
from json import dump
from math import logdef count(srcf, tgtf):#{class: {word: freq}} #model为字典嵌套,外层为每个类别,内层统计每个类别出现的子词频率model = {}with open(srcf,"rb") as fsrc, open(tgtf,"rb") as ftgt:for sline, tline in zip(fsrc, ftgt):_s, _t = sline.strip(), tline.strip()if _s and _t:_s, _class = _s.decode("utf-8"), _t.decode("utf-8")if _class not in model:#如果model中没有这个类别model[_class] = {}#为model中这个类别作初始化_ = model[_class]#取出model中_class的类别的字典for word in _s.split():#遍历一行里面空格隔开的每个分词_[word] = _.get(word,0) + 1 #出现一次则将分词的词频+1return model              def getidf(modin):rs = {}for v in modin.values():for word in v.keys(): if word in rs:continue for _class in modin.keys():dic = modin[_class]if word in dic :rs[word] = rs.get(word,0) + 1_ = len(modin) #统计所有类别的个数return {word: -log(freq / _) for word, freq in rs.items()}# 返回{分词:分词出现的类别数/总类别数}def tfidf(modin)idf = getidf(modin)rs = {}for _class, v in modin.items():#遍历整个词典;键为modin.keys(),值为modin.values()_ = float(sum(v.values()))#统计这个类别中所有子词出现的总个数,转化成浮点数rs[_class] = {word: (freq / _ * idf[word]) for word, freq in v.items()}#tf * idf return rs#得到的rs字典即为每个子词的 tf * idf 值def save(modin, frs):with open(frs, "w") as f:dump(modin, f) #用dump方法向文件写strif __name__=="__main__":save(tfidf(count(*sys.argv[1:3])),sys.argv[3])

和仅基于TF算模型的步骤相似,在命令行输入:

:~/nlp/tnews$ python tfidf.train.py src.train.bpe.txt tgt.train.s.txt model/tfidf/model.txt
:~/nlp/tnews$ python predict.py src.dev.bpe.txt model/tfidf/model.txt pred.tfidf.dev.txt
:~/nlp/tnews$ python acc.py pred.tfidf.dev.txt tgt.dev.s.txt 
47.4

可以看到,经过TF-IDF算法调整后,模型预测的准确率可以达到47.4%,相比于之前的0.45%有显著的提高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/27004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

String[] 转ArrayList<String>

这种方式会将数组转换为一个固定大小的列表&#xff0c;虽然可以直接转换为ArrayList&#xff0c;但是这个列表是基于原数组的一个视图&#xff0c;对视图的更改会反映到原数组中&#xff0c;反之亦然。如果需要独立于原数组操作&#xff0c;请使用下面的方法。 使用循环添加&…

百度之星2024题目记录

目录 1 介绍2 训练-钻石level3 参考 1 介绍 本博客记录百度之星2024编程比赛相关题目。 2 训练-钻石level 题目1&#xff1a;BD202401补给 解题思路&#xff1a;排序贪心。优惠到最后购买不了任何商品时才使用。 C代码如下&#xff0c; #include<bits/stdc.h> usin…

【并集查找】839. 相似字符串组

本文涉及知识点 并集查找&#xff08;并差集) 图论知识汇总 LeetCode839. 相似字符串组 如果交换字符串 X 中的两个不同位置的字母&#xff0c;使得它和字符串 Y 相等&#xff0c;那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的&#xff0c;那它们也是相似的。…

现在Java行情不好可以转.net吗?

转向.NET开发可能是一个选择&#xff0c;但要注意以下几点。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在后台发给你。 技术转…

「前端+鸿蒙」鸿蒙应用开发-ArkTS-声明式UI组件化

鸿蒙应用开发中&#xff0c;ArkTS 是一个基于 TypeScript 的开发框架&#xff0c;它允许开发者使用声明式 UI 和组件化的方式来构建用户界面。声明式 UI 意味着你通过描述 UI 的状态和状态的变化来更新界面&#xff0c;而不是通过命令式地操作 DOM。组件化则是将 UI 拆分成独立…

闭包、内存泄漏、垃圾回收详解

首先要说清楚这个话题&#xff0c;必须要先清楚什么是垃圾回收&#xff0c;要清楚什么是垃圾回收呢&#xff0c;必须要知道什么是垃圾&#xff0c;所谓的垃圾就是不再需要的内存&#xff0c;需要或者不需要是由人为来决定的 <!DOCTYPE html> <html lang"en"…

STM32的FreeRtos的学习

首先就是去官网下载一个源文件&#xff1a;FreeRtos官网 下载下来的是一个zip文件&#xff0c;解压缩了。 然后再工程文件夹中创建个文件夹&#xff1a; 在这个文件夹中创建3个文件夹&#xff1a; 然后开始把下载下来的文件夹中的文件挑选出来放到我们的工程文件夹中&#xff1…

canal应用

1. canal分享模块链接 分享模块 分享链接Mysql日志-binlog有道云笔记 (youdao.com)canal-deployer实战有道云笔记 (youdao.com)canal-tcp客户端 有道云笔记 (youdao.com)canal-adapter实战有道云笔记 (youdao.com) canal源码下载地址 Gitee 极速下载/canal 2. 客户端测试链接…

Kafka多维度调优

优化金字塔 应用程序层面 框架层面&#xff08;Broker层面&#xff09; JVM层面 操作系统层面 应用程序层面&#xff1a;应当优化业务代码合理使用kafka&#xff0c;合理规划主题&#xff0c;合理规划分区&#xff0c;合理设计数据结构&#xff1b; 框架层面&#xff1a;在不…

SPI转四串口芯片CH9434的设计

一、CH9434的介绍 CH9434 是一款SPI转四串口转接芯片&#xff0c;提供四组全双工的9线异步串口&#xff0c;用于单片机/嵌入式系统扩展异步串口。CH9434包含四个兼容16C550的异步串口&#xff0c;最高支持4Mbps波特率通讯。最多支持25 路GPIO&#xff0c;提供半双工收发自动切换…

IO高级 -- 文件操作(Path、Paths、Files)

一、基础&#xff1a;File 1.1 构造方法&#xff1a; 1、 public File(String pathname) &#xff1a;通过给定的路径来创建新的 File实例。2、 public File(String parent, String child) &#xff1a;从父路径(字符串)和子路径创建新的 File实例。3、 public File(File pare…

《Windows API每日一练》4.1 GDI绘图

本节必须掌握的知识点&#xff1a; GDI原理 GDI函数调用 GDI基本图形 4.1.1 GDI原理 GDI&#xff0c;全称是Graphics Device Interface&#xff08;图形设备接口&#xff09;&#xff0c;是微软Windows操作系统中提供的一套用于渲染图形和格式化文本的API&#xff08;应用程序…

京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设

京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设 京准电钟 NTP时间同步服务器助力水库水坝水利自动化建设 水库大坝监测系统主要包括渗流监测系统、流量监测系统、雨量监测系统、沉降监测系统组成。每一个监测系统由监测仪器及自动化数据采集装置&#xff08;内置通信装…

【机器学习】鸢尾花分类:机器学习领域经典入门项目实战

学习机器学习&#xff0c;就像学习任何新技能一样&#xff0c;最好的方法之一就是通过实战来巩固理论知识。鸢尾花分类项目是一个经典的入门项目&#xff0c;它不仅简单易懂&#xff0c;还能帮助我们掌握机器学习的基本步骤和方法。 鸢尾花数据集&#xff08;Iris Dataset&…

对象存储服务的加密特性

实现思路 加密特性的方案&#xff0c;涉及如下设计点&#xff1a; 密钥的用途加密的位置加密的算法加密密钥的使用加密密钥的管理 密钥的用途 密钥的用途分为管理密钥和数据密钥。 管理密钥用于加密数据密钥&#xff0c;需要定期更换&#xff0c;更换成本低&#xff1b;假如…

计算机游戏因为d3dcompiler_47.dll丢失无法启动怎么办?解决只要d3dcompiler_47.dll丢失无法启动游戏软件的方法

d3dcompiler_47.dll 是一个动态链接库文件&#xff0c;属于 Microsoft DirectX 的一部分&#xff0c;主要负责编译和运行 3D 图形程序。它是支持 Direct3D 功能的核心组件&#xff0c;Direct3D 是一种用于编程 3D 图形的 API&#xff0c;广泛应用于游戏和图形密集型应用程序中。…

Linux 如何查看磁盘空间占用

Linux 磁盘空间满会导致的问题 当 Linux 系统的磁盘空间满时&#xff0c;可能会导致一系列问题和不良表现&#xff0c;影响系统性能和稳定性。导致的问题可能是&#xff1a; 系统性能下降&#xff1a;磁盘空间不足会使得系统无法正常写入临时文件&#xff0c;影响系统操作和运…

html实现粘贴excel数据,在页面表格中复制

录入数据时&#xff0c;有时候需要把excel中的数据一条条粘贴到页面中&#xff0c;当数据量过多时&#xff0c;这种操作很令人崩溃。本篇文章实现了从excel复制好多行数据后,可在页面粘贴的功能 具体实现代码 <!DOCTYPE html> <html lang"en"> <head…

ArcGIS for js 4.x FeatureLayer 点选查询

示例&#xff1a; 代码如下&#xff1a; <template><view id"mapView"></view></template><script setup> import "arcgis/core/assets/esri/themes/light/main.css"; import Map from "arcgis/core/Map.js"; im…

SAP ABAP 编码规范

文章目录 前言一、案例介绍/笔者需求二、命名规范 a.类型&#xff08;Type&#xff09; b.全局&#xff08;Global&#xff09; c.局部&#xff08;Local&#xff09; d.子程序&#xff08;Perform&#xff09; e.函数…