FZU軟工第五次作業-詞組頻率分析
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裏存儲了論文的具體網址
- 如下圖 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軟工第五次作業-詞組頻率分析