【項目總結】自然語言處理在現實生活中運用


【項目總結】自然語言處理在現實生活中運用

作者 白寧超

2015年11月9日23:27:51

摘要:自然語言處理或者是文本挖掘以及數據挖掘,近來一直是研究的熱點。很多人相想數據挖掘,或者自然語言處理,就有一種莫名的距離感。其實,走進去你會發現它的美,它在現實生活中解決難題的應用之美,跟它相結合的數學之美,還有它與統計學的自然融合。語言只是一種實現工具,真正難度的是模型的理解和對模型的構建。本文結合自然語言處理的基本方法,完成對2002--2010年17套GET真題的核心單詞抽取。麻雀雖小,也算五臟俱全,包含整個數據處理過程,比較簡單。中等開發的程序員都可以實現,其中包括數據清洗,停用詞處理,分詞,詞頻統計,排序等常用方法。(本文原創,分享供於學習,轉載標明出處:【項目總結】自然語言處理在現實生活中運用

相關文章


【文本處理】自然語言處理在現實生活中運用

【文本處理】多種貝葉斯模型構建及文本分類的實現

【文本處理】快速了解什么是自然語言處理

【文本處理】領域本體構建方法概述

【文本挖掘(1)】OpenNLP:駕馭文本,分詞那些事

【文本挖掘(2)】【NLP】Tika 文本預處理:抽取各種格式文件內容

【文本挖掘(3)】自己動手搭建搜索工具

1 需求分析與描述:

    首先談下這款軟件的來源和用途吧,本科至今沒有掛科記錄,很不幸第一次《英語學位英語考試<GET>》掛科了。於是,筆者開始瘋狂的做題和背單詞,對於GET真題很多單詞不認識,抱着有道詞典,逐字翻譯耗時耗力。再說歷來10余年試題考試單詞范圍是一定的,把出現頻率高的單詞,所謂核心單詞掌握了不就事倍功半了?問題來了,不像四六級詞匯或者考研詞匯市場有專門賣的。當時就開始設想,如果我收集10余年真題,然后去除所有非單詞結構(真題算是結構化數據,有一定規則,比較容易處理。此過程其實就是數據清洗過程)最后把所有單詞集中匯總,再去除如:a/an/of/on/frist等停用詞(中文文本處理也需要對停用詞處理,諸如:的,地,是等)。處理好的單詞進行去重和詞頻統計,最后再利用網絡工具對英語翻譯。然后根據詞頻排序。 基於以上思路,結合筆者前不久對數據挖掘中分類實現的經驗和暑假學習的統計學知識最終上周末(10.31—11.1)花了2天時間搞定,這些自然語言處理的基礎方法在分類,聚類等數據挖掘和本體領域構建中都有使用。最后我將其核心方法進行梳理,下面咱們具體展開。

2 自然語言處理結果預覽:

前面提到本算法是對自然語言中常規英文試卷的處理,那么開始收集原始數據吧。

1 歷年(2002—2010年)GET考試真題,文檔格式不一,包括txt/pdf/word等如下圖:

2 對所有格式不一的文檔進行統計處理成txt文檔,格式化(去除漢字/標點/空格等非英文單詞)和去除停用詞(去除891個停用詞)處理后結果如下:【17套試卷原始單詞(含重復)82158個,數據清洗處理后32011個】

3 對清洗后的單詞進行去重和詞頻統計:【去重后7895個單詞尺寸】

4 顯示這10余年考試的詞匯量還是很大的,加上停用詞,考查詞匯量在9000左右,那么常考的應該沒那么多。試想下17套試卷中,去除停用詞后出現頻率大於5的和小於25次【1674個單詞】的數據應該是合理的,那么我就指定閾值提取。

5 最后一步,中英文翻譯(目前通過google工具快速查詢,然后合並)。最終效果如下:(處理的最終txt結果和自己word整理的結果)

3 算法思想和解決方案:

算法思想:

1歷年(2002—2010年)GET考試真題,文檔格式不一。網上收集

2 對所有格式不一的文檔進行統計處理成txt文檔,格式化(去除漢字/標點/空格等非英文單詞)和去除停用詞(去除891個停用詞)處理。

利用正則表達式去除非英文單詞的字符,通過規則的空格分離,停用詞表網上一大堆,通過將其與單詞比對,不在停用詞表的追加存儲

3 對清洗后的單詞進行去重和詞頻統計

通過Map統計詞頻,實體存儲:單詞-詞頻。(數組也可以,只是面對特別大的數據,數組存在越界問題)。排序:根據詞頻或者字母

4 提取核心詞匯,大於5的和小於25次的數據,可以自己制定閾值。

遍歷list<實體>列表時候,通過獲取實體的詞頻屬性控制選取詞匯表尺寸。

5 最后一步,中英文翻譯。

將批量單詞通過google翻譯,可以獲取常用意思,對於發音,詞義,詞性等沒有處理。這里也是可以改進地方,其實也很簡單,后面詳解,最后自己講結果在word里面排版。

4 Java語言對需求實現詳解:

 1 文件保存路徑定義

    public static final  String stopWordTable ="./getFile/partStopWord.txt";       //停用詞詞表文件地址
    public static final  String    srcfilepath="./srcFile";                           //待處理的源文件地址
    public static final  String    stopfilepath="./getFile/temp.txt";                 //待處理的源文件地址
    public static final  String    tarfilepath="./getFile/getcoreword.txt";           //源文件和目標文件地址

2 對原始文件數據清理以及停用詞處理

    //對文本文件預處理
    public static void dataCleanFile(String srcfilepath,String stopfilepath){
        String reg = "[^a-zA-Z]";                          //去除噪音,獲取英文單詞開始的內容
        String sb=CommontMethod.readStrFiles(srcfilepath, " ").toLowerCase().replaceAll(reg, " ").replaceAll(" +"," ").trim();   //接收清洗后的數據  
        String[] srcWordsList =sb.split(" ");              //按照規則,將單詞放在數組里面
        System.out.println("2002至2010年GET試卷原始單詞數:【"+srcWordsList.length+"】個。");
        System.out.println("正在對如:on,of,a等停用詞處理,大約需要30秒,請等候...");
        
        StringBuffer stopWordSb=new StringBuffer();         //存放去除停用詞后的 
        for(int i=0;i<srcWordsList.length;i++){
            if(IsStopWord(srcWordsList[i])==false)
                stopWordSb.append(srcWordsList[i].toString().trim()+"\n");  //不是停用詞,則追加字符串
        }
        String[] stopWordsList =stopWordSb.toString().split("\n");
        System.out.println("對單詞集停用詞處理結束,核心單詞共計:【"+stopWordsList.length+"】個。已經保存至./getFile/temp.txt下,請查閱!");
        CommontMethod.writeStrFile(stopWordSb.toString(), stopfilepath, "\n"); //將預處理后並標注的數據,指定位置保存
    } 

3 處理后的數據進行核心單詞匯總和詞頻統計

   //統計詞頻
      public static void countWordNums(String stopfilepath,String tarfilepath){
        //統計GET試卷核心單詞以及詞頻
          Map<String,Integer> shlMap=new HashMap<String,Integer>(); //去重計數
          
          //整個詞彙表
          StringBuffer WordsTable=new StringBuffer();   
          List<Word> wordsList = new ArrayList<Word>();             //統計詞頻排序
          
          StringBuffer tarWordSb=new StringBuffer();                //存放去除停用詞后的,詞頻指定的單詞;
          List<Word> wordList = new ArrayList<Word>();              //統計詞頻排序
          int sum=0;//計算非重複單詞個數。
          int setnum=10; //設定保存詞頻條件
          shlMap=CommontMethod.countWords(shlMap, stopfilepath.trim(), " ");
          int count=0;
          for (Map.Entry<String, Integer> entry : shlMap.entrySet()) {
              if(entry.getValue()>0){
                  wordsList.add(new Word(entry.getKey(), entry.getValue()));//統計頻率詞彙表
                  count++;
                  if(entry.getValue()>setnum){
                        wordList.add(new Word(entry.getKey(), entry.getValue()));//統計指定頻率詞彙表
                        sum++;
                    }
            }
          }
          
          
          //詞彙表大小
          StringBuffer EglChindSb=new StringBuffer();   //存放中英對照詞表;
          String[] freWords= CommontMethod.wordsFre(wordsList,WordsTable).split("\n"); //根據詞頻結果排序,並進行保存
          String[] Tranlation=CommontMethod.readStrFile("./getFile/Translation.txt", "\n").split("\n");//中文翻譯
          for(int i=0;i<Tranlation.length&&i<freWords.length;i++){
              EglChindSb.append(freWords[i]+"\t"+Tranlation[i]+"\n");
          }
          CommontMethod.writeStrFile(EglChindSb.toString(),"./getFile/worstable.txt","\n");
          System.out.println("整個詞彙表為:【"+count+"】個。已經保存至./getFile/wordstable.txt下,請查閱!");
          
          
          //根據詞頻結果排序,並進行保存
          String fWords= CommontMethod.wordsFre(wordList,tarWordSb);          
          CommontMethod.writeStrFile(fWords.toString(), tarfilepath,"\n");     
          System.out.println("篩選出現"+setnum+"次以上的單詞:【"+sum+"】個。已經保存至./getFile/getcoreword.txt下,請查閱!");
          //根據字母有序打印
          //CommontMethod.init(shlMap);
      } 

4 運行結果分析

1 程序處理核心代碼,其中第一個類存放公共方法(小面小結有),第二個類主函數,如上代碼。第三個方法實體類統計詞頻,這樣設計,應對大數據,數據小數組即可。

2 處理后得到的結果,核心單詞,數據清洗結果,停用詞,翻譯,最終結果等。

3 原始試卷共計82158個單詞

4 數據清洗和停用詞處理后剩下32011個單詞

5 去重后總共7895個單詞的考察范圍

6 提取10次以上核心單詞623個,即便5次以上不過1500個單詞

性能方面運行25秒是穩定的,這個主要是對7895個單詞排序問題耗時比較多

5 自然語言常用方法小結(JAVA實現,C#類似):

1 實體的基本使用

/**
 * 
 */
package com.baiboy.bnc;

/**
 * @author 白寧超
 *
 */
public class Word {
    private String words;
    private int frequence;
    
    public Word(String words, int frequence) {
        this.words = words;
        this.frequence = frequence;
    }
    public String getWords() {
        return words;
    }
    public void setWords(String words) {
        this.words = words;
    }
    public int getFrequence() {
        return frequence;
    }
    public void setFrequence(int frequence) {
        this.frequence = frequence;
    }
    public String print(){
        return "["+this.frequence+"] "+this.words; 
    }
}
View Code

2 批量讀取目錄下的文件

    /**
     * 對單個文件的讀取,並將整個以字符串形式返回
     * @param srcfilepath 讀取文件的地址
     * @param separ   逐行讀取的分隔符號,如:" ", "\t", ","等
     * @return  sb 字符串
     */
    public static String readStrFiles(String fileDirPath,String separ){
        StringBuffer sb=new StringBuffer();
        BufferedReader srcFileBr=null;
        File dir=new File(fileDirPath);
        if(dir.exists()&&dir.isDirectory()){
            File[] files=dir.listFiles();  //獲取所有文件
            try{
                for(File file:files){//遍歷訓練集文件
                    srcFileBr = new BufferedReader(new InputStreamReader(new FileInputStream(new File(file.toString())),"UTF-8"));//讀取原文件
                    String line = null;
                    while((line=srcFileBr.readLine())!=null){
                        if(line.length()>0)
                        sb.append(line.trim()+separ);
                    } 
                }
                srcFileBr.close();
            }
            catch(Exception ex){
                System.out.println(ex.getMessage());
            }
        }
        else
            System.out.println("你選擇的不是目錄文件");
        return sb.toString().trim();
    }

3 讀取單個文件

    /**
     * 對單個文件的讀取,並將整個以字符串形式返回
     * @param srcfilepath 讀取文件的地址
     * @param separ   逐行讀取的分隔符號,如:" ", "\t", ","等
     * @return  sb 字符串
     */
    public static String readStrFile(String srcfilepath,String separ){
        StringBuffer sb=new StringBuffer();
        try{
            BufferedReader srcFileBr = new BufferedReader(new InputStreamReader(new FileInputStream(new File(srcfilepath)),"UTF-8"));//讀取原文件
            String line = null;
            while((line=srcFileBr.readLine())!=null){
                if(line.length()>0)
                sb.append(line.trim()+separ);
            } 
            srcFileBr.close();
        }
        catch(Exception ex){
            System.out.println(ex.getMessage());
        }
        return sb.toString().trim();
    }


4 文件預處理,並以字符串結果返回

    /**
     * 對文件的讀取,並將整個以字符串形式返回
     * @param shlMap 傳入的map集合
     * @param tarfilepath 讀取文件的地址
     * @param separ   逐行讀取的分隔符號,如:" ", "\t", ","等
     * @return  sb 字符串
     */
    public static Map<String,Integer> countWords(Map<String,Integer> tarMap,String tarfilepath,String separ){
        StringBuffer sb=new StringBuffer();
          try{
              //讀取原文件
              BufferedReader srcFileBr = new BufferedReader(new InputStreamReader(new FileInputStream(new File(tarfilepath)),"UTF-8"));
               //對讀入的文本進行預處理
              String paragraph = null;
              while((paragraph=srcFileBr.readLine())!=null&&paragraph.length()>0){
                  String[] words = paragraph.split(separ); 
                //遍歷所有單詞
                for(String word:words){
                    if(tarMap.containsKey(word))
                        tarMap.put(word, tarMap.get(word)+1);
                    else
                        tarMap.put(word, 1);
                }
              } 
              srcFileBr.close();
          }
          catch(Exception ex){
              System.out.println(ex.getMessage());
          }
        return tarMap;
    }

5 指定保存文件

    /**
     * 將字符串寫到指定文件中
     * @param str          待寫入的字符串
     * @param tarfilepath  目標文件路徑
     * @param separ        逐行讀取的分隔符號,如:" ", "\t", ","等
     */
     public static void writeStrFile(String str,String tarfilepath,String separ){
        try{
            OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(tarfilepath)), "UTF-8");// 構建OutputStreamWriter對象,參數可以指定編碼,默認為操作系統默認編碼,windows上是gbk
            writer.append(str+separ);// 刷新緩存沖,寫入到文件,如果下面已經沒有寫入的內容了,直接close也會寫入
            writer.close();//關閉寫入流,同時會把緩沖區內容寫入文件,所以上面的注釋掉
        }
        catch(Exception ex){
            System.out.println(ex.getMessage());
        }
    }


