вівторок, 8 листопада 2011 р.

Задача №6 Лабораторна робота №8

Умова. Написати програму для створення двовимірного масиву word_vowels елементами якого є набори. Програма повинна обробити список слів і додати кожне зі слів до word_vowels[l][v] де l – довжина слова, v – кількість голосних у слові.

Отже, якщо у мене, наприклад, список слів Іван, Петро, Василь - то я повинен в комірку масиву з індексами 4,2 записати слово Іван, в комірку масиву з індексами 5,2 записати слово Петро, в комірку масиву з індексами 6,2 записати слово Василь.

import nltk
from nltk.corpus import brown
list_w=[w.lower() for w in brown.words()[:10]] # список слів
# побудова двовимірного масиву
# сторінка 12 лаб.робота №7
#>>> m, n = 3, 7
#>>> array = [[set() for i in range(n)] for j in range(m)]
#>>> array[2][5].add('Alice')
#>>> pprint.pprint(array)
#[[set([]), set([]), set([]), set([]), set([]), set([]), set([])],
#[set([]), set([]), set([]), set([]), set([]), set([]), set([])],
#[set([]), set([]), set([]), set([]), set([]), set(['Alice']), set([])]]
#m, n - розміри масиву #як їх отримати,
#максимальне значення, яке може мати m - це довжина найдовшого слова зі списку #максимальне значення, яке може мати n - це найбільша кількість голосних у слові зі списку len_m_of_array=max([len(w) for w in list_w])
len_n_of_array=max([len([char for char in w if char in 'aoieu']) for w in list_w])
array = [[set() for i in range(len_n_of_array+1)] for j in range(len_m_of_array+1)]
# заповнення масиву
for w in list_w:
    array[len(w)][len([char for char in w if char in 'aoieu'])].add(w)
# переглядаю масив
pprint.pprint(array)

Це рішення має одну проблему. Я два рази для кожного слова знаходжу його довжину та кількість голосних. Перший раз при визначенні розміру масиву, а другий при його заповненні. Потрібно подумати, як уникнути цих подвійних обчислень.

середа, 28 вересня 2011 р.

Unicode в Python (лаб.робота №5)

Додаткову інформацію по роботі з Unicode в Python
можна знайти в:
http://boodebr.org/main/python/all-about-python-and-unicode
http://www.uchi-it.ru/9/11/6.html
http://www.py-my.ru/post/4bfb3c6a1d41c846bc00009b
або
http://www.python.su/forum/viewtopic.php?id=7742
http://www.python.su/forum/viewtopic.php?id=11747

пʼятниця, 6 травня 2011 р.

Перегляд синсетів у які входить задане слово (Задача №7 11 розділ)

Write a function which displays the complete entry for a lexeme. When the
lexeme is incorrectly spelled, it should display the entry for the most similarly
spelled lexeme.

Вважаю, що entry для lexeme це будуть всі синсети з WordNet у які вона входить.
Повністю повторення прикладу зі сторінки 424 але все зроблено, як одна функція, хоча так робити недоцільно. Кожен раз коли виконується ця функція відбувається індексування словника signatures = nltk.Index((signature(w), w) for w in nltk.corpus.words.words())

import nltk, re

def fuzzy_spell(word):
mappings = [('ph', 'f'), ('ght', 't'), ('^kn', 'n'), ('qu', 'kw'), ('[aeiou]+', 'a'), (r'(.)\1', r'\1')]
def signature(word):
for patt, repl in mappings:
word = re.sub(patt, repl, word)
pieces = re.findall('[^aeiou]+', word)
return ''.join(char for piece in pieces for char in piece)

signatures = nltk.Index((signature(w), w) for w in nltk.corpus.words.words())

def rank(word, wordlist):
ranked = sorted((nltk.edit_distance(word, w), w) for w in wordlist)
return [word for (_, word) in ranked]

# Якщо слово відсутнє у словнику то вважаємо що воно містить помилку
if word not in nltk.corpus.words.words():
sig = signature(word)
if sig in signatures:
for words in rank(word, signatures[sig]):
print words, nltk.corpus.wordnet.synsets(words)
else:
return []
else:
print nltk.corpus.wordnet.synsets(word)

print fuzzy_spell('closeі')

Зовсім просто (Задача№1 11 Розділ)

In Example 11.8 the new field appeared at the bottom of the entry. Modify this program so that it inserts the new subelement right after the lx field. (Hint: create the new cv field using Element('cv'), assign a text value to it, then use the insert() method of the parent element.)

Доволі все просто.
import nltk,re,pprint
from nltk.corpus import toolbox
from nltk.etree.ElementTree import Element

lexicon = toolbox.xml('rotokas.dic')

def cv(s):
s = s.lower()
s = re.sub(r'[^a-z]', r'_', s)
s = re.sub(r'[aeiou]', r'V', s)
s = re.sub(r'[^V_]', r'C', s)
return (s)

