語義分析之ansj_seg+word2vec的使用


語義分析,我是一個初學者,有很多東西,需要理論和實踐結合后,才能理解的相對清楚。

 

今天,我就在語義理解中基於背景語料的情況,實現語義上下文的預測,比如,我說“王寶強”,你會想到什么?別告訴沒有“馬蓉”,別告訴我沒有“泰囧”, 再比如,我說“白百何”, 不要說你沒有想到“出軌”兩個字。。。這就是語義預測,也就是相關信息的映射。這個,就是word2vec的功勞了。

 

word2vec是谷歌開源的一個語義預測框架。主要是將詞映射到一個維度空間上,每個詞都有n個不同維度的信息,用vector表示,詞與詞之間的關系,就用vec之間的cosine值來表示,consine值越大,說明這兩個詞之間的關系越近。詳細的word2vec的介紹,自行查閱相關資料。

 

不過,這里,有必要說一下的就是word2vec有兩個重要的模型:

  • CBOW(Continuous Bag-of-Words Model)是一種根據上下文的詞語預測當前詞語的出現概率的模型,其圖示如下圖左。CBOW是已知上下文,估算當前詞語的語言模型;
  • 而Skip-gram只是逆轉了CBOW的因果關系而已,即已知當前詞語,預測上下文,其圖示如下圖右;

這兩個模型,對應不同的使用場景。

 

word2vec的一個重要數據邏輯,就是貝葉斯定律,通俗的說,這個定理就是指:在某件事情發生的前提下,再發生另外一件事情的概率。具體的理論,自行學習去吧!

 

ansj_seg,是中科院開源的一個中文分詞工具。這是一個ictclas的java實現.基本上重寫了所有的數據結構和算法.詞典是用的開源版的ictclas所提供的.並且進行了部分的人工優化。

今天這篇博文的主要內容,就是通過ansj_seg對搜狗實驗室的語料進行分詞,然后用word2vec的skip-gram模型進行預測上下文。正如前面說的,說到王寶強,白百何之類的人物名稱,你會得到什么信息。。。

 

下載sogou實驗室的語料,地址:http://www.sogou.com/labs/resource/ca.php

關於這個語料的內容,直接去搜狗實驗室的網站上了解吧。這里不多說。我直接下載的zip包。

 

下載ansj_seg的jar包,這里我用的是最新版本5.1.1.下載地址:http://central.maven.org/maven2/org/ansj/ansj_seg/5.1.1/ansj_seg-5.1.1.jar

獲取這個jar包,官方要求最好配合最新版本的nlp-lang一起用,我這里也是最新版本。這個的下載地址:http://central.maven.org/maven2/org/nlpcn/nlp-lang/1.7.2/nlp-lang-1.7.2.jar

 

下載word2vec的源碼,這里下載的是github上的master版本。下載地址:https://github.com/svn2github/word2vec

 

下面進行具體的操作。

1》將語料進行預處理,取出其中的content。

cat news_tensite_xml.dat | iconv -f gbk -t utf-8 -c | grep "<content>" > corpus.txt

這里,iconv指令需要了解一點點基礎知識,他是一個字符集轉換工具,-f表示源字符集,-t表示轉換后的編碼字符集,上述指令中是將gbk字符集轉換為utf-8的字符集,-c表示丟棄任何無效的字符(基於字符集)

 

2》通過java程序,處理content,基於ansj將其中的內容進行分詞。

java代碼如下:

/**
 * @author "shihuc"
 * @date   2017年4月12日
 */
package ansjDemo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.HashSet;
import java.util.Set;

import org.ansj.domain.Result;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;

/**
 * @author chengsh05
 *
 */
public class AnsjSegDemo {

    /**
     * @param args
     */
     private static final String TAG_START_CONTENT = "<content>";
        private static final String TAG_END_CONTENT = "</content>";
        private static final String INPUT_FILE = "./src/ansjDemo/corpus.txt";
        private static final String OUTPUT_FILE = "./src/ansjDemo/corpus_out.txt";