6 詞頻排序(中英文通用)

     /**
      * 根據單詞詞頻排序
      * @param wordList   存放單詞和詞頻
      * @param tarWordSb  存放排序后的結果
      * @return
      */
     public static String wordsFre(List<Word> wordList,StringBuffer tarWordSb){
         Collections.sort(wordList, new Comparator<Word>() {
                @Override
                public int compare(Word word1, Word word2) {
                    if(word1.getFrequence()>word2.getFrequence())
                        return -1;
                    else if(word1.getFrequence()<word2.getFrequence()) 
                        return 1;
                    else
                        return 0;
                }
              });
              for (Word word : wordList) {
                  tarWordSb.append(word.print()).append("\n");
            }
              return tarWordSb.toString();
     }

7 根據字符有序排列

     /**
      * 根據字符有序排列
      * @param shlMap
      */
     public static void init(Map<String,Integer> shlMap){
          List<Map.Entry<String, Integer>> mHashMapEntryList=new ArrayList<Map.Entry<String,Integer>>(shlMap.entrySet());
        System.out.println("-----> 排序前的順序");
        for (int i = 0; i < mHashMapEntryList.size(); i++) {
           System.out.println(mHashMapEntryList.get(i));
        }
        Collections.sort(mHashMapEntryList, new Comparator<Map.Entry<String,Integer>>() {
          @Override
          public int compare(Map.Entry<String,Integer> firstMapEntry, Map.Entry<String,Integer> secondMapEntry) {
            return firstMapEntry.getKey().compareTo(secondMapEntry.getKey());
          }
        });
        
        System.out.println("-----> 排序后的順序");
        for (int i = 0; i < mHashMapEntryList.size(); i++) {
            System.out.println(mHashMapEntryList.get(i));
         }
      }

