【深度学习】PyCorrector中文文本纠错实战
PyCorrector中文文本糾錯(cuò)實(shí)戰(zhàn)
- PyCorrector糾錯(cuò)工具實(shí)踐和代碼詳解
 - 模型調(diào)參demo
 
1. 簡介
中文文本糾錯(cuò)工具。音似、形似錯(cuò)字(或變體字)糾正,可用于中文拼音、筆畫輸入法的錯(cuò)誤糾正。python3.6開發(fā)。
pycorrector依據(jù)語言模型檢測錯(cuò)別字位置,通過拼音音似特征、筆畫五筆編輯距離特征及語言模型困惑度特征糾正錯(cuò)別字。
1.1 在線Demo
https://www.borntowin.cn/product/corrector
1.2 Question
中文文本糾錯(cuò)任務(wù),常見錯(cuò)誤類型包括:
- 諧音字詞,如 配副眼睛-配副眼鏡
 - 混淆音字詞,如 流浪織女-牛郎織女
 - 字詞順序顛倒,如 伍迪艾倫-艾倫伍迪
 - 字詞補(bǔ)全,如 愛有天意-假如愛有天意
 - 形似字錯(cuò)誤,如 高梁-高粱
 - 中文拼音全拼,如 xingfu-幸福
 - 中文拼音縮寫,如 sz-深圳
 - 語法錯(cuò)誤,如 想象難以-難以想象
 
當(dāng)然,針對不同業(yè)務(wù)場景,這些問題并不一定全部存在,比如輸入法中需要處理前四種,搜索引擎需要處理所有類型,語音識(shí)別后文本糾錯(cuò)只需要處理前兩種,
 其中’形似字錯(cuò)誤’主要針對五筆或者筆畫手寫輸入等。
1.3 Solution
1.3.1 規(guī)則的解決思路
整合這兩種粒度的疑似錯(cuò)誤結(jié)果,形成疑似錯(cuò)誤位置候選集;
1.3.2 深度模型的解決思路
1.4 Feature
1.4.1 模型
- kenlm:kenlm統(tǒng)計(jì)語言模型工具
 - rnn_attention模型:參考Stanford University的nlc模型,該模型是參加2014英文文本糾錯(cuò)比賽并取得第一名的方法
 - rnn_crf模型:參考阿里巴巴2016參賽中文語法糾錯(cuò)比賽CGED2018并取得第一名的方法(整理中)
 - seq2seq_attention模型:在seq2seq模型加上attention機(jī)制,對于長文本效果更好,模型更容易收斂,但容易過擬合
 - transformer模型:全attention的結(jié)構(gòu)代替了lstm用于解決sequence to sequence問題,語義特征提取效果更好
 - bert模型:中文fine-tuned模型,使用MASK特征糾正錯(cuò)字
 - conv_seq2seq模型:基于Facebook出品的fairseq,北京語言大學(xué)團(tuán)隊(duì)改進(jìn)ConvS2S模型用于中文糾錯(cuò),在NLPCC-2018的中文語法糾錯(cuò)比賽中,是唯一使用單模型并取得第三名的成績
 
1.4.2 錯(cuò)誤檢測
- 字粒度:語言模型困惑度(ppl)檢測某字的似然概率值低于句子文本平均值,則判定該字是疑似錯(cuò)別字的概率大。
 - 詞粒度:切詞后不在詞典中的詞是疑似錯(cuò)詞的概率大。
 
1.4.3 錯(cuò)誤糾正
- 通過錯(cuò)誤檢測定位所有疑似錯(cuò)誤后,取所有疑似錯(cuò)字的音似、形似候選詞,
 - 使用候選詞替換,基于語言模型得到類似翻譯模型的候選排序結(jié)果,得到最優(yōu)糾正詞。
 
1.4.4 思考
2. 安裝
作者開源代碼中介紹有兩種安裝方式:
- pip安裝
 
用戶:適合做工程實(shí)踐,不關(guān)心算法細(xì)節(jié),直接調(diào)包使用。
- 源碼安裝
 
