當前nlp任務中關於信息抽取,主要集中在關鍵詞的抽取,一般基於TF-IDF的關鍵詞抽取、基於TextRank的關鍵詞抽取、基於Word2Vec詞聚類的關鍵詞抽取,以及多種演算法相融合的關鍵詞抽取等。這裡我們探討一個另一類nlp任務,技能詞抽取或者說領域詞抽取,很少有文章涉及相關並給出代碼。這裡我將結合這類nlp任務進行代碼實踐。這裡的實例將通過爬去的職位jd來抽取其中的技能詞。
領域詞或者技能詞抽取常用的方法:互信息,文檔頻率,信息增益,卡方檢驗等。 這裡重點介紹兩種方法:卡方檢驗和信息增益來提取特徵詞。
卡方檢驗的數學原理:卡方越大,則特徵越重要。
按照卡方的定義,若隨機變數 相互獨立,且隨機變數 服從正態分布,則定義隨機變數 ,其分布服從 分布。則當原假設(變數之間相互獨立)是正確的時候,卡方值越趨近於0,反之卡方越大,則拒絕原假設,變數相關。
由於實際當中數據的離散形式,原假設 為真,可認為樣本落入某個區間的頻率與概率應很接近,這裡考慮皮爾遜檢驗統計量:
對於表格第一行第一列50(A),現在計算期望值 ,注意到數字50表示包含深度學習的JD中有50個JD為"自然語言處理工程師",那麼對應的期望可理解為JD為"自然語言處理工程師"中有多少是包含深度學習的。
因此 表示所有JD數據中包含深度學習的頻率,若不相關,則 應該是自然語言處理工程師包含深度學習的期望值。
從而 ,.
類似的理解可以計算出
,
最終可以計算出"深度學習"和"自然語言處理工程師"的卡方值:
.
步驟如下:
給出關鍵代碼:
# 統計每一類別下單詞的數量 categories = ["nlp", "deep_learing", "kg", "machine_learning", "data_mining"] words_count = dict([(c, {}) for c in categories]) # 統計所有的單詞數量情況 total_word_count = {} for c in categories: if c == "nlp": for ele in nlp_job_desc_clean: for word in ele: words_count[c].setdefault(word, 0) words_count[c][word] += 1 total_word_count.setdefault(word, 0) total_word_count[word] += 1
if c == "deep_learing": for ele in dl_job_desc_clean: for word in ele: words_count[c].setdefault(word, 0) words_count[c][word] += 1 total_word_count.setdefault(word, 0) total_word_count[word] += 1
if c == "kg": for ele in kg_job_desc_clean: for word in ele: words_count[c].setdefault(word, 0) words_count[c][word] += 1 total_word_count.setdefault(word, 0) total_word_count[word] += 1
if c == "machine_learning": for ele in ml_job_desc_clean: for word in ele: words_count[c].setdefault(word, 0) words_count[c][word] += 1 total_word_count.setdefault(word, 0) total_word_count[word] += 1
if c == "data_mining": for ele in dm_job_desc_clean: for word in ele: words_count[c].setdefault(word, 0) words_count[c][word] += 1 total_word_count.setdefault(word, 0) total_word_count[word] += 1 # 計算每一類別下包含詞的數量 total_words_by_category = {} for category in categories: total_words_by_category[category] = sum(words_count[category].values()) # 計算每一個類別每一個單詞的卡方 chi_sq_by_category = dict([(c, {}) for c in categories]) for word in total_word_count.keys(): for category in categories: # N11:表某一類別下包含某個詞的數量 N11 = words_count[category].get(word, 0) # N10:表示其他類別包含某個詞的數量 N10 = total_word_count[word] - N11 # N01:表示此類別不包含這個詞的單詞數量 N01 = total_words_by_category[category] - N11 # N00:表示其他類別不包含這個單詞的單詞總數 N00 = sum(total_words_by_category[category] - N11 for c in categories if c != category) N = N11 + N10 + N01 + N00 chi_sq = 1.0*N*(N11*N00-N10*N01)**2/((N11+N10)*(N11+N01)*(N00+N01)*(N00+N10)) chi_sq_by_category[category][word] = chi_sq # 按照卡方的大小排序輸出到本地文件 with open("category_words.csv", "w", encoding="utf-8") as f: writer = csv.writer(f) for category in categories: word_chi_list = list(reversed(sorted(chi_sq_by_category[category].items(), key= lambda x:x[1])))[:1000] word_chi_cat = [] for word, chi in word_chi_list: word_chi_cat.append([word, chi, category]) writer.writerows(word_chi_cat)
結果部分輸出如下:
數據,1934.6760870027745,nlp 自然語言,1464.8865979301315,nlp NLP,1388.0876278390624,nlp 數據挖掘,1271.120149134985,nlp 文本,1203.2663612466956,nlp 分詞,1110.2437471563965,nlp 語義,1107.6088372648412,nlp 處理,893.8005019396106,nlp 數據挖掘,894.425714802237,machine_learning 分析,849.364072365675,machine_learning 經驗,685.4235591202735,machine_learning 數據,663.6835255954395,machine_learning 熟悉,564.9863796044659,machine_learning 工作,536.3657771984433,machine_learning 業務,525.0699906150004,machine_learning Wecash,523.4344527862742,machine_learning 數據分析,479.5537323258305,machine_learning 挖掘,453.5459692097794,machine_learning 開發,450.23639496658603,machine_learning
信息增益的數學原理:信息增益越大,則特徵越重要。
對於一個隨機變數 ,若取值為 ,且相應的概率為 ,則 的信息熵定義為:
衡量的是一個隨機變數取值越多,則包含的信息量就越大。
而信息增益衡量的是一個特徵詞對於JD的穩點程度,或者說是對類別(職位jd)的重要程度或者相關程度。即有沒有這個詞,對JD的影響度。即:
其中 為條件熵。
說明:
表示所有文檔中沒有出現特徵 的頻率。
表示第 類文檔中沒有出現特徵 在所有文檔中不出現特徵 的頻率。
表示所有文檔中出現特徵 的頻率。
表示第 類文檔中出現特徵 在所有文檔中出現特徵 的頻率。
# 開始計算信息增益 def info_gain(x, y, k=None): total_num = len(y) num_class = {} num_pos_per_class = {} num_neg_per_class = {} for xi, yi in zip(x, y): num_class[yi] = num_class.get(yi, 0) + 1 for index, xii in enumerate(xi): if index not in num_pos_per_class: num_pos_per_class[index] = {} num_neg_per_class[index] = {} if yi not in num_pos_per_class[index]: num_pos_per_class[index][yi] = 0 num_neg_per_class[index][yi] = 0 if xii != 0: num_pos_per_class[index][yi] = num_pos_per_class[index].get(yi) + 1 else: num_neg_per_class[index][yi] = num_neg_per_class[index].get(yi) + 1 # 上述就計算出來每一個辭彙在每一類的分布情況
# 對每一個單詞在對多個類別的數據進行加總 num_pos_word = {} for w, dic in num_pos_per_class.items(): num_pos_word[w] = sum(num_pos_per_class[w].values()) num_neg_word = dict([(w, total_num - num) for w, num in num_pos_word.items()])
# 計算熵,先計算關於類別的熵值 HD = 0 for c, num in num_class.items(): p = float(num)/total_num HD = HD - p*math.log(p, 2)
# 開始計算每個詞的信息增益 IG = {} for w in num_pos_per_class.keys(): POS = 0 for yi, num in num_pos_per_class[w].items(): p = (float(num)+0.00001)/ (num_pos_word[w] + 0.00001*total_num) POS = POS - p*math.log(p, 2)
NEG = 0 for yi, num in num_neg_per_class[w].items(): p = (float(num)+0.00001)/ (num_neg_word[w] + 0.00001*total_num) NEG = NEG - p*math.log(p, 2)
p = float(num_pos_word[w])/total_num IG[w] = round(HD - p*POS - (1 - p)*NEG, 4) IG = sorted(IG.items(), key=lambda x:x[1], reverse=True) if k == None: return IG else: return IG[:k]
部分結果如下:
[自然語言, 分詞, 深度, 數據, nlp, 語義, 處理, 識別, 數據挖掘, 實體, 視覺, 數據分析, caffe, 詞性, 文本, 領域, 機器, sql, 圖譜, 中文, 情感, 問答, 學習, 圖像處理, cuda, cnn, 對話, 建模, 圖像, tensorflow, 業務, 模式識別, 標註, 論文, hive, hadoop, theano, 命名, 檢測, 發表, 新詞, sas, 用戶, 運營, 資料庫, spss, 數據倉庫, rnn, 行為, 挖掘, 句法分析, 統計學, 語音, 自動, 人工智慧, 基礎理論, 抽取, 知識, 碩士, torch, 會議, 機器翻譯, 物體, 圖像識別, 數據模型, 人臉, 研發, 研究, 知識庫,
總結:這裡提取技能詞分別使用了卡方檢驗和信息增益,事實上在提取技能詞的過程中使用了多種方法,例如會構建模板方法,熟悉**,掌握**等這樣的無監督方法,去提取高質量的技能詞,也會結合tfidf,crf等等。乾貨不成文,希望大家多關注我的知乎,歡迎點贊和評論。
參考:
推薦閱讀: