利用word2vec對關鍵詞進行聚類

轉載自:http://blog.csdn.net/zhaoxinfan/article/details/11069485

繼上次提取關鍵詞之後,項目組長又要求我對關鍵詞進行聚類。說實話,我不太明白對關鍵詞聚類跟新聞推薦有什麼聯繫,不過他說什麼我照做就是了。

按照一般的思路,可以用新聞ID向量來表示某個關鍵詞,這就像廣告推薦系統裏面用用戶訪問類別向量來表示用戶一樣,然後就可以用kmeans的方法進行聚類了。不過對於新聞來說存在一個問題,那就量太大,如果給你十萬篇新聞,那每一個關鍵詞將需要十萬維的向量表示,隨着新聞數迅速增加,那維度就更大了,這計算起來難度太大。於是,這個方法思路簡單但是不可行。

好在我們有word2vec這個工具,這是google的一個開源工具,能夠僅僅根據輸入的詞的集合計算出詞與詞直接的距離,既然距離知道了自然也就能聚類了,而且這個工具本身就自帶了聚類功能,很是強大。下面正式介紹如何使用該工具進行詞的分析,關鍵詞分析和聚類自然也就包含其中了。word2vec官網地址看這裏:https://code.google.com/p/word2vec/

1、尋找語料

要分析,第一步肯定是收集數據,這裏不可能一下子就得到所有詞的集合,最常見的方法是自己寫個爬蟲去收集網頁上的數據。不過,如果不需要實時性,我們可以使用別人提供好的網頁數據,例如搜狗2012年6月到7月的新聞數據:http://www.sogou.com/labs/dl/ca.html 直接下載完整版,註冊一個帳號,然後用ftp下載,ubuntu下推薦用filezilla





下載得到的數據有1.5G



2、分詞

我們得到的1.5的數據是包含一些html標籤的,我們只需要新聞內容,也就是content其中的值。首先可以通過簡單的命令把非content的標籤幹掉

[plain] view plaincopy
  1. cat news_tensite_xml.dat | iconv -f gbk -t utf-8 -c | grep "<content>"  > corpus.txt  

得到了corpus.txt文件只含有content標籤之間的內容,再對內容進行分詞即可,這裏推薦使用之前提到過的ANSJ,沒聽過的看這裏:http://blog.csdn.net/zhaoxinfan/article/details/10403917

下面是調用ANSJ進行分詞的程序:

[java] view plaincopy
  1. import java.util.HashSet;  
  2. import java.util.List;  
  3. import java.util.Set;  
  4. import java.io.BufferedReader;  
  5. import java.io.BufferedWriter;  
  6. import java.io.File;  
  7. import java.io.FileInputStream;  
  8. import java.io.FileReader;  
  9. import java.io.FileWriter;  
  10. import java.io.IOException;  
  11. import java.io.InputStreamReader;  
  12. import java.io.PrintWriter;  
  13. import java.io.StringReader;  
  14. import java.util.Iterator;  
  15.   
  16. import love.cq.util.IOUtil;  
  17.   
  18. import org.ansj.app.newWord.LearnTool;  
  19. import org.ansj.domain.Term;  
  20. import org.ansj.recognition.NatureRecognition;  
  21. import org.ansj.splitWord.Analysis;  
  22. import org.ansj.splitWord.analysis.NlpAnalysis;  
  23. import org.ansj.splitWord.analysis.ToAnalysis;  
  24. import org.ansj.util.*;  
  25. import org.ansj.recognition.*;  
  26.   
  27.   
  28. public class test {  
  29.     public static final String TAG_START_CONTENT = "<content>";  
  30.     public static final String TAG_END_CONTENT = "</content>";  
  31.       
  32.     public static void main(String[] args) {  
  33.         String temp = null ;  
  34.           
  35.         BufferedReader reader = null;  
  36.         PrintWriter pw = null;  
  37.         try {  
  38.             reader = IOUtil.getReader("corpus.txt""UTF-8") ;  
  39.             ToAnalysis.parse("test 123 孫") ;  
  40.             pw = new PrintWriter("resultbig.txt");  
  41.             long start = System.currentTimeMillis()  ;  
  42.             int allCount =0 ;  
  43.             int termcnt = 0;  
  44.             Set<String> set = new HashSet<String>();  
  45.             while((temp=reader.readLine())!=null){  
  46.                 temp = temp.trim();  
  47.                 if (temp.startsWith(TAG_START_CONTENT)) {  
  48.                     int end = temp.indexOf(TAG_END_CONTENT);  
  49.                     String content = temp.substring(TAG_START_CONTENT.length(), end);  
  50.                     //System.out.println(content);  
  51.                     if (content.length() > 0) {  
  52.                         allCount += content.length() ;  
  53.                         List<Term> result = ToAnalysis.parse(content);  
  54.                         for (Term term: result) {  
  55.                             String item = term.getName().trim();  
  56.                             if (item.length() > 0) {  
  57.                                 termcnt++;  
  58.                                 pw.print(item.trim() + " ");  
  59.                                 set.add(item);  
  60.                             }  
  61.                         }  
  62.                         pw.println();  
  63.                     }  
  64.                 }  
  65.             }  
  66.             long end = System.currentTimeMillis() ;  
  67.             System.out.println("共" + termcnt + "個term," + set.size() + "個不同的詞,共 "  
  68.                     +allCount+" 個字符,每秒處理了:"+(allCount*1000.0/(end-start)));  
  69.         } catch (IOException e) {   
  70.             e.printStackTrace();  
  71.         } finally {  
  72.             if (null != reader) {  
  73.                 try {  
  74.                     reader.close();  
  75.                 } catch (IOException e) {  
  76.                     e.printStackTrace();  
  77.                 }  
  78.             }  
  79.             if (null != pw) {  
  80.                 pw.close();  
  81.             }  
  82.         }  
  83.     }  
  84. }  

經過對新聞內容分詞之後,得到的輸出文件resultbig.txt有2.2G,其中的格式如下:


這個文件就是word2vec工具的輸入文件


3、本地運行word2vec進行分析

首先要做的肯定是從官網上下載word2vec的源碼:http://word2vec.googlecode.com/svn/trunk/ ,然後把其中makefile文件的.txt後綴去掉,在終端下執行make操作,這時能發現word2vec文件夾下多了好幾個東西。接下來就是輸入resultbig.txt進行分析了:

[plain] view plaincopy
  1. ./word2vec -train resultbig.txt -output vectors.bin -cbow 0 -size 200 -window 5 -negative 0 -hs 1 -sample 1e-3 -threads 12 -binary 1  

這裏我們指定輸出爲vectors.bin文件,顯然輸出到文件便於以後重複利用,省得每次都要計算一遍,要知道處理這2.2G的詞集合需要接近半個小時的時間:




下面再輸入計算距離的命令即可計算與每個詞最接近的詞了:

[plain] view plaincopy
  1. ./distance vectors.bin  

這裏列出一些有意思的輸出:





怎麼樣,是不是覺得還挺靠譜的?補充一點,由於word2vec計算的是餘弦值,距離範圍爲0-1之間,值越大代表這兩個詞關聯度越高,所以越排在上面的詞與輸入的詞越緊密。

至於聚類,只需要另一個命令即可:

[plain] view plaincopy
  1. ./word2vec -train resultbig.txt -output classes.txt -cbow 0 -size 200 -window 5 -negative 0 -hs 1 -sample 1e-3 -threads 12 -classes 500  

按類別排序:

[plain] view plaincopy
  1. sort classes.txt -k 2 -n > classes.sorted.txt  



後記:如果想要了解word2vec的實現原理,應該讀一讀官網後面的三篇參考文獻。顯然,最主要的應該是這篇: Distributed Representations of Words and Phrases and their Compositionality   遺憾的是這篇文章已投稿但還未發佈

最後感謝曉陽童鞋向我提到這個工具,不愧是立志要成爲NLP專家的人。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章