第一次個人編程作業


第一次個人編程作業:論文查重

1.計算模塊接口的設計與實現過程

  • 類:

    1.readToString
    包含方法 String readToString(String fileName)
    讀入文件名,把文件轉為字符串。
    2.getCos
    包含方法 double getCos(String s1, String s2)
    計算兩個字符串的余弦值。
    3.writeToAnswer
    包含方法 void writeIntoAnswer(double sum,String ans)
    將結果寫入ans文件。
    
  • 流程圖

  • 實現過程
     大致的過程在類中已經說明,這里主要寫一下核心算法——余弦相似性的應用。
     先貼上參考資料: TF-IDF與余弦相似性的應用
     資料中是利用詞頻計算余弦相似性,這里不再贅述。講講踩的坑,我先嘗試用字頻方法寫了一下,結果相當不理想:只有rep的結果接近0.8,其他大部分都在0.98左右,dis的幾個文件結果甚至達到1。打開文件一看發現dis文本基本就是原文換個順序或者加個回車(順便一提一開始寫的是對比每行的重復率,發現dis文本的重復率都奇低,因為中間插了回車= =直接拉胯)。到這里我想着用詞頻判斷會不會好一點,於是又把字換成詞(感謝安利我hanlphxd)。but,情況沒有改善……我冷靜下來仔細想想,這整個文本去掉標點,還不計順序,詞頻和字頻的區別也確實不大……
     總之待在坑里我想到另一個方法,原理還是算余弦,這次把所有詞的出現位置用Map<String,Vector<Integer> >記錄下來,於是每個詞在兩個文本中都對應有一個向量,也就是獲得了這個詞在兩個文本中的位置向量,接下來可以計算它們的余弦值了。

  • 例:

    句子A:普通的DISCO我們普通的搖
    
    句子B:旁邊普通的路人在普通的瞧
    

如何計算以上兩個句子的余弦值?

  • 第一步:分詞
    在maven中配置hanlp,網上能搜到教程就不多說了,以及這里也踩到坑了,jdk版本太高居然還會配置失敗……附個pom.xml文件的圖吧

    搞半天終於成功了,淚目。

  • 第二步:列出所有詞,存入它們出現的位置,注意只判斷漢字

    句子A:普通0 的1 我們2 普通3 的4 搖5
    句子B:旁邊0 普通1 的2 路人3 在4 普通5 的6 瞧7

  • 第三步:得到兩個文本中所有詞的位置向量

舉個栗子:

  普通:句子A:[0,3] 句子B:[1,5]
  • 第四步:計算兩個文本中“普通”的余弦值:cosθ=\(\frac {0×1+3×5}{\sqrt {0+3^2}×\sqrt {1^2+5^2}}\)≈0.98

余弦值可以表示兩個向量之間的夾角大小,夾角越小,也就是余弦值越大,兩個向量就越相似。(只是我這個不是二維的向量,其實我也不知道能不能叫向量……

使用TreeMap存儲詞位置向量也是因為要確保是兩個文本中相同的詞對比,如果某個詞只在一個文本中出現就不會被計算到。

  • 第五步:計算所有詞的余弦值,同時記錄詞數。所有余弦值相加除以詞數就是最終結果了。

結果總算能看了。

2.計算模塊接口部分的性能改進

主要的改進思路都在實現過程↑里說了,也就是把詞頻向量改成詞位置向量。如果沒用詞位置而是字位置應該會更慢,也更耗內存。少踩了一個坑導致現在不知道性能改進怎么寫。

性能分析

  • Overview
  • Live memory

畢竟每個詞都要開兩個Vector ……理所當然的吃內存。

  • CPU views

沒想到是matches消耗最大。主要是用來判斷漢字了,也就是兩個文本的每個字符都要判斷一遍……這樣想想也挺正常,但是這個好像無法避免啊……

3.計算模塊部分單元測試展示

新增了重復率0和1的測試,其他幾個用的是作業提供的樣例。主要測試getCos方法。

  • 測試代碼
import org.junit.Assert;
import static org.junit.Assert.*;
public class mainTest {

    @org.junit.Test
    public void origAndOrig() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(1.0,sum,0);
    }
    @org.junit.Test
    public void completeDiff(){
        String s1="乘風破浪會有時";
        String s2="直掛雲帆濟滄海";
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0,sum,0);
    }
    @org.junit.Test
    public void origAndAdd() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig_0.8_add.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0.8,sum,0.2);
    }
    @org.junit.Test
    public void origAndDel() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig_0.8_del.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0.8,sum,0.2);
    }
    @org.junit.Test
    public void origAndMix() {
        String s1=readToString.readToString("testfile/orig.txt");
        String s2=readToString.readToString("testfile/orig_0.8_mix.txt");
        double sum=getCos.getCos(s1,s2);
        Assert.assertEquals(0.8,sum,0.2);
    }

