前言
| 軟件工程 | 軟件工程 |
|---|---|
| 作業要求 | 要求 |
| 作業目標 | 代碼實現,功能優化,單元測試PSP表格實現 |
代碼在github
具體要求如下:
功能:論文查重
需求描述如下:
設計一個論文查重算法,給出一個原文文件和一個在這份原文上經過了增刪改的抄襲版論文的文件,在答案文件中輸出其重復率。
原文示例:今天是星期天,天氣晴,今天晚上我要去看電影。
抄襲版示例:今天是周天,天氣晴朗,我晚上要去看電影。
要求輸入輸出采用文件輸入輸出,規范如下:
從命令行參數給出:論文原文的文件的絕對路徑。
從命令行參數給出:抄襲版論文的文件的絕對路徑。
從命令行參數給出:輸出的答案文件的絕對路徑。
我們提供一份樣例,課堂上下發,上傳到班級群,使用方法是:orig.txt是原文,其他orig_add.txt等均為抄襲版論文。
注意:答案文件中輸出的答案為浮點型,精確到小數點后兩位
PSP
| PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
|---|---|---|---|
| Planning | 計划 | 20 | 30 |
| · Estimate | · 估計這個任務需要多少時間 | 35 | 45 |
| Development | 開發 | 200 | 300 |
| · Analysis | · 需求分析 (包括學習新技術) | 120 | 150 |
| · Design Spec | · 生成設計文檔 | 30 | 50 |
| · Design Review | · 設計復審 | 30 | 40 |
| · Coding Standard | · 代碼規范 (為目前的開發制定合適的規范) | 30 | 50 |
| · Design | · 具體設計 | 50 | 60 |
| · Coding | · 具體編碼 | 200 | 300 |
| · Code Review | · 代碼復審 | 30 | 50 |
| · Test | · 測試(自我測試,修改代碼,提交修改) | 60 | 30 |
| Reporting | 報告 | 30 | 50 |
| · Test Repor | · 測試報告 | 30 | 50 |
| · Size Measurement | · 計算工作量 | 10 | 10 |
| · Postmortem & Process Improvement Plan | · 事后總結, 並提出過程改進計划 | 30 | 30 |
| 合計 | 400 | 500 |
實現思路
思考了很久,最后發現該程序主要是需要實現比較兩個文本的文本相似度問題
於是搜索引擎查找應該如何實現,發現有如下方法:

查看了各種方法之后,比較覺得利用余弦距離實現比較實際。
余弦相似度
余弦相似度量:計算個體間的相似度。
相似度越小,距離越大。相似度越大,距離越小。
假設有3個物品,item1,item2和item3,用向量表示分別為:
item1[1,1,0,0,1],
item2[0,0,1,2,1],
item3[0,0,1,2,0],
即五維空間中的3個點。用歐式距離公式計算item1、itme2之間的距離,以及item2和item3之間的距離,分別是:
item1-item2=

item2-item3=

用余弦函數計算item1和item2夾角間的余弦值為:

用余弦函數計算item2和item3夾角間的余弦值為:

由此可得出item1和item2相似度小,兩個之間的距離大(距離為7),item2和itme3相似度大,兩者之間的距離小(距離為1)。
余弦相似度算法: 一個向量空間中兩個向量夾角間的余弦值作為衡量兩個個體之間差異的大小,余弦值接近1,夾角趨於0,表明兩個向量越相似,余弦值接近於0,夾角趨於90度,表明兩個向量越不相似。
余弦相似度量: 計算個體間的相似度。
相似度越小,距離越大。相似度越大,距離越小。
余弦相似度算法:一個向量空間中兩個向量夾角間的余弦值作為衡量兩個個體之間差異的大小,余弦值接近1,夾角趨於0,表明兩個向量越相似,余弦值接近於0,夾角趨於90度,表明兩個向量越不相似。
下面我們介紹使用余弦相似度計算兩段文本的相似度。思路:1、分詞;2、列出所有詞;3、分詞編碼;4、詞頻向量化;5、套用余弦函數計量兩個句子的相似度。
句子A:這只皮靴號碼大了。那只號碼合適。
句子B:這只皮靴號碼不小,那只更合適。
1、分詞:
使用結巴分詞對上面兩個句子分詞后,分別得到兩個列表:
listA=[‘這‘, ‘只‘, ‘皮靴‘, ‘號碼‘, ‘大‘, ‘了‘, ‘那‘, ‘只‘, ‘號碼‘, ‘合適‘]
listB=[‘這‘, ‘只‘, ‘皮靴‘, ‘號碼‘, ‘不小‘, ‘那‘, ‘只‘, ‘更合‘, ‘合適‘]
2、列出所有詞,將listA和listB放在一個set中,得到:
set={‘不小’, ‘了’, ‘合適’, ‘那’, ‘只’, ‘皮靴’, ‘更合’, ‘號碼’, ‘這’, ‘大’}
將上述set轉換為dict,key為set中的詞,value為set中詞出現的位置,即‘這’:1這樣的形式。
dict1={‘不小’: 0, ‘了’: 1, ‘合適’: 2, ‘那’: 3, ‘只’: 4, ‘皮靴’: 5, ‘更合’: 6, ‘號碼’: 7, ‘這’: 8, ‘大’: 9},可以看出“不小”這個詞在set中排第1,下標為0。
3、將listA和listB進行編碼,將每個字轉換為出現在set中的位置,轉換后為:
listAcode=[8, 4, 5, 7, 9, 1, 3, 4, 7, 2]
listBcode=[8, 4, 5, 7, 0, 3, 4, 6, 2]
我們來分析listAcode,結合dict1,可以看到8對應的字是“這”,4對應的字是“只”,9對應的字是“大”,就是句子A和句子B轉換為用數字來表示。
4、對listAcode和listBcode進行oneHot編碼,就是計算每個分詞出現的次數。oneHot編號后得到的結果如下:
listAcodeOneHot = [0, 1, 1, 1, 2, 1, 0, 2, 1, 1]
listBcodeOneHot = [1, 0, 1, 1, 2, 1, 1, 1, 1, 0]
步驟一
將我們的文本處理,因為我們要比對兩個文本的話,一定需要將文本的一切符號,包括換行符以及所有的標點符號,去掉。這里我主要利用了python的正則表達式實現
def Symbol_filter(str):
# 使用正則表達式過濾標點符號
result = re.sub('\W+', '', str).replace("_", '')
return result
def get_content (path):
# 文本處理,將我們的文本處理為字符串,並且過濾掉標點符號
string = ''
file = open(path, 'r', encoding='UTF-8')
one_line = file.readline()
while one_line:
string += one_line
one_line = file.readline()
# 調用標點符號過濾函數
string = Symbol_filter(string)
file.close()
return string
實現效果:
步驟二
接下來需要思考我們怎樣去實現文本相似度的計算,這里我查找了很多資料找到了jieba包和gensim包的利用

大致思路如上圖(但是有幾步步驟可以省略)
通過jieba分詞進行處理
代碼實現:
def Turn_vector(str):
# 將我們的字符串切片
string = jieba.lcut(str)
return string
求文本相似度
def similarity_vul(str_x,str_y):
texts = [str_x, str_y]
# 語料庫
dictionary = gensim.corpora.Dictionary(texts)
print(dictionary)
num_features = len(dictionary.token2id)
# 利用doc2bow作為詞袋模型
corpus = [dictionary.doc2bow(text) for text in texts]
print(corpus)
similarity = gensim.similarities.Similarity('-Similarity-index', corpus, num_features)
# 獲取文章相似度
test_corpus_1 = dictionary.doc2bow(str_x)
cosine_sim = similarity[test_corpus_1][1]
return cosine_sim
函數主要流程圖

性能分析
時間分析:

代碼覆蓋率:

函數時間占用

單元測試
測試代碼
import unittest
from main import *
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertEqual(main_test(), 0.98)
if __name__ == '__main__':
unittest.main()
測試效果如下圖

異常處理
考慮輸入的路徑可能出錯,不存在文件的情況,則可以用以下的代碼處理這個異常
if not os.path.exists(path_1):
print("文件不存在")
exit()