用戶:希望了解代碼實(shí)現(xiàn),做一些深層次修改。
2.1 源碼安裝步驟詳解
我們詳細(xì)講解第二種-源碼安裝方式。除了完成上面源碼安裝步驟外,我們還需要安裝一些必要的庫。
2.1.1 安裝必要的庫
pip install -r requirements.txt2.1.2 安裝kenlm
kenlm是一個(gè)統(tǒng)計(jì)語言模型的開源工具,如圖所示,代碼96%都是C++實(shí)現(xiàn)的,所以效率極高,訓(xùn)練速度快,在GitHub上現(xiàn)有1.1K Star
 
pip安裝kenlm
安裝命令如下
pip install kenlm- 筆者嘗試直接pip安裝,報(bào)了如下錯(cuò)誤,機(jī)器環(huán)境(MAC OS 10.15.4)。
 
 若報(bào)錯(cuò),則進(jìn)行如下源碼安裝kenlm,安裝成功則跳過該步驟。
源碼安裝kenlm
下載kenlm代碼
運(yùn)行安裝命令
如下圖所示,則已經(jīng)安裝成功。
3. 環(huán)境測試
3.1 源碼結(jié)構(gòu)
代碼結(jié)構(gòu)如下(clone時(shí)間2020/5/5):
. ├── LICENSE ├── README.md ├── _config.yml ├── build │?? ├── bdist.macosx-10.7-x86_64 │?? └── lib ├── dist │?? └── pycorrector-0.2.7-py3.6.egg ├── docs │?? ├── git_image │?? ├── logo.svg │?? └── 基于深度學(xué)習(xí)的中文文本自動(dòng)校對研究與實(shí)現(xiàn).pdf ├── examples │?? ├── base_demo.py │?? ├── detect_demo.py │?? ├── disable_char_error.py │?? ├── en_correct_demo.py │?? ├── load_custom_language_model.py │?? ├── my_custom_confusion.txt │?? ├── traditional_simplified_chinese_demo.py │?? └── use_custom_confusion.py ├── pycorrector │?? ├── __init__.py │?? ├── __main__.py │?? ├── __pycache__ │?? ├── bert │?? ├── config.py │?? ├── conv_seq2seq │?? ├── corrector.py │?? ├── data │?? ├── deep_context │?? ├── detector.py │?? ├── en_spell.py │?? ├── seq2seq_attention │?? ├── transformer │?? ├── utils │?? └── version.py ├── pycorrector.egg-info │?? ├── PKG-INFO │?? ├── SOURCES.txt │?? ├── dependency_links.txt │?? ├── not-zip-safe │?? ├── requires.txt │?? └── top_level.txt ├── requirements-dev.txt ├── requirements.txt ├── setup.py └── tests├── bert_corrector_test.py├── char_error_test.py├── confusion_test.py├── conv_s2s_interactive_demo.py├── corrector_test.py├── detector_test.py├── en_spell_test.py├── error_correct_test.py├── eval_test.py├── file_correct_test.py├── fix_bug.py├── kenlm_test.py├── memory_test.py├── ner_error_test.py├── ngram_words.txt├── speed_test.py├── test_file.txt├── tokenizer_test.py├── trigram_test.py├── util_test.py└── word_error_test.py3.2 運(yùn)行examples
源碼中examples文件夾中放置了大量的demo,我們可以首先我們先運(yùn)行幾個(gè)demo測試下效果。
3.2.1 運(yùn)行base_demo.py文件
代碼很簡單,調(diào)用了pycorrector.correct方法進(jìn)行糾錯(cuò),入?yún)⑹谴m錯(cuò)的語句,輸出是原句和出錯(cuò)的詞以及糾正的詞,和具體位置。
import syssys.path.append("..")import pycorrectorpycorrector.set_log_level('INFO') if __name__ == '__main__':corrected_sent, detail = pycorrector.correct('少先隊(duì)員因該為老人讓坐')print(corrected_sent, detail)第一次運(yùn)行,講下載預(yù)設(shè)的語料,大概需要10分鐘
下載完之后,講可以進(jìn)行語義糾錯(cuò),如下圖,因該->應(yīng)該,坐標(biāo)在第4位到第6位,同理,另外一個(gè)坐->座,坐標(biāo)在第10位到第11位。
接下來,讀一讀代碼,理一理邏輯。
4. 代碼詳解:
項(xiàng)目源碼位于pycorrector/pycorrector文件夾中
. ├── bert ├── config.py ├── conv_seq2seq ├── corrector.py ├── data ├── deep_context ├── detector.py ├── en_spell.py ├── seq2seq_attention ├── transformer ├── utils └── version.py4.1 傳統(tǒng)規(guī)則模型
模型代碼對應(yīng)于如下三個(gè)文件:
. ├── corrector.py ├── detector.py └── en_spell.py| corrector.py | 拼寫和筆畫糾正 | 
| detector.py | 錯(cuò)誤詞檢測器 | 
| en_spell.py | 英語糾正 | 
4.1.1 錯(cuò)誤詞檢測器(Detector)
4.1.1.1 代碼結(jié)構(gòu)介紹
detector.py中包含一個(gè)Detector類,通過初始化這個(gè)類,來獲得實(shí)例,進(jìn)行錯(cuò)誤詞的檢測。如圖所示,初始化的時(shí)候,我們可以設(shè)置很多配置文件的路徑,比如停用詞文件(stopwords_path)和自定義詞頻文件(custom_word_freq_path)等。
 