8 停用詞處理如何判斷?

    // 停用詞處理器
    public static boolean IsStopWord(String word)
    {
         String sb2=CommontMethod.readStrFile(stopWordTable, "\n");
         String[] stopWordsList =sb2.split("\n");
         for(int i=0;i<stopWordsList.length;i++){
             if(word.equalsIgnoreCase(stopWordsList[i]))
                 return true;
         }
         return false;
     }

 

6 擴展改進與移植展望:

      本項目由於實際需求,對其做了初步完善。基本自然語言處理方法和流程都包含了,諸如詞頻統計,停用詞處理,單詞統計,還有文件的基本操作,再結合數學模型或者統計模型可以做復雜的自然語言或者文本處理。比如朴素貝葉斯分類,首先弄明白貝葉斯分類模型,其實就是對貝葉斯公式的理解和推導。之后結合本項目詞頻統計文件操作,數據清洗,中文分詞,停用詞處理就做出來了。再如,本體構建,也是需要對數據清洗,詞頻統計,結果發射概率和轉移概率,文本標注等實現,后面我會陸續發布相關文章。

      至於本算法改進,可以對翻譯部分改進,一種基於詞庫的檢索,包括詞性,詞義,詞標等匹配。另外一種是對英文詞組的分詞處理,利用英文分詞解決。移植方面,可以利用C#語言在窗體上開發,最后打包應用軟件。實際上我本科至於對窗體一直很熱衷。也可以做成領域下核心詞匯分析提取。諸如歷年考研真題,高考真題,中考真題,軟件開發某一方向詞匯,建築學詞匯等多重應用。做成多個APP,移植到移動軟件方面。

 


免責聲明!

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



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