Text Tag Recommendation
--------2013/12/20
一: 背景
Kaggle上 facebook招聘比賽III。任務要求是給定文本中抽取關鍵詞,這裏稱作tag吧。
訓練集是Stack Exchange sites上面的大量問答文本,每個post上面有網頁的title ,body, 用戶打的tags。
示例如下:
測試集是也是同樣的文本信息,但是沒有tags,這次比賽的任務就是讓參賽者從文本中預測出文本的tags,比如上面的java,unit-testing....
二: 評估指標
F1-score= 2*(precision *recall)/(precision+ recall)
這裏的問題是 任務是沒有告訴你文本的tags數量。如果你預測tags數量過多,precision就會很低,但是,recall相對高點。這裏的任務,預測tags數量也是很重要的一方面。
三:訓練集和測試集數據分佈
訓練集有600w+的文本,tags數量1-5,訓練集合中tags數量是4w+,
測試集合大約200w+。
四: tag 預測推薦算法
1: 數據預處理
最開始時候我從文本中剔除掉停用詞和對詞進行詞幹提取,但是效果沒有提升。後來,我放棄停止詞和詞幹的處理。在progamming中有一些停止詞還是有價值的。
同時我把類似這樣的詞: opt/lib/unix 分開成opt,lib, unix等等。
Title全部信息,body是抽取其中text信息。
2: cross-validation
從600w+訓練樣本中抽取前100w樣本做cv訓練集,接着從取10w樣本作爲cv 測試集(其實從取1w cv測試集和10w測試集評估結果差別不大)。
3:訓練模型
3.1 文本語料
從訓練樣本中計算詞term的詞頻字典,term-tag共同出現頻率詞典。
訓練樣本的文本信息只包括title信息,沒有body信息,一方面大量訓練樣本已包含足夠
多的文本語料,而body含有很多噪音數據,加入訓練樣本中,會造成預測F1值降低。
文本語料中採用2-grams和1-grams,相對只用1-grams,F1在測試集上表現能提高0.02的精度。
去除掉一些低頻詞,低頻tag,高頻詞。低頻的,我們認爲不可靠。
def is_pass_record(prob_term_1,prob_target_1,prob_term_1_target_1,):
if prob_term_1_target_1 <1e-5:
return 1
if prob_term_1 < 0.00001 or prob_target_1< 0.00001 or prob_term_1 > 0.1 :
return 1
return 0
3.2 計算單詞和tag的相關性得分
(1)互信息相關得分
Mutual_Score(term,.tag) = P(tag| term) -P(tag)
(2) z-score相關得分:
Z_Score(term, tag) =
(3) Ensemble 相關得分
先對Mutual_Score 和Z_Score進行歸一化:
Ensemble過程中mutual score 的權重分配是0.7 和 z-score的權重是0.3。
(4)詞的權重
根據訓練樣本,計算詞的權重。不同單詞,它關聯的tags是否是最終預測的tag的重要程度不一樣。
方法有兩種:
方法1:tf-idf值作爲單詞的重要性值。
方法2: entropy值作爲詞重要性
根據上面得到詞頻和詞-tag頻值,可以計算Wj值
同時歸一化中分母換成norm-2 :
最終採用了entropy值作爲詞的權重。
4: 預測模型
4.1 預測樣本文本信息
預測文本信息主要是通過get_ensemble_title_and_body(title, body)函數得到的,綜合利用title和body文本信息。
def extract_body(body):
body_term = body.split()
body_li = []
body_li.extend(body_term[:20])
body_li.extend(body_term[-20:-1])
text = ' '.join(body_li)
return text
def get_ensemble_title_and_body(title, body):
title_w = 5
doc = (title + ' ') * title_w + body
return doc
4.2 預測
(1)通過4.1獲得每一個post的文本信息,根本文本中單詞的權重和單詞-tag關聯性預測tags:
(2)獲取得分最高的前5個tags。
pred_tags = [[tag ,w] for tag, w in pred_tags_dict.iteritems()]
pred_tags = heapq.nlargest(5, pred_tags, key=lambda x: x[1])
(3)預測最終tag數目
根據pred_tags的tag得分,把低得分的tag去掉。
Threshold設定爲0.7
def get_estimate_tags_num_by_association_prob(pred_tags):
threshold = 0.7
i = 0
sum_w = 0
total = np.sum([item[1] for item in pred_tags])
for item in pred_tags:
sum_w += item[1]
i +=1
if sum_w > threshold * total:
break
return i
這一步提升很明顯,提升0.03左右。
(4)cv結果
cv 10w結果大約在0.508。
precision: 0.505054166667
recall: 0.511442333333
F1-Score: 0.508228176801
(5)最後提交
這次比賽的的測試集中大約有119w的測試樣本在訓練集合中出現。約佔總體樣本的60%。
所以,第一步從測試集中把這119w樣本找出來,用訓練集中對應的tag預測。剩下40%的樣本用上面的說的關聯預測算法來做。
找相同文檔的算法:
給每個訓練集單詞建立倒排索引(這次把訓練樣本中去除停用詞和詞幹處理,且是binary索引,不考慮tf-idf)。
Kaggle 上leaderboard上,我是最後2周才加入的(2013/12/08提交第一次結果,2013/12/20 比賽結束),提交了7個結果,結果爲0.72,(最高是0.81)
排名是 93, 總的隊伍數目是367. 大約25% 左右。這裏說明下,0.72對應的的cv結果只有0.39左右。
所以,我在比賽結束後的優化結果是cv是0.50,相信leaderboard上的結果會有相對較高的提升,後續加上結果, 結果是
五:總結
這次kaggle比賽,對text挖掘有了比較不錯的提高,在實驗中,試驗了很多方案,比如KNN,topic model。 KNN的速度太慢,即使在建立tf-idf倒排索引的情況下,依然滿足不了需求,同時在這次比賽中knn精度也沒什麼好的表現,值得提到的是,對於相似文本的尋找算法,可以借鑑Wand算法來提升速度,這也是這個數據集後續可以再做的地方,text去重性能計算。Topic model是一個組粒度的預測方法,對於本次樣本中基本上都是program 文本,所以這種粗粒度的預測方法效果很差。
可以總結一句話:通過實驗,我們才能知道什麼方法纔是可行的,什麼是不可行的。
六 : 參考文獻:
[1] http://www.kaggle.com/c/facebook-recruiting-iii-keyword-extraction
[2] Stanley C, Byrne M D. Predicting Tags for StackOverflow Posts[J].
[3] 司憲策. 基於內容的社會標籤推薦與分析研究 [D][D]. 北京: 清華大學計算機科學與技術系, 2010.
[4] Eisterlehner F, Hotho A, Jäschke R. ECML PKDD Discovery Challenge 2009 (DC09)[J]. CEUR-WS. org, 2009, 497.