貝葉斯推斷及其互連網應用(三):拼字檢查

來源:互聯網
上載者:User

標籤:des   blog   http   io   os   使用   ar   strong   for   

(這個系列的第一部分介紹了貝葉斯定理,第二部分介紹了如何過濾垃圾郵件,今天是第三部分。)

使用Google的時候,如果你拼錯一個單詞,它會提醒你正確的拼法。

比如,你不小心輸入了seperate。

Google告訴你,這個詞是不存在的,正確的拼法是separate。

這就叫做"拼字檢查"(spelling corrector)。有好幾種方法可以實現這個功能,Google使用的是基於貝葉斯推斷的統計學方法。這種方法的特點就是快,很短的時間內處理大量文本,並且有很高的精確度(90%以上)。Google的研發總監Peter Norvig,寫過一篇著名的文章,解釋這種方法的原理。

下面我們就來看看,怎麼利用貝葉斯推斷,實現"拼字檢查"。其實很簡單,一小段代碼就夠了。

一、原理

使用者輸入了一個單詞。這時分成兩種情況:拼字正確,或者拼字不正確。我們把拼字正確的情況記做c(代表correct),拼字錯誤的情況記做w(代表wrong)。

所謂"拼字檢查",就是在發生w的情況下,試圖推斷出c。從機率論的角度看,就是已知w,然後在若干個備選方案中,找出可能性最大的那個c,也就是求下面這個式子的最大值。

  P(c|w)

根據貝葉斯定理:

  P(c|w) = P(w|c) * P(c) / P(w)

對於所有備選的c來說,對應的都是同一個w,所以它們的P(w)是相同的,因此我們求的其實是

  P(w|c) * P(c)

的最大值。

P(c)的含義是,某個正確的詞的出現"機率",它可以用"頻率"代替。如果我們有一個足夠大的文本庫,那麼這個文本庫中每個單詞的出現頻率,就相當於它的發生機率。某個詞的出現頻率越高,P(c)就越大。

P(w|c)的含義是,在試圖拼字c的情況下,出現拼字錯誤w的機率。這需要統計資料的支援,但是為了簡化問題,我們假設兩個單詞在字形上越接近, 就有越可能拼錯,P(w|C)就越大。舉例來說,相差一個字母的拼法,就比相差兩個字母的拼法,發生機率更高。你想拼字單詞hello,那麼錯誤拼成 hallo(相差一個字母)的可能性,就比拼成haallo高(相差兩個字母)。

所以,我們只要找到與輸入單詞在字形上最相近的那些詞,再在其中挑出出現頻率最高的一個,就能實現 P(w|c) * P(c) 的最大值。

二、演算法

最簡單的演算法,只需要四步就夠了。

第一步,建立一個足夠大的文本庫。

網上有一些免費來源,比如古登堡計劃、Wiktionary、英國國家語料庫等等。

第二步,取出文本庫的每一個單詞,統計它們的出現頻率。

第三步,根據使用者輸入的單詞,得到其所有可能的拼字相近的形式。

所謂"拼字相近",指的是兩個單詞之間的"編輯距離"(edit distance)不超過2。也就是說,兩個詞只相差1到2個字母,只通過----刪除、交換、更改和插入----這四種操作中的一種,就可以讓一個詞變成另一個詞。

第四步,比較所有拼字相近的詞在文本庫的出現頻率。頻率最高的那個詞,就是正確的拼法。

根據Peter Norvig的驗證,這種演算法的精確度大約為60%-70%(10個拼字錯誤能夠檢查出6個。)雖然不令人滿意,但是能夠接受。畢竟它足夠簡單,計算速度極快。(本文的最後部分,將詳細討論這種演算法的缺陷在哪裡。)

三、代碼

我們使用Python語言,實現上一節的演算法。

第一步,把網上下載的文本庫儲存為big.txt檔案。這步不需要編程。

第二步,載入Python的正則語言模組(re)和collections模組,後面要用到。

  import re, collections

第三步,定義words()函數,用來取出文本庫的每一個詞。

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

lower()將所有詞都轉成小寫,避免因為大小寫不同,而被算作兩個詞。

第四步,定義一個train()函數,用來建立一個"字典"結構。文本庫的每一個詞,都是這個"字典"的鍵;它們所對應的值,就是這個詞在文本庫的出現頻率。

  def train(features):

    model = collections.defaultdict(lambda: 1)

    for f in features:

      model[f] += 1

    return model

collections.defaultdict(lambda: 1)的意思是,每一個詞的預設出現頻率為1。這是針對那些沒有出現在文本庫的詞。如果一個詞沒有在文本庫出現,我們並不能認定它就是一個不存在的詞,因此 將每個詞出現的預設頻率設為1。以後每出現一次,頻率就增加1。

第五步,使用words()和train()函數,產生上一步的"詞頻字典",放入變數NWORDS。

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

第六步,定義edits1()函數,用來產生所有與輸入參數word的"編輯距離"為1的詞。

  alphabet = ‘abcdefghijklmnopqrstuvwxyz‘

  def edits1(word):

    splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]

    deletes = [a + b[1:] for a, b in splits if b]

    transposes = [a + b[1] + b[0] + b[2:] for a, b in splits if len(b)>1]

    replaces = [a + c + b[1:] for a, b in splits for c in alphabet if b]

    inserts = [a + c + b for a, b in splits for c in alphabet]

    return set(deletes + transposes + replaces + inserts)

edit1()函數中的幾個變數的含義如下:

  (1)splits:將word依次按照每一位分割成前後兩半。比如,‘abc‘會被分割成 [(‘‘, ‘abc‘), (‘a‘, ‘bc‘), (‘ab‘, ‘c‘), (‘abc‘, ‘‘)] 。

  (2)beletes:依次刪除word的每一位後、所形成的所有新詞。比如,‘abc‘對應的deletes就是 [‘bc‘, ‘ac‘, ‘ab‘] 。

  (3)transposes:依次交換word的鄰近兩位,所形成的所有新詞。比如,‘abc‘對應的transposes就是 [‘bac‘, ‘acb‘] 。

  (4)replaces:將word的每一位依次替換成其他25個字母,所形成的所有新詞。比如,‘abc‘對應的replaces就是 [‘abc‘, ‘bbc‘, ‘cbc‘, ... , ‘abx‘, ‘ aby‘, ‘abz‘ ] ,一共包含78個詞(26 × 3)。

  (5)inserts:在word的鄰近兩位之間依次插入一個字母,所形成的所有新詞。比如,‘abc‘ 對應的inserts就是[‘aabc‘, ‘babc‘, ‘cabc‘, ..., ‘abcx‘, ‘abcy‘, ‘abcz‘],一共包含104個詞(26 × 4)。

最後,edit1()返回deletes、transposes、replaces、inserts的合集,這就是與word"編輯距離"等於1的所有詞。對於一個n位的詞,會返回54n+25個詞。

第七步,定義edit2()函數,用來產生所有與word的"編輯距離"為2的詞語。

  def edits2(word):

    return set(e2 for e1 in edits1(word) for e2 in edits1(e1))

但是這樣的話,會返回一個 (54n+25) * (54n+25) 的數組,實在是太大了。因此,我們將edit2()改為known_edits2()函數,將返回的詞限定為在文本庫中出現過的詞。

  def known_edits2(word):

    return set(e2 for e1 in edits1(word) for e2 in edits1(e1) if e2 in NWORDS)

第八步,定義correct()函數,用來從所有備選的詞中,選出使用者最可能想要拼字的詞。

  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=NWORDS.get)

我們採用的規則為:

  (1)如果word是文本庫現有的詞,說明該詞拼字正確,直接返回這個詞;

  (2)如果word不是現有的詞,則返回"編輯距離"為1的詞之中,在文本庫出現頻率最高的那個詞;

  (3)如果"編輯距離"為1的詞,都不是文本庫現有的詞,則返回"編輯距離"為2的詞中,出現頻率最高的那個詞;

  (4)如果上述三條規則,都無法得到結果,則直接返回word。

至此,代碼全部完成,合起來一共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):

    splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]

    deletes = [a + b[1:] for a, b in splits if b]

    transposes = [a + b[1] + b[0] + b[2:] for a, b in splits if len(b)>1]

    replaces = [a + c + b[1:] for a, b in splits for c in alphabet if b]

    inserts = [a + c + b for a, b in splits for c in alphabet]

    return set(deletes + transposes + replaces + inserts)

  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):

    candidates = known([word]) or known(edits1(word)) or known_edits2(word) or [word]

    return max(candidates, key=NWORDS.get)

使用方法如下:

  >>> correct(‘speling‘)

  ‘spelling‘

  >>> correct(‘korrecter‘)

  ‘corrector‘

四、缺陷

我們使用的這種演算法,有一些缺陷,如果投入生產環境,必須在這些方面加入改進。

(1)文本庫必須有很高的精確性,不能包含拼字錯誤的詞。

如果使用者輸入一個錯誤的拼法,文本庫恰好包含了這種拼法,它就會被當成正確的拼法。

(2)對於不包含在文本庫中的新詞,沒有提出解決辦法。

如果使用者輸入一個新詞,這個詞不在文本庫之中,就會被當作錯誤的拼字進行糾正。

(3)程式返回的是"編輯距離"為1的詞,但某些情況下,正確的詞的"編輯距離"為2。

比如,使用者輸入reciet,會被糾正為recite(編輯距離為1),但使用者真正想要輸入的詞是receipt(編輯距離為2)。也就是說,"編輯距離"越短越正確的規則,並非所有情況下都成立。

(4)有些常見拼字錯誤的"編輯距離"大於2。

這樣的錯誤,程式無法發現。下面就是一些例子,每一行前面那個詞是正確的拼法,後面那個則是常見的錯誤拼法。

purple perpul
curtains courtens
minutes muinets
successful sucssuful
inefficient ineffiect
availability avaiblity
dissension desention
unnecessarily unessasarily
necessary nessasary
unnecessary unessessay
night nite
assessing accesing
necessitates nessisitates

(5)使用者輸入的詞的拼字正確,但是其實想輸入的是另一個詞。

比如,使用者輸入是where,這個詞拼字正確,程式不會糾正。但是,使用者真正想輸入的其實是were,不小心多打了一個h。

(6)程式返回的是出現頻率最高的詞,但使用者真正想輸入的是另一個詞。

比如,使用者輸入ther,程式會返回the,因為它的出現頻率最高。但是,使用者真正想輸入的其實是their,少打了一個i。也就是說,出現頻率最高的詞,不一定就是使用者想輸入的詞。

(7)某些詞有不同的拼法,程式無法辨別。

比如,英國英語和美國英語的拼法不一致。英國使用者輸入‘humur‘,應該被糾正為‘humour‘;美國使用者輸入‘humur‘,應該被糾正為‘humor‘。但是,我們的程式會統一糾正為‘humor‘。

(完)

貝葉斯推斷及其互連網應用(三):拼字檢查

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.