天然語言處理中算法設計有兩大部分:分而治之 和 轉化 思想。一個是將大問題簡化爲小問題,另外一個是將問題抽象化,向向已知轉化。前者的例子:歸併排序;後者的例子:判斷相鄰元素是否相同(與排序)。
此次總結的天然語言中經常使用的一些基本算法,算是入個門了。算法
使用遞歸速度上會受影響,可是便於理解算法深層嵌套對象。而一些函數式編程語言會將尾遞歸優化爲迭代。編程
def func(wordlist): length = len(wordlist) if length==1: return 1 else: return func(wordlist[1:])*length
from nltk.corpus import wordnet as wn def func(s):#s是WordNet裏面的對象 return 1+sum(func(child) for child in s.hyponyms()) dog = wn.synset('dog.n.01') print(func(dog))
創建一個嵌套的字典結構,每一級的嵌套包含既定前綴的全部單詞。而子查找樹含有全部可能的後續詞。數據結構
def WordTree(trie,key,value): if key: first , rest = key[0],key[1:] if first not in trie: trie[first] = {} WordTree(trie[first],rest,value) else: trie['value'] = value WordDict = {} WordTree(WordDict,'cat','cat') WordTree(WordDict,'dog','dog') print(WordDict)
登山法是完徹底全的貪心法,每次都鼠目寸光的選擇一個當前最優解,所以只能搜索到局部的最優值。模擬退火其實也是一種貪心算法,可是它的搜索過程引入了隨機因素。模擬退火算法以必定的機率來接受一個比當前解要差的解,所以有可能會跳出這個局部的最優解,達到全局的最優解。app
import nltk from random import randint #text = 'doyou' #segs = '01000' def segment(text,segs):#根據segs,返回切割好的詞鏈表 words = [] last = 0 for i in range(len(segs)): if segs[i]=='1':#每當碰見1,說明是詞分界 words.append(text[last:i+1]) last = i+1 words.append(text[last:]) return words def evaluate(text,segs): #計算這種詞分界的得分。做爲分詞質量,得分值越小越好(分的越細和更準確之間的平衡) words = segment(text,segs) text_size = len(words) lexicon_size = len(' '.join(list(set(words)))) return text_size + lexicon_size ###################################如下是退火算法的非肯定性搜索############################################ def filp(segs,pos):#在pos位置擾動 return segs[:pos]+str(1-int(segs[pos]))+segs[pos+1:] def filp_n(segs,n):#擾動n次 for i in range(n): segs = filp(segs,randint(0,len(segs)-1))#隨機位置擾動 return segs def anneal(text,segs,iterations,cooling_rate): temperature = float(len(segs)) while temperature>=0.5: best_segs,best = segs,evaluate(text,segs) for i in range(iterations):#擾動次數 guess = filp_n(segs,int(round(temperature))) score = evaluate(text,guess) if score<best: best ,best_segs = score,guess score,segs = best,best_segs temperature = temperature/cooling_rate #擾動邊界,進行降溫 print( evaluate(text,segs),segment(text,segs)) print() return segs text = 'doyouseethekittyseethedoggydoyoulikethekittylikethedoggy' seg = '0000000000000001000000000010000000000000000100000000000' anneal(text,seg,5000,1.2)
它在天然語言中運用很是普遍。首先他須要一張表,用來將每一次的子結果存放在查找表之中。避免了重複計算子問題!!!dom
這裏咱們討論一個梵文組合旋律的問題。短音節:S,一個長度;長音節:L,兩個長度。因此構建長度爲2的方式:{SS,L}。編程語言
def func1(n): if n==0: return [""] elif n==1: return ["S"] else: s = ["S" + item for item in func1(n-1)] l = ["L" + item for item in func1(n-2)] return s+l print(func1(4))
以前遞歸十分佔用時間,若是是40個音節,咱們須要重複計算632445986次。若是使用動態規劃,咱們能夠把結果存到一個表中,須要時候調用,而不是很坑爹重複計算。函數式編程
def func2(n):#採用自下而上的動態規劃 lookup = [[""],["S"]] for i in range(n-1): s = ["S"+ item for item in lookup[i+1]] l = ["L" + item for item in lookup[i]] lookup.append(s+l) return lookup print(func2(4)[4]) print(func2(4))
def func3(n,lookup={0:[""],1:["S"]}):#採用自上而下的動態規劃 if n not in lookup: s = ["S" + item for item in func3(n-1)] l = ["L" + item for item in func3(n-2)] lookup[n] = s+l return lookup[n]#必須返回lookup[n].不然遞歸的時候會出錯 print(func3(4))
對於以上兩種方法,自下而上的方法在某些時候會浪費資源,由於,子問題不必定是解決主問題的必要條件。函數
裝飾器
@memoize
會存儲每次函數調用時的結果及參數,那麼以後的在調用,就不用重複計算。而咱們能夠只把精力放在上層邏輯,而不是更關注性能和時間(被解決了)性能
from nltk import memoize @memoize def func4(n): if n==0: return [""] elif n==1: return ["S"] else: s = ["S" + item for item in func4(n-1)] l = ["L" + item for item in func4(n-2)] return s+l print(func4(4))
這裏主要介紹一下除了上述兩種主要算法外,一些小的使用技巧和相關基礎概念。優化
詞彙多樣性主要取決於:平均詞長(字母個數/每一個單詞)、平均句長(單詞個數/每一個句子)和文本中沒歌詞出現的次數。
from nltk.corpus import gutenberg for fileid in gutenberg.fileids(): num_chars = len(gutenberg.raw(fileid)) num_words = len(gutenberg.words(fileid)) num_sents = len(gutenberg.sents(fileid)) num_vocab = len(set(w.lower() for w in gutenberg.words(fileid))) print(int(num_chars/num_words),int(num_words/num_sents),int(num_words/num_vocab),'from',fileid)
文體差別性能夠體如今不少方面:動詞、情態動詞、名詞等等。這裏咱們以情態動詞爲例,來分析常見情態動詞的在不一樣文本的差異。
from nltk.corpus import brown from nltk import FreqDist,ConditionalFreqDist cfd = ConditionalFreqDist(( genere,word) for genere in brown.categories() for word in brown.words(categories=genere)) genres=['news','religion','hobbies'] models = ['can','could','will','may','might','must'] cfd.tabulate(conditions = genres,samples=models)
從《創世紀》中獲得全部的雙連詞,根據機率分佈,來判斷哪些詞最有可能跟在給定詞後面。
import nltk def create_sentence(cfd,word,num=15): for i in range(num): print(word,end=" ") word = cfd[word].max()#查找word最有可能的後綴 text= nltk.corpus.genesis.words("english-kjv.txt") bigrams = nltk.bigrams(text) cfd = nltk.ConditionalFreqDist(bigrams) print(create_sentence(cfd,'living'))
單詞長度>=3,而且必定有r,且只能出現'egivrvonl'中的字母。
puzzle_word = nltk.FreqDist('egivrvonl') base_word = 'r' wordlist = nltk.corpus.words.words() result = [w for w in wordlist if len(w)>=3 and base_word in w and nltk.FreqDist(w)<=puzzle_word] #經過FreqDist比較法(比較鍵對應的value),來完成字母只出現一次的要求!!! print(result)
除了研究算法,分析內部實現外。構造輔助數據結構,能夠顯著加快程序執行。
import nltk def raw(file): contents = open(file).read() return str(contents) def snippet(doc,term):#查找doc中term的定位 text = ' '*30+raw(doc)+' '*30 pos = text.index(term) return text[pos-30:pos+30] files = nltk.corpus.movie_reviews.abspaths() idx = nltk.Index((w,f) for f in files for w in raw(f).split()) #注意nltk.Index格式 query = 'tem' while query!='quit' and query: query = input('>>> input the word:') if query in idx: for doc in idx[query]: print(snippet(doc,query)) else: print('Not found')
歡迎進一步交流本博文相關內容:
博客園地址 : http://www.cnblogs.com/AsuraDong/
CSDN地址 : http://blog.csdn.net/asuradong
也能夠致信進行交流 : xiaochiyijiu@163.com
歡迎轉載 , 但請指明出處 : )