PyCorrector文字糾錯工具實踐和程式碼詳解
PyCorrector文字糾錯工具程式碼詳解
1。 簡介
中文文字糾錯工具。音似、形似錯字(或變體字)糾正,可用於中文拼音、筆畫輸入法的錯誤糾正。python3。6開發。
pycorrector
依據語言模型檢測錯別字位置,透過
拼音音似特徵
、筆畫五筆
編輯距離
特徵及語言模型
困惑度
特徵糾正錯別字。
1。1 線上Demo
https://www。
borntowin。cn/product/co
rrector
1。2 Question
中文文字糾錯任務,常見錯誤型別包括:
諧音字詞,如 配副眼睛-配副眼鏡
混淆音字詞,如 流浪織女-牛郎織女
字詞順序顛倒,如 伍迪艾倫-艾倫伍迪
字詞補全,如 愛有天意-假如愛有天意
形似字錯誤,如 高梁-高粱
中文拼音全拼,如 xingfu-幸福
中文拼音縮寫,如 sz-深圳
語法錯誤,如 想象難以-難以想象
當然,針對不同業務場景,這些問題並不一定全部存在,比如輸入法中需要處理前四種,搜尋引擎需要處理所有型別,語音識別後文本糾錯只需要處理前兩種, 其中‘形似字錯誤’主要針對五筆或者筆畫手寫輸入等。
1。3 Solution
1。3。1 規則的解決思路
中文糾錯分為兩步走,第一步是錯誤檢測,第二步是錯誤糾正;
錯誤檢測部分先透過結巴中文分詞器切詞,由於句子中含有錯別字,所以切詞結果往往會有切分錯誤的情況,這樣從字粒度和詞粒度兩方面檢測錯誤, 整合這兩種粒度的疑似錯誤結果,形成疑似錯誤位置候選集;
錯誤糾正部分,是遍歷所有的疑似錯誤位置,並使用音似、形似詞典替換錯誤位置的詞,然後透過語言模型計算句子困惑度,對所有候選集結果比較並排序,得到最優糾正詞。
1。3。2 深度模型的解決思路
端到端的深度模型可以避免人工提取特徵,減少人工工作量,RNN序列模型對文字任務擬合能力強,rnn_attention在英文文字糾錯比賽中取得第一名成績,證明應用效果不錯;
CRF會計算全域性最優輸出節點的條件機率,對句子中特定錯誤型別的檢測,會根據整句話判定該錯誤,阿里參賽2016中文語法糾錯任務並取得第一名,證明應用效果不錯;
seq2seq模型是使用encoder-decoder結構解決序列轉換問題,目前在序列轉換任務中(如機器翻譯、對話生成、文字摘要、影象描述)使用最廣泛、效果最好的模型之一。
1。4 Feature
1。4。1 模型
kenlm:kenlm統計語言模型工具
rnn_attention模型:參考Stanford University的nlc模型,該模型是參加2014英文文字糾錯比賽並取得第一名的方法
rnn_crf模型:參考阿里巴巴2016參賽中文語法糾錯比賽CGED2018並取得第一名的方法(整理中)
seq2seq_attention模型:在seq2seq模型加上attention機制,對於長文字效果更好,模型更容易收斂,但容易過擬合
transformer模型:全attention的結構代替了lstm用於解決sequence to sequence問題,語義特徵提取效果更好
bert模型:中文fine-tuned模型,使用MASK特徵糾正錯字
conv_seq2seq模型:基於Facebook出品的fairseq,北京語言大學團隊改進ConvS2S模型用於中文糾錯,在NLPCC-2018的中文語法糾錯比賽中,是唯一使用單模型並取得第三名的成績
1。4。2 錯誤檢測
字粒度:語言模型困惑度(ppl)檢測某字的似然機率值低於句子文字平均值,則判定該字是疑似錯別字的機率大。
詞粒度:切詞後不在詞典中的詞是疑似錯詞的機率大。
1。4。3 錯誤糾正
透過錯誤檢測定位所有疑似錯誤後,取所有疑似錯字的音似、形似候選詞,
使用候選詞替換,基於語言模型得到類似翻譯模型的候選排序結果,得到最優糾正詞。
1。4。4 思考
現在的處理手段,在詞粒度的錯誤召回還不錯,但錯誤糾正的準確率還有待提高,更多優質的糾錯集及糾錯詞庫會有提升,我更希望演算法上有更大的突破。
另外,現在的文字錯誤不再侷限於字詞粒度上的拼寫錯誤,需要提高中文語法錯誤檢測(CGED, Chinese Grammar Error Diagnosis)及糾正能力,列在TODO中,後續調研。
2。 安裝
作者開原始碼中介紹有兩種安裝方式: +
pip
安裝
pip install pycorrector
使用者:適合做工程實踐,不關心演算法細節,直接調包使用。
原始碼安裝
git clone https://github。com/shibing624/pycorrector。git
cd pycorrector
python setup。py install
使用者:希望瞭解程式碼實現,做一些深層次修改。
2。1 原始碼安裝步驟詳解
我們詳細講解第二種-原始碼安裝方式。除了完成上面原始碼安裝步驟外,我們還需要安裝一些必要的庫。
2。1。1 安裝必要的庫
pip install -r requirements。txt
2。1。2 安裝kenlm
kenlm
是一個統計語言模型的開源工具,如圖所示,程式碼
96%
都是
C++
實現的,所以效率極高,訓練速度快,在GitHub上現有
1。1K Star
kenlm開原始碼庫
pip
安裝kenlm
安裝命令如下
pip install kenlm
筆者嘗試直接
pip
安裝,報瞭如下錯誤,機器環境
(MAC OS 10。15。4)
。
kenlm安裝報錯
若報錯,則進行如下
原始碼安裝kenlm
,安裝成功則跳過該步驟。
原始碼安裝kenlm
下載kenlm程式碼
執行安裝命令
python setup。py install
如下圖所示,則已經安裝成功。
kenlm原始碼安裝成功
3。 環境測試
3。1 原始碼結構
程式碼結構如下(clone時間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
│ └── 基於深度學習的中文文字自動校對研究與實現。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。py
3。2 執行
examples
原始碼中
examples
資料夾中放置了大量的
demo
,我們可以首先我們先執行幾個
demo
測試下效果。
3。2。1 執行
base_demo。py
檔案
PyCorrector原始碼examples
程式碼很簡單,呼叫了
pycorrector。correct
方法進行糾錯,入參是待糾錯的語句,輸出是原句和出錯的詞以及糾正的詞,和具體位置。
import sys
sys。path。append(“。。”)
import pycorrector
pycorrector。set_log_level(‘INFO’)
if __name__ == ‘__main__’:
corrected_sent, detail = pycorrector。correct(‘少先隊員因該為老人讓坐’)
print(corrected_sent, detail)
第一次執行,講下載預設的語料,大概需要10分鐘
第一次執行,需要下載必要的語料資料
下載完之後,講可以進行語義糾錯,如下圖,
因該
->
應該
,座標在第
4
位到第
6
位,同理,另外一個
坐
->
座
,座標在第
10
位到第
11
位。
糾錯結果
接下來,讀一讀程式碼,理一理邏輯。
4。 程式碼詳解:
專案原始碼位於
pycorrector/pycorrector
資料夾中
。
├── bert
├── config。py
├── conv_seq2seq
├── corrector。py
├── data
├── deep_context
├── detector。py
├── en_spell。py
├── seq2seq_attention
├── transformer
├── utils
└── version。py
4。1 傳統規則模型
模型程式碼對應於如下三個檔案:
。
├── corrector。py
├── detector。py
└── en_spell。py
corrector。py
-拼寫糾錯
detector。py
-錯誤詞檢測器|
en_spell。py
-英語糾錯
4。1。1 錯誤詞檢測器(
Detector
)
4。1。1。1 程式碼結構介紹
detector。py
中包含一個
Detector
類,透過初始化這個類,來獲得例項,進行錯誤詞的檢測。如圖所示,初始化的時候,我們可以設定很多配置檔案的路徑,比如停用詞檔案(
stopwords_path
)和自定義詞頻檔案(
custom_word_freq_path
)等。
Detector類初始化方法
Detector
類的基礎關係如下圖所示:
Detector類繼承關係
其中
Object
是基類,提供常用的類方法:
Detector類方法
Detector
繼承了
Object
類,並且實現了錯詞檢測的基本方法,如下圖所示:
Detector實現的方法
4。1。1。2 錯詞檢測類例項化
當去例項化
Detector
類的時候,只會儲存一些基本諸如檔案路徑和配置引數的值,並不會立即載入模型進行初始化。
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’,
# 人民日報訓練語言模型 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_path
self。word_freq_path = word_freq_path
self。custom_word_freq_path = custom_word_freq_path
self。custom_confusion_path = custom_confusion_path
self。person_name_path = person_name_path
self。place_name_path = place_name_path
self。stopwords_path = stopwords_path
self。is_char_error_detect = True
self。is_word_error_detect = True
self。initialized_detector = False
self。lm = None
self。word_freq = None
self。custom_confusion = None
self。custom_word_freq = None
self。person_names = None
self。place_names = None
self。stopwords = None
self。tokenizer = None
4。1。1。2 錯詞檢測類初始化
初始化操作被放到
_initialize_detector
函式中,透過
check_detector_initialized
來判斷是否需要進行初始化。
def _initialize_detector(self):
t1 = time。time()
try:
import kenlm
except 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))
# 詞、頻數dict
self。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 = True
def check_detector_initialized(self):
if not self。initialized_detector:
self。_initialize_detector()
4。1。1。3 初始化呼叫
當進行模型或者自定義詞表等設定的開頭,都會呼叫
check_detector_initialized
進行初始化設定的檢測,進行配置檔案的載入。
check_detector_initialized初始化呼叫
在進行n元文分數計算和詞頻統計時候,也先檢測是否初始化。
check_detector_initialized初始化呼叫
另外在核心功能,錯詞檢測的時候,均會進行初始化:
糾錯前 先進行check_detector_initialized初始化檢測
4。1。1。4 錯詞檢測
當進行錯詞檢測的時候,先拿到例項化後的物件,然後呼叫
detect
類方法進行檢測。
文字檢測Demo
detect
類方法中,入參為待檢測文字,然後將依次序進行如下處理: 1。 空字元判斷 2。 初始化 3。 編碼統一 4。 文字歸一化 5。 長句切分為短句
檢測模組的具體實現
然後對於每個短句,呼叫
detect_short
方法進行錯詞檢測,然後會依次對短文字進行錯詞搜尋。
搜尋分3種,依次進行。 1。 混淆集匹配 2。 詞級別搜尋 3。 字級別搜尋
混淆集匹配
直接在句子中遍歷是否在混淆集中有出現,出現則直接新增到錯誤列表中。嚴格的匹配邏輯,可以透過修改混淆集檔案,進行詞的新增刪除。
詞級別搜尋
依次進行
切詞
,然後遍歷每個詞,若詞不在詞典中,也認為是錯誤。這類詞包括一些實體,一些錯詞,一些沒有在詞典中出現過,但是是正確的詞等。這條規則比較嚴格,錯詞不放過,但是也錯殺了一些其他正確詞。但是優點同第一,可以靈活修改詞典。因此,這步需要一個好的預先構造的詞典。
詞級別的搜尋
字級別搜尋
與詞級別搜尋不同,字級別不需要進行切詞,依次進行打分。分數由一個基於人民日報語料預訓練好的語言模型得出。 具體計算步驟如下: 1。 計算基於字的2-gram和3-gram的得分列表,二者取平均得到sent的每個字的分數。 2。 根據每個字的平均得分列表,找到可能的錯誤字的位置。
根據每個字的平均得分列表,找到可能的錯誤字的位置(
self。_get_maybe_error_index
);因此,這裡要考慮找錯的具體邏輯。程式碼中的實現是基於類似平均絕對離差(
MAD
)的統計概念,這裡也是一個策略上的改進的方向,甚至多種策略的共同組合判斷。
字級別的搜尋
其中MAD的計算如下:
程式碼中的實際計算不同與上述方式,以程式碼實現為主。這裡可以抽象出的一個問題是:輸入是一個得分列表,輸出是錯誤位置。能否透過learning的方式獲得一個最優策略。
最後的結果是上述三種情況的並集。
接下來就是候選集的構造了(
self。generate_items
)。分情況討論如下:
第一種情況:
confusion
,如果是自定義混淆集中的錯誤,直接修改為對應的正確的值就可以了。
第二種情況:對於
word
和
char
兩種情況,沒有對應的正確的值,就需要透過語言模型來從候選集中找了。
候選集的構造邏輯如下,輸入是
item
,也就是檢錯階段得到的可能的錯誤詞。首先,同音字和形近字表共同可以構建一個基於字的混淆集(
confusion_char_set
)。其次,藉助於常用字表和
item
之間的編輯距離,可以構建一個比較粗的候選詞集,透過常用詞表可以做一個過濾,最後加上同音的限制條件,可以得到一個更小的基於詞的混淆集(
confusion_word_set
)。最後,還有一個自定義的混淆集(
confusion_custom _set
)。
有了上述的表,就可以構建候選集了。透過候選詞的長度來分情況討論:
第一:長度為1。直接利用基於字的混淆集來替換。
第二:長度為2。分別替換每一個字。
第三:長度為3。同上。
最後,合併所有的候選集。那麼透過上述的構造過程,可能導致一些無效詞或者字的出現,因此要做一些過濾處理,最後按照選出一些候選集的子集來處理。程式碼中的規則是基於詞頻來處理,選擇topk個詞作為候選集。
4。1。2 拼寫和筆畫糾正(
Corrector
)
原始碼中透過
corrector。py
中定義的
Corrector
類來完成拼寫糾錯,透過初始化這個類,來獲得例項,進行糾正。如圖所示,初始化的時候,我們可以設定很多配置檔案的路徑,比如停用詞檔案(
stopwords_path
)和自定義詞頻檔案(
custom_word_freq_path
)等。
類初始化
同樣,
Corrector
類例項化的時候,也可以自定義詞頻檔案,停用詞等基本資訊,透過修改這些配置檔案,來適應自己的業務場景。
Corrector類的初始化方法
繼承關係
Corrector
的整合關係如下圖,
Corrector
繼承了上文中介紹的
Detector
類,能夠完成一些基本的長句切分,錯詞檢測功能。
Corrector類的繼承關係
示例程式碼
透過原始碼中給到的
Demo
,我們來了解下該功能的呼叫。透過獲取到
Corrector
示例,呼叫
correct
方法進行檢測:
corrector糾錯Demo
首先
import
的時候會首先呼叫
__ini__。py
,在這個檔案中會對必要的工具類進行初始化
導包前在__ini__檔案中進行初始化
然後將呼叫
correct
方法進行句子改錯
corrector糾錯的具體實現
4。1。3 英文檢測
英文拼寫糾錯被作者發在了另外一個類中
EnSpell
,透過載入一個很大的英文語料來進行英文糾錯,具體細節將在下文闡述。
英文拼寫糾錯語料
原始碼示例
首先,我們透過原始碼的例子來了解如何使用,如圖所示,當我們例項化
EnSpell
之後,直接呼叫
en_correct
方法即可對傳入的單詞進行糾錯。
英文拼寫糾錯Demo
繼承關係
從繼承關係可以瞭解到,
EnSpell
並未和上文講到的兩個模組有任何關係,是一個獨立的模組。
EnSpell類繼承關係
程式碼詳解
糾錯主入口是
correct
方法,返回最大機率的候選。
問題來了: + 候選值怎麼確定? + 機率怎麼確定?
拼寫糾錯主方法
候選值生成
首先,透過
candidates
方法獲得可能的正確地拼寫詞,獲取編輯
距離1
,
2
內的候選值以及
當前值
和
子集
。
候選值生成方法
編輯距離詞的構建方法以及詞的過濾,可以參考作者的原始碼實現,這裡將不再贅述。
編輯距離詞生成方法
然後依次遍歷單詞中的每個字母,篩選出存在於
WORDS
字典中的詞。
子集生成
WORDS
是我們開頭講到的那個超大英文語料的詞頻字典。
語料詞頻字典
機率計算
機率計算透過呼叫
probability
方法進行計算,
機率計算方法
計算方法是使用當前
詞的頻率
/
總得詞頻數量
,十分簡單粗暴。
4。2 深度學習模型
4。2。1 Seq2Seq Attention模型
Seq2Seq基本結構
Seq2Seq示例
Encoder程式碼實現
Encoder程式碼
Decoder程式碼實現
Decoder程式碼
使用
BahdanauAttention
下圖是不同
Attention
的區別,還有很多種類的
Attention
,本文不再贅述
BahdanauAttention和LuongAttention
程式碼實現
BahdanauAttention實現
Seq2Seq Attention
訓練
訓練很簡單,程式碼作者都已經寫好,只需要按上面步驟安裝好必要的環境,然後再
seq2seq_attention
路徑下,建立
output資料夾
,然後將訓練集命名為
train。txt
、測試命名為
test。txt
。
資料集路徑
Seq2Seq Attention訓練資料集
資料集結構
每一行包括一個樣本資訊,包括兩部分資訊,輸入資料
X
和標籤
Y
,使用
\t
進行分割,
X
和
Y
均為切詞後的句子,詞之間用空格分隔。
資料集分隔符
訓練
資料整理好後,之間執行
train。py
即可進行訓練
訓練過程
當模型訓練完之後,會儲存模型,並且進行預測。到這裡,模型的訓練就跑通了,若要使用該模型,只需要將測試資料處理成
train。txt
和
test。txt
相同的格式,然後訓練多輪,即可得到較好的結果。
結果輸出
4。2。2
transformer
模型
transformer結構圖
同樣,
transformer
模型的使用也和上文
Seq2Seq Attention
類似。只是資料格式稍有變化。
transformer
本文將不再贅述,可以參考文後留下的參考資料。
資料集結構
使用NLPCC2018比賽資料集進行訓練,該資料格式是原始文字,未做切詞處理。同樣在路徑下建立
output
資料夾,命名為
data。train
,在執行
train。py
的時候,會自動去切分輸入資料和標籤資料,並且構建輸入輸出的
vocab
。
transformer訓練資料結構
訓練
transformer
直接執行
train
方法即可進行訓練,訓練過程較長。
transformer訓練主入口
訓練過程中,會列印
loss
和學習率的變化,學習率會先增加,後減小,是由於
transformer
使用了變化的學習率,具體可以參考官網的示例。
transformer訓練過程
作者還提供幾個其他模型,使用方法類似,本文就不再贅述了。
歡迎各位打賞點贊訂閱
參考