1. 程式人生 > >怎樣寫一個拼寫檢查器-貝葉斯-python

怎樣寫一個拼寫檢查器-貝葉斯-python

怎樣寫一個拼寫檢查器 

Peter Norvig
翻譯: Eric You XU
原版: http://norvig.com/spell-correct.html 翻譯: http://blog.youxu.info/spell-correct.html


上個星期, 我的兩個朋友 Dean 和 Bill 分別告訴我說他們對 Google 的快速高質量的拼寫檢查工具感到驚奇. 比如說在搜尋的時候鍵入 [speling], 在不到 0.1 秒的時間內, Google 會返回: 你要找的是不是 [spelling]. (Yahoo! 和 微軟也有類似的功能). 讓我感到有點奇怪的是我原想 Dean 和 Bill 這兩個很牛的工程師和數學家應該對於使用統計語言模型構建拼寫檢查器有職業的敏感.
但是他們似乎沒有這個想法. 我後來想了想, 他們的確沒什麼理由很熟悉統計語言模型. 不是他們的知識有問題, 而是我預想的本來就是不對的.



我覺得, 如果對這方面的工作做個解釋, 他們和其他人肯定會受益. 然而像Google 的那樣工業強度的拼寫檢查器的全部細節只會讓人感到迷惑而不是受到啟迪. 前幾天我乘飛機回家的時候, 順便寫了幾十行程式, 作為一個玩具性質的拼寫檢查器. 這個拼寫檢查器大約1秒能處理10多個單詞, 並且達到 80% -90% 的準確率. 下面就是我的程式碼, 用Python 2.5 寫成, 一共21 行, 是一個功能完備的拼寫檢查器.

import re, collections

def words(text): return re.findall('[a-z]+', text.lower()) 

def
train(features):
model = collections.defaultdict(lambda: 1) for f in features: model[f] += 1 return model NWORDS = train(words(file('big.txt').read())) alphabet = 'abcdefghijklmnopqrstuvwxyz' def edits1(word): n = len(word) return set([word[0:i]+word[i+1:] for i in range(n)] + # deletion
[word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet]) # insertion def known_edits2(word): return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS) def known(words): return set(w for w in words if w in NWORDS) def correct(word): # 這裡判斷並不是用p(c) * p(w/c) 而是先看了p(w/c)找到距離這個單詞最近的正確單詞 -可能是本身在字典中,可能是距離為1,可能距離為二可能不在字典中 # konwn 就是距離為0在字典中的,即如果是正確的並且是距離w為0,known(edits1)就是正確的一個單詞且距離w為1的單詞 candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word] ## 找到候選的可能單詞即p(c/w)之後,看那個在字典中頻率高就選擇那個 return max(candidates, key=lambda w: NWORDS[w])

這段程式碼定義了一個函式叫 correct, 它以一個單詞作為輸入引數, 返回最可能的拼寫建議結果. 比如說:

>>> correct('speling')
'spelling'
>>> correct('korrecter')
'corrector'

拼寫檢查器的原理, 一些簡單的概率知識

我簡單的介紹一下它的工作原理. 給定一個單詞, 我們的任務是選擇和它最相似的拼寫正確的單詞. (如果這個單詞本身拼寫就是正確的, 那麼最相近的就是它自己啦). 當然, 不可能絕對的找到相近的單詞, 比如說給定 lates 這個單詞, 它應該別更正為 late 呢 還是 latest 呢? 這些困難指示我們, 需要使用概率論, 而不是基於規則的判斷. 我們說, 給定一個詞 w, 在所有正確的拼寫詞中, 我們想要找一個正確的詞 c, 使得對於 w 的條件概率最大, 也就是說:

argmax c P( c| w)
按照  貝葉斯理論  上面的式子等價於:
argmax c P( w| c) P( c) / P( w)
因為使用者可以輸錯任何詞, 因此對於任何 c 來講, 出現 w 的概率 P(w) 都是一樣的, 從而我們在上式中忽略它, 寫成:
argmax c P( w| c) P( c)
這個式子有三個部分, 從右到左, 分別是:

1. P(c), 文章中出現一個正確拼寫詞 c 的概率, 也就是說, 在英語文章中, c 出現的概率有多大呢? 因為這個概率完全由英語這種語言決定, 我們稱之為做 語言模型. 好比說, 英語中出現 the 的概率  P(‘the’) 就相對高, 而出現  P(‘zxzxzxzyy’) 的概率接近0(假設後者也是一個詞的話).

2. P(w|c), 在使用者想鍵入 c 的情況下敲成 w 的概率. 因為這個是代表使用者會以多大的概率把 c 敲錯成 w, 因此這個被稱為 誤 差模型.

3. argmax c, 用來列舉所有可能的 c 並且選取概率最大的, 因為我們有理由相信, 一個(正確的)單詞出現的頻率高, 使用者又容易把它敲成另一個錯誤的單詞, 那麼, 那個敲錯的單詞應該被更正為這個正確的.
有人肯定要問, 你笨啊, 為什麼把最簡單的一個 P( c | w ) 變成兩項複雜的式子來計算? 答案是本質上 P(c|w) 就是和這兩項同時相關的, 因此拆成兩項反而容易處理. 舉個例子, 比如一個單詞 thew 拼錯了. 看上去 thaw 應該是正確的, 因為就是把 a 打成 e 了. 然而, 也有可能使用者想要的是 the, 因為 the 是英語中常見的一個詞, 並且很有可能打字時候手不小心從 e 滑到 w 了. 因此, 在這種情況下, 我們想要計算  P( c | w ), 就必須同時考慮 c 出現的概率和從 c 到 w 的概率. 把一項拆成兩項反而讓這個問題更加容易更加清晰.

現在, 讓我們看看程式究竟是怎麼一回事. 首先是計算 P(c), 我們可以讀入一個巨大的文字檔案,  big.txt , 這個裡面大約有幾百萬個詞(相當於是語料庫了). 這個檔案是由 Gutenberg 計劃  中可以獲取的一些書,  Wiktionary  和  British National Corpus  語料庫構成. (當時在飛機上我只有福爾摩斯全集, 我後來又加入了一些, 直到效果不再顯著提高為止).

然後, 我們利用一個叫 words 的函式把語料中的單詞全部抽取出來, 轉成小寫, 並且去除單詞中間的特殊符號. 這樣, 單詞就會成為字母序列, don’t 就變成 don 和 t 了. 1  接著我們訓練一個概率模型, 別被這個術語嚇倒, 實際上就是數一數每個單詞出現幾次. 在 train 函式中, 我們就做這個事情.
def words(text): return re.findall('[a-z]+', text.lower()) 

def train(features):
    model = collections.defaultdict(lambda: 1)
    for f in features:
        model[f] += 1
    return model

NWORDS = train(words(file('big.txt').read()))

實際上, NWORDS[w] 儲存了單詞 w 在語料中出現了多少次. 不過一個問題是要是遇到我們從來沒有過見過的新詞怎麼辦. 假如說一個詞拼寫完全正確, 但是語料庫中沒有包含這個詞, 從而這個詞也永遠不會出現在訓練集中. 於是, 我們就要返回出現這個詞的概率是0. 這個情況不太妙, 因為概率為0這個代表了這個事件絕對不可能發生, 而在我們的概率模型中, 我們期望用一個很小的概率來代表這種情況. 實際上處理這個問題有很多成型的標準方法, 我們選取一個最簡單的方法: 從來沒有過見過的新詞一律假設出現過一次. 這個過程一般成為”平滑化”, 因為我們把概率分佈為0的設定為一個小的概率值. 在語言實現上, 我們可以使用Python collention 包中的 defaultdict 類, 這個類和 python 標準的 dict (其他語言中可能稱之為 hash 表) 一樣, 唯一的不同就是可以給任意的鍵設定一個預設值, 在我們的例子中, 我們使用一個匿名的 lambda:1 函式, 設定預設值為 1.


然後的問題是: 給定一個單詞 w, 怎麼能夠列舉所有可能的正確的拼寫呢? 實際上前人已經研究得很充分了, 這個就是一個 編輯距離 的概 念. 這兩個詞之間的編輯距離
定義為使用了幾次插入(在詞中插入一個單字母), 刪除(刪除一個單字母), 交換(交換相鄰兩個字母), 替換(把一個字母換成另一個)的操作從一個詞變到另一個詞.
下面這個函式可以返回所有與單詞 w 編輯距離為 1 的集合.
def edits1(word):
    n = len(word)
    return set([word[0:i]+word[i+1:] for i in range(n)] +                     # deletion
               [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] + # transposition
               [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] + # alteration
               [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet])  # insertion

