1. 程式人生 > >FZU軟工第五次作業-詞組頻率分析

FZU軟工第五次作業-詞組頻率分析

\n openacc ntc dex div 學習 trac numbers access

00.前言:

  • 本次作業鏈接
  • 隊友博客鏈接
  • github倉庫鏈接
  • 結對成員:031602605 陳浩 and 031602634 吳誌鴻

01.分工:

  • 031602605 陳浩:負責詞頻分析部分,在原WordCount的基礎上進行升級,添加新的命令行參數支持更多的功能包括自定義輸入輸出文件,權重詞頻統計,詞組統計等新功能的設計。
  • 031602634 吳誌鴻:負責關於爬蟲部分的所有設計,從CVPR2018官網爬取今年的論文列表,以及其他拓展功能的設計。

02.PSP表格:

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 30 25
Estimate 估計這個任務需要多少時間 30 25
Development 開發 480 600
Analysis 需求分析 (包括學習新技術) 60 120
Design Spec 生成設計文檔 35 30
Design Review 設計復審 20 20
Coding Standard 代碼規範 (為目前的開發制定合適的規範) 20 25
Design 具體設計 120 150
Coding 具體編碼 120 120
Code Review 代碼復審 120 150
Test 測試(自我測試,修改代碼,提交修改) 60 120
Reporting 報告 90 120
Test Repor 測試報告 30 45
Size Measurement 計算工作量 10 15
Postmortem & Process Improvement Plan 事後總結, 並提出過程改進計劃 30 35
合計 1225 1600

03.解題思路描述與設計實現說明:

解題思路簡述:

  • 本次作業主要分成兩個步驟,先是根據題目需要,按照格式要求爬取論文列表;之後在對爬取的論文列表進行詞頻分析。實現對WordCount的升級。
  • 爬蟲使用
    • 工具:Python 3.6
    • 思路:一開始是先去CVPR2018官網檢查頁面元素觀察規律;發現每一篇論文都有對應一個超鏈接,並且點開後就有論文的基本信息。之後就按照特征進行爬取即可。
      • 如下圖 id = content的塊裏存儲了所需的論文列表,ptitle裏存儲了論文的具體網址
        技術分享圖片

關鍵代碼

  • 讀取網頁內容
    target_url = ‘http://openaccess.thecvf.com/CVPR2018.py‘
    target_req = request.Request(url = target_url, headers = head)
    target_response = request.urlopen(target_req)
    target_html = target_response.read().decode(‘utf-8‘)
    #創建BeautifulSoup對象
    listmain_soup = BeautifulSoup(target_html,‘lxml‘)
    #搜索文檔樹,找出div標簽中id為content的所有子標簽
    chapters = listmain_soup.find_all(‘div‘,id = ‘content‘)
    #使用查詢結果再創建一個BeautifulSoup對象,對其繼續進行解析
    download_soup = BeautifulSoup(str(chapters), ‘lxml‘)
  • 依次打開對應論文網址進行爬取,內容讀入到文件
    file = open(‘.\\result.txt‘, ‘w‘, encoding=‘utf-8‘)
    numbers = len(download_soup.dl.contents) 
    index = 0
    #開始記錄內容標誌位,只要正文卷下面的鏈接,最新章節列表鏈接剔除
    begin_flag = True
    #遍歷dl標簽下所有子節點
    for child in download_soup.dl.children:
        #濾除回車
        if(child != ‘\n‘ and child.name != "dd"):
            print(child.name)
            #爬取鏈接下載鏈接內容
            if begin_flag == True and child.a != None:
                download_url = "http://openaccess.thecvf.com/"  +  child.a.get(‘href‘)
                download_req = request.Request(url = download_url, headers = head)
                download_response = request.urlopen(download_req)
                download_html = download_response.read().decode(‘utf-8‘)
                download_name = child.a.string
                soup_texts = BeautifulSoup(download_html, ‘lxml‘)
                #改
                texts = soup_texts.find_all(‘div‘,id = ‘content‘)
                soup_text = BeautifulSoup(str(texts), ‘lxml‘)
                write_flag = True
                file.write(str(index) +‘\n‘)
                #將爬取內容寫入文件
                file.write(‘Title: ‘+download_name + ‘\n‘)
                #作者信息
