NLP天然語言處理相關技術說明及樣例(附源碼)

一、簡單概述

1.1 NLP概念

NLP(Natural Language Processing),天然語言處理,又稱NLU(Natural Language Understanding)天然語言理解,是語言信息處理的分支,也是人工智能的核心課題,簡單來講就是讓計算機理解天然語言。php

1.2 NLP涉及的內容及技術

天然語言處理研究包含的內容十分普遍,這裏只列舉出其中的其中的一部分(主要是在移動易系統中涉及到的),包括分詞處理(Word-Segment),詞性標註(Part-of-Speech tagging),句法分析(Parsing),信息檢索(Infomation-Retrieval),文字校對(Text-Rroofing),詞向量模型(WordVector-Model),語言模型(Language-Model),問答系統(Question-Answer-System)。以下逐一介紹。html

二、前期準備

  1. Lucene使用經驗java

  2. python使用經驗python

  3. 相關工具包以下:git

工具 版本 下載地址
哈工大LTP ltp4j download
berkeleylm berkeleylm 1.1.5 download
ElasticSearch elasticsearch-2.4.5 download

三、具體實現

3.1 分詞(Word-Segment)

3.1.1 這裏主要介紹中文分詞的實現,實現中文分詞的方法有許多種,例如StandfordCore NLP(具體實現參見【NLP】使用 Stanford NLP 進行中文分詞 ),jieba分詞,這裏使用哈工大的語言技術平臺LTP(包括後面的詞性標註,句法分析)。具體步驟以下:github

  • 首先下載LTP4J的jar包(download),算法

  • 下載完解壓縮後的文件包爲ltp4j-master,相應的jar包就在output文件夾下的jar文件夾中。spring

  • 下載編譯好的C++動態連接庫download,解壓後以下所示:Markdown  apache

  • 將文件夾中的全部內容複製到jdk的bin目錄下,以下所示:kTGmd.png微信

  • 構建Java項目,將jar包導入到項目中,右鍵項目buildpath,爲添加的jar包添加原本地庫依賴,路勁即下載解壓後的dll動態庫文件路徑,以下所示:kT3OH.png

  • 接着即是中文分詞的測試了,實現代碼以下:

package ccw.ltpdemo;
    import java.util.ArrayList;
    import java.util.List;
    
    import edu.hit.ir.ltp4j.Segmentor;
    public class ltpSegmentDemo {
        public static void main(String[] args) {
            Segmentor segmentor = new Segmentor();
            if(segmentor.create("D:/NLP/ltp/ltp_data_v3.4.0/ltp_data_v3.4.0/cws.model")<0)
            {
                System.out.println("model load failed");
            }
            else
            {
                String sent = "這是中文分詞測試";
                List<String> words = new ArrayList<String>();
                int size = segmentor.segment(sent, words);
                for(String word :words)
                {
                    System.out.print(word+"\t");
                }
                segmentor.release();
            }
        }
    }

3.1.2 效果以下:

Markdown

3.2 詞性標註(Part-of-Speech tagging)

3.2.1 這裏介紹如何經過ltp實現中文的詞性標註,具體實現代碼以下:

package ccw.ltpdemo;
    import java.util.ArrayList;
    import java.util.List;
    
    import edu.hit.ir.ltp4j.Postagger;
    public class ltpPostaggerDemo {
        public static void main(String[] args) {
            Postagger postagger = new Postagger();
            if(postagger.create("D:/NLP/ltp/ltp_data_v3.4.0/ltp_data_v3.4.0/pos.model")<0)
            {
                System.out.println("model load failed");
            }
            else
            {
                List<String> words = new ArrayList<String>();
                words.add("我");
                words.add("是");
                words.add("中國");
                words.add("人");
                List<String> values = new ArrayList<String>();
                
                int size = postagger.postag(words, values);
                for(int i = 0;i<words.size();i++)
                {
                    System.out.print(words.get(i)+" "+values.get(i)+"\t");
                }
                postagger.release();
            }
        }
    }

3.2.2 實現效果以下:
Markdown

3.3 句法分析(Parsing)

