4720
Використання Юнікоду при обробці текстів на мові Python
Лабораторная работа
Информатика, кибернетика и программирование
Мета робота Вивчення методів роботи з файлами на локальних дисках та з Інтернету. Використання Юнікоду при обробці текстів. Нормалізація текстів, стемінг, лематизація та сегментація. Короткі теоретичні відомості Виконання цієї лабо...
Украинкский
2012-11-25
852 KB
5 чел.
Виконання цієї лабораторної роботи необхідно розпочати з:
>>> from __future__ import division
>>> import nltk, re, pprint
Корпуси текстів та тексти з Інтернету є важливими джерелами даних для здійснення лінгвістичних досліджень. Звичайно, якщо дослідник має власноруч зібрані тексти, то потрібні засоби для доступу до них.
В результаті виконання лабораторної роботи будуть вирішені наступні питання:
Частина електронних книжок з Project Gutenberg розповсюджується разом з NLTK у вигляді корпуса текстів. Для використання інших текстів з цього проекту можна переглянути каталог 25000 електронних книжок за адресою http://www.gutenberg.org/catalog/ та встановити адресу (URL) потрібного текстового файлу в ASCII кодуванні. 90% текстів в Project Gutenberg є англійською мовою, але він включає також тексти більше ніж 50-ма іншими мовами (каталонська, китайська, датська, фінська, французька, німецька, італійська, португальська, іспанська…).
Текст за номером 2554 це переклад англійською Crime and Punishment(Злочин і кара), і отримати доступ до тексту можна наступним чином:
>>> from urllib import urlopen
>>> url = "http://www.gutenberg.org/files/2554/2554.txt"
>>> raw = urlopen(url).read()
>>> type(raw)
<type 'str'>
>>> len(raw)
1176831
>>> raw[:75]
'The Project Gutenberg EBook of Crime and Punishment, by Fyodor Dostoevsky\r\n'
Виконання read() займає певний час протягом якого відбувається завантаження цієї великої книжки. При використанні проксі сервера для доступу до Інтернету, при необхідності, його параметри потрібно вказати:
>>> proxies = {'http': 'http://www.someproxy.com:3128'}
>>> raw = urlopen(url, proxies=proxies).read()
Текст книжки збережений як значення змінної raw. Змінна raw містить стрічку довжиною 1,176,831 символів. (Перевірити тип змінної можна скориставшись type(raw).) Стрічка яка відповідає вмісту книжки містить багато не цікавої для аналізу інформації: пробіли, пусті стрічки, межі стрічки. Символи \r and \n , які є в тексті, це символи переводу каретки та початку нового рядка. Для подальшої роботи з текстом потрібно розділити текст на окремі слова та виділити розділові знаки. Такий процес називають токенізацією. При використанні програми токенізації з NLTK отримуємо список слів та розділових знаків.
>>> tokens = nltk.word_tokenize(raw)
>>> type(tokens)
<type 'list'>
>>> len(tokens)
255809
>>> tokens[:10]
['The', 'Project', 'Gutenberg', 'EBook', 'of', 'Crime', 'and', 'Punishment', ',', 'by']
Бібліотека NLTK використовувалась тільки на етапі токенізації і не використовувалась при доступі за адресою в Інтернеті та при читанні стрічки. Для подальшої роботи список перетворюється в NLTK текст і над ним можна здійснювати різноманітні операції:
>>> text = nltk.Text(tokens)
>>> type(text)
<type 'nltk.text.Text'>
>>> text[1020:1060]
['CHAPTER', 'I', 'On', 'an', 'exceptionally', 'hot', 'evening', 'early', 'in',
'July', 'a', 'young', 'man', 'came', 'out', 'of', 'the', 'garret', 'in',
'which', 'he', 'lodged', 'in', 'S', '.', 'Place', 'and', 'walked', 'slowly',
',', 'as', 'though', 'in', 'hesitation', ',', 'towards', 'K', '.', 'bridge', '.']
>>> text.collocations()
Katerina Ivanovna; Pulcheria Alexandrovna; Avdotya Romanovna; Pyotr
Petrovitch; Project Gutenberg; Marfa Petrovna; Rodion Romanovitch;
Sofya Semyonovna; Nikodim Fomitch; did not; Hay Market; Andrey
Semyonovitch; old woman; Literary Archive; Dmitri Prokofitch; great
deal; United States; Praskovya Pavlovna; Porfiry Petrovitch; ear rings
В побудованих колокаціях зустрічається Project Gutenberg, словосполучення, яке не міститься в тексті книжки. Завантажений текст з сайту містить метатекстову розмітку (інформацію про автора, про текст, про людей які готували електронний варіант та ін.). Ця інформація може бути, як на початку тексту так і в його кінці . Для роботи власне з текстом книжки потрібно в ручному режимі знайти межі цих додаткових даних і за допомогою зрізів доступитися до тексту.
>>> raw.find("PART I")
5303
>>> raw.rfind("End of Project Gutenberg's Crime")
1157681
>>> raw = raw[5303:1157681]
>>> raw.find("PART I")
0
Методи find() та rfind() ("пошук з кінця") допомагають знайти потрібні індекси для їх подальшого використання в зрізах. Значення зрізу переприсвоюється змінній raw.
Більшість текстів в Інтернеті є у вигляді HTML документів (файлів). Інтернет сторінки можна зберігати на диску у вигляді файлів і доступатися до них. Python також дозволяє працювати Інтернет сторінками безпосередньо використовуючи функцію urlopen. Для прикладу переглянемо текст з BBC News story з назвою Blondes to die out in 200 years:
>>> url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
>>> html = urlopen(url).read()
>>> html[:60]
'<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN'
Текст, який вивели на екран містить HTML розмітку (мета теги, JavaScript, форми , таблиці). Вилучення тексту з HTML файлу доволі розповсюджена задача, яка в NLTK вирішується за допомогою функції nltk.clean_html(). Ця функція обробляє HTML стрічку і повертає текст у вигляді зручному для подальшої обробки (токенізації).
>>> raw = nltk.clean_html(html)
>>> tokens = nltk.word_tokenize(raw)
>>> tokens
['BBC', 'NEWS', '|', 'Health', '|', 'Blondes', "'", 'to', 'die', 'out', ...]
Видалення іншої небажаної інформації проводиться в ручному режимі, аналогічно до попередніх прикладів з електронними книжками.
>>> tokens = tokens[96:399]
>>> text = nltk.Text(tokens)
>>> text.concordance('gene')
they say too few people now carry the gene for blondes to last beyond the next tw
t blonde hair is caused by a recessive gene . In order for a child to have blonde
to have blonde hair , it must have the gene on both sides of the family in the gra
there is a disadvantage of having that gene or by chance . They don ' t disappear
ondes would disappear is if having the gene was a disadvantage and I do not think
Виконати самостійно: Здійсніть аналіз результатів пошуку в Інтернеті наступного словосполучення "the of" . Чи можна аналогічним чином знайти найчастотніші колокації англійської мови?
Блогосфера важливе джерело текстів, як формальних так і не формальних. З допомогою бібліотеки Python Universal Feed Parser, http://feedparser.org/, можна отримати доступ до вмісту блогів, як показано у наступному прикладі:
>>> import feedparser
>>> llog = feedparser.parse("http://languagelog.ldc.upenn.edu/nll/?feed=atom")
>>> llog['feed']['title']
u'Language Log'
>>> len(llog.entries)
15
>>> post = llog.entries[2]
>>> post.title
u"He's My BF"
>>> content = post.content[0].value
>>> content[:70]
u'<p>Today I was chatting with three of our visiting graduate students f'
>>> nltk.word_tokenize(nltk.html_clean(content))
>>> nltk.word_tokenize(nltk.clean_html(llog.entries[2].content[0].value))
[u'Today', u'I', u'was', u'chatting', u'with', u'three', u'of', u'our', u'visiting',
u'graduate', u'students', u'from', u'the', u'PRC', u'.', u'Thinking', u'that', u'I',
u'was', u'being', u'au', u'courant', u',', u'I', u'mentioned', u'the', u'expression',
u'DUI4XIANG4', u'\u5c0d\u8c61', u'("', u'boy', u'/', u'girl', u'friend', u'"', ...]
Для читання локальних файлів необхідно використовувати вбудовану функцію Python open() та read() метод. Якщо існує файл document.txt, то змінній raw можна присвоїти його вміст:
>>> f = open('document.txt')
>>> raw = f.read()
Якщо інтерпретатор не знайде файл, він видасть помилку, подібну до наступної:
>>> f = open('document.txt')
Traceback (most recent call last):
File "<pyshell#7>", line 1, in -toplevel-
f = open('document.txt')
IOError: [Errno 2] No such file or directory: 'document.txt'
Для перевірки чи дійсно файл є в потрібній директорії у графічному інтерфейсі IDLE використовується команда Open з пункту меню File. Можна також перевірити вміст директорії наступним чином:
>>> import os
>>> os.listdir('.')
Інша можлива проблема при читанні текстових файлів це різні способи маркування нового рядка у файлах різних операційних систем. При виклику функція open() може містити другий параметр для контролю відкривання файлу open('document.txt', 'rU') („r” файл для читання, ”U” universal дозволяє ігнорувати різні способи, які використовуються для маркування нового рядка).
Для читання вмісту файлу можна використати багато різних методів. Метод read() , використаний до обєкту файл (f), читає вміст файлу і представляє його стрічкою:
>>> f.read()
'Time flies like an arrow.\nFruit flies like a banana.\n'
Символ '\n' це символ нового рядка.
Файл можна читати стрічка за стрічкою, використовуючи for-цикл і використовувати зріз [:-1] або метод strip() для видалення символів нового рядка:
>>> f = open('document.txt', 'rU')
>>> for line in f:
... print line.strip()
Time flies like an arrow.
Fruit flies like a banana.
За допомогою цих методів також можна доступитися і до файлів з корпусів, які розповсюджуються з NLTK. Потрібно використати nltk.data.find() для одержання шляху до будь-якого файлу корпуса, а далі відкривати та читати файл, як показано:
>>> path = nltk.data.find('corpora/gutenberg/melville-moby_dick.txt')
>>> raw = open(path, 'rU').read()
Для вводу тексту з клавіатури (при взаємодії користувача з програмою) потрібно використати функцію raw_input(). Після збереження введеного тексту у змінній з ним можна працювати як зі звичайною стрічкою.
>>> s = raw_input("Enter some text: ")
Enter some text: On an exceptionally hot evening early in July
>>> print "You typed", len(nltk.word_tokenize(s)), "words."
You typed 8 words.
На Рис 1. представлена узагальнена, на основі вищевикладеного, схема початкової обробки текстів природною мовою. Початкова обробка текстів природною мовою може містити наступні етапи: відкривання та читання тексту з Інтернету, видалення розмітки, токенізація, перетворення тексту в NLTK текст.
Коли відбувається доступ до вмісту файлу чи вмісту вебсторінки і коли видаляється HTML розмітка то відбувається обробка стрічки:
>>> raw = open('document.txt').read()
>>> type(raw)
<type 'str'>
Результати токенізації список в який входять всі слова з тексту. Нормалізація та сортування цього списку приводить до отримання інших списків:
>>> tokens = nltk.word_tokenize(raw)
>>> type(tokens)
<type 'list'>
>>> words = [w.lower() for w in tokens]
>>> type(words)
<type 'list'>
>>> vocab = sorted(set(words))
>>> type(vocab)
<type 'list'>
Тип обєкту визначає, які операції можуть бути здійснені з цим обєктом. Наприклад, можна додавати елементи до списку але не можна до стрічки:
>>> vocab.append('blog')
>>> raw.append('blog')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'append'
Стрічки та списки можна поєднувати з іншими стрічками та списками, але не можна поєднувати стрічки зі списками:
>>> query = 'Who knows?'
>>> beatles = ['john', 'paul', 'george', 'ringo']
>>> query + beatles
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'list' objects
Програми обробки природної мови повинні працювати з різними мовами та з різними наборами символів. Твердження «1 байт = 1 символ» є застарілим і в переважній більшості практичних випадків є хибним. В англомовному світі переважно використовується ASCII кодування символів. В Європі використовується розширений Latin набір символів, який містить такі символи датської та норвежської, як "ø", угорської - "ő", іспанської та бретонської -"ñ" та "ň" чеської та словацької мов. Розглянемо, як використовується Unicode при обробці текстів, що містять відмінні від ASCII символи.
Юнікод, (англ. Unicode) це промисловий стандарт розроблений, щоб зробити можливим для текстів і символів (графічних знаків) всіх писемних систем світу узгоджене представлення (репрезентацію) і обробку компютерами. Юнікод підтримує більш ніж мільйон символів. Кожному символу ставиться у відповідність число, яке називають кодовою точкою. В Python кодові точки записуються у вигляді \uXXXX , де XXXX - чотири символи шістнадцяткового числа.
В межах програми обробка стрічок Unicode відбувається аналогічно до звичайних стрічок. Однак, коли Unicode символи зберігаються у файл або виводяться на екран, вони повинні бути закодовані, як потік байт. Деякі кодування (такі як ASCII та Latin-2) використовують один байт для представлення одної кодової точки і відповідно підтримують невеликий набір символів Unicode, достатній для одної мови. Інші кодування (такі як UTF-8) використовують послідовності байтів і можуть представити весь набір символів Unicode.
Текст у файлах є в певному кодування і потрібен певний механізм для перетворення його до Unicode. Такий механізм називають декодування. Навпаки записати Unicode символи у файл або вивести на екран можна тільки попередньо перетворивши їх у потрібне кодування. Таке перетворення називають кодуванням. Рис.2.
Рис.2. Кодування і декодування Unicode.
From a Unicode perspective, characters are abstract entities which can be realized as one or more glyphs. Only glyphs can appear on a screen or be printed on paper. A font is a mapping from characters to glyphs.
Нехай існують невеликі текстові файли відомого кодування. Файл Ukrainian1-Cyrillic (перше речення декларації прав людини) з текстом українською мовою в кодуванні Сyrillic та файл polish-lat2.txt з текстом польською мовою у кодуванні Latin-2 (ISO-8859-2). За допомогою функції nltk.data.find() знайдемо місцезнаходження цих файлів:
>>> path = nltk.data.find('corpora/udhr/Ukrainian1-Cyrillic')
>>> path1 = nltk.data.find('corpora/unicode_samples/polish-lat2.txt')
Модуль Python codecs забезпечує функції читання кодованих даних в Unicode стрічку і запису Unicode стрічки в кодовану форму. Функція codecs.open() потребує параметра кодування файлу для читання чи запису. Перед використанням модуля потрібно його імпортувати і при читанні вказувати тип кодування:
>>> import codecs
>>> f = codecs.open(path, encoding='cyrillic')
>>> f1 = codecs.open(path1, encoding='latin2')
Список параметрів кодування модуля codecs, можна переглянути за адресою http://docs.python.org/lib/standard-encodings.html. Для запису даних у файл потрібно скористатись наступною конструкцією: f = codecs.open(path, 'w', encoding='utf-8').
Текст прочитаний з f буде в Unicode. Для представлення цього тексту на екрані потрібно його закодувати. В Python кодування unicode_escape перетворює всі не ASCII символи в їх представлення \uXXXX . Кодові точки вище ASCII 0=127 але до 256 представляються у двоцифровій формі \хХХ.
>>> for line in f1:
... line = line.strip()
... print line.encode('unicode_escape')
Pruska Biblioteka Pa\u0144stwowa. Jej dawne zbiory znane pod nazw\u0105
"Berlinka" to skarb kultury i sztuki niemieckiej. Przewiezione przez
Niemc\xf3w pod koniec II wojny \u015bwiatowej na Dolny \u015al\u0105sk, zosta\u0142y
odnalezione po 1945 r. na terytorium Polski. Trafi\u0142y do Biblioteki
Jagiello\u0144skiej w Krakowie, obejmuj\u0105 ponad 500 tys. zabytkowych
archiwali\xf3w, m.in. manuskrypty Goethego, Mozarta, Beethovena, Bacha.
В першій стрічці послідовність \u0144, починається з символу \u. Відповідний Unicode символ буде відображатися на екрані, як ń. В третій стрічці є символ \xf3, який відповідає ó, оскільки він є з проміжку 128-255.
В Python, Unicode стрічку записують вказавши на початку символ u, ( u'hello' Юнікод стрічка). Довільний Unicode символ може бути визначений всередині Unicode стрічки використовуючи представлення \uXXXX . Можна знайти числове значення кодової точки використовуючи функцію ord():
>>> ord('a')
97
>>> ord('а')
224
В шістнадцятковій формі 97 це 0061 а 224 це 00Е0 , що дозволяє представити ці символи відповідними кодовими точками:
>>> a = u'\u0061'
>>> b = u'\u00E0'
>>> a
u'a'
>>> print a
a
>>> b
u'\xe0'
>>> print b
à
При роботі з символами певного локального кодування, для можливості використання стандартних методів вводу і редагування стрічок у файлі Python потрібно записати стрічку '# -*- coding: <coding> -*-' першим або другим рядком у файлі. <coding> це стрічка така як 'latin-1', 'big5' or 'utf-8' (Рис.4.).
В попередніх лабораторних роботах перед обробкою тексту всі літери слів перетворювались у малі літери (set(w.lower() for w in text).). Використовуючи lower() текст нормалізується для усунення розбіжностей між «The» та «the». Часто потрібно відділити від слів афікси. Така задача називається стемінгом. Для перевірки чи словоформа є словом у словнику потрібно здійснити лематизацію (отримати канонічну форму лексеми):
>>> raw = """DENNIS: Listen, strange women lying in ponds distributing swords
... is no basis for a system of government. Supreme executive power derives from
... a mandate from the masses, not from some farcical aquatic ceremony."""
>>> tokens = nltk.word_tokenize(raw)
NLTK містить декілька стандартних програм для здійснення стемінгу. Porter та Lancaster програми здійснюють стемінг (відкидання афіксів) на основі наборів правил. Porter стемер коректно обробляє слово lying (lie), а Lancaster стемер це слово обробляє з помилкою.
>>> porter = nltk.PorterStemmer()
>>> lancaster = nltk.LancasterStemmer()
>>> [porter.stem(t) for t in tokens]
['DENNI', ':', 'Listen', ',', 'strang', 'women', 'lie', 'in', 'pond',
'distribut', 'sword', 'is', 'no', 'basi', 'for', 'a', 'system', 'of', 'govern',
'.', 'Suprem', 'execut', 'power', 'deriv', 'from', 'a', 'mandat', 'from',
'the', 'mass', ',', 'not', 'from', 'some', 'farcic', 'aquat', 'ceremoni', '.']
>>> [lancaster.stem(t) for t in tokens]
['den', ':', 'list', ',', 'strange', 'wom', 'lying', 'in', 'pond', 'distribut',
'sword', 'is', 'no', 'bas', 'for', 'a', 'system', 'of', 'govern', '.', 'suprem',
'execut', 'pow', 'der', 'from', 'a', 'mand', 'from', 'the', 'mass', ',', 'not',
'from', 'som', 'farc', 'aqu', 'ceremony', '.']
Результати стемінгу не є однозначними і тому стемер вибирається той, який дає кращі результати для визначеної задачі. Porter стемер найкраще використовувати для індексування деякого тексту для підтримки пошукових операцій (забезпечення пошуку всіх форм слова). Порівняйте результати побудови конкордансу для слова “lie”, без індексування та з попереднім індексуванням (class IndexedText(object)).
class IndexedText(object): def __init__(self, stemmer, text): self._text = text self._stemmer = stemmer self._index = nltk.Index((self._stem(word), i) for (i, word) in enumerate(text)) def concordance(self, word, width=40): key = self._stem(word) wc = width/4 # words of context for i in self._index[key]: lcontext = ' '.join(self._text[i-wc:i]) rcontext = ' '.join(self._text[i:i+wc]) ldisplay = '%*s' % (width, lcontext[-width:]) rdisplay = '%-*s' % (width, rcontext[:width]) print ldisplay, rdisplay def _stem(self, word): return self._stemmer.stem(word).lower() |
>>> porter = nltk.PorterStemmer()
>>> grail = nltk.corpus.webtext.words('grail.txt')
>>> text = IndexedText(porter, grail)
>>> text.concordance('lie')
r king ! DENNIS : Listen , strange women lying in ponds distributing swords is no
beat a very brave retreat . ROBIN : All lies ! MINSTREL : [ singing ] Bravest of
Nay . Nay . Come . Come . You may lie here . Oh , but you are wounded !
doctors immediately ! No , no , please ! Lie down . [ clap clap ] PIGLET : Well
ere is much danger , for beyond the cave lies the Gorge of Eternal Peril , which
you . Oh ... TIM : To the north there lies a cave -- the cave of Caerbannog --
h it and lived ! Bones of full fifty men lie strewn about its lair . So , brave k
not stop our fight ' til each one of you lies dead , and the Holy Grail returns t
>>> grail = nltk.Text(nltk.corpus.webtext.words('grail.txt'))
>>> grail.concordance('lie')
Nay . Nay . Come . Come . You may lie here . Oh , but you are wounded !
doctors immediately ! No , no , please ! Lie down . [ clap clap ] PIGLET : Well
h it and lived ! Bones of full fifty men lie strewn about its lair . So , brave k
WordNet лематизатор видаляє афікси тільки якщо слово, яке отримується в процесі лематизації є в його словнику. Ця процедура робить лематизацію повільнішою за стемінг. Слово lying оброблено з помилкою, але слово women перетворено у woman.
>>> wnl = nltk.WordNetLemmatizer()
>>> [wnl.lemmatize(t) for t in tokens]
['DENNIS', ':', 'Listen', ',', 'strange', 'woman', 'lying', 'in', 'pond',
'distributing', 'sword', 'is', 'no', 'basis', 'for', 'a', 'system', 'of',
'government', '.', 'Supreme', 'executive', 'power', 'derives', 'from', 'a',
'mandate', 'from', 'the', 'mass', ',', 'not', 'from', 'some', 'farcical',
'aquatic', 'ceremony', '.']
WordNet лематизатор можна використовувати для побудови словника деякого тексту. В результаті можна отримати список лем.
Токенізація це найбільш загальний випадок сегментації.
Робота з текстами на рівні окремих слів часто передбачає можливість поділу тексту на окремі речення. Деякі корпуси забезпечують можливість доступу на рівні окремих речень. В наступному прикладі визначається середня довжина речення в корпусі Brown:
>>> len(nltk.corpus.brown.words()) / len(nltk.corpus.brown.sents())
20.250994070456922
У випадку якщо текст представлений, як послідовність символів, то перед токенізацією необхідно поділити текст на окремі речення. NLTK забезпечує таку можливість за допомогою програми Punkt сегментації тексту на речення. У наступному прикладі показано використання цієї програми:
>>> sent_tokenizer=nltk.data.load('tokenizers/punkt/english.pickle')
>>> text = nltk.corpus.gutenberg.raw('chesterton-thursday.txt')
>>> sents = sent_tokenizer.tokenize(text)
>>> pprint.pprint(sents[171:181])
['"Nonsense!',
'" said Gregory, who was very rational when anyone else\nattempted paradox.',
'"Why do all the clerks and navvies in the\nrailway trains look so sad and tired,...',
'I will\ntell you.',
'It is because they know that the train is going right.',
'It\nis because they know that whatever place they have taken a ticket\nfor that ...',
'It is because after they have\npassed Sloane Square they know that the next stat...',
'Oh, their wild rapture!',
'oh,\ntheir eyes like stars and their souls again in Eden, if the next\nstation w...'
'"\n\n"It is you who are unpoetical," replied the poet Syme.']
Зауважимо, що цей приклад це одне речення, повідомлення про промову Mr Lucian Gregory. Промова містить декілька речень і доцільно їх виділити, як окремі стрічки.
Сегментація тексту на речення є складною процедурою, оскільки навіть такий явний розділовий знак межі речення, як крапка, може використовуватись у скороченнях, абревіатурах та їх комбінаціях.
Для деяких писемностей токенізація тексту є складною процедурою оскільки складно встановити межі слова. Наприклад три символи стрічки китайською 爱国人 (ai4 "love" (verb), guo3 "country", ren2 "person") можуть бути поділені і як爱国 / 人, "country-loving person" і як爱 / 国人, "love country-person."
Подібна проблема виникає при обробці усного мовлення, коли слухач змушений сегментувати безперервний потік звуків на окремі слова. Особливо складно це зробити, якщо слова є попередньо невідомі (при вивченні мови або якщо немовля сприймає мову батьків). Розглянемо наступний приклад в якому видалені межі слів:
(1) |
a. doyouseethekitty b. seethedoggy c. doyoulikethekitty d. likethedoggy |
Спочатку потрібно описати задачу: потрібно знайти спосіб відділити текст від власне сегментації. Цього можна досягнути промарнувавши кожен символ булевим значенням, яке буде вказувати, що після символу йде межа слова. Подібний результат може отримати учень на слух сприймаючи паузи при вимові. Результат представимо наступним чином. seg1 початкова сегментація, seg2 - результуюча сегментація:
>>> text = "doyouseethekittyseethedoggydoyoulikethekittylikethedoggy"
>>> seg1 = "0000000000000001000000000010000000000000000100000000000"
>>> seg2 = "0100100100100001001001000010100100010010000100010010000"
Стрічки сегментації містять нулі та одиниці та їх довжина менша на один символ від початкового тексту (текст довжиною N елементів може мати тільки N-1 місць розділу). Функція segment() в наступному прикладі дозволяє отримати оригінальний сегментований текст на основі поданого вище представлення.
def segment(text, segs): words = [] last = 0 for i in range(len(segs)): if segs[i] == '1': words.append(text[last:i+1]) last = i+1 words.append(text[last:]) return words |
>>> text = "doyouseethekittyseethedoggydoyoulikethekittylikethedoggy"
>>> seg1 = "0000000000000001000000000010000000000000000100000000000"
>>> seg2 = "0100100100100001001001000010100100010010000100010010000"
>>> segment(text, seg1)
['doyouseethekitty', 'seethedoggy', 'doyoulikethekitty', 'likethedoggy']
>>> segment(text, seg2)
['do', 'you', 'see', 'the', 'kitty', 'see', 'the', 'doggy', 'do', 'you',
'like', 'the', kitty', 'like', 'the', 'doggy']
Тепер задачу сегментації можна розглядати як пошукову задачу: знайти стрічку бітів, яка приводить до коректної сегментації стрічки тексту на слова. Учень запамятовує слова і зберігає їх у словнику. Маючи відповідний словник, можливо здійснити реконструкцію початкового тексту, як послідовність лексичних одиниць. Побудуємо цільову функцію, значення якої буде оптимізоване на основі розміру словника та розміру інформації необхідної для реконструкції початкового тексту зі словника Рис.3..
Рис.3. Визначення цільової функції:
На основі гіпотетично сегментованого початкового тексту(ліва колонка ) отримуємо словник та таблицю (середня колонка), які забезпечують реконструкцію початкового тексту. Загальна кількість символів у лексиконі (з врахуванням символу границі слова) та сума елементів в таблиці служать числовим значенням для оцінки якості сегментації (права колонка). Менше, за знайдене, значення вказує на кращий варіант сегментації.
Для розрахунку цієї цільової функції можна використати наступну функцію:
def evaluate(text, segs): words = segment(text, segs) text_size = len(words) lexicon_size = len(' '.join(list(set(words)))) return text_size + lexicon_size |
>>> text = "doyouseethekittyseethedoggydoyoulikethekittylikethedoggy"
>>> seg1 = "0000000000000001000000000010000000000000000100000000000"
>>> seg2 = "0100100100100001001001000010100100010010000100010010000"
>>> seg3 = "0000100100000011001000000110000100010000001100010000001"
>>> segment(text, seg3)
['doyou', 'see', 'thekitt', 'y', 'see', 'thedogg', 'y', 'doyou', 'like',
'thekitt', 'y', 'like', 'thedogg', 'y']
>>> evaluate(text, seg3)
46
>>> evaluate(text, seg2)
47
>>> evaluate(text, seg1)
63
Останній крок це власне пошук послідовності нулів та одиниць, яка максимізує цю цільову функцію. Потрібно зазначити, що найкращий варіант сегментації містить «слово» thekitty, оскільки недостатньо даних для подальшої сегментації.
from random import randint def flip(segs, pos): return segs[:pos] + str(1-int(segs[pos])) + segs[pos+1:] def flip_n(segs, n): for i in range(n): segs = flip(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 = flip_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) return segs |
>>> text = "doyouseethekittyseethedoggydoyoulikethekittylikethedoggy"
>>> seg1 = "0000000000000001000000000010000000000000000100000000000"
>>> anneal(text, seg1, 5000, 1.2)
60 ['doyouseetheki', 'tty', 'see', 'thedoggy', 'doyouliketh', 'ekittylike', 'thedoggy']
58 ['doy', 'ouseetheki', 'ttysee', 'thedoggy', 'doy', 'o', 'ulikethekittylike', 'thedoggy']
56 ['doyou', 'seetheki', 'ttysee', 'thedoggy', 'doyou', 'liketh', 'ekittylike', 'thedoggy']
54 ['doyou', 'seethekit', 'tysee', 'thedoggy', 'doyou', 'likethekittylike', 'thedoggy']
53 ['doyou', 'seethekit', 'tysee', 'thedoggy', 'doyou', 'like', 'thekitty', 'like', 'thedoggy']
51 ['doyou', 'seethekittysee', 'thedoggy', 'doyou', 'like', 'thekitty', 'like', 'thedoggy']
42 ['doyou', 'see', 'thekitty', 'see', 'thedoggy', 'doyou', 'like', 'thekitty', 'like', 'thedoggy']
'0000100100000001001000000010000100010000000100010000000'
Останній крок це власне пошук послідовності нулів та одиниць, яка максимізує цю цільову функцію. Потрібно зазначити, що найкращий варіант сегментації містить «слово» thekitty, оскільки недостатньо даних для подальшої сегментації.
Відомі два шляхи вивести на екран вміст обєктів:
>>> word = 'cat'
>>> sentence = """hello
... world"""
>>> print word
cat
>>> print sentence
hello
world
>>> word
'cat'
>>> sentence
'hello\nworld'
Команда print дозволяє отримати найбільш придатне, для читання, представлення обєкту. Другий спосіб ввести назву змінної в командному рядку показує стрічку, яка може бути використана для оновлення цього обєкту. Важливо зрозуміти, ці два результати це стрічки, виведені на екран для користувача і не дають ніякої інформації про фактичне внутрішнє представлення обєкта.
Існують інші шляхи для виведення на екран обєкту, як стрічки символів. Вони можуть також використовуватися і для експортування даних у файл певного формату, який буде використаний зовнішньою програмою.
Форматовані вихідні дані звичайно містять комбінацію змінних та наперед визначених стрічок. Наприклад результати частотного розподілу можна представити як:
>>> fdist = nltk.FreqDist(['dog', 'cat', 'dog', 'cat', 'dog', 'snake', 'dog', 'cat'])
>>> for word in fdist:
... print word, '->', fdist[word], ';',
dog -> 4 ; cat -> 3 ; snake -> 1 ;
Уникнути небажаних пробілів та покращити читабельність програми дозволяє використання виразів форматування.
>>> for word in fdist:
... print '%s->%d;' % (word, fdist[word]),
dog->4; cat->3; snake->1;
Тестування стрічки, яка містить вираз форматування дозволяє глибше зрозуміти механізм форматованого виводу.
>>> '%s->%d;' % ('cat', 3)
'cat->3;'
>>> '%s->%d;' % 'cat'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
Спеціальні символи %s та %d вказують на тимчасові місця (позиції) для стрічок та цілих (десяткових чисел). Ці символи записуються всередині стрічки і використовується оператор % для їх комбінування, подібно до наступного прикладу:
>>> '%s->' % 'cat'
'cat->'
>>> '%d' % 3
'3'
>>> 'I want a %s right now' % 'coffee'
'I want a coffee right now'
У виразі можуть бути багато тимчасових позицій але після оператора % потрібно визначити кортеж з такою самою кількістю значень.
>>> "%s wants a %s %s" % ("Lee", "sandwich", "for lunch")
'Lee wants a sandwich for lunch'
Значення для тимчасових позицій можна вказувати не безпосередньо, а наприклад так як в зроблено у наступному циклі:
>>> template = 'Lee wants a %s right now'
>>> menu = ['sandwich', 'spam fritter', 'pancake']
>>> for snack in menu:
... print template % snack
...
Lee wants a sandwich right now
Lee wants a spam fritter right now
Lee wants a pancake right now
Символи %s та %d називають специфікаторами формату. Вони починаються з символу % і завершуються символом таким як s (для стрічки) чи d (для цілих чисел). Стрічка, яка містить специфікатори формату називають стрічкою форматування. Поєднання стрічки форматування з оператором % та кортежем значень утворює стрічку виразу форматування.
Стрічка форматування виводить вихідні дані на екран або на сторінку довільної ширини такої як %s та %d. Можна визначити ширину як %6s, що дозволить отримати стрічку доповнену пробілами до ширини 6 символів. Значення змінної буде вирівняне по правому краю, а у випадку використання символу «мінус » - по лівому краю. У випадку, коли наперед невідомо, якої ширини може бути змінна в стрічці форматування використовується зірочка, яка потім визначається за допомогою окремої змінної.
>>> '%6s' % 'dog'
' dog'
>>> '%-6s' % 'dog'
'dog '
>>> width = 6
>>> '%-*s' % (width, 'dog')
'dog '
>>> '%*s' % (15, "Monty Python")
' Monty Python'
Інший спосіб контролю символів використовується для цілих чисел та чисел з плаваючою крапкою. Оскільки символ % має спеціальне значення в стрічці форматування то при необхідності використати цей символ у вихідних даних потрібно додати перед ним ще один символ % .
>>> count, total = 3205, 9375
>>> "accuracy for %d words: %2.4f%%" % (total, 100 * count / total)
'accuracy for 9375 words: 34.1867%'
Для представлення даних у вигляді таблиці також необхідно використовувати стрічки форматування. В наступному прикладі показано, яким чином працює функція представлення у вигляді таблиці результатів умовного частотного розподілу.
def tabulate(cfdist, words, categories): print '%-16s' % 'Category', for word in words: # column headings print '%6s' % word, for category in categories: print '%-16s' % category, # row heading for word in words: # for each word print '%6d' % cfdist[category][word], # print table cell print # end the row |
>>> from nltk.corpus import brown
>>> cfd = nltk.ConditionalFreqDist(
... (genre, word)
... for genre in brown.categories()
... for word in brown.words(categories=genre))
>>> genres = ['news', 'religion', 'hobbies', 'science_fiction', 'romance', 'humor']
>>> modals = ['can', 'could', 'may', 'might', 'must', 'will']
>>> tabulate(cfd, modals, genres)
Category can could may might must will
news 93 86 66 38 50 389
religion 82 59 78 12 54 71
hobbies 268 58 131 22 83 264
science_fiction 16 49 4 12 8 16
romance 74 193 11 51 45 43
humor 16 30 8 8 9 13
Використовуючи вираз width = max(len(w) for w in words) можна автоматично налаштувати ширину колонки, а кома в кінці операторів print не дозволяє колонкам перекриватися між собою.
Наступний приклад показує, яким чином відкрити файл output.txt для запису, та зберегти в ньому результати роботи програми.
>>> output_file = open('output.txt', 'w')
>>> words = set(nltk.corpus.genesis.words('english-kjv.txt'))
>>> for word in sorted(words):
... output_file.write(word + "\n")
Виконати самостійно:
Перевірити навіщо перед записом у файл до кожного рядка додається символ \n . Спробуйте також використати word + "\r\n" вираз. Що станеться, якщо опустити ці символи output_file.write(word)?
При записі у файл не текстових даних їх попередньо потрібно перетворити у стрічку. Перетворення можна зробити використавши стрічку форматування, як було показано вище або зробити перетворення наступним чином:
>>> len(words)
2789
>>> str(len(words))
'2789'
>>> output_file.write(str(len(words)) + "\n")
>>> output_file.close()
Текстові вихідні дані часто корисно обробити для покращення їх відображення. Розглянемо наступний приклад зі складним виглядом оператора print:
>>> saying = ['After', 'all', 'is', 'said', 'and', 'done', ',',
... 'more', 'is', 'said', 'than', 'done', '.']
>>> for word in saying:
... print word, '(' + str(len(word)) + '),',
After (5), all (3), is (2), said (4), and (3), done (4), , (1), more (4), is (2), said (4), than (4), done (4), . (1),
Аналогічний результат, але з фіксованою довжиною рядка можна отримати з використанням модуля textwrap :
>>> from textwrap import fill
>>> format = '%s (%d),'
>>> pieces = [format % (word, len(word)) for word in saying]
>>> output = ' '.join(pieces)
>>> wrapped = fill(output)
>>> print wrapped
After (5), all (3), is (2), said (4), and (3), done (4), , (1), more
(4), is (2), said (4), than (4), done (4), . (1),
from __future__ import division |
Імпортування модуля для роботи з числами з плаваючою крапкою |
urlopen(url).read() |
Функція відкривання та читання файла за адресою url |
nltk.word_tokenize(raw) |
Токенізація тексту raw |
nltk.Text(tokens) |
Перетворення тексту tokens в NLTK текст |
raw.find |
Знайти стрічку в raw |
raw.rfind |
Знайти стрічку в raw. Пошук здійснювати з кінця. |
nltk.clean_html(html) |
Очистити текст від html розмітки. |
open('document.txt') |
Відкрити файл |
f.read() |
Прочитати файл |
os.listdir('.') |
Встановити вміст директорії |
line.strip() |
Обрізати стрічку по останньому символу |
nltk.data.find('corpora/gutenberg/melville-moby_dick.txt') |
Знайти місцезнаходження файлу |
open(path, 'rU').read() |
Відкрити файл за вказаним шляхом для читання і прочитати його. Різні способи маркування нового рядка ігноруються |
raw_input("Enter some text: ") |
Ввести текст з клавіатури |
codecs.open(path1, encoding='latin2') |
|
ord('a') |
|
line.encode('unicode_escape') |
|
nltk.PorterStemmer() |
Модуль Porter стемера |
nltk.LancasterStemmer() |
Модуль Lancaster стемера |
nltk.WordNetLemmatizer() |
Модуль WordNet лематизатора |
nltk.data.load('tokenizers/punkt/english.pickle') |
|
sent_tokenizer.tokenize(text) |
Сегментувати текст на окремі речення |
open('output.txt', 'w') |
Відкрити файл для запису |
output_file.write(word + "\n") |
Записати у файл word та символ початку нового рядка |
1. Ознайомитися з теоретичними відомостями.
2. Виконати приклади, які використовуються в теоретичних відомостях.
3. Виконати наступні вправи.
|
>>> sent = ['The', 'dog', 'gave', 'John', 'the', 'newspaper'] >>> result = [] >>> for word in sent: ... word_len = (word, len(word)) ... result.append(word_len) >>> result [('The', 3), ('dog', 3), ('gave', 4), ('John', 4), ('the', 3), ('newspaper', 9)] |
|
>>> words = ['attribution', 'confabulation', 'elocution', ... 'sequoia', 'tenacious', 'unidirectional'] >>> vsequences = set() >>> for word in words: ... vowels = [] ... for char in word: ... if char in 'aeiou': ... vowels.append(char) ... vsequences.add(''.join(vowels)) >>> sorted(vsequences) ['aiuio', 'eaiou', 'eouio', 'euoia', 'oauaio', 'uiieioa'] |
4. Підготувати і оформити звіт.
Варіант |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Номери завдань |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
|
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
|
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
|
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
|
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
|
8 |
6 |
13 |
11 |
8 |
6 |
13 |
11 |
8 |
6 |
13 |
8 |
6 |
13 |
11 |
|
9 |
10 |
12 |
9 |
10 |
12 |
9 |
10 |
12 |
9 |
10 |
12 |
9 |
10 |
12 |
|
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
|
Варіант |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
Номери завдань |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
2 |
|
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
3 |
|
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
4 |
|
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
5 |
|
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
7 |
|
13 |
8 |
6 |
11 |
13 |
8 |
6 |
11 |
13 |
8 |
6 |
13 |
8 |
6 |
11 |
|
9 |
10 |
12 |
9 |
10 |
12 |
9 |
10 |
12 |
9 |
10 |
12 |
9 |
10 |
12 |
|
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
14 |
15 |
15 |
|
А также другие работы, которые могут Вас заинтересовать | |||
37026. | ПУЛЬТОВОЕ ОБОРУДОВАНИЕ | 1023.5 KB | |
Содержание I Техническое описание лабораторного модуля Назначение 4 Технические характеристики 4 Состав и конструктивное исполнение 4 Подготовка к работе 5 II Технические характеристики и основы программирования таймера Н5СХ Общие сведения 6 Подключение входных и Выходных сигналов 7 Программирование таймера Н5СХ 8 III Технические характеристики и основы программирования счётчика Н7СХ Общие сведения 16 Подключение входных и выходных сигналов 17 Программирование счётчика Н7СХ 18 IV Технические характеристики и основы... | |||
37027. | Весна пришла. Сценарий класного часа | 17.01 KB | |
Настя: Ребята мы сегодня поговорим об одном из прекрасных времен года о весне. Далее Настя рассказывает о праздниках весны какие праздники знают дети Рассказ о космонавте Ю. Настя Аня Полет Белки и Стрелки в космос был осуществлен 19 августа 1960 года когда одна из ракет стартовавшая с космодрома Байконур вывела на орбиту космический корабль Восток с собаками на борту. Настя Какой бывает весна 3. | |||
37029. | Что нами движет при выборе профессии. Классный час | 18.3 KB | |
Как вы думаете о чем сегодня пойдет речь Сегодня разговор наш будет о профессиональном самоопределении процессе и результате самостоятельного и сознательного выбора профессии. Означает ли это что к выбору профессии можно идти путем проб и ошибок А может надо сознательно готовить себя к будущей профессиональной деятельности Что мы имеем в виду когда произносим слово âпрофессияâ Профессия понятие многозначное: общность всех людей занятых данным видом труда. Выбор профессии можно отнести к самым сложным от... | |||
37031. | В гостях у светофора | 26.16 KB | |
Игра Разрешается запрещается. Ребята давайте вспомним правила дорожного движения и поиграем в игру Разрешается запрещается. Если вы считаете что моё высказывание верное то говорите разрешается а если не верное то запрещается. Играть на мостовойзапрещается. | |||