คำตอบโดย https://stackoverflow.com/users/1515832/generic-humanนั้นยอดเยี่ยมมาก แต่การนำสิ่งนี้ไปใช้ได้ดีที่สุดที่ฉันเคยเห็นมาคือ Peter Norvig เขียนด้วยตัวเองในหนังสือ 'Beautiful Data'
ก่อนที่ฉันจะวางโค้ดของเขาให้ฉันขยายความว่าทำไมวิธีการของ Norvig จึงแม่นยำกว่า (แม้ว่าจะช้ากว่าเล็กน้อยและนานกว่าในแง่ของโค้ด)
1) ข้อมูลดีขึ้นเล็กน้อย - ทั้งในแง่ของขนาดและในแง่ของความแม่นยำ (เขาใช้การนับจำนวนคำมากกว่าการจัดอันดับแบบธรรมดา) 2) ที่สำคัญกว่านั้นคือตรรกะที่อยู่เบื้องหลัง n-g ที่ทำให้แนวทางนั้นแม่นยำจริงๆ .
ตัวอย่างที่เขาให้ไว้ในหนังสือของเขาคือปัญหาของการแยกสตริง 'sitdown' ตอนนี้วิธีการแยกสตริงที่ไม่ใช่ bigram จะพิจารณา p ('sit') * p ('down') และถ้าน้อยกว่า p ('sitdown') ซึ่งเป็นกรณีที่ค่อนข้างบ่อย - มันจะไม่แยก แต่เราต้องการให้ (เกือบตลอดเวลา)
อย่างไรก็ตามเมื่อคุณมีโมเดล bigram คุณสามารถกำหนดค่า p ('นั่งลง') เป็น bigram vs p ('sitdown') และอดีตชนะ โดยทั่วไปหากคุณไม่ใช้ bigrams จะถือว่าความน่าจะเป็นของคำที่คุณแยกเป็นอิสระซึ่งไม่ใช่ในกรณีนี้คำบางคำมีแนวโน้มที่จะปรากฏทีละคำ น่าเสียดายที่คำเหล่านี้มักจะติดกันเป็นจำนวนมากและทำให้ตัวแยกสับสน
นี่คือลิงก์ไปยังข้อมูล (เป็นข้อมูลสำหรับปัญหา 3 ปัญหาที่แยกจากกันและการแบ่งกลุ่มเป็นเพียงหนึ่งเดียวโปรดอ่านรายละเอียดในบท): http://norvig.com/ngrams/
และนี่คือลิงค์ไปยังรหัส: http://norvig.com/ngrams/ngrams.py
ลิงก์เหล่านี้ใช้งานได้ระยะหนึ่งแล้ว แต่ฉันจะคัดลอกและวางส่วนการแบ่งส่วนของโค้ดที่นี่
import re, string, random, glob, operator, heapq
from collections import defaultdict
from math import log10
def memo(f):
"Memoize function f."
table = {}
def fmemo(*args):
if args not in table:
table[args] = f(*args)
return table[args]
fmemo.memo = table
return fmemo
def test(verbose=None):
"""Run some tests, taken from the chapter.
Since the hillclimbing algorithm is randomized, some tests may fail."""
import doctest
print 'Running tests...'
doctest.testfile('ngrams-test.txt', verbose=verbose)
################ Word Segmentation (p. 223)
@memo
def segment(text):
"Return a list of words that is the best segmentation of text."
if not text: return []
candidates = ([first]+segment(rem) for first,rem in splits(text))
return max(candidates, key=Pwords)
def splits(text, L=20):
"Return a list of all possible (first, rem) pairs, len(first)<=L."
return [(text[:i+1], text[i+1:])
for i in range(min(len(text), L))]
def Pwords(words):
"The Naive Bayes probability of a sequence of words."
return product(Pw(w) for w in words)
#### Support functions (p. 224)
def product(nums):
"Return the product of a sequence of numbers."
return reduce(operator.mul, nums, 1)
class Pdist(dict):
"A probability distribution estimated from counts in datafile."
def __init__(self, data=[], N=None, missingfn=None):
for key,count in data:
self[key] = self.get(key, 0) + int(count)
self.N = float(N or sum(self.itervalues()))
self.missingfn = missingfn or (lambda k, N: 1./N)
def __call__(self, key):
if key in self: return self[key]/self.N
else: return self.missingfn(key, self.N)
def datafile(name, sep='\t'):
"Read key,value pairs from file."
for line in file(name):
yield line.split(sep)
def avoid_long_words(key, N):
"Estimate the probability of an unknown word."
return 10./(N * 10**len(key))
N = 1024908267229 ## Number of tokens
Pw = Pdist(datafile('count_1w.txt'), N, avoid_long_words)
#### segment2: second version, with bigram counts, (p. 226-227)
def cPw(word, prev):
"Conditional probability of word, given previous word."
try:
return P2w[prev + ' ' + word]/float(Pw[prev])
except KeyError:
return Pw(word)
P2w = Pdist(datafile('count_2w.txt'), N)
@memo
def segment2(text, prev='<S>'):
"Return (log P(words), words), where words is the best segmentation."
if not text: return 0.0, []
candidates = [combine(log10(cPw(first, prev)), first, segment2(rem, first))
for first,rem in splits(text)]
return max(candidates)
def combine(Pfirst, first, (Prem, rem)):
"Combine first and rem results into one (probability, words) pair."
return Pfirst+Prem, [first]+rem