Знайомство з Python, машинним навчанням та бібліотекою NLTK

Знайомимося з Python

Представниця замовника запропонувала використовувати машинне навчання, можливо на базі Apache Mahout та Hadoop, оскільки нещодавно читала про ці технології. Однак розробники як з її, так і нашої команди мали більше досвіду роботи з Ruby, а не з Java™. У цій статті я розповідаю про всі технічні дослідження, процес навчання і, нарешті, про підсумкову реалізацію рішення.

Що таке машинне навчання?

Невдала спроба - Mahout та Ruby

Розібравшись у тому, що є машинне навчання, ми перейшли до наступного кроку — пошуку способів реалізації. На думку клієнта, гарною відправною точкою міг би стати Mahout. Я завантажив код із сервера Apache і почав вивчати процес машинного навчання в Mahout та Hadoop. На жаль, я виявив, що Mahout складний у вивченні навіть для досвідченого розробника Java і не має працюючих прикладів коду. Не менше засмутило обмежену кількість інфраструктур та gem-пакетів для машинного навчання на Ruby.

Знахідка - Python і NLTK

Я продовжив шукати рішення; у результатах пошуку постійно виявлялися згадки про Python. Як прихильник Ruby, я знав, що Python є динамічною мовою програмування і використовує таку саму об'єктно-орієнтовану текстову модель інтерпретації, хоча ніколи не вивчала цієї мови. Незважаючи на ці подібності, я багато років ухилявся від вивчення Python, вважаючи його зайвим знанням. Таким чином, Python був моєю "сліпою плямою", і я підозрюю, що така ж картина спостерігається у багатьох колег-програмістів на Ruby.

Пошук книг з машинного навчання та детальне вивчення їх змісту показали, що значна частина подібних систем реалізується на Python у поєднанні з бібліотекою для робіт з природними мовами.Natural Language Toolkit (NLTK) Подальші пошуки дозволили з'ясувати, що Python використовується набагато частіше, ніж я думав, наприклад, у движку Google App, на YouTube, а також на веб-сайтах, які використовують Django. Виявляється, його спочатку встановлено на робочих станціях Mac OS X, з якими я щодня працюю! Більше того, Python має цікаві стандартні бібліотеки (наприклад, NumPy та SciPy) для математичних розрахунків, наукових досліджень та інженерних рішень. Хто ж міг знати?

Виявивши елегантні приклади коду, я вирішив використати рішення на Python. Наприклад, наведений нижче однорядковий код робить все необхідне для отримання RSS-новини за протоколом HTTP та друкування її вмісту:

Просуваємось до мети разом з Python

Python Package Index (pip) – стандартний менеджер пакетів у Python. Це саме та програма, яку ви використовуватимете для додавання бібліотек до вашої системи. Він аналогічний gem для бібліотек Ruby. Щоб додати бібліотеку NLTK до вашої системи, потрібно виконати наступну команду:

Щоб відобразити список бібліотек Python, встановлених у вашій системі, використовуйте команду:

Запуск програм

Запуск програм на Python відбувається так само просто. Якщо у вас є програма locomotive_main.py , яка приймає три аргументи, ви можете скомпілювати та запустити виконання коду за допомогою наступної команди на python :

Синтаксис if __name__ == "__main__" , наведений у лістингу 1, використовується в мові Python для того, щоб визначити, чи запущено файл окремо з командного рядка або викликаний іншим фрагментом коду. Щоб зробити програму виконуваною, додайте до неї перевірку на "__main__" .

Лістинг 1. Перевірка статусу Main

virtualenv

Багато програмістів на Ruby знайомі зпроблемою загальних системних бібліотек, що також називаються gem. Застосування загальносистемних наборів бібліотек зазвичай небажане, оскільки один із ваших проектів може покладатися на версію 1.0.0 наявної бібліотеки, а інший — на версію 1.2.7. Розробники Java стикаються з подібною проблемою у випадку загальносистемної змінної CLASSPATH. Подібно до інструменту rvm у Ruby, у Python використовується інструмент virtualenv (див. посилання в розділі Ресурси), що створює окремі середовища виконання програм, включаючи спеціальні інструкції Python та набори бібліотек. Команди в лістингу 2 показують, як створити віртуальне середовище виконання з ім'ям p1_env для вашого проекту p1, до складу якого будуть входити бібліотеки feedparser, numpy, scipy та nltk.

Лістинг 2. Створення віртуального середовища виконання за допомогою virualenv

Скрипт для активації вашого віртуального середовища необхідно запускати щоразу, коли ви працюєте з вашим проектом у вікні оболонки. Зверніть увагу, що після виконання скрипта активації змінюється командне запрошення оболонки. Для зручності переходу до каталогу вашого проекту та активації віртуального середовища після створення вікна оболонки у вашій системі корисно додати файл

