BERT是谷歌公司於2018年11月發佈的一款新模型,它一種預訓練語言表示的方法,在大量文本語料(維基百科)上訓練了一個通用的「語言理解」模型,而後用這個模型去執行想作的NLP任務。一經公佈,它便引爆了整個NLP界,其在11個主流NLP任務中都取得優異的結果,所以成爲NLP領域最吸引人的一個模型。簡單來講,BERT就是在訓練了大量的文本語料(無監督)以後,可以在對英語中的單詞(或中文的漢字)給出一個向量表示,使得該單詞(或漢字)具備必定的語義表示能力,所以,BERT具備必定的先驗知識,在NLP任務中表現十分搶眼。
在文章利用bert-serving-server搭建bert詞向量服務(一) 中,做者簡潔明瞭地介紹瞭如何利用bert-serving-server來獲取中文漢字的詞向量,這大大下降了通常從業者使用BERT的門檻。
結合筆者這段時間的工做體會以及思考,筆者嘗試着給出BERT的幾個可能的應用,以下:python
因爲筆者才疏學淺且撰寫文章時間倉促,文章中有不足之處,請讀者多多批評指正!git
BERT公佈已經半年多了,如今已經成爲NLP中的深度學習模型中必不可少的工具,通常會加載在模型中的Embedding層。因爲篇幅緣由,筆者再也不介紹本身的BERT項目,而是介紹幾個BERT在基本任務中的Github項目:github
能夠看到,BERT已經普遍應用於NLP基本任務中,在開源項目中導出能夠見到它的身影,而且這些項目的做者也寫了很是細緻的代碼工程,便於上手。web
在具體講述下面的三個應用前,咱們先了解下BERT應用的項目結構,以下:算法
其中,bert_client_lmj.py爲調用BERT詞向量服務,具體可參考文章利用bert-serving-server搭建bert詞向量服務(一) ,完整的Python代碼以下:數據庫
# -*- coding:utf-8 -*- from bert_serving.client import BertClient from sklearn.metrics.pairwise import cosine_similarity class Encoding(object): def __init__(self): self.server_ip = "127.0.0.1" self.bert_client = BertClient(ip=self.server_ip) def encode(self, query): tensor = self.bert_client.encode([query]) return tensor def query_similarity(self, query_list): tensors = self.bert_client.encode(query_list) return cosine_similarity(tensors)[0][1] if __name__ == "__main__": ec = Encoding() print(ec.encode("中國").shape) print(ec.encode("美國").shape) print("中國和美國的向量類似度:", ec.query_similarity(["中國", "美國"]))
利用詞向量能夠查找文章中與指定詞語最相近的幾個詞語。具體的作法爲:現將文章分詞,對分詞後的每一個詞,查詢其與指定詞語的類似度,最後按類似度輸出詞語便可。咱們的示例文章爲老舍的《養花》,內容以下:微信
我愛花,因此也愛養花。我可還沒成爲養花專家,由於沒有工夫去研究和試驗。我只把養花當作生活中的一種樂趣,花開得大小好壞都不計較,只要開花,我就高興。在個人小院子裏,一到夏天盡是花草,小貓只好上房去玩,地上沒有它們的運動場。
花雖然多,可是沒有奇花異草。珍貴的花草不易養活,看着一棵好花生病要死,是件難過的事。北京的氣候,對養花來講不算很好,冬天冷,春天多風,夏天不是乾旱就是大雨傾盆,秋天最好,但是會突然鬧霜凍。在這種氣候裏,想把南方的好花養活,我尚未那麼大的本事。所以,我只養些好種易活、本身會奮鬥的花草。
不過,儘管花草本身會奮鬥,我如果置之不理,任其自生自滅,大半仍是會死的。我得每天照管它們,像好朋友似的關心它們。一來二去,我摸着一些門道:有的喜陰,就別放在太陽地裏;有的喜幹,就別多澆水。摸着門道,花草養活了,並且三年五載老活着、開花,多麼有意思啊!不是亂吹,這就是知識呀!多得些知識決不是壞事。
我不是有腿病嗎,不但不利於行,也不利於久坐。我不知道花草們受個人照顧,感謝我不感謝;我可得感謝它們。我工做的時候,我老是寫一下子就到院中去看看,澆澆這棵,搬搬那盆,而後回到屋裏再寫一下子,而後再出去。如此循環,讓腦力勞動和體力勞動獲得適當的調節,有益身心,勝於吃藥。要是遇上暴風驟雨或天氣突變,就得全家動員,搶救花草,十分緊張。幾百盆花,都要很快地搶到屋裏去,令人腰痠腿疼,熱汗直流。次日,天氣好了,又得把花都搬出去,就又一次腰痠腿疼,熱汗直流。但是,這多麼有意思呀!不勞動,連棵花也養不活,這難道不是真理嗎?
送牛奶的同志進門就誇「好香」,這使咱們全家都感到驕傲。趕到曇花開放的時候,約幾位朋友來看看,更有秉燭夜遊的味道——曇花總在夜裏開放。花分根了,一棵分爲幾棵,就贈給朋友們一些。看着友人拿走本身的勞動果實,內心天然特別歡喜。
固然,也有傷心的時候,今年夏天就有這麼一回。三百棵菊秧還在地上(沒到移入盆中的時候),下了暴雨,鄰家的牆倒了,菊秧被砸死三十多種,一百多棵。全家人幾天都沒有笑容。
有喜有憂,有笑有淚,有花有果,有香有色,既須勞動,又長見識,這就是養花的樂趣。工具
指定詞語爲「開心」,查詢《養花》一文中與「開心」最爲接近的5個詞語,完整的Python代碼以下:(find_similar_words.py)學習
# -*- coding:utf-8 -*- import jieba from bert_client_lmj import Encoding from operator import itemgetter # 讀取文章 with open('./doc.txt', 'r', encoding='utf-8') as f: content = f.read().replace('\n', '') ec = Encoding() similar_word_dict = {} # 查找文章中與'開心'的最接近的詞語 words = list(jieba.cut(content)) for word in words: print(word) if word not in similar_word_dict.keys(): similar_word_dict[word] = ec.query_similarity([word, '開心']) # 按類似度從高到低排序 sorted_dict = sorted(similar_word_dict.items(), key=itemgetter(1), reverse=True) print('與%s最接近的5個詞語及類似度以下:' % '開心') for _ in sorted_dict[:5]: print(_)
輸出的結果以下:.net
與開心最接近的5個詞語及類似度以下: ('難過', 0.9070794) ('高興', 0.89517105) ('樂趣', 0.89260685) ('驕傲', 0.87363803) ('我愛花', 0.86954254)
在事件抽取中,咱們每每須要抽取一些指定的元素,好比在下面的句子中,
巴基斯坦當地時間2014年12月16日早晨,巴基斯坦塔利班運動武裝分子襲擊了西北部白沙瓦市一所軍人子弟學校,打死141人,其中132人爲12歲至16歲的學生。
咱們須要抽取襲擊者,也就是恐怖組織這個元素。
直接從句法分析,也許能夠獲得必定的效果,但因爲事件描述方式多變,句法分析會顯得比較複雜且效果不必定能保證。這時候,咱們嘗試BERT詞向量,它在必定程度上能夠做爲補充策略,幫助咱們定位到事件的元素。具體的想法以下:
在這裏,咱們的事件元素爲恐怖組織,指定的模板爲「伊斯蘭組織」,完整的Python程序以下(find_similar_entity_in_sentence.py):
# -*- coding:utf-8 -*- import jieba from operator import itemgetter from bert_client_lmj import Encoding # 建立n-gram def compute_ngrams(sequence, n): lst = list(zip(*[sequence[index:] for index in range(n)])) for i in range(len(lst)): lst[i] = ''.join(lst[i]) return lst # 模板 template = '伊斯蘭組織' # 示例句子 doc = "巴基斯坦當地時間2014年12月16日早晨,巴基斯坦塔利班運動武裝分子襲擊了西北部白沙瓦市一所軍人子弟學校,打死141人,其中132人爲12歲至16歲的學生。" words = list(jieba.cut(doc)) all_lst = [] for j in range(1, 5): all_lst.extend(compute_ngrams(words, j)) ec = Encoding() similar_word_dict = {} # 查找文章中與template的最接近的詞語 for word in all_lst: print(word) if word not in similar_word_dict.keys(): similar_word_dict[word] = ec.query_similarity([word, template]) # 按類似度從高到低排序 sorted_dict = sorted(similar_word_dict.items(), key=itemgetter(1), reverse=True) print('與%s最接近的實體是: %s,類似度爲 %s.' %(template, sorted_dict[0][0], sorted_dict[0][1]))
輸出的結果以下:
與伊斯蘭組織最接近的實體是: 塔利班運動武裝分子,類似度爲 0.8953854.
能夠看到,該算法成功地幫助咱們定位到了恐怖組織:塔利班運動武裝分子,效果很好,可是因爲是無監督產生的詞向量,效果不必定可控,並且該算法運行速度較慢,這點能夠從工程上加以改進。
在智能問答中,咱們每每會採用知識圖譜或者數據庫存儲實體,其中一個難點就是實體對齊。舉個例子,咱們在數據庫中儲存的實體以下:(entities.txt)
094型/晉級
052C型(旅洋Ⅱ級)
遼寧艦/瓦良格/Varyag
傑拉爾德·R·福特號航空母艦
052D型(旅洋III級)
054A型
CVN-72/林肯號/Lincoln
這樣的實體名字很複雜,若是用戶想查詢實體「遼寧艦」,就會碰到困難,可是因爲實體以儲存在數據庫或知識圖譜中,實體很差直接修改。一種辦法是經過關鍵字匹配定位實體,在這裏,咱們能夠藉助BERT詞向量來實現,完整的Python代碼以下:(Entity_Alignment.py)
# -*- coding:utf-8 -*- from bert_client_lmj import Encoding from operator import itemgetter with open('entities.txt', 'r', encoding='utf-8') as f: entities = [_.strip() for _ in f.readlines()] ec = Encoding() def entity_alignment(query): similar_word_dict = {} # 查找已有實體中與query最接近的實體 for entity in entities: if entity not in similar_word_dict.keys(): similar_word_dict[entity] = ec.query_similarity([entity, query]) # 按類似度從高到低排序 sorted_dict = sorted(similar_word_dict.items(), key=itemgetter(1), reverse=True) return sorted_dict[0] query = '遼寧艦' result = entity_alignment(query) print('查詢實體:%s,匹配實體:%s 。' %(query, result)) query = '林肯號' result = entity_alignment(query) print('查詢實體:%s,匹配實體:%s 。' %(query, result))
輸出的結果以下:
查詢實體:遼寧艦,匹配實體:('遼寧艦/瓦良格/Varyag', 0.8534695) 。 查詢實體:林肯號,匹配實體:('CVN-72/林肯號/Lincoln', 0.8389378) 。
在這裏,查詢的速度應該不是困難,由於咱們能夠將已儲存的實體以離線的方式查詢其詞向量並儲存,這樣進來一個查詢到實體,只查詢一次詞向量,並計算其與離線的詞向量的類似度。這種方法也存在缺陷,主要是因爲詞向量的無監督,實體對齊有時候不會很準,但做爲一種補充策略,也許能夠考慮。
本文介紹了筆者這段時間所思考的BERT詞向量的幾個應用,因爲能力有限,文章中會存在考慮不當的地方,還請讀者多多批評指正。
另外,筆者將會持續調研詞向量方面的技術,好比騰訊詞向量,百度詞向量等,歡迎你們關注~
注意:不妨瞭解下筆者的微信公衆號: Python爬蟲與算法(微信號爲:easy_web_scrape), 歡迎你們關注~