Python實例:申報項目查重系統設計與實現
作者:白寧超
2017年5月18日17:51:37
摘要:關於查重系統很多人並不陌生,無論本科還是碩博畢業都不可避免涉及論文查重問題,這也對學術不正之風起到一定糾正作用。單位主要針對科技項目申報審核,傳統的方式人力物力比較大,且伴隨季度性的繁重工作,效率不高。基於此,單位覺得開發一款可以達到實用的智能查重系統。遍及網絡文獻,終未得到有價值的參考資料,這個也是自然。首先類似知網,paperpass這樣的商業公司其畢業申報專利並進行保密,其他科研單位因發展需要也不會開源。筆者就結合NLP相關知識進行設計一款自主的查重系統,首先采用自然語言處理方法主要提出兩個模型:科技項目查重的訓練模型和科技項目查重的測試模型。其中訓練模型主要對數據的清洗預處理及其規約化處理,測試系統也是主查重系統,對其查重原理和性能進行設計實現。最后將其封裝成包,PHP或者Java等語言調用即可。(本文原創編著,轉載注明出處:Python實例:申報項目查重系統設計與實現)
1 開發環境部署
硬件環境:普通台式機或者筆記本一台,可以正常連網,配置不作特別要求
軟件環境:win7以上系統,本機采用WIN10 64位系統
開發環境:Sublime + Python3.5(Anaconda)
分詞工具:先行的分詞工具都可以,本文采用結巴分詞
PHP調用環境:WampServer(php開發集成環境)
2 查重系統需要分析
背景:科技相關工作者通過計划項目管理平台進行項目申報,這個過程中存在涉嫌造假,修改本人以往項目等一系列違規操作。為了遏制這種現象,開發一款智能的項目查重系統必然不能或缺。
需求:低版本主要控制申報項目的標題和簡介查重問題,實現對相似度較高的項目進行查重。用戶提交申報項目后,自動審查是否存在違規行為。
解決:1 從服務器中導出今年真實的申報項目作為訓練集(目前采用真實項目2400多個),通過對訓練數據集的一系列數據清洗,然后進行語料庫構建工作。2 采用文本相似度原理對測試文本進行建模,最后通過文本相似算法的實現,完成查重系統。3 PHP調用python查重文件,實現操作。
問題:1 真實語料規模有所限制,伴隨語料擴大效果更好。2 文本相似度多種算法比較,包括:歐幾里德距離、余弦定理、皮爾遜相關度、曼哈頓距離、Jaccard系數、gensim相似度等,改進版采用合適的相似度算法。3 對同義詞、近義詞、稀有詞、核心詞等權重問題的改進 4 后續改進針對整篇文章和主題識別
3 查重系統設計流程
本查重系統設計分為兩大步驟:訓練模型和測試模型。
訓練模型:
1 首先從數據庫中導出原始申報項目核心字段數據,其中subject代表項目課題名稱,summary代表課題項目簡介。
2 通過算法完成語料標記工作,具體算法原理和實現參見下文,效果如下圖:
3 對數據進行預處理,其中包括正則匹配、文本分詞、停用詞處理、字符串操作、規約化數據等,處理后結果如下:
4 采用余弦相似度進行相似算法處理,最后識別結果如下:
5 PHP調用Python算法的運行結果:
當重復率大於閾值時:
當重復率小於閾值時:
4 科技項目查重訓練模型
1 針對路徑進行配置,其中源語料為datas.txt,標記處理后保存到同目錄下的flagdatas.txt中
2 對原始語料進行標記和簡單清洗,之所以采用標記,一方面便於序列化展示,另一方面區分項目名稱和簡介。
3 保存標記后的語料並統計處理結果
4 對標記數據進行分詞處理。本文采用的結巴分詞,分詞后可以采用兩種情況的處理,其一是不帶詞性標記的處理方式,直接將分詞結果與停用詞詞典進行比對,去除停用詞。其二是采用帶有詞性標注的分詞結果,我們然后采用詞典和去除詞性的方式進行預處理,其中諸如虛詞,助詞等可以根據業務需求去除。而相對於名詞、動詞等主要詞性,可以通過增加權重的方式進行處理。最終保村分詞結果。作為訓練語料庫即對比語料庫。
具體預處理源碼如下:
# 1 針對語料路徑進行配置 path="../CheckRepeat/database/OrigCorpus/datas.txt" # 訓練語料庫 flagpath="../CheckRepeat/database/OrigCorpus/flagdatas.txt" # 語料標記 # 2 對原始語料進行標記和簡單清洗 listset="" # 標記后的語料集合 i,j=1,1 with open(path,'r',encoding='utf-8') as f: for rline in f.readlines(): line = rline.strip().replace(" ","") if "summary" in line : listset +="\n"+str(i)+"_"+line i+=1 # 簡介打標簽 elif "subject" not in line: listset +=line elif "subject" in line: listset +="\n"+str(j)+"_"+line j+=1 # 項目題目打標簽 # 3 保存標記后語料並統計標記結果 with open(flagpath,'w',encoding='utf-8') as f1: f1.write(listset.strip()) print("="*70) print("項目共計標題:"+str(len(listset.split("subject"))-1)) print("項目共計簡介:"+str(len(listset.split("summary"))-1)) print("-"*70) # 4 對標記數據進行分詞處理 cutpath="../CheckRepeat/database/OrigCorpus/cutdatas.txt" # 保存分詞后的結果 cutword(flagpath,cutpath)
其執行2405條數據的性能如下:具體分析發現啟動分詞工具耗時1.2秒左右,其余耗時量主要是停用詞比對時,兩個矩陣計算造成的,這個問題可以通過算法改進,縮短耗時。
5 科技項目查重實現(測試)系統
1 接收項目文件(項目名稱/項目簡介)進行預處理操作,其實這個過程跟模型訓練時的預處理算法是一致的。
2 將訓練階段處理后的數據進行處理,分割出項目名稱/項目簡介,分布存放在兩個list中。
3 判斷參數是否為空,如果不為空將對應的參數放入驗證查重結果。
4 以測試數據和訓練的每條數據的詞項構建文本向量,通過文本向量的夾角即判斷文本相似度,並反饋出結果。本文采用的是python自帶的difflib模塊進行處理,difflib是python提供的比較序列(string list)差異的模塊。實現了三個類:1>SequenceMatcher 任意類型序列的比較 (可以比較字符串)2>Differ 對字符串進行比較3>HtmlDiff 將比較結果輸出為html格式.理由是其相對比較成熟,本項目的處理量並不大。倘若處理G級或P級規模的數據,可以考慮使用google的gensim相似度算法,大大提高處理速度,幸運的是該算法也是python自帶的一個模塊。當然對其原理的理解,建議還是自己實現下。
5 對查重后的結果進行處理,可以保存到本地,也可以直接輸出,由於本項目主要提供php調用,切php調用執行py文件之后,對輸出結果不能換行處理,所以本項目添加一些html標簽和css樣式。
查重實現核心源碼如下:
def checkfun(namestr): subject={} # 記錄查重結果,鍵值對,原句+重復率 summary={} # 1 找到對比庫的歷史數據 checkpath ="../CheckRepeat/database/OrigCorpus/cutdatas.txt" # 數據庫中對比項目語料庫 with open(checkpath,"r",encoding="utf-8") as f: checklist=[line[:] for line in f.readlines()] subjectname=[sub for sub in checklist if "subject" in sub] # 項目名稱 summaryname=[summ for summ in checklist if "summary" in summ] # 項目簡介 if "subject" in namestr: # 2 進行項目名稱驗證操作 for rline in subjectname: line = ''.join(str(rline).split(' ')[2:]) subp = difflib.SequenceMatcher(None,namestr.split('\n')[0].replace('subject',''),line).ratio() subject[line]=float('%.4f'%(subp)) if "summary" in namestr: # 3 進行項目簡介驗證操作 for rline in summaryname: line = ''.join(str(rline).split(' ')[2:]) sump = difflib.SequenceMatcher(None,namestr.split('\n')[1].replace('summary',''),line).ratio() summary[line]=float('%.4f'%(sump)) # 4 打印檢測結果 outreslut="" sort1=sorted(subject.items(),key=lambda e:e[1],reverse=True) #排序 outreslut +="項目名稱:"+"*"*5+"["+namestr.split('\n')[0].replace('subject','') + "]"+"*"*5+"的查重結果如下:\n\n" for item in sort1[:1]: if item[1] >= 0.5: outreslut += "與項目庫中\t[<span style=\"color:red\">"+item[0].replace("\n",'')+"</span>]\t的相似率最高:<span style=\"color:red\">"+str(item[1]) +"</span>\n" else: outreslut += "<span style=\"color:green\">沒有查出重復的項目簡介</span>\n" sort2=sorted(summary.items(),key=lambda e:e[1],reverse=True) #排序 outreslut += "\n\n項目簡介:"+"*"*5+"["+namestr.split('\n')[1].replace('summary','') + "]"+"*"*5+"的查重結果如下:\n\n" for item in sort2[:1]: if item[1] >= 0.5: outreslut += "與項目庫中\t[<span style=\"color:red\">"+item[0].replace("\n",'')+"</span>]\t的相似率最高:<span style=\"color:red\">"+str(item[1]) +"</span>\n" else: outreslut += "<span style=\"color:green\">沒有查出重復的項目簡介</span>\n" # 5 寫到文件里面 with open("../CheckRepeat/database/DealCorpus/checkout.txt",'w',encoding='utf-8') as f: f.write(outreslut) print(outreslut)
其運行結果如下:
6 PHP調用Python查重系統
1 php調用主要是$program變量下的語句,原理是:
python.exe的絕對路徑,空格,調用py的主文件絕對路徑,空格,參數1,空格,參數2
2 py文件通過sys相關參數進行接收,sys.argv[0]是py文本名,所以1-n對應你傳入的參數如下:
# subject = sys.argv[1]
# summary = sys.argv[2]
3 exec($program,$result,$N)其執行py程序並返回一條字符串,返回結果保存在$result中。$N代表執行是否成功,成功返回1
4 shell_exec($program) 返回py文件所有語句的輸出
5 php與py之間存在亂碼問題,可以通過mb_convert_encoding ($output,"UTF-8", "GBK")解決
<?php $name=mb_convert_encoding($_POST['projectname'], "GBK","UTF-8"); $sumb=mb_convert_encoding($_POST['projectsumb'], "GBK","UTF-8"); $program="D:/Users/Administrator/Anaconda3/python E:/pythonSource/CheckArticle/CheckRepeat/checkIndex.py ".$name." ".$sumb.""; #注意使用絕對路徑.$name."".$sumb $output = nl2br(shell_exec($program)); // $program1="D:/Users/Administrator/Anaconda3/python ../CheckRepeat/test.py ".$name." ".$sumb.""; #注意使用絕對路徑.$name."".$sumb // $result1 = shell_exec($program1); echo mb_convert_encoding ($output,"UTF-8", "GBK"); // if ($output!=null){ // print_r(nl2br(file_get_contents('../CheckRepeat/database/DealCorpus/checkout.txt'))); // } ?>
7 參考文獻:
[1] TF-IDF與余弦相似性的應用:http://www.ruanyifeng.com/blog/2013/03/tf-idf.html
[2] 海量數據相似度計算之simhash短文本查找:http://www.lanceyan.com/tag/simhash
[3] 用TF特征向量和simhash指紋計算中文文本的相似度:https://github.com/zyymax/text-similarity
[4] gensim文檔相似度判斷:http://kekefund.com/2016/05/27/gensim-similarity/
[5] python文本相似度計算:http://www.jianshu.com/p/edf666d3995f
[6] 如何計算兩個文檔的相似度:https://flystarhe.github.io/2016/08/30/document-similarity/
[7] gensim文檔相似度判斷:http://kekefund.com/2016/05/27/gensim-similarity/