/.bash_profile запис на зразок наступного:

Базова структура коду

Освоївши прості програми рівня Hello World, розробнику на Python необхідно навчитися правильно структурувати код з урахуванням каталогів і імен файлів. Як і Java або Ruby, у Python є для цього свої правила. Якщо говорити коротко, Python використовує для угруповання пов'язаного коду концепцію пакетів та використовує однозначно певні простори імен. З метою демонстрації в цій статті код розміщується в кореневому каталозі проекту, наприклад,

/ p1. У ньому єпідкаталог locomotive, що містить однойменний Python-пакет. Ця структура каталогів показано у лістингу 3.

Лістинг 3. Приклад структури каталогів

Зверніть увагу на файли з дивною назвою __init__.py. У цих файлах містяться інструкції Python для підвантаження необхідних бібліотек до вашого середовища, а також до ваших спеціальних програм, які знаходяться в тому ж каталозі. У лістингу 4 наведено вміст файлу locomotive/__init__.py.

Лістинг 4. locomotive/__init__.py

При структурі пакету locomotive , показаній у лістингу 4, основні програми з кореневого каталогу вашого проекту можуть імпортувати та використовувати його. Наприклад, файл locomotive_main.py містить наступні команди імпорту:

Тестування

Стандартна Python-бібліотека unittest надає зручні ресурси для тестування. Розробники Java, знайомі з JUnit, а також фахівці з Ruby, що працюють з інфраструктурою Test::Unit, легко зрозуміють код Python unittest з лістингу 5.

Лістинг 5. Python unittest

Вміст листингу 5 також демонструє особливість Python: для успішної компіляції код повинен мати однаково встановлені відступи. Метод tearDown(self) може бути дивним - навіщо в коді тесту запрограмований успішний результат проходження? Насправді, в цьому немає нічого страшного. Таким чином, у Python можна запрограмувати порожній метод.

Інструменти

Що мені дійсно було необхідно — так це інтегроване середовище розробки (IDE) з підсвічуванням синтаксису, завершенням коду та можливістю виконання з контрольними точками, щоб освоїтись у Python. Як користувач Eclipse IDE для Java, я спочатку звернув увагу на pyeclipse . Цей модуль працює досить непогано, але іноді дужеповільно. Зрештою, я вибрав IDE PyCharm, яка задовольнила всі мої вимоги.

Отже, озброївшись базовими знаннями про Python та його екосистему, я нарешті був готовий до реалізації машинного навчання.

Вилучення та обробка стрічок новин

Одна із складностей проекту полягала в тому, що клієнт ще не визначив перелік цільових стрічок новин RSS. Також не було і «даних для навчання». Тому стрічки новин та тренувальні дані на початковому етапі розробки доводилося імітувати.

Перший спосіб отримання зразків даних стрічок новин, який я використав, полягав у тому, щоб зберегти вміст списку стрічок RSS у текстовому файлі. Python має дуже непогану бібліотеку для обробки стрічок RSS під назвою feedparser , яка дозволяє приховати відмінності між різними форматами RSS і Atom. Ще одна корисна бібліотека для серіалізації простих текстових об'єктів жартівливо названа pickle (маринад). Обидві бібліотеки використовуються в коді з лістингу 6, який зберігає кожну стрічку RSS у "замаринованому" вигляді для подальшого використання. Як ви можете бачити, програмний код Python є лаконічним і потужним.

Лістинг 6. Клас CaptureFeeds

Особливий інтерес представляє файл

Природна мова – це складно

Англійська, як і будь-яка інша природна мова (мова повсякденного спілкування) відрізняється надзвичайною неоднорідністю та непослідовністю з погляду комп'ютерної обробки. Насамперед виникає питання з регістром. Чи можна вважати слово Bronco рівним bronco? Відповідь буде: "можливо". Також важливі пунктуація та прогалини. Чи можна порівнювати bronco? з bronco чи bronco,? Начебто так. Далі, існують форми множини і подібні слова. Чи можна вважати run, running та ran еквівалентнимиформами? Залежить від ситуації. Ці три слова є однокорінними. А якщо слова з природної мови також супроводжуються тегами HTML? У цьому випадку вам доведеться працювати з такими елементами, якbronco. Нарешті, існує проблема слів, що часто використовуються, але фактично нічого не значущих, таких як артиклі, спілки і прийменники. Ці звані допоміжні слова ускладнюють обробку. Таким чином, природна мова дуже безладна і вимагає очищення перед початком роботи.

На щастя, Python і NLTK дозволяють легко позбутися цього сміття. Метод normalized_words з класу RssItem в лістингу 7 дозволяє виключити всі ці перешкоди. Зокрема, зверніть увагу, як NLTK очищає сирий текст статті від вбудованих HTML-тегів за допомогою всього одного рядка коду! Крім того, за допомогою регулярного виразу виконується видалення пунктуації, після чого текст поділяється на слова та переводиться в нижній регістр.

Лістинг 7. Клас RssItem

Перелік допоміжних слів береться із NLTK однією командою; підтримуються та інші природні мови.

Класифікація за простим байєсовським алгоритмом

Алгоритм Naive Bayes (простий алгоритм Байєса) широко відомий і вбудований в NLTK у вигляді класу nltk.NaiveBayesClassifier . Байєсовський алгоритм дозволяє класифікувати елементи за фактом наявності чи відсутності певних елементів у складі. У випадку зі стрічками RSS як елементи використовуються певні (очищені) слова природної мови. Алгоритм є "простим" у тому сенсі, що не має на увазі взаємозв'язків між елементами (у нашому випадку словами).

Проте в англійській мові є понад 250 000 слів. Безумовно, я не хотів би створювати об'єкт із 250 000 логічних значень для кожної стрічки RSS,щоб реалізувати алгоритм. Отже, які слова використовувати? Якщо говорити коротко, це повинні бути слова, що найчастіше зустрічаються з тестових даних, які не є допоміжними. NLTK має дуже зручний клас nltk.probability.FreqDist , який дозволяє виявити ці популярні слова. А наведений у лістингу 8 метод collect_all_words повертає масив, що містить всі слова з усіх тренувальних нотаток.

Далі цей масив обробляється методом identify_top_words , який повертає слова, що найчастіше зустрічаються. Зручна функція класу nltk.FreqDist фактично створює хеш, але його ключі виявляються відсортованими відповідно до відповідних значень (кількості входжень). Таким чином, можна легко виділити 1000 найпоширеніших слів, вказавши діапазон індексів [:1000] відповідно до синтаксису Python.

Лістинг 8. Використання класу nltk.FreqDist

/nltk_data/corpora/reuters/cats.txt, про який я вже говорив раніше. Читання файлу на Python відбувається просто:

Наступним кроком є ​​отримання характеристик кожного повідомлення зі стрічки RSS. Ця дія виконує метод features з класу RssItem, продемонстрований нижче. Працюючи даного методу масив всіх слів ( all_words ) статті спочатку скорочується до меншого за розмірами набору унікальних слів ( set ) з допомогою усунення дублікатів слів. Далі виконується прохід за списком найпоширеніших слів top_words та перевірка їх наявності чи відсутності у статті. В результаті ми отримуємо хеш з 1000 логічних значень, ключами якого є слова з префіксом w_ . Відповідний код на Python дуже короткий.

Далі я збираю тренувальний набір повідомлень RSS та їх індивідуальних характеристик та передаю їх на обробку алгоритму. Код із лістингу 9демонструє виконання цього завдання. Зверніть увагу, що навчання класифікатора займає рівно один рядок коду.

Лістинг 9. Навчання nltk.NaiveBayes >

Менш проста класифікація

Як говорилося раніше, наш алгоритм не передбачає наявності взаємозв'язків між індивідуальними параметрами. Отже, фрази типу " machine learning " і " learning machine " чи " New York Jet " і " jet to New York " є еквівалентами (прийменник «to» виключається як допоміжне слово). У природній мові між цими словами є очевидні зв'язки. Як зробити алгоритм менш «простим» та навчити його розпізнавати ці взаємозв'язки між словами?

Один із методів — включити до набору параметрів поширені словосполучення із двох (біграми) та трьох слів (триграми). І ми вже не дивуємося, що в NLTK є підтримка цих можливостей у вигляді функцій nltk.bigrams(. ) і nltk.trigrams(. ) . Так само як бібліотека вибирала з усього набору даних N найпоширеніших слів, вона може ідентифікувати найпопулярніші дво- і трислівні словосполучення і використовувати їх як параметри.

Ваші результати можуть бути іншими

Рекомендації до алгоритму k-Nearest Neighbors

Висновок

Знайомство з Python, NLTK та машинним навчанням виявилося цікавим та приємним. Python — це потужна та лаконічна мова програмування, яка тепер стала однією з основних частин мого інструментарію розробника. Він чудово підходить для реалізації машинного навчання, обробки природної мови та математичних та наукових додатків. Крім того, хоча я не згадав цього в цій статті, він здався мені корисним для створення діаграм та графіків. І якщо у вас Python також був «у сліпій зоні», я раджу вампознайомитись із ним.

Ресурси для скачування

Схожі теми

Коментарі