天天看點

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

  在上篇實作了電影詳情和短評資料的抓取。到目前為止,已經抓了2000多部電影電視以及20000多的短評資料。

  資料本身沒有規律和價值,需要通過分析提煉成知識才有意義。抱着試試玩的想法,準備做一個有關情感分析方面的統計,看看這些評論裡面的小夥伴都抱着什麼态度來看待自己看過的電影,懷着何種心情寫下的短評。

  鑒于爬取的是短評資料,少則10來個字,多則百來個字,網上查找了下,發現Google開源的Word2Vec比較合适,于是今天搗鼓了一天,把自己遇到的問題和運作的結果在這裡做個總結。

  Word2Ve是google 推出的做詞嵌入(word embedding)的開源工具。 簡單的說,它在給定的語料庫上訓練一個模型,然後會輸出所有出現在語料庫上的單詞的向量表示,這個向量稱為"word embedding"。基于這個向量表示,可以計算詞與詞之間的關系,例如相似性(同義詞等),語義關聯性(中國 - 北京 = 英國 - 倫敦)等。

  算法的原理如果有興趣,可以找資料了解。

  這裡使用Word2Vec的大緻流程如下:

    1. 擷取資料(這裡是豆瓣電影短評資料)

    2. 資料處理(将短評資料使用分詞器分詞,并以空格連接配接分詞結果)

    3. 訓練資料(将上述處理好符合要求的資料作為輸入進行訓練,得到訓練模型)

    4. 載入訓練模型,分析感興趣的次元(比如,近義詞分詞,關聯詞分析)

    Github: https://github.com/NLPchina/Word2VEC_java

擷取資料

  資料就用短評資料,2萬多條,對應的大概是2000多部的電影,一部電影抓的短評數在10條左右。(這個樣本量很小了,數量越大,越來接近真實情況。)

  将這些短評資料中從資料庫(Mysql)中抽出來放入一個txt文檔中。

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

資料處理

  主要是對短評分詞。顯然這裡要用到中文分詞器,可以選的很多,比如Ansj、IKAnalyzer等等。在我看來都差不多,無非就是做個分詞,當然了起碼需要過濾停用詞,比如“啊”“哦”“的”這樣幾乎沒有意義的詞往往出現的頻率還比較高,如果不過濾會增加沒有意義的權重,影響結果。網上找了個停用詞庫,已經放雲盤上了,點選這裡可以下載下傳。

  起初準備用Ansj,測試代碼也寫好了,可以發現停用詞庫總是加載不上去。

package com.jackie.crawler.doubanmovie.utils;

import org.ansj.recognition.impl.FilterRecognition;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Filter;

/**
 * Created by Administrator on 2016/12/3 0003.
 */
public class TokenizerAnalyzerUtils {

    public TokenizerAnalyzerUtils(){

    }

    public static void main(String args[]) {
        getAnalyzerResult("不同于計算機,人類一睜眼就能迅速看到和看明白一個場景,因為人的大腦皮層至少有一半以上海量神經元參與了視覺任務的完成。");
    }

    public static String getAnalyzerResult(String input) {
        HashMap<String, String> filterMap = new HashMap<String, String>();
        List<String> stopWordsList = getStopWordsList();
        FilterRecognition filterRecognition = new FilterRecognition();
        filterRecognition.insertStopWords(stopWordsList);
        System.out.println(ToAnalysis.parse(input));
        return null;
    }


    private static List<String> getStopWordsList(){
        List<String> stopWordList = null;
        File stopWordFile = new File(TokenizerAnalyzerUtils.class.getResource("/library/stopwords.dic").getPath());
        try {
            stopWordList = FileUtils.readLines(stopWordFile, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("fail to load stop word dictionary");
        }
        return stopWordList;
    }

}           

複制

  有人說Ansj現在已經沒人維護了,而且在找資料的過程中,發現很多博文有不一緻的地方,或者可能是沒有交代清楚吧,比如這裡的FileRecognition類是在5.0.2版本才有的,而之前的一個版本居然是0.9,0.9版本用的是FilterModifWord,總之沒有成功加載到停用詞庫,而且顯示的結果也不是我想要的。  

  得到結果如下(包含了停用詞的,還有标點符号)

不同于/c,計算機/n,,/w,人類/n,一/m,睜眼/n,就/d,能/v,迅速/ad,看到/v,和/c,看/v,明白/v,一個/m,場景/n,,/w,因為/c,人/n,的/uj,大腦皮層/l,至少/d,有/v,一半/m,以上/f,海量/n,神經元/n,參與/v,了/ul,視覺/n,任務/n,的/uj,完成/v,。/w           

複制

  後來開始用IKAnalyzer,使用IKAnalyzer倒是異常的順利

public class TokenizerAnalyzerUtils {

    public TokenizerAnalyzerUtils(){

    }


    public static void main(String args[]) throws IOException {
        String tokenizerResult = getAnalyzerResult("不同于計算機,人類一睜眼就能迅速看到和看明白一個場景,因為人的大腦皮層至少有一半以上海量神經元參與了視覺任務的完成。");
        System.out.println(tokenizerResult);
    }