Detector類的基礎(chǔ)關(guān)系如下圖所示:
 
其中Object是基類,提供常用的類方法:
 
Detector繼承了Object類,并且實(shí)現(xiàn)了錯(cuò)詞檢測的基本方法,如下圖所示:
 
4.1.1.2 錯(cuò)詞檢測類實(shí)例化
當(dāng)去實(shí)例化Detector類的時(shí)候,只會(huì)保存一些基本諸如文件路徑和配置參數(shù)的值,并不會(huì)立即加載模型進(jìn)行初始化。
class Detector(object):pre_trained_language_models = {# 語言模型 2.95GB'zh_giga.no_cna_cmn.prune01244.klm': 'https://deepspeech.bj.bcebos.com/zh_lm/zh_giga.no_cna_cmn.prune01244.klm',# 人民日報(bào)訓(xùn)練語言模型 20MB'people_chars_lm.klm': 'https://www.borntowin.cn/mm/emb_models/people_chars_lm.klm'}def __init__(self, language_model_path=config.language_model_path,word_freq_path=config.word_freq_path,custom_word_freq_path=config.custom_word_freq_path,custom_confusion_path=config.custom_confusion_path,person_name_path=config.person_name_path,place_name_path=config.place_name_path,stopwords_path=config.stopwords_path):self.name = 'detector'self.language_model_path = language_model_pathself.word_freq_path = word_freq_pathself.custom_word_freq_path = custom_word_freq_pathself.custom_confusion_path = custom_confusion_pathself.person_name_path = person_name_pathself.place_name_path = place_name_pathself.stopwords_path = stopwords_pathself.is_char_error_detect = Trueself.is_word_error_detect = Trueself.initialized_detector = Falseself.lm = Noneself.word_freq = Noneself.custom_confusion = Noneself.custom_word_freq = Noneself.person_names = Noneself.place_names = Noneself.stopwords = Noneself.tokenizer = None4.1.1.2 錯(cuò)詞檢測類初始化
初始化操作被放到_initialize_detector函數(shù)中,通過check_detector_initialized來判斷是否需要進(jìn)行初始化。
def _initialize_detector(self):t1 = time.time()try:import kenlmexcept ImportError:raise ImportError('pycorrector dependencies are not fully installed, ''they are required for statistical language model.''Please use "pip install kenlm" to install it.''if you are Win, Please install kenlm in cgwin.')if not os.path.exists(self.language_model_path):filename = self.pre_trained_language_models.get(self.language_model_path,'zh_giga.no_cna_cmn.prune01244.klm')url = self.pre_trained_language_models.get(filename)get_file(filename, url, extract=True,cache_dir=config.USER_DIR,cache_subdir=config.USER_DATA_DIR,verbose=1)self.lm = kenlm.Model(self.language_model_path)t2 = time.time()logger.debug('Loaded language model: %s, spend: %.3f s.' % (self.language_model_path, t2 - t1))# 詞、頻數(shù)dictself.word_freq = self.load_word_freq_dict(self.word_freq_path)# 自定義混淆集self.custom_confusion = self._get_custom_confusion_dict(self.custom_confusion_path)# 自定義切詞詞典self.custom_word_freq = self.load_word_freq_dict(self.custom_word_freq_path)self.person_names = self.load_word_freq_dict(self.person_name_path)self.place_names = self.load_word_freq_dict(self.place_name_path)self.stopwords = self.load_word_freq_dict(self.stopwords_path)# 合并切詞詞典及自定義詞典self.custom_word_freq.update(self.person_names)self.custom_word_freq.update(self.place_names)self.custom_word_freq.update(self.stopwords)self.word_freq.update(self.custom_word_freq)self.tokenizer = Tokenizer(dict_path=self.word_freq_path, custom_word_freq_dict=self.custom_word_freq,custom_confusion_dict=self.custom_confusion)t3 = time.time()logger.debug('Loaded dict file, spend: %.3f s.' % (t3 - t2))self.initialized_detector = Truedef check_detector_initialized(self):if not self.initialized_detector:self._initialize_detector()4.1.1.3 初始化調(diào)用
當(dāng)進(jìn)行模型或者自定義詞表等設(shè)置的開頭,都會(huì)調(diào)用check_detector_initialized進(jìn)行初始化設(shè)置的檢測,進(jìn)行配置文件的加載。
 