def add_cv_field(entry):

for field in entry:
if field.tag == 'lx':
cv_field = Element('cv')
cv_field.text = cv(field.text)
entry.insert(1,cv_field)

print nltk.toolbox.to_sfm_string(lexicon[53])
add_cv_field(lexicon[53])
print nltk.toolbox.to_sfm_string(lexicon[53])

пʼятниця, 11 березня 2011 р.

Які будуть ще ідеї (Задача №8 Розділ №6)

Умова задачі: Word features can be very useful for performing document classification, since the words that appear in a document give a strong indication about what its semantic content is. However, many words occur very infrequently, and some of the most informative words in a document may never have occurred in our training data. One solution is to make use of a lexicon, which describes how different words relate to one another. Using WordNet lexicon, augment the movie review document classifier presented in this chapter to use features that generalize the words that appear in a document, making it more likely that they will match words found in the training data.

#Спробую врахувати вплив гіпернімів.
#Варіант 1.
#Згідно приладу з підручника:
import nltk
import random
from nltk.corpus import movie_reviews
from nltk.corpus import wordnet as wn
documents = [(list(movie_reviews.words(fileid)), category)
for category in movie_reviews.categories()
for fileid in movie_reviews.fileids(category)]
random.shuffle(documents)
all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
word_features = all_words.keys()[:2000]
def document_features(document):
document_words = set(document)
features = {}
for word in word_features:
features['contains(%s)' % word] = (word in document_words)
return features
featuresets = [(document_features(d), c) for (d,c) in documents]
train_set, test_set = featuresets[1800:], featuresets[:200]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print nltk.classify.accuracy(classifier, test_set)
#Отримую базову точність
#Якщо слово не попало в список з 2000 найчастотніших але його гіперніми зустрічаються серед слів корпуса то:
#Збільшую частоту для цих гіпернімів і знову беру 2000 найчастотніших
for word in all_words.keys():
if all_words[word]<70:
for synset in wn.synsets(word):
for hypernyms in synset.hypernyms():
for l_names in hypernyms.lemma_names:
all_words.inc(l_names)
word_features1 = all_words.keys()[:2000]
# Яких змін зазнав список найчастотніших слів
print len([word for word in word_features1 if word not in word_features])
word_features = all_words.keys()[:2000]
featuresets = [(document_features(d), c) for (d,c) in documents]
train_set, test_set = featuresets[1800:], featuresets[:200]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print nltk.classify.accuracy(classifier, test_set)

Результати погані точність не збільшилась, список змінився на 270 слів.
0.725
270
0.71
>>>
0.715
270
0.72
>>>
0.765
270
0.75
>>>
0.805
270
0.805
>>>
0.69
270
0.705
>>>
#Варіант 2.
#Згідно приладу з підручника:
import nltk
import random
from nltk.corpus import movie_reviews
from nltk.corpus import wordnet as wn
documents = [(list(movie_reviews.words(fileid)), category)
for category in movie_reviews.categories()
for fileid in movie_reviews.fileids(category)[:100]]
random.shuffle(documents)
all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
print len(all_words)
word_features = all_words.keys()[:200]
def document_features(document):
document_words = set(document)
features = {}
for word in word_features:
features['contains(%s)' % word] = (word in document_words)
return features
featuresets = [(document_features(d), c) for (d,c) in documents]
train_set, test_set = featuresets[80:], featuresets[:20]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print nltk.classify.accuracy(classifier, test_set)
#Отримую базову точність
#Якщо слово не попало в список найчастотніших але його гіперніми входять у цей список то:
#додати це слово до списку найчастотніших
for word in all_words.keys()[:300]:
if all_words[word]< all_words[all_words.keys()[200]]:
for synset in wn.synsets(word):
for hypernyms in synset.hypernyms():
for l_names in hypernyms.lemma_names:
if l_names in all_words.keys()[:200]:
if word not in word_features:
word_features.append(word)
#Скільки слів додалося до списку
print len(word_features)
featuresets = [(document_features(d), c) for (d,c) in documents]
train_set, test_set = featuresets[80:], featuresets[:20]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print nltk.classify.accuracy(classifier, test_set)
Слів додалося 37 а точність так і не збільшилась.

>>>
0.55
237
0.65
>>>
0.55
237
0.5
>>>
0.65
237
0.55
>>>
39768
0.5
237
0.5
>>>
0.65
237
0.65
>>>

Потрібні ще ідеї по використанню WordNet для рішення цієї задачі

понеділок, 21 лютого 2011 р.

Чому точність зменшилась(Задача №30, Розділ№5 )

Умова задачі:
Preprocess the Brown News data by replacing low-frequency words with UNK,
but leaving the tags untouched. Now train and evaluate a bigram tagger on this
data. How much does this help? What is the contribution of the unigram tagger
and default tagger now?

