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

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

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

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

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

#ext.py
#encoding: utf-8

import sys
from json import loads

def handle(srcf, rsf, rstf):#打开三个文件,将句子写入rsf,将标签写入rstf

    ens = "\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.txt
  10000 src.dev.s.txt
  53360 src.train.s.txt
  10000 tgt.dev.s.txt
  53360 tgt.train.s.txt
 126720 总计

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

 :~/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-8

import sys
from json import dump
from math import log

def 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 #出现一次则将分词的词频+1
    return 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方法向文件写str
        
if __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-8

import sys
from json import loads
from math import inf

def 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, None
    for _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         #总词数+1
        if _s > 0.0:            #排除n=0的情况
            _s = _s / _n        #求平均分数
            if _s > _max_score: #更新每行出现词标签频率最高的logp以及类别
                _max_class = _class
                _max_score = _s
    if _max_class is None:
        _max_class = _class
    
    return _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_class
                fwrt.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 sys

def 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 #如果预测正确,累加c
                t += 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-8

import sys
from json import dump
from math import log

def 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 #出现一次则将分词的词频+1
    return 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方法向文件写str
        
if __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%有显著的提高。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

汽车零部件巨头营收PK:博世稳居榜首,宁德时代净利润率亮眼

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 在竞争激烈的汽车行业&#xff0c;各大巨头在2023年的营收表现可谓是各有千秋。近日&#xff0c;一份关于汽车行业主要企业的营收和利润率数据引…

SAP Web IDE 安装使用

For training SAP Web IDE 是基于 Eclipse 内核的在线开发 IDE&#xff0c;可以使用在线的试用版本&#xff0c;但服务器在德国&#xff0c;访问的网速特别慢。也可以使用 Personal Edition&#xff0c;在本机启动和编写代码。 打开官网下载WEBIDE工具包&#xff0c;包含 Tri…

汇编:EFLAGS寄存器

EFLAGS寄存器是x86架构处理器中的一个状态寄存器&#xff0c;用于存储当前处理器状态和控制特定操作&#xff1b;寄存器中的各个标志位可以影响指令执行&#xff0c;并且指令执行过程中也可以修改这些标志位&#xff0c;每个位都有特定的含义。 EFLAGS寄存器图示&#xff1a; …

1. NAS和SAN存储

NAS和SAN存储 一、存储设备1、根据工作方式2、DAS 直接附加存储3、NAS存储4、SAN存储 二、模拟配置SAN存储1、创建虚拟机、安装openfiler2、访问openfiler webUI3、创建RAID设备4、开启iSCSI服务5、配置SAN存储设备共享空间5.1 设置IQN 6、业务服务器连接使用存储6.1 安装客户端…

NodeClub:NodeJS构造开源交流社区

NodeClub&#xff1a; 连接每一个想法&#xff0c;NodeClub让社区更生动- 精选真开源&#xff0c;释放新价值。 概览 NodeClub是一个基于Node.js和MongoDB构建的社区系统&#xff0c;专为开发者和社区爱好者设计。它提供了一套完整的社区功能&#xff0c;包括用户管理、内容发…

ASP淘特二手房房地产系统源码

源码介绍 ASP淘特二手房房地产系统源码主要提供了房屋信息出售、出租、求购、求租、合租等信息的发布平台。 本系统已提供成熟的赢利模式&#xff0c;通过向中介会员提供发布信息平台收取会员费为网站的主要收入来源&#xff0c;中介会员申请开通后&#xff0c;可以添加经济人…

通用大模型VS垂直大模型,你更青睐哪一方?

AI大模型之辩&#xff1a;通用与垂直&#xff0c;谁将引领未来&#xff1f; 在人工智能&#xff08;AI&#xff09;领域&#xff0c;大模型技术的崛起无疑为整个行业带来了革命性的变革。然而&#xff0c;随着技术的深入发展&#xff0c;AI大模型的战场似乎正在悄然分化&#…

【5.x】ELK日志分析

ELK日志分析 一、ELK概述 1、ELK简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将ElasticSearch、Logstash和Kiabana三个开源工具配合使用&#xff0c;完成更强大的用户对日志的查询、排序、统计需求。 一个完整的集中式日志系统&#xff0c;需要包含以下几个主…