3.3.1 這裏介紹如何經過ltp實現對中文句子的句法分析,核心方法int size = Parser.parse(words,tags,heads,deprels),其中,words[]表示待分析的詞序列,tags[]表示待分析的詞的詞性序列,heads[]表示結果依存弧,heads[i]表明第i個節點的父節點編號(其中第0個表示根節點root),deprels[]表示依存弧的關係類型,size表示返回結果中詞的個數。實現代碼以下:

package ccw.ltpdemo;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import edu.hit.ir.ltp4j.Parser;
    
    public class ltpParserDemo {
        
        /**
         * @param args
         */
        public static void main(String[] args) {
            
            Parser parser = new Parser();
            if(parser.create("D:/NLP/ltp/ltp_data_v3.4.0/ltp_data_v3.4.0/parser.model")<0)
            {
                System.out.println("model load failed");
            }
            else
            {
                 List<String> words = new ArrayList<String>();
                    List<String> tags = new ArrayList<String>();
                    words.add("我");tags.add("r");
                    words.add("很是");tags.add("d");
                    words.add("喜歡");tags.add("v");
                    words.add("音樂");tags.add("n");
                    List<Integer> heads = new ArrayList<Integer>();
                    List<String> deprels = new ArrayList<String>();
                    int size = Parser.parse(words,tags,heads,deprels);
                    for(int i = 0;i<size;i++) {
                      System.out.print(heads.get(i)+":"+deprels.get(i));
                      if(i==size-1) {
                        System.out.println();
                      }
                      else{
                        System.out.print("        ");
                      }
                    }
    
                    parser.release();
            }
        }
    
    }

3.3.2 實現效果以下:
Markdown

3.4 信息檢索(Information-Retrieval)

信息檢索(Information Retrieval)是用戶進行信息查詢和獲取的主要方式,是查找信息的方法和手段。狹義的信息檢索僅指信息查詢(Information Search)。即用戶根據須要,採用必定的方法,藉助檢索工具,從信息集合中找出所須要信息的查找過程。實現參見移動易實現全文搜索

3.5 文字校對(Text-Rroofing),語言模型(Language-Model)

3.5.1 N元模型(N-gram)

首先介紹N-gram模型,N-gram模型是天然語言處理中一個很是重要的概念,一般,在NLP中,基於必定的語料庫, 能夠經過N-gram來預計或者評估一個句子是否合理。對於一個句子T,假設T由詞序列w1,w2,w3...wn組成,那麼T出現的機率

  • P(T)=P(w1,w2,w3...wn)=P(w1)P(w2|w1)P(w3|w2,w1)...p(wn|wn-1,...w2,w1),
    此機率在參數巨大的狀況下顯然不容易計算,所以引入了馬爾可夫鏈(即每一個詞出現的機率僅僅與它的先後幾個詞相關),這樣能夠大幅度縮小計算的長度,即

  • P(wi|w1,⋯,wi−1)=P(wi|wi−n+1,⋯,wi−1)
    特別的,當n取值較小時:

當n=1時,即每個詞出現的機率只由該詞的詞頻決定,稱爲一元模型(unigram-model):

  • P(w1,w2,⋯,wm)=∏i=1mP(wi)
    設M表示語料庫中的總字數,c(wi)表示wi在語料庫中出現的次數,那麼

  • P(wi)=C(wi)/M
    當n=2時,即每個詞出現的機率只由該詞的前一個詞以及後一個詞決定,稱爲二元模型(bigram-model):

  • P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−1)
    設M表示語料庫中的總字數,c(wi-1WI)表示wi-1wi在語料庫中出現的次數,那麼

  • P(wi|wi−1)=C(wi−1wi)/C(wi−1)
    當n=3時,稱爲三元模型(trigram-model):

  • P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−2wi−1)
    那麼

  • P(wi|wi−1wi-2)=C(wi-2wi−1wi)/C(wi−2wi-1)

3.5.2 中文拼寫糾錯

接着介紹如何經過Lucene提供的spellChecker(拼寫校訂)模塊實現中文字詞的糾錯,首先建立語料詞庫,以下所示:
Markdown
而後在代碼中建立索引並測試,具體實現代碼以下:

