小白使用百度 NLP 实现语料库 TTR 统计

本人是一个文科生,本科学英语,硕士学翻译。学院里都流行的是语言学研究,我个人更喜欢的是计算语言学,看了几本书和论文之后就对计算语言学和语料库语言学深感兴趣。奈何读书那会对这些技术一窍不通,代码也看不进去,工作几年后还是对这方面感兴趣,就从10月份开始学python,做了个基础的入门,然后开始挑选适合自己的NLP技术方案。

 

对于技术小白来说,能少写代码就少写代码,越简单越好,因为我只是奔着一个目的来的:输入文本,处理文本,返回处理后的数据。即可。

 

这次我要实现的是语料库语言学研究中的TTR(Type Token Ratio)的统计,它表示的是一个文本中相同的词和所有词之间的比例关系。

 

举个例子:

 

CIPS-SIGHAN CLP 2010(国家自然科学基金(基金号:90920004))中,TTR在不同类型的文体中有着不一样的数据(下图),若以其中“文学”的数据为基准,其他作者的文本的TTR超过这个基准数据,那意味着作者在写作的时候用词丰富性不够、重复内容较多、语言较通俗易懂等,低于这个基准数据,则意味着作者用了一些生僻的词语、用词丰富性很高等。

 

 

这仅仅是语言学进行分析时的一个指标,还有很多很多其他的。这里只讨论如何用python+百度NLP去实现TTR。

 

本人半路出家写代码,肯定有各种各样的问题,还请各路大神出来指点,写出来的代码能运行不报错,就ok了。

 

为何选百度

 

为什幺选百度?

 

同时留下了没有技术和没有金钱的泪水……

 

 

精挑细选(数个小时的尝试),阿里云的NLP,10万次要270块钱,腾讯文智5万次免费,百度云免费用QPS = 5 (Query Per Second,每秒请求次数(这个我当时还不知道啥意思,当超了的时候才明白是一秒只能请求多少次))。

 

阿里云的基础版说是有每日5万次免费额度,但是没找到在哪。文智的api和sdk,卡在了签名鉴权上,放弃。

 

最后只能选择百度,而且现在只写百度,以后等我学会了其他的, 我再把另外两家补上。

 

对于小白来说,除了python本身的一些代码我不需要查之外,剩下的都得慢慢学慢慢查,变学变进步吧!所以,技术文档的详细程度也决定我的动手能力和成果。

 

NLP号,启动!

 

 

(插一句,妈耶,掘金上传图片的背景颜色还会忽闪忽闪的)

 

环境:python 3.7,baidu-aip 模块,vscode+jupyter notebook(比心)

 

在shell里输入,然后等待安装完成

 

pip install baidu-aip

 

提示success之后,务必命令行里输入python,进入python后

 

import aip

 

确认不会报错,就可以启动了!

 

wait,钥匙没插进去呢

 

在启动之前,需要去百度云控制面板获取开启本服务的APP_ID

 

 

点击左侧的应用列表,创建应用

 

 

自然语言处理无法勾选,怎幺回事,哦原来是默认全选的,这些足够了,写好名称和描述(随意),立即创建,拿到发动机钥匙了!

 

留好,下面会用到

 

程序设计思路

 

 

    1. python读取文档内容,写入变量

 

    1. 对内容进行简单处理(去除前后空格)

 

    1. 送入百度NLP去处理

 

    1. 返回数据后提取相关数据,计算TTR

 

 

差不多就是这幺个顺序,发动机,打火,启动!

 

 

好,让我们来处理一下这首朱自清的现代诗《赠友》

 

你的手像火把,

 

你的眼像波涛,

 

你的言语如石头,

 

怎能使我忘记呢?

 

你飞渡洞庭湖,

 

你飞渡扬子江;

 

你要建红色的天国在地上!

 

地上是荆棘呀,

 

地上是狐兔呀,

 

地上是行尸呀;

 

你将为一把快刀,

 

披荆斩棘的快刀!

 

你将为一声狮子吼,

 

