Нормалният алгоритъм на ngram традиционно работи само с предварителен контекст и има основателна причина: биграмният маркер взема решения, като взема предвид таговете на последните две думи, плюс текущата дума. Така че, освен ако не маркирате в две преминавания, етикетът на следващата дума все още не е известен. Но вие се интересувате от думи ngrams, а не от ngrams етикети, така че нищо не ви пречи да обучите маркер за ngram, където ngram се състои от думи от двете страни. И вие наистина можете да го направите лесно с NLTK.
Всички ngram тагери на NLTK правят тагове ngrams, отляво; но можете лесно да извлечете свой собствен маркер от техния абстрактен базов клас, ContextTagger
:
import nltk
from nltk.tag import ContextTagger
class TwoSidedTagger(ContextTagger):
left = 2
right = 1
def context(self, tokens, index, history):
left = self.left
right = self.right
tokens = tuple(t.lower() for t in tokens)
if index < left:
tokens = ("<start>",) * left + tokens
index += left
if index + right >= len(tokens):
tokens = tokens + ("<end>",) * right
return tokens[index-left:index+right+1]
Това дефинира маркер за тетраграма (2+1+1), където текущата дума е трета в ngram, а не последна, както обикновено. След това можете да инициализирате и обучите маркер точно като обикновените маркери на ngram (вижте глава 5 от книгата NLTK, особено раздели 5.4ff). Нека първо да видим как бихте изградили маркер за част от речта, като използвате част от корпуса на Браун като данни за обучение:
data = list(nltk.corpus.brown.tagged_sents(categories="news"))
train_sents = data[400:]
test_sents = data[:400]
twosidedtagger = TwoSidedTagger({}, backoff=nltk.DefaultTagger('NN'))
twosidedtagger._train(train_sents)
Подобно на всички ngram тагери в NLTK, този ще делегира на backoff tagger, ако бъде помолен да тагне ngram, който не е видял по време на обучението. За простота използвах прост "тагер по подразбиране" като маркер за отстъпване, но вероятно ще трябва да използвате нещо по-мощно (вижте отново главата за NLTK).
След това можете да използвате маркера си, за да маркирате нов текст или да го оцените с вече маркиран тестов набор:
>>> print(twosidedtagger.tag("There were dogs everywhere .".split()))
>>> print(twosidedtagger.evaluate(test_sents))
Предсказващи думи:
Горният маркер присвоява POS етикет, като взема предвид близките думи; но вашата цел е да предвидите самата дума, така че имате нужда от различни данни за обучение и различен тагер по подразбиране. NLTK API очаква данни за обучение във формата (word, LABEL)
, където LABEL
е стойността, която искате да генерирате. Във вашия случай LABEL
е само текущата дума; така че направете данните си за обучение, както следва:
data = [ zip(s,s) for s in nltk.corpus.brown.sents(categories="news") ]
train_sents = data[400:]
test_sents = data[:400]
twosidedtagger = TwoSidedTagger({}, backoff=nltk.DefaultTagger('the')) # most common word
twosidedtagger._train(train_sents)
Няма смисъл целевата дума да се появява в "контекстната" ngram, така че трябва също да промените метода context()
, така че върнатата ngram да не го включва:
def context(self, tokens, index, history):
...
return tokens[index-left:index] + tokens[index+1:index+right+1]
Този маркер използва триграми, състоящи се от две думи отляво и една отдясно на текущата дума.
С тези модификации ще създадете маркер, който извежда най-вероятната дума на всяка позиция. Опитайте и както ви харесва.
Прогноза:
Моите очаквания са, че ще имате нужда от огромно количество данни за обучение, преди да можете да постигнете прилична производителност. Проблемът е, че маркерите на ngram могат да предложат етикет само за контексти, които са видели по време на обучението.
За да създадете тагер, който обобщава, обмислете използването на NLTK за обучение на „последователен класификатор“. Можете да използвате каквито функции искате, включително думите преди и след - разбира се, колко добре ще работи е ваш проблем. NLTK класификаторът API е подобен на този за ContextTagger
, но контекстната функция (известна още като функционална функция) връща речник, а не кортеж. Отново вижте книгата за NLTK и изходния код.
person
alexis
schedule
27.08.2015
for t, token in enumerate(tokens): do_something(tokens[t-n:t+m])
- person clemtoy   schedule 17.08.2015