实战分享之专业领域词汇无监督挖掘
作者丨蘇劍林
單位丨廣州火焰信息科技有限公司
研究方向丨NLP,神經網絡
個人主頁丨kexue.fm
去年?Data?Fountain?曾舉辦了一個“電力專業領域詞匯挖掘”的比賽,該比賽有意思的地方在于它是一個“無監督”的比賽,也就是說它考驗的是從大量的語料中無監督挖掘專業詞匯的能力。?
大賽主頁:
https://www.datafountain.cn/competitions/320/details
這個確實是工業界比較有價值的一個能力,又想著我之前也在無監督新詞發現中做過一定的研究,加之“無監督比賽”的新穎性,所以當時毫不猶豫地參加了,然而最終排名并不靠前。
不管怎樣,還是分享一下我自己的做法,這是一個真正意義上的無監督做法,也許會對部分讀者有些參考價值。
基準對比
首先,新詞發現部分,用到了我自己寫的庫?NLP?Zero,基本思路是先分別對“比賽所給語料”、“自己爬的一部分百科百科語料”做新詞發現,然后兩者進行對比,就能找到一批“比賽所給語料”的特征詞。?
NLP?Zero:
https://kexue.fm/archives/5597
參考的源碼是:
import?re
import?pandas?as?pd
import?pymongo
import?logging
logging.basicConfig(level?=?logging.INFO,?format?=?'%(asctime)s?-?%(name)s?-?%(message)s')
class?D:?#?讀取比賽方所給語料
????def?__iter__(self):
????????with?open('data.txt')?as?f:
????????????for?l?in?f:
????????????????l?=?l.strip().decode('utf-8')
????????????????l?=?re.sub(u'[^\u4e00-\u9fa5]+',?'?',?l)
????????????????yield?l
class?DO:?#?讀取自己的語料(相當于平行語料)
????def?__iter__(self):
????????db?=?pymongo.MongoClient().baike.items
????????for?i?in?db.find().limit(300000):
????????????l?=?i['content']
????????????l?=?re.sub(u'[^\u4e00-\u9fa5]+',?'?',?l)
????????????yield?l
#?在比賽方語料中做新詞發現
f?=?Word_Finder(min_proba=1e-6,?min_pmi=0.5)
f.train(D())?#?統計互信息
f.find(D())?#?構建詞庫
#?導出詞表
words?=?pd.Series(f.words).sort_values(ascending=False)
#?在自己的語料中做新詞發現
fo?=?Word_Finder(min_proba=1e-6,?min_pmi=0.5)
fo.train(DO())?#?統計互信息
fo.find(DO())?#?構建詞庫
#?導出詞表
other_words?=?pd.Series(fo.words).sort_values(ascending=False)
other_words?=?other_words?/?other_words.sum()?*?words.sum()?#?總詞頻歸一化(這樣才便于對比)
"""對比兩份語料詞頻,得到特征詞。
對比指標是(比賽方語料的詞頻?+?alpha)/(自己語料的詞頻?+?beta);
alpha和beta的計算參考自?http://www.matrix67.com/blog/archives/5044
"""
WORDS?=?words.copy()
OTHER_WORDS?=?other_words.copy()
total_zeros?=?(WORDS?+?OTHER_WORDS).fillna(0)?*?0
words?=?WORDS?+?total_zeros
other_words?=?OTHER_WORDS?+?total_zeros
total?=?words?+?other_words
alpha?=?words.sum()?/?total.sum()
result?=?(words?+?total.mean()?*?alpha)?/?(total?+?total.mean())
result?=?result.sort_values(ascending=False)
idxs?=?[i?for?i?in?result.index?if?len(i)?>=?2]?#?排除掉單字詞
#?導出csv格式
pd.Series(idxs[:20000]).to_csv('result_1.csv',?encoding='utf-8',?header=None,?index=None)
語義篩選
注意到,按照上述方法導出來的詞表,頂多算是“語料特征詞”,但是還不完全是“電力專業領域詞匯”。如果著眼于電力詞匯,那么需要對詞表進行語義上的篩選。
我的做法是:用導出來的詞表對比賽語料進行分詞,然后訓練一個?Word2Vec?模型,根據?Word2Vec?得到的詞向量來對詞進行聚類。
首先是訓練?Word2Vec:
tokenizer?=?f.export_tokenizer()
class?DW:
????def?__iter__(self):
????????for?l?in?D():
????????????yield?tokenizer.tokenize(l,?combine_Aa123=False)
from?gensim.models?import?Word2Vec
word_size?=?100
word2vec?=?Word2Vec(DW(),?size=word_size,?min_count=2,?sg=1,?negative=10)
然后是聚類,不過這不是嚴格意義上的聚類,而是根據我們自己跳出來的若干個種子詞,然后找到一批相似詞來。算法是用相似的傳遞性(有點類似基于連通性的聚類算法),即?A?和?B?相似,B?和?C也相似,那么?A、B、C?就聚為一類(哪怕A、C從指標上看是不相似的)。
當然,這樣傳遞下去很可能把整個詞表都遍歷了,所以要逐步加強對相似的限制。比如?A?是種子詞,B、C?都不是種子詞,A、B?的相似度為?0.6?就定義它為相似,B、C?的相似度要大于?0.7?才能認為它們相似,不然這樣一級級地傳遞下去,后面的詞就會離種子詞的語義越來越遠。
聚類算法如下:
from?multiprocessing.dummy?import?Queue
def?most_similar(word,?center_vec=None,?neg_vec=None):
????"""根據給定詞、中心向量和負向量找最相近的詞
????"""
????vec?=?word2vec[word]?+?center_vec?-?neg_vec
????return?word2vec.similar_by_vector(vec,?topn=200)
def?find_words(start_words,?center_words=None,?neg_words=None,?min_sim=0.6,?max_sim=1.,?alpha=0.25):
????if?center_words?==?None?and?neg_words?==?None:
????????min_sim?=?max(min_sim,?0.6)
????center_vec,?neg_vec?=?np.zeros([word_size]),?np.zeros([word_size])
????if?center_words:?#?中心向量是所有種子詞向量的平均
????????_?=?0
????????for?w?in?center_words:
????????????if?w?in?word2vec.wv.vocab:
????????????????center_vec?+=?word2vec[w]
????????????????_?+=?1
????????if?_?>?0:
????????????center_vec?/=?_
????if?neg_words:?#?負向量是所有負種子詞向量的平均(本文沒有用到它)
????????_?=?0
????????for?w?in?neg_words:
????????????if?w?in?word2vec.wv.vocab:
????????????????neg_vec?+=?word2vec[w]
????????????????_?+=?1
????????if?_?>?0:
????????????neg_vec?/=?_
????queue_count?=?1
????task_count?=?0
????cluster?=?[]
????queue?=?Queue()?#?建立隊列
????for?w?in?start_words:
????????queue.put((0,?w))
????????if?w?not?in?cluster:
????????????cluster.append(w)
????while?not?queue.empty():
????????idx,?word?=?queue.get()
????????queue_count?-=?1
????????task_count?+=?1
????????sims?=?most_similar(word,?center_vec,?neg_vec)
????????min_sim_?=?min_sim?+?(max_sim-min_sim)?*?(1-np.exp(-alpha*idx))
????????if?task_count?%?10?==?0:
????????????log?=?'%s?in?cluster,?%s?in?queue,?%s?tasks?done,?%s?min_sim'%(len(cluster),?queue_count,?task_count,?min_sim_)
????????????print?log
????????for?i,j?in?sims:
????????????if?j?>=?min_sim_:
????????????????if?i?not?in?cluster?and?is_good(i):?#?is_good是人工寫的過濾規則
????????????????????queue.put((idx+1,?i))
????????????????????if?i?not?in?cluster?and?is_good(i):
????????????????????????cluster.append(i)
????????????????????queue_count?+=?1
????return?cluster
規則過濾
總的來說,無監督算法始終是難以做到完美的,在工程上,常見的方法就是人工觀察結果然后手寫一些規則來處理。在這個任務中,由于前面是純無監督的,哪怕進行了語義聚類,還是會出來一些非電力專業詞匯(比如“麥克斯韋方程”),甚至還保留一些“非詞”,所以我寫了一通規則來過濾(寫得有點丑):
????if?re.findall(u'[\u4e00-\u9fa5]',?w)?\
????????and?len(i)?>=?2\
????????and?not?re.findall(u'[較很越增]|[多少大小長短高低好差]',?w)\
????????and?not?u'的'?in?w\
????????and?not?u'了'?in?w\
????????and?not?u'這'?in?w\
????????and?not?u'那'?in?w\
????????and?not?u'到'?in?w\
????????and?not?w[-1]?in?u'為一人給內中后省市局院上所在有與及廠稿下廳部商者從獎出'\
????????and?not?w[0]?in?u'每各該個被其從與及當為'\
????????and?not?w[-2:]?in?[u'問題',?u'市場',?u'郵件',?u'合約',?u'假設',?u'編號',?u'預算',?u'施加',?u'戰略',?u'狀況',?u'工作',?u'考核',?u'評估',?u'需求',?u'溝通',?u'階段',?u'賬號',?u'意識',?u'價值',?u'事故',?u'競爭',?u'交易',?u'趨勢',?u'主任',?u'價格',?u'門戶',?u'治區',?u'培養',?u'職責',?u'社會',?u'主義',?u'辦法',?u'干部',?u'員會',?u'商務',?u'發展',?u'原因',?u'情況',?u'國家',?u'園區',?u'伙伴',?u'對手',?u'目標',?u'委員',?u'人員',?u'如下',?u'況下',?u'見圖',?u'全國',?u'創新',?u'共享',?u'資訊',?u'隊伍',?u'農村',?u'貢獻',?u'爭力',?u'地區',?u'客戶',?u'領域',?u'查詢',?u'應用',?u'可以',?u'運營',?u'成員',?u'書記',?u'附近',?u'結果',?u'經理',?u'學位',?u'經營',?u'思想',?u'監管',?u'能力',?u'責任',?u'意見',?u'精神',?u'講話',?u'營銷',?u'業務',?u'總裁',?u'見表',?u'電力',?u'主編',?u'作者',?u'專輯',?u'學報',?u'創建',?u'支持',?u'資助',?u'規劃',?u'計劃',?u'資金',?u'代表',?u'部門',?u'版社',?u'表明',?u'證明',?u'專家',?u'教授',?u'教師',?u'基金',?u'如圖',?u'位于',?u'從事',?u'公司',?u'企業',?u'專業',?u'思路',?u'集團',?u'建設',?u'管理',?u'水平',?u'領導',?u'體系',?u'政務',?u'單位',?u'部分',?u'董事',?u'院士',?u'經濟',?u'意義',?u'內部',?u'項目',?u'建設',?u'服務',?u'總部',?u'管理',?u'討論',?u'改進',?u'文獻']\
????????and?not?w[:2]?in?[u'考慮',?u'圖中',?u'每個',?u'出席',?u'一個',?u'隨著',?u'不會',?u'本次',?u'產生',?u'查詢',?u'是否',?u'作者']\
????????and?not?(u'博士'?in?w?or?u'碩士'?in?w?or?u'研究生'?in?w)\
????????and?not?(len(set(w))?==?1?and?len(w)?>?1)\
????????and?not?(w[0]?in?u'一二三四五六七八九十'?and?len(w)?==?2)\
????????and?re.findall(u'[^一七廠月二夕氣產蘭丫田洲戶尹尸甲乙日卜幾口工舊門目曰石悶匕勺]',?w)\
????????and?not?u'進一步'?in?w:
????????return?True
????else:
????????return?False
至此,我們就可以完整地執行這個算法了:
start_words?=?[u'電網',?u'電壓',?u'直流',?u'電力系統',?u'變壓器',?u'電流',?u'負荷',?u'發電機',?u'變電站',?u'機組',?u'母線',?u'電容',?u'放電',?u'等效',?u'節點',?u'電機',?u'故障',?u'輸電線路',?u'波形',?u'電感',?u'導線',?u'繼電',?u'輸電',?u'參數',?u'無功',?u'線路',?u'仿真',?u'功率',?u'短路',?u'控制器',?u'諧波',?u'勵磁',?u'電阻',?u'模型',?u'開關',?u'繞組',?u'電力',?u'電廠',?u'算法',?u'供電',?u'阻抗',?u'調度',?u'發電',?u'場強',?u'電源',?u'負載',?u'擾動',?u'儲能',?u'電弧',?u'配電',?u'系數',?u'雷電',?u'輸出',?u'并聯',?u'回路',?u'濾波器',?u'電纜',?u'分布式',?u'故障診斷',?u'充電',?u'絕緣',?u'接地',?u'感應',?u'額定',?u'高壓',?u'相位',?u'可靠性',?u'數學模型',?u'接線',?u'穩態',?u'誤差',?u'電場強度',?u'電容器',?u'電場',?u'線圈',?u'非線性',?u'接入',?u'模態',?u'神經網絡',?u'頻率',?u'風速',?u'小波',?u'補償',?u'電路',?u'曲線',?u'峰值',?u'容量',?u'有效性',?u'采樣',?u'信號',?u'電極',?u'實測',?u'變電',?u'間隙',?u'模塊',?u'試驗',?u'濾波',?u'量測',?u'元件',?u'最優',?u'損耗',?u'特性',?u'諧振',?u'帶電',?u'瞬時',?u'阻尼',?u'轉速',?u'優化',?u'低壓',?u'系統',?u'停電',?u'選取',?u'傳感器',?u'耦合',?u'振蕩',?u'線性',?u'信息系統',?u'矩陣',?u'可控',?u'脈沖',?u'控制',?u'套管',?u'監控',?u'汽輪機',?u'擊穿',?u'延時',?u'聯絡線',?u'矢量',?u'整流',?u'傳輸',?u'檢修',?u'模擬',?u'高頻',?u'測量',?u'樣本',?u'高級工程師',?u'變換',?u'試樣',?u'試驗研究',?u'平均值',?u'向量',?u'特征值',?u'導體',?u'電暈',?u'磁通',?u'千伏',?u'切換',?u'響應',?u'效率']
cluster_words?=?find_words(start_words,?min_sim=0.6,?alpha=0.35)
result2?=?result[cluster_words].sort_values(ascending=False)
idxs?=?[i?for?i?in?result2.index?if?is_good(i)]
pd.Series([i?for?i?in?idxs?if?len(i)?>?2][:10000]).to_csv('result_1_2.csv',?encoding='utf-8',?header=None,?index=None)
最終結果(部分):
發電機
變電站
過電壓
可靠性
控制器
斷路器
分布式
輸電線路
數學模型
濾波器
電容器
故障診斷
神經網絡
直流電壓
等離子體
聯絡線
傳感器
汽輪機
晶閘管
電動機
約束條件
數據庫
可行性
持續時間
整流器
穩定性
調節器
電磁場
后記感想
本文的算法在榜上的成績大約是?0.22?左右,封榜時排在?100?名左右,榜首已經是?0.49?了,所以從成績來看其實沒什么值得炫耀的。不過當時聽說不少人拿現成的專業詞典去做字標注,所以當時就沒做下去了。要是真的那樣子的話,我覺得就很沒意思了。
總之,本文算是提供了一個無監督抽取專業詞的實現模版,如果讀者覺得有可取之處,大方取之即可;如果覺得一無是處,敬請無視它。
點擊以下標題查看作者其他文章:?
變分自編碼器VAE:原來是這么一回事?|?附源碼
再談變分自編碼器VAE:從貝葉斯觀點出發
變分自編碼器VAE:這樣做為什么能成?
簡單修改,讓GAN的判別器秒變編碼器
深度學習中的互信息:無監督提取特征
全新視角:用變分推斷統一理解生成模型
可逆ResNet:極致的暴力美學
深度學習中的Lipschitz約束:泛化與生成模型
#投 稿 通 道#
?讓你的論文被更多人看到?
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢??答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly?或許可以成為一座橋梁,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。?
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是最新論文解讀,也可以是學習心得或技術干貨。我們的目的只有一個,讓知識真正流動起來。
??來稿標準:
? 稿件確系個人原創作品,來稿需注明作者個人信息(姓名+學校/工作單位+學歷/職位+研究方向)?
? 如果文章并非首發,請在投稿時提醒并附上所有已發布鏈接?
? PaperWeekly 默認每篇文章都是首發,均會添加“原創”標志
??投稿郵箱:
??投稿郵箱:hr@paperweekly.site?
? 所有文章配圖,請單獨在附件中發送?
? 請留下即時聯系方式(微信或手機),以便我們在編輯發布時和作者溝通
?
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
關于PaperWeekly
PaperWeekly?是一個推薦、解讀、討論、報道人工智能前沿論文成果的學術平臺。如果你研究或從事?AI?領域,歡迎在公眾號后臺點擊「交流群」,小助手將把你帶入?PaperWeekly?的交流群里。
▽ 點擊 |?閱讀原文?| 查看作者博客
總結
以上是生活随笔為你收集整理的实战分享之专业领域词汇无监督挖掘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 读完这些论文和代码,你就能在搜狐算法大赛
- 下一篇: 面向新闻媒体的命名实体识别技术