package ccw.spring.ccw.lucencedemo;
      import java.io.BufferedReader;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileReader;
      import java.io.IOException;
      import java.io.InputStreamReader;
      import java.util.Iterator;
      import org.apache.lucene.index.IndexReader;
      import org.apache.lucene.index.IndexWriterConfig;
      import org.apache.lucene.search.spell.LuceneDictionary;
      import org.apache.lucene.search.spell.PlainTextDictionary;
      import org.apache.lucene.search.spell.SpellChecker;
      import org.apache.lucene.search.suggest.InputIterator;
      import org.apache.lucene.store.Directory;
      import org.apache.lucene.store.FSDirectory;
      import org.apache.lucene.util.Version;
      public class Spellcheck {
        public static String directorypath;
        public static String origindirectorypath;
        public SpellChecker spellcheck;
        public LuceneDictionary dict;
        
    /**
     * 建立索引
     * a
     * @return
     * @throws IOException
     * boolean
     */
    public static void createIndex(String directorypath,String origindirectorypath) throws IOException
    {
        Directory directory = FSDirectory.open(new File(directorypath));
        
        SpellChecker spellchecker = new SpellChecker(directory);
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_9, null);
        PlainTextDictionary pdic = new PlainTextDictionary(new InputStreamReader(new FileInputStream(new File(origindirectorypath)),"utf-8"));
        spellchecker.indexDictionary(new PlainTextDictionary(new File(origindirectorypath)), config, false);
        directory.close();
        spellchecker.close();
    }
    public Spellcheck(String opath ,String path)
    {
        origindirectorypath = opath;
        directorypath = path;
        Directory directory;
        try {
               directory = FSDirectory.open(new File(directorypath));
               spellcheck = new SpellChecker(directory);
               IndexReader oriIndex = IndexReader.open(directory);
               dict = new LuceneDictionary(oriIndex,"name");
            }
         catch (IOException e) {
                     e.printStackTrace();
                 }
             
    }
    public void setAccuracy(float v)
    {
        spellcheck.setAccuracy(v);
    }
    
    
    public String[]search(String queryString, int suggestionsNumber)
    {
        String[]suggestions = null;
        try {
              if (exist(queryString))
              return null;
              suggestions = spellcheck.suggestSimilar(queryString,suggestionsNumber);
            }
             catch (IOException e) 
            {
              e.printStackTrace();
            }
            return suggestions;
    }
              
    private boolean exist(String queryString) throws IOException {
        InputIterator ite =  dict.getEntryIterator();
            while (ite.hasContexts())
              {
                  if (ite.next().equals(queryString))
                      return true;
              }
                     return false;
        }
    
    
    public static void main(String[] args) throws IOException {
        String opath = "D:\\Lucene\\NLPLucene\\words.txt";
        String ipath = "D:\\Lucene\\NLPLucene\\index";
        Spellcheck.createIndex(ipath, opath);
        Spellcheck spellcheck = new Spellcheck(opath,ipath);
        //spellcheck.createSpellIndex();
        
        spellcheck.setAccuracy((float) 0.5);
        String [] result = spellcheck.search("麻辣糖", 15);
        if(result.length==0||null==result)
        {
            System.out.println("未發現錯誤");
        }
        else
        {
            System.out.println("你是否是要找:");
            for(String hit:result)
            {
                System.out.println(hit);
            }
        }
    }
    
                 
    }

實現效果以下:
Markdown

3.5.3 中文語言模型訓練

這裏主要介紹中文語言模型的訓練,中文語言模型的訓練主要基於N-gram算法,目前開源的語言模型訓練的工具主要有SRILM、KenLM、 berkeleylm 等幾種,KenLm較SRILM性能上要好一些,用C++編寫,支持單機大數據的訓練。berkeleylm是用java寫。本文主要介紹如何經過berkelylm實現中文語言模型的訓練。

  • 首先須要下載berkeleylm的jar包(download),完成後將jar包導入到java項目中。

  • 而後準備訓練的語料庫,首先經過ltp將每一句文本分詞,而後將分完詞的語句寫入txt文件,以下所示:
    Markdown

  • 接着就是對語料庫的訓練,首先要讀取分完詞的文本,而後就是對每一個詞計算在給定上下文中出現的機率,這裏的機率是對10取對數後計算獲得的,最後將結果按照給定的格式存儲,能夠按照.arpa或者二進制.bin文件存儲。文件格式以下:
    Markdown  