 在進(jìn)行n元文分?jǐn)?shù)計(jì)算和詞頻統(tǒng)計(jì)時(shí)候,也先檢測是否初始化。
 
另外在核心功能,錯(cuò)詞檢測的時(shí)候,均會(huì)進(jìn)行初始化:
 
4.1.1.4 錯(cuò)詞檢測
當(dāng)進(jìn)行錯(cuò)詞檢測的時(shí)候,先拿到實(shí)例化后的對象,然后調(diào)用detect類方法進(jìn)行檢測。
detect類方法中,入?yún)榇龣z測文本,然后將依次序進(jìn)行如下處理:
然后對于每個(gè)短句,調(diào)用detect_short方法進(jìn)行錯(cuò)詞檢測,然后會(huì)依次對短文本進(jìn)行錯(cuò)詞搜索。
搜索分3種,依次進(jìn)行。
混淆集匹配
直接在句子中遍歷是否在混淆集中有出現(xiàn),出現(xiàn)則直接添加到錯(cuò)誤列表中。嚴(yán)格的匹配邏輯,可以通過修改混淆集文件,進(jìn)行詞的添加刪除。
詞級(jí)別搜索
依次進(jìn)行切詞,然后遍歷每個(gè)詞,若詞不在詞典中,也認(rèn)為是錯(cuò)誤。這類詞包括一些實(shí)體,一些錯(cuò)詞,一些沒有在詞典中出現(xiàn)過,但是是正確的詞等。這條規(guī)則比較嚴(yán)格,錯(cuò)詞不放過,但是也錯(cuò)殺了一些其他正確詞。但是優(yōu)點(diǎn)同第一,可以靈活修改詞典。因此,這步需要一個(gè)好的預(yù)先構(gòu)造的詞典。
 
字級(jí)別搜索
與詞級(jí)別搜索不同,字級(jí)別不需要進(jìn)行切詞,依次進(jìn)行打分。分?jǐn)?shù)由一個(gè)基于人民日報(bào)語料預(yù)訓(xùn)練好的語言模型得出。
 具體計(jì)算步驟如下:
根據(jù)每個(gè)字的平均得分列表,找到可能的錯(cuò)誤字的位置(self._get_maybe_error_index);因此,這里要考慮找錯(cuò)的具體邏輯。代碼中的實(shí)現(xiàn)是基于類似平均絕對離差(MAD)的統(tǒng)計(jì)概念,這里也是一個(gè)策略上的改進(jìn)的方向,甚至多種策略的共同組合判斷。
 其中MAD的計(jì)算如下:
代碼中的實(shí)際計(jì)算不同與上述方式,以代碼實(shí)現(xiàn)為主。這里可以抽象出的一個(gè)問題是:輸入是一個(gè)得分列表,輸出是錯(cuò)誤位置。能否通過learning的方式獲得一個(gè)最優(yōu)策略。
最后的結(jié)果是上述三種情況的并集。
接下來就是候選集的構(gòu)造了(self.generate_items)。分情況討論如下:
第一種情況:confusion,如果是自定義混淆集中的錯(cuò)誤,直接修改為對應(yīng)的正確的值就可以了。
第二種情況:對于word和char兩種情況,沒有對應(yīng)的正確的值,就需要通過語言模型來從候選集中找了。
候選集的構(gòu)造邏輯如下,輸入是item,也就是檢錯(cuò)階段得到的可能的錯(cuò)誤詞。首先,同音字和形近字表共同可以構(gòu)建一個(gè)基于字的混淆集(confusion_char_set)。其次,借助于常用字表和item之間的編輯距離,可以構(gòu)建一個(gè)比較粗的候選詞集,通過常用詞表可以做一個(gè)過濾,最后加上同音的限制條件,可以得到一個(gè)更小的基于詞的混淆集(confusion_word_set)。最后,還有一個(gè)自定義的混淆集(confusion_custom _set)。
有了上述的表,就可以構(gòu)建候選集了。通過候選詞的長度來分情況討論:
第一:長度為1。直接利用基于字的混淆集來替換。
第二:長度為2。分別替換每一個(gè)字。
第三:長度為3。同上。
最后,合并所有的候選集。那么通過上述的構(gòu)造過程,可能導(dǎo)致一些無效詞或者字的出現(xiàn),因此要做一些過濾處理,最后按照選出一些候選集的子集來處理。代碼中的規(guī)則是基于詞頻來處理,選擇topk個(gè)詞作為候選集。
4.1.2 拼寫和筆畫糾正(Corrector)
源碼中通過corrector.py中定義的Corrector類來完成拼寫糾錯(cuò),通過初始化這個(gè)類,來獲得實(shí)例,進(jìn)行糾正。如圖所示,初始化的時(shí)候,我們可以設(shè)置很多配置文件的路徑,比如停用詞文件(stopwords_path)和自定義詞頻文件(custom_word_freq_path)等。
類初始化
同樣,Corrector類實(shí)例化的時(shí)候,也可以自定義詞頻文件,停用詞等基本信息,通過修改這些配置文件,來適應(yīng)自己的業(yè)務(wù)場景。
繼承關(guān)系
Corrector的集成關(guān)系如下圖,Corrector繼承了上文中介紹的Detector類,能夠完成一些基本的長句切分,錯(cuò)詞檢測功能。
示例代碼
通過源碼中給到的Demo,我們來了解下該功能的調(diào)用。通過獲取到Corrector示例,調(diào)用correct方法進(jìn)行檢測:
首先import的時(shí)候會(huì)首先調(diào)用__ini__.py,在這個(gè)文件中會(huì)對必要的工具類進(jìn)行初始化
然后將調(diào)用correct方法進(jìn)行句子改錯(cuò)
4.1.3 英文檢測
英文拼寫糾錯(cuò)被作者發(fā)在了另外一個(gè)類中EnSpell,通過加載一個(gè)很大的英文語料來進(jìn)行英文糾錯(cuò),具體細(xì)節(jié)將在下文闡述。
源碼示例
首先,我們通過源碼的例子來了解如何使用,如圖所示,當(dāng)我們實(shí)例化EnSpell之后,直接調(diào)用en_correct方法即可對傳入的單詞進(jìn)行糾錯(cuò)。
繼承關(guān)系
從繼承關(guān)系可以了解到,EnSpell并未和上文講到的兩個(gè)模塊有任何關(guān)系,是一個(gè)獨(dú)立的模塊。
 