后面的幾個代碼大同小異,就是改了個文件名,這里不放了。

  • 測試結果

  • 測試覆蓋率


核心方法getCos覆蓋率為100%,readIntoString沒覆蓋到的都是catch塊。

4.計算模塊部分異常處理說明

參考資料

  • 空文本異常
      //測試
        try{
            throw new EmptyTextException("Empty Text!");
        }catch(EmptyTextException e){
            e.printStackTrace();
        }
public class EmptyTextException extends Exception{
    public EmptyTextException(){
        super();
    }
    public EmptyTextException(String message){
        super(message);
    }
    public EmptyTextException(String message,Throwable cause){
        super(message,cause);
    }
    public EmptyTextException(Throwable cause){
        super(cause);
    }
}

  • 測試結果

5.PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鍾) 實際耗時(分鍾)
Planning 計划 120 100
· Estimate · 估計這個任務需要多少時間 60 60
Development 開發 420 1440
Analysis · 需求分析 (包括學習新技術) 60 360
· Design Spec · 生成設計文檔 60 120
· Design Review · 設計復審 20 20
· Coding Standard · 代碼規范 (為目前的開發制定合適的規范) 20 20
· Design · 具體設計 60 45
· Coding · 具體編碼 180 300
· Code Review · 代碼復審 30 15
· Test · 測試(自我測試,修改代碼,提交修改) 60 60
Reporting 報告 60 90
· Test Report · 測試報告 40 30
· Size Measurement · 計算工作量 10 10
· Postmortem & Process Improvement Plan · 事后總結, 並提出過程改進計划 30 45
· 合計 1230 2715

6.遇到的問題

  • 問題1
    創建測試的方法培訓文檔里講的有點模糊,點擊類名(我找了半天Solve在哪,原來是就是它的類名……),alt+enter就可以創建測試了。

  • 問題2

    照着預培訓文檔里面寫,結果編譯器報不能比較浮點數……?

  • 解決方法:應該用assertEquals(expected, actual, delta) 比較浮點數大小,只要期望值和聲明值之間的差值小等於delta,那么就斷言相等。Math.abs(expected - actual) <= delta

    這樣寫就沒問題了

  • 問題3
    導jar包是最費勁的……好幾次導出來結果命令窗口中執行出錯。
    提示沒有主清單解決方法:用WinRAR打開jar包,找到這個文件
    打開添加Main-Class: main,注意冒號后有空格。

    還有這個問題
    至今沒明白具體原因,搜索到的答案基本都是說路徑配置出錯,然而我編譯器里就運行的好好的……這種情況一般是導出的時候沒設置好,就猜+搜+搞,莫名其妙就行了。

7.總結

我變禿了,也變強了

  • 第一次用jprofiler,第一次用git,第一次配置maven……學到了很多東西,主要是增強了查資料的能力……整個過程走下來寫代碼反而是最輕松的,學習新東西是最難的,單一個git的用法就研究了一下午。找到的教程總是走到一半就出問題,然后又去百度解決方法,怎么導jar包,怎么配置maven,怎么用git push,於是瀏覽器常常掛着好幾個窗口。在過程中也意識到自己跟別人的差距,在別人眼里可能是常識的東西我得搞幾小時,好在最后都順利解決了。
  • 編碼過程中同樣遇到了一些困難,因為Java語言還不太熟練,寫代碼的時候下意識的想用C的東西,有時候寫到一半開始面向百度編程。比如存詞向量的存儲,開始寫了個map<string,vector >offset1,編譯器直接報錯,在編譯器的提示下蒙出了正確寫法Map<String, Vector > Offset1=new TreeMap<String,Vector >();
    ……害,積累的東西太少,現在開始吃虧了,好在這也是一個積累的過程,今后的學習和工作中總會派上用場吧。

“你的負擔將變成禮物,你受的苦將照亮你的路。”


免責聲明!

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



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