顯然, 這個集合很大. 對於一個長度為 n 的單詞, 可能有n種刪除, n-1中對換, 26n 種 (譯註: 實際上是 25n 種)替換 和 26(n+1) 種插入 (譯註: 實際上比這個小, 因為在一個字母前後再插入這個字母構成的詞是等價的). 這樣的話, 一共就是 54n + 25 中情況 (當中還有一點重複). 比如說, 和 something 這個單詞的編輯距離為1 的詞按照這個算來是 511 個, 而實際上是 494 個.

一般講拼寫檢查的文獻宣稱大約80-95%的拼寫錯誤都是介於編譯距離 1 以內. 然而下面我們看到, 當我對於一個有270個拼寫錯誤的語料做實驗的時候, 我發現只有76%的拼寫錯誤是屬於編輯距離為1的集合. 或許是我選取的例子比典型的例子難處理一點吧. 不管怎樣, 我覺得這個結果不夠好, 因此我開始考慮編輯距離為 2 的那些單詞了. 這個事情很簡單, 遞迴的來看, 就是把 edit1 函式再作用在 edit1 函式的返回集合的每一個元素上就行了. 因此, 我們定義函式 edit2:

def edits2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1))
這個語句寫起來很簡單, 實際上背後是很龐大的計算量: 與 something 編輯距離為2的單詞居然達到了 114,324 個. 不過編輯距離放寬到2以後, 我們基本上就能覆蓋所有的情況了, 在270個樣例中, 只有3個的編輯距離大於2. 當然我們可以做一些小小的優化: 在這些編輯距離小於2的詞中間, 只把那些正確的詞作為候選詞. 我們仍然考慮所有的可能性, 但是不需要構建一個很大的集合, 因此, 我們構建一個函式叫做  known_edits2 , 這個函式只返回那些正確的並且與 w 編輯距離小於2 的詞的集合:
def known_edits2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)

現在, 在剛才的 something 例子中, known_edits2(‘something’) 只能返回 3 個單詞: ‘smoothing’, ‘something’ 和 ‘soothing’, 而實際上所有編輯距離為 1 或者  2 的詞一共有 114,324 個. 這個優化大約把速度提高了 10%.

最後剩下的就是誤差模型部分 P(w|c) 了. 這個也是當時難住我的部分. 當時我在飛機上, 沒有網路, 也就沒有資料用來構建一個拼寫錯誤模型. 不過我有一些常識性的知識: 把一個母音拼成另一個的概率要大於子音 (因為人常常把 hello 打成 hallo 這樣); 把單詞的第一個字母拼錯的概率會相對小, 等等. 但是我並沒有具體的數字去支撐這些證據. 因此, 我選擇了一個簡單的方法: 編輯距離為1的正確單詞比編輯距離為2的優先順序高, 而編輯距離為0的正確單詞優先順序比編輯距離為1的高. 因此, 用程式碼寫出來就是:

(譯註: 此處作者使用了Python語言的一個巧妙性質: 短路表示式. 在下面的程式碼中, 如果known(set)非空, candidate 就會選取這個集合, 而不繼續計算後面的; 因此, 通過Python語言的短路表示式, 作者很簡單的實現了優先順序)

def known(words): return set(w for w in words if w in NWORDS)

def correct(word):
    candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]
    return max(candidates, key=lambda w: NWORDS[w])

correct 函式從一個候選集合中選取最大概率的. 實際上, 就是選取有最大 P(c) 值的那個. 所有的 P(c) 值都儲存在 NWORDS 結構中.


效果

現在我們看看演算法效果怎麼樣. 在飛機上我嘗試了好幾個例子, 效果還行. 飛機著陸後, 我從牛津文字檔案庫 (Oxford Text Archive)下載了 Roger Mitton 的  Birkbeck 拼寫錯誤語料庫 . 從這個庫中, 我取出了兩個集合, 作為我要做拼寫檢查的目標. 第一個集合用來作為在開發中作為參考, 第二個作為最後的結果測試. 也就是說, 我程式完成之前不參考它, 而把程式在其上的測試結果作為最後的效果. 用兩個集合一個訓練一個對照是一種良好的實踐, 至少這樣可以避免我通過對特定資料集合進行特殊調整從而自欺欺人. 這裡我給出了一個測試的例子和一個執行測試的例子. 實際的完整測試例子和程式可以參見  spell.py .
tests1 = { 'access': 'acess', 'accessing': 'accesing', 'accommodation':
    'accomodation acommodation acomodation', 'account': 'acount', ...}

tests2 = {'forbidden': 'forbiden', 'decisions': 'deciscions descisions',
    'supposedly': 'supposidly', 'embellishing': 'embelishing', ...}

def spelltest(tests, bias=None, verbose=False):
    import time
    n, bad, unknown, start = 0, 0, 0, time.clock()
    if bias:
        for target in tests: NWORDS[target] += bias
    for target,wrongs in tests.items():
        for wrong in wrongs.split():
            n += 1
            w = correct(wrong)
            if w!=target:
                bad += 1
                unknown += (target not in NWORDS)
                if verbose:
                    print '%r => %r (%d); expected %r (%d)' % (
                        wrong, w, NWORDS[w], target, NWORDS[target])
    return dict(bad=bad, n=n, bias=bias, pct=int(100. - 100.*bad/n), 
                unknown=unknown, secs=int(time.clock()-start) )

print spelltest(tests1)
print spelltest(tests2) ## only do this after everything is debugged

這個程式給出了下面的輸出:

{'bad': 68, 'bias': None, 'unknown': 15, 'secs': 16, 'pct': 74, 'n': 270}
{'bad': 130, 'bias': None, 'unknown': 43, 'secs': 26, 'pct': 67, 'n': 400}

在270個測試樣本上 270 , 我大約能在13秒內得到 74% 的正確率 (每秒17個正確詞), 在測試集上, 我得到 67% 正確率 (每秒 15 個).

更新:  在這篇文章的原來版本中, 我把結果錯誤的報告高了. 原因是程式中一個小bug. 雖然這個 bug 很不起眼, 但我實際上應該能夠避免. 我為對閱讀我老版本的這篇文章的讀者造成感到抱歉. 在 spelltest 源程式的第四行, 我忽略了if bias:  並且把 bias 預設值賦值為0. 我原來想: 如果 bias 是0 , NWORDS[target] += bias這個語句就不起作用. 而實際上, 雖然這個語句沒有改變 NWORDS[target] 的值, 這個卻讓 (target in NWORDS) 為真. 這樣的話, spelltest 就會把訓練集合中那些不認識的正確拼寫的單詞都當成認識來處理了, 程式就會”作弊”. 我很喜歡 defaultdict 的簡潔, 所以在程式中使用了它, 如果使用 dicts 就不會有這個問題了. 2

結論: 我達到了簡潔, 快速開發和執行速度這三個目標, 不過準確率不算太好.


將來工作

