本人是一个文科生, 本科学英语, 硕士学翻译. 学院里都流行的是语言学研究, 我个人更喜欢的是计算语言学, 看了几本书和论文之后就对计算语言学和语料库语言学深感兴趣. 奈何读书那会对这些技术一窍不通, 代码也看不进去, 工作几年后还是对这方面感兴趣, 就从 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
点击左侧的应用列表, 创建应用
自然语言处理无法勾选, 怎么回事, 哦原来是默认全选的, 这些足够了, 写好名称和描述(随意), 立即创建, 拿到发动机钥匙了!
留好, 下面会用到
程序设计思路
python 读取文档内容, 写入变量
对内容进行简单处理(去除前后空格)
送入百度 NLP 去处理
返回数据后提取相关数据, 计算 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 我觉得也很有意思, 是用来做情感分析的. 配合抓取内容后对情绪进行分析, 可以了解某个人物或者某个事件里文字所反映出来的情绪, 对于一些事情做辅助判断.
下一篇写这个好了, 还是蛮有意思的.
本人半路出家写代码, 肯定有各种各样的问题, 还请各路大神出来指点.
来源: https://juejin.im/post/5bfb8d886fb9a04a0f64d92d