實現代碼以下:

package ccw.berkeleylm;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    import edu.berkeley.nlp.lm.ConfigOptions;
    import edu.berkeley.nlp.lm.StringWordIndexer;
    import edu.berkeley.nlp.lm.io.ArpaLmReader;
    import edu.berkeley.nlp.lm.io.LmReaders;
    import edu.berkeley.nlp.lm.util.Logger;
    
    public class demo {
        
        
        private static void usage() {
            System.err.println("Usage: <lmOrder> <ARPA lm output file> <textfiles>*");
            System.exit(1);
        }
        
        public void makelml(String [] argv)
        {
            if (argv.length < 2) {
                usage();
            }
            final int lmOrder = Integer.parseInt(argv[0]);
            final String outputFile = argv[1];
            final List<String> inputFiles = new ArrayList<String>();
            for (int i = 2; i < argv.length; ++i) {
                inputFiles.add(argv[i]);
            }
            if (inputFiles.isEmpty()) inputFiles.add("-");
            Logger.setGlobalLogger(new Logger.SystemLogger(System.out, System.err));
            Logger.startTrack("Reading text files " + inputFiles + " and writing to file " + outputFile);
            final StringWordIndexer wordIndexer = new StringWordIndexer();
            wordIndexer.setStartSymbol(ArpaLmReader.START_SYMBOL);
            wordIndexer.setEndSymbol(ArpaLmReader.END_SYMBOL);
            wordIndexer.setUnkSymbol(ArpaLmReader.UNK_SYMBOL);
            LmReaders.createKneserNeyLmFromTextFiles(inputFiles, wordIndexer, lmOrder, new File(outputFile), new ConfigOptions());
            Logger.endTrack();
        }
        
        public static void main(String[] args) {
            
            demo d = new demo();
            String inputfile = "D:\\NLP\\languagematerial\\quest.txt";
            String outputfile = "D:\\NLP\\languagematerial\\q.arpa";
            String s[]={"8",outputfile,inputfile};
            d.makelml(s);
            
        }
    
    }
  • 最後就是讀取模型,而後判斷句子的類似性,實現代碼以下:

package ccw.berkeleylm;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    import edu.berkeley.nlp.lm.ArrayEncodedProbBackoffLm;
    import edu.berkeley.nlp.lm.ConfigOptions;
    import edu.berkeley.nlp.lm.StringWordIndexer;
    import edu.berkeley.nlp.lm.io.LmReaders;
    
    public class readdemo {
        
        public static ArrayEncodedProbBackoffLm<String> getLm(boolean compress,String file) {
            final File lmFile = new File(file);
            final ConfigOptions configOptions = new ConfigOptions();
            configOptions.unknownWordLogProb = 0.0f;
            final ArrayEncodedProbBackoffLm<String> lm = LmReaders.readArrayEncodedLmFromArpa(lmFile.getPath(), compress, new StringWordIndexer(), configOptions,
                Integer.MAX_VALUE);
            return lm;
        }
        
        
        public static void main(String[] args) {
            readdemo read = new readdemo();
            LmReaders readers = new LmReaders();
            ArrayEncodedProbBackoffLm<String> model = (ArrayEncodedProbBackoffLm) readdemo.getLm(false, "D:\\NLP\\languagematerial\\q.arpa");
            String sentence = "是";
            String [] words = sentence.split(" ");
            List<String> list = new ArrayList<String>();
            for(String word : words)
            {
                System.out.println(word);
                list.add(word);
            }
            float score = model.getLogProb(list);
            System.out.println(score);
        }
    
    }

實現效果以下:

Markdown 

3.5.4 同義詞詞林