        public static void main(String[] args) throws Exception {
            BufferedReader reader = null;
            PrintWriter pw = null;            
            Reader fr = null;
            try {
                System.out.println("開始處理分詞...");
                File file = new File(INPUT_FILE);
                fr =  new FileReader(file);
                reader = new BufferedReader(fr);
                pw = new PrintWriter(OUTPUT_FILE);
                long start = System.currentTimeMillis();
                int totalCharactorLength = 0;
                int totalTermCount = 0;
                Set<String> set = new HashSet<String>();
                String temp = null;
                while ((temp = reader.readLine()) != null) {
                    temp = temp.trim();
                    if (temp.startsWith(TAG_START_CONTENT)) {
                        //System.out.println("處理文本:" + temp);
                        int end = temp.indexOf(TAG_END_CONTENT);
                        String content = temp.substring(TAG_START_CONTENT.length(), end);
                        totalCharactorLength += content.length();
                        Result result = ToAnalysis.parse(content);
                        for (Term term : result) {
                            String item = term.getName().trim();
                            totalTermCount++;
                            pw.print(item + " ");
                            set.add(item);
                        }
                        pw.println();
                    }
                }

                long end = System.currentTimeMillis();
                System.out.println("" + totalTermCount + "個Term,共" 
                    + set.size() + "個不同的Term,共 " 
                    + totalCharactorLength + "個字符,每秒處理字符數:" 
                    + (totalCharactorLength * 1000.0 / (end - start)));
            } finally {
                if(fr != null){
                    fr.close();
                }
                if(reader != null){
                    reader.close();
                }
                if(pw != null){
                    pw.close();
                }
            }
        }

}

編譯的日志輸出,可以反映出一些邏輯:

開始處理分詞...
log4j:WARN No such property [datePattern] in org.apache.log4j.RollingFileAppender.
[2017-04-12 16:56:09] [ WARN] [main] [org.ansj.util.MyStaticValue.<clinit>(MyStaticValue.java:91)] - not find library.properties in classpath use it by default !
  [2017-04-12 16:56:09] [ INFO] [main] [org.ansj.dic.impl.File2Stream.toStream(File2Stream.java:29)] - path to stream library/ambiguity.dic
  [2017-04-12 16:56:09] [ERROR] [main] [org.ansj.library.AmbiguityLibrary.init(AmbiguityLibrary.java:112)] - Init ambiguity library error :org.ansj.exception.LibraryException:  path :library/ambiguity.dic file:E:\2016\workwps\RProject\library\ambiguity.dic not found or can not to read, path: library/ambiguity.dic
  [2017-04-12 16:56:09] [DEBUG] [main] [org.ansj.library.DicLibrary.init(DicLibrary.java:167)] - begin init dic !
  [2017-04-12 16:56:09] [ INFO] [main] [org.ansj.dic.impl.File2Stream.toStream(File2Stream.java:29)] - path to stream library/default.dic
  [2017-04-12 16:56:09] [ERROR] [main] [org.ansj.library.DicLibrary.init(DicLibrary.java:195)] - Init ambiguity library error :org.ansj.exception.LibraryException:  path :library/default.dic file:E:\2016\workwps\RProject\library\default.dic not found or can not to read, path: library/default.dic
  [2017-04-12 16:56:10] [ INFO] [main] [org.ansj.library.DATDictionary.loadDAT(DATDictionary.java:54)] - init core library ok use time : 468
  [2017-04-12 16:56:10] [ INFO] [main] [org.ansj.library.NgramLibrary.<clinit>(NgramLibrary.java:17)] - init ngram ok use time :358
  共388487481個Term,共941062個不同的Term,共 622383693個字符,每秒處理字符數:1461210.679044084

 

3》編譯word2vec

編譯之前,看看我的機器配置吧,個人覺得還是不錯的機器。

