【NLP】Python實例:基於文本相似度對申報項目進行查重設計


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/

 

 

 


免責聲明!

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



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