代碼詳解
糾錯(cuò)主入口是correct方法,返回最大概率的候選.
問題來了:
- 候選值怎么確定?
 - 概率怎么確定?
 
候選值生成
首先,通過candidates方法獲得可能的正確地拼寫詞,獲取編輯距離1,2內(nèi)的候選值以及當(dāng)前值和子集。
編輯距離詞的構(gòu)建方法以及詞的過濾,可以參考作者的源碼實(shí)現(xiàn),這里將不再贅述。
 
然后依次遍歷單詞中的每個(gè)字母,篩選出存在于WORDS字典中的詞。
 
WORDS是我們開頭講到的那個(gè)超大英文語料的詞頻字典。
 
概率計(jì)算
概率計(jì)算通過調(diào)用probability方法進(jìn)行計(jì)算,
 
 計(jì)算方法是使用當(dāng)前詞的頻率/總得詞頻數(shù)量,十分簡單粗暴。
4.2 深度學(xué)習(xí)模型
4.2.1 Seq2Seq Attention模型
Seq2Seq基本結(jié)構(gòu)
Encoder代碼實(shí)現(xiàn)
Decoder代碼實(shí)現(xiàn)
使用BahdanauAttention
下圖是不同Attention的區(qū)別,還有很多種類的Attention,本文不再贅述
代碼實(shí)現(xiàn)
Seq2Seq Attention訓(xùn)練
訓(xùn)練很簡單,代碼作者都已經(jīng)寫好,只需要按上面步驟安裝好必要的環(huán)境,然后再seq2seq_attention路徑下,創(chuàng)建output文件夾,然后將訓(xùn)練集命名為train.txt 、測試命名為test.txt。
數(shù)據(jù)集路徑
數(shù)據(jù)集結(jié)構(gòu)
每一行包括一個(gè)樣本信息,包括兩部分信息,輸入數(shù)據(jù)X和標(biāo)簽Y,使用\t進(jìn)行分割,X和Y均為切詞后的句子,詞之間用空格分隔。
訓(xùn)練
數(shù)據(jù)整理好后,之間運(yùn)行train.py即可進(jìn)行訓(xùn)練
當(dāng)模型訓(xùn)練完之后,會(huì)保存模型,并且進(jìn)行預(yù)測。到這里,模型的訓(xùn)練就跑通了,若要使用該模型,只需要將測試數(shù)據(jù)處理成train.txt 和test.txt相同的格式,然后訓(xùn)練多輪,即可得到較好的結(jié)果。
4.2.2 transformer模型
同樣,transformer模型的使用也和上文Seq2Seq Attention類似。只是數(shù)據(jù)格式稍有變化。transformer本文將不再贅述,可以參考文后留下的參考資料。
數(shù)據(jù)集結(jié)構(gòu)
使用NLPCC2018比賽數(shù)據(jù)集進(jìn)行訓(xùn)練,該數(shù)據(jù)格式是原始文本,未做切詞處理。同樣在路徑下創(chuàng)建output文件夾,命名為data.train,在運(yùn)行train.py的時(shí)候,會(huì)自動(dòng)去切分輸入數(shù)據(jù)和標(biāo)簽數(shù)據(jù),并且構(gòu)建輸入輸出的vocab。
訓(xùn)練transformer
直接運(yùn)行train方法即可進(jìn)行訓(xùn)練,訓(xùn)練過程較長。
訓(xùn)練過程中,會(huì)打印loss和學(xué)習(xí)率的變化,學(xué)習(xí)率會(huì)先增加,后減小,是由于transformer使用了變化的學(xué)習(xí)率,具體可以參考官網(wǎng)的示例。
作者還提供幾個(gè)其他模型,使用方法類似,本文就不再贅述了。
參考
總結(jié)
以上是生活随笔為你收集整理的【深度学习】PyCorrector中文文本纠错实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: [css] 你有没有使用过“形似猫头鹰
 - 下一篇: [css] 使用css实现蒙版的效果