狐兔们披靡奔走!

 

你将为春雷一震,

 

让行尸们惊醒!

 

我爱看你的骑马,

 

在尘土里驰骋---

 

一会儿, 不见踪影!

 

我爱看你的手杖,

 

那铁的铁的手杖;

 

它有颜色,有斤两,

 

有铮铮的声响!

 

我想你是一阵飞沙

 

走石的狂风,

 

要吹倒那不能摇撼的黄金的王宫!

 

那黄金的王宫!

 

呜---吹呀!

 

去年一个夏天大早我见着你:

 

你何其憔悴呢?

 

你的眼还涩着,

 

你的发太长了!

 

但你的血的热加倍的薰灼着!

 

在灰泥里辗转的我,

 

仿佛被焙炙着一般!---

 

你如郁烈的雪茄烟,

 

你如酽酽的白兰地,

 

你如通红通红的辣椒,

 

我怎能忘记你呢?

 

读取文档内容,写入变量

 

from aip import AipNlp
import time  
file = open("C:\\workspace\\nltk\\shi.txt",
            'r',
            encoding="utf-8-sig").readlines() 
for i in range(len(file)):
    file[i].strip() #将句子前后的空格去掉    print('\n【即将分词这一句】',file[i],'\n\n')
    seg_word(file[i]) #送入百度NLP处理

 

读取文档的过程中遇到过一个问题,编码采用utf-8的话会在开头出现一个奇怪的字符,才用utf-8-sig后就不再出现这个问题。

 

此时file是个list。我觉得在写python的过程中,时时刻刻需要注意变量的类型,几乎每时每刻我都在打印内容,好找错误。

 

加入for循环,循环list[i]的,使用.strip()去除每一行前后的空格。而在某些文本中,空格还会在处理的时候报错,我还需要去手动修改文档。现在还没学会这个技能,所以文档里的内容是没有包含空行和奇怪空格的。学艺不精,慢慢来吧。

 

*百度nlp不能输入空白内容,会报错。

 

送入百度NLP去处理

 

在此之前,我们要完成对nlp接口的配置,你需要替换成自己的id、API_key、Secret_key。

 

APP_ID = "148595**"
API_KEY= "M91DlMXQ0rTAvlL******"
SECRET_KEY = "FzCDK9hzWPd1Qgvt*****"
client =AipNlp(APP_ID, API_KEY, SECRET_KEY)

 

这里使用nlp的lexer接口,输入句子,返回分词结果。(接口说明文档)

 

def seg_word(abc):    
    text = abc    
    res = client.lexer(text) #返回的是词典    
    res_1 = res['items'] #从词典中取出返回值中的核心内容items,是一个新的字典        
    for i in range(len(res_1)):        
        s = res_1[i]['item'] #从字典的每一条中抽出一个对应的分词数据来        
        # print('将把这个词放入列表中【',s,'】\n')        
        seg_list.append(s) #加入list中,变成只获得分词    
    print('\n现已分词的词语包括',seg_list)

 

res返回的是一个json字典结构,我们需要的内容是items对应的value:

 

{'log_id': 2198288401988815642, 
    'text': '你', 
    'items': [{'loc_details': [],
             'byte_offset': 0,
             'uri': '',
             'pos': 'r',
             'ne': '',
             'item': '你',
             'basic_words': ['你'],
             'byte_length': 2,
             'formal': ''}]
}

 

分词的结果中,我们需要的是item对应的value,它就是TTR中的一个token。我们取这个Token的值,放入一个新的列表seg_list中。用append方式添加。

 

seg_word这个function会将file文档里的每一行进行分词,在这个function里使用for循环,将每一个分词的结果提取items下面的item的value,写入seg_list(这是一个list)。

 

seg_list里包含这首诗的所有分词后的Token。

 

计算TTR

 

然后我们打印一下这个列表的内容,以及计算一下它的长度。

 

print(seg_list)token_count = len(seg_list)print(token_count)

 

 

可以看到,这首诗所有的token都在做合理,还算比较准的。长度为251,有251个词,包含重复的。这就是我们要计算TTR里面的Token了。

 

现在,我们要将这些Token去重,获得一个不重复的词语列表。

 

type_count = set()
for i in range(len(seg_list)):
    print("现在我们将【",seg_list[i],'】加入type列表')
    type_count.add(seg_list[i])
print('type列表目前包含:', type_count)
print('type的长度为', len(type_count))

 

去重的一个思路是使用set,因为set里只包含唯一的元素,非常适合去重使用。

 

 

然后for循环,将内容不断地往set里面塞……

 

 

得到的结果是:type的长度是118,也就是说这首诗里面不重复的词有118个。

 

最后我们来计算一下这个ratio。

 

ratio = float(len(type_count) / token_count) * 100
print('Type Token Ratio: {:.2f}%'.format(ratio))

 

 

哎嘿,结果出来了,这首诗的TTR是47.01%。

 

 

emmmm……好像比上面提到的基准数据差的很远很远呢。原因很简单,诗句的用词本身就很精炼,我们回过头去看那首诗就明白了,为了保持诗的结构,会使用很多重复的词语,比如“你”、“飞”、“地上”、“手杖”、“我”等。

 

———————–

 

完整代码

 

#coding=utf-8
#%%  用过jupyter的都说好
from aip import AipNlp
import time APP_ID = "14859***"
API_KEY= "M91DlMXQ0rT*****"
SECRET_KEY = "FzCDK9hzWPd1Qgvt*********"
client =AipNlp(APP_ID, API_KEY, SECRET_KEY)
# client.lexer(text) #核心运行
seg_list = []
type_count = set()
#%% def seg_word(abc):
    text = abc
    res = client.lexer(text) #这里返回的是词典
    res_1 = res['items'] #从词典中取出返回值中的核心内容items,是一个新的字典
    # print('百度nlp处理后的数据:\n',res_1,'\n')
    for i in range(len(res_1)):
        s = res_1[i]['item'] #从字典的每一条中抽出一个对应的分词数据来
        # print('将把这个词放入列表中【',s,'】\n')
        seg_list.append(s) #加入list中,变成只获得分词
    print('\n现已分词的词语包括',seg_list)
#延时器
def sleep(a):
     a=int(a)
     time.sleep(a)
#%% 读取文档内容
file = open("C:\\workspace\\nltk\\shi.txt",'r',encoding="utf-8-sig").readlines()
# print('原始文档内容:\n',file,'\n')
#循环将句子或段落带入function,超量预警\a响铃
for i in range(len(file)):
    #加入一个5次请求的1秒延时器
    n = i + 1
    m = (i/5)*10
    if (m%5) ==0:
        sleep(1)
        print("\a***********************可能会超过api限制,休息1秒*************************")
    file[i].strip() #将句子前后的空格去掉
    print('\n【即将分词这一句】',file[i],'\n\n')
    seg_word(file[i]) #送入function去让百度处理
#%% 计算token词数量
print(seg_list)
token_count = len(seg_list)
print(token_count)
#%%计算type词数量
for i in range(len(seg_list)):
    print("现在我们将【",seg_list[i],'】加入type列表')
    type_count.add(seg_list[i])
print('type列表目前包含:', type_count)
print('type的长度为', len(type_count))
#%%计算type token ratio
ratio = float(len(type_count) / token_count) * 100
print('Type Token Ratio: {:.2f}%'.format(ratio))

 

完整代码在这里,里面还加入了sleep延时的功能。

 

因为百度NLP的QPS是5,也就是一秒5次,所以我写了一个每请求5次就休息1秒的函数放在里面,这样就不会超量后报错。其实也可以直接在每一次请求后sleep 0.2-0.3秒来确保稳定且不会超过额度。

 

文章里的lexer接口只是其中的一个,还有sentimentClassify我觉得也很有意思,是用来做情感分析的。配合抓取内容后对情绪进行分析,可以了解某个人物或者某个事件里文字所反映出来的情绪,对于一些事情做辅助判断。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注