За умовою задачі очікуємо підвищення точності аналізатора.
Пишемо програму:
>>> import nltk
>>> from nltk.corpus import brown
>>> brown_tagged_sents = brown.tagged_sents(categories='news')
>>> vocab = nltk.FreqDist(brown.words(categories='news'))
# Важливий момент. Заміна всіх слів з частотоу =< 2 на "UNK"
>>> mapping = nltk.defaultdict(lambda: 'UNK')
>>> for v,t in brown.tagged_words(categories='news'):
if vocab[v]>2:
mapping[v],t = v,t
# Частотніші слова у словнику відобразили самих на себе
# Всі інші міняємо і результат зберігаємо у списку new_tagged_sents
>>> new_tagged_sents=[]
>>> for i in brown.tagged_sents(categories='news'):
new_tagged_sents.append([(mapping[v],t) for (v,t) in i])

# Поглянули чи дійсно відбулася заміна
>>> brown.tagged_sents(categories='news')[10]
[('It', 'PPS'), ('urged', 'VBD'), ('that', 'CS'), ('the', 'AT'), ('city', 'NN'), ('``', '``'), ('take', 'VB'), ('steps', 'NNS'), ('to', 'TO'), ('remedy', 'VB'), ("''", "''"), ('this', 'DT'), ('problem', 'NN'), ('.', '.')]
>>> new_tagged_sents[10]
[('It', 'PPS'), ('urged', 'VBD'), ('that', 'CS'), ('the', 'AT'), ('city', 'NN'), ('``', '``'), ('take', 'VB'), ('steps', 'NNS'), ('to', 'TO'), ('UNK', 'VB'), ("''", "''"), ('this', 'DT'), ('problem', 'NN'), ('.', '.')]

# будуємо набори даних для тренування тестування
# будуємо і тренуємо аналізатори
# оцінюємо точність їх роботи
>>> size = int(len(brown_tagged_sents) * 0.9)
>>> train_sents1 = brown_tagged_sents[:size]
>>> test_sents1 = brown_tagged_sents[size:]
>>> train_sents2 = new_tagged_sents[:size]
>>> test_sents2 = new_tagged_sents[size:]
>>> t0 = nltk.DefaultTagger('NN')
>>> t1 = nltk.UnigramTagger(train_sents1, backoff=t0)
>>> t2 = nltk.BigramTagger(train_sents1, backoff=t1)
>>> print t2.evaluate(test_sents1)
0.844911791089
>>> t1 = nltk.UnigramTagger(train_sents2, backoff=t0)
>>> t2 = nltk.BigramTagger(train_sents2, backoff=t1)
>>> print t2.evaluate(test_sents2)
0.836938104256
>>> t2 = nltk.BigramTagger(train_sents2, backoff=t0)
>>> print t2.evaluate(test_sents2)
0.722416027111

Результат несподіваний. Точність зменшилась.
Хто знає чому?

вівторок, 6 квітня 2010 р.

Задача №7 розділ №6 "Learning to Classify Text"

Умова задачі:
The dialogue act classifier assigns labels to individual posts, without considering
the context in which the post is found. However, dialogue acts are highly dependent
on context, and some sequences of dialogue act are much more likely than
others. For example, a ynQuestion dialogue act is much more likely to be answered
by a yanswer than by a greeting. Make use of this fact to build a consecutive classifier
for labeling dialogue acts. Be sure to consider what features might be useful.
See the code for the consecutive classifier for part-of-speech tags in Example 6-5
to get some ideas.

Варіант рішення:
В умові задачі міститься підказка.
Потрібно поєднати приклад 6-5 та приклад зі сторінки 235.
Ось що в мене вийшло.
>>> import nltk
>>> posts = nltk.corpus.nps_chat.xml_posts()
>>> history=[] # змінна для збереження типу попереднього повідомлення (post’s dialogue act type)
# функція для вилучення властивостей(ознак) з повідомлення
>>> def dialogue_act_features(post,i,history):
features = {}
for word in nltk.word_tokenize(post):
features['contains(%s)' % word.lower()] = True
if i == 0:
features["prev-class"] = ""
else:
features["prev-class"] = history[i-1] # тип попереднього повідомлення
return features
>>> featuresets=[]
>>> for i, post in enumerate(posts): # обробляємо пронумерований список повідомлень
featuresets.append((dialogue_act_features(post.text, i, history), post.get('class')))
history.append(post.get('class')) # список типів всіх повідомлень
>>> size = int(len(featuresets) * 0.1)
>>> train_set, test_set = featuresets[size:], featuresets[:size]
>>> classifier = nltk.NaiveBayesClassifier.train(train_set)
>>> print nltk.classify.accuracy(classifier, test_set)
0.644886363636

# а чому точність нижча ніж у прикладі зі сторінки 235, де тип попереднього повідомлення не враховувався?