【FPGA项目】bin文件ram存取回环测试

&#x1f389;欢迎来到FPGA专栏~bin文件ram存取回环测试 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大…

Java常用的设计模式,如单例模式、工厂模式、观察者模式等

设计模式是软件工程中的一种解决方案&#xff0c;用于应对常见的设计问题和挑战。它们提供了一种标准化的方式来解决设计难题&#xff0c;使代码更加灵活、可扩展和易于维护。 单例模式&#xff08;Singleton Pattern&#xff09; 概述 单例模式确保一个类只有一个实例&…

C/C++图形渲染引擎开发方向有钱景吗?

在当前的技术环境下&#xff0c;特别是在图形渲染引擎开发领域&#xff0c;的确存在一些挑战和变化。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私…

Jenkins For Windows编译构建C#项目环境搭建(完整版)

安装Jenkins 下载Windows安装包 官方下载地址 选择稳定版&#xff0c;这里下载的是最新版&#xff0c;如需下载指定版本点击 以前的发行版 配置java环境 下载 java jdk 17 jdk17官方下载链接 这里下载的是msi版本的安装包 安装jdk17 双击运行安装包&#xff0c;一直下…

SpringMVC-基础架构

一、什么是MVC 二、什么是SpringMVC 三、SpringMVC的特点 四、配置SpringMVC 简单流程&#xff1a; 总体框架 1.创建pom.xml依赖 <!--打包方式--><packaging>war</packaging><!--依赖--><dependencies><dependency><groupId>org.s…

得物架构面试:如何保证服务发布过程中流量无损?

哈喽&#xff0c;大家好&#xff0c;我是明智 今天跟大家讨论一个服务稳定性相关的话题&#xff0c;对于大部分做业务的小伙伴来说&#xff0c;很少会被问到这类问题 不过你如果你希望面试公司的一些基础部门&#xff0c;例如&#xff1a;基础架构、效能开发、服务稳定性保障等…

ubtun虚拟机安装

选择镜像后启动 选择第一个回车 加载完成后 &#xff0c;进入Ubuntu安装界面&#xff0c;安装语言选择English&#xff0c;完成后按一下回车&#xff1a; 此时弹出安装器可更新提示&#xff0c;下方选项选择第二个Continue without updating&#xff08;不更新&#xff0c;继续…

复旦微FMQL20SM全国产ARM+FPGA核心板,替代xilinx ZYNQ7020系列

FMQL20SM核心板一款全国产工业核心板。基于复旦微FMQL20S400M四核ARM Cortex-A7&#xff08;PS端&#xff09; FPGA可编程逻辑资源&#xff08;PL端&#xff09;异构多核SoC处理器设计的全国产工业核心板&#xff0c;PS端主频高达1GHz。 核心板简介 FMQL20SM核心板是一款全国…

PHP地方门户分类信息网站源码讯客分类信息系统源码(含手机版)

源码介绍 1.上传程序到网站根目录,访问http://域名/install/index.php 进行安装,不要直接打开网址&#xff0c;先直接安装; 2.安装完成后 后台恢复数据即可 默认帐号密码都是admin http://域名/admin/ 3.不要删除任何文件&#xff0c;因为删除文件或者修改代码可能造成错误 运…

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

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

Django 5 Web应用开发实战

文章目录 一、内容简介二、目录内容三、值得一读四、适读人群 一、内容简介 《Django 5 Web应用开发实战》集Django架站基础、项目实践、开发经验于一体&#xff0c;是一本从零基础到精通Django Web企业级开发技术的实战指南。《Django 5 Web应用开发实战》内容以Python 3.x和…

Java多线程面试重点-1

0. 什么是并发&#xff1f;什么是并行&#xff1f; 并发&#xff1a;把时间分成一段一段&#xff0c;每个线程轮流抢占时间段。 如果时间段非常短&#xff0c;线程切换非常快&#xff0c;被称为伪并行。并行&#xff1a;多个线程可以同时运行。 并发与并行造成的影响&#xff…