#                authors=soup_text.find(‘div‘,id = ‘authors‘)
#                file.write(‘Title: ‘authors.string + ‘\n‘)
                abstract=soup_text.find(‘div‘,id = ‘abstract‘)
                file.write(‘Abstract: ‘+abstract.string[1:] + ‘\n‘)
                file.write(‘\n\n‘)
                index += 1
    file.close()
  • 代碼組織與內部實現設計(類圖)
    • 主要由兩個類組成
    • Word類用於實現詞頻分析的功能
      技術分享圖片
    • FIle類用於實現對命令行參數的解析,實現對文本的輸入輸出
      技術分享圖片
  • 說明算法的關鍵與關鍵實現部分流程圖
    • 主要說明新加三個功能
    • 1.解析命令行參數:
      - 遍歷*argv[]數組
      • 遇到 -i 時 讀取後面的輸入路徑字符,並寫入輸入路徑中
      • 遇到 -o 時 讀取後面的輸出路徑字符,並寫入輸出路徑中
      • 遇到 -m 時 讀取後面的字符串,並利用函數轉換成正整數,存儲數值
      • 遇到 -n 時 讀取後面的字符串,並利用函數轉換成正整數,存儲數值
      • 遇到 -w 時 讀取後面的字符串,並利用函數轉換成正整數,存儲數值
    • 2.權重統計:
      • 首先根據從-w參數後讀出的數值是0和1分別選擇兩種權重模式。
      • 每次利用getline函數讀取一行,判斷開頭第一個單詞是 “Title“” 還是“Absatra”,在根據權重模式來分別計算兩種情況下單詞的權重值。
      • 流程圖如下
        技術分享圖片
    • 3.詞組統計:
      • 獲取一個合法輸入單詞,將單詞的起始加入隊列,判斷下一個單詞是不是合法單詞,如果不是,清空隊列,重新獲取單詞;如果是,則循環上一次操作,隊列長度+1,直到隊列長度符合輸入的指定詞組長度,從隊列中得到單詞位置,記錄詞組。
      • 流程圖如下
        技術分享圖片

04.附加題設計與展示:

  • 設計的創意獨到之處

  • 實現思路

  • 實現成果展示

05.關鍵代碼解釋:

  • 解析命令行參數
void File::del(char *argv[])
{
    for (int i = 1; argv[i] != NULL; i = i + 2)
    {
                //遇到-i參數時,讀入讀取文件地址
        if (strcmp(argv[i],"-i") == 0) {
            input = argv[i + 1];
            continue;
        }
                //遇到-o參數時,讀入寫入文件地址
        else if (strcmp(argv[i], "-o") == 0) {
            output = argv[i + 1];
            continue;
        }
                //遇到-w參數時,判斷權重
        else if (strcmp(argv[i], "-w") == 0) {
            fcountquz = atoi(argv[i + 1]);
            //cout << countquz << "aaa" << endl;
        }
                //遇到-m參數時,得到詞組大小
        else if (strcmp(argv[i], "-m") == 0) {
            fcountphrase = atoi(argv[i + 1]);
            //cout << countphrase << "bbb" << endl;
        }
                //遇到-n參數時,得到輸出前n個詞組
        else if (strcmp(argv[i], "-n") == 0) {
            fouttop = atoi(argv[i + 1]);
            //cout << outtop << "ccc" << endl;
        }
    }
    return;
}
  • 詞組判斷
if (answord >= 4)
{           
    //  獲得一個單詞後
    phraseans++;
    phrase.push_back(i- answord);//單詞位置送入隊列當中
    if (phraseans == countphrase)//判斷隊列長度是否符合
    {
        int lon = phrase.front();//詞組起始位置;
        phrase.pop_front();
        str = name.substr(lon, i - lon);
        mapword[str] = mapword[str] + beishu;
        phraseans--;//輸出隊列中的首個單詞位置
    }
    words++;
}                                   

06.性能分析與改進:

改進思路

  • 本次代碼是在原有的WordCount的基礎上對代碼進行改進,在對命令行參數進行解析時,我主要是用一個循環匹配命令行標誌位,之後用atoi函數獲得對應參數;在單詞/詞組的存儲上,我還是使用unordered_map進行存儲,在詞組判斷方面我主要是用雙向隊列deque存儲每個單詞的起始位置,當隊列當中存儲的標誌位達到上限時,輸出標誌位,截取字符串,這樣做法的好處在於無論是單個的單詞,還是一個詞組,這樣都可以使用,原本我打算用指針對單詞標識位進行存儲,後面發現鏈表查找效率太低,所以用了deque。暫時沒有更好的思路了。

  • 測試代碼時其中一個測試樣例如下(命令行參數為-i ./result.txt -o output.txt -w 1 -m 3 )
0
Title: Embodied Question Answering
Abstract: We present a new AI task -- Embodied Question Answering (EmbodiedQA) -- where an agent is spawned at a random location in a 3D environment and asked a question ("What color is the car?"). In order to answer, the agent must first intelligently navigate to explore the environment, gather necessary visual information through first-person (egocentric) vision, and then answer the question ("orange").  EmbodiedQA requires a range of AI skills -- language understanding, visual recognition, active perception, goal-driven navigation, commonsense reasoning, long-term memory, and grounding language into actions. In this work, we develop a dataset of questions and answers in House3D environments, evaluation metrics, and a hierarchical model trained with imitation and reinforcement learning.
  • 樣本輸出結果:
characters: 817
words: 80
lines: 2
<embodied question answering>: 11
<active perception, goal>: 1
<agent must first>: 1
<answering (embodiedqa) -- where>: 1
<commonsense reasoning, long>: 1
<driven navigation, commonsense>: 1
<environment, gather necessary>: 1
<environments, evaluation metrics>: 1
<first intelligently navigate>: 1
<first-person (egocentric>: 1
  • 本次性能分析測試主要使用從CVPR2018官網爬取的979篇文章的標題和摘要來進行測試,性能分析圖如下:
    技術分享圖片
  • 程序中消耗最大的函數:
    技術分享圖片

  • 測試得到代碼覆蓋率如下所示
    技術分享圖片
  • 代碼覆蓋率達到了90%,沒有更高的原因在於代碼中有寫入對文件的異常處理如下
    技術分享圖片

07.單元測試:

  • 本次一共做了10組單元測試如下,重要測試附有代碼,具體如下:
  • 1.測試空白輸入文本(行數、字符數和單詞數都應該為0,測試函數Countcharacters、Countlines、Countwords)
TEST_CLASS(EmptyTest)
    {
    public:
        TEST_METHOD(TestMethod1)
        {
            //空白文本
            File f;
            Word w;
            f.Filein();
            int num = w.Countcharacters(f.fin);
            int num1 = w.Countlines(f.fin);
            int num2 = w.Countwords(f.fin);
            Assert::IsTrue( (num==0) &&(num1==0) && (num2== 0) );
            // TODO: 在此輸入測試代碼
        }
    };
  • 2.測試不存在輸入文本文本輸入(文本輸入路徑不存在,返回值為1,測試Filein函數)
TEST_CLASS(UnexistTest)
    {
    public:
        TEST_METHOD(TestMethod1)
        {
            //錯誤文本
            File f;
            Word w;
            f.input = "./unexist.txt";
            int num=f.Filein();
            Assert::IsTrue(num == 1);
            // TODO: 在此輸入測試代碼
        }
    };
  • 3.測試只含Title的輸入文本(返回行數為1行,測試函數Countcharacters、Countlines、Countwords)

  • 4.測試只含Abstract的文本輸入(返回行數為1行,測試函數Countcharacters、Countlines、Countwords)

  • 5.測試純數字樣本(返回字符數應該為0,測試函數Countwords)

    TEST_CLASS(NumTest)
      {
      public:
          TEST_METHOD(TestMethod1)
          {
              //測試純數字樣本
              File f;
              Word w;
              f.input = "./input2.txt";
              f.Filein();
              int num2 = w.Countwords(f.fin);
              Assert::IsTrue(num2 == 0);
              // TODO: 在此輸入測試代碼
          }
      };
    
  • 6.測試基本案例(從爬取論文列表中選出其中一項,測試函數Countcharacters、Countlines、Countwords)

  • 7.測試大型樣本含權重樣本輸出詞組(從爬取論文列表中選出其中多項,測試函數Countcharacters、Countlines、Countwords、Counttop10)

    TEST_CLASS(TopTest1)
      {
      public:
          TEST_METHOD(TestMethod1)
          {
              //測試大型樣本含權重樣本輸出詞組
              File f;
              Word w;
              w.set(3, 1, 20);
              f.input = "./top.txt";
              f.Filein();
              vector<pair<string, int>> v = w.Counttop10(f.fin, 20);
              int characters = w.Countcharacters(f.fin);
              int word = w.Countwords(f.fin);
              int line = w.Countlines(f.fin);
              vector<pair<string, int>>::iterator iter = v.begin();
              Assert::IsTrue(characters == 2915 && word ==287 && line == 6 && iter->second == 11);
              // TODO: 在此輸入測試代碼
          }
      };
    
  • 8.測試大型樣本不含權重樣本輸出詞組

  • 9.測試含權重樣本的輸出詞組(從爬取論文列表中選出其中多項,讀取詞組內容,測試函數Countcharacters、Countlines、Countwords、Counttop10)

TEST_CLASS(PhraseTest1)
    {
    public:
        TEST_METHOD(TestMethod1)
        {
            //測試含權重樣本的輸出詞組
            File f;
            Word w;
            w.set(3, 1, 66);
            f.input = "./P.txt";
            f.Filein();
            vector<pair<string, int>> v = w.Counttop10(f.fin,66);
            int characters = w.Countcharacters(f.fin);
            int word = w.Countwords(f.fin);
            int line = w.Countlines(f.fin);
            int num = v.size();
            vector<pair<string, int>>::iterator iter = v.begin();
            Assert::IsTrue(characters == 817 && word == 80 && line == 2 && num == 38 && iter->first == "embodied question answering" && iter->second==11);
            // TODO: 在此輸入測試代碼
        }
    };
  • 10.測試不含權重樣本的輸出詞組
    技術分享圖片

08.Github的代碼簽入記錄:

技術分享圖片

9.遇到的代碼模塊異常或結對困難及解決方法:

  • 問題描述
    ??在單元測試時,發現單元測試一直通過不了;在詞組統計時也出現問題,存在非ascll碼。
  • 做過哪些嘗試
    ??單元測試無法通過一開始以為是代碼的問題,後面發現是還是對vs的使用不夠熟悉。對於詞組統計中存在的非ascll碼選擇先進行過濾之後在進行排查。
  • 是否解決
    ??最後兩個問題都成功解決了
  • 有何收獲
    ??對vs的單元測試操作流程更加熟悉,同時對於c++debug能力有所提升。對數據內容需要進行合理的判斷,判斷他們的特征從而找出解題的途徑。

10.評價你的隊友:

  • ??阿鴻小哥哥是一個很負責很有趣的人,做事態度認真負責,學習效率很快,我們一起討論過怎麽用c++實現爬蟲,最後只討論出一個思路,在實現的時候發現對c++的第三庫不是很熟悉,我比較浮躁的時候,阿鴻都會很冷靜的繼續完成作業。需要改進的地方是阿鴻代碼基礎不是很好,還是需要盡快提高代碼能力帶帶我。(畢竟我也是蒟蒻...orz)

11.學習進度條:


第N周 新增代碼(行) 累計代碼(行) 本周學習耗時(小時) 累計學習耗時(小時) 重要成長
1 1200 1000 20 20 VS初步學習,溫習了c++ stl庫
2 500 1700 12 32 學習了Axure RP,理解了NABCD模型
3 1000 2700 20 52 溫習了Python,學習了一些Android開發的知識
4 500 3200 10 62 繼續學習了一些Android開發的知識

FZU軟工第五次作業-詞組頻率分析