    public static String getAnalyzerResult(String input) {
        StringReader sr=new StringReader(input);
        IKSegmenter ik=new IKSegmenter(sr, true);//true is use smart
        Lexeme lex=null;
        List<String> stopWordsList = getStopWordsList();
        StringBuilder stringBuilder = new StringBuilder();

        try {
            while((lex=ik.next())!=null){
                if(stopWordsList.contains(lex.getLexemeText())) {
                    continue;
                }
                stringBuilder.append(lex.getLexemeText() + Constants.BLANKSPACE);
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("failed to parse input content");
        }
        return stringBuilder.toString();
    }


    private static List<String> getStopWordsList(){
        List<String> stopWordList = null;
        File stopWordFile = new File(TokenizerAnalyzerUtils.class.getResource("/library/stopwords.dic").getPath());
        try {
             stopWordList = FileUtils.readLines(stopWordFile, Constants.ENCODING);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("fail to load stop word dictionary");
        }
        return stopWordList;
    }

}           

複制

  結果如下:

不同于 計算機 人類 睜眼 就能 明白 場景 為人 大腦皮層 至少有 一半 海量 神經元 參與 視覺            

複制

  這個結果我很滿意^^.

  于是這裡是IKAnalyzer完成了短評分詞,過程如下

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

  總共耗時20s左右,分詞後部分結果

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

注意

  這裡可能會遇到停用詞亂碼問題,在加載到停用詞檔案的時候會發現如下情況

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

  這時候需要打開停用詞檔案,将其設定以UTF-8編碼

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

  再次加載停用詞檔案,沒有亂碼情況

Java豆瓣電影爬蟲——使用Word2Vec分析電影短評資料

訓練資料

  有了上述的分詞後的檔案,就可以作為Word2Vec算法的輸入用來訓練模型了。

  這部分代碼可以參看上面的GitHub代碼中的Word2VEC.java類。這裡稍作修改,完成了資料分詞,模型訓練和使用。

Learn learn = new Learn();
learn.learnFile(new File("src/main/resource/library/comment/tokenizerResult.txt"));
learn.saveModel(new File("src/main/resource/library/comment/vector.mod"));           

複制

 這裡的tokenizerResult.txt是第二步分詞後結果儲存的檔案, vector.mod是用于存儲訓練後的模型的。

  執行的過程資訊如下

alpha:0.025	Progress: 0%
alpha:0.02425520272641302	Progress: 2%
alpha:0.023510777182349687	Progress: 5%
alpha:0.022766202946476896	Progress: 8%
alpha:0.02202244651555614	Progress: 11%
alpha:0.021277946625588077	Progress: 14%
alpha:0.020533446735620017	Progress: 17%
alpha:0.019789318575175605	Progress: 20%
alpha:0.019045041722921735	Progress: 23%
alpha:0.018301433983810438	Progress: 26%
alpha:0.0175555215216525	Progress: 29%
alpha:0.01681057555625606	Progress: 32%
alpha:0.01606689347124003	Progress: 35%
alpha:0.01532283965670035	Progress: 38%
alpha:0.01457930626349378	Progress: 41%
alpha:0.013835772870287213	Progress: 44%
alpha:0.013092090785271182	Progress: 47%
alpha:0.012348185662540964	Progress: 50%
alpha:0.011604503577524936	Progress: 53%
alpha:0.010859334574414304	Progress: 56%
alpha:0.010115429451684083	Progress: 59%
alpha:0.009371673020763324	Progress: 62%
alpha:0.008627619206223644	Progress: 65%
alpha:0.007880814593208949	Progress: 68%
alpha:0.007136091665526695	Progress: 71%
alpha:0.006392260888701207	Progress: 74%
alpha:0.005648430111875719	Progress: 77%
alpha:0.0049033354546698165	Progress: 80%
alpha:0.004158240797463914	Progress: 83%
alpha:0.0034141126370195035	Progress: 86%
alpha:0.0026687949420994093	Progress: 89%
alpha:0.001923551593084047	Progress: 92%
alpha:0.0011763752505457	Progress: 95%
Vocab size: 41817
Words in train file: 336265
sucess train over!           

複制

載入訓練模型,分析感興趣的次元

  載入模型,挑一個關鍵字/詞,就可以找到與之相關聯的近義詞,這裡挑了一些字詞做測試。

Word2VEC vec = new Word2VEC();
vec.loadJavaModel("src/main/resource/library/comment/vector.mod");

List<String> wordList = new ArrayList<String>();
wordList.add("爛片");
wordList.add("畫面");
wordList.add("星");
wordList.add("幽默");
for (String word : wordList) {
	System.out.println(word + "\t" +
			vec.distance(word));
}           

複制

