小白使用百度 NLP 實現語料庫 TTR 統計

Rowson·張瓏馨發表於2018-11-26

本人是一個文科生,本科學英語,碩士學翻譯。學院裡都流行的是語言學研究,我個人更喜歡的是計算語言學,看了幾本書和論文之後就對計算語言學和語料庫語言學深感興趣。奈何讀書那會對這些技術一竅不通,程式碼也看不進去,工作幾年後還是對這方面感興趣,就從10月份開始學python,做了個基礎的入門,然後開始挑選適合自己的NLP技術方案。

對於技術小白來說,能少寫程式碼就少寫程式碼,越簡單越好,因為我只是奔著一個目的來的:輸入文字,處理文字,返回處理後的資料。即可。

這次我要實現的是語料庫語言學研究中的TTR(Type Token Ratio)的統計,它表示的是一個文字中相同的詞和所有詞之間的比例關係。

舉個例子:

CIPS-SIGHAN CLP 2010(國家自然科學基金(基金號:90920004))中,TTR在不同型別的文體中有著不一樣的資料(下圖),若以其中“文學”的資料為基準,其他作者的文字的TTR超過這個基準資料,那意味著作者在寫作的時候用詞豐富性不夠、重複內容較多、語言較通俗易懂等,低於這個基準資料,則意味著作者用了一些生僻的詞語、用詞豐富性很高等。
小白使用百度 NLP 實現語料庫 TTR 統計

這僅僅是語言學進行分析時的一個指標,還有很多很多其他的。這裡只討論如何用python+百度NLP去實現TTR。

本人半路出家寫程式碼,肯定有各種各樣的問題,還請各路大神出來指點,寫出來的程式碼能執行不報錯,就ok了。

為何選百度

為什麼選百度?

同時留下了沒有技術和沒有金錢的淚水……

小白使用百度 NLP 實現語料庫 TTR 統計

精挑細選(數個小時的嘗試),阿里雲的NLP,10萬次要270塊錢,騰訊文智5萬次免費,百度雲免費用QPS = 5 (Query Per Second,每秒請求次數(這個我當時還不知道啥意思,當超了的時候才明白是一秒只能請求多少次))。

阿里雲的基礎版說是有每日5萬次免費額度,但是沒找到在哪。文智的api和sdk,卡在了簽名鑑權上,放棄。

最後只能選擇百度,而且現在只寫百度,以後等我學會了其他的, 我再把另外兩家補上。

對於小白來說,除了python本身的一些程式碼我不需要查之外,剩下的都得慢慢學慢慢查,變學變進步吧!所以,技術文件的詳細程度也決定我的動手能力和成果。

NLP號,啟動!

小白使用百度 NLP 實現語料庫 TTR 統計

(插一句,媽耶,掘金上傳圖片的背景顏色還會忽閃忽閃的)

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

在shell裡輸入,然後等待安裝完成

pip install baidu-aip複製程式碼

提示success之後,務必命令列裡輸入python,進入python後

import aip
複製程式碼

確認不會報錯,就可以啟動了!

wait,鑰匙沒插進去呢

在啟動之前,需要去百度雲控制皮膚獲取開啟本服務的APP_ID

小白使用百度 NLP 實現語料庫 TTR 統計

點選左側的應用列表,建立應用

小白使用百度 NLP 實現語料庫 TTR 統計

自然語言處理無法勾選,怎麼回事,哦原來是預設全選的,這些足夠了,寫好名稱和描述(隨意),立即建立,拿到發動機鑰匙了!

留好,下面會用到

程式設計思路

  1. python讀取文件內容,寫入變數
  2. 對內容進行簡單處理(去除前後空格)
  3. 送入百度NLP去處理
  4. 返回資料後提取相關資料,計算TTR

差不多就是這麼個順序,發動機,打火,啟動!

小白使用百度 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)複製程式碼

小白使用百度 NLP 實現語料庫 TTR 統計

可以看到,這首詩所有的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裡只包含唯一的元素,非常適合去重使用。

小白使用百度 NLP 實現語料庫 TTR 統計

然後for迴圈,將內容不斷地往set裡面塞……

小白使用百度 NLP 實現語料庫 TTR 統計

得到的結果是:type的長度是118,也就是說這首詩裡面不重複的詞有118個。

最後我們來計算一下這個ratio。

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

小白使用百度 NLP 實現語料庫 TTR 統計

哎嘿,結果出來了,這首詩的TTR是47.01%。


小白使用百度 NLP 實現語料庫 TTR 統計

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我覺得也很有意思,是用來做情感分析的。配合抓取內容後對情緒進行分析,可以瞭解某個人物或者某個事件裡文字所反映出來的情緒,對於一些事情做輔助判斷。

下一篇寫這個好了,還是蠻有意思的。

本人半路出家寫程式碼,肯定有各種各樣的問題,還請各路大神出來指點。


相關文章