這裏使用哈工大提供的同義詞詞林,詞林提供三層編碼,第一級大類用大寫英文字母表示,第二級中類用小寫字母表示,第三級小類用二位十進制整數表示,第四級詞羣用大寫英文字母表示,第五級原子詞羣用二位十進制整數表示。編碼表以下所示:

Markdown   

第八位的標記有三種,分別是「=「、」#「、」@「,=表明相等、同義,#表明不等、同類,@表明自我封閉、獨立,它在詞典中既沒有同義詞,也沒有相關詞。經過同義詞詞林能夠比較兩詞的類似程度,代碼實現以下:

package cilin;
    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.InputStreamReader;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Vector;
    public class CiLin {
    public static HashMap<String, List<String>> keyWord_Identifier_HashMap;//<關鍵詞,編號List集合>哈希
    
    public int zero_KeyWord_Depth = 12;
    public static HashMap<String, Integer> first_KeyWord_Depth_HashMap;//<第一層編號,深度>哈希
    public static HashMap<String, Integer> second_KeyWord_Depth_HashMap;//<前二層編號,深度>哈希
    public static HashMap<String, Integer> third_KeyWord_Depth_HashMap;//<前三層編號,深度>哈希
    public static HashMap<String, Integer> fourth_KeyWord_Depth_HashMap;//<前四層編號,深度>哈希
    //public HashMap<String, HashSet<String>> ciLin_Sort_keyWord_HashMap = new HashMap<String, HashSet<String>>();//<(同義詞)編號,關鍵詞Set集合>哈希
    
    static{
        keyWord_Identifier_HashMap = new HashMap<String, List<String>>();
        first_KeyWord_Depth_HashMap = new HashMap<String, Integer>();
        second_KeyWord_Depth_HashMap = new HashMap<String, Integer>();
        third_KeyWord_Depth_HashMap = new HashMap<String, Integer>();
        fourth_KeyWord_Depth_HashMap = new HashMap<String, Integer>();
        initCiLin();
    }
    
    //3.初始化詞林相關
    public static void initCiLin(){
        int i;
        String str = null;
        String[] strs = null;
        List<String> list = null;
        BufferedReader inFile = null;
        try {
            //初始化<關鍵詞, 編號set>哈希
            inFile = new BufferedReader(new InputStreamReader(new FileInputStream("cilin/keyWord_Identifier_HashMap.txt"), "utf-8"));// 讀取文本
            while((str = inFile.readLine()) != null){
                strs = str.split(" ");
                list = new Vector<String>();
                for (i = 1; i < strs.length; i++) 
                    list.add(strs[i]);
                keyWord_Identifier_HashMap.put(strs[0], list);
            }
            
            //初始化<第一層編號,高度>哈希
            inFile.close();
            inFile = new BufferedReader(new InputStreamReader(new FileInputStream("cilin/first_KeyWord_Depth_HashMap.txt"), "utf-8"));// 讀取文本
            while ((str = inFile.readLine()) != null){
                strs = str.split(" ");
                first_KeyWord_Depth_HashMap.put(strs[0], Integer.valueOf(strs[1]));
            }
            
            //初始化<前二層編號,高度>哈希
            inFile.close();
            inFile = new BufferedReader(new InputStreamReader(new FileInputStream("cilin/second_KeyWord_Depth_HashMap.txt"), "utf-8"));// 讀取文本
            while ((str = inFile.readLine()) != null){
                strs = str.split(" ");
                second_KeyWord_Depth_HashMap.put(strs[0], Integer.valueOf(strs[1]));
            }
            
            //初始化<前三層編號,高度>哈希
            inFile.close();
            inFile = new BufferedReader(new InputStreamReader(new FileInputStream("cilin/third_KeyWord_Depth_HashMap.txt"), "utf-8"));// 讀取文本
            while ((str = inFile.readLine()) != null){
                strs = str.split(" ");
                third_KeyWord_Depth_HashMap.put(strs[0], Integer.valueOf(strs[1]));
            }
        
            //初始化<前四層編號,高度>哈希
            inFile.close();
            inFile = new BufferedReader(new InputStreamReader(new FileInputStream("cilin/fourth_KeyWord_Depth_HashMap.txt"), "utf-8"));// 讀取文本
            while ((str = inFile.readLine()) != null){
                strs = str.split(" ");
                fourth_KeyWord_Depth_HashMap.put(strs[0], Integer.valueOf(strs[1]));
            }
            inFile.close();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
        
    //根據兩個關鍵詞計算類似度
    public static double calcWordsSimilarity(String key1, String key2){
        List<String> identifierList1 = null, identifierList2 = null;//詞林編號list
        if(key1.equals(key2))
            return 1.0;
        
        if (!keyWord_Identifier_HashMap.containsKey(key1) || !keyWord_Identifier_HashMap.containsKey(key2)) {//其中有一個不在詞林中,則返回類似度爲0.1
            //System.out.println(key1 + "  " + key2 + "有一個不在同義詞詞林中!");
            return 0.1;
        }
        identifierList1 = keyWord_Identifier_HashMap.get(key1);//取得第一個詞的編號集合
        identifierList2 = keyWord_Identifier_HashMap.get(key2);//取得第二個詞的編號集合
        
        return getMaxIdentifierSimilarity(identifierList1, identifierList2);
    }
        
    public static double getMaxIdentifierSimilarity(List<String> identifierList1, List<String> identifierList2){
        int i, j;
        double maxSimilarity = 0, similarity = 0;
        for(i = 0; i < identifierList1.size(); i++){
            j = 0;
            while(j < identifierList2.size()){
                similarity = getIdentifierSimilarity(identifierList1.get(i), identifierList2.get(j));
                System.out.println(identifierList1.get(i) + "  " + identifierList2.get(j) + "  " + similarity);
                if(similarity > maxSimilarity)
                    maxSimilarity = similarity;
                if(maxSimilarity == 1.0)    
                    return maxSimilarity;
                j++;
            }
        }
        return maxSimilarity;
    }
        
    public static double getIdentifierSimilarity(String identifier1, String identifier2){
        int n = 0, k = 0;//n是分支層的節點總數, k是兩個分支間的距離.
        //double a = 0.5, b = 0.6, c = 0.7, d = 0.96;
        double a = 0.65, b = 0.8, c = 0.9, d = 0.96;
        if(identifier1.equals(identifier2)){//在第五層相等
            if(identifier1.substring(7).equals("="))
                return 1.0;
            else 
                return 0.5;
        }
        else if(identifier1.substring(0, 5).equals(identifier2.substring(0, 5))){//在第四層相等 Da13A01=
            n = fourth_KeyWord_Depth_HashMap.get(identifier1.substring(0, 5));
            k = Integer.valueOf(identifier1.substring(5, 7)) - Integer.valueOf(identifier2.substring(5, 7));
            if(k < 0) k = -k;
            return Math.cos(n * Math.PI / 180) * ((double)(n - k + 1) / n) * d;
        }
        else if(identifier1.substring(0, 4).equals(identifier2.substring(0, 4))){//在第三層相等 Da13A01=
            n = third_KeyWord_Depth_HashMap.get(identifier1.substring(0, 4));
            k = identifier1.substring(4, 5).charAt(0) - identifier2.substring(4, 5).charAt(0);
            if(k < 0) k = -k;
            return Math.cos(n * Math.PI / 180) * ((double)(n - k + 1) / n) * c;
        }
        else if(identifier1.substring(0, 2).equals(identifier2.substring(0, 2))){//在第二層相等
            n = second_KeyWord_Depth_HashMap.get(identifier1.substring(0, 2));
            k = Integer.valueOf(identifier1.substring(2, 4)) - Integer.valueOf(identifier2.substring(2, 4));
            if(k < 0) k = -k;
            return Math.cos(n * Math.PI / 180) * ((double)(n - k + 1) / n) * b;
        }
        else if(identifier1.substring(0, 1).equals(identifier2.substring(0, 1))){//在第一層相等
            n = first_KeyWord_Depth_HashMap.get(identifier1.substring(0, 1));
            k = identifier1.substring(1, 2).charAt(0) - identifier2.substring(1, 2).charAt(0);
            if(k < 0) k = -k;
            return Math.cos(n * Math.PI / 180) * ((double)(n - k + 1) / n) * a;
        }
        
        return 0.1;
    }
    }
    
    //測試
    public class Test {
        public static void main(String args[]) {
            String word1 = "類似", word2 = "相像";
            double sim = 0;
            sim = CiLin.calcWordsSimilarity(word1, word2);//計算兩個詞的類似度
            System.out.println(word1 + "  " + word2 + "的類似度爲:" + sim);
        }
    }

測試效果以下:

Markdown

3.6 詞向量模型(WordVector-Model)

3.6.1 詞向量

詞向量顧名思義,就是用一個向量的形式表示一個詞。爲何這麼作?天然語言理解問題轉化爲機器學習問題的第一步都是經過一種方法把這些符號數學化。詞向量具備良好的語義特性,是表示詞語特徵的經常使用方式。詞向量的每一維的值表明一個具備必定的語義和語法上解釋的特徵。

3.6.2 Word2vec

Word2vec是Google公司在2013年開放的一款用於訓練詞向量的軟件工具。它根據給定的語料庫,經過優化後的訓練模型快速有效的將一個詞語表達成向量形式,其核心架構包括CBOW和Skip-gram。Word2vec包含兩種訓練模型,分別是CBOW和Skip_gram(輸入層、發射層、輸出層),以下圖所示:
Markdown

3.6.3 word2vec 訓練詞向量

# coding:utf-8
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    from gensim.models import Word2Vec
    import logging,gensim,os
     
    class TextLoader(object):
        def __init__(self):
            pass
     
        def __iter__(self):
            input = open('corpus-seg.txt','r')
            line = str(input.readline())
            counter = 0
            while line!=None and len(line) > 4:
                #print line
                segments = line.split(' ')
                yield  segments
                line = str(input.readline())
     
    sentences = TextLoader()
    model = gensim.models.Word2Vec(sentences, workers=8)
    model.save('word2vector2.model')
    print 'ok'
# coding:utf-8
    import sys
    reload(sys)
    sys.setdefaultencoding( "utf-8" )
    from gensim.models import Word2Vec
    import logging,gensim,os
     
    #模型的加載
    model = Word2Vec.load('word2vector.model')
    #比較兩個詞語的類似度,越高越好
    print('"唐山" 和 "中國" 的類似度:'+ str(model.similarity('唐山','中國')))
    print('"中國" 和 "祖國" 的類似度:'+ str(model.similarity('祖國','中國')))
    print('"中國" 和 "中國" 的類似度:'+ str(model.similarity('中國','中國')))
    #使用一些詞語來限定,分爲正向和負向的
    result = model.most_similar(positive=['中國', '城市'], negative=['學生'])
    print('同"中國"與"城市"二詞接近,可是與"學生"不接近的詞有:')
    for item in result:
        print('   "'+item[0]+'"  類似度:'+str(item[1]))
     
    result = model.most_similar(positive=['男人','權利'], negative=['女人'])
    print('同"男人"和"權利"接近,可是與"女人"不接近的詞有:')
    for item in result:
        print('   "'+item[0]+'"  類似度:'+str(item[1]))
     
    result = model.most_similar(positive=['女人','法律'], negative=['男人'])
    print('同"女人"和"法律"接近,可是與"男人"不接近的詞有:')
    for item in result:
        print('   "'+item[0]+'"  類似度:'+str(item[1]))
    #從一堆詞裏面找到不匹配的
    print("老師 學生 上課 校長 , 有哪一個是不匹配的? word2vec結果說是:"+model.doesnt_match("老師 學生 上課 校長".split()))
    print("汽車 火車 單車 相機 , 有哪一個是不匹配的? word2vec結果說是:"+model.doesnt_match("汽車 火車 單車 相機".split()))
    print("大米 白色 藍色 綠色 紅色 , 有哪一個是不匹配的? word2vec結果說是:"+model.doesnt_match("大米 白色 藍色 綠色 紅色 ".split()))
    #直接查看某個詞的向量
    print('中國的特徵向量是:')
    print(model['中國'])

效果以下:
圖片9.png

微信公衆服務號

wechat.jpg

相關文章
相關標籤/搜索