[root@localhost word2vec-master]# cat /proc/cpuinfo| grep "processor"| wc -l
24
[root@localhost word2vec-master]# free -h
              total        used        free      shared  buff/cache   available
Mem:            31G        2.9G         17G         33M         10G         27G
Swap:           15G          0B         15G

現在,開始對源碼進行編譯。

[root@localhost word2vec-master]# make
gcc word2vec.c -o word2vec -lm -pthread -O3 -march=native -Wall -funroll-loops -Wno-unused-result
gcc word2phrase.c -o word2phrase -lm -pthread -O3 -march=native -Wall -funroll-loops -Wno-unused-result
gcc distance.c -o distance -lm -pthread -O3 -march=native -Wall -funroll-loops -Wno-unused-result
distance.c: In function ‘main’:
distance.c:31:8: warning: unused variable ‘ch’ [-Wunused-variable]
   char ch;
        ^
gcc word-analogy.c -o word-analogy -lm -pthread -O3 -march=native -Wall -funroll-loops -Wno-unused-result
word-analogy.c: In function ‘main’:
word-analogy.c:31:8: warning: unused variable ‘ch’ [-Wunused-variable]
   char ch;
        ^
gcc compute-accuracy.c -o compute-accuracy -lm -pthread -O3 -march=native -Wall -funroll-loops -Wno-unused-result
compute-accuracy.c: In function ‘main’:
compute-accuracy.c:29:109: warning: unused variable ‘ch’ [-Wunused-variable]
   char st1[max_size], st2[max_size], st3[max_size], st4[max_size], bestw[N][max_size], file_name[max_size], ch;
                                                                                                             ^
chmod +x *.sh

 

4》對java分詞后的文件,基於word2vec進行訓練預測,主要基於skip-gram。

[root@localhost resouce]# bash word2vec_train.sh 
Starting training using file corpus_out.txt
Vocab size: 330438
Words in train file: 388694484
Alpha: 0.000002  Progress: 100.00%  Words/thread/sec: 11.20k  begin: 1491990795
end:   1491998336
gap:   7541

 

下面,看看我的word2vec_train.sh的內容是啥吧:

#!/bin/bash

BEGIN_TIME=`date +"%Y-%m-%d %H:%M:%S"`

word2vec -train corpus_out.txt -output vectors.bin -cbow 0 -size 200 -window 5 -negative 0 -hs 1 -sample 1e-3 -threads 50 -binary 1

END_TIME=`date +"%Y-%m-%d %H:%M:%S"`
time1=`date -d "$BEGIN_TIME" +%s`
time2=`date -d "$END_TIME" +%s`

gap=$[$time2-$time1]

echo "begin: $time1"
echo "end:   $time2"
echo "gap:   $gap"

說明下上面紅色部分的含義:

-train 訓練數據 
-output 結果輸入文件,即每個詞的向量 
-cbow 是否使用cbow模型,0表示使用skip-gram模型,1表示使用cbow模型,默認情況下是skip-gram模型,cbow模型快一些,skip-gram模型效果好一些 
-size 表示輸出的詞向量維數 
-window 為訓練的窗口大小,5表示每個詞考慮前5個詞與后5個詞(實際代碼中還有一個隨機選窗口的過程,窗口大小<=5) 
-negative 表示是否使用負例采樣方法0表示不使用,其它的值目前還不是很清楚 
-hs 是否使用Hierarchical Softmax方法,0表示不使用,1表示使用 
-sample 表示采樣的閾值,如果一個詞在訓練樣本中出現的頻率越大,那么就越會被采樣
-binary 表示輸出的結果文件是否采用二進制存儲,0表示不使用(即普通的文本存儲,可以打開查看),1表示使用,即vectors.bin的存儲類型

 

從我的shell腳本,可以看出,這個將近2G的文本,在24核,幾十G內存,我起50個線程。將CPU全都跑滿的情況下,也跑了7500多秒。

 

5》驗證效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM