1. 程式人生 > >機器學習:單詞拼寫糾正器python實現

機器學習:單詞拼寫糾正器python實現

01 樸素貝葉斯分類實戰

前面介紹了貝葉斯的基本理論,樸素貝葉斯分類器,拉普拉斯修正,文章的連結如下:
機器學習:說說貝葉斯分類
樸素貝葉斯分類器:例子解釋
樸素貝葉斯分類:拉普拉斯修正
在這3篇推送中用例子詳細闡述了貝葉斯公式和樸素貝葉斯如何做分類,以及如何修正一些屬性某些取值概率。

下面,藉助樸素貝葉斯分類器的基本思想,編寫一個單詞拼寫糾正器,它大致實現的功能如下:
如果使用者輸入的單詞存在,則直接提示在字典中發現,並返回
如果單詞不在詞典中,糾正器會猜測使用者的可能輸入,然後做出最多兩步的距離調整,並返回糾正後,使用者最可能想輸入的前三個單詞
如果經過最多的兩步調整後,還是未找到,則提示想輸入的單詞在字典中不存在。

02 糾正器實現原理

1 如使用者輸入了 hella,糾正後發現的3個最有可能的輸入如下:
‘want to input: hello’, ‘hell’, ‘fella’
2 如使用者輸入了appropreate,糾正器糾正後:
‘want to input: appropriate’
3 如使用者輸入了owesomes,糾正器糾正後:
‘want to input: awesome’
4 如使用者輸入了grduallyare,糾正器糾正後:
grduallyare not found in dictionary!

以上是糾正器能實現的糾正例項,那麼該如何實現這麼一個單詞拼寫錯誤檢查和糾正的工具呢。

如果使用者實際輸入的單詞為 w(word的簡寫), 然後拼寫糾正器猜測使用者實際想輸入的單詞為 c1, c2 , c3 , ……. 因此,我們可以猜測使用者輸入了 P(c1 | w) ,P(c2 | w),P(c3 | w)等等這些多種猜測。如果發現P(c1 | w) 的概率最大,那麼使用者很有可能想輸入的那個單詞為 c1 。這個概率可以統一表示為:
P(c | w)
如何求解這個概率的最大值?

將以上概率做如下轉化來求解:使用者想輸入的很可能在語料庫的這個 c 時,有可能被錯誤的輸入為了 w1,w2,w3 ,…… 則這個概率可以統一表示為:
P(w | c)
使用者錯誤地輸入成 w1,w2,w3,……,它們之間是相互獨立的,因此可以根據樸素貝葉斯分類器的理論,進一步將後驗概率 P(c | w) 的求解轉化為求解如下的目標函式:
max ( P(c) * P(w | c) / P(w) )
上式中 P(c)為先驗概率,下載一個比較豐富的單詞拼寫都正確的英文單詞庫後,統計下每個單詞出現的頻次,就是單詞 c 的出現的概率;
P(w) 是與問題分類無關的量,因為使用者有可能輸入任意一個單詞;
P(w | c) 是一個類條件概率:使用者想輸入c(c在語料庫中是有對應的,在此處需要注意:我們取的語料庫不能100%保證一定存在任意一個正確的單詞,所以在統計的過程中,假定單詞至少出現1次),但是被錯誤地輸入為了 wi 的概率。

P(w | c) 的求解方法通常會有很多種,比如使用者想輸入hello,但是實際輸入了 hella,它們之間的區別僅僅是最後一個字元輸入錯誤,這個出現的概率還是挺大的吧;但是,再看看下面這個例子。

如果使用者想輸入awesome, 但是實際輸入成了owesomes,輸錯了1個字元,多添加了 1個字元,這種情況發生的概率就比上面那種小一些吧。

因此,在本文中設計的糾正器沒有直接去量化 P(w | c) 這個概率,而是採取了從定性上進行分析,通常經過一步調整出現的概率大於經過兩步調整出現的概率。所以,當糾正器遇到一個待糾正的詞語時,它會糾正一步,如果發現了,就直接返回了;否則才會進行兩步調整,這種調整的優先順序的原理是根據 P(w | c) 。

這樣先驗概率 P(c) 和類條件概率 P(w | c) 的求解方法就弄明白了,當一步糾正就能在語料庫找到對應後,就不會進行兩步糾正,但是一步糾正會返回多個,此時再根據P(c)找出這些中的出現頻次最多的,這樣最終的結果便是猜測到的使用者最有可能想輸入的單詞。

03 糾正器Python程式碼

構建先驗概率P(c),語料庫下載了老友記的1-10部+呼嘯山莊全部組成的單詞庫。

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

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

ipath = './bigword.txt'
uipath = ipath.encode("utf8")
htxt = open(uipath,'r',errors ='ignore')
cwords = tolower(htxt.read())
#get P(c)
nwords = train(cwords) 
nwords
類條件概率
alpha = 'abcdefghijklmnopqrstuvwxyz'
#一步調整
def version1(word):
    n = len(word)
    add_a_char = [word[0:i] + c + word[i:] for i in range(n+1) for c in alpha]
    delete_a_char = [word[0:i] + word[i+1:] for i in range(n)]
    revise_a_char = [word[0:i] + c + word[i+1:] for i in range(n) for c in alpha]
    swap_adjacent_two_chars = [word[0:i] + word[i+1]+ word[i]+ word[i+2:] for i in range(n-1)] 
    return set( add_a_char + delete_a_char +
               revise_a_char +  swap_adjacent_two_chars)
#兩步調整           
def version2(word):
    return set(e2 for e1 in edits1(word) for e2 in edits1(e1))


樸素貝葉斯分類器

def identify(words):
    return set(w for w in words if w in nwords)

def getMax(wanteds):
    threewanteds=[]
    maxword = max(wanteds,key=lambda w : nwords[w])
    threewanteds.append('want to input: '+ maxword)
    wanteds.remove(maxword)
    if len(wanteds)>0:
        maxword = max(wanteds,key=lambda w : nwords[w])
        threewanteds.append(maxword)
        wanteds.remove(maxword)
        if len(wanteds)>0:
            maxword = max(wanteds,key=lambda w : nwords[w])
            threewanteds.append(maxword)   
    return threewanteds
def bayesClassifier(word):
    #如果字典中有輸入的單詞,直接返回
    if identify([word]):
        return 'found: '+ word
    #一步調整
    wanteds = identify(version1(word)) 
    if len(wanteds)>0:
        return getMax(wanteds)
    #兩步調整
    wanteds = identify(version2(word))
    if len(wanteds)>0:
        return getMax(wanteds)
    #不再修正,直接提示這個單詞不在當前的詞典中
    else:    
        return [word + ' not found in dictionary!' ]

歡迎關注 演算法channel

這裡寫圖片描述