1. 程式人生 > >【NLP】Python實例:基於文本相似度對申報項目進行查重設計

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

用戶 strip() 字符串 執行 原創 這樣的 string 得到 亂碼問題

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/

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