  于是得到這樣的結果

爛片	[找出	0.8132788, 賠得	0.80554706, 國産	0.7825366, low.	0.7741908, com	0.7687579, 給錢	0.767631, 一連	0.7641382, 跑不了	0.7631712, 新銳	0.7623174, 同義詞	0.75706816, 穩準狠	0.7499448, 邊遠	0.7494703, 上榜	0.7416989, 發生地	0.7386223, 不露	0.73250484, 合璧	0.72888064, 強烈要求	0.72720224, ph	0.7261214, 看了	0.7227549, 800萬	0.7197921, 評為	0.7159896, 拍	0.7146899, 中國和中國香港	0.711021, 洗澡水	0.7107486, 尤其在	0.70475745, 資源回收筒	0.6954223, 圈錢	0.69508225, 茴	0.6948879, 預告	0.6924497, 名頭	0.69223696, 視訊短片	0.69158596, 爛	0.6871433, 海報	0.6825464, trejo	0.68159693, 好吧	0.68023074, visconti	0.6795105, 包場	0.6774618, 解衣	0.67674035, 恐怖片	0.673455]
畫面	[老套	0.89498436, 還算	0.8892616, 硬傷	0.88743657, 新意	0.88360095, 整部	0.88049525, 起碼	0.87579376, 效果	0.8723363, 剪輯	0.8638494, 整體	0.8612348, 制作	0.86086303, 昏昏欲睡	0.857126, 特效	0.85197854, 打鬥	0.85099876, 攝影	0.84997845, 品質	0.84938455, 六段	0.8493726, 邏輯	0.8487664, 弱智	0.84785265, 中規中矩	0.8462457, 緊湊	0.84502685, 抗戰	0.84373015, 亮點	0.84305227, 爛了	0.8415472, 狗血	0.8381111, 硬生生	0.8376552, 紀實	0.83595145, 湊合	0.8355879, 速度感	0.8338623, 小成本	0.8309567, 沒啥	0.82943296, 糟糕了	0.8270213, 套路	0.82666814, 津津有味	0.82574165, 拖沓	0.82400554, 六分	0.82379186, 設計	0.82302237, 不好	0.8223004, 動作	0.82218575, 場面	0.8185956]
星	[1	0.88681066, 3	0.85820633, 一顆	0.8508274, 4	0.8403223, 兩	0.83607787, 擠車	0.8331463, 100人	0.8301175, 拉薩	0.8287235, 小角	0.8236963, 2	0.8233104, 6條	0.8203937, 加	0.81798214, 大熱天	0.8086839, 5	0.807189, 1房	0.8015023, 怦然	0.7956209, 一星	0.7947131, 水瓶	0.7909956, 沈迷	0.7895043, 送給	0.78651863, 2.6	0.7715048, 12房	0.77091134, 猶豫	0.7638156, 五花八門	0.7578704, 顆	0.7568228, 我弟	0.75334215, 蠍	0.74807864, 居于	0.7474736, 翠	0.7466134, 吵着	0.74357903, 三顆	0.7402853, 鬧着	0.7341734, 羽泉	0.73370975, 保底	0.7296697, 扣	0.7204362, 絕壁	0.7204167, 彤	0.7147855, 14條	0.71267885, 水軍	0.7117269]
幽默	[排程	0.94056773, 平面	0.92473197, 沒必要	0.91582817, 故事情節	0.9113235, 緩	0.90921956, 笑出來	0.9008162, 輕松愉快	0.89958227, 本該	0.8973848, 掌控	0.8947682, 不俗	0.8913032, 差點	0.89119893, 手持	0.8907271, 不舒服	0.89064366, 很棒	0.8881191, 十幾	0.8875753, 捧腹大笑	0.8866181, 色調	0.88600373, 搞怪	0.88330764, 一直都	0.88276887, 都很	0.8826765, 人選	0.88094795, 刺耳	0.8807346, 情愛	0.88056815, 橋段	0.8788211, 成人	0.8787862, 狗咬狗	0.8783484, 災難	0.8778411, 表現力	0.87725157, 網	0.87571883, 平穩	0.87560874, 更沒有	0.8752619, 一樂	0.8751746, 港片	0.87489885, 漫畫	0.8747164, 不下來	0.87460196, 萬萬	0.8739581, 群體性	0.87352765, 推進	0.8735161, 瘋癫	0.8726621]           

複制

  這裡每個詞後面的系數表示關聯性,在0-1之間,數值越大,表示關聯性越強。

  從這裡還是能夠看出一些關聯性的,比如“爛片”-“賠的”- “low”(居然還有“國産”),“畫面”-“老套”-“硬傷”-“緊湊”,“星”-“一顆”-“1”-“送給”, “幽默”-“笑出來”-“輕松愉快”-“很棒”

  當然了,如果資料量足夠大,我們就能夠看到更加有趣的東西了。

至此,我們明白了

  • Word2Vec是什麼,有什麼用,怎麼用
  • 常用的中文分詞器以及具體用法,如何加載停用詞庫等
  • Word2Vec如何訓練資料得到模型
  • Word2Vec如何使用訓練的模型分析有趣的次元

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”将是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公衆号,我會将我的文章推送給您,并和您一起分享我日常閱讀過的優質文章。