怎樣才能做到更好結果呢? 讓我們回過頭來看看概率模型中的三個因素:  (1) P( c ); (2) P( w | c ); and (3) argmax c . 我們通過程式給出錯誤答案的那些例子入手, 看看這三個因素外, 我們還忽略了什麼.

  1. P(c), 語言模型. 在語言模型中, 有兩種問題會造成最後的錯誤識別. 其中最嚴重的一個因素就是 未知單詞. 在訓練集合中, 一共有15個未知單詞, 它們大約佔了5%; 在測試集合中, 有43個未知詞, 它們佔了11%. 當把 spelltest 的呼叫引數 verbose 設定為 True 的時候: 我們可以看到下面的輸出:
    correct('economtric') => 'economic' (121); expected 'econometric' (1)
    correct('embaras') => 'embargo' (8); expected 'embarrass' (1)
    correct('colate') => 'coat' (173); expected 'collate' (1)
    correct('orentated') => 'orentated' (1); expected 'orientated' (1)
    correct('unequivocaly') => 'unequivocal' (2); expected 'unequivocally' (1)
    correct('generataed') => 'generate' (2); expected 'generated' (1)
    correct('guidlines') => 'guideline' (2); expected 'guidelines' (1)

    在這個結果中, 我們可以使用看到 correct 函式作用在那些拼錯的單詞上的結果. (其中 NWORDS 中單詞出現次數在括號中),  然後是我們期望的輸出以及出現的次數. 這個結果告訴我們, 如果程式根本就不知道 ‘econometric’ 是一個單詞, 它也就不可能去把 ‘economtric’ 糾正成 ‘econometric’. 這個問題可以通過往訓練集合中加入更多語料來解決, 不過也有可能引入更多錯誤. 同時注意到最後四行, 實際上我們的訓練集中有正確的單詞, 只是形式略有不同. 因此, 我們可以改進一下程式, 比如在動詞後面加 ‘-ed’ 或者在名詞後面加 ‘-s’ 也是合法的.

    第二個可能導致錯誤的因素是概率: 兩個詞都出現在我們的字典裡面了, 但是恰恰我們選的概率大的那個不是使用者想要的. 不過我要說的是這個問題其實不是最嚴重的, 也不是獨立發生的, 其他原因可能更加嚴重. 

    我們可以模擬一下看看如果我們提高語言模型, 最後結果能好多少. 比如說, 我們在訓練集上小”作弊”一下. 我們在 spelltest 函式中有一個引數叫做 bias, 實際上就是代表把正確的拼寫詞多新增幾次, 以便提高語言模型中相應的概率. 比如說, 在語料中, 假設正確的詞出現的頻率多了1次, 或者10次, 或者更多. 如果我們增加 bias 這個引數的值, 可以看到訓練集和測試集上的準確率都顯著提高了.

    Bias 訓練集. 測試集
    0 74% 67%
    1 74% 70%
    10 76% 73%
    100 82% 77%
    1000 89% 80%


    在兩個集合上我們都能做到大約 80-90%. 這個顯示出如果我們有一個好的語言模型, 我們或能達到準確率這個目標. 不過, 這個顯得過於樂觀了, 因為構建一個更大的語言模型會引入新的詞, 從而可能還會引入一些錯誤結果, 儘管這個地方我們沒觀察到這個現象.

    處理未知詞還有另外一種辦法, 比如說, 假如遇到這個詞: “electroencephalographicallz”, 比較好的糾正的方法是把最後的 “z” 變成 “y”, 因為 ‘-cally’ 是英文中很常見的一個字尾. 雖然 “electroencephalographically” 這個詞也不在我們的字典中, 我們也能通過基於音節或者字首字尾等性質給出拼寫建議. 當然, 這種簡單前後綴判斷的方法比基於構詞法的要簡單的多.

  2. P(w|c) 是誤差模型. 到目前為止, 我們都是用的一個很簡陋的模型: 距離越短, 概率越大. 這個也造成了一些問題, 比如下面的例子中, correct 函式返回了編輯距離為 1 的詞作為答案, 而正確答案恰恰編輯距離是 2: 
    correct('reciet') => 'recite' (5); expected 'receipt' (14)
    correct('adres') => 'acres' (37); expected 'address' (77)
    correct('rember') => 'member' (51); expected 'remember' (162)
    correct('juse') => 'just' (768); expected 'juice' (6)
    correct('accesing') => 'acceding' (2); expected 'assessing' (1)

    舉個例子, 程式認為在 ‘adres’ 中把 ‘d’ 變成 ‘c’ 從而得到 ‘acres’ 的優先順序比把 d 寫成 dd 以及 s 寫成 ss 的優先順序高, 從而作出了錯誤的判斷. 還有些時候程式在兩個編輯距離一樣的候選詞中選擇了錯誤的一個, 比如:

    correct('thay') => 'that' (12513); expected 'they' (4939)
    correct('cleark') => 'clear' (234); expected 'clerk' (26)
    correct('wer') => 'her' (5285); expected 'were' (4290)
    correct('bonas') => 'bones' (263); expected 'bonus' (3)
    correct('plesent') => 'present' (330); expected 'pleasant' (97)

    這個例子給我們一個同樣的教訓: 在 ‘thay’ 中, 把 ‘a’ 變成 ‘e’ 的概率比把 ‘y’ 拼成 ‘t’ 大. 為了正確的選擇 ‘they’, 我們至少要在先驗概率上乘以 2.5, 才能使得最後 they 的機率超過 that, 從而選擇 they. 

    顯然, 我們可以用一個更好的模型來衡量拼錯單詞的概率. 比如說, 把一個字母順手打成兩個, 或者把一個母音打成另一個的情況都應該比其他打字錯誤更加容易發生. 當然, 更好的辦法還是從資料入手: 比如說, 找一個拼寫錯誤語料, 然後統計插入; 刪除; 交換和變換在給定周圍字母情況下的概率. 為了採集到這些概率, 可能我們需要非常大的資料集. 比如說, 如果我們帶著觀察左右兩個字母作為上下文, 看看一個字母替換成另一個的概率, 就一共有 266 種情況, 也就是大約超過 3 億個情況. 然後每種情況需要平均幾個證據作為支撐, 因此我們知道10億個字母的訓練集. 如果為了保證更好的質量, 可能至少100億個才差不多.

    需要注意的是, 語言模型和誤差模型之間是有聯絡的. 我們的程式中假設了編輯距離為 1 的優先於編輯距離為 2 的. 這種誤差模型或多或少也使得語言模型的優點難以發揮. 我們之所以沒有往語言模型中加入很多不常用的單詞, 是因為我們擔心新增這些單詞後, 他們恰好和我們要更正的詞編輯距離是1, 從而那些出現頻率更高但是編輯距離為 2 的單詞就不可能被選中了. 如果有一個更加好的誤差模型, 或許我們就能夠放心大膽的新增更多的不常用單詞了. 下面就是一個因為新增不常用單詞影響結果的例子:

    correct('wonted') => 'wonted' (2); expected 'wanted' (214)
    correct('planed') => 'planed' (2); expected 'planned' (16)
    correct('forth') => 'forth' (83); expected 'fourth' (79)
    correct('et') => 'et' (20); expected 'set' (325)
  3. 列舉所有可能的概率並且選擇最大的: argmaxc. 我們的程式枚舉了直到編輯距離為2的所有單詞. 在測試集合中, 270個單詞中, 只有3個編輯距離大於2, 但是在測試集合中, 400箇中卻有23個. 他們是:
    purple perpul
    curtains courtens
    minutes muinets
    
    successful sucssuful
    hierarchy heiarky
    profession preffeson
    weighted wagted
    inefficient ineffiect
    availability avaiblity
    thermawear thermawhere
    nature natior
    dissension desention
    unnecessarily unessasarily
    disappointing dissapoiting
    acquaintances aquantences
    thoughts thorts
    criticism citisum
    immediately imidatly
    necessary necasery
    necessary nessasary
    necessary nessisary
    unnecessary unessessay
    night nite
    minutes muiuets
    assessing accesing
    necessitates nessisitates
    

    我們可以考慮有限的允許一些編輯距離為3的情況. 比如說, 我們可以只允許在母音旁邊插入一個母音, 或者把母音替換, 或者把 c 寫成 s 等等. 這些基本上就覆蓋了上面所有的情況了. 

  4. 第四種, 也是最好的一種改進方法是改進 correct  函式的介面, 讓他可以分析上下文給出決斷. 因為很多情況下, 僅僅根據單詞本身做決斷很難, 這個單詞本身就在字典中, 但是在上下文中, 應該被更正為另一個單詞. 比如說: 
    correct('where') => 'where' (123); expected 'were' (452)
    correct('latter') => 'latter' (11); expected 'later' (116)
    correct('advice') => 'advice' (64); expected 'advise' (20)

    如果單看 ‘where’ 這個單詞本身, 我們無從知曉說什麼情況下該把 correct(‘where’) 返回 ‘were’ , 又在什麼情況下返回 ‘where’. 但是如果我們給 correct 函式的是:’They where going’, 這時候 “where” 就應該被更正為 “were”. 

    上下文可以幫助程式從多個候選答案中選出最好的, 比如說: 

    correct('hown') => 'how' (1316); expected 'shown' (114)
    correct('ther') => 'the' (81031); expected 'their' (3956)
    correct('quies') => 'quiet' (119); expected 'queries' (1)
    correct('natior') => 'nation' (170); expected 'nature' (171)
    correct('thear') => 'their' (3956); expected 'there' (4973)
    correct('carrers') => 'carriers' (7); expected 'careers' (2)

    為什麼 ‘thear’ 要被更正為 ‘there’ 而不是 ‘their’ 呢?  只看單詞本身, 這個問題不好回答, 不過一旦放句子 ‘There’s no there thear’ 中, 答案就立即清楚明瞭了. 

    要構建一個同時能處理多個詞(詞以及上下文)的系統, 我們需要大量的資料. 所幸的是 Google 已經公開發布了最長 5個單詞的所有序列數 據庫, 這個是從上千億個詞的語料資料中收集得到的. 我相信一個能達到 90% 準確率的拼寫檢查器已經需要考慮上下文以做決定了. 不過, 這個, 咱們改天討論 :)


  5. 我們可以通過優化訓練資料和測試資料來提高準確率. 我們抓取了大約100萬個單詞並且假設這些詞都是拼寫正確的. 但是這個事情並不這麼完美, 這些資料集也可能有錯. 我們可以嘗試這找出這些錯並且修正他們. 這個地方, 修正測試集合並不困難. 我留意到至少有三種情況下, 測試集合說我們的程式給出了錯誤的答案, 而我卻認為我們程式的答案比測試集給的答案要好, 比如說: (實際上測試集給的三個答案的拼寫都不正確)
    correct('aranging') => 'arranging' (20); expected 'arrangeing' (1)
    correct('sumarys') => 'summary' (17); expected 'summarys' (1)
    correct('aurgument') => 'argument' (33); expected 'auguments' (1)

    我們還可以決定英語的變種, 以便訓練我們的程式, 比如說下面的三個錯誤是因為美式英語和英式英語拼發不一樣造成的, (我們的訓練集兩者都有):

    correct
                
               

    相關推薦

    怎樣一個拼寫檢查--python

    怎樣寫一個拼寫檢查器  Peter Norvig 翻譯: Eric You XU 原版: http://norvig.com/spell-correct.html 翻譯: htt

    一個小例子看公式的應用(學習簡單、基礎、入門的例子)

    從一個小例子看貝葉斯公式的應用 應用Bayesian公式考察如下的例項並回答問題。 張某為了解自己患上了X疾病的可能性,去醫院作常規血液檢查。其結果居然為陽性,他趕忙到網上查詢。根據網上的資料,血液檢查實驗是有誤差的,這種實驗有“1%的假陽性率和1%的

    樸素python小樣本實例

    else take dataset 核心 inpu lis def hle 模型 樸素貝葉斯優點:在數據較少的情況下仍然有效,可以處理多類別問題缺點:對於輸入數據的準備方式較為敏感適用數據類型:標稱型數據樸素貝葉斯決策理論的核心思想:選擇具有最高概率的決策樸素貝葉斯的一般過

    機器學習實戰——樸素Python實現記錄

    問題:regEx= re.compile('\\W*') 屬於列印錯誤。 正確:     regEx = re.compile('\W*') regEx = re.compile('\W*') 關於'\W' 和'\w'區別,可參考部落格:https://

    資料探勘十大演算法(九):樸素 python和sklearn實現

    第三個演算法終於算是稍有了解了,其實當你結合資料瞭解了它的實現原理後,你會發現確實很樸素。這裡對樸素貝葉斯演算法做一個介紹和總結,包括(原理、一個程式碼示例、sklearn實現),皆為親自實踐後的感悟,下面進入正文。 原理: 首先我們需要了解概率論的一些簡單知識:

    學習筆記——Kaggle_Digit Recognizer (樸素 Python實現)

    本文是個人學習筆記,該篇主要學習樸素貝葉斯演算法概念,並應用sklearn.naive_bayes演算法包解決Kaggle入門級Digit Recognizer。 貝葉斯定理 對於貝葉斯定理的瞭解和學習大部分都是從概率論開始的,但實際貝葉斯

    樸素 python 實現

    百度文庫 文庫2 機器學習實戰的樸素貝葉斯的程式碼太複雜 """ Created on Thu Aug 10 15:08:59 2017 @author: luogan """ #coding=gbk #Naive Bayes #Calculate

    樸素Python實現

    貝葉斯定理:                                    from math import * from numpy import * import random 建立資料集和標籤 def loadData(): postingList

    機器學習演算法-樸素Python實現

    引文:前面提到的K最近鄰演算法和決策樹演算法,資料例項最終被明確的劃分到某個分類中,下面介紹一種不能完全確定資料例項應該劃分到哪個類別,或者說只能給資料例項屬於給定分類的概率。 基於貝葉斯決策理論的分類方法之樸素貝葉斯 優點:在資料較少的情況下仍然有效

    樸素python程式碼實現(西瓜書)

    樸素貝葉斯python程式碼實現(西瓜書) 摘要: 樸素貝葉斯也是機器學習中一種非常常見的分類方法,對於二分類問題,並且資料集特徵為離散型屬性的時候, 使用起來非常的方便。原理簡單,訓練效率高,擬合效果好。 樸素貝葉斯 貝葉斯公式: 樸素貝葉斯之所以稱這為樸素,是因為假設了各個特徵是相互獨立的,因此假定下

    拼寫檢查

    ida 貝葉斯 read alter lower AD rect open altera 本拼寫檢查器是基於樸素貝葉斯的基礎來寫的,貝葉斯公式以及原理就不在詳述。直接上代碼 import re, collections def words(text): re

    實現拼寫檢查

    alt rec lam 最終 findall features 判斷 edit correct 貝葉斯公式 p(A|D)=p(A)*p(D|A)/p(D); 可以應用於垃圾郵件的過濾和拼寫檢查 例如:對於拼寫檢查,寫出一個單詞D,判斷該單詞為正確單詞A的概率。為上述條件概率

    機器學習小實戰(三) 實現拼寫檢查

    一、貝葉斯(Bayes)簡介      貝葉斯老爺爺是一位有名的老人家!貝葉斯演算法和概率有關,貝葉斯公式其實高中學過的,就是忘了而已。 二、貝葉斯實現拼寫檢查器 1. 原理 argmaxc P(A|B)=argmaxc P(B|A) P(A) /P(B) P(

    小專案--實現拼寫檢查

    求解:argmaxc P(c|w) -> argmaxc P(w|c)P©/P(w) P©:文章中出現一個正確拼寫詞c的概率,也就是語料庫中c出現的概率有多大 P(w|c):在使用者想鍵入c的情況下敲成w的概率,也就是使用者會以多大的概率把c敲錯成w argmaxc:用來列舉所有可能的

    機器學習-拼寫糾正實戰

    tdi eth 最大的 date oot 操作 dal 用戶 優先 #python版本3.7 import re, collections #將語料庫裏的單詞全部轉換為小寫def words(text): return re.findall(‘[a-z]+‘, text.l

    決策分類 MNIST手數據集 分類 python實現

    row 出了 net 訓練集 貝葉斯公式 影響 集中 oat blog 轉載: (1) https://zhuanlan.zhihu.com/p/51200626    (2) 菊安醬的機器學習第三期    (3) 代碼來自:https://github.com

    我對分類的理解

    log enter roman 高斯 clas http style 理解 times 我們能夠得到其統計概率密度例如以下: 這樣我們就知道該概率密度曲線大致符合正態分布。例如以下圖所看到的 大概能夠看出它在中心非常集中,邊

    機器學習:分類

    貝葉斯 逆向 檢測 .net 極大似然估計 href ref .com blank 參考文獻 從貝葉斯定理說開去 關鍵詞:逆向概率;先驗概率;後驗概率 我所理解的貝葉斯定理--知乎專欄 關鍵詞:醫院病癥檢測中的真假陽性 似然與極大似然估計--知乎專欄 關鍵詞:似然與概率的區

    機器學習系列——樸素分類(二)

    表示 -h line log ima 條件 code 樸素貝葉斯 spa 貝葉斯定理: 其中: 表示事件B已經發生的前提下,事件A發生的概率,叫做事件B發生下事件A的條件概率。其基本求解公式為:。 機器學習系列——樸素貝葉斯分類器(二)

    樸素分類的應用 Naive Bayes classifier

    upload dia get 等號 分布 eat 實現 維基 5.5 一、病人分類的例子 讓我從一個例子開始講起,你會看到貝葉斯分類器很好懂,一點都不難。 某個醫院早上收了六個門診病人,如下表。   癥狀  職業   疾病   打噴嚏 護士   感冒   打噴嚏