Академический Документы
Профессиональный Документы
Культура Документы
Москва,2017
УДК 004.438Python:004.6
ББК 32.973.22
Р28
Рашка С.
Р28 Python и машинное обучение / пер. с англ. А. В. Логунова. - М.: ДМК Пресс,
2017. - 418 с.: ил.
ISBN 978-5-97060-409-0
Книга предоставит вам доступ в мир прогнозной аналитики и продемонстрирует, по
чему Python является одним из лидирующих языков науки о данных. Охватывая широкий
круг мощных библиотек Python, в том числе scikit-learn, Theano и Keras, предлаrая руко
водство и советы по всем вопросам, начиная с анализа мнений и заканчивая нейронными
сетями, книга ответит на большинство ваших вопросов по машинному обучению.
Издание предназначено для специалистов по анализу данных, находящихся в поисках
более широкого и практического понимания принципов машинного обучения.
УДК 004.438Python:004.6
ББК 32.973.22
Copyright ©Packt PuЫishing 201.5. First puЬlished in the English language under the title 'Python
Machine Leaгning - (9781783555130)'
Все пра ва защищен ы . Любая часть этой I<ниги н е может быть воспроизведена в I<aI<oй бы
то ни было форме и I<аI<ими бы то ни было средствами без письмешюго разреше 1шя владельцев
авторс1шх прав.
Применение регуляри зованных методов для регре ссии." " .. ".""""" .."... " .................. " .. 277
Превращение линейной регрессионной модели в криволинейную -
поли номиальная регрессия ................................................................................... .... .................. 278
Моделирование нелинейных связей в наборе данных Housing """""."""""""""" 280
Обработка нелинейных связей при помощи случайных лесов"""""""".""""""" . 283
Регрессия на основе дерева решений."""" " ." """ " """" """""""""""."" "" """"""" 283
Регрессия на основе случайного леса" """"""" ". """"".".""""".""""""".""""""". 285
Резюме " ... " ....... """ .............................. " .." ........... " ................. " ............ " .. " .... " ............................ " .. 287
Приложение А ........................... ".. "......................... "...................................... "...".... "."." ... ". 380
Оценка моделей ........" .... " ............. """ .. " ......... " ..... "" .................................................. " .... " ..".. " ..
380
Что такое переобучение? ........ " .. "" ....."........."" ......." ... " ....................... " ...... " ......"." .. ".. " ..
380
Как оценивать модель?"" ....." .... "" .... " ......." ... ""." .. " ... "" .............. " ................""."." ..".. ".. 381
Сценарий 1. Элементарно обучить простую модель""""""""""""""""" """"" " 381
Сценарий 2. Натренировать модель и выполнить тонкую настройку
(оптимизировать гиперпараметры) " ......." ............................" ................... "." ...... " .... 382
Сценарий 3. Построить разные модели и сравнить разные алгоритмы
(например, SVM против логистической регрессии против случайных
лесов и т. д. ) """"""""""""""""""""""""""""""""""".""".""""""."""""".""""""""" 383
Перекрестная проверка. Оценка качества оценщика""""""""""""""""""""""""". 384
Перекрестная проверка с исключением по одному""""""""""""""""""""."""""". 386
Пример стратифицированной k-блочной перекрестной проверки"" " """"""" " ". 387
Расширенный пример вложенной перекрестной проверки""" " """"""". "" "" " """ 387
А. Вложенная кросс-валидация : быстрая версия""""""""""""""""".""""."""" 388
Б. Вложенная кросс - валидация: ручной подход с распечаткой
модельных параметров ................ " .... " .......................... " ...................................... " .." ..... 388
В. Регулярная k-блочная кросс-валидация для оптимизации модели
на полном наборе тренировочных данных"" " """""".""""""""""""""."""""""" 389
График проверочной (валидационной) кривой""""""""""""""" " """"""""."""""" 389
Настройка типового конвейера и сеточного поиска."""" """""""""" "" """ " """"" " 391
Машинное обучение .................... """" .... " ..." .... "."""" ..... " ........." ....... " ..... "" .............. "."." .. " .. 393
В чем разница между классификатором и моделью?""""""""" " """"""""" "" """". 393
В чем разница между функцией стоимости и функцией потерь?""""""""""""". 394
Обеспечение персистентности моделей scikit-learn на ocнoвeJSON " """" " """" . 395
н
аверное, не стоит и говорить, что машинное обучение стало одной из самых за
хватывающих технологий современности. Такие крупные компании, как Google,
Facebook, Apple, Amazon, IBM, и еще многие другие небезосновательно вкладывают
значительный капитал в разработку методов и программных приложений в обла
сти машинного обучения. Хотя может показаться, что термин «машинное обучение»
сегодня уже набил оскомину, совершенно очевидно, что весь этот ажиотаж не яв
ляется результатом рекламной шумихи. Эта захватывающая область исследования
открывает путь к новым возможностям и стала неотъемлемой частью нашей по
вседневной жизни. Разговоры с речевым ассистентом по смартфону, предоставление
рекомендаций относительно подходящего продукта для клиентов, предотвращение
актов мошенничества с кредитными картами, фильтрация спама из входящих со
общений электронной почты, обнаружение и диагностирование внутренних заболе
ваний - и этот список можно продолжать.
Если вы хотите стать практиком в области машинного обучения, более основа
тельным решателем задач или, возможно, даже обдумываете карьеру в научно - ис
следовательской области, связанной с машинным обучением, то эта книга для вас!
Однако новичка теоретические идеи, лежащие в основании машинного обучения,
нередко могут подавлять своей сложностью . И все же многие из опубликованных
в последние годы практических изданий способны помочь вам приступить к работе
с машинным обучением на основе реализации мощных алгоритмов обучения. По
моему мнению, использование практических примеров программного кода служит
ковая система по полным текстам научных публикаций всех форматов и дисциплин. Про
ект работает с ноября 2004 r. - Прш.t. перев .
16 Введение
данными подхода.
Условные обозначения
В этой книге вы найдете ряд текстовых стилей, которые выделяют различные виды
информации. Вот некоторые примеры этих стилей и объяснение их значения.
Кодовые слова в тексте, имена таблиц баз данных, папок, файлов, расширения
файлов, пути, ввод данных пользователем и дескрипторы социальной сети Twitter
показаны следующим образом: «И уже установленные пакеты могут быть обновле
ны при помощи флага --upgrade».
Отзывы читателей 19
с
D
dtype : iпt64
Отзывы читателей
Отзывы наших читателей всегда приветствуются. Сообщите нам, что вы думаете об
этой книге - что вам понравилось, а что нет. Обратная связь с читателя ми для нас
очень важна, поскольку она помогает нам форм ировать названия книг, из которых
вы действительно получите максимум полезного.
Отзыв по общим вопросам можно отправить по адресу feedback@packtpub.com,
упомянув заголовок книги в теме вашего электронного сообщения.
20 Введение
Служба поддержки
Теперь, когда вы являетесь довольным владельцем книги издательства «Packt», мы
предложим вам ряд возможностей с целью помочь вам получать максимум от своей
покупки.
Опечатки
Вопросы
Если у вас есть вопрос по каким-либо аспектам этой книги, то вы можете связаться
с нами по электронному адресу questions@packtpub.com, и мы приложим все уси
лия, чтобы решить ваш вопрос.
Комментарий переводчика
Весь материал книги приведен в соответствие с последними действующими версия
ми библиотек (время перевода книги - октябрь-ноябрь 2016 г.), дополнен свежей
информацией и протестирован в среде Windows 10 и Fedora 24. При тестировании
исходного кода за основу взят Python версии 3.5.2.
Большинство содержащихся в книге технических терминов и аббревиатур для
удобства кратко определено в сносках, а для некоторых терминов в силу отсутствия
единой терминологии приведены соответствующие варианты наименований или
пояснения. Книга дополнена ~глоссарием основных терминов и сокращений». В при
ложении к книге особое внимание уделено оценке качества машиннообучаемых мо
делей .
На веб-сайте Github имеется веб-страница книги, где содержатся обновляемые
исходные коды прилагаемых к книге программ, много дополнительных материалов
либо как 1vl1i: рiрЗ install scipy-0 .18. l - cp35 - cp35m-win_amd64 - any. whl
рiрЗ i nstall jupyter
рiрЗ install theano
рiрЗ install keras
р i рЗ instal l nltk
р i рЗ install seaborn
р i рЗ install flask
рiрЗ install pyprind
рiрЗ install wtforms
факультатиш10:
рiрЗ i nsta l l mlxtend
рiрЗ install pyqtS
рiрЗ install spyder
И чтобы обновить:
Во всех вышеперечисленных случаях речь идет о версии Spydeг для Python 3 (на
момент инсталляции это был Python 3.5.2).
Установка среды Spydeг в Windows:
рiрЗ install spyder
Глава 1
Наделение компьютеров
способностью обучаться на данных
п
о моему убеждению, машинное обучение как область практической деятельно
СJИ и как сфера научных исследований алгоритмов, извлекающих смысл из дан
ных, является самой захватывающей отраслью всей информатики! В наш век пере
избытка данных при помощи самообучающихся алгоритмов мы можем превратить
эти данные в знание. Благодаря тому что за последние несколько лет были разра
ботаны многочисленные мощные библиотеки с открытым исходным кодом, вероят
но, никогда еще не появлялся столь подходящий момент для того, чтобы ворваться
в область машинного обучения и научиться использовать мощные алгоритмы, по
зволяющие обнаруживать в данных повторяющиеся образы и делать прогнозы о бу
дущих событиях.
В этой главе мы узнаем об общих принципах машинного обучения и его типах.
Вместе с вводным курсом в соответствующую терминологию мы заложим основу
для того, чтобы успешно использовать методы машинного обучения для решения
практических задач .
Обучение с подкреплением
1
Класс - это множество всех объектов с данным значением метки. - Пршt. перев.
Под образцом, или экземпляром (sample), здесь и далее по тексту подразумевается совокуп
2
о о 1
1
о 1
о + 1
1
00 1
+
о 1
+ +
1
о
1
Х2 • о 1
о о 1
1
+ +
+ ++
1
о 1
о 1
о 1
1
+ + +
о 1
1
+ + +
Xi
1
SAT Math - тест по математике для будущих бакалавров ( http://test-sat. ru/sat-math/). -
Прим. перев.
Три типа лtашинного обучепия 29
1
Геометрически пересечение (intercept) - это точка, в которой линия регрессии пересекает
ось У. В формуле линейной регрессии пересечение - это свободный коэффициент, кото
рому равна з ависимая переменная, если предиктор, или независимая переменная, равен
Среда
Вознагражд ение
Состояние
А1 ....
~
,.. ~
,..
:-- Действие
.
Агент
при помощи трех - или двухмерных точечных графиков или гистограмм. Рисунок
ниже показывает пример использования нелинейного снижения размерности для
сжатия трехмерного швейцарского рулета (Swiss гoll) в новое двумерное подпро
странство признаков:
32 Наделение ко11тыотеров способностью обучаться на данных
Чашелистик
Признаки
/ Метки классов
(цели)
(атрибуты, данные измерений, размерности)
1
Ирисы Фишера (Iгis) - набор данных для задачи классификации, на примере которого Ро
нальд Фишер в 1936 году продемонстрировал разработанный им метод дискриминантного
анализа . Для каждого экземпляра измерялись четыре характеристики/признака (в сантиме
трах): длина чашелистика (sepal length), ширина чашелистика (sepal width); длина лепестка
(petal length), ширина лепестка (petal width). Этот набор данных уже стал классическим
и часто используется в литературе для иллюстрации работы различных статистических ал
горитмов (см. https://ru.wikipedia.org/wiki/Иpиcы_ Фишepa). - Пршt. перев.
Дорожная карта для построения сuсте.м .машииного обучения 33
В остальной части этой книги мы будем использовать надстрочный индекс (i) для обо
с+ значения i-й тренировочного образца и подстрочный индекс j для обозначения j-й раз
мерности тренировочного набора данных.
Мы используем заглавные (прописные) буквы, набранные жирным шрифтом, для обо
1
значения векторов (х Е R"x ) и строчные буквы, набранные жирным шрифтом, для обо
значения матриц соответственно (Х Е R"x'") . Для обозначения в векторе или матрице
отдельных элементов мы пишем буквы курсивом, соответственно х<") или х~~
Например, x:so обозначает первое измерение 150-го образца цветков, длину чашелис
тика. Таким образом, каждая строка в этой матрице признаков представляет один эк
земпляр цветка и может быть записана как четырехмерный вектор-строка :J-i> Е JR 1x
4
,
X(I)
)
х<2>
)
x<1so)
)
[
у(!) ]
денная ниже схема показывает типичную диаграмму потока операций при исполь
зовании машинного обучения в прогнозном моделировании, которое мы обсудим
в последующих подразделах:
Выделение
и масштабирование признаков
Отбор признаков
Снижение размерности
Взятие выборок
1м;ки1----1
Алгоритм
.----' - ---. :• .......
Окончательная ..._._ --1
Тренировочный Новые данные
•-------·
-
1
--------------.i
1
1
.... -""!!''!!!1'-•-"'
1
:
1
1
Отбор модели
Перекрестная проверка
Метрики качества
Гиперпараметрическая
оптимизация
Исходные (необработанные) данные редко поступают в том виде и в той форме, ко
торые необходимы для оптимальной работы алгоритма обучения. Поэтому предобра
ботка данных является одним из самых важных этапов в любом приложении с ис
пользованием машинного обучения. Если в качестве примера взять набор данных
цветков ириса из предыдущего раздела, то исходные данные можно представить как
Д. Вульперт (1996) показал, что попытки найти алгоритм обучения без смещений беспо
1
лезны. Кроме того, он продемонстрировал, что в свободном от шума сценарии, где функция
потерь эквивалентна коэффициенту ошибочной классификации, если вы интересуетесь
ошибкой вне тренировочного набора, то между алгоритмами обучения нет никаких апри
орных различий . См . http://www.no-free-lunch .org/. - Прим. перев .
Иными словами, когда у вас в руках молоток, все задачи кажутся гвоздями.
- Пршt. перев.
В машинном обучении точность (precision) является мерой статистической изменчивости
3
После того как мы отобрали модель, подогнанную под тренировочный набор дан
ных, мы можем воспользоваться тестовым набором данных и оценить, насколько
хорошо она работает на этих ранее не встречавшихся ей данных, чтобы вычислить
ошибку обобщения . Если мы удовлетворены качеством модели, то теперь мы можем
использовать ее для прогнозирования новых будущих данных. Важно отметить, что
параметры для таких ранее упомянутых процедур, как масштабирование признаков
и снижение размерности, получают исключительно из тренировочного набора дан
ных, и те же самые параметры позже применяют повторно для трансформирования
тестового набора данных, а также любых новых образцов данных - в противном
случае измеренное на тестовых данных качество модели может оказаться сверхоп
тимистичной.
1
Перекрестная проверка, валидация (cross validation, CV) - это метод подтверждения ра
ботоспособности моделей, который используется для оценки того, насколько точно про
гнозная модель будет работать на практике . Задача перекрестной проверки - выделить
из тренировочного набора данных подмножество данных, которое будет использоваться
с целью « протестировать » модель на этапе ее тренировки (так называемый перекрестно
проверочный набор), с тем чтобы ограничить такие проблемы , как переобучение, и дать
представление о том, как модель будет обобщаться на независимый набор данных (т . е . ра
нее не встречавшихся данных, например из реальной задачи). - Прим . перев.
Использовапие Python длл .маши1111ого обучеиия 37
данными, делая работу с табличными данными еще более удобной. В помощь на
шей познавательной деятельности, а также в целях визуализации количественных
данных, что порой бывает чрезвычайно полезно делать для интуитивного пони
мания материала, мы будем пользоваться полностью настраиваемой библиотекой
matplotlib.
Номера версий основных пакетов Python, которые использовались при написа
нии этой книги , упомянуты ниже . Удостоверьтесь, что номера версий ваших уста
новленных пакетов идентичны приведенным в списке или выше, чтобы примеры
программ гарантированно выполнялись корректно:
Hello world
3.0 ......-----...~-~--.....------..-~~--.......
2 .5
2.0
1..5
$ cd ~/code/python-machine-learning-book
$ jupyter notebook
: QI ;- « /
t - - -
code / ch01
D ..
О D images
1/;' ch01 .ipynb
О D README.md
Резюме
В этой главе мы провели широкомасштабную разведку обширной области информа
тики - машинного обучения и ознакомились с общей картиной и ее главными кон
цепциями, которые мы собираемся разобрать в следующих главах более подробно.
Мы узнали, что обучение с учителем состоит из двух важных подобластей: задач
классификации и регрессии. В отличие от модели классификации, которая позво
ляет распределять (категоризировать) объекты по известным классам, мы можем
применять регрессию с целью предсказания непрерывных результатов для целевых
После того как мы охватим все важные концепции типичного конвейера машин
ного обучения 1, в главе 8 <.<Применение алгоритмов машинного обучения в анализе мне
ний» займемся реализацией модели, предназначенной для прогнозирования эмоций
в тексте, в главе 9 <.<Встраивание алгоритма машинного обучения в веб-приложение»
мы встроим ее в веб-приложение, чтобы поделиться им с миром . Затем в главе 10
<.<Прогнозирование непрерывных целевых величин на основе регрессионного анализа»
мы воспользуемся алгоритмами машинного обучения для регрессионного анализа,
которые позволят прогнозировать непрерывные выходные переменные, и в главе 11
<.<Работа с немаркированными данными - кластерный анализ» мы применим алго
ритмы кластеризации, которые позволят находить в данных скрытые структуры.
' В информатике конвейер представляет собой набор элементов обработки данных, соеди
ненных последовательно, где выход одного элемента является входом следующего. Эле
менты конвейера часто выполняются параллельно по принципу квантования по времени . -
Прим . перев.
Глава2
Тренировка алгоритмов
машинного обучения
для задачи классификации
в методами
этой главе мы воспользуемся двумя первыми алгоритмически описанными
машинного обучения, применяемыми для классификации данных,
персептроном и адаптивными линейными нейронами. Мы начнем с пошаговой реа
лизации персептрона на языке Python и его тренировки для решения задачи клас
сификации различных видов цветков из набора данных Ирисов Фишера (lгis). Это
позволит нам понять принцип работы алгоритмов машинного обучения при реше
нии задачи классификации и каким образом их можно эффективно реализовывать
на Python. Затем мы перейдем к обсуждению азов оптимизации с использованием
адаптивных линейных нейронов, которые заложат основу для использования более
мощных классификаторов на основе библиотеки машинного обучения scikit-learn
в следующей г.лаве З «Обзор 1(,Jlассификаторов с использованием библиотеки scikit-
learn».
В этой главе мы затронем следующие темы:
Qf> формирование интуитивного понимания алгоритмов машинного обучения;
~ использование библиотек pandas, NumPy и matplotlib для чтения, обработки
и визуализации данных;
Аксон
Более формально эту проблему можно изложить как задачу бинарной классифи
кации, где для простоты мы обозначим наши два класса как 1 (положительный класс)
и - 1 (отрицательный класс). Затем можно определить передаточную функцию, или
функцию активации ф(z), которая принимает линейную комбинацию определенных
входных значений, или входной вектор х, и соответствующий весовой вектор w, где
z- это так называемый чистый вход (от англ. net input) (z = w1x 1 + ". + w";x,,,):
Персептрон еще часто называют пороговым сумматором. Правило, или процесс, обучения -
это применяемый многократно метод (математическая формула) обновления весов и узла
смещения модели, который улучшает ее эффективность. - При.м. перев.
После обучения персептрон готов работать в одном из двух режимов: распознавания либо
обобщения. - Прим. перев .
44 IjJенировка алгориmJ.юв J.tашинного обучения для задачи классификации
Теперь если активация отдельно взятого образца x<i), т. е. выход из ф(z), превы
шает заданный пороге, то мы распознаем класс 1, в противном случае - класс -1.
В алгоритме персептрона функция активации ф(-) - это простая едu1tИЧ1tая стуnе1t
чатая фу1tкцuя, которую иногда также называют стуnе1tчатой фу1tкцией Хевисайда
(или функцией единичного скачка):
ф(z)={1, еслиz~О
-1, иначе
Например' 123]х[~] =
[ tx 4+2 х5 3х6 =
+ 32.
14
3 2]т
[1 3 5]·
[5 6
=
2 4 6
ф(wтх) =О
ф(wrx)
ф(w х) <о
1
\ ф(и/х):::: о
о
1 о
о + +
о
00 +
Х2
о о + +
WTX о о + +
-1
00
о + ++
о + +++
о +
Х1
w1 := w1 + ЛwJ"
где ri - это темп обучения (константа между О.О и 1.0), y<iJ_ истинная метка класса
i-го тренировочного образца и y(i) - идентифицированная метка класса. Важно от
метить, что все веса в весовом векторе обновляются одновременно, т. е. мы не вы
числяем ·[/> повторно до тех пор, пока все веса Лw1 не будут обновлены. Конкретно
для двумерного набора данных мы запишем обновление следующим образом:
о
Оо 1
1
+ о о о • о
+ о о
Х2
о о 1
1 •
+
+ Х2 о ... + Х2 о •++
•• +
Оо 1
1
• • Оо
о +
• + ... о
о
О 1'
, +
• о + • о о
Х1 Х1 Х1
.......,-'!+!"'!!'!'• Выход
входа
Если вы еще незнакомы с научными библиотеками Python или же вам требуется осве
с+ жить свои з нания, пожалуйста, обратитесь к следующим ресурсам:
Библиотека NumPy: http://wiki . scipy. org[Тentative_NumPy_Tutorial
Библиотека Pandas: http://pandas . pydata . org/pandas-docs/staЫe/ tutorials.html
Библиотека Matplotlib: http://matplotlib.org/users/beginner. html
Кроме того, для того чтобы лучше отслеживать приводимые примеры, рекомендуется
скачать блокноты Jupyter с веб-сайта Packt. По поводу общих сведений о записных
книжках Jupyter посетите http://jupyter.org/ или обратитесь к документации по IPython
http://ipython . readthedocs.io/en/staЫe/. Исходные коды примеров, блокноты Jupyter и до
полнительную информацию можно также получить на странице книги на Github https://
github.com/rasbt/python-machine-learning-book.
import numpy as np
class Perceptron(object):
"""класс ификатор на основе персептрона.
Параметры
eta : float
Темп обучения (между О.О и 1 .0 )
n iter : int
Проходы по тренировочному набору данных.
Атрибуты
w : 1 - мерный массив
Весовые коэффициенты после подгонки.
errors список
def fit(self, Х, у) :
"""Выполнить подгонку модели под тренировочные данные .
Параметры
Реализация алгоритлtа обучеиия персептроиа 11а Python 49
Возвращает
self : object
self . w = np . ze r os (l + X. shape[l] )
self . errors = []
de f net_input(self , Х ):
' " Рассчитать
1
чистый вход
11 11 11 11
Вместо того чтобы для вычисления скалярного произведения двух массивов а и Ь ис
с+ пользовать библ иотеку NumPy, применяя для этого
a.dot(b) или np.dot(a, Ь), это вычис
ление можно выполнить на чистом в виде
sum([i*j, for i, j in zip( a, Ь)]. Однако
Python
преимущество использования библиотеки NumPy над классическими структурами
цикла for на Python заключается в том, что ее арифметические операции векторизо
ваны . Векторизация означает, что элементарная арифметическая операция автомати
чески применяется ко всем элементам в массиве. Формулируя наши арифметические
операции в виде последовательности инструкций на массиве вместо выполнения серии
операций для каждого элемента по одному, мы можем лучше использовать наши со
временные процессорные архитектуры с поддержкой ОКМД (SIMD), т. е. одиночного
потока команд, множественного потока данных (single instruction, multiple data). Кро
ме того, в библиотеке NшnPy используются высокооптимизированные динамические
библиотеки линейной алгебры, такие как библиотека основных подпрограмм линейной
алгебры BLAS (Basic Linear Algebra Subprograms) и пакет линейной алгебры LAPACK
(Linear Algebra Package), написанные на С или Fortran. Наконец, библиотека NurnPy
также поз воля ет писать исходный код в более компактном и интуитивном виде, исполь
зуя основы линейной алгебры, такие как скалярные произведения векторов и матриц.
»>
i mport pandas as pd
url = ' https : //archive.ics . uci . edu/ml/machine - learning-dataЬases/iris/iris . data'
df = pd . read_csv(url , header=None)
df. tail ()
о 1 2 3 4
145 6.7 3.0 5.2 2.3 Iris-v i гginica
• • • щети нистый 1
5 1х хх ра з ноцветны й х
х хх хххх
х хх х~х ххх х
х ххх хх
х !! !! ~ х х х
х
!! х я
х х х
х х
• •
••
• ···==·····=·
• •
О'-----"-----'----~---~---~---~---~
4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5
длина чашелистика [см]
~ 2.5
~
-&
~ 2.0. - - - ·
~
"";;
С1
.g 1.5
s:
3
С1
"'~ 1.0
5
Q
~ 0.5
'7'
1 10
Эпохи
Реализация ал2оритма обучеиил персептроиа па Python 53
создаем пару матричных массивов xxl и хх2 , используя для этого функцию mesh-
grid библиотеки NumPy 1• Учитывая, что мы натренировали наш персептронный
классификатор на двупризнаковых размерностях, мы должны сгладить матричные
массивы и создать матрицу с тем же самым числом столбцов, что и у тренировочно
го подмножества цветков ириса, в результате чего можно применить метод predict
и идентифицировать метки классов Z для соответствующих точек матрицы . После
преобразования формы идентифицированных меток классов Z в матрицу с такими
же размерностями, что и у xxl и хх2 , можно начертить контурный график, исполь
зуя для этого функцию contourf библиотеки matplotlib, которая ставит в соответ
ствие разным областям решения разный цвет по каждому идентифицированному
классу в матричном массиве:
plot_decision_regions(X, у, classifier=ppn)
рlt . хlаЬеl('дпина чашелистика [см]')
pl t . ylaЬel ( 'длина лепестка [см] ' )
plt . legend(loc='upper left')
plt. show()
•
•···==·1•··=·
• :•
4
длина чашелистика [см]
с+
Несмотря на то что персептрон полностью р аспределил цветки ириса на два класса, схо
димость представляет для персептрона одну из самых больших проблем. Фрэнк Розен
блатт математически доказал, что правило обучения персептрона сходится , только если
два класса могут быть разделены линейной гиперплоскостью. Однако если полностью
разделить классы такой линейной границей решения невозможно, то без установления
максимального числа эпох веса никогда не прекратят обновляться.
Выход
1
Член полусуммы - добавлен просто для удобства; как мы убедимся в после-
2
дующих абзацах, он упростит получение градиента. В отличие от единичной сту
пенчатой функции, основное преимущество этой непрерывной линейной функции
активации состоит в том, что функция стоимости становится дифференцируемой.
Еще одно хорошее свойство этой функции стоимости - в том, что она выпуклая;
следовательно, мы можем использовать простой и одновременно мощный алгоритм
оптимизации - алгоритм градиентного спуска для нахождения весов, которые мини
мизируют нашу функцию стоимости для решения задачи классификации образцов
в наборе данных цветков ириса
1
•
Как проиллюстрировано на нижеследующем рисунке, мы можем описать лежа
щую в основе градиентного спуска идею как спуск вниз по склону холма, пока не
,/---------
'\ 1
w := w + Лw.
Здесь изменение веса Лw определяется как отрицательный градиент, помножен
ный на темп обучения 11:
Лw = - 11V](w).
owj owj 2 i
= L(y<i) _ф(z(i»)(-x)i))
=-L:(y<i) _ф(z(i»)x)i).
i
cl ass AdalineGD(object) :
"'"'Классификатор на основе ADALINE (ADAptive Llnear NEuron ).
Параметры
eta : float
Темп обучения ( между О .О и 1 . 0)
n iter : int
58 Тренировка алгоритмов .машинного обучения для задачи классификации
Атрибуты
w_ : 1 - мерный массив
Веса после подгонки .
errors список
def fi t( self , Х , у ):
""" Выполнить подгонку под т ренир о в о чные д анные .
Параметры
Возвращает
self : объект
def predict(self , Х ):
11111
' В е рнуть метку класса посл е е дин ичного скачка """
re t ur n np . where (s el f . ac tivat ion(X) >= О . О , 1, - 1)
Вместо того чтобы обновлять веса после выполнения оценки каждого отдельного
тренировочного образца, как в персептроне, мы вычисляем градиент на основе всего
тренировочного набора данных, используя self.eta * errors.sum() для веса в нулевой
позиции и self.eta * X.T.dot(errors) для весов от 1 пот , где X.T.dot(errors) - это мат-
Адаптивные линейные нейроны и сходшюсть обучения 59
Темп обучения ТJ и число э пох n_iter - это так наз ы ваемые гиперпарw.tетры алгорит
с+ мов о,ручен ия ADALIN E и персептрона . В главе 4 .-.Создание хороших тренир овочных
наборов - предобработка даииых'-> мы рассмотрим различные методы автоматического
нахождения значений различ ных гиперпараметров, которые п риводят к оптимальному
качеству модели классиф икац ии данных .
4Z
40'-----'~---'-~--'-~-'-~.1..----'~-'-~-'----"
1 4 10
Эпохи Эпохи
Исходный вес
~
Глобальный минимум
СТОИМОСТИ Jmin(w)
w w
х. -µ.
X J'.=-J _ _
J
•
cr j
Здесь x j - это вектор, состоящий из значения j-го признака из всех трени р овоч
ных образцов п.
Стандартизацию можно легко получить при помощи методов mean и std библио
теки NumPy:
X_std = np.copy(X)
X_std[: , 0] (Х[ : ,0] - X[:,0].mean()) / X[ : ,0] . std()
X_std[ :, l] = (Х[ :, 1] - X[ : ,l].mean()) / X[ :, l] . std()
•=i •• 1. •l 1 - 1•
•1 • ·-
-z
-z -1 о
40
""
о
\О
:s::
3
о
><
:;; 30
":s::
:т
...:g,
С(
~ 20
"'::Е
:о:
б'
10
O'--~~-'-~~-i.~~~"--~~-'-~~--'~~~..._~~-'-~~--'
о 4 10 12 14 16
Эпохи
нять переоценку всего тренировочного набора данных каждый раз, когда мы делаем
один шаг к глобальному минимуму.
Популярной альтернативой алгоритму пакетного градиентного спуска является
стохастический градuе1tт1lый спуск, иногда также именуемый итератив1tьtМ, или
динамическим (онлайновым), градиентным спуском. Вместо обновления весов, ос
новываясь на сумме накопленных ошибок по всем образцам x<i):
С!
тического градиентного спуска. В методе fit мы теперь будем обновлять веса после
каждого тренировочного образца. Кроме того, мы реализуем дополнительный метод
частичной подгонки partial_fit, который повторно не инициализирует веса, чтобы
учесть динамическое обучение . Для того чтобы удостовериться, что наш алгоритм
сходился после тренировки, мы вычислим стоимость как усредненную стоимость
class AdalineSGD(object):
""" Классификатор на основе ADALINE (ADAptive Linear Neuron).
Параметры
eta : float
Темп обучения (между О . О и 1 . 0)
n iter : int
Пр охо ды по тренировочному набору данных.
Атрибуты
errors : list/cпиcoк
Число случаев ошибочной классификации в каждой эпохе.
shuffie : bool (по умолчанию: True)
Перемешивает тренировочные данные в каждой эпохе, если True,
для предотвращения зацикливания .
Пар аметры
Возвращает
self : объект
r = пp . raпdom.permutatioп(leп(y))
returп X[r], y[r]
sel f . w = пp . zeros(l + m)
self.w iпitialized = True
plt. show ()
I 40
::! 1
.&
:;: 1i
~
~ 30
~
~
g
ш
о
l
ю
20
~ 1 "s
••..•:i·1•···· •
-1 ~
~ 10
-1
о
-2 -1 о 10 12 14 16
длин а чашелистика {стандартмзоаан н аА) Эпохи
Как видно, средняя стоимость довольно быстро уходит вниз, а окончательная гра
ница решения после 15 эпох выглядит аналогично пакетному градиентному спуску.
Если нашу модель нужно обновить, например в сценарии динамического обучения
с потоковой передачей данных, то мы можем просто вызывать метод partial_fit на
индивидуальных образцах, к примеру ada.partial_fit(X_std[O, :], у[О]).
Резю~1е 67
Резюме
В этой главе мы получили хорошее представление об основных принципах работы
линейных классификаторов, используемых в обучении с учителем. После реализа
ции персептрона мы познакомились с методами эффективной тренировки адаптив
ных линейных нейронов (ADALINE) векторизованной версией метода градиентного
спуска и с динамическим обучением методом стохастического градиентного спуска.
Научившись реализовывать простые классификаторы на Python, теперь мы готовы
перейти к следующей главе, где воспользуемся библиотекой машинного обучения
scikit-l eaгn языка Python для доступа к более продвинутым и мощным стандартным
машиннообучаемым классификаторам, которые обычно используются в научных
кругах и в информационной отрасли.
ГлаваЗ
Обзор классификаторов
с использованием библиотеки
scikit-learn
в
этой главе мы пройдемся по массиву популярных и мощных алгоритмов машин
ного обучения, которые обычно используются в научных кругах и в информаци
онной отрасли . По мере ознакомления с различиями между несколькими алгорит
мами обучения с учителем для решения задачи классификации мы также получим
интуитивное представление об их индивидуальных достоинствах и недостатках .
Кроме того, мы сделаем наши первые шаги в работе с библиотекой scikit-learn, кото
рая предлагает пользователю удобный интерфейс для эффективного и продуктив
ного использования этих алгоритмов .
Поскол ьку принятый в книге подход состоит в посте пенном накоплении зна
ний в области машинного обучения, в этой главе мы в основном сосредоточимся
на основных принципах работы разных алгоритмов и в дальнейшем в целях более
детального обсуждения будем пересматривать такие темы, как, например, отбор
и предобработка признаков, качественной метрики и тонкая настройка гиперпара
метров.
>»
y_pred = ppn . predict(X_test_std)
print('Чиcлo ошибочно классифицированных образцов : %d ' % (y_tes t ! = y_pred) .sum())
Число ошибочно классифицированных обра зцов: 4
>»
from sklearn.metrics import accuracy_score
%. 2f ' % accuracy_score(y_test , y_pred))
print( ' Bepнocть:
Верность: 0 .91
Здесь у_test - это истинные метки классов, y_pred - метки классов, которые мы
идентифицировали ранее .
Отметим, что в этой главе мы оцениваем качество наших моделей, основываясь на те
с+ стовом наборе. В главе 5 «Сжатие данных путе.м снижения раз.мерности;, вы узнаете
о полезных методах, включая графический анализ, такой как кривые обучения, чтобы
обнаруживать и предотвращать переобучение. Термин «переобучение;, означает, что
модель хорошо захватывает повторяющиеся образы (закономерности, паттерны) в тре
нировочных данных, но ей не удается хорошо обобщаться на ранее не встречавшиеся ей
данные.
• • 11 о
Х Хх 1
ооо 2
ООО тестовый набор
-2
-2 -1
дпина лепестка [ста ндартизова нн ая]
Класс Percept ron, а также другие функции и классы библиотеки scikit-learn имеют до
с+ полнительные параметры, которые мы для ясности пропускаем. Дополнительную ин
формацию об этих параметрах можно прочесть, воспользовав ш ись функцией Python
help (например, help(Perceptron)) или просмотрев превосходную онлайн-документацию
по библиотеке scikit-learn на http://sc i ki t-lea rn . org/staЫe/.
Здесь р(у = 1~) - это условная вероятность, что отдельно взятый образец принад
лежит классу 1 при наличии его признаков х.
Далее нас на самом деле интересует предсказание вероятности, что определенный
образец принадлежит отдельно взятому классу, т. е. обратная форма функции логит.
Ее также называют логистической функцией, иногда просто сокращенно сиг.моидой,
или сигмоидальной функцией, из-за ее характерной формы в виде латинской буквы S.
1
ф(z)=-- .
1+ e- z
def sigmoid ( z) :
return 1 . 0 / (1.0 + np.exp (-z))
z = np.arange(-7 , 7, 0.1 )
phi_ z = sigmoid (z)
plt . plot(z , phi_ z)
plt.axvline(O.O, color= ' k ')
plt.axhspan(O . O, 1.0, facecolo r='l. 0 ', alpha=l.0 , ls= ' dotted ')
plt . axhline (y=0.5 , ls= ' dotted ', color= ' k ')
plt. yticks ( [О. О , О . 5, 1 . О ] )
plt.ylim (- 0 . 1, 1 . 1)
plt. xl abel ( ' z ' )
pl t . yl abel( ' $\ ph i (z) $ ' )
plt. show ()
1.0
,,...._
~0.5
о. о
-8 -6 -4 -2 4 8
z
в главе
2 «Тренировка алгоритмов машинного обучения для задачи классификации~ .
В ADALINE мы в качестве функции активации использовали тождественное ото
бражение ф(z) = z. В логистической регрессии эта функция активации просто стано
вится сигмоидальной функцией, которую мы определили ранее, как проиллюстри
ровано на следующем рисунке:
ч и стого
входа
А { 1, еслиz ~ О.О
у =
О, иначе
циента есть отдельно взятое заболевание при наличии определенных симптомов; вот
почему логистическая регрессия имеет широкую популярность в сфере медицины.
Моделирование вероятностей 1СJ1ассов логистической регрессии 77
1
Правдоподобие (likelilюod), или функция правдоподобия, - это совместное распределение
образца из параметрического распределения , рассматриваемое как функция параметра.
При этом используется совместная функция плотности (в случае образца из непрерывного
распределения) либо совместная вероятность (в случае образца из дискретного распреде
ления) , вычисленные для значений выборочных данных . - Прим . перев.
Во избежание проблемы арифметического переполнения снизу (приводящего к исчезнове
2
нию разрядов) при работе с числами с плавающей точкой, находящимися слишком близко
к нулю, пользуются свойствами логарифмов: logab= и exp(log[(x) = х]), кото
loga + logb
рые позволяют произведение р 1 * ". * р 11 представить в виде эквивалентной суммы логариф
мов: exp(log(p 1) + ". + log(p 11 ) . - Прш.t . перев.
3
Градиентный (наискорейший) подъем (gradient ascent) - алгоритм градиентного подъема,
инкрементный алгоритм оптимизации, или поиска оптимального решения, где приближе
ние к локальному максимуму функции идет шагами, пропорциональными величине гради
ента этой функции в текущей точке. - Прим . перев .
' Речь о методе под названием «оценка максимального правдоподобия». Это метод оценива
ния неизвестного параметра путем максимизации функции вероятности, в результате кото
рой приобретаются значения параметров модели, которые делают данные «ближе» к реаль
ным. - Прим . перев.
78 Обзор 1СЛассификаторов с исполъзование.м библиотеки scikit-leam
5
j(w), если у= 1
j(w), если у= О
4 1
1
з
,,-._
~
:::::;
2
• •11 о
ххх
"'
""'
""'"'
ооо 2
о
Q.
ООо
~
....
"'Q.
"'
с:[
"'"
з.
"'t
"'
с:
"'"'
""'"'
Q.
3"'
-1
.}j:
-2
-2 -1
длина лепестка [стандартиз и рованная]
80 Обзор классифшсаторов с исполъзование.м библиотеки scikit-leam
Глядя на прив еде нный выше пример исходного кода, который использовался для
тренировки модели LogisticRegression, вам, наверное, теперь интересно, что это за за
гадочный параметр С. Мы разберемся в нем через пару секунд, а пока в следующем
подразделе сначала кратко остановимися на понятии переобучения и регуляризации.
В свою очередь, мы можем предсказывать вероятность принадлежности образцов
классам при помощи метода predict_proba. Например, можно предсказать вероятно
сти первого образ ца ириса щетинистого:
8
-l(w)= ( у--(1-у)
1 1 ) -ф(z).
8
owj ф(z) 1-ф(z) owj
8
Теперь можно в наше первое уравнение повторно подставить -ф(z) = ф(z)(1 - ф(z))
8z
и получить следующее:
w := w + лw.
Лw = 11Vl(w).
Поскольку максимизация логарифмического правдоподобия эквивалентна ми
нимизации определенной ранее функции стоимости], мы можем записать правило
обновления на основе градиентного спуска следующим образом:
w := w + Лw, Лw := - 11V](w).
Таким образом, оно эквивалентно правилу на основе градиентного спуска в ада
лине из главы 2 «Тренировка алгоритмов машинного обучения для задачи классифи
кацuu'i>.
Х2 \ Х2 ~ Х2
\
\ о о,/ о:
о, + о /+ ~
""+
о '.+ + о•
+ ~ +
о +~\+ ' + " +
о +'t ...+ ~''+ + +
о о ... о~---ь-"
Переобучение Х1 Хоро ш и й ком про ми с Х1 Недообуч ен ие Х1
( в ысо кое сме ще н ие) ( вы со кая ди сперси я)
С = _!.
л
weights , pararns = [] , []
for с in np . arange (- 5, 5 ) :
lr = Logistic Regression (C=lO**c , randorn_sta t e=O )
lr .fit(X_train_s t d , y_ train )
weigh t s . append (lr . coef_ [l] )
pararns . appe nd (lO**c )
weights = np . array (weights )
plt . plot (pararns , we i ghts [ :, О] , lаЬ еl= ' дл и на лепес тка ' )
plt.plot (pa r arns , weight s[ :, 1] , l i ne st yl e=' -- ', l аЬ еl= ' шири н а л е пе стк а ' )
plt . ylabel ( ' вeco в o й коэффициент ' )
plt . xlabel ( ' С ' )
plt . l egend (loc= ' upper left ')
plt . xscale ( ' log ' )
plt . show ()
Учитывая, что метки классов в наборе данных iris в библ иоте ке scikit-learn равны {О, 1, 2} ,
1
этот класс должен быть 2-м в наборе отсортированных в восходящем порядке данных, обо
з н а чая метку кла сса 1. - Прим . перев .
84 Обзор классификаторов с использованием библиотеки scikit-leam
длина лепестка
ширина лепестка
....
:z:
"':'§-
!""
С>
>S
С>
"'8 '
~ -1 '
''
''
-2
Параметр С
с+
Поскольку всестороннее освещение отдельных алгоритмов классификации выходит за
рамки этой книги. настоятельно рекомендуем читателям, которые хотят узнать о логис
тической регрессии больше. обратиться к книге доктора Скотта Менарда "догистиче
ская регрессия: от вводного курса до продвинутых концепций и приложений;, (Dr. Scott
Menard, Logistic Regression: From Introductory to Advanced Concepts and Applications)
издательства "sage;, .
Зазор
\
Х2 1. Х2
\ 1
\ 1
'' \ !f. + + Граница решения
'' ~ и/х= О
+ ++
1\
о ~· , « Положительная »
о "•' \\ « Отрицательная » --- гиперплоскость
W0 + u/xpos = 1; (1 )
W0 + W тХпсg = -1. (2)
Если вычесть два линейных уравнения (1 ) и (2) друг из друга, то получим:
т
=> w (xpos - хпсg) = 2.
Можно выполнить его нормализацию по длине (величине ) вектора w, которая
определяется следующим образом:
WT ( xpos - Xneg ) 2
llwll l wl'
Тогда левую часть предыдущего уравнения можно интерпретировать как расстоя
ние между положительной и отрицательной гиперплоскостями, т. е. ту самую мар
жу, которую мы хотим максимизировать.
Теперь целевая функция SVM сводится к максимизации этого зазаора путем мак-
2
симизации lwll с учетом ограничения, что образцы классифицированы правильно.
Эти два уравнения, в сущности, говорят о том, что все отрицательные образцы
должны попасть на одну сторону от отрицательной гиперплоскости, тогда как все
положительные образцы должны остаться за положительной гиперплоскостью. Это
можно записать более сжато следующим образом:
!+
~ 1 tl + +
+
+
ol
о l
"'""___v......................1 _,,,о,__......-• xl
1
'-----.---.........,..........,.........,...х1
Большая велич и на па рам етра С Малая вел ич и на пара м етра С
Изучив ключевые идеи, лежащие в основе линейного метода SVM, теперь натре
нируем модель SVM для классификации различных цветков в нашем наборе дан
ных цветков ириса:
••• о
ххх
о:'
"' ооо 2
"'"'"'
"'
о
CL
~
,_
s:
CL
:&
:r
"'
~
"'""t;
....
...
с
"'"'"'s: -1
CL
s:
3
-2
-z -1
длин а л еп естка [ста нда ртизирова н н ая]
88 Обзор 1'/lассификаторов с использованием библиотеки scikit-leam
с+
Логистическая регрессия в сопоставлении с методом опорных векторов
В практических задачах классификации линейная логистическая регрессия и линейные
методы опорных векторов часто приводят к очень похожим результатам. Логистическая
регрессия пытается максимизировать условные вероятности тренировочных данных ,
что делает ее более подверженной выбросам, чем методы опорных векторов (SVM). Ме
тоды SVM главным образом сосредоточены на точках, ближайших к границе решения
(опорных векторах). С другой стороны, преимущество логистической регрессии состоит
в том, что это более простая модель, которую проще реализовать. Кроме того , модели
логистической регрессии можно легко обновлять, что выглядит привлекательным при
работе с потоковой передачей данных. '
plt.scatter(X_xor[y_xor==l , О] , X_xor[y_xor==l , 1] ,
с= 'Ь', marker= ' x ', label= ' l ' )
plt.scatter(X_xor[y_xor==- 1, 0] , X_xor[y_xor==- 1, 1],
c= ' r ', marker= ' s ', label= '- 1 ')
plt . ylim (- 3 . 0)
pl t . legend ()
plt . show ()
После выполнения приведенного выше исходного кода у нас будет набор данных
XOR со случайным шумом, как показано на нижеследующем рисунке:
х
х
•
х
х
х х
~
~
х8
•
х
""' • • •
• • ••
)\.., :х
rl'
хХ х )f')!x
х
х х ~ .~r1'8 °JJ •
• •
• • •
~х х хх ~
х
8
х х х 3~
':18
,_,.
х
•х х
••• • •
• r•
'f!c х хх х х
•• • х
1118
• . . , 1 •• " ~ хх х
• "-..; •11 ~ ~.,,,х !Е х
-1
-z
•
. ".-. . • • • •
il
•
х
"
• ~
х х.
х
х
х
х
•
х
-з~~~~~~~~~~~~~~~~~---~~~~~~~~~
-3 -2 -1
Это дает нам возможность разделить эти два показанных на графике класса ли
нейной гиперплоскостью, которая становится нелинейной границей решения, если
ее спроецировать назад на исходное пространство признаков:
1.5 , - - - - - - - - - - - - - ,
1.0
. . ..
.,. . t :• •. . .
2.0
1.5
o.s
..
·"' .. . . ' .. ~-.
1•
1.0
~=· ."i.~. ~:-:4
о.о
: .. -.·1·· ~
·'с...
: :ь~
. '· ...
.;
:·: ф
0.5
23
-o.s "
:: " .
. ............ "11".\
·11·•
..
••
,,,. . о.о
-1.0
• • • "
" ..
• • • • •• 1 • • •
- 1 ~1·'-=
, ,--,_1~
.о---=-0-=-
.• -7'0.0,---0=".s----,1"'"
.o--,'1.s
z . 1.0 1.5 -1 .т- l.О- . z
2 1
х
1
1. s.-------------~
1.0 .. ..
o.s
. ·:· . t :· •. . : 1
..
,. ·' •i
о.о :
".. :·:.
- 0.5
•"' .
- 1.0
.. ."........
~:
.. .
1
- -.:1.s - 1.0 -o.s о.о o.s 1.0 i.s
Х1
x(i) x(j)2)
k(x(i>,x(j)) == ехр
[ 2~2 •
-1
-2
-3
-3 -2 -1
92 Обзор классификаторов с использованием библиотеки scikit-leam
••• о
..."'
:i:
:i:
ххх
ооо
1
2
"'"'
о ООо
С>.
;!;
....""
С>.
"'
с<
:i:
~
"'><
~
i::::
"'
.}j:
с;
-1
"'
:i:
""
С>.
""3
-2
-2 -1 о
дл и на лепестка [стандартизированная]
Теперь на итоговом графике можно увидеть, что граница решения вокруг классов
О и 1 намного компактнее относительно большой величины параметра у:
••• о
х хх 1
~
х
х
"'"'
о
с.
ооо 2
ООО тестовый набор
8mJ 0 0>
0oo
О
00
ООО
~
о оо о
0 0
....s
~
о
о
"'
~
"'
..."'t;
с
"'
с:
-1
"
"'s
х
с.
s
3
-2
-2 -1
дл и на лепестка [стандартизи рова н ная]
Хотя эта модель очень хорошо подогнана под тренировочный набор данных, т. е.
хорошо им соответствует, такой классификатор, скорее всего, будет иметь высокую
ошибку обобщения на ранее не наблюдавшихся данных, а это, в свою очередь, сви
детельствует о том, что оптимизация параметра у играет одинаково важную роль
в управлении переобучения.
Дождливо
Пасмурно
Да
Остаться дома
Для того чтобы расщепить узлы в самых информативных признаках, нам нужно опре
делить целевую функцию, которую мы хотим оптимизировать алгоритмом обучения
на основе дерева. Здесь наша целевая функция состоит в максимизации прироста ин
формации при каждом расщеплении, которую мы определяем следующим образом:
т N.
IG(Dp,f)=I(Dp)- I - I(D1)·1
j=t NP
Здесь p(ilt) - это доля образцов, которая принадлежит классу i для отдельно взя
того узла t. Следовательно, энтропия равна О, если все образцы в узле принадлежат
одному и тому же классу, и энтропия максимальна, если у нас равномерное рас
i=i i=i
i=i
Мера неоднородности, или мера Джини (Gini impurity), - это вероятность, что случайный
1
образец будет классифицирован правильно, если произвольно выбрать метку согласно рас
пределению в ветви дерева. Не следует путать с коэффициентом Джини в статистике, где
он представляет собой показатель степени расслоения общества по отношению к какому
либо признаку. - Прим. перев.
2
Речь идет о вероятностной мере неопределенности Шеннона (или информационной энтро
пии). Данная формула означает, что прирост информации равен утраченной неопределен
ности. - Прщt. перев.
96 Обзор 1СЛассификаторов с использование.м библиоте~сu scikit-leam
Iit) = 1 - max{p(ilt)}.
А в
3
А: lн(Dлевый)=1-4=0.25;
3
А: lн(Dправьrй)=1-4=0.25;
4 4
A:IGн =0.5- 0.25- 0.25=0.25;
8 8
4 1
В: lн(Dлевый)=1-в=3;
В: lн(Dправый)=1-1=0;
6 1
В: IGн =0.5- х -0=0.25.
8 3
Однако_мера неоднородности Джини предпочтет расщепление в сценарии
в( IGн = 0.16) над сценарием A(IGE = 0.125), который действительно более однороден:
lc(Dp)=1-(0.5 2 +О.5 2 )=0.5;
А Iс(D,"ы,)=1-[Ш +Ш'Н=0375;
А: Ic (D,,~"") =1-[Ш' +Ш' н =0375;
4 4
A:IGc =0.5--0.375--0.375=0.125;
8 8
Обучение на основе деревьев peuurnuй 97
цщ~">=1-[Ю +Ш'Н=о4,
2
В: Iс(Dправый)=1-(1 +0 )=0;
2
6 - -
В: IGc =0.S- 0.4-0=0.16.
8
Аналогичным образом энтропийный критерий будет отдавать предпочтение сце
нарию B(IG11 = 0.31) перед сценарием A(IG11 = 0.19):
I н(DР) = -(O.Slog 2(0.5)+0.5log 2 (0.5)) = 1;
6
В: IGн =1- 0.92-0=0.31.
8
Для более наглядного сравнения трех разных критериев неоднородности, которые
мы обсудили выше , построим график индексов неоднородности для вероятностного
диапазона [О, 1] для класса 1. Отметим, что мы также добавим масштабированную
версию энтропии (энтропия/2) , чтобы уч есть , что мера неоднородности Джини яв
ляется промежуточной между энтропией и ошибкой классификации. Соответству
ющий исходный код выглядит следующим образом:
def entropy ( р ) :
return - p*np. log2 (р) - (1 - р) *np . log 2 ( (1 - р ))
def error ( р) :
return 1 - np . max([p , 1 - р] )
х = np . arange ( О . О , 1 . О , О . О 1)
[ ' Ыасk ', ' lightgray', 'red ' , ' green ' , ' cyan']):
line = ax.pl ot (x , i, label=lab, linestyle=ls, lw=2, color=c)
ax.legend(loc='upper center ', bbox_to_anchor=(0 . 5, 1.15) ,
ncol=З , fancybox=True, shadow=False)
ax.axhline(y=0 . 5, linewidth=l , color= ' k', linestyle= ' - - ')
ax.axhline(y=l.0 , linewidth=l, color= ' k ' , linestyle= ' -- ' )
plt.ylim ( [O , 1.1])
plt . xlabel ('p(i= l ) ')
рlt.уlаЬеl( ' Индекс неоднородности')
plt . show ()
1.0
0.8
s:
t:;
~
g. 0.6
~
"'~
:с
~ 0.4
- - - - - - ------
,;
--
- - ... .....-·.,..-·.. - --.......- ,- - - - - - - - - -
',
:s::
:с
; ' '
,, ''
;
0.2 ,, '
'
,' '
Деревья решений могут создавать сложные границы решения путем деления про
странства признаков на прямоугольники. Однако мы должны быть осторожными,
поскольку чем глубже дерево решений, тем сложнее становится граница решения,
что может легко закончиться переобучением. Теперь, используя scikit - leaш, натре
нируем дерево решений с максимальной глубиной 3, применив в качестве крите
рия неоднородности энтропию. Хотя для целей визуализации может понадобиться
шкалирование признаков, отметим, что масштабирование признаков не является
необходимой составной частью алгоритмов деревьев решений . Соответствующий
исходный код выглядит следующим образом:
Обучение на основе деревьев реuиний 99
3.0 ••• о
ххх 1
2.5 ооо 2
ООО тестов ы й набор
'"i' 2.0
~
"'
...""t:; 1.5
...
с
"'"'
х 1.0
"'
Q.
:s:
3 0.5
о. о
-0.5
о 4
длина леп естка [см ]
3.0 ••• о
ххх 1
2.5 ооо 2
ООо тестовы й набор
~ 2.0
~
\:!
...t; 1.5
...
с
с;
"' 1.0
:z:
s:
"'"
s:
.JI:
3
0.5
о.о
-0.5
4
дл ин а л е п естка [см]
k ближайших соседей - aлгopumJot ленивого обучения 103
с+
Параметрические модели в сопоставлении с непараметрическими
Алгоритмы машинного обучения можно сгруппировать в параметрические и непара
метрические модели. Используя параметрические модели, мы выполняем оценивание
параметров из тренировочного набора данных, чтобы извлечь функцию, которая сможет
классифицировать новые точки данных, больше не требуя исходного тренировочного
набора данных. Типичными примерами параметрических моделей являются персептрон,
логистическая регрессия и линейный метод опорных векторов (SVM). Напротив, непа
раметрические модели не могут быть охарактеризованы фиксированным набором па
раметров, и число параметров в них растет вместе с тренировочными данными. Двумя
примерами непараметрических моделей, которые мы видели до сих пор, являются клас
сификатор на основе дерева решений/случайного леса и ядерный метод SVM.
Модель k ближайших соседей (KNN) принадлежит подкатеrории непараметрических
моделе й , которая описывается как обучение на примерах (или по прецедентам , in-
stance-based). Модели, в основе которых лежит обучение на примерах, характеризуются
запоминанием тренировочного набора данных, и ленивое обучение является особым
случаем обучения на примерах, которое связано с отсутствующей (нулевой) стоимо
стыо во время процесса обучения.
lx +
lx ...
3 х h.
Предсказать
Задав пять соседей в модели KNN для этого набора данных, мы получаем относи
тельно гладкую границу решения, как показано на нижеследующем рисунке:
k ближайuшх соседей - Шlгopum.At ленивого обучения 105
а:
..
ххх
" о
1
"'~
:z: ооо 2
"'
о
с:>.
;:;
"',_
с:>.
;]!:
:z:
"'
~
"'
"
t;
"'"'
"'
""' -1
х
"'
с:>.
"'3
-2
-2 -1
длина лепестка [стандартизированная]
тая евклидова мера расстояния. При этом если мы используем евклидову меру рас
стояния, также важно данные стандартизировать, благодаря чему каждый признак
вносит в расстояние одинаковый вклад. Расстояние Минковского (' minkowski' ),
которое мы использовали в приведенном выше примере, является простым обоб
щением евклидова расстояния и расстояния городских кварталов (манхэттенского ),
которое можно записать следующим образом:
Проклятие размерности
с+ Важно упомянуть, что модель KNN очень восприимчива к переобучению из-за явления
проклятия размерности , когда пространство признаков становится все более разрежен
ным для растущего числа размерностей тренировочного набора данных фиксированно-
106 Обзор 1СЛассификаторов с использованиеJ.t библиотеки scikit-leam
Резюме
В этой главе вы узнали о разнообразных алгоритмах машинного обучения, которые
используются для решения линейных и нелинейных задач . Мы убедились, что де
ревья решений особенно привлекательны, в случае если интерпретируемости моде
ли уделено должное внимание. Логистическая регрессия - это не только полезная
модель для динамического ( онлайнового) обучения методом стохастического гра
диентного спуска, она позволяет также прогнозировать вероятность отдельно взя
к
ачество данных и объем полезной информации, которую они содержат, являют
ся ключевыми факторами, которые определяют, как хорошо алгоритм машинно
го обучения сможет обучиться . Следовательно, крайне важно сначала набор данных
обязательно проверить и подвергнуть предварительной обработке и только потом
подавать его на вход обучаемого алгоритма. В этой главе мы обсудим ключевые ме
тоды предобработки данных, которые помогут создавать хорошие машиннообучае
мые модели .
>»
import pandas as pd
from io import StringIO
А в с D
о 1.0 2.0 3.0 4.0
1 5.0 6.0 NaN 8.0
2 10.0 11.0 12.0 NaN
При помощи приведен н ого выше исходного кода мы считали данные в формате
CSV в таблицу данных DataFram e библиотеки pandas, используя для этого функцию
read_csv, при этом отметим, что две пустые ячейки были заменены на NaN. Функция
StringlO в прив еденном выше примере использовалась просто в ц елях иллюстрации .
Она позволяет считывать строковое значение, присвоенное переменной csv_data ,
в объект DataFrame библиотеки pandas так, как будто это обычный СSV-файл на
нашем же стком диске .
>»
df. i s null () . sum()
А О
в о
с
D 1
dt ype : i nt6 4
Один из самых простых способов реш ения проблемы пропущенных данных состо
ит в том, чтобы просто удалить соответствующие признаки (столбцы) или образцы
(строки) из набора данных полностью; строки с пропущенными значениями можно
легко отбросить при помощи метода dropna :
>»
df. dropna ()
А в с D
о 1.0 2.0 3.0 4.0
Точно так же можно отбросить столбцы, которые в любой строке имеют, по край
ней мере, одно значение NaN; это делается путем установки параметра оси ax is в 1:
>»
df.dropna (ax is=l)
А в
о 1.0 2.0
1 5.0 6.0
2 10.0 11.0
А в с D
о 1.0 2.0 3.0 4.0
1 5.0 6.0 NaN 8.0
2 10.0 11.0 12.0 NaN
# отбросить строки, если в них менее 4 значений нe - NaN
df.dropna(thresh=4 )
А в с D
о 1.0 2.0 3.0 4.0
#отбросить строки , если в оп ределе нных столбцах (здесь ' С') имеется NaN
df.dropna(subset=[ 'C' ] )
А в с D
о 1.0 2.0 3.0 4.0
2 10.0 11.0 12.0 NaN
»>
from sklearn . preprocessi ng import I mput er
i mr = Impu t er(missing_values=' Na N', strategy='mean', a xi s =O )
i mr = imr.fit( df )
i mpu t ed_da t a = imr . transfo r m(d f. va l ues)
imputed_da t a
array ( [ [ 1. , 2 ., 3 ., 4 . ] '
[ 5. ' 6 .' 7 . 5, 8 . ] '
[ 10. , 11 ., 12 ., 6 . ]] )
Здесь каждое з нач е ние NaN заменено на соответствующее среднее значение, кото
рое вычисляется отдельно для каждого признакового столбца. Если поменять значе
ние оси axis=O на axis=l, то будет вычислено среднее строки. Другими значениями
параметра strategy может быть median либо most_frequent, где в последнем случае
пропущенные з нач ения меняются на самые частотные значения. Это удобно при
импутации значений категориальных признаков.
Тренировочные Тестовые
данные данные
Модель
est.transform (X_train )
Трансформированные Трансформированные
тренировочные данные тестовые данные
тода predict можно использовать для выполнения прогнозов о новых образцах дан
ных, как проиллюстрировано на нижеследующем рисунке:
Тренировочные Тренировочные
данные метки
est.fit(X_train, y_train)
Модель
est.predict(X_test)
Идентифициро
ванные метки
112 Создание хороших тренировочных наборов - предобработка данных
например XL = L+ 1= М + 2.
>»
size_mapping # словарь соответствий
'XL': 3 ,
'L' : 2,
'М' : 1)
df[ ' размер ' ] df [ 'размер'] . map(size_mapping)
df
Обработка категориальных дттых 113
сваиваем отдельно взятой строковой метке. Поэтому можно просто нумеровать мет
ки классов, начиная с О:
>»
impor t nurnpy as np
class_mapping {labe l:idx for idx,label in
enurnerate(np.unique(df[ ' мeткa ' ] )) )
class_mapping
{ ' класс 1 ' : О, 'класс2 ' : 1)
>»
df [ ' метка ' ) df[ ' метка ' ] . rnap(class_rnapping)
df
»>
from s kl earn.preprocessing impo r t LabelEncoder
class_l e = LabelE ncoder()
у= class _l e.fi t _ transform( d f [ 'мeткa' ) .values)
у
array( [0, 1, 0) )
»>
class_l e. i nverse_ trans f orm( y)
arra y ( [ ' классl', 'класс2' , 'классl' ] , dtype=object)
»>
Х = df[ [ 'цвет ' , ' раз м ер ', 'цeнa' J ).values
color_ le = LabelEncoder()
Х [ :, 0) = color_ le.fit_ transform(X [ :, 0] )
х
array( [[ l , 1, 10.1 ) ,
[2, 2, 13 . 5),
[0 , 3, 15.3 ) ), dtype=ob j ect)
обучения теперь предположит, что зеленый больше синего, а красный больше зелеио
zо. Хотя это допущение является неправильным, алгоритм все равно способен про
извести полезные результаты. Однако эти результаты не будут оптимальными.
Общее обходное решение этой проблемы состоит в использовании метода, кото
рый наз ывается прямым кодированием . В основе этого подхода лежит идея, кото
1
рая состоит в том, чтобы в столбце номинального признака создавать новый фиктив
ный признак для каждого уникального знач ения. В данном случае признак «цвет »
можно преобразовать в три новых признака: синий, зеленый и красный. Затем
можно использовать двоичные значения для указания отдельно взятого цвета об
разца; например, синий образец может быть закодирована как синий=l, зеленый=О,
красный = О. Для выполнения этого преобразования можно воспользоваться классом
OneHotEncoder, который реализован в модуле scikit-learn.preprocessing:
>»
from sklearn . prep rocessing i mpo rt OneHotEncoder
ohe = OneHotEncoder (cat egorical_f eat ur es=[O] )
ohe . fit_ transform ( Х ) . toarray ()
array( [[ О . , 1. , О . , 1. , 10 . 1] ,
[о . / о. ' 1. ' 2 . / 13 . 5] ,
[ 1 .• о . ' о . ' 3 . / 15 . 3]] )
»>
pd . get dummies (d f [ [ ' це на ', 'цве т ', 'разме р ' ]] )
1 13.5 2 о 1 о
2 15.3 3 о о 1
Прямое кодирование (oп e- hot eпcodiпg) - двоичный код фи ксированной длины , содержа
1
щий только одну 1. При обратном кодировании имеется только один О. Длина кода опреде
л я ется коли<1 еством кодируе мых приз наков, или объектов. - Прим . перев.
116 Создание хороших тренировочных наборов - предобработка данных
Набор данных сортов вин Wine - это еще один общедоступный набор данных,
предлагаемый репозиторием машинного обучения UCI ( https://archive.i cs.uci. edu/ml/
dat aset s/Wine); он состоит из 178 образцов белых вин с 13 признаками, описываю
щими их различные химические свойства .
Пользуясь библиотекой pandas, мы прочитаем набор данных вин непосредствен
но из репозитория машинного обучения UCI:
»>
url = ' https://archive.ics . uci.edu/ml/machinelearning-databases/wine/wine.data '
df_wine = pd.read_csv(url, header=None)
df wine . columns [ 'Метка класса', 'Алкоголь' ,
'Яблочная кислота', ' Зола ' ,
< Щелочность золы> , <Магний>,
<Всего фе н ола>, <Флаваноиды >,
<Фенолы нефлаваноидные> , <Проантоцианины> ,
"'"
Q ~ QJ
:3 ~ ~
~
=
5 Q
"' =
Q
= =
..Q
t ..... к
~
=
:.:
..Q
t
о:
Q
:3
~
~ =
"'Q:='= Q
=
м
~ ~
-
"'"' =
~
!:"'
..Q
о:
Q
....
~
~
Q
~ '=
~
QJ
-6'
Q
"'=
-@="'=
§
!;:
=
=
"'
="'
:.:Q
о
ci- а
= ""' "'
QJ
§
"'=
е
Q Q"'
Q
::; о: "'
= ~i
Q
"'а
QJ N\O
о: Q
~
QJ -6' !::
с"'
е
QJ
::?J"'
QJ
\О Q. Q.
"' =
о
::?J
1 14.23
t:i::
1.71
м
2.43 =
15.6 127
~
2.80 3.06
е~
0.28 2.29 5.64
о
1.04
о~
3.92 1065
1 1 13.20 1.78 2.14 11.2 100 2.65 2.76 0.26 1.28 4.38 1.05 3.40 1050
2 1 13.16 2.36 2.67 18.6 101 2.80 3.24 0.30 2.81 5.68 1.03 3.17 1185
3 1 14.37 1.95 2.50 16.8 113 3.85 3.49 0.24 2.18 7.80 0.86 3.45 1480
4 1 13.24 2.59 2.87 21.0 118 2.80 2.69 0.39 1.82 4.32 1.04 2.93 735
(+ Следует учесть, что когда мы делим набор данных на тренировочный и тестовый набо
ры данных, то мы лишаемся части ценной информации, из которой алгоритм обучения
мог бы извлечь выгоду. Поэтому мы не выделяем слишком много информации для тес
тового набора. Однако чем меньше тестовый набор, тем менее точной является оценка
ошибки обобщения. Деление набора данных на тренировочный и тестовый наборы на
прямую касается уравновешивания этого компромисса . На практике обычно использу
ются пропорции разделения 60:40, 70:30 или 80:20, в зависимости от размера исходного
набора данных. Однако для крупных наборов данных разделение на тренировочный
и тестовый наборы в пропорциях 90:10 или 99:1 также распространено и приемлемо.
Вместо того чтобы после тренировки и оценки модели отбросить выделенные тестовые
данные, неплохо для настройки оптимального качества также переобучить классифика
тор на всем наборе данных.
Здесь x<i) - это отдельно взятый образец, x 111 ;n - наименьшее значение в признако
вом столбце и х""'' - соответственно наибольшее значение.
Процедура минимаксного масштабирования реализована в библиотеке scikit-
leaш и может быть применена следующим образом:
L2:JJwll~ = I:wJ.
j=1
Построим график контуров выпуклой функции стоимости для двух весовых коэф
фициентов w1 и w2• Здесь мы рассмотрим функцию стоимости с извлечением весов
в виде суммы квадратичных ошибок (SSE), которую мы использовали для ADALINE
в главе 2 «Тренировка алгоритмов машинного обучения для задачи Ю1ассификации~, по
скольку она симметрична и проще для изображения на графике, чем функция стои
мости логистической регрессии; хотя к последней применимы те же самые принци
пы. Напомним, что наша задача состоит в том, чтобы найти комбинацию весовых
коэффициентов, которые минимизируют функцию стоимости для тренировочных
данных, как показано на нижеследующем рисунке (точка посередине эллипсов):
Минимизировать
--------- стоимость
------ -------
Отбор содержательных признаков 121
Миним и зировать
/ / ,/- ---------:~-
! 1
r ,
\ \
\ \
//
_,.../·
---------~-------<----~---";----~-- ~
--
Ми н имизировать
стоимость + ш тра ф
(w1 =О)
'' \ ''
'
'' ''
' т
\
JJwll1= l:lwjl
j=1 llwll2 =~t, wj
Отбор содержательных признаков 123
»>
lr = LogisticRegression (penalty= 'll', С=О.1)
>»
lr.intercept_
array([ - 0.38379237 , - 0.1580855 , - 0.70047966])
1
Функции в виде у = f(x ) могут содержать две и более точек пересечения оси Х, а некото
рые двухмерные математические отношения, такие как круги, эллипсы и гиперболы, могут
иметь более одной точки пересечения оси У. Точки пересечения оси Х функций, если они
есть, часто труднее локализовать, чем точку пересечения оси У, т . к. нахождение последней
закл ючается всего лишь в оценке функции при х = О. - Прим. перев.
124 Создапие хороших трепuровочпых паборов - предобработка даппых
Отмечаем для себя, что весовые векторы разрежены, т. е. имеют всего несколько
ненулевых записей. В результате L1-регуляризации, которая служит в качестве ме
тода отбора признаков, мы только что натренировали модель, устойчивую к потен
циально нерелевантным (лишним) признакам в этом наборе данных.
Наконец, построим график траекторий регуляризации , т. е. весовых коэффициен
тов, разных призна ков для разных сил регуляризации:
Чтобы учесть постоянное смещение (Ьias unit), единицы должны быть заменены на нули
1
(как в главе 2). При этом отметим, что scikit-learn хранит смещение и веса раздельно, и по
т
этому лучше записать так: w1x 1 + ... + rш";хт + Ь = LXjlШ1 + Ь = w x+ Ь. - Прим. перев.
1
j=O
Отбор содержательных признтсов 125
Алкоголь
Яблочная кислота
Зола
Щелочность золы
Магний
Всего фенола
Флаванонды
"" -10
Фенолы нефлаван о идные
Q
"
~ -15
Проантоцианины
Интенсивность цвета
-20 Оттенок
OD280_0D315 разбавленных вин
-25 Пролин
10·! 104 1Q·J 10-1 10-1 100 101 101 101 10~ 101
Параметр С
вательный прямой отбор (sequential forward selection, SFS), метод отбора « плюс 1 минус r»
(plus-1 minus-г selection), двунаправленный (Ьidiгectional search) и плавающий поиск (floating
searcl1) . См., например: http://research.cs.tamu.edu/prism/lectures/pr/pr_lll.pdf. - ПpUJ11 . перев.
126 Создание хороших тренировочных наборов - предобработка данных
Жадные алгоритмы на каждом шаге комбинаторной задачи поиска делают локально оп
с+ тимальный выбор и обычно производят субоптимальное решение задачи, в отличие от
алгоритмов исчерпывающего поиска, которые оценивают все возможные комбинации
и гарантированно находят оптимальное решение. Однако на практике исчерпывающий
поиск часто в вычислительном плане не выполним, тогда как жадные алгоритмы допус
ство признаков не будет содержать нужное число признаков. Для определения того,
какой признак удалять на каждом шаге, нам нужно определить критериальную
функцию], которую мы хотим минимизировать. Вычисленный данной функцией
критерий может быть просто разницей в качестве классификатора после и до уда
ления отдельно взятого признака. Тогда удаляемый на каждом шаге признак можно
просто определить как признак, который максимизирует этот критерий; или в более
интуитивных терминах на каждом шаге мы устраняем признак, который вызывает
наименьшую потерю качества после удаления. Основываясь на предыдущем опре
делении алгоритма SBS, его можно в общих чертах обрисовать в 4 простых шагах:
1. Инициализировать алгоритм, назначив k = d, где d- это размерность полно
признакового пространства.
class SBS():
def init (self, estimator , k_features,
scoring=accuracy_score,
test_size=0.25, random_state=l):
self . scoring = scoring
self.estimator = clone(estimator)
self.k- f eatures = k- features
self.test- size = test - size
self.random state random state
def fit(self, Х , у) :
Х train, X_ test, у train, y_test
Отбор содержателы1ых nрUЗ11аков 127
dim = X_train.shape[l]
self.indices = tuple(range(dim))
self.subsets = [self.indices_]
score = self. _calc_score(X_train, y_train ,
X_test , y_test , self .indices
self.scores [score]
best = np.argmax(scores)
self.indices_ = subsets[best]
self.subsets_ .append(self.indices
dim - = 1
self.scores .append(scores[best])
self.k score self.scores [- 1]
return self
Несмотря на то что наша реализация SBS и так уже разделяет набор данных внут
ри функции fit на тестовый и тренировочный наборы данных, мы все же подали на
вход алгоритма тренировочный набор данных X_train. Метод fit класса SBS затем
создаст новые тренировочные подмножества для тестирования (валидации) и трени
ровки. По этой причине этот тестовый набор также называется проверочным (вали
дационным) набором данных. Этот подход необходим для того, чтобы не дать на
шему исходному тестовому набору стать составной частыо тренировочиы:х даиных.
Напомним, что наш алгоритм SBS на каждом шаге накапливает оценки в подмно
жестве лучших признаков, поэтому перейдем к более захватывающей части нашей
реализации и построим график верности классификации классификатора KNN, ко
торый был вычислен на перекрестно-проверочном наборе данных. Соответствую
щий исходный код выглядит следующим образом:
plt.grid()
plt. show ()
1.10
1.05 .. .. . . .. . .... ;
1.00
0.95
. ..
.. . . .... ... ... . . . .. . . . . .. . .. . ... ..... .. .
...t; .. ..
о
0.90 ... . ·: . .. .. . .. . . . . .:· . . . . . . . ... . . . . . . .. . .. . -. .. .. ...... .. ·; .. . ... . . . .. .
..
~ ~
..
0.80 .. . .. . .. ...;....... . .. . .. :. . .
0.75
0.70
о 4 10 12 14
Число признаков
Отбор содержательных приз11тсов 129
»>
kS = list(sbs.subsets_[8 ] )
print(df_wine.columns[l: ] [k5])
Indех( [ ' Алкоголь ', ' Яблочная кислота ', ' Щелочность золы 1
, 'Оттенок', 'Пролин'],
dtype = ' object ')
>»
knn.fit (X_train_std, y_train)
При меньшем, чем наполовину, числе исходных признаков из набора данных сор
тов вин верность предсказания на тестовом наборе улучшилась почти на 2%. Кроме
того, мы уменьшили переобучение, которую можно определить по малому разрыву
между верностью на тестовом наборе ( -96.3%) и верностью на тренировочном на-
боре ( -96.0%).
Алrторитмы отбора признаков в библиотеке scikit-leam
с+ Библиотека scikit-learn располагает многими другими алгоритмами отбора признаков.
Они включают в себя рекурсивный алгоритм обратного устранения признаков на осно
ве весовых коэффициентов признаков, методы отбора признаков по важности на основе
деревьев и одномерные статистические методы. Всестороннее обсуждение различных
методов отбора признаков выходит за рамки этой книги. Впрочем, хороший сводный
обзор с показательными примерами можно найти на http://scikit-learn.org/staЫe/modules/
feature_selection.html.
130 Создание хороших тренировочных наборов - предобработка данных
>»
from sklearn . ensemЫe import RandomForestClassifier
feat_labels = df_wine . columns[l:]
forest = RandomForestClassifie r (n_estimators=lOOO O,
random_state=O,
n_ j obs=-1)
forest .fit(X_ train, y_train )
importances = forest.feature importances
indices = np . argsort(importances) ( ::-1 ]
for f in range(X_ train . shape[l]):
print ("%2d) %- *s %f " % (f + 1, 30 ,
feat_labels[indices[f]] ,
importances[indices[f]]))
1) Интенсивность цвета 0 . 182483
2) Проли н 0 . 158610
3) Флаваноиды 0 . 150948
4) 00280 00315 разбавленных вин 0.131987
5) Алкоголь 0 . 106589
6) Оттенок 0 . 078243
7) Всего фенола 0.060718
8) Щелочность золы 0 . 032033
9) Яблочная кислота 0.025400
10) Проан тоцианины 0 . 022351
11) Магний 0.022078
12) Фенолы нефлаваноидные 0 . 014645
13) Зола 0 . 013916
plt.title('Baжнocти признаков')
plt . bar(range(X_ train.shape[l]), importances[indices],
color= ' lightЫue ', align= 'center ' )
plt . xticks(range(X_ train . shape [l]) ,
Определение ва:ж:ности признаков при по.мощи случайных лесов 131
Важности признаков
0.20 .---...---,----т----т--r---т---.---.----т---,...-.-.,.---,--....,...---.
0.15
0.10
0.05
....... ~
:;; ж ..а
"' ... :;; :;;
""'1=:s: "'
:;;
...
:s:
'°
о
"'=t
"' "'
"[
:s:
"' "'
е ж
"'
о
ж "'
о
о
5
ж
"'"'т
е
~
о
;: ...
"
;:
=t
о
....
ж
~
"'::z:б ~ a:i о ;:;
~
... "'"'
о
"'
о
С>.
"'
-е-
....
"' 3" "" с= "'
:i:
::с
:s:
С>.
"'
,..,
"' :;;
а
о :i:
о,
С>
&"'
00
N
о
о
ности, тогда как информация о другом признаке ( или признаках) может оказаться
не захваченной полностью. С другой стороны, нам не нужно беспокоиться по этому
поводу, если нас интересует только предсказательная способность модели, а не ин
терпретация важностей признаков. В заключение этого раздела о важностях при
знаков и случайных лесах стоит упомянуть, что в библиотеке scikit -leaгn также реа
лизован метод transform, который отбирает признаки, основываясь на определенном
пользователями пороге после подгонки модели, который очень часто применяется
в случае, если мы хотим использовать RandomForestC lassifier в качестве селектора
признаков и промежуточного звена в конвейере библиотеки scikit- leaгn; это позво
ляет соединять разные шаги предобработки с оценщиком, как мы убедимся в главе 6
«Изучение наиболее успешных методов оценки модели и тоюсой 1-tастройки гиперпа -
132 Создание хороших тренировочных наборов - предобработка данных
раметров». К примеру, можно установить порог в 0.15 для сведения набора данных
к 3 наиболее важным признакам - интенсивность цвета, пролин и флавоноиды, ис
пользуя для этого нижеследующий исходный код:
>»
Х selected = forest.transform(X_ train, threshold=0.15)
X_selected.shape
(124, 3)
Резюме
Мы начали эту главу с того, что рассмотрели часто используемые методы, которые
обеспечив ают правильную обработку пропущенных данных. Прежде чем передать
данные на вход алго ритма машинного обучения, мы также должны удостоверить
ся, что категориальные переменные имеют правильную кодировку, и мы увидели,
Сжатие данных
путем снижения размерности
в
zлаве 4 «Создание хороших тренировочных наборов - предобработка даюtЪL"С»
вы узнали о разных подходах к решению задачи снижения размерности набора
данных на основе различных методов отбора признаков. Помимо методики отбора
признаков, альтернативным подходом к решению этой задачи является методика
выделения призиаков . В этой главе вы узнаете о трех основополагающих методах,
которые помогут резюмировать информационное содержание набора данных путем
преобразования его в новое подпространство признаков более низкой размерности,
чем исходное. В машинном обучении методы сжатия данных представляют собой
важную тему; они помогают накапливать и анализировать увеличивающиеся объ
емы данных, производимые и собираемые в нашу современную технологическую
эпоху. В этой главе мы охватим следующие темы:
r1r анализ главных компонент для сжатия данных без учителя;
rr линейный дискриминантный анализ как метод снижения размерности с учи
телем для максимизации разделимости классов;
РС2
)(
)(
)(
Прежде чем рассмотреть алгоритм РСА для снижения размерности более подроб
но, сведем этот подход к нескольким простым шагам:
import pandas as pd
url = ' ht tp s : //archive .ics . uci . edu/ ml/machi ne l ea r nin g-databases/ wi ne/ wi ne . da t a '
df_wine = pd . read_csv (url , heade r=None )
LV = Лv.
>»
i mpor t nump y as np
cov_mat = np . cov (X_t r ain_s t d .T)
e igen_vals , eigen_vecs = np.li nal g. eig (cov_mat)
р rin t ( ' \nСоб с тв е нные з нач е ния \n %s' % e i ge n_va ls)
Собственные з начения
опред еляется как его евклидова норма (или евклидова длина), т . е. как квадратный корень
из суммы квадратов элементов вектора: l xll=~xl +х~ + ... +х~. - Прим. перев.
Собственная пара матрицы - это вещественное число Л. и вектор z матрицы А, если они
2
с+
Хотя функция numpy.linalg. elg предназначена для разложения несимметричных ква
дратных матриц, вы можете обнаружить, что в определенных случаях она возвращает
комплексные собственные значения.
Родственная функция numpy.linalg.elgh реализована для разложения эрмитовых (само
сопряженных) матриц, которые представляют собой численно более стабильный под
ход для работы с симметричными матрицами, такими как ковариационная матрица;
функция numpy.linalg .eigh всегда возвращает веществе нные собственные з начения.
Учитывая, что мы хотим снизить размерность нашего набора данных путем его
сжатия в новое подпространство признаков, мы отберем лишь подмножество соб
ственных векторов (главных компонент), которые содержат б6льшую часть ин
формации (дисперсии) . Поскольку собственные значения определяют величину
собственных векторов, нам нужно отсортировать собственные значения в порядке
убывания величины векторов; нас интересуют верхние k собственных векторов, ос
новываясь на значениях соответствующих им собствен ных значений. Но прежде
чем собрать эти k наиболее информативных собственных векторов, построим гра
фик долей объясненной дисперсии собственных значений.
Доля объясненной дисперсии собственного значения Л; - это просто частное соб
ственного значения \ и полной суммы собственных значений:
Л, j
d
"""
L..J j=1 JЛ,
Тогда при помощи функции cumsum библиотеки NumPy можно вычислить ку
мулятивную сумму объясненных дисперсий, которую затем можно отобразить на
графике, воспользовавшись функцией step библиотеки matplotlib:
1.2 ....----.-----.-----.-----.-----т-----.-------.
s
s
~ 0.8
5
~
•S
о
:z:
:z: 0.6
"'...,:z:
о:
..Е:
о
о: 0.4
с:
о
c:t
0.2
8 10 12 14
Главные ко м поненты
Преобразование признаков
eigen_pairs = [ (np.abs(eigen_vals[i]),eigen_vecs[: , i ] )
for i in range(len(eigen_vals))]
eigen_pairs.sort(reverse=True)
»>
w = np .h s t ack ( (eige n_pai rs [О] [1] [ :, np. newa xis] ,
eige n_pa i rs [1] [1] [ : , np.newa xi s] ))
print ( ' Матрица W: \ n ' , w)
Матрица W:
[[ 0 . 1466981 1 0 . 50417079]
[- 0 . 24224554 0 . 24216889 ]
[- 0 . 02993442 0 . 28698484 ]
[- 0 . 25519002 - 0 . 06468718 ]
[ 0 . 12079772 0 . 22995385 ]
( 0 . 38934455 0 . 09363991]
[ 0 . 42326486 0 . 01088622 ]
(- 0.30634956 0 . 018 70216]
[ 0.30572219 0 . 03040352 ]
(- 0 . 09869191 0.54527081]
[ 0 . 30032535 - 0.27924322]
[ 0 . 36821154 - 0 . 174365 ]
[ 0 . 29259713 0 . 36315461]]
x' = xW.
»>
X_train_std[O] . dot (w)
array([ 2.59891628 , 0 . 00484089] )
Х' =XW.
X_train_pca = X_t ra i n_std . dot (w)
Как видно на итоговом графике, данные более разбросаны вдоль оси Х - первая
главная компонента, чем вдоль второй главной компоненты (ось У), что согласуется
с графиком долей объясненной дисперсии, который мы построили в предыдущем
подразделе. Вместе с тем интуитивно можно отм етить, что линейный классифика
тор , вероятно, смо жет хорошо разделить классы:
140 Сжатие данных путем снижения разАtерности
"....
• •
• ,& •
11
••""
• •• •
"•: .
·-·'
.... "..,..
N
u
а.
-1
• • х
х
х х
• х
х
х ххх
•
. ""
• ••••• •
х х
~
х
ххх 2 х
-4
••• з
-5
-6 -4 -2 4 6
РС 1
z = Z. reshape(xxl . shape)
plt . contourf(x xl, хх2, Z, alpha=0.4 , crnap=crnap)
plt.xlirn(xxl.rnin(), xxl.rnax())
plt . ylirn(xx2.rnin(), xx2.rnax())
""
u
Q.
•••••••••1.• ,,,.•
..., ".
о
о
-1 00
ое о 0
о
-2 ••• •1 •• ••
ххх 2
о %f о
•
-3
ооо 3
о о
•
-4 -2 о 4
РС 1
N
u
сь
Q.
()
-1
о о
о
• •.:~.
••
о
-2
о
о о
•
••• • ••
-3 ххх 2
о
-4 ооо 3
-4 -z о 4
РС 1
>»
рса = PCA (n_compo ne nt s=Non e)
X_ t rain_pca = pca . fi t _ t ra nsform (X_t r a in_std)
pca . explained_variance_ra t io_
a rray ( [ О. 37329648 , О . 18818 92 6, О .1 08 96791, О . 0772 4389 ,
0 . 064785 95 ,
о . 04592014 ' о. 03986936 , о. 025 21914 ' о . 022 58 181, о . 01830924 ,
0 . 01635336 , 0.01284 271 , 0.00642076] )
Сжатие дттых с учителеJ.t путем линейного диС11.ршtинантн.ого анализа 143
Следующий ниже рисунок резюмирует идею LDA для двуклассовой задачи. Об
разцы из класса 1 показаны крестиками, соответственно, образцы из класса 2 -
окружностями:
144 Сжатие данных путем снижения раз.мерности
)(
х
)( • •• •
..
х
~ х ~
х
~
••• tt• • •
х
"
),(
~ х )(
)()( х
х х
)((
•• • \t
)(
Хх • • ••
х
•
Как показано на оси Х (LD 1), линейный дискриминант хорошо разделяет два
нормально распределенных класса. Несмотря на то что приведенный в качестве
примера линейный дискриминант на оси У (LD 2) захватывает довольно много дис
персии в наборе данных, он не может быть хорошим линейным дискриминантом,
поскольку не захватывает какой-либо информации о различении классов .
Одно из допущений в LDA состоит в том, что данные нормально распределены.
Кроме того, мы также допускаем, что классы имеют идентичные ковариационные
матрицы и что признаки статистически независимы друг от друга. Однако даже если
одно или несколько допущений слегка нарушается, то метод LDA для снижения
размерности может все еще работать относительно хорошо (R. О. Duda, Р. Е. Hart
and D. G. Stork. Pattern Classification («Классификация образов»). 2°d Edition. New
York, 2001).
Прежде чем в следующих подразделах мы рассмотрим внутреннее устройство
LDA, сначала резюмируем ключевые шаги алгоритма LDA:
1. Стандартизировать d-мерный набор данных (d - это число признаков).
2. Для каждого класса вычислить d-мерный вектор средних.
3. Создать матрицу разброса между классами S8 и матрицу разброса внутри
классов Siv·
4. Вычислить собственные векторы и соответствующие собственные значения
матрицы sv) s в .
5. Выбрать k собственных векторов, которые соответствуют k самым большим
собственным значениям для построения dхk- матрицы преобразования W;
собственные векторы являются столбцами этой матрицы.
6. Спроецировать образцы на новое подпространство признаков при помощи
матрицы преобразования W.
мы до определе нной степе ни эти допущения нарушаем , то LDA смож ет относ ительно
хорошо продолжить выполняться в задачах снижения размерности и класс ификации
(R. О. Duda, Р. Е. Hart and D. G. Stork. Pattem Classification ( <~ Классификация образов~) .
2"d Edition. New York, 2001).
Учитывая, что в разделе РСА в начале этой главы мы уже стандартизировали призна
ки набора данных сортов вин , мы можем пропустить первый шаг и сразу перейти к вы
числению векторов средних значений, которые мы будем использовать для построения
матриц разброса соответственно внутри классов и между классами. Каждый вектор
средних т; хранит среднее значение признака µ'" относительно образцов класса i:
1 с
mi =-Ixm.
n; xeD;
[
µi,amroro.ль
т. = µi,яблочная кислота
iE {1, 2, 3}.
1 :
µi,пролин
>»
np . set_printoptions (precision=4 )
mean _ vecs = [ ]
for label in range (l , 4 ) :
mean_vecs . append(np. mean(
X_train_ std[y_ train==l abel] , a xi s=O ))
print (' BC %s : %s\n ' %(label , me an_vecs[labe l - 1] ))
S; = L: <х - т; )(х - т; )т .
XEDi
146 Сжатие данных путем снижения раз.мерности
>»
d = 13 # число признаков
S W = np . zeros ( (d, d))
for label ,mv in zip(range(l,4), mean_vecs):
class_scatter = np .z eros((d , d))
for row in X_train [y_ train == label ] :
row, mv = row.reshape(d, 1) , mv.reshape(d, 1)
class_scatter += (row-mv) . dot((row -mv) .Т)
s w += class scatter
рrint( ' Внутриклассовая матрица разброса: %sx%s ' % (S_W.shape[O ] , S_W.shape[l ] ))
Внутриклассовая матрица разброса : 13х13
>»
рrint('Распределение меток классов: %s ' % np.bincount(y_ train) [l: ] )
Распределение меток классов: (40 49 35]
1 1 с т
L; = N- Sw = N- ~)x-mi)(x-mi) .
1 1 xeD;
»>
d = 13 # число признаков
S W = np .zeros((d , d))
for label , mv in zip(range(l, 4) , mean_vecs):
class_scatter = np.cov(X_ train_std[y_train==label] .Т)
S- W += class - scatter
рrint('Масштабированная внутриклассовая матрица разброса: %sx%s'
% (S_W.shape [O] , S_W .shape [l]))
Масштабированная внутриклассовая матрица разброса: lЗхlЗ
Sв = 'L,,p;(mi-m)(mi-m)т.
i=1
»>
mean_overall = np . mean(X_train_std, axis=O)
d = 13 # число признаков
S В = np . zeros ( (d, d))
for i,mean_vec in enumerate(mean_vecs) :
Сжатие дттых с учителеАt nymeAt линейного дисхршtинантного анализа 147
Оставшиеся шаги алгоритма LDA аналогичны шагам алгоритма РСА. Однако вмес
то выполнения разложения по собственным значениям на ковариационной матри це
мы решаем обобщенную задачу на собственные значения матрицы Sµ}Sв:
eigen_vals , eigen_vecs = \
np . linalg . eig (np . linalg . i nv (S_W) .do t (S_B) )
»>
eigen_pairs [ (np.abs (eigen_val s [i] ) , e igen_ve c s[ : , i] )
for i i n ra nge (l en( eigen_vals )) ]
eigen_pa i rs sorted (eigen_pairs ,
ke y=lambda k: k[O] , re verse=True )
рrint( ' Собственные значения в убывающем порядке : \n ' )
for eigen_val in e i gen_pairs :
print (eigen_val[O] )
452 . 721581245
156 . 43636122
8 . 11327596465е - 14
2 . 78687384543е - 14
2 . 7868738454Зе - 14
2 . 27622032758е - 14
2.27622032758е - 14
1 . 97162599817е - 14
1 . 32484714652е - 14
1 . 32484714652е-14
1.03791501611е - 14
5 . 94140664834е - 15
2.12636975748е - 16
tot = sum(eigen_vals.real)
discr = [(i / tot) for i in sorted(eigen_vals.real, reverse=True)]
cum_discr = np.cumsum(discr)
plt.bar(range(l, 14) , discr , alpha=0.5, align='center',
lаЬеl= ' индивидуальная "дискриминабель ност ь"')
1.0
' 0.8
6
С>
..."'
] 0.6
"'"'s: кумулятивная "дискриминабельность"
:Е
s: - индивидуальная "дискриминабельность"
~ 0.4
.~
"'а
с.: 0.2
о.о
I __________ _
4 6 10 12 14
Линейные дискриминанты
Матрица W:
( ( 0 . 0662 - 0.3797]
[- 0 . 0386 - 0 . 2206]
( 0 . 0217 - 0 . 3816]
(- 0 . 184 0 . 3018]
( 0 . 0034 0 . 0141]
[- 0.2326 0.0234]
[ 0.774 7 0.1869]
( 0 . 08 11 0.0696]
[- 0 . 0875 0 . 1796]
(- 0 . 185 - 0 . 284 ]
[ 0 . 066 0 . 2349}
0 . 3805 0.073 ]
0 . 3285 - 0 . 5971]]
Х' = XW.
Х train lda = X_train_std.do t( w)
colors = [ ' r ' , ' Ь ' , ' g ' ]
markers = [ s 1
' х ',
1
, ' о' ]
for 1, с , т in zip(np . unique (y_ train), colors , markers ):
plt . scatter (X_train_lda [y_train==l, 0]* (- 1) ,
X_ train_ lda[y_t rain==l , 1]* (- 1) ,
с=с , labe l= l , ma rker=m)
plt. xlabel ( 'L D 1 ' )
plt.ylabel (' LD 2 ')
plt.legend (loc= ' lower right ')
plt. show ()
Как видно на получ ив шемся графике , т еп ерь в новом подпространстве призн аков
три класса вин линейно разделимы:
"... .
• •
• ."• •
·-
1
-r.·•·
•. J. .•
• • l\t ••
• • ~•
.:.
•
,,,. • 8
Мх
х
• •
х
х х
-1 хХ "х Х
х
мхх )!( .;<х
х х *~ ~~х
-2 )1( .;'-
х х xjfc х
••• 1
-3 ххх 2
-4
х
••• з
-3 -2 -1
LO 1
150 Сжатие данных путем снижения разАtерностu
lr = LogisticRegression ()
lr = lr .fit (X_t rain_lda , y_ t rain )
plo t _decision_ regions (X_t r a in_lda , y_train, class i fie r=l r)
pl t . xlabel ( 'LD l ')
plt . yl abe l ( ' LD 2 ' )
pl t .legend(loc= ' lower lef t ' )
plt. show ()
• ••
4
....,....1\.
"..
о
о0 о
о о ..9.
8 'g
о
N
g
••
• • '11
•• •.. О
§о
(i)
оО О
(:)
0
о
-2
-4
ххх
ооо
-6
-6 -4 -2 4
lD 1
)(
х х ,"' )( )(
х ,"'
.
х х х ", х
...
х х
х х х
"," • )(
..
)( х / ".,,, ,.."!"' ~ - . . . .... ,
х х х / •
• ••
. ......
"\
х х
", /'
•• •
х х
~
,,,""" \ : х
х / •• )(
'\ '
)(
/)(
... " ..,, __ ..,.,,.,, "'
,,, •
/'
• х
)( х
'----------------+Х1
т
Х = [х 11 х2 ] ;
!Ф
z = [ xf ,~2х1 х2 ,х~ У.
Другими словами, посредством ядерного РСА мы выполняем нелинейное отобра
жение, которое преобразует данные в пространство более высокой размерности,
и используем стандартный РСА в этом более высокоразмерном пространстве для
проецирования данных назад в пространство более низкой размерности, где образ
цы могут быть разделены линейным классификатором (при условии, что образцы
могут быть разделены во входном пространстве по плотности). Однако оборотной
стороной этого подхода является то, что он в вычислительном плане очень затратен,
и поэтому мы используем ядерный трюк. При помощи ядерного трюка можно вы
числить подобие между двумя векторами признаков высокой размерности в исход
ном пространстве признаков.
1~ (i) (i)
ajk =- L/xj -µj)(xk -µk).
п i=1
L=.!.. i:x(i)x(i)T_
п i=1
LV =AV
1
=>-ф(Х)ф(Х/ а=Ла
п
1
=>-Ка=Ла.
п
К= ф(Х)ф(Х)r.
Другими словами, после ядерного РСА мы получаем образцы, которые уже спрое
цированы на соответствующие компоненты, вместо того чтобы строить матрицу
преобраз ования, как при подходе на основе стандартного РСА. В сущности, ядер
ная функция (или просто ядро) может пониматься как функция, которая вычисляет
скалярное произведение между двумя векторами - меру подобия.
Наиболее распространены следующие ядра:
"11"" полиномиальное ядро :
"11"" радиальная базисная функция (РБФ, гadial basis function, RBF), или гауссо
во ядро, которую мы будем использовать в последующих примерах в следую
щем подразделе:
Использоваиие ядериого .Atemoдa аиализа главиых ко.Аmо11е11т 155
Резюмируя все, что мы обсудили до сих пор, можно определить следующие три
шага для реализации ядерного РСА с РБФ в качестве ядра.
1. Вычислить матрицу ядра (матрицу подобия) k, где нам нужно вычислить сле
дующее:
Параметры
n_components : int
Число возвращаемых главных компонент
Возвращает
Х_рс np.column_stack((eigvecs[:, - i]
for i in range(l, n_components + 1)))
return Х_рс
Использование ядерного .метода анализа главиых ко.мпоиеит 157
Оборотная сторона использования ядерного РСА с РБФ в качестве ядра для сни
же ния р азмерности состоит в том, что нам нужно априорно определить параметр у.
1.0
••••••••••••
•••• ••••
• •• •• д
д
д • •• д
0.5
•• •• •• ••
.д
•
••
д •
•
•\)
••
.
д
д
•
••
.•
•
•
•
о.о
• •• • •••
•• ••
•• ••
••••• •••
••
-0.5 ••••••••••••
-1 .5 -1.0 -0.5 о. о 0.5 1.0 1.5 2.0 2.5
0.8
•
{ !.\
0.6
0.4
0.2
=
\J )
N ;; 'PPl! tt""'" ...
u о.о
"'-
-0.2
-0.4
-0.6
-0.8
••
-2.0 -1.5 -1 .0 -0.5 о. о 0.5 1.0 1.5 2.0 -2.0 -1 .5 -1 .0 -0.5 о.о 0.5 1.0 1.5 2.0
РС1 РС1
Напомним, что РСА это метод машинного обучения без учителя, и, в отличие от
с+
- LDA,
в нем информация о метках классов для максимизации дисперсии не используется.
В данном случае треугольные и круглые символы были добавлены для целей визуали
за ции , просто чтобы обозначить степень разделения.
Теперь видно, что эти два класса (круги и треугольники) хорошо линейно разде
лены, и поэтому преобразованные данные стали тренировочным набором, который
подходит для передачи на вход линейных классификаторов:
0.20
0.15
0.10
0.05
с:: 0.00 ан а
Q.
- 0.05
-0.10
-0.15
-0.20
-0.2 -0.2 -0.1 -О. О О.О О.О 0.1 0.2 0.2 -0.2 -0.2 -0.1 - О. О О. О О.О 0.1 0.2 0.2
РС1 РС1
1.5 .-------.------.------т-----....------т------.
1.0
0.5
о. о
-0.5
- 1.0
scikit_pca = PCA(n_components=2)
X_spca = scikit_pca.fit_transform(X)
fig, ах= plt.subplots(nrows=l , ncol s=2 , figsize=(7 , 3))
ax[O] . scat te r(X_spca[y==O , О] , X_spca[y==O , 1] ,
color= ' red ' , marker= • л • , alpha=0.5)
ах [О] . scatter(X_spca[y==l, 0] , X_spca[ y==l, 1] ,
color= ' Ьlue ' , marker= ' o' , alpha=0.5)
ax [l] .scatter(X_spca[y==O , О] , np.zeros(( 500 , 1)) +0 . 02 ,
color= ' red ', marker='л ' , alpha=0.5)
ax[l ] .scatter(X_spca[y==l , О] , np.zeros((S00,1))-0.02 ,
color= 'Ьlue ' , marker='o', alpha=0.5)
ах [О] .set_xlabe l( ' PCl ' )
ах[О] .set_ylabel( ' PC2')
ax [l] .set_ ylim ( [- 1, 1])
ax[l] .set_yticks( [] )
ах [ 1] . set _xlabel ( 'PCl')
plt. show ()
1.5
1.0
0.5
"'
u
Q.
О. О
-0.5
-1.0
-1.5
-1.5 -1.0 -0.5 О.О 0.5 1.0 1.5 -1.5 -1 .0 -0.5 о. о 0.5 1.0 1.5
РС 1 РС1
При услов и и, что им еется подходя щее значение для у , посмотрим , будем ли мы
удачливее с реал изацией ядерного РСА на основе РБФ :
И сно ва ядерный РСА на основе РБФ спроец иро вал данн ые на н о в ое под про
с транство , где эти дв а класса ст ановятся л ин ейно раздели мым и:
0.08
~•·
0.06
0.04
N
u
Q.
0.02
0.00
-0.02
-0.04
-0.06
- ,.
.,_". .
'- ··
,,:~~~i
-0.08
-0.06 -0.04 -0.02 0.00 0.02 0.04 О. Об 0.08 -0.06 -0.04 -0.02 0.00 0.02 0.04 0.06 0.08
РС1 РС1
162 Сжатие данных путем снижения ра:шерности
ф(х'/v.
= ''f/i)k(x',x<;»т .
j
Ка= Ал.
Параметры
gamma: float
Настроечный параметр я дра РБФ
n_components : int
Чис л о возвращаемых главных компонент
Во зв ращает
lamЬdas : список
Собственные значения
N = K. shape[OJ
one_n np.ones ( (N, N)) / N
К= К - one_n . dot (K) - K. dot (one_n) + one_n.dot (K) . dot (one_n)
Х , у= make_moons(n_samples=lOO , random_state=l23 )
al phas, lambdas =rbf ke r nel_pca (X, gamma =lS , n_components=l)
новой точкой данных х', и наша задача состоит в том, чтобы спроецировать ее на
это новое подпространство:
>»
х new Х [25] # новая точка данных
х леw
»>
x_reproj project_x (x_new , Х , # проецирование "н овой" точки данных
gamma=lS, alphas=alphas, lambdas=lambdas)
x_ reproj
array( [ 0.07877284])
0.015
исходная проекция точки Х[25]
0.010
0.005
* перепроецированная точка Х[25]
О.ООО llM•MMMMMMMMMAA
••• ~
-0.005
-0.010
-0.015
-0.20 -0.15 -0.10 -0.05 0.00 0.05 0.10 0.15 0.20
0.4
... """
••
.........••
0.2 "
...."li ··"
"" д •
•
••
о
д" " д
• •• ••
""
д
" ••
N
О. О
u
Q_
" •
•••
д
"" ••
д д "" •• •
••
-0.2
"" ""
-0.4
.
""•
".""" ... ""
"
••
.........
• •о
Резюме
В этой главе вы узнали о трех разных основополагающих методах снижения раз
мерности, применяемых для выделения признаков: стандартном методе РСА, LDA
и ядерном методе РСА. Используя РСА, мы спроецировали данные на подпростран
ство более низкой размерности для максимизации дисперсии вдоль ортогональных
осей признаков, при этом игнорируя метки классов. Метод LDA, в отличие от РСА,
применяется для снижения размерности с учителем, т. е. в нем при попытке макси
гиперпараметров
в
предыдущих главах вы узнали о ключевых алгоритмах машинного обучения
для выполнения классификации и о методах приведения данных в приемлемый
вид перед подачей их на вход этих алгоритмов. Теперь пора узнать об успешно за
рекомендовавших себя на практике методах создания хороших машиннообучаемых
моделей посредством тонкой настройки алгоритмов и о методах оценки качества
моделей! В этой главе мы узнаем, как:
<:r извлекать объективные оценки качества моделей;
<:r диагностировать типичные проблемы алгоритмов машинного обучения;
<:r выполнять тонкую настройку машиннообучаемых моделей;
cr оценивать прогнозные модели при помощи различных метрик оценки ка
чества .
impo rt pandas as pd
url = ' ht t ps://archive . ics.uci.edu/ml/machi nelearning- da t abases/breas t- cancer -
wis cons in /wdbc . da t a '
df = pd . read_csv (url , header=None)
>»
le .t ransform( [ ' М ', 'В' ] )
array ( [1, 0] )
вой шкале. Поэтому прежде чем мы сможем подать столбцы набора данных BCW
на вход линейного классификатора, такого как классификатор на основе логистиче
ской регрессии, нам нужно их стандартизировать. Кроме того, мы предположим, что
нам нужно, используя анализ главных компонент (РСА), который мы представили
в главе 5 %Сжатие данных путем снижения размер1юсти», применяемый для вы
деления признаков с целью снижения размерности, сжать данные с исходных 30
размерностей в более низкое двухмерное подпространство. Вместо раздельного вы
полнения шагов по подгонке и преобразованию тренировочного и тестового набо
ров данных мы можем расположить объекты StandardScaler, РСА и LogisticRegression
друг за другом в конвейере:
»>
fr om sklearn.preprocessing import StandardScaler
f rom s kl earn.decomposition import РСА
f rom sklearn.linear_model import LogisticRegression
f rom s klearn.pipeline import Pipeline
pipe_ lr Pipeline([('scl' , StandardScaler()),
('рса', PCA(n_ components=2)),
('clf ' , LogisticRegression(random_state= l )) ] )
pi pe_l r.fit(X_ train, y_train)
p ri nt( ' Bepнocть на т ес т овом наборе: % . Зf' % pipe_ lr.score(X_ test, y_test))
Верность на т е стовом наборе: 0 . 947
Метки классов
0 pipeline.fit 0 pipeline.pгedict
КОНВЕЙЕР
Масштабирование
Алгоритм обучения
pгedict
Прогнозная модель Метки классов
Иными словами, для каждого блока двум наборам одинакового размера, dO и d1, случайно
1
наз начаются точки данных (обычно это делается путем перемешивания массива данных
и его разбивки на две части). Затем мы тренируем на dO и тестируем на d1, после чего тре
нируем на d 1 и тестируем на dO. - Прим. перев.
172 Изучение наиболее успешных методов оцен'/СИ .моделей и тотсой настрой1."И
Исходны й набор
;_ щ D!!CS:
.-------------------...····················-·······...-----------,
Трен и ровочны й набор Проверочны й набор Тестовы й набор
"
1 -я итерация 1 ] .. 1' J_ ~
1 1, 1~ - 1.
GE1
2-я итерация J
1 J~ 1." 1 ~ ~Е2
1
••
10
Е = -~)-
10
3-я итерация J
i=I '
QЕз
1
J 1
•••
'"
174 Изучение наиболее успешных .методов оценки .моделей и тонкой настройJСU
»>
import numpy as np
fr om sklearn. mode l _ sele c tion import StratifiedKFold
kf old = StratifiedKFo l d (y= y_ train,
n_folds=lO ,
random_ state=l)
scores []
»>
from sklearn.model_selection import cross_val_score
scores ; cross_val_score(estimator;pipe_lr ,
X;X_ train ,
y;y_train ,
cv;lO ,
n _j obs;l)
рrint( ' Оценки перекрестно -пр оверочной верности: %s ' % scores )
Оценки перекрестно - проверочной верности: [ 0 . 89130435 0 . 97826087 0 . 97826087
0 . 91304348 0.93478261 о . 97777778
0 . 93333333 0 . 95555556 0 . 97777778
0 . 95555556]
рrint( 'Пе рекрестно-проверочная верность : %. 3f +/ - %.3f ' %
(np.mean(scores) , np . std(scores)))
Перекрестно - проверочная верность : 0.950 +/ - 0 . 029
Если модель для данного тренировочного набора данных слишком сложна, напри
мер в этой модели имеется слишком много степеней свободы либо параметров, то
она демонстрирует тенденцию к переобучению под тренировочные данные и недо
статочно хорошо обобщается на ранее не встречавшиеся данные . Нередко сократить
степень переобучения помогает аккумулирование большего количества тренировоч
ных образцов. Однако на практике эта работа часто может оказаться очень затратной
или просто невыполнимой. Построив график модельных верностей на тренировоч
ном и проверочном ( валидационном) наборах как функций размера тренировочно
го набора, можно легко установить, страдает ли модель от высокой дисперсии или
высокого смещения и поможет ли аккумулирование большего количества данных
решить эту проблему. Но прежде чем мы приступим к обсуждению того, как постро
ить график кривых обучения в библиотеке sckit-learn, обсудим эти две распростра
ненные модельные проблемы, вкратце рассмотрев следующую ниже иллюстрацию:
Отладка алгоритrшов при по~ющи кривой обучения и проверочной кривой 177
Хороший компромисс
«смещение-дисперсия»
,... ... - Верность на тренировочном ' ......."" ........ "."....."......."""."."".""""..""""::.":.".~ ".~".~ .";:i:i."~"
наборе
_,,
- Проверочная верность t
о
:i::
а.
-·-· Требуемая верность ф
CD
1.00 .----.------.----т---....----т----.----.---.....----.
~
. . .
: ~;.. t·,.-•· ·-· ::i"."e;:: -
:: :
.
,8 z~
: _"...;:-•-::_.._...;:•
: .
8,
. . .
.
~ -
.. . ·
.~ 8
.
,,.8
_;...•--•--11:-
1 :
:' /
• -:- •' .: .
: .:: .: : .
.: .
:...... : : : . ' :
0.95 ····· w;·--· ....." .. "" .. "" .. --··:·""----:· ·· " -- ·":-- ·--· ·--·:··· ·" .. " :"".".
.. .. .
.Q
.
~
.. ~
..
~
>=
проверочная точность
~
о
а. 0.90
Q}
CD
0.85
Принцип работы метода поиска по сетке параметров довольно прост, это парадигма
исчерпывающего поиска «путем грубой силы», где мы задаем список значений для
различных гиперпараметров, и компьютер оценивает качество модели для их каж
1
Оптимизационный поиск по сетке параметров, или сеточный поиск (grid seaгch) - это по
просту исчерпывающий поиск в заданном вручную подмножестве rиперпараметрическо
rо пространства с целью нахождения оптимальной комбинации гиперпараметров. - Прим.
перев.
182 Изучение наиболее успешных методов оценки моделей и тонкой настройки
»>
from s kl earn.mode l selection import GridSearchCV
from sklear n . svm import SVC
pipe_svc = Pipeline( [ ( ' scl ', StandardScaler()),
('clf', SVC(random_state=l)) ] )
param_ range = [0 . 000 1, 0.001 , 0 . 01, 0.1, 1.0, 10 . О, 100 . 0, 1000 .0 ]
param_grid [( 'clf_ C': param_ range,
'clf kernel ' : [ ' linear' ] ) ,
( 'clf_C': param_ range,
'c l f_ gamma': param_ range,
' clf_ kernel': [ ' rЬf' ] }]
gs Gri dSearchCV(est i mator=pipe_svc,
param_grid=param_grid ,
scoring= ' accuracy ',
cv= lO,
n_ jobs=-1)
gs = gs.fit (X_train, y_ train)
print(gs.best_score_ ) #перекрестно- п роверочная верность
0.978021978022
print(gs . best_params_ ) #наилучшие параметры
( 'clf_ C': 0 .1, 'clf_ kernel': 'linear'}
»>
clf = gs.best_est i mator_
clf . fit(X_ train, y_train)
print('Bepнocть на тестовом наборе: % .Зf' % clf.score(X_test , y_test))
Верность на тестовом наборе: 0.965
статистики (например, среднего или медианы), полученное с помощью большого числа об
разцов, отобранных из конкретной генеральной совокупности. - Прим. перев.
Тонкая настройка .маuщннообучаелtъ~~ люделей л1етодол1 сеточного поиска 183
нации параметров. Более подробно и с большим количеством примеров эта тема рассма
тривается на http://s c ikit- l earn. o rg/staЫe/ m odu l es/gr i d_se a rc h . html#ra n d om iz e d- pa ra m eter
optimization.
,_
1", . ,._,·"""" :ь д=- ==1= 0д,е ""'""I .ос=> '" "' " ' ·""~"""""'J Внешний ци кл
Трен ировать
;-;:>!•.o:;;f.'!.'P.'\."'::·.'i'••• параметра ми
Внутренний цикл
Перекрес~:н о
Тренировочн ый блок п ровероч ны и бл ок
} Настрои ть п а ра м етры
184 Изучение наиболее успешных .методов оценки .моделей и тонкой настройки
>»
gs GridSearc hCV (estimator;pipe_svc ,
param_grid;param_grid ,
scoring; ' accu ra cy ',
cv;2 ,
n_j obs;- 1)
scores; cross_val_score(gs , X_train, y_train, scoring; ' accuracy ', cv;5)
рr i nt( ' Перекрестно-nроверочная верность: % .Зf +/ - % . Зf ' % (
np.mean(scores), np . std(scores)))
Пере крестно - проверочная верность: 0.96 5 +/ - 0.025
»>
from sklearn . tree import DecisionTreeClassifier
gs ; GridSearchCV(
estimator;DecisionTreeClassifier (random_ state;O) ,
param_grid;[
{ ' max_depth ': [1, 2, 3, 4, 5, 6, 7, None] )] ,
scoring= ' accuracy ',
cv;5)
scores cross_val score(gs ,
X_train ,
y_ train ,
scoring; ' accuracy ',
cv;2)
рr in t( ' Перекрестно - проверочная верность: % .Зf +/ - % .Зf ' %
np . mean(scores), np.std(scores)))
Пере крес тно - проверочная верность: 0 . 921 +/ - 0 . 029
Спрогнозированный класс
р N
Исти н но Л ожно
Р положительные отр и цательные
(ТР) (FN)
Фа ктический класс
Ложно- Истинно
N положительные отр и цательные
(FP) (TN)
Хотя эти метрики можно легко вычислить вручную путем сравнения истинных
и предсказанных меток классов, библиотека scikit-leaгn предлагает вспомогатель
ную функцию confusion_matrix, которую можно использовать следующим образом:
»>
from sklearn.metrics import confusion_mat rix
pipe_svc .fit( X_ train , y_train )
y_pred = pipe_svc.predict (X_test)
confmat = confusion_matrix (y_true =y_test , y_pred=y_pred)
print(confmat)
[ [ 71 1]
[ 2 40]]
После выполнения приведенного выше исходного кода был получен массив, кото
рый предоставляет информацию о различных типах ошибок, сделанных классифи
катором на тестовом наборе данных, которые можно проиллюстрировать, изобразив
матрицу несоотв етствий из предыдущего рисунка при помощи функции matshow
библиотеки matplotlib:
fig , ах= plt . subplots(figsize= (2.5 , 2 . 5))
ax . matshow (confmat , cmap=plt.cm.Blues , alpha=0 . 3)
for i in range(confmat . shape [O] ):
for j in range(confmat . shape[l] ):
ax.text(x=j , y=i ,
s=confmat[i , j ] ,
va= ' center ', ha= ' center ' )
рlt.хlаЬеl (' распознанная метка ' )
plt . ylabel ('истинная метка ')
plt . show ()
1
Для справки: tгue positives (ТР)- истинно положительные исходы, true negatives (ТN) -
истинно отрицательные исходы, false positives (FP) - ложноположительные исходы и false
negatives (FN) - ложноотрицательные исходы. - Пpu..llt . перев.
186 Изучение наиболее успешных методов оцен1СU моделей и тоmсой настройкu
о 71
"'"':i;:~
"'"'
:х:
:х:
:s:
t:;
х
40
предсказанная метка
ERR= FP+FN
FP+FN +ТР+ТN
В этом случае верность прогноза можно вычислить непосредственно из ошибки:
TP+TN
АСС 1-ERR.
FP+FN +ТР+ТN
Метрики оценки качества «доля истинно положительных исходов,,-, (tгue positive
rate, TPR) и «доля ложноположительных исходов» (false positive rate, FPR) осо
бенно полезны для задач с несбалансированными классами :
1
ТР
PRE
TP+FP'
REC=TPR= ТР = ТР
Р FN+TP
На практике часто комбинация точности и полноты используется в так называе
мой метрике F1:
Fi= 2 PRExREC .
PRE+REC
Все эти оценочные метрики реализованы в scikit-leaш и могут быть импортирова
ны из модуля sklearn .metrics, как показано в следующем ниже фрагменте:
»>
from sklearn.metrics impor t precision_score
from sklearn.metrics import recall_score, fl score
рrint( ' Точность: % . Зf ' % precision_score(
y_ true=y_test , y_pred=y_pred))
Точность : 0.976
рrint('Полнота: % .Зf' % recall_score(
y_true=y_test, y_pred=y_pred))
Полнота: 0 . 952
print( ' Meтpикa Fl: % .Зf ' % fl_score(
y_true=y_test , y_pred=y_pred))
Метрика Fl : 0 . 964
используя для этого параметр scoring. Полный список разных значений, которые
принимаются этим параметром, можно найти на http://scikit- leam.org/staЫe/modules/
model_evaluation .html.
Напомним, что положительный класс в библиотеке scikit-leaш - это класс, кото
рый маркирован как класс 1. Если мы хотим указать другую положuтелъную мет
ку, то мы можем создать наш собственный регистратор оценок (scorer), воспользо-
188 Изучение наиболее успешных .методов оцен1СU J.tаделей и тонкой настройки
с+
Аналогично RОС - кривым можно вычислить кривые точности-полноты для различных
вероятностных порогов классификатора. Функция для построения графика кривых точ
ности-полноты также реализована в scikit-learn и задокументирована на веб-странице
http://scikit-learn.org/staЫe/modules/generated/sklearn.metrics . precision_recall_curve . html .
random_ state=O ,
C=l00 . 0))])
Х _ train2= Х _ train [ : , [ 4, 14] ]
cv = StratifiedKFold (y_ train,
n_folds=З ,
random_ state=l)
fig = plt.figure(figsize=(7, 5))
mean tpr = О . О
mean_fpr = np.linspace(O , 1, 100)
all_tpr = []
График RОС-криво й
1.0
0.8
><
:;;
...
х
~ 0.6
ilE
с:>
а
с:
о
х
х
t:s: 0.4
"' RОС·бло к1 (площадь= 0.69)
~ RОС·блок 2 (площадь= 0.78)
0.2 RОС·блок 3 (площадь= 0.76)
/
случайное гадание
средняя ROC (площадь= 0.75)
о. о идеальная работоспособность
»>
pi pe_lr = pipe_lr.fit (X_train2, y_train)
y_pred2 = pipe_lr.predict(X_test[: , [4 , 14]])
from sklearn.metrics import roc_auc_score
from sklearn.metrics import accuracy_score
print( ' ROC AUC : % .Зf ' % roc_auc_score(
y_true=y_test , y_score=y_pred2))
ROC AUC: 0 . 662
print( ' Bep нocть : % . Зf ' % accuracy_score(
y_true=y_test , y_pred=y_pred2))
Верность: 0.711
единичную точку отсечения на кривой ROC, А. П. Брэдли показал, что площадь под
RОС-кривой и метрики верности в основном друг с другом согласуются (А. Р. Brad-
ley, «The Use of the Area Under the ROC Curve in the Evaluatioв of Machine Learniвg
Algoгithms», Pattern гecogвition, 30 (7):1145-1159, 1997 («Использование площади под
кривой ROC при оценке алгоритмов машинного обучения»)).
Резюме
В начале этой главы мы обсудили, как соединять в цепочку различные методы пре
образования и классификаторы в виде удобных модельных конвейеров, которые
помогли нам эффективнее тренировать и тестировать машиннообучаемые модели.
Затем мы использовали эти конвейеры для выполнения одного из ключевых мето
дов отбора и оценивания модели - k- блочную перекрестную проверку. Используя
k- блочную перекрестную проверку, мы построили график с кривой обучения и про
верочной (валидационной) кривой для диагностирования типичных проблем алго
ритмов обучения, таких как переобучение и недообучение. Затем, используя опти
мизационный поиск по сетке параметров, мы выполнили тонкую настройку модели .
Мы завершили эту главу рассмотрением матрицы несоответствий и всевозможных
метрик оценки качества, которые могут быть полезными для дальнейшей оптими
зации модели для конкретной практической задачи. Теперь мы достаточно хорошо
оснащены ключевыми методами, чтобы успешно строить модели машинного обуче
ния с учителем для классификации данных.
В следующей главе мы рассмотрим ансамблевые методы, т. е. методы, которые по
зволяют комбинировать две и более моделей и алгоритмов классификации данных
для дальнейшего повышения предсказательной способности системы машинного
обучения.
Глава 7
Объединение моделей
для методов ансамблевого обучения
в
предыдущей главе мы сосредоточились на наиболее успешных практических
методах тонкой настройки и оценки разных моделей классификации данных.
В этой главе, опираясь на эти методы, мы разберем методы построения наборов
классификаторов, которые нередко могут давать более хорошую предсказательную
способность, чем любой из ее индивидуальных членов. Вы узнаете о том, как:
c:r выполнять прогнозы на основе мажоритарного голосования;
c:r сократить переобучение путем извлечения случайных комбинаций из трени
ровочного набора с повторами;
c:r строить мощные модели из слабых учеников, которые обучаются на своих
ошибках.
1
Мода (пюdе) - значение во множестве наблюдений, которое встречается наиболее часто,
т. е. з начение признака, имеющее наибольшую частоту в статистическом ряду распределе
ния . - Прим. перев.
194 Обьедине11ие J.t0делей для J.tетодов ансамблевого обучения
•••••••••• Единогласие
••••
т разных классификаторов ( Ct> ... , Ст).
О О О Плюрализм
\[
Тренировочный на~ор
i :: i
11
"'
о:
у
ф
Классификационные модели
'---
с! С2 ... c"lm
1:1
Q)
::i:
::i:
~
i i ! i
Прогнозы Р1 Р2 Р,,,
"-....,......- ......__ • • • ,.__,.
! i ! !
Окончательный прогноз
Обучение при по.мощи анса.J1tблей 195
если "''fy/x) ~О
иначе
Для иллюстрации того, почему ансамблевые методы могут работать лучше инди
видуальных классификаторов, взятых в отдельности, применим простые принципы
из комбинаторики. Пусть в следующем ниже примере все п базовых классифика
торов для задачи двухклассовой классификации имеют одинаковую частоту появ
ления ошибок Е. Кроме того, примем, что классификаторы независимы и частоты
ошибок не коррелированы. При таких допущениях можно выразить вероятность по
явления ошибки ансамбля из базовых классификаторов просто как функцию массы
вероятности биномиального распределения :
1
P(y~k)= f(
k=6
11
\ o.25k(1 - 0.25) 11-k = 0.034.
k/
Как видно, частота ошибок ансамбля (0.034) намного ниже частоты ошибок каж
дого индивидуального классификатора (0.25), в случае если все допущения соблю
дены. Отметим, что в этом упрощенном примере разделение 50 на 50 на равное чис
ло классификаторов п трактуется как ошибка, поскольку верно лишь в половине
случаев. Чтобы сравнить такой идеализированный ансамблевый классификатор
с базовым классификатором на диапазоне разных базовых частот ошибок, реализу
ем функцию массы вероятности на Python:
1
Функция массы вероятности (probability mass function, PMF) дает относительную вероят
ность того , что случайная дискретная величина в точности равна некоторому значению. Би
номинальное распределение - распределение количества «успехов~> в последовательности
»>
from scipy.mis c import соmЬ
impo r t math
def ensemЬle_ error (n _ classifier , error) :
k star t = int(math . ceil(n_classifier / 2.0)) #int для совместим. с Python 2.7
probs = [comb (n _ classifier , k) *
error**k *
(l - error)**(n_classifier - k)
for k in range(k_start , n classifier + 1) ]
return sum(probs)
ensemЬle _ error (n_ classifier=ll, error=O. 25)
0.034327507 01 904 2969
import numpy as np
error_ range = np.arange(O.O , 1.01, 0.01)
ens_ errors = [ ensemЬle _ error (n _ classifier=l 1, error=error)
for error in error_ range]
import matplotlib . pyplot as plt
pl t.plot(error_ range , ens_errors,
lаЬеl='Ансамблевая ошибка',
linewidth=2)
plt.plot(error_range, error_range,
linestyle=' --', lаЬеl= 'Ба зовая ошибка ',
linewi dth=2)
рlt.хlаЬеl('Базовая ошибка ')
рlt.уlаЬеl('Базовая/ансамблевая ошибка ')
plt.legend(loc= ' upper left')
plt. grid ()
plt.show()
1.0 .------.-------г-----.-----::;_.,----..,..
Ансамблевая ошибка
,
Базовая ошибка
0.8 ._...~~~~~~~~~-- .... .. ... . .
,,
\:1
\О
s:
3
о
::; 0.6
"'
"'
.а
"'
~
~ 0.4
,,
::;
"'~
"' :.,,,"
,
0.2 " " .... . ... """
,,
,,
0.2 0.4 0.6 0.8 1.0
Базовая ошибка
= argm~x[0.2x io + 0.2хio+О.6х4]=1.
1
у= mode{O, О, 1, 1, 1} = 1.
Для перевода принципа взвешенного мажоритарного голосования в исходный
код на
Python можно воспользоваться вспомогательными функциями библиотеки
NumPy argmax и Ьincount:
>»
import numpy as np
np.argmax(np.bincount( [O, О, 1] , weights=[0.2 , 0.2, 0.6]))
1
'f)=argm~x
1
'2:w1pii.
j=1
Параметры
def fit(self , Х , у ):
"" " Выполни ть подгонку классификаторов.
Пара метры
Возвращает
self : объек т
self.laЫenc = LabelEncoder()
self . laЫenc_ . fit(y)
self.classes = self . laЫen c . classes
self.classifiers = [}
for clf in self . classifiers:
fitted_clf = clone (clf) .fit (Х ,
self . laЫenc .trans form (y))
self. classifiers_. append (fitted_clf)
return self
Для лучшего понимания отдельных частей в исходный код было добавлено много
комментариев. Но прежде чем мы реализуем оставшиеся методы, сделаем неболь
шой перерыв и обсудим фрагмент исходного кода, который на первый взгляд может
показаться озадачивающим . Мы исп ользовали родительские классы BaseEstimator
и ClassifierMixin, чтобы бесплатно получить немного базовой функциональности,
включая методы get_params и set_params для установки и возврата значений пара
метров классификатора, и соответственно метод score для вычисления оценки точ
ности прогнозирования . Кроме того, отметим, что мы импортировали модуль six,
чтобы сделать ансамблевый мажоритарный классификатор MajorityVoteClassifier со
вместимым с Pytl10n 2. 7.
Реализация простого 1(Лассифика тора с 1о1ажоритарны.м голосование1оt 201
Параметры
Возвращает
def predict_proba(self, Х) :
Спрогнозировать вероятности классов для Х .
111111
Параметры
Возвращает
avg_proba массивоnодобный ,
202 Обьедuне1tие 11юделей для 11tетодов ансаJ1tблевого обучения
probas = np.asarray([clf.predict_proba(X)
for clf in self.classifiers ])
avg_proba = np.average(probas ,
axis=O, weights=self.weights)
return avg_proba
Отметим, что для вычислений оценки площади под RОС- кривой библио
с+
(ROC AUC)
те ка scikit-learn использует метод predict_proba (если он применим). В гл аве З ~ обзор
классификаторов с исполъзование.лt библиотеки scikit-lean1 ~ мы увидели , как вычисля
ются ве роятности классов в логистических регрессионных моделях. В деревьях реше
ний в ероятности вычисляются из частотного ве ктора , создаваемого для каждого узла
во время тренировки. Ве ктор аккумулирует значения частоты каждой м етки класса , вы
числяе мой из распределения меток классов в этом узл е. Затем частоты нормализуют
ся (нормируются) так , чтобы в сумме составлять 1. В алгоритме k бл ижайши х соседе й
метки классов k ближайших соседей агрегируются аналогичным образом, в результате
ч е го воз вращаются нормированные частоты меток классов . Несмотря на то ч то воз вра
щ аемые классификаторами на основе дерева реш е ний и k ближайши х соседей нормиро
в анные вероятности могут выглядеть похожими на в е роятности, полученные из логи
стич ес кой ре грессионной модели, мы должны осоз н авать, что они все - та ки выводятся
н е и з функций масс вероятносте й.
>»
from sklearn . model_ selection i mpor t cros s_val_s core
from sklearn . li near_model i mport Logi s t ic Regression
from sklearn . tree import Dec is i onTreeCl a ss ifier
from sklearn. neighbors import KNeighborsClassifier
from sklearn . pi peline i mport Pipeline
i mport numpy as np
clfl ; Logisti cRegres s ion(p enalty; ' l2 ' ,
С;О . 001 ,
ra ndom_state;O )
clf2 DecisionTreeCl a ssifier (max_ depth;l ,
cr"i t erion; ' en t ropy ' ,
random_ stat e;O )
clfЗ KNeighborsClassifier (n_neighbors; l ,
р ;2 ,
»>
mv_clf = MajorityVoteClassifier(classifiers= [pipel, clf2, рiреЗ])
clf_labels += [ 'М ажоритарное голосование' ]
a l l _clf = [pipel, clf2 , рiреЗ, mv_clf]
for clf, label in zip(all_clf , clf_labels) :
scores = cros s_val_s core(est i mator=clf,
X=X_train,
y=y_ train,
cv=lO,
scoring='roc_auc')
print("ROC AUC: %0 .2 f (+/ - %0.2f) [% s)"
% (scores. mean (), scores . std (), label))
ROC AUC: 0 . 92 (+/ - 0 .20) [Логистическа я регрессия ]
ROC AUC : 0 . 92 (+/ - 0.15 ) [Дерево решений)
ROC AUC: 0 . 93 (+/ - 0.10) [К ближайших соседей]
ROC AUC: 0 .97 (+/ - о .10) [ Мажоритарное голосование ]
Оценка и тонкая настройка ансамблевого 1СJ1ассификатора 205
for clf, label , clr , ls in zip(all_ clf , clf_ labels , colors, linestyles):
# принимая , что метка положительного класса равна 1
y_pred = clf .fit (X_ train ,
y_ train) .predict_proba(X_ test) [:, 1]
fpr, tpr, thresholds = roc_curve(y_ true=y_ test,
y_score=y_pred)
roc_auc = auc(x=fpr, y=tpr)
plt.plot(fpr, tpr,
color=clr,
linestyle=ls,
labe l =' %s (auc %0.2f)' % (label, roc_auc))
plt.legend(loc= ' lower right ' )
plt.plot ( [0 , 1] , [О , 1] ,
linestyle=' - -',
color= ' gray ',
linewidth=2)
plt.xlim( [- 0.1, 1.1 ] )
plt . yl im([ - 0 . 1, 1.1 ] )
plt.grid()
plt . xlabel('Дoля ложноположительных ' )
plt.ylabel( ' Дoля истинно положительных ' )
plt . show ()
1.0 """
''" /j:~:, :_.;:~~-+~:.:~.:-~ -.~<~~ -~ :-.:.-:· -~ ~-; ~-.,,:
>< 0.8 ....... .'. !. :::. . . .. ~. . . . . . . . . . . . . ;<tt. . ...•. . ...
:;;
:z: "'
..о
"'
а:
'}
1
а •/
.,._ ;
;
Лоrистическая регрессия(auc =0.92)
~ 0.2 ~" ;'
Дерево решений (auc =0.89)
;
1 ; К ближайших соседей (auc =0.86)
о.о 1- . • . . • . ~ .~ Мажоритарное голосование (auc = 0.95)
; ;
о.о 0.2 0.4 0.6 0.8 1.0
Доля ложн оположи тельных
С учетом того, что для примеров классификации мы отобрали всего два признака,
будет интересно увидеть, на что фактически похожа область решения ансамблевого
классификатора. Несмотря на то что перед подгонкой модели нет необходимости
стандартизировать тренировочные признаки, потому что наши конвейеры логис
тической регрессии и k ближайших соседей автоматически об этом позаботятся,
в целях визуализации мы выполним стандартизацию тренировочного набора, в ре
зультате чего области решений дерева решений будут в той же самой шкале. Соот
ветствующий исходный код выглядит следующим образом:
sc = StandardScaler()
X_train_std = sc . fit_transform(X_train)
from itertools import product
х min X_train_std[:, 0) .min () - l
х max X_train_std[ : , О] .max() + l
y_min X_tr ain_std[ :, 1 ) .min() - l
y_max X_ train_std [ :, 1] . max() + 1
хх, уу = np.meshgrid(np.arange(x_min , x_max , 0 .1),
np.arange(y_min , y_max , 0 . 1))
f, axarr plt.subplots(nrows=2, ncols=2 ,
sharex= col ', 1
s=50 )
axarr[idx[O], idx[l]] . set title(tt)
plt . text(-3.5 , - 4.5 ,
s= ' Шири на чашелистика [станд артизованная ] ' ,
ha= ' center ' , va= ' center ', fon tsize=l2)
plt.text( - 10.5 , 4. 5,
s= ' Длина лепестка [стан дар тизованн ая ] ',
";<
"'::z:::z:
"'"'
о
с.
-1
s:
,_"'s: -2
с.
"'::z:
с:[
-3
"'
..12. К бл ижа й ш их со седе й М а жоритарное голосован и е
"'
t""
"'<::
"'
о:;
"'::z:s:
~
-1
-2
-з
-з -2 -1 о -з -2 -1
1
Одноуровневое дерево решений, также именуемое пенько;11 решения (decision stшnp ), пред
ставляет собой дерево с одним внутренним узлом (корнем), который непосредственно под
ключе н к терминальным узлам (своим листьям). Пене к ре шения выполняет прогноз, ис
ходя из значения всего одного входного объекта. - Прим. перев.
208 Объединение .моделей для .методов анса.11tблевого обучения
Перед тем как вы узнаете, как выполнять тонкую настройку отдельных парамет
ров классификатора для ансамблевой классификации, вызовем метод get_params,
чтобы получить общее представление о том, каким образом обращаться к отдель
ным параметрам внутри объекта для поиска по сетке параметров GridSearch:
>»
mv_clf.get_params()
»>
for params , mean_score , scores in grid . grid_scores_ :
print (" %0.3f+/ - %0.2f %r"
% (mean_score, scores . std() / 2 , params))
0 . 967 +/ - 0 . 05 ( 'pipeline - l _ clf_C': 0.00 1, ' deci siontreeclassifier
max_ depth ' : 1}
0.967 +/ - 0.05 { ' pipe li ne - 1 clf С ' : 0.1 , ' decisiontreeclassifier_max_
depth ' : 1 }
1.000 + /-0.ОО { ' pipeline- 1 clf С ' : 100.0, ' decisiontreeclassifie r
max_dept h ': 1)
0 . 967 +/ -0 . 05 { 'pipe line - 1 clf С ' : 0.001 , ' decisiontreeclassifier
max_dept h': 2)
0 . 967 +/ - 0 . 05 { ' pipeline- 1 clf С ' : 0 .1 , ' decisio ntre eclassifier_max_
depth ' : 2)
1 . 000 +/ - 0 . 00 { ' pipeline- 1 clf С ' : 100 . 0 , 'decisi ontreeclassifier
max _ depth ' : 2 )
с+
Подход на основе мажоритарного голосования, который мы реализовали в этом разделе,
иногда также упоминается как многоярусное обобщение (stacking). Однако алгоритм
многоярусного обобщения чаще используется в сочетании с лоrистической регрессион
ной моделью, которая прогнозирует окончательную метку класса, используя в качестве
входа прогнозы индивидуальных классификаторов ансамбля, как более подробно опи
сано Дэвидом Х. Уолпертом в: D. Н. Wolpert, «Stacked generalization», Neural networks,
5(2):241 -259, 1992 («Многоярусное обобщение . Нейронные сети»).
210 Обьедuненuе Аt0делей для методов ансшtблевого обучения
Бутстрап-выборки 1 т) 1
1
~1 .. .
1~
! 1 / :::I:
о
tD
о:
... у
ф
! ! ! !
'
Прогнозы Р2
~ \......../ ~'
! ! ! !
Окончательны й прогноз
Затем переведем метки классов в двоичный формат и разделим набор данных со
ответственно на тренировочный ( 60%) и тестовый ( 40%) наборы:
»>
from sklearn.metrics import accuracy_score
tree = tree . fit (X_ tra in , y_ train)
y_train_pred = tree.predict(X_ train)
y_test_pred = tree.predict(X_test)
tree_ train = accuracy_score(y_train , y_train_pred)
tree test = accuracy score(y test, у test pred)
print('Bep нocть дере;а решен~й на тр~ниро;очном/тестовом наборах %. 3f/ %.3f '
% (tree_train, tree_test))
Верность дерева решений на тренировочном/тестовом наборах 1.000/0.833
х min х
- train [ :, О] .mi n () - 1
х max х
- train [ : , О] .max() + 1
y_min х train[: , 1] .min () - 1
-
y_max х train [ : , 1] .max () + 1
хх , = np . meshgrid(np.arange(x_min , x_max , 0 . 1) ,
уу
np .arange (y_min , y_max , 0 . 1))
f , axarr plt.subplots(nrows=l, ncols=2,
sharex= ' col ',
sharey= ' row ' ,
figsize= (8, 3))
for idx , clf , tt in zip ( [O , 1] ,
[tree , bag] ,
[<Дерево решений> , <Бэг гинг>]) :
clf . fit (X_ train , y_train)
Как указывалось Лео Брейманом (L. Bгe i man, «Bias, Vaгiance and Aгcing Classi-
fieгs», 1996 («Смещение, дисперсия и АRС-классификаторы с адаптивным ресэмп
лингом и комбинированием»)), бустинг может привести к уменьшению смещения
и дисперсии, по сравнению с моделями на основе бэггинга. На практике, однако,
алгоритмы бустинга, такие как AdaBoost, также известны своей высокой дисперси
ей, т. е . тенденцией к переобучению на тестовых данных (G. Raetsch, Т. Onoda and
К. R. Muelleг, «An lmpгovement
of Adaboost to Avoid Oveгfitting». In Ргос. of the Int.
Conf. on Neшal Infoгmation Pгocessing. Citeseeг, 1998 ( «Улучшение алгоритма Ada-
boost для 11едопуще11ия переобуче11ия» ) ).
В отличие от описанной здесь исходной процедуры бустинга, алгоритм AdaBoost
для тренировки слабых учеников использует полный тренировочный набор, где тре
нировочные образцы взвешиваются повторно в каждой итерации с целью построе
ния сильного классификатора, который обучается на ошибках предыдущих слабых
учеников в ансамбле. Прежде чем мы глубже погрузимся в конкретные подробности
алгоритма AdaBoost, взглянем на следующий ниже рисунок, чтобы лучше уловить
суть ключевой идеи, лежащей в основе алгоритма AdaBoost:
• •• Х2
1
1 •
1
•
•
-·------------- 1
• е:·
....
.... А
• е :
1
• ...
Х1
®
Х2
..• • :
·-
- - - ,.... - - - - - !"11!" _1_ - -
1
1
1А.
• '
.... ......
1
После того как мы вычислили коэффициент ai' теперь можно обновить весовой
векто р, используя для этого следующее равенство:
w :=wxexp(-aj хуху).
218 Обьедшитие .моделей для .методов ансалtблевоzо обучения
0.1хехр(-0.424х1х1):::: 0.065.
Аналогичным образом мы увеличим i-й вес, в случае если fJ спрогнозировал мет
ку ошибочно, как здесь:
>»
f rom skl earn . e n semЫe impo r t AdaBoo s t Cl assifier
t r ee = Dec i s i onTreeClassifier (cr iter i on='ent ropy ' ,
max_depth=l ,
random_ state=O )
ada = AdaBoos tClassifier (bas e _ estima tor=tree ,
n_estimator s=500 ,
lea rning_ra te=O . l,
random_ s tate=O )
t r ee = tree .fit (X_train , y_tra in )
y_t rain_pred = tree . predict (X_tra i n)
y_ te s t _pred = tree . predict (X_t e st )
tree_t rain = accuracy_score( y_t rain , y_trai n_p red )
tree_ te s t = ac curacy_score( y_tes t , y_te s t _pred)
pri nt( ' B e pнocть д е р е в а ре ше ний н а т ре н иров очн ом/т е сто в ом набора х % . Зf/ % . Зf '
% (t r ee_train , t ree_ te s t))
Ве р н ость д е р ева решений на тр е н ир о в о чн ом/ те сто вом на б ор а х 0 . 845/0 . 854
Усиление слабых учеников ..\lеmодо.м адаптивного бустиига 219
Как видно, пенек дерева реш ений показывает тенденцию к недообучению под
тр е нировочные данные, в отличие от неподрезанного дерева решений, которое мы
видели в пр едыдущем раз деле:
»>
ada = ada .fit( X_t ra in, y_t rain )
y_t rain_pred = ada .predi c t( X_tra in )
y_test_pred = ada .predict (X_tes t )
ada_tra i n = accuracy_score (y_t r ain , y_t ra in_pr ed )
ada_test = accuracy_score (y_ tes t, y_ t es t _pred )
print( 'B epнocть AdaBoos t на т р ен ир овочном/т е стов ом наборах % . Зf/ % .З f '
% (ada_t ra in , ada_tes t))
Вернос ть AdaBoost на тр е н ир о вочн ом/тестов ом н а борах 1 . 000 /0 .8 75
Как видно, модель AdaBoost правильно идентифицирует все метки классов тре
нировочного набора и также показывает слегка улучшенное качество на тестовом
наборе, по сравнению с пеньком дерева решений . Однако мы также видим, что вме
сте с наше й попыткой уменьшить смещение модели мы привнесли дополнительную
д исперсию .
х min х
- trai n [: , 0] .min () - 1
х max х
-
t rain [ :, О] . max () + 1
y_min х
- trai n [ :, 1] .mi n () - 1
y_max х train[ :, 1] . max () +
-
хх , = np .mes hgrid (np . arange(x_mi n, х - max , о .1 )'
уу
axarr[idx ] . set_title(t t )
axarr[O] . sеt_уlаЬ е l (' Ал когол ь ', fo ntsiz e=l 2)
plt. t ext(l0 . 2, - 1 . 2,
s= ' Отт е нок ',
ha= 'center ',
va= ' cen t er ',
fo nts ize=l2 )
plt. s how ()
. "... .. .
2.0
1.5
.с
о:;
~ "
~
о
""
~
1.0
0.5
•....~~J
....
~·<'lt' -~
• • •••••
·~
о.о
-0.5
11 12 13 14 15 11 12 13 14 15
Опенок
Резюме
В этой главе мы рассмотрели некоторые самые популярные и широко используе
мые методы ансамблевого обучения. Ансамблевые методы объединяют различные
модели классификации для уравновешивания их индивидуальных слабых сторон,
что нередко приводит к стабильным и качественно работающим моделям, которые
выглядят очень привлекательными для промышленного применения и конкурсов
по машинному обучению.
В начале этой главы мы реализовали на Pythoн ансамблевый мажоритарный
классификатор MajorityVoteClassifier, позволяющий объединять разные алгоритмы
классификации данных. Затем мы рассмотрели бэггинг, широко применяемый ме
тод снижения дисперсии модели путем извлечения случайных образцов из трени
ровочного набора и объединения отдельно натренированных классификаторов ма
жоритарным голосованием . Потом мы обсудили алгоритм AdaBoost, основанный на
слабых учениках, которые в дальнейшем обучаются на ошибках .
На протяжении всех предыдущих глав мы обсуждали разные алгоритмы обуче
ния, тонкую настройку их параметров и методы оценки их качества работы . В сле
дующей главе мы рассмотрим отдельно взятое приложение машинного обучения,
анализ мнений, которое, несомненно, стало интересной темой в эру Интернета и со
циальных медиа.
Глава8
Применение алгоритмов
машинного обучения в анализе мнений
в
о времена и годы Интернета и социальных медиа мнения, отзывы и рекоменда
ции людей стали ценным ресурсом для политологов и деловых кругов. Благо
даря современным технологиям мы теперь в состоянии аккумулировать и анализи
тельный означает, что в IMDb фильм было оценен более ш естью звездами, и отри
цательный означает, что в IMDb фильм получил менее пяти звезд. В последующих
разделах мы узнаем, как выделять содержательную информацию из подмножества
этих киноотзывов для построения машиннообучаемой модели, которая способна
прогнозировать, понравился определенному рецензенту фильм или нет.
Сжатый архив набора данных киноотзывов (84.1 Мб) можно скачать с http://
ai.stanford.edu/-amaas/data/sentiment/ как сжатый архив gzip в архиве taгball:
r;,- если вы работаете в Linux или Мае OS Х, то можете открыть новое окно тер
минала, п е рейти при помощи команды cd в каталог загрузки download и вы
полнить команду tar -zxf acllmdb_vl .tar.gz, чтобы разархивировать набор дан
ных ;
import numpy a s np
np.ra ndom. s eed( O)
df = df . re i ndex( np . random . permuta t ion(df.index))
df.t o_c sv (' ./movie_dat a . c sv ', i nde x=Fa lse)
Киноотзыв Мнение
о ln 1974, the teenager Ma rt ha Moxl ey (Maggi e Gr... 1
1 ОК" .so." 1 really like Kгi s Kгistoffe гs on а". о
как текст или слова, на вход алгоритма машинного обучения, нам нужно их преоб
разовать в числовую форму. В этом разделе мы представим модель мешка слов 1 , по
зволяющую представлять текст как числовые векторы признаков . В основе модели
мешка слов лежит довольно простая идея, которую можно резюмировать следую
щим образом:
1) создать из всего набора документов вокабуляр уникальных лексем (tokens) -
например, слов;
import numpy as np
fr om sklearn.feature extraction.text import CountVectorizer
count = CountVectorizer()
docs = np.array( [
'The sun is shining ',
' The weather is sweet',
' The sun is shining and the weather is sweet, and one and one is two ' ])
bag = count.fit_transform(docs)
>»
print(count . vocabulary_ )
{ ' one': 2 , ' the ': 6, 1
sweet 1 : 5, ' and': О, ' sun 1 : 4, ' is': 1 , ' shining ': З, ' two' : 7 ,
'weather': 8)
)))
print(bag . toarray())
[[ 0 1 0 1 1 0 1 0 0 ]
[0 1 о о о 1 1 о 1]
[ 332 2 1]]
с+
Последовательность элементов в модели мешка слов, которую мы только что создали,
также называется однограммной, или униграммной, моделью - каждый элемент или
лексема в вокабуляре представляет одно слово. · В более широком смысле в обработке
ЕЯ непрерывные последовательности элементов - слова, буквы или символы - назы
ваются также п-граммами. Выбор числа п в п-граммной модели зависит от отдельно
взятого приложения ; например, исследование Канариса и др . показало, что п-граммы
размером 3 и 4 дают хорошее качество антиспамной фильтрации электронных писем
(Ioannis Kanaris, Konstantinos Kanaris, Ioannis Houvardas and Efstathios Stamatatos,
«Words vs Character N-Grams for Anti-Spam Filtering». lnternational journal on Artificial
Intelligence Tools, 16(06):1047-1067, 2007 ( « Слова и знаки в п - гра.м.мах для антиспа.м-
1юй фильтрации»)). Резюмируя концепцию п-граммного представления, униграммное
(однограммное) и биграммное (двуграммное) представления нашего первого документа
«tl1e sun is shining» были бы сконструированы следующим образом:
• однограммное: "the", "sun", "is", "shining";
• двуграммное: "the sun", "sun is", "is shining".
Класс векторизации частотностей CountVectorizer в библиотеке scikit-learn при помо
щи своего параметра ngram_range позволяет использовать разные п-граммные модели.
>»
from sklearn. feature extraction. text import TfidfTransformer
tfidf = TfidfTransformer ()
np.set_printoptions(precision=2)
pr1nt(tfidf.fit_ transform(count.fit transforrn(docs)) .toarray())
[[ о. 0.43 о. 0 . 56 0.56 о . 0 . 43 о . о . ]
[ о. 0.43 о . о . о. 0 . 56 0.43 о . 0 . 56 ]
[ 0.66 0.39 0 . 44 0 . 17 0 . 17 0.17 0.26 0.22 0.17]]
idf("is",d3)=log~:~ =0.
228 Применение алгоритлюв Аtашuнного обучения в анализе мнений
Теперь, чтобы вычислить коэффициент tf-idf, нам просто нужно прибавить 1 к об
ратной частоте документа и умножить ее на частоту термина:
них всех нежелательных символов. В качестве иллюстрации того, почему это важно
сделать, покажем последние 50 символов из первого документа в перемешанном на
боре данных киноотзывов:
»>
df . loc [O , ' review ' ] [-5 0 : ]
' is seven . <br /><br />Title (Brazil ): Not AvailaЬle '
Как здесь видно, текст содержит разметку HTML, а также пунктуацию и другие
небуквенные символы. В то время как разметка HTML не несет какой-то большой
полезной семантики, знаки препинания в определенных контекстах обработки ЕЯ
(NLP) могут представлять полезную дополнительную информацию. Однако для
простоты мы теперь удалим все знаки препинания, сохранив только символы эмо
ций, или эмограммы, такие как «:)», поскольку они определенно пригодятся для
анализа мнений. Для выполнения этой задачи мы воспользуемся библиотекой ре
гулярных выражений Python ге, как показано ниже:
import re
def preprocessor(text):
text = re.sub( ' <[л>]*> ', , text)
emoticons = re . findall( '( ? :: l ; l= )(?:-)?(?:\) l\ ( IDIP)', text)
te xt = re.sub( ' [\ W] + ', ' ', text . lower()) + \
' '. join (emot icons ). replace ( ' - ', ' ' )
return text
»>
preprocessor (df. loc [О , 'review'] [- 50 : ])
' is seven title brazil not availaЫe '
preprocessor( "</a>This : ) is : (а test :-) !")
' this is а test : ) : ( : ) '
»>
def tokenizer(text):
return text . split()
tokenizer( ' runners like running and thus they run ' )
[ 'runners ', ' like ', ' running ', 'and', 'thus ', 'they ', ' run']
с+
Алгоритм выделения основ слов Портера является, вероятно, одним из старейших
и простейших алгоритмов выделения основ слов. Другие популярные алгоритмы выде
ления основ слов включают более новый стеммер Snowball (Porter2, или «английский»
стеммер) и стеммер Ланкастерского университета (стеммер Пэйс-Хаска), который
быстрее, но и агрессивнее алгоритма Портера. Альтернативные алгоритмы выделения
основ слов также имеются в библиотеке NLTK (http://www.nltk.org/api/nltk.stem.html) 1•
Следует учесть, что, как показано в предыдущем примере, алгоритм выделения основ
слов может создавать несуществующие слова, такие как thu (из thus). Между тем ме
тод под названием «лемматизация» стремится получать канонические (грамматически
правильные) формы отдельных слов - так называемые леммы. Однако лемматизация
в вычислительном плане более трудна и затратна, по сравнению со стеммингом, при
этом на практике было отмечено, что стемминг и лемматизация оказывают мало влия
ния на качество выполнения классификации текстов (Michal Тошаn, Roman Tesar and
Karel jezek, «lnfluence of >Vord normalization on text classification». Pюceedings of InSciT,
р. 354-358, 2006 («Влияние нормализации слов 1ш классификацию текстов»)).
import nltk
nltk.download( ' stopwords ')
о
NLTK поставляется с большим корпусом текстовых данных, миниатюрных грамматик,
натренированных моделей и т. д. Полный перечень размещен на http://nltk.org/nltk_data/.
Чтобы установить эти да нные, можно воспол ьзоваться за грузчиком NLTK, как описано
ниже .
Помимо отдельных пактов данных, можно скачать всю коллекцию (используя параметр
«ail») либо только данные, которые требуются для примеров и упражнений из книги
по NLTK (используя параметр «book»), либо только корпус документов без грамматик
и натренированных моделей (используя параметр «all-corpora»).
Чтобы вызвать окно загрузчика, нужно запустить интерпретатор Python и набрать сле
дующие команды:
import nltk
nl tk . download ()
1 N\ТК Oownloader а х
11ot inrt1lled
Penn Т redl11nk б.1 К8 notinmlled
Expнimenral Oata for Question Classification 122.SКВ not insuilled
The Reuters·2157$ bertcflm,1ork corp11$, ApteМod 6.1 мв not il\$t'1iled
PдSCAL RT( Cha.llcnges 1, 2, гnd З 377.211:8 11otiмtallcd
semcor SemCor 3,0' 4 .2МВ not installed
senseval SEN>EVAL 2 Corput: )en$e Т agged Т~ 2.1 мв not installed
scntcnce..pol11rity Scntcмc Polority Oot1iet v1.0 478.tKB not inst11lled
scnliwordnet SentiWordNet 4.5МВ not installtd
Wke1pe.sre Shat:cspc11re ХМL Corpus S.mplc 464JKB not inst11lled j
· ~inica_treeb.tnk SinН:a Т reeЬAnk Cctpus S,,mple 878,.2 кв not iмuilled
smultron SMUL TRON Corpus Somplc 1&2.З кв not imt11llcd
n•te...unron С · an Stгtc of thc Unicщ Address Со us 789.ЗКВ not iмt11l ltd
Нажмите на элементе меню File (Файл) и выберите Change Download Directory (По
менять каталог для скачивания) . Для централизованной установки поменяйте его на
C:\nltk_data (Windows), /usr/local/share/nltk_data (Мае) или /usr/share/nltk_data (Linux).
Далее выберите пакеты и коллекции, которые вы хотите скачать. Например, на сним
ке экрана каталог для закачки данных установлен в каталог C:\py_projects\Python
и машинное обучение\dаtа, и если выбрать stopwords, то туда будет скачан пакет стоп
слов для разных языков, включая русский.
Если вы не устанавливаете данные в одно из этих центральных мест расположений, то
вам нужно задать переменную окружения NLTK_DATA, в которой прописать местораспо
ложение данных (на компьютере под управлением Windows правой кнопкой нажать
на кнопке Пуск и затем выбрать Система => Дополнительные параметры системы =>
Переменные среды=> Переменные среды пользователя=> Создать ... ). Проверить, что
данные были установлены, можно следующим образом (подразумевается, что был ска
чан лингвистический корпус Университета Брауна):
>»
from nltk.corpus import brown
brown. words ()
[ ' The ' , ' Ful ton', ' County ', ' Grand ', 'Jury ', 'said ', ... ]
»>
from nltk.corpus import stopwords
stop = stopwords.words('english ' )
[w for w in tokenizer_porter( 'a runner likes running and runs а lot ') (-10:]
if w not in stop]
Инициал изировав при помощи приведенного выше исходного кода объект Grid-
SearchCV и его сетку параметров, мы ограничились небольшим числом комбинаций
параметров, поскольку число векторов признаков и большой вокабуляр могут сде
л ать по и ск по с е тк е п а рам е тров в вычисл ит ел ь н о м отношении довольно затратным;
нят ь Д О 40 минут.
В приведенном выше примере мы заменили ве кторизатор частотностей Count-
Vectorizer и преобразователь tf- idf'oв Tfidfrra nsformer из предыдущего подраздела на
векторизатор TfidNectori ze r, который объединяет упомянутые объекты . Наша сет
ка параметров param_grid состояла из двух словарей параметров . В первом слова
р е мы исполь з овали в е кторизатор TfidfVectorizer с его н астройками по умолчан и ю
( use_idf=True, smooth_idf= True и norm =' l2'), чтобы выч ислить tf- idf'ы; во втором сло
варе мы установили эти параметры в use_idf=False, smooth_idf=False и norm=None,
чтобы натренировать модель на основе исходных частот терминов . Кроме того, для
н е поср едственно самого логистич еского регрессионного классификатора мы натре
нировали модели с использованием L2- и L 1- регуляризации и штрафного параметра
clf_ penalty и затем сравнили раз н ые силы регуляризации, задав диап аз он з начени й
для параметра обратной регуляризации С.
После завершения поиска по сетке параметров можно распечатать наилучший на
бор параметров :
»>
print ( ' Наилучший набор %s ' % gs_ lr_tfidf .best_params_ )
парамет р ов :
Наилучший набор параметров : (<clf_ C ': 10.0 , ' vect_ stop_ words ': None ,
' clf _ penal ty ' : ' 12 ', ' vect token i zer ' : <func ti on tokenizer at
О х7fбс704948с8> , ' vect ngram_ ra nge ': (1 , 1))
234 Применение алгоритмов .машинного обучения в анализе мнений
>»
рrint('Перекрестно-проверочная верность : % .Зf'
% gs _ lr _ tfidf . best _ score _ )
Перекрестно-проверочная верность : 0 . 897
clf = gs_ lr_ tfidf.best_estimator_
print('Bepнocть на тестовом наборе : % .Зf'
% clf.score(X_ test, y_ test))
Вернос т ь на тес т овом наборе: 0.899
impor t numpy as np
import re
from nltk.corpus i mpo r t stopwords
stop = stopwords.words ( ' englis h')
def tokenizer(text):
text = re . sub( ' <[л>]*> ', ", text )
emoticons = re .findall (' (?:: 1; 1=) ( ? :-) ?(? : \ ) l\ ( IDIP ) ' ,
text.lower())
text = re.sub( ' [\W]+ ', ' ', text. lower ()) \
+ ' ' . join (emoticons). replace ( '-', '' )
tokenized = [w for w in t ext .split () if w not in stop]
return tokenized
>»
next(stream_docs (path= ' . / movie_data . csv'))
(' " In 1974 , the teenager Martha Mo xley ... ', 1 )
»>
import pyprind
pbar = pyprind.ProgBar(45)
classes = np.array([O, 1))
for in range(45) :
X_train , y_train get_minibatch(doc_stream, size=lOOO)
if not Х train:
break
X_train = vect.transform(X_train)
clf . partial _fit (X_ train , y_ train, classes=classes)
pbar. upda te ()
0% 100 %
[ #НННН########Н########### J 1 ЕТА [sec] : О . ООО
Tota l time elapsed: 50.063 sec
»>
X_ test , y_ test = get_minibatch( doc_stream , size=SOOO )
X_ t est = vect . transform( X_te s t)
p r in t(' Be p нo c ть : % . Зf ' % clf .scor e( X_t es t, y_te s t))
Верность: 0.868
Как видно, верность модели составляет 87%, слегка ниже верности, которую мы
достигли в предыдущем разделе в результате поиска по сетке параметров с целью
тонкой настройки гиперпараметров. Вместе с тем обучение вне ядра очень эффек
тивно испол ьзует память и заняло менее минуты на выполнение всей работы . В за
ключение мы можем задействовать последние 5000 документов для обновления на
шей модели:
Н есмотря на то что модель мешка слов продолжает оставаться наиболее широко ис
с+ пользуемой моделью для классификации текстов, она не рассматривает структуру пред
ложений и грамматику. Популярным расширением модел и мешка слов является тема
тическая модель латентного распределения Дирихле, которая рассматривает скрытую
се мантику слов (D. М . Blei, А. У. Ng and М. 1. Jordan , «Latent Dirichlet allocation». The
Journal of шachine Learning research, 3:993-1022, 2003 («Латентное распределение Ди
рихле » )) .
Более современной альтернативой модели мешка слов является алгоритм word2vec,
который был опубликован компанией Google в 2013 r. (Т. Mikolov, К. Chen, G. Coг
rado and J. Dean, «Efficient Estimation of Word Representations in Vector Space». arXiv
prepгint arXiv:1301.3781, 2013 ( «Эффектив11ая оценка представлений сло в в векторно.м
пространстве» )) . Алгоритм word2vec - это алгоритм обучения без учителя на основе
н е йронных сетей, который пытается автоматич ески извлечь связи между словами. В ос
нове алгоритма \Vord2vec лежит идея, которая состоит в том, чтобы помещать слова,
име ющие подобные значения, в подобные группы; благодаря умному распределению
ве кторного пространства (vectoг-spacing) модель может воспроиз водить определенны е
слова при помощи простой векторной математики , например король - .мужчина + жен
щи11а = корол ева .
Резюме
В этой главе мы научились использовать алгоритмы машинного обучения для вы
полн е ния задачи кл ассификации текстовых документов на основе их полярности,
составляющей базовую задачу в анализе мнений в области обработки естественного
238 Применение алгориттv.юв машинного обучения в анализе .мнений
ности, используя для этого метод частоты термина - обратной частоты документа
(tf-idf).
Ввиду больших векторов признаков, создаваемых во время этого процесса, работа
с текстовыми данными может быть в вычислительном плане довольно затратной;
в последнем разделе мы научились использовать инкрементное обучение (вне ядра)
для обучения алгоритма машинного обучения без загрузки всего набора данных
в оперативную память компьютера.
Встраивание алгоритма
машинного обучения в веб-приложение
import pickle
import os
dest = os.path.join('movieclassifier', 'pkl_objects')
if not os.path .exi sts(dest):
os.makedirs(dest)
pickle.dump(stop,
open(os.path.join(dest , 'stopwords.pkl'), 'wb'),
protocol=4)
pickle.dump(clf,
open (os. path. join (dest, 'classifier. pkl ' ), 'wb'),
protocol=4 )
import pickle
import re
import os
from vectorizer import vect
clf = pickle.load (open(
os . path. join ( 'p kl _ objects',
' classifier . pkl') , ' rb ' ))
>»
import numpy as np
label = {О: ' negative ', l: ' positive ' )
example = [ ' I love this movie ' ]
Х = vect .t ransform(example)
рrint( 'П рогноз : % s\nВероятность : %. 2f%% ' %\
(label [clf . predict (Х) [О]] ,
np.max(clf.predict_proba(X) )* 100) )
П рогноз : positive
Вероятность: 91 . 56%
С учетом того, что наш классификатор возвращает метки классов в виде целых
чисел, мы определили простой словарь Pytho11, в котором целым числам поставлены
в соответствие мнения . Затем мы применили хэширующий векторизатор Hashing-
242 Встраивание алгоритма .лtаши1111ого обучеиия в веб-приложеиие
ми, как Google, Mozilla, Adobe, Apple, Microsoft, и еще множеством других. Если вы
хотите узнать о SQLite больше, то рекомендуем посетить ее официальный веб-сайт
на http://www.sqlite.org.
К счастью, следуя философии Python «батарейки в комплекте » , в стандартной
библиотеке Python уже имеется программный интерфейс (API) sqliteЗ, который по
зволяет работать с базами данных SQLite (для получения дополнительной инфор
мации о sqliteЗ , пожалуйста, посетите https://docs.python. org/3.4/library/sqliteЗ.html).
Выполнив следующий ниже исходный код, мы создадим в каталоге movieclassifier
новую базу данных SQLite и сохраним два киноотзыва в качестве примера:
impo rt s q li t eЗ
impo rt os
conn ; s qli teЗ .co n nect (' r e vi e ws .sqlite')
с ; conn .cu r so r ()
c . e xecute(' CREATE TABLE r evi ew_db' \
' (r evi ew Т Е ХТ , sentiment INTEGER, date ТЕ Х Т) ')
examp l e l ; ' I love th is movie'
c . execute(" INSERT INTO r evi ew_db" \
" (r evi ew, s entiment, date) VALUES" \
" ( ? , ? , DATETIME('now'))", (examplel, 1))
example2 ; 'I di sliked this movie'
c.execut e (" INS ERT INTO r evi ew_db" \
" (review , se nt i me nt, date) VALUES" \
"(?, ? , DATETI ME('now'))", (example2, 0))
co nn. commi t ()
conn . clos e()
Настройка базы данных SQLite дл.я храненил данных 243
>»
conn = sqliteЗ . co nne c t(' reviews . sqli t e ' )
с =conn . cursor ()
c . execute( "SELECT * FROM revi ew_db WHERE da t e " \
" BETWEEN ' 2015 - 01 - 01 00 : 00 : 00 ' AND DATETIME ( ' now ' ) ")
results = c.fetchall ()
conn . close ()
print(results )
[ ( ' I love this movi e ', 1, ' 2015- 06-02 16 : 02 : 12 ' ) , ( ' I disliked t his
mov i e ' , О , ' 2015- 06 - 02 16 : 02 : 12 ' ) ]
о х
8 Менеджер SQlite - C:\ Us." Х +
~~~~~~--~~~~--~~-'--
~ Q chrome://sqlitemanager/conten t/sqlitem.
\ii!3a данных
- Iаблица ИttДекс Qрооuводн;,я ~блица
-
Т~~1ггер
О. Поиск
Инструменты Справка
-
@ ~ 1D i;j; f{x) 1d mf 1mmf 1 Каталог ->г=I(В==ы==бе=р===
ите==бщ
== да==
нн==ых==n==роф
=ИJU1
=)===;3 Открыть 1
1 revlews.sql •• 3 Структур• Просмотр" Поиск Выnолн•пь 3аnрос Настройки БД
>Master ТаЫе (1)
TABLE lreviev~_db 11 Поись 1 1Покшть ,g.сё j j До.§овить ~мровать j ~ "'
vTaЬlt!s (1)
rowi d review sentiment date
vreview_db
l love th is movie 2016-11-27 06:48:32
revlew
sentiment l disliked this movie
1 lo l201б-11-27 06:48:32
dat e
vViews(O)
>lndexes (0)
>Triggers (0)
< >
SQUte3.13.0 Gl!Clco 50.О 0.8.3.1-sig ned.1-sig ned Exdusive Количество файлов в выбранном ката" . ЕТ: 1 ms
Разработка веб-приложения
в веб-платформе Flask
После того как в предыдущем подразделе мы подготовили исходный код для выпол
нения классификации киноотзывов, обсудим основы веб-платформы Flask, требую
щейся для разработки нашего веб-приложения. После первого выпуска Армином
Ронэкэром в 2010 г. за прошедшие годы веб-платформа Flask получила огромную
популярность, и примеры популярных приложений с использованием Flask вклю
чают Linkedln и Pinteгest. Поскольку веб-платформа
Flask написана на Python, она
предоставляет нам, программистам на
Python, удобный интерфейс для встраивания
существующего исходного кода на Python, такого как наш классификатор кино
фильмов.
Если библиотека Flask еще не установлена в вашей среде Python, вы можете лег
ко установить ее при помощи менеджера пакетов pip прямо из вашего терминала
(во время написания книги последним стабильным выпуском была версия 0.10.1, во
время перевода данной книги - 0.11.1 ): pip install flask.
Разработка веб-пршюжения в веб-платформе Flask 245
lst_flask_app_ l/
арр . ру
templates/
firs t _app.html
Файл приложения арр . ру будет содержать основной исходный код, который бу
дет исполняться интерпретатором Python для выполнения веб - приложения на ос
нове
Flask. Каталог templates - это место, где Flask будет искать статические файлы
HTML для вывода в окне веб-браузера. Теперь посмотрим на содержимое файла
арр . ру :
арр = Flask(~name~
pythonЗ арр.ру
* Running on http://127.0.0.1:5000/
Эта строка содержит адрес нашего локального сервера. Теперь можно ввести этот
адрес в наш веб-браузер и в результате увидеть веб-приложение в действии. Если
все выполнено правильно, то мы должны увидеть простой веб-сайт с приветствен
ным сообщением: <~ Привет! Это мое первое веб-приложение Flaskl i>
Протокол запуска веб-приложения Flask в Windows:
о Открыть окно терминала/командной строки и набрать:
С :\> cd C:\Users\пyть\к\пpилoжeнию-Flask\lst_flask_app
C:\Users\пyть\к\пpилoжeнию - Flask\lst_flask_app_l> python арр.ру
* Running оп http://127.0 . 0.1:5000/ (Press CTRL+C to quit)
* Restarting wit h stat
* Debugger is active!
* Debugger pin code: 281 - 870-455
о х
П<реое приложение Х
1~ .. 1
1 nоnриветсrво1111тъ 1
Разработка веб-прwюженил в веб-платфорлtе Flask 247
После того как кнопка отправки данных на сервер ( Поприветствовать) была на
жата и форма была пров ерена, будет сгенерирована новая страница HTML, которая
п окажет имя поль з ов ателя .
Привет, Sebastian!
Для такого приложения нам нужно настроить новую структуру каталогов, кото
рая выглядит следующ им образом:
lst_flask_app_2/
арр . ру
static/
s t yle . css
templates/
_formhelpers .html
first_app . html
hello. html
if name main
app.run(debug;True)
body {
fo nt - size : 2em;
<!doctype html>
<html>
<head>
< t itle> П epвo e пр ил ожение</ titlе >
<link rel= "style sheet"
href= " { { url _ fo r ( ' s t a t i c ' , file name = ' style . c s s ') } }" >
</ head>
<body>
<!doctype html>
<html>
<head>
<title> П epвoe приложение</titlе>
<li nk rel= "stylesheet"
href= " { { url _ for ( 'static ' , fil ename= ' style. css ') } }">
</head>
<body>
<div>Hello (( name }}</div>
</body>
</html>
pythonЗ арр.ру
о х
1 отnравкть отзыв 1
После того как отзыв будет отправлен на сервер, пользователь увидит новую
страницу, демонстрирующую спрогнозированную метку класса и вероятность прог
ноза. Кроме того, пользователь сможет предоставить отклик об этом прогнозе путем
нажатия на кнопке Правильно или Неправильно, как показано на следующем ниже
снимке экрана:
Кn1Jt(Ифи1tаци.я IQIHOф~t.nbMOS х +
+ Q localhost:SOOO/ results С\. Поиск
Ваш кивоопыв:
Предсказание:
-· ~ app:j;"y- ------- --
,.. pkl_objects
• classlfier.pkl
• stopwords.pkl
[j revlews.sqllte
,.. stat.lo
[tJ style.oss
,.. templates
!!) _tormhelpers.html
~ results.html
!!.) rev lewform.html
.!J thanks.html
~ veotorlzer.py
В предыдущем разделе этой главы мы уже создали файл vectorizer.py, базу дан
ных SQLite reviews.sqlite и подкаталог pkl_objects с законсервированными объекта
ми Python.
Файл а рр . ру в главном каталоге - это сценарий Python с нашим исходным ко
дом для веб-платформы Flask. Файл базы данных review.sq lite (который мы созда
ли ранее в этой главе) мы будем использовать для хранения киноотзывов, которые
отправляются в наше веб-приложение. Подкаталог шаблонов temp lates содержит
НТМL-шаблоны, которые будут использоваться веб-платформой Flask для генери
рования страниц и их показа в браузере, и подкаталог static будет содержать прос
той файл CSS для корректировки внешнего вида генерируемого НТМL-кода.
Учитывая, что файл арр . ру довольно длинный, мы освоим его в два этапа. Первая
часть файла арр . ру импортирует библиотеки, модули и объекты Python, которые
252 Встраивание алгориm11tа машинного обучения в веб-приложение
de f train(document, у) :
Х = vect. transform ( [document] )
clf . partial_fit(X, [ у ] )
<body>
<h2> П ожалуйста , вв е дите ваш киноот зыв: </ h 2>
</body>
</html>
<body>
< hЗ > ва ш киноотзыв : </ h З>
<div>{{ content }}</div>
<input t ype; hidden value; • {{ predi ction }} ' name; ' prediction'>
<input t ype;hi dden value; ' {{ con t ent )) ' name; ' review ' >
</form>
</div>
</body>
</html>
body{
widt h:бO Op x;
.button{
padding- top: 20рх ;
<!doctype html>
<html>
<head>
<titlе>Классифи ция кинофильмов</titlе>
</ head>
<body>
<hЗ> Благ одарим за ваш отзыв!</hЗ>
</ body>
</ html>
pythonЗ арр . ру
Развертывание веб-приложения
на публичном сервере
После того как мы проверили работу веб-приложения локально, мы теперь готовы
развернуть наше веб-приложение на публичном сервере. В целях данного практиче
ского руководства мы воспользуемся службой веб-хостингаPythonAnywhere, кото
рая специализируется на хостинге веб-приложений Python и делает его чрезвычай
но простым и беспроблемным. Кроме того, веб-служба PythonAnywhere предлагает
возможность создания аккаунта новичка, который позволяет бесплатно запускать
одно веб-приложение.
Для того чтобы создать новый аккаунт веб-службы PythonAnywhe1·e, надо зайти на
веб-сайт https://www.pythonanywhere.com и нажать на ссылке Pricing & signup (Стои
мость услуг и регистрация), расположенной в верхнем правом углу. Далее нажать на
кнопке Create а Beginner account (Создать аккаунт новичка), где нужно ввести имя
пользователя, пароль и действительный адрес электронной почты . После прочтения
правил и ограничений и согласия с ними у нас должен быть новый аккаунт.
К сожалению, бесплатный аккаунт новичка не позволяет получать из нашего тер
минала или командной строки доступ к удаленному серверу по протоколу SSH. По
этому для управления веб-приложением нам придется использовать веб-интерфейс
веб-службы PythonAnywhere. Но прежде чем мы сможем выгрузить наши локаль
ные файлы приложения на сервер, мы должны создать новое веб-приложение для
нашего аккаунта PythonAnywheгe. После нажатия на кнопке Dashboard (Личный
кабинет) в верхнем правом углу мы получим доступ к панели управления, показан
ной в верхней части страницы. Затем нажимаем на вкладку Web (Веб-приложение),
которая теперь видима в верхней части страницы. Далее нажимаем на кнопку Add
а new web арр (Добавить новое веб-приложение) слева, которая позволяет создать
новое веб-приложение Flask на Python 3.5, которое мы назовем классификатором
кинофильмов movieclassifier.
После создания нового приложения для нашего аккаунта PythonAnywhere мы
направляемся на вкладку Files (Файлы) для выгрузки файлов из нашего локаль-
Развертывание веб-приложения иа публ ичиолt сервере 257
...
ный каталог movieclassifier, как показано на следующем ниже снимке экрана:
[]
6/'"- у
~ python onywhere
8 _pycache_/ 8
lii pkl_objects/ 8
'ii static/ 8
'ii templates/ 8
li
li
арр.ру
reviews.sqllte
.1.
.1.
~
•
i
2015·06·02 16:35
201 5·06·04 02:19
э . о кв
7 . ОКВ
Upload а file: Choose File по flle selected 6% full (32.2 МВ of уоиг 512.О МВ quota)
Несмотря на то что наша прогнозная модель обновляется на лету каждый раз, когда
пользователь предоставляет отклик о результате классификации, обновления в объ
екте clf будут сброшены в случае аварийного прекращения работы веб-сервера или
его перезапуска. Если мы перезагрузим веб- приложение, то объект clf будет повтор
но инициализирован из файла консервации classifier.pkl . Один из вариантов перма
нентного закрепления обновлений состоит в том, чтобы консервировать объект clf
повторно после каждого обновления. Однако это стало бы в вычислительном плане
очень неэффективно с растущим числом пользователей и может повредить файл
консервации, в случае если пользователи п р едоставят отклик о результате прогноза
impor t pickle
import sqliteЗ
import пumpy as пр
import os
classes = пр . array ( [О , 1] )
X_traiп = vect.traпsform(X )
model . partial_fit(X_ train, у, classes=classes)
results = c.fetchmaпy(batch_ size)
сопп. close ()
returп model
cur_d ir = os . path.dirпame(_file_)
clf = pickle.load(opeп(os . path.joiп(cur_dir ,
' pkl_objects ',
' classifier.pkl ' ) , ' rb ' ))
db = os.pa th. joiп(cur_dir , 'reviews.sqlite ' )
# classifier . pkl .
pickle.dump (clf , open (os .pa t h. join (cur_dir ,
' pkl _ obj ects ' , ' classifier . pkl ' ) , ' wb ' )
# , protocol=4 )
Создав сценарий update.py, те перь можно тоже выгрузить его в каталог movieclas-
sifier на веб-хостинге PythonAnywheгe и импортировать функцию update_model
в главном сценарии приложения арр . ру для обновления классификатора из базы
данных SQLite всякий раз, когда мы перезапускаем веб-приложение. Для того что
бы это выполнить, нам просто нужно добавить в начало сценария арр . ру строку ис
ходного кода с импортом функции update_model из сценария update .py:
# import update function from local dir
from update i mport update_model
if name ma i n
clf = update_model(dЬ_path="db", model=clf, bat ch_size=lOOOO)
Резюме
В этой глав е вы изучили много полезных и практических тем, расширяющих наши
познания теории машинного обучения. Вы узнали, как выполнять сериализацию
модели после ее тренировки и как загружать ее для более поздних случаев исполь
зования . Кроме того, мы создали базу данных SQLite для эффективного хранения
данных и создали веб - приложение, позволяющее сделать наш классификатор кино
фильмов доступным для внешнего мира.
На протяжении всей книги мы подробно обсудили много идей и принципов ма
шинного обучения, наиболее успешных практических методов и моделей с учи
телем для классификации даю-rых. В следующей главе мы рассмотрим еще одну
подкатегорию обучения с учителем - регрессионный анализ , который позволяет
прогнозировать результирующие переменные в непрерывной шкале, в отличие от
категориальных меток классов классификационных моделей, с которыми мы рабо
тали до сих пор.
Глава 10
Прогнозирование значений
...
непрерывнои целевои переменнои
... ...
на основе регрессионного анализа
в
предыдущих главах вы много узнали об основных принципах, лежащих в основе
обучеиия с учителем, и иатреиировали миожество разиых моделей для выпол
нения задач классификации с целью идентификации принадлежиости группе или
категориальных переменных. В этой главе мы окунемся в еще одну подкатегорию
обучения с учителем: реzресситтый анш~из.
Регрессионные модели используются для предсказания целевых переменных
в непрерывной шкале, что делает их привлекательными для решения многих вопро
сов в науке и для приложений в информационной отрасли, таких как понимание
связей м е жду переменными, оценивание тенденций или создание прогнозов. Одним
из примеров может быть предсказание продаж компании в будущие месяцы .
В этой главе мы обсудим основные понятия регрессионных моделей и затронем
следующие темы:
у= W 0 + W 1X .
Здесь вес w 0 - это точка пересечения оси У и rv1 - коэффициент объясняющей
переменной. Наша задача - извлечь веса линейного уравнения, чтобы описать связь
между объясняющей и целевой переменными, которую затем можно использовать
для предсказания откликов новых объясняющих переменных, не бывших частью
тренировочного набора данных.
Разведочный анализ набора данных Нousing 261
х (объясняющая переменная)
' Для справки : уравнение простой регрессии имеет на правой стороне точку пересечения (ш0 )
и объясняющую переменную с коэффициентом наклона (ш~). В отличие от него, уравне
ние множественной регрессии имеет на правой стороне две или более объясняющих пере
менных, каждая со своим коэффициентом наклона. - Прим. перев.
262 Прогнозирование значений непрерывной целевой переменной 11а 0С11ове регрессион11ого
import pandas as pd
url = 'https : //archive . ics.uci.edu/ml/machine-learningdatabases/housing/housing.data'
df = pd.read_c sv(url, heade r=None, sep= ' \s+ ')
df.columns = [ ' CRIM ', 'ZN', ' INDUS ', 'CHAS',
'NOX', 'RM 1 , 1
AGE 1
, 'DIS', ' RA0 1 ,
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD ТАХ PTRATIO в LSTAT MEDV
о 0.00632 18 2.31 о 0.538 6.575 65.2 4.0900 1 296 15.3 396.90 4.98 24.0
1 0.02731 о 7.07 о 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14 21.6
2 0.02729 о 7.07 о 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03 34.7
3 0.03237 о 2.18 о 0.458 6.998 45.8 6.0622 3 222 18.7 394.63 2.94 33.4
4 0.06905 о 2.18 о 0.458 7.147 54 .2 6.0622 3 222 18.7 396.90 5.33 36.2
Разведочный анализ набора данных Нousing 263
1- "
<( "
1- "
V\ "
...J о L_
,. ,. .
~i1::
•."
:
, 1 •
'
i
1
1
...
. ,·
. .
.:
'•
'-~
V\ "'
: --·
:;:)
Q "
-z .,'
"
.,
" .. -
. .. . .•
··--
tr·
-
;:;
:
., .
' .. . '
)( '·'
..
о '·'
f/·
••
. " i.... . .. .:•: ~--·i
1. ••
.. ," .
z"
,,"
~;/:~..
·' " "..; -:°•
.
~ '
~
>: r
•
',;,
.
.·.··..
i&[i1.;
·;.:1
:
~~1!
i::;
,•
1
1 . ..... "..
11·'!-1:
,_ .·. .
Q "
LLI "
:Е "
'- 10 о 10
LSTAT
;о 30
" 40 ~
~~iijl;
_,о
..
5
INDUS
10 15 202".:Ю
t~i i
0.3 0 <40.S0.0 0.70.110.lil 10
NOX
..
1
3.
1,'.'
" 5 11
RM
' 11 9 10 о
.l
10
MEDV
20 :ю '<1 50 11О
264 Прогнозирование значений непрерывной целевой пере.менной на основе регрессионного
sn s . r eset_ori g()
признаков .
import numpy as np
ст = np.corrcoef(df [cols ] . values.T)
sns . set(font_ scale=l.5)
hm = sns.heatmap(cm ,
cbar=True ,
annot=True ,
1
Для справки: z-оценка, или стандартная оценка, позволяет определить меру отклонения
величины от сред него, т. е. показываает, на сколько стандартных отклонений от среднего
лежит выбранное значение переменной . - Прим . перев.
266 Прогнозирование значений непрерывной целевой переАtенной на основе регрессионного
square=Tru e,
f rnt=' . 2f',
an not_kw s ={ 's iz e': 15) ,
yt i ckl abe l s=col s,
xticklabe l s =col s}
plt. s how ()
0.8
0.4
О.О
-0.4
-0.8
Для того чтобы выполнить подгонку линейной регрессионной модели, нас инте
ресуют те признаки, которые имеют высокую корреляцию с нашей целевой пере
менной MEDV. Глядя на приведенную выше корреляционную матрицу, мы видим,
что наша целевая переменная MEDV показывает наибольшую корреляцию с пере
менной l.STAT (-0.74). Однако, как вы помните из матрицы точечных графиков,
имеется явная нелинейная связь между l.STAT и MEDV. С другой стороны, кор
реляция между RM и MEDV тоже относительно высока (0.70), и при наличии ли
нейной связи между этими двумя переменными, которые мы наблюдали в матрице
точечных графиков, переменная RM кажется хорошим вариантом в качестве объяс
няющей переменной, которая понадобится в следующем разделе во время ознаком
ления с принципами работы простой линейной регрессионной модели.
Здесь у - это предсказанное значение у= 7р/х (отметим, что член 1/2 используется
просто для удобства получения правила обновления при градиентном спуске). По
существу, линейная регрессия по МНК может пониматься как ADALINE без еди
ничной ступенчатой функции, в результате чего вместо меток классов - 1 и 1 мы по
лучаем непрерывные целевые значения. В качестве демонстрации подобия возьмем
реализацию градиентного спуска для ADALINE из zлавы 2 «Тренировка алгоритмов
.лtашинного обучения для задачи классификации» и удалим единичную ступенчатую
функцию, чтобы реализовать нашу первую линейную регрессионную модель:
class LinearRegressionGD(object) :
def predict(self, Х) :
return self.net_ i nput(X)
268 Прогнозирование значений непрерывной целевой переменной на ornoвe регрессионного
Если вам нужно освежить память по поводу того, каким образом обновляются
веса,- делая шаг в противоположном от градиента направлении, - пожалуйста, об
ратитесь к разделу об ADALINE в главе 2 «Тренировка алгоритмов машинного об
учения для задачи Юlассификации».
Чтобы увидеть наш линейный регрессор LinearRegressionGD в действии, вос
пользуемся переменной RM (число комнат) из набора данных жилищного фонда
в качестве объясняющей переменной для тренировки модели, которая может пред
сказывать MEDV (цены на жилье). Более того, для лучшей сходимости алгоритма
градиентного спуска мы стандартизируем переменные. Соответствующий исходный
код выглядит следующим образом:
Как видно на следующем ниже графике, алгоритм ГС сходился после пятой эпохи:
260
240
220
200
u.J
:::::
180
160
140
120
о 10 15 zo
Эпоха
Реализацил линейной регрессиоиной лtодели oбьtttHЪl.At лtетодолt наш~енъших квадратов 269
Далее посмотрим, нас колько хорошо линия линейной регрессии подогнана под
тренировочные данные . Для этого определим простую вспомогательную функцию,
которая построит точечный график тренировочных образцов и добавит линию ре
грессии:
Теперь воспользуемся функцией lin_reg plot, чтобы построить график числа ком
нат в сопоставлении с ценами на жилье:
-о;
о о о о о
"'
:i: о «в'о о°О Q
о
:i:
::! c9g
~ ~о
....:s:
а.
~
:i:
о
~
>
о
о
w
~ о
о о
с::
с;
о о
о
q:
о
..; -1 о
:;; о
.... о
CD
с;
"'
~ -2
-3
-5 -4 -3 -z -1 о 4
Среднее число ко мнат [RMl (ста нда ртизован ное)
»>
num_rooms_std = sc_x.transform([5 . 0])
price_std = lr.predict(num_rooms_std)
print("Цeнa в тыс. долл.: %. Зf" % sc_y . 1nverse transform(pr1ce_std))
»>
print('Haклoн: % .Зf' % lr.w_ [l])
Наклон: О. 695
»>
from sklearn.linear model import LinearRegression
slr = LinearRegression()
slr.fit(X, у)
print('Haклoн: % . Зf' % slr . coef_[OJ)
Наклон: 9.102
рrint('Пересечение: % .Зf' % slr.1ntercept
Пересечение: -34.671
60
50 о о о о о о
40
'>'
а
LIJ
~
30
""'..;ct
Q
...
:;; 20
""':z:
"'
:::r 10
-10'--~~~'--~~~'--~~~'--~~~'--~~----'--~~----~~~--'
3 8 10
Средн ее ч и сло ко м нат [RM]
Как альтернатива использова нию библиотек машинного обучения также су ществует ре
с+ ш е ние в замкнутой форме (аналитическое) для реш ения МНК с применением системы
линейных уравнений, которое можно найти в большинстве учебников по основам мате
матич еской статистики:
не-выбросов.
3. Выполнить повторную подгонку модели с использованием всех не - выбросов.
4. Оценить ошибку подогнанной модели относительно не - выбросов.
5. Завершить алгоритм, в случае если качество соответствует определенному
заданному пользователем порогу либо если было достигнуто фиксированное
число итераций; в противном случае вернуться к шагу 1.
Теперь обернем нашу линейную модель в алгоритм RANSAC, воспользовавшись
для этого объектом RANSACRegressor библиотеки scikit-leaш:
для Median Absolute Deviation. Однако выбор надлежащего значения для порога
не- выбросов зависит от конкретной задачи, в чем кроется один из недостатков ал
горитма RANSAC. За последние годы для автоматического отбора хорошего порога
не-выбросов было разработано множество разных подходов. Вы можете найти де
тальное обсуждение данной темы в: R. Toldo and А. Fusiello's, <1Automatic Estimation
of tl1e Inlieг Thгeshold in Robust Multiple Stгuctuгes Fitting». In Image Analysis and
Pгocessing- ICIAP 2009, р. 123-131 . Spгingeг, 2009 ( <1Автоматическая оценка порога
не -выбросов для робастной подгонки множествею-tых структур») .
После подгонки модели RANSAC получим не- выбросы и выбросы из подогнан
ной линейной регрессионной модели RANSAC и построим совместный график с ли
нейной подгонкой:
60
50
••• Не-выбросы
о Е1 [] [] Е1
[] [] [] Выбросы
40
5'
С>
~ 30
[]
"'
а
со;
..;
20
[]
~
"'
~
10
8
ж
-10
-20
2 10
Среднее чи сло ко мнат [RM]
1
Медианное абсолютное отклонение вычисляется как MAD = шedian(~; - i l), где Х; - это
абсолютное значение точки данных, i - медианное значение группы. Используется вместо
среднего отклонения, когда крайние значения из области отклонений должны оказывать
меньшее влияние на величину отклонения в силу того, что медиана затрагивается крайни
ми з наче ниями области отклонений в меньшей степени, чем среднее. - Пршt. перев.
27 4 Проzнозирование значений непрерывмй целевой переменмй на ос1юве регрессионNОго
Ко гда мы расп ечатаем наклон (угл ово й коэ ффициент) и точку пересечения мо
дели, выполнив нижеследующий исходный код, то увидим, что линия линейной
регрессии немного отличается от подгонки, которую мы получили в предыдущем
откликов:
о ~э [] Тестовые данные
•
~ []: &· Е1 •
[] []..[]L·а1·•[]
...
....
"'t;
••
о
-10
• • •
[]
Е1 .~
С1
• •
-20 ••
- з о~~~~~~~~~~~~~~~~~~~~~~~~~~~
-10 10 20 30 40 50
Предсказан н ые зна ч ения
разных р е грессионных моделей или для тонкой настрой ки их параметров путем по
иска по сетке параметров и перекрестной проверки:
>»
from sklearn . met r i cs i mport mean_squared_e r r or
pri nt(' MSE тре н и ровка : % . З f, т ес т ир о ван ие: % .Зf' % (
mean_squared_e rror (y_t rai n, y_ t ra i n_pred ),
me an_squared_e rror(y_t e s t , y_te s t _pred )))
Мы увидим, что MSE на тренировочном наборе равна 19.96, а MSE тестового на
бора намного больше со значением 27.20, что указывает на то, что наша модель пере
подогнана под тренировочные данные.
Здесь SSE - это сумма квадратичных ошибок, SST - полная сумма квадратов, или,
другими словами, это просто дисперсия переменной отклика. Быстро покажем, что
R2 - это на самом деле просто приведенная версия MSE:
R2 = 1_ SSE.
ssт'
1- I ~ <Y(i) - y(i) )2
1- п 1=1
__!_" ~ (y(i) - µ )2
п L..,1=1 у
1_ MSE.
Var(y)
»>
f rom skl earn . me t ric s i mport r2_s cor e
pr in t('R '2 т ре нир овка : % . Зf , т е стиров а н ие: % . З f' %
(r 2_score(y_ tr a i n, y_t r ain_pr ed),
r2_s core (y_t es t, y_t est_pr ed )))
Прuщщение регуллризованных .методов для регрессии 277
Здесь
т
Здесь
т
Однако ограничение метода lasso состоит в том, что он отбирает не более п пере
менных, если т > п. Компромиссом между гребневой регрессией и методом lasso
является эластичная сеть, при которой имеются L1-штраф для генерирования раз
реженности и L2-штраф для преодоления некоторых ограничений метода lasso, та
ких как число отобранных переменных.
' Метод эластичной сети - это метод регуляризованной регрессии, компенсирующий недо
статки метода lasso добавлением L1- и L2-штрафов. - Пршt . перев.
278 Прошозирование значений непрерывной целевой переменной на основе регрессионного
"!
п т т
Например, если установить ll_ratio равным 1.0, то регрессор эластичной сети Elas-
ticNet будет равен lasso. По поводу более подробной информации о разных реали
зациях линейной регрессии, пожалуйста, обратитесь к документации на http://scikit-
learn.org/staЫe/modules/linear_model.html.
lr . fit(X , у )
X_fit = np.ara nge (250 , 600 , 10) [ :, np.newa xis]
y_ l i n_fit = l r . predict (X_ fit)
pr . fi t (X_ quad , у )
y_ quad_ fit = pr . predic t (quadrat:i.c . fit t rans f orm (X_ fit ))
линейная подгонка
квадратичная подгонка
350
300
250 •
•
200'--~~"--~~.._~~ .........~~-'-~~........~~-'-~~-'-~~.......~~--'
200 250 300 350 400 450 500 550 600 650
>»
y_ lin_pred = lr . predict(X)
y_quad_pred = pr.predict(X_quad)
рrint('Тренировочная MSE линейная: % .Зf , квадратичная : % .Зf ' % (
mean_squared_error(y, y_lin_pred),
mean_squared_error(y, y_quad_pred) ))
Трениро вочная MSE линейная : 569 . 780 , квадратичная: 61.330
# линейная подгонка
X_fit = np.arange(X . min (), X. max( ), 1 ) [: , пp . newaxis]
regr = regr.fit(X , у)
y_lin_fit = regr . predict( X_fit)
linear_ r2 = r2_score (y, regr . predict (X))
# квадратичная подгонка
regr = regr .fit(X_quad, у)
y_ quad_f1t = regr .predict (quadr atic .f1t transform (X_fit ))
quadratic_r2 = r2_score(y , regr.predict (X_quad) )
# кубическая подгонка
regr = regr.fit (X_cuЬic , у )
у_ cuЬic _fit = regr . predict (cuЬic. fit transform (Х _fi t) )
cuЬic_r2 = r2_score(y , regr . predic t (X_cuЬic))
60
л ин е йная (d=1), R 2 =О. 54
50 •е квадратич н ая (d=2), R 2 = О . 64
кубическа я (d =З) , R 2 = о . 66
40
>
С> трен и ровочные точ ки
.....
~
.: 30
~
..;
:;; 20
1-
"'
.,,"'
:ж:
:s'
10
······ ········ ...
." ".
········
- 10'--~~-'-~~-'-~~--'~~~'--~~-'-~~-'-~~--'~~~.1--~~~
-5 10 15 20 25 30 35 40
% насел е ни я с более н и з ким статусо м [LSTAT]
линейная (d=1), R 2 = О . 69
тренировочные данные
1~~~~~~~~~~~~~~~~~~~~~~~~~~~
-1 4
log(%населе ни я с более н и з ким статусо м [LSТАТ])
N . N .
JG(Dp,X; )=l(Dp )- левыи /(Dлевый) - правыи /(DправьШ ) .
NP NP
284 Протозирование значений непрерывной целевой переменной на осн.ове регрессионного
~ _..!_" (i)
Yt - L,,,iY ·
N.D
IE t
Как видно на итоговом графике, дерево решений захватывает общий тренд в дан
ных. Однако ограничение этой модели состоит в том, что она не захватывает не
прерывность и дифференцируемость нужного предсказания. Кроме того, нам нужно
быть осторожными относительно выбора надлежащего значения для глубины дере
ва, чтобы не было переобучения либо недообучения под данные; в данном случае
глубина 3, похоже, представляет собой хороший выбор:
Превращение линейной регрессиотюй людели в криволинейную 285
50 OC!f:Do О 00
~
u.J
40
~
ci
с;
& 30
._;
:;;
,_
"'
~ 20
~ о
<ь о
10
о
о
О'--~--1.~~-"-~~-'-~~"'-~---'~~-'-~~-'-~~..._~__,
-5 10 15 20 25 30 35 40
% населен ия с более низким статусом [LSТAT]
>»
Х; df.i l oc [ : , :-1 ] .values
у df[ 'MEDV' ] .val ues
;
plt.scatter(y_train_pred ,
y_train_pred - y_train,
с='Ыас k ',
marker= ' о ',
s=35 ,
alpha=0.5,
lаЬеl = 'Трен ировочные данные')
plt.scatter(y_test_pred,
y_test_pred - y_test,
c='lightgreen',
marker=' s' ,
s=35,
alpha=0.7 ,
lаЬеl = ' Тестовые данные')
рlt.хlаЬеl('Предскаэанные значения')
plt.ylabel ('Остатки ' )
plt.legend(loc= ' upper left ')
plt.hlines(y=O, xmin=-10 , xmax=50, lw=2 , color='red')
plt.xlim( [- 10 , 50 ] )
plt. show ()
25
20
•••
ооо
Тренировочные да н ные
Тестовые данные о
15
10 о IQ
:s:
...""
~
о
-5
о
-10
о
-1 5
-10 10 20 30 40 50
Предсказан н ые зн ач е ни я
Резюме
В начале этой главы вы научились использовать простой линейный регрессионный
анализ для моделирования связи между единственной объясняющей переменной
(независимой переменной) и непрерывной переменной отклика (зависимой пере
менной) . Затем мы обсудили широко применяемый метод разведочного анализа
данных для поиска повторяющихся образов и аномалий в данных, который являет
ся важным п ервым шагом в задачах прогнозного моделирования.
терных деревьев;
типов означает, что каждый кластер представлен прототипом, который может быть
либо центроидом (средним) подобных точек с непрерывными признаками, либо
медоидом (наиболее представительной или наиболее часто встречающейся точкой)
в случае категориальных признаков. В то время как алгоритм k средних очень хоро
шо выполняет идентификацию кластеров сферической формы, один из недостатков
этого алгоритма кластеризации состоит в том, что нам нужно априорно указывать
Х, у= ma k e _Ыobs(n_ samples=150,
n_features=2 ,
centers = З,
cluster_std=0 . 5,
shuffle=True ,
random_state=O)
import matplotlib.pyplot as plt
plt .s catter (Х[: , О] ,
х [:' l] '
c='white',
marker=' о ',
s=SO)
plt.grid()
plt. show ()
Мы только что создали набор данных, который состоит из 150 случайно сгенери
рованных точек, сгруппированных примерно на три области с более высокой плот
ностью, которые визуализируются на двумерном точечном графике:
:о о ' :
·:·· ·@··0~· ···· 08· ·:··
··о fg___~~()
0
4 · : ..... ;
080 00о . о о о: о о: '
о~о оо···········
~ : о
оо" ····· ···· · · · ·· · · ··· · ··· Jgi~
g:>o
Q)t()OO О
o<JO О
··· · ···· · · · : · ·· · ·· · ·· · · · :.·· · ·· ·· ·· · · · ·:· ·· ·· ·· · ··· · · ·:·---- -0
. ... :···- о.... ·--·-· ···· ·· ·· ··
6
-1'--~~~'--~~~'--~~~~'
· ~~~-~·
· ~~~--'-~~~-'-~~~-'
-3 -2 -1
Группирование обьектов по подобию .методо.м k средних 291
2. Назначить каждый образец самому ближайшему к ней центроиду µ<i),j Е {1, ""
k} .
3. Переместить каждый центроид в центр образцов, которые были ему назна
чены.
странстве:
d(x,y)
2
= ~)х1 -у1) 2 = llx-yll ~ ·
j=1
п k
SSE = LLw(i,J) lx<i) -µ(})11:.
i=1 }=1
Здесь µ(J) - это представительная точка (центроид) для кластера} и wи.л = 1, в случае
если образец x<i) находится в группе j, и w(i,j) = О в противном случае.
Познакомившись с тем, как работает простой алгоритм k средних, теперь приме
ним его к нашему модельному набору данных при помощи класса KMeans с реализа
цией алгоритма k средних из модуля sklea rn. cluster библиотеки scikit-learn:
from skl earn. clus ter impor t KMea ns
km = KMeans( n_clusters =З ,
ini t = ' r andom 1 ,
n_i nit =l O,
max _ i t er=ЗOO ,
tol=le - 04 ,
random_sta t e=O )
y_km = km. fit _predi ct (Х)
292 Работа с не.маркированными даннъ~tu - 1С11астерный анализ
Алгоритм k-средних++
Еще одна проблема с алгоритмом k средних состоит в том, что один или более
кластеров могут быть пустыми. Отметим, что эта проблема не существует для k
медоидов или алгоритма нечетких С-средних, который мы обсудим в следующем
подразделе. Впрочем, в текущей реализации k средних в библиотеке scikit-leaгn эта
проблема учтена. Если кластер пуст, то алгоритм отыщет образец, находящийся на
наибольшем удалении от центроида пустого кластера. Затем он сделает центроид
равным этой самой дальней точке.
Когда мы применяем алгоритм k средних к реальным данным, при этом пользуясь ев
с+ клидовой метрикой расстояния, мы должны убедиться, что признаки измеряются в той
ж е самой шкале, и при необходимости применить стандартизацию на основе z-оценки
либо минимаксное масштабирование.
После того как мы предсказали метки кластеров y_km и обсудили трудности ал
горитма k средних, теперь в наглядной форме покажем кластеры, которые алгоритм
k средних идентифицировал в наборе данных, вместе с кластерными центроидами.
Они хранятся в атрибуте centers_ подогнанного объекта KMeans:
plt.scatter (X[y_ km==0 , 0] ,
X[y_km==O , l] ,
s=50 ,
с= ' lightgreen ',
marker= ' s ',
label= ' кластер 1 ' )
plt . scatter(X[y_ km== l , 0] ,
X[y_km==l , l] ,
s=SO,
с = ' orange ',
marker=' о ',
lаЬеl = ' кластер 2 ')
plt . scatter(X[y_km==2 , 0J ,
X[y_ km==2 , 1] ,
s=SO ,
с = ' lightЫue ',
-1'--~~~'--~~~'--~~~...._~~~...._~~~-'-~~~-'-~~---'
-3 -2 -1 4
µ<1) ~о]
µ(2)~1.
[ µ(3) ~0
Например, если в приведенном выше примере k сред них выбрать три кластерных
центра, то принадлежность образца х< 0 кластеру µ<i>, можно вычислить следующим
образом:
Центр µ<iJ непосредственно самого кластера вычисляется как среднее всех образ
цов в кластере, взвешенного на степень принадлежности своему собственному клас
теру:
. L ~ wm(i.J) x<i)
µ (J) = ---'1--'
=1' - - - - -
" п wm(i,j)
.L.ti=1
Одна из основных трудностей в обучении без учителя состоит в том, что мы не зна
ем точного ответа. В нашем наборе данных нет раз и навсегда установленных дан
ных о метках классов, позволяющих применять методы, которые мы использовали
ние метрики - такие как внутрикластерная SSE (искажение или инерция), которую
мы обсудили ранее в этой главе, - для сравнения качества разных кластеризаций по
методу k средних. Удобно то, что нам не нужно вычислять внутрикластерную SSE
явным образом, поскольку этот показатель уже доступен в атрибуте inertia_ после
подгонки модели KMeans:
Группировт1ие объектов по подобию .методо,111 k средних 297
»>
рrint( 'Искажение : %. 2f ' % krn.iner t ia_)
Искажение : 72.48
700
600
500
"'"'
~ 400
3
:s::
300
200
100
О '-~~-'-~~-'-~~--'~~~'--~~-'-~~-'-~~--'~~~'--~--'
1 4 10
Ч и сло кла стеров
298 Работа с немаркированными данными - 1СJ1астерный анализ
Еще одна внутренняя метрика для оценки качества кластеризации представлена си
луэтным анализом, который также может применяться к другим алгоритмам клас
теризации, помимо k средних, которые мы обсудим далее в этой главе. Силуэтный
анализ может использоваться в качестве графического инструмента для построения
графика меры плотности группировки образцов в кластерах. Чтобы вычислить си
луэтный коэффициент одиночного образца в нашем наборе данных, можно приме
нить следующие три шага.
y_ax_lower , y_ax_upper = О , О
yticks = []
for i , с in enumerate(cluster_labels):
c_sil houette_vals = silhouette_vals[y_km с]
c_silhouette_vals . sort()
y_ax_upper += len(c_silhouette_vals)
color = cm.jet(float(i) / n_clusters) # float(i) для совместимост и с Python 2.7
plt . barh(range(y_ax_ lower , y_ax_upper) ,
c_ silhouette_vals ,
height =l . O,
edgecolor~ ' none ',
color=color )
yticks.append((y_ax_lower +у ах upper) / 2)
y_ax_ lower += len(c_silhouette_vals)
silhouette_avg = np.mean(silhouette_vals)
plt.axvline(silhouette_avg,
color= "red ",
linestyle= "- - " )
plt.yticks(yticks , cluster_ labels + 1)
plt . ylabel ('Кластер')
рlt.хlаЬеl('Силуэтный коэффициент ' )
plt. show ()
2 ')
lаЬеl= ' кластер
plt . scatter(km . cluster_cen t ers_ [ :, 0] ,
km.cluster_centers_[ :, l ] ,
s=25 0,
marker= ' * ',
с= ' red ',
lаЬеl = ' центроиды ')
plt . legend()
plt . grid ()
plt . show ()
. . a:t:J кластер 1
:о о : о .
о о g~~:
. ООО кластер 2
центроиды
: : .
о :о 0 : °во : 0 о 00
о~ооо··· ····· ·········.
*:
~ : о ~
z ...""" . .;" о".~ " , . о"." "" .г" "" "" . i" "2::~".~""""
.
.········:·
. :
···: ·~~
.
··. ·· ····
" .. " " ... ... .. .. " .. ....... ...... .. .. "...... ..... .. ." .. " ..0. """ " .0 .
о
"" ..
-1'--~~~_._~~~-'-~~~---~~~--'~~~~"--~~~-'-~~~-'
-З -2 -1 4
cluster_labels = np.unique(y_km)
n_clusters = cluster_labels.s hape(O ]
silhouette vals silhouette_samples(X ,
y_km ,
metric= ' euclidean')
y_ax_lower , y_ax_upper = О, О
yticks = (]
for i, с in enumerate(cluster_ labels):
c_ silhouette_vals = silhouette_vals(y_km с]
c_silhouette_vals.sort()
y_ax_upper += len(c_silhoue tte_vals)
color = cm.jet(i / n_clusters)
plt . barh(range(y_ax_lower , y_ax_upper) ,
c_silhouette_vals ,
height=l . O,
edgecolor= ' none ',
color=color)
yticks.append((y_ax_lower + y_ax_upper) / 2)
y_ax_lower += len(c_silhouette_vals)
silhouette_avg = np.mean(silhouette_vals)
plt . axvline(silhouette_avg , color="red" , linestyle=" --")
plt.yticks(yticks , cluster_labels + 1)
pl t. ylabel ('Кластер ' )
рlt.хlаЬеl( ' Силуэтный коэффициент ' )
plt. show ()
Как видно на итоговом графике, силуэты теперь имеют визуально различ имые
длину и ширину, предоставляя новые доказательства субоптимальной кластериза
ции:
Организация кластеров
в виде иерархического дерева
шее. Подход на основе полной связи подобен методу одиночной связи, но, вместо
того чтобы в каждой паре кластеров сравнивать самых похожих членов, для выпол
нения объединения мы сравниваем наиболее различающихся членов. Это показано
на приведенной ниже схеме:
с+
Другие широко используемые алгоритмы агломеративной иерархической кластериз а
ции предст авлены методом средней связи и методом Уорда . В методе средней связи мы
объед иняем пары кластеров, основываясь на минимальных средних расстояниях между
всеми членами в двух кластерах. В методе Уорда объединяются те два кластера, которые
приводят к минимальному увеличению общей внутрикластерной SSE.
х у z
ID О 6.964692 2.861393 2.268515
ID 1 5.513148 7.194690 4.231065
ID_2 9.807642 6.848297 4.809319
ID З 3.921175 3.431 780 7.290497
ID 4 4.385722 0.596779 3.980443
»>
from scipy.cluster . hierarchy import linkage
help (linkage)
[".]
Parameters:
у : ndarray
А condensed or redundant distance matrix . А condensed
distance matrix is а flat array containing the upper
triangular of the distance matrix. This is the form
that pdist returns . Alternatively, а collection of m
observation vectors in n dimensions may Ье passed as
an m Ьу n array.
1
Перевод документации:
Параметры:
у : ndarray
Матрица сжатых или избыточных расстояний. Матрица сжатых расстояний - это плоский мас
сив , содержащий верхний треугольник матрицы расстояний. Это та форма, в которой функ
ция pdist возвращает результат . Как вариант в качестве массива размера m на n можно
передать коллекцию m векторов наблюдений в n измерениях
method : str , факультативно
Используемый алгоритм связи. См. раздел методов связи ниже по поводу полного описания
metric : str, факультативно
Используемая метрика расстояния. См. функцию distance.pdist по поводу списка допусти
мых метрик расстояния
Возвращает
Иерархическая кластеризация, кодированная в виде матрицы связей. - При.м. перев.
Организация кластеро в в виде иерархического дерева 305
Returns :
Z : ndarray
The hierarchi cal cl us t ering encoded as а li nkage ma t rix.
[ ... ]
Если вы будете выполнять приведенный выше исходный код или читать элект
ронную версию этой книги, то заметите, что ветки в итоговой дендограмме по
казаны в различных цветах. Окрашивающая схема получена на основании списка
цветов библиотеки matplotlib, которые зациклены для порогов расстояний в ден
дограмме. Например, для по каза дендограммы в черном цвете вы можете раском
менти ровать соответствующие участки, которые вставлены в приведенны й выше
исходный код.
новы ваясь на евклидовой м етрике расстояния, образ цы ID_O и ID_4, а затем ID_ 1
и ID 2 являются самым и похожими .
axd . set_xticks ( [] )
axd . set_yticks ( [] )
for i iп axd.spines.val ues ():
i . set_visiЬle ( False )
fig . col orbar (сах )
axm . set_xticklabels ( [ ' ' ] + list(df_row c lust.columпs) )
axm. set_ yticklabels ( [ ' ' ] + lis t( df_ rowclus t .index ) )
plt . show ()
ID_4
-
ID_O
IDJ
ID_2
ID_1
>»
from sklearn . cluster import AgglomerativeClustering
ас = AgglomerativeClustering(n_clusters=2,
affinity='euclidean',
linkage= ' complete ')
labels = ac.fit_predict(X)
print('Meтки кластеров: % s ' % labels)
Метки кластеров: [О 1 1 О О] ]
Глядя на предсказанные метки кластеров, мы видим, что 1-й, 4-й и 5-й образцы
(ID_O, ID_З и ID_4) были назначены кластеру О, а образцы ID_1 и ID_2 - кластеру
1, что согласуется с результатами, которые мы можем наблюдать на дендограмме.
Локализация областей высокой плотности алгоритмо1~t DBSCAN 309
(j/" точка рассматривается как корневая, если, по меньшей мере, указанное число
окрестных точек (MinPts) попадает в пределы указанного радиуса s;
r:ft" граничная точка - это точка, имеющая соседей меньше, чем MinPts в преде
лах Е, но лежащая в пределах радиуса Е корневой точки;
r:ft" все остальные точки, которые не являются ни корневыми, ни граничными
После маркировки точек как корневых, граничных либо шумовых алгоритм DB-
SCAN можно резюмировать в двух простых шагах:
1) сформировать для каждой корневой т·очки отдельный кластер либо связную
группу корневых точек (корневые точки являются связными, в случае если
они расположены не дальше, чем s);
2) назначить каждую граничную точку кластеру соответствующей корневой
точки .
Корневая точка
Шумовая точка Граничная точка
о
,"
...
.----х
\ ,
1
1
,'
•
"......
------ ,
• ...,<.'
/
1
1
'
', \
\
' ... "-,-
\
... "
'
1
1
.
о
.
,
1 \ 1 • "" / ... ,
1 \ 1 , / '
' ,,
'
. "
/ / \
/ \
\
\
'~, ·... -~ __ ...... "'
/ \
'
', ,' 1
...... ... " __ .,.... ...... '' . /
'
\ 1
о MinPts =3 \ 1
о
о
310 Работа с немар~сированными данными - кластерный анализ
-0.S
:~~
. , ."-., ..
/
• •
-1 . О---~----------~---~--~-------
-1 .5 -1.0 -0.S о. о o.s 1.0 1.S 2.0 2.5
s=40 ,
lаЬеl= ' класте р 2' )
ах2 . sеt_titlе (' Агломеративна я кл астери зация ' )
pl t. lege nd ()
plt . show()
1.0
0.5
о .о
-0.5
-1.0 .___....__ _.__ _.__ __._ _....__.....__ _.__ _, -1 .0 .___....__ _.__ __.__ __._ _....__.....__ _.__ _,
-1.5 -1.0 -0.5 о. о 0.5 1.0 1.5 2.0 2.5 - 1.5 -1.0 -0.5 о.о 0.5 1.0 1.5 2.0 2.5
marker='s ',
s=40 ,
lаЬеl='кластер 2 ')
plt.legend()
plt. show ()
1.5 r------.-----.----,-----,.---..,.-----.----.,.----,
ооо кластер 1
••• кластер 2
1.0
0.5
о.о
-0.5
-1.0 ~-------------.....__--~---------'----~--~
-1.5 -1 .0 -0.5 о.о 0.5 1.0 1.5 2.0 2.5
с+
До настоя щего момента мы увидели три сам ы е фундаментальные категории алгорит
мов кластеризации: алгоритм k средних на основе прототипов, алгоритм агломератив
ной иерархической )(Jlастеризации и плотностная кластеризация на основе алго ритма
DBSCAN. Однако стоит упомянуть и четвертый класс более продвинутых алгоритмов
кластеризации, которые мы не затронули в этой главе: rрафовые алгоритмы кластери
зации. Вероятно, наиболее выдающиеся представители семейства графовых алгоритмов
кластеризации представлены алгоритмами спектральной кластеризации. Несмотря на
то что существует много разных реализаций спектральной кластеризации, все они объ
единены тем, что используют собственные векторы из матрицы подобия для получения
кластерных связей. Поскольку спектральная кластеризация выходит за рамки этой кни
ги, вы можете прочитать превосходное учебное пособие Ульрике фон Люксбург, чтобы
узнать об этой теме больше (U. Von Luxbuгg, «А Tutorial оп Spectral Clustering», Statis-
tics and computing, 17(4):395-416, 2007 ( «Учебн.ое пособие по спектралыюй кластериза
ции»)). Оно находится в свободном доступе на arXiv по прямой ссылке http://arxiv.org/
pdf/0711 .0189vl.pdf.
Резюме
В этой главе вы узнали о трех разных алгоритмах кластеризации, которые спо
собны помочь в обнаружении скрытых структур или информации в данных. Мы
начали эту главу с подхода на основе прототипов, алгоритма k средних, который
кластеризует образцы в сферические формы на основе заданного числа кластерных
центроидов. Учитывая, что кластеризация является методом обучения без учителя,
мы не можем себе позволить роскошь раз и навсегда определенных данных о мет
ках классов с целью оценки качества модели. Поэтому мы обратились к полезным
внутренним метрикам оценки качества, таким как метод локтя или силуэтный ана
лиз, в качестве попытки количественно определить качество кластеризации.
ка интереса нейронные сети еще раз стали самой горячей темой в научных иссле
дованиях в области машинного обучения. Благодаря разработанным в последние
годы алгоритмам глубокого обучения нейронные сети воспринимаются как передо
вая технология для многих сложных задач, таких как классификация изображений
и распознавание речи. В главе 12 <.Z Тренировка искусственных нейронных сетей для
распознавания изображений» мы построим с нуля нашу собственную многослойную
нейронную сеть. В главе 1З <-ZРаспараллеливание тренировки нейронных сетей при
помощи Тhеапо» мы представим мощные библиотеки, которые способны помочь
наиболее эффективно тренировать сложные сетевые архитектуры.
Глава 12
Тренировка
искусственных нейронных сетей
для распознавания изображений
к
ак вы, возможно, знаете, глубокое обучение сейчас привлекает к себе много
внимания СМИ и является, вне всякого сомнения, самой горячей темой в сфере
машинного обучения. Глубокое обучение может пониматься как набор алгоритмов,
разработанных для наиболее эффективной тренировки искусственных нейронных
сетей, состоящих из множества слоев . В этой главе вы изучите основы икусствен
ных нейронных сетей, благодаря чему будете хорошо подготовлены для дальней
шего изучения самых захватывающих областей научного исследования в сфере
машинного обучения, а также продвинутых программных библиотек Python для
работы с глубоким обучением .
Мы затронем следующие темы:
rв- получение концептуального представления о многослойных нейронных сетях;
<1r тренировка нейронных сетей для задачи классификации изображений;
r:r реализация мощного алгоритма обратного распространения ошибки;
r:r отладка реализаций нейронных сетей .
сетям вспыхнул вновь, когда в 1986 г. Д . Румел харт, Г. Хинтон и Р. Дж. Уильямс
повторно открыли и популяризировали алгоритм обратного распространения
ошибки (backpгopagation) для более эффективной тренировки нейронных сетей
(Rumelhart D. Е., Нinton G. Е . , Williams R.J. ( 1986). Leamiпg Representations Ьу Back-
propagatiпgErrors («Обучение представлений методом обратного распространения
ошибки»). Nature 323 (6088): 533-536), и мы обсудим этот алгоритм более подроб
но далее в этой главе.
В течение предыдущего десятилетия многие другие важные прорывные работы
привели к тому, что теперь мы называем алгоритмами глубокого обучения; они
используются для создания из немаркированных данных детекторов признаков
' В химической отрасли виртуальный скрининг (virtual screening) - выбор соединений путем
оценки их желательности в вычислительной модели. - Прим. перев.
Моделирование сложныr: фун1сций искусственными нейронными сеmя.А1и 317
Эта глава полностью посвящена многослойным нейронным сетям: тому, как они ра
ботают и как их тренировать для решения сложных задач. Однако, прежде чем мы
углубимся в архитектуру отдельно взятой многослойной нейронной сети , кратко
повторим некоторые понятия однослойных нейронных сетей, которые мы предста
вили в главе 2 «Тренировка алгоритмов машинного обучения для задачи юzассифика
ции», в частности алгоритм адаптивного линейного нейрона (ADALINE), который
показан на нижеследующем рисунке:
Элемент ,- - - - , Функция
1
смещения,
активации
А Предсказанная
У метка класса
Функция Единичная
1
1 \ чистого ступенчатая
1 - - - -
входа функция
' Весовые
' коэффициенты
Входные
значения
w := w + Лw,
где Лw = - riV(w).
Другими словами, мы вычисляли градиент, основываясь на всем тренировочном
наборе, и обновляли веса модели, делая шаг в противоположном от градиента на
правлении V.J(w). Для того чтобы найти оптимальные веса модели, мы оптимизи
ровали целевую функцию, которую определили как функцию стоимости на осно
ве суммы квадратичных ошибок.J(w) (sum of squai·ed епогs, SSE). Кроме того, мы
умножали градиент на темп обучения Т] , коэффициент, который мы выбрали тща
тельно, чтобы сбалансировать скорость обучения против риска непопадания по гло
бальному минимуму функции стоимости.
В оптимизации на основе градиентного спуска мы после каждой эпохи обновляли
все веса одновременно, при этом мы определили частную производную для каждого
_о__ ](w)=-I<y<i)-a(i))xji>.
owj i
318 Тренировка искусственных нейронных сетей для распознавания изображений
Здесь y<i) - это целевая метка класса отдельно взятого образца x<iJ и a<iJ - актива
ция нейрона, т. е . линейная функция в частном случае ADALINE. Кроме того, мы
определили фуикцию активации ф( ·) как:
ф(z) = z =а.
Здесь чистый вход z- это линейная комбинация весов, которая соединяет вход
ной слой с выходным:
z = 'Lwjxj =wт х.
j
Для вычисления градиентного обновления мы использовали активацию ф(z), тог
да как для предсказания мы реализовали пороговую функцию (функцию Хевисай
да), которая запихивает непрерывнозначный выход в бинарные метки классов:
• { 1, еслиg(z)~О
у=
-1, иначе
В этом разделе мы увидим, как соединять два и более единичных нейрона в много
слойную нейронную сеть с прямым распространением сигналов; этот особый тип
сети также называется многослойным персептроном (МСП, MLP). Следующий
ниже рисунок объясняет принцип работы MLP, состоящего из трех слоев: один
входной слой, один скрытый слой и один выходной слой. Узлы (units) в скрытом
слое полностью связаны с входным слоем, и соответственно, выходной слой полно
стью связан со скрытым слоем. Если такая сеть имеет более одного скрытого слоя,
то мы также называем ее глубокой искусственной нейронной сетью.
Для создания более глубокой сетевой архитектуры мы можем добавить в MLP про
с+ извольное число скрытых слоев. П рактически число слоев и узлов в нейронной сети
можно представить как дополнительные rипе рп араметры, которые мы хотим оптими
зировать для данной практической задачи, используя для этого перекрестную проверку,
которую мы обсудили в главе 6 «Анализ наиболее успешнъt:х приелюв оценивания люделей
и тонкой настройки гиперпаралtетров>-> .
Однако градиенты ошибки, которые мы вычислим позже методом обратного распро
странения ошибки, стали бы по мере добавления в сеть новых слоев во все возрастаю
щей степени уменьшаться. Эта проблема исчезновения градиента еще более усложняет
извлечение модели. Поэтому, чтобы предварительно натренировать такие глубокие ней
росетевые структуры, были разработаны специальные алгоритмы под общим названием
глубокого обучения .
a<t) 1
о
a<t) x(i)
a<t) = 1 1
a<t ) x<i)
т т
вого коэффициента, катаный соединяет k-ый узел в слое l сj-ым узлом в слое l + 1,
мы записали w)~k· а не wk,j· То, что поначалу может выглядеть немного необычным,
обретет больший смысл в более поздних разделах, где мы займемся векторизацией
представления нейронной сети. Например, мы обобщаем веса, которые соединяют
входной и скрытый слои, матрицей w< 1> Е 1Rhx [m+ 1J, где h - это число скрытых узлов
и т +1- число входных узлов плюс узел смещения. Поскольку эту форму записи
важно усвоить, чтобы понимать смысл упоминаемых далее в этой главе понятий,
подытожим то, что мы только что обсудили, описательной иллюстрацией упрощен
ного многослойного персептрона в конфигурации 3-4-3:
Слой/=1 Слой l = 2
с 3 входными с3 скрытыми
узлами (т =3) узлами (h = 3)
без учета смещения без учета смещения Слой/= 3
leJ~~?
Число слоев: L =3 Соединяет 1-й нейрон
без смещения в слое
с 3-м узлом слоя 3
а}2) = ф( z}2> ).
Здесь z?) - это чистый вход и ф( ·) - функция активации, которая для извлече
ния связывающих нейроны весов должна быть дифференцируемой на основе гради
ентного подхода. Чтобы суметь решать такие сложные задачи, как классификация
изображений , в нашей модели MLP нужны нелинейные функции активации, напри
мер сигмоидальная (логистическая) функция активации , которую мы использова
л и в лоrистической реrрессии в главе З « Обзор 'КJlассификаторов с использованием
библиотеки scikit-leam»:
1
ф(z)=--.
i+e-z
Как мы помним, сигмоидальная функция - это S-образная кривая, которая прое
цирует чистый вход z на логистическое распределение в диапазоне от О до 1, рассе
кая ось У в z= О, как показано на следующ е м ниже графике:
,. . __ 1
~ ф(z)=--.
--е- 1+e-z
0.5 ----- -··· · ---· ·· ·· --··· ···· · ·· ····· · · --- -- -- ---- -· · ···· ····· --· ··· ·
о.о
-8 -6 -4 -2 о 2 4 б z 8
MLP явля ется тип и чным прим ером искусственной нейронной сети прямого рас
пространения сигналов. Термин «прямое распространение сигналов» обозначает, что
каждый слой служит входом в следующий слой без циклов , в отличие от рекурреит
ных нейрою-t ых сетей, архитектуры, которую мы обсудим далее в этой главе. Термин
« многослойный персептрон» может показ аться немного озадачивающим, поскольку
322 Тренировка искусственных нейронных сетей для распознавания изображений
Здесь а<1) - это наш [т + 1] х 1-мерный вектор признаков образца х< 1 > плюс узел
смещения. w<1) - это hx[m + 1]-мерная весовая матрица, где h - число скрытых
узлов в нейронной сети . После матрично-векторного умножения получаем hx 1 -мер
ный вектор чистого входа z< 2>для вычисления активации а< 2 > (где а< 2 > Е JR.1'x1). Более
того, мы можем обобщить это вычисление на все п образцов в тренировочном на
боре:
Здесь А(1) - это теперь пх [т + 1)-матрица, при этом матрично -матричное умноже
ние в результате даст hхп-мерную матрицу z< > чистых входов. В конце к каждому
2
A<2J = ф(z<2» .
Z(З) = W(2)A(2).
если вы плохо знакомы с этой темой. Прежде чем продолжить обсуждение алгорит
ма обратного распространения ошибки, который используется для извлечения весов
модели MLP, немного отдохнем от теории и взглянем на нейронную сеть в действии .
Теория не йронных сетей может быть довольно сложной для понимания. В связи с этим
с+ предлагаем два дополнительных ресурса, где некоторые понятия, которые мы обсужда
ем в этой главе, разбираются более подробно:
• Hastie Т., Friedman ]., Tibshirani R. The Elements of Statistical Leaming («Элементы ста
тистического обучения~). Vol. 2. Springer, 2009;
• Bishop С. М. et al. Pattem Recognition and Machine Leaming («Распознавание образов
и машинное обучение~ ). Vol. 1. Springer New York, 2006.
В этом раздел е мы натр е нируем нашу первую многослойную нейронную сеть за
даче классификации рукописных цифр из популярного набора данных MNIST - со
кращенно от Mixed National Institute of Standaгds and Technology, смешанная база
данных Национального института стандартов и технологий, созданная Я. ЛеКуном
и др. и служащая популярным эталонным набором данных для алгоритмов машин
ного обучения (LeCun У., Bottou L., Bengio У., Haffneг Р. Gradient-based Learning Ap-
plied to Document Recognition («Обучение на основе градиентных методов примени
тельно к распознаванию документов~). Pгoceedings of the IEEE, 86(11):2278-2324,
Novembeг 1998).
gzip *uЬyte . gz -d
реализации MLP:
import os
import struct
import numpy as np
Функция load_mnist возвращает два массива, первый - это пхm-м ерный массив
NumPy (i mages), где п - число образцов и т - число признаков. Тренировочный на
бор данных состоит из 60 ООО тренировочных цифр; тестовый набор соответственно
содержит 1О ООО образцов. Изображения в наборе данных MNIST представляют со
бой растры размером 28 х 28 пикселов, в котором каждый пиксел представлен значе
нием интенсивности полутоновой шкалы . Здесь мы разворачиваем 28Х28 пикселов
в одномерные векторы-строки, соответствующие строкам в нашем массиве изобра
жений (784 пиксела в расчете на строку или изображение) . Второй возвращаемой
функцией load_mn ist (l abels) массив содержит соответствующую целевую перемен
ную, метки классов (целые числа 0-9) рукописных цифр.
Способ считывания изображения может сперва показаться немного необычным:
Чтобы разобраться в том, как эти две строки исходного кода работают, посмотрим
на описание набора данных на веб-сайте MNIST:
При помощи двух строк приведенного выше и сходного кода мы сначала считы
ваем магическое число, которое представляет собой описание формата файла или
файлового протокола, а также число элементов (п) из файлового буфера и только
потом методом fromfi le считываем последующие байты в массив NumPy. Знач ение
параметра fmt ( >11), которое мы передали в качестве аргумента в struct .unpack, имеет
дв е части :
" > : обратный порядок байтов (определяет порядок, в котором хранится после
довательность байтов); если термины прямой и обратиый порядок байтов вам
незнакомы, то превосходную статью по данной теме можно найти в Википе
дии (https://ru . wikipedia . org/wiki/Пopядoк_бaйтoв);
<7" 1: беззнаковое целое число.
Выполнив следующий ниже фрагмент исходного кода, теперь загруз им 60 ООО
тренировочных экземпляров и 10 ООО тестовых образцов из каталога mn ist, куда мы
разархивиров ал и набор данных MNISТ:
>»
X_train , y_t rai n = load_mnis t('mnist ' , ki nd= ' tra in ' )
рrint( ' Тренировка - строки : % d , столб цы : %d '
% (X_train . s hape[O] , X_train.s hape [l] ))
Тренировка - строки: 60 ООО , столбцы : 784
Чтобы полу чить представление о том , как выглядят изображения в MNIST, по
кажем примеры цифр 0-9 в наглядном виде после приведения 784-пиксельных
векторов из нашей матрицы признаков в исходные изображения в формате 28 х 28,
которые можно вывести на экран при помощи imshow библиотеки matplotlib:
import matplotlib.pyplot as pl t
fig , ах= plt.subplots (nrows=2 , ncols=S , share x=True , s harey=True ,)
ах = ax .fla t ten ()
for i in range (lO) :
img = X_ tra1n[y train == i] [О] .reshape (28 , 28 )
ax[i] .imshow (img , cmap= ' Greys ', in t e r polation= ' nearest ' )
ах [ О] . se t _xt icks ( [] )
ах[О] . se t _yticks ( [J )
pl t . tight _layout ()
plt . show ()
/' ~ 3 ч
В дополнение к этому также пост роим график с повто р ными примерами одной
и той же цифры, чтобы увидеть, насколько в действительности эти примеры почер
ка друг от друга отличаются:
fig , plt.subplots(nrows=S ,
ах=
ncols=S ,
sharex=True ,
sharey=True , )
ах = ax.flatten ()
for i in range(25):
img = X_train[y_tra1n == 7] [1] . reshape(28 , 28)
ax[i] . imshow(img, cmap= ' Greys', interpolation= ' nearest')
ах[О] .set_xticks([])
ах[О] .set_yticks([J)
plt.tight_layout()
plt. show ()
np.savetxt (' train_img . csv ', X_train , fmt= ' %i ', delimi t er= ', ')
np.savet xt(' train_labe ls . csv ', y_t r a in , f mt = ' %i ', delimi t er= ', ')
np . savetxt (' test_img.csv ' , X_t est , fmt= 1 %i ', delimiter= ', ')
np . savetxt (' tes t _labels.csv ', y_te s t, f mt = ' %i ', delimi t er= ',')
Пар амет ры
n_out pu t : in t
Чи сло вы ходн ых узло в.
Должно р авн я ться ч и сл у ун и к а льн ых м е т о к класс ов .
n f ea t ures : int
Чи сло пр изнаков (ра змер но с т е й ) в це л евом н а боре да нных .
Д о лжн о рав н ят ьс я чи слу столбц о в в массиве Х.
Атрибуты
cost l i st/cпиcoк
Сумма квадратичных ошибок после каждой эпохи.
Параметры
Возвращает
def in1t1al1ze_we1ghts(self) :
" " " Инициализировать веса малыми случайными числами. " " "
wl np . random . uniform(-1.0 , 1.0,
size=self . n_hidden*(self.n_features + 1))
wl wl.reshape(self . n_hidden , self.n_features + 1)
w2 np . random.uniform( - 1.0 , 1 . 0,
size=self . n_output*(self.n_hidden + 1))
w2 w2.reshape(self.n_output , self.n hidden + 1)
return wl , w2
def sigmoid_gradient(self, z) :
" "" Вычисли ть градиент логистической функции"""
sg = self. s1gmo1d (z)
return sg * (1 - sg)
Параметры
Классификация рукоnиС11ых цифр 331
Возвращает
Возвращает
332 Тренировка искусственных нейронных сетей для распознавания изображений
cost : float
Регуляризованная стоимость .
Параметры
Возвращает
# обратное распространение
sigmaЗ = аЗ - y_enc
z2 = self ._add_bias_unit(z2, how='row')
sigma2 = w2 . T. dot(sigma3) * self ._sigmoid_gradient(z2)
sigma2 = sigma2 [ 1 :, : ]
gradl sigma2 . dot(al )
grad2 = sigma3 . dot(a2 . T)
# ре г уляризовать
gradl [ : , 1:] += self . 12 * wl [: , 1: ]
gradl [: , 1 : ] += self . 11 * np . sign (wl [ : , 1:] )
grad2[ :, l : ] += self . 12 * w2[ :, 1:]
Классифи~сацил рукописных цифр 333
def predict(self , Х) :
'""' Идентифицировать ( предсказать ) метки классов
Пара метры
Возвращает
if leп ( X.shape)
1; 2:
raise AttributeError(
'Х должен быть массивом [п_samples , п features] . \п '
' Используйте Х[ :, Nопе] для 1 - признаковой классификации ,'
' \плибо X[[i}} для 1 - точечной классификации ' )
Парамет ры
на устройстве stderr.
Возвращает
self
self . cost ; [}
Х data , у data; Х . сору (), у . сору ()
у_епс; self._eпcode labels(y , self.п_ou tp ut)
if pr1пt_progress :
334 Тренировка искусственных нейронных сетей дл.я распознавания изображений
if self . shuffie:
idx = пp.raпdom.permutatioп(y data . shape[O])
X_data , у_епс = X_data[idx] , y_eпc[:,idx ]
wl=self . wl ,
w2=self . w2)
self . cost . appeпd(cost)
returп self
>»
nn .fit(X_train , y_ train , print_progress=True)
Эпоха: 1000/1000
1500
ь
~
s:
1000
С)
.:;
500
Хотя на графике уже можно увидет ь , что алгоритм оптимизации сходился при
близительно после 800 эпох (40 000/50 = 800), мы построим график более гладкой
вер сии функции стоимости в сопоставлении с числом эпох путем усреднения по
мини - пакетным интервалам. Исходный код следующий:
Следующий ниже график дает нам более ясную картину, указывающую на то, что
алгоритм обучения сходился вскоре после 800- й эпохи :
Классификация рукописных цифр 337
1500
"'
t:;
~ 1000
"
о
500
О'--~~~~--'-~~~~~-'-~~~~~""-~~~~--'~~~~~-'
о 200 400 600 800 1000
Эпох и
»>
y_train_pred = nn.predict(X_train)
асс = np.surn(y_train == y_ train_pred, axis=O) / X_ train.shape(O]
print('Bepнocть на трен ир овочном наборе : %.2f%% ' % (ас с* 100 ))
Верность на тренировочном наборе: 97 .59 %
>»
y_test_pred = nn . predict(X_ test)
асс= np.sum(y_ test == y_test_pred , axis=O) / X_test . shape [OJ
print( ' Bepнocть на тестовом наборе: %.2f%% ' % (а сс * 100))
Верность на тестовом наборе: 95.62 %
1) t: 5 р : 6 2) t: 4 р: 9 3) t: 2 р: 3 4) t: 9 р : 8 5) t: 5 р: 3
t.; 11 9 11 ~ 11 <ь 11 ~
6) t: 8 р : 7 7) t: 4 р : 2 8) t: 6 р: о 9) t: 8 р: 9 10)t: 9p: 7
? ; ь ~ у
11)t: 2p:7
7
16)t: 2р : З
11
11
12) t: 5 р : 3
.s
17) t: 6 р : о
11
11
13) t: 5 р : о
~
18) t: 9 р : 8
11
11
14) t: 2 р : 7
~
19) t: 3 р : 5
11
11
,
15)t : 5р : З
20) t: 9 р : 3
2 11
L? 11 $ · 11 3 11 ~
21)t:8p:2 22) t: 4 р: 1 23) t: 8 р : 3 24) t: 7 р : 1 25) t: 8 р: 2
.8 11
у 11 ~ 11 f 11 ~
Как видно на приведенном выше рисунке, некоторые из этих изображений слож
но классифицировать правильно даже людям . Например, мы видим , что цифра 9
классифицируется как 3 или 8, в случае если нижняя часть цифры имеет крюко
образное искривление (подграфики 4, 18 и 20).
Треиировка иС1.усстве1111ой иейроииой сети 339
Здесь аи> - это сигмоидальная активация i-го узла в одном из слоев, который мы
вычисляем на шаге прямого распространения сигналов:
п t
](w) =-IDY) log(a)i))+(1-yT)log(1-a)i)).
i:1 j:1
Здесь надстрочный индекс i- это индекс отдельно взятого образца в нашем тре
нировочном наборе.
Следующий ниже обобщенный член регуляризации поначалу выглядит довольно
сложным, но здесь мы просто вычисляем сумму всех весов слоя l (без члена смеще
ния), которую мы добавили к первому столбцу:
L-1 щ щ+1
~"""<w~i?)2.
2L.L.L. J,I
/:1 i:1 j:1
о
-щJ<W).
8w ],1. .
На этом упрощенном рисунке может показаться, что w< 1) и w< 2) имеют одно и то
же число строк и столбцов, что, как правило, это не так, в случае если мы не ини
циализируем MLP одинаковым числом скрытых узлов, выходных узлов и входных
признаков.
Входх
Л(З) = а(З ) - у.
бф(z<2))
Здесь - это просто производная сигмоидальной функции активации, ко-
5z<2>
торую мы реализовали как _sigmoid_gradient:
-°-(- 1-)
ф' (z) = /)z 1+e-z
C+~-z J
((1+~-z)J
1
(1+e-z )
=ф(z)-(ф(z)) 2
=ф(z)(1-ф(z))
=а(1-а) .
Далее нам нужно накопить частную производную каждого }-го узла в слое l
и i-ю ошибку узла в слое l + 1:
сt> ·= л~1~ + a~t)s:~1+1)
л 1,) • 1,] } u1 •
Напомним, что для каждого образца в тренировочном наборе нам нужно вычис
лить обновление л~:J. Следовательно, будет проще, если реализовать его в вектори
зованной версии, как в нашей предыдущей программной реализации MLP:
л<f) = л<f) + л< 1+ 1 J(А(/Jуг.
_д_}(W) = a(/)8(/+t)
auP> /,)
1 '
Входной х
~2
8 (2) = (W(2»T 8 (3) дg(z< >)
az<2>
(ошиб ка скрытого слоя)
8у 8/ 8g
8х 8g 8х
В контексте вычислительной атrгебры был разработан ряд методов для очень эф
фективного решения таких задач под общим названием методов автоматического
дифференцирования. Если вам интересно больше узнать об автоматюrеском диффе
ренцировании в приложениях с использованием машинного обучения, то рекомен
дуем обратиться к следующему ресурсу: Baydin А. G., Pearlтиtter В . А. Autornatic Dif-
feгentiation of Algoгithrns fог Machine Learning. aгXiv pгepгint aгXiv:1404.7456, 2014
(Бейдин А. Г., Перлматтер А. «Автоматическое дифференцирование алгоритмов для
машитюго обучения»), который имеется в свободном доступе по прямой ссылке на
ar Xiv по http://arxiv.org/pdf/1404.7456.pdf.
Автоматическое дифференцирование имеет два режима, соответственно прямой
и обратный режимы. Обратное распространение ошибки - это просто частный слу
чай обратного режима автоматического дифференцирования. Ключевой момент за
ключается в том, что применение цепного правила в прямом режиме может быть
довольно затратным, поскольку нам пришлось бы умножать большие матрицы для
каждого слоя (якобианы), которые мы в конечном счете умножаем на вектор для
получения выхода. Трюк обратного режима состоит в том, что мы начинаем справа
налево: мы умножаем матрицу на вектор и в результате получаем другой вектор,
который умножается на следующую матрицу, и т. д. Матрично - векторное умноже
ние является в вычислительном плане намного более дешевым, чем матрично - мат
ричное умножение, и именно по этой причине алгоритм обратного распространения
ошибки является одним из самых популярных алгоритмов, используемых для тре
нировки нейронных сетей.
Здесь Е - это, как правило, очень небольшое число, например 1е-5 (отметим, что
1е-5 - это просто более удобная запись числа 0.00001). Эта конечная разностная ап
проксимация может быть интуитивно представлена как наклон секущей, соединяю
щей точки функции стоимости для двух весов w и w +Е (оба скалярные величины),
как показано на нижеследующем рисунке . Для простоты надстрочные и подстроч
ные индексы мы опускаем.
Стоимость
](ш)
w
0.1 0.2
Ошибка= 111~ - 1~ 11
2.
111~ - 1~112
Относительная оши б ка= lll~ + lll~ .
112 112
Возвращает
num_gradl np.zeros(np.shape(wl))
epsilon_aryl = np.zeros(np . shape(wl))
for i in range(wl.shape[O)):
for j in range(wl . shape[l]) :
epsilon aryl[i , j) epsilon
al , z2 , а2, zЗ, аЗ = self._feedforward(
Х,
wl - epsilon aryl ,
w2)
costl self . _get_cost (y_enc ,
аз ,
wl - epsilon_aryl ,
w2)
al, z2, а2 , zЗ , аЗ self . feedforward(
х,
wl + epsilon aryl ,
w2)
cost2 self . _get cost(y_enc ,
аз,
wl + epsilon aryl,
w2)
num_gradl[i, j) (cost2 - costl) / (2 * epsilon)
epsilon_aryl[i, j] =О
num_grad2 = np.zeros(np.shape(w2))
epsilon_ary2 = np . zeros(np.shape(w2))
for i in range(w2.shape[0]):
for j in range(w2 . shape[l]) :
epsilon_ary2[i , j] epsilon
al, z2 , а2, zЗ, аЗ = self . _feedforward(
х,
wl ,
w2 - epsilon_ary2)
costl self . _get_cos t(y_enc,
аз ,
wl ,
w2 - epsilon ary2)
al, z2 , а2 , zЗ , аз self. feedforward(
х,
wl ,
w2 + epsilon_ary2)
cost2 self . _get cost(y enc ,
аз,
wl ,
w2 + epsilon_ary2)
num_grad2[i, j] (cost2 - costl) / (2 * epsilon)
epsilon_ary2[i , j] =О
class MLPGradientCheck(object):
[ ... ]
def fit(self, Х, у, print_progress=False) :
[ ... ]
# вычислить градиент методом обратного распространения
gradl, grad2 = self. _get_gradient(
al=al,
а2=а2,
аЗ=аЗ ,
z2=z2 ,
y_enc=y_enc [ : , idx] ,
wl=self . wl,
w2=self.w2)
## начало проверки градиента
grad_diff = self . _gradient_checking(
X=X[idx],
у_enc=y _enc [ : , idx] ,
wl=self.wl,
w2=self.w2,
epsilon=le-5,
gradl=gradl ,
grad2=grad2)
if grad_diff <= le- 7:
print( ' Ycпeшнo: %s' % grad_diff)
elif grad_diff <= le - 4:
рrint( ' Предупреждение : %s' % grad_d iff)
else:
рrint( ' ПРОБЛЕМА: %s ' % grad_diff)
return self
>»
nn che ck .fit(X_ train[ : SJ , y_ t r a in[:S ] , print_progress =Fals e )
Ус п ешн о : 2 . 5 671 2936241 е- 10
Успе шно : 2 . 94 6 03251069 е- 10
Усп е шно: 2 . 3 7 6 1 5620231 е- 10
Успешн о: 2 . 4 34 69423226 е- 10
У с п ешно : З. 3 787 2073158 е- 10
Усп ешно : З . 6 3 466384861 е- 10
Успе ш но : 2 . 22472120785е - 10
Усп ешн о : 2 . 3 31 6 37 08438 е-1 0
Успешн о : З . 4 4653 6865 51е- 10
У спе шн о: 2.17 1 6 170 7 211 е- 10
Локальный м ин им ум
Глобальны й ми н им у м
J(W)
Сверточные нейронные сети (convolutional neural network, CNN или ConvNet) за
воевали популярность в компьютерном зрении из-за их экстраординарно хороше
рисунке:
Другие нейросетевые архитектуры 353
Здесь ключевая идея состоит в том, что если детектор признаков полезен в одной
части изображения, то он может оказаться полезным и в другой части тоже. Хоро
ший побочный эффект этого подхода состоит в том, что он значительно сокращает
количество параметров, которые должны быть извлечены. Поскольку различным
участкам изображения разрешено быть представленными по-разному, нейронные
сети CNN в особенности хороши в распознавании объектов на изображении различ
ных размеров и различных положений. Нам особо не нужно беспокоиться о норма
лизации и центрировании изображений, так как это делалось в MNISТ.
В нейронных сетях CNN за сверточным слоем следует объединяющий слой (poo-
liпg layer) ( иногда также именуемый подвыборкой) . При объединении мы обобщаем
окрестные детекторы признаков для сокращения количества признаков для следую
щего слоя. Объединение может пониматься как простой метод выделения призна
ков, где мы берем среднее или максимальное значение участка окрестных признаков
и передаем его в следующий слой. Для создания глубокой сверточной нейронной
сети мы складываем два и более слоев в ярусы, чередуя сверточные и объединяю
щие слои, после чего мы соединяем их с многослойным персептроном для выполне
ния классификации. Это показано на нижеследующем рисунке:
-fl
Выходная
метка
Карты Классификатор
Входное изображение признаков
Карты признаков
354 Трениро81Са искусственных нейронных сетей для распознавания изображений
Рекуррентные нейронные сети (РНС, recuпent neuгal netwoгk, RNN) можно рас
сматривать как нейронные сети прямого распространения сигналов с циклами об
ратной связи или обратным распространением по времени. В нейронных сетях RNN
нейроны срабатывают только в течение ограниченного количества времени, прежде
чем они будут (временно) деактивированы. В свою очередь, эти нейроны активиру
ют другие нейроны, которые срабатывают в более поздний момент времени . В своей
основе рекуррентные нейронные сети можно представить как MLP с дополнитель
ной переменной времени. Временная составляющая и динамическая структура дает
сети возможность использовать не только текущие входы, но и входы, которые она
Рекурренц и я
г------- ~
1
-- --- - - -J
Входно й сл о й С крыты й сло й Выходно й слой
сетевую архитектуру, как правило, намного труднее тренировать. Это вызвано тем,
что мы не можем просто распространять ошибку назад от слоя к слою; мы должны
учитывать дополнительный компонент времени, который усиливает проблему ис
чезновения и взрыва градиента. В 1997 г. Юрген Шмидхубер и его коллеги предста
вили так называемые долгие кратко временные элементы памяти для преодоления
этой проблемы : Long Shoгt Тегm Меmогу (LSTM); Hochreiter S" Schтidhиber]. Long
Shoгt- teгm Меmогу. Neuгal Computation, 9(8):1735-1780, 1997 («долгая кратковре
менная память ~ ) .
Однако мы должны отметить, что существует много разных вариантов нейрон
ных сетей RNN, и их детальное обсуждение выходит за рамки этой книги.
РезюJ11е 355
Резюме
В этой главе вы узнали о самых важных идеях, лежащих в основе многослойных ис
кусственных нейронных сетей, которые в настоящее время являются самой горячей
темой исследований в области машинного обучения . В главе 2 «Тренировка алгорит
мов машишюго обучения для задачи 1СЛассификаи,ии» мы начали наше путешествие
с простых однослойных нейросетевых структур, затем мы соединили многослойные
нейроны в мощную нейросетевую архитектуру для решения таких сложных задач,
как распознавание рукописных цифр. Мы сняли покров загадочности с популяр
ного алгоритма обратного распространения ошибки, который является одним из
базовых элементов многих моделей нейронных сетей, использующихся в глубоком
обучении. Изучив алгоритм обратного распространения ошибки, мы смогли обно
вить веса сложной нейронной сети. Мы также добавили полезные видоизменения,
такие как мини-пакетное обучение и адаптивный темп обучения, которые позволя
ют тренировать нейронную сеть более эффективно.
Глава 13
Распараллеливаниетренировки
нейронных сетей при помощи Theano
в
предыдущей главе мы прошлись по большому количеству математических по
нятий, чтобы разобраться в том, как работают искусственные нейронные сети
прямого распространения сигналов, в частности в работе многослойных персеп
тронов . Прежде всего наличие хорошего понимания математической подоплеки ал
горитмов машинного обучения очень важно, поскольку оно помогает нам исполь
зовать эти мощные алгоритмы наиболее эффективно и правш~ыю. В предыдущих
главах вы посвятили много времени изучению наиболее успешных практических
методов машинного обучения и даже попрактиковались в самостоятельной реализа
ции алгоритмов с нуля. В этой главе вы можете почивать на лаврах, откинувшись на
спинку кресла, и наслаждаться увлекательной поездкой по одной из самых мощных
библиотек, которая используется исследователями в области машинного обучения
для экспериментирования с глубокими нейронными сетями и очень эффективной
их тренировки. Значительная часть современных научных исследований использует
компьютеры с мощными графическими процессорами (ГП, GPU). Если вам инте
ресно окунуться в глубокое обучение, которое в настоящее время является самой
горячей темой в исследованиях в области машинного обучения, то эта глава опре
деленно для вас. Однако не переживайте, если у вас нет доступа к ГП; в этой главе
использование ГП будет факультативным .
Прежде чем мы начнем, сделаем краткий обзор тем, которые мы затронем в этой
главе:
имеющими два и более ядер . В предыдущих главах мы увидели, что многие функ
ции в библиотеке scikit-leaш позволяют распределять вычисления по нескольким
процессорам. Однако по причине глобальной блокировки интерпретатора (global
inteгpгeteг lock, GIL) по умолчанию Python ограничивается выполнением на одном
ядре. Однако, несмотря на то что для распределения вычислений по двум и более
ядрам мы используем в своих интересах его библиотеку multiprocessing, мы должны
учитывать, что даже продвинутые настольные компьютеры редко имеют больше 8
или 16 таких ядер .
Если мы вспомним предыдущую главу, где мы реализовали очень простой много
слойный персептрон всего с одним скрытым слоем, состоящим из 50 узлов, то там
нам уже пришлось выполнять оптимизацию приблизительно 1000 весовых коэффи
циентов, чтобы извлечь модель для очень простой задачи классификации изобра
жений . Изображения в MNIST довольно маленькие (28 х 28 пикселов), и мы можем
только вообразить взрывной рост числа параметров, если мы захотим добавить до
полнительные скрытые слои или работать с изображениями, имеющими более вы
сокую плотность пикселов. Такая задача быстро стала бы невыполнимой для одно
го-единственного процессора. Теперь вопрос состоит в том, каким образом можно
преодолевать такие проблемы эффективнее. Оч евидное решение этой проблемы со
стоит в том, чтобы использовать графические процессоры (ГП). Графический про
цессор - это настоящий энергетик. Видеокарту можно представить как небольшой
компьютерный кластер внутри вашей машины. Еще одно его преимущество состоит
в том, что, как видно из следующей ниже обзорной таблицы, современные ГП от
носительно дешевы, по сравнению с современным ЦП:
в наших задачах машинного обучения? Трудность состоит в том, что написание ис
ходного кода, пр едназначенного для гэпов, н е так тривиально, как выполнение ис
>»
import theano
from theano import tensor as Т
# инициализировать
xl Т. scalar ()
wl Т . scalar ()
wO T . scalar ()
zl wl * xl + wO
# скомпилировать
360 Распараллеливание тренировки нейронных сетей при по.мощи Тhеапо
# и с полнить
рrin t(' Чистый в х од : %.2f ' % ne t_input (2. 0, 1.0 , 0 .5))
Чистый вхо д: 2 .5 0
export THEANO_FLAGS=floatX=float32
»>
print(theano .config.device )
cpu
Кроме того, может быть удобным создать в своем корневом (домашнем) каталоге
файл .thea norc, чтобы сделать эти настройки конфигурации постоянными. Напри
мер, чтобы всегда использовать float3 2 и ГП, вы можете создать такой файл .the-
anorc, включив приведенные ниже настройки . Команда следующая:
[global]
floatX=float32
device=gpu
>»
import numpy as np
# инициализировать
# если Theano в 64 - разрядном режиме ,
вы выполняете то
# вам нужно использовать dmatrix вместо fmatrix
х = T.fmatrix(name='x')
х sum = T.sum(x, axis=O)
# ско мп илировать
calc_sum = theano.function(inputs=[x], outputs=x_sum)
Как мы видели ранее, работая с библиотекой Theano, нам нужно выполнить всего
три основных шага: определить переменную, скомпилировать код и выполнить его .
Предыдущий пример показывает, что Theano может работать как с типами Python,
так и с типами NumPy: list и numpy.ndarray.
с+
Отметим , что когда мы создавали тензорную переменную fmatrix, которая может приго
диться во время отладки нашего кода либо распечатки графа Theano, мы использовали
дополнительный именованный аргумент (в данном случае х). Например, если распеча
тать символ х тензорной переменной fmatrix, не давая ему имени name, функция печати
print возвратит его тип тензора TensorType:
>»
print(x)
<TensorType (float32, matrix) >
»>
print (х)
х
>»
print (х. type ())
<TensorType (float32 , matrix)>
Сборка, ко.мnШ1Яцuя и вътолнение выражений в Theano 363
Theano также имеет очень умную систему управления памятью, которая для уско
рения работы использует память повторно. Если более конкретно, то Theano рас
пределяет объем памяти по нескольким устройствам, ЦП и ГП; чтобы отслеживать
изменения в объеме памяти, она назначает псевдонимы соответствующим буферам.
Далее мы посмотрим на переменную shared, позволяющую распределять большие
объекты (массивы ) и предоставлять нескольким функциям чтения и записи право
доступа, в результате чего после компиляции мы также можем выполнять обновле
ния на этих объектах. Подробное описание обработки памяти в Theano выходит за
рамки этой книги. Поэтому призываем вас отслеживать обновление информации
о Theano и об управлении памятью на http://deeplea rning .net/software/theano/tu t orial/
aliasing.html.
»>
# инициализировать
х = T.fmatrix('x')
w = theano.shared(np.asarray([[O.O, О.О, О.О] ] ,
dtype=theano.config.floatX))
z = x . dot (w . T)
upda t e = [ [ w, w + 1 . О] ]
# скомпилиро вать
ne t inpu t theano.function(inputs=[x] ,
updates=update ,
outputs=z)
# исполнить
da ta = np.array( [[ l, 2 , З]] ,
dtype=theano.config.floatX)
for i in range (5):
print( ' z%d : ' % i , net_input(data))
zO : [ [ О . ]]
zl: [ [ 6 . ]]
z2 : [ [ 12 . ]]
zЗ : [ [ 18.]]
z4: [ [ 24.]]
объявили, что хотим обновлять массив w значением 1.0 после каждой итерации
цикла for. После того как мы определили, какой объект мы хотим обновлять и ка
ким образом, мы передали эту информацию параметру update компилятора theano.
function.
Еще один ловкий трюк в библиотеке Theano состоит в том, чтобы использовать
переменную givens для вставки значений в граф перед его компиляцией . Используя
этот подход, мы можем сократить количество перемещений из оперативной памяти
(RAM) через ЦП в ГП, ускоряя работу алгоритмов обучения, в которых исполь
зуются совместно используемые (общие) переменные. Если в компиляторе theano.
fun ction использовать параметр inputs, то данные будут перемещаться из ЦП в ГП
многократно, например если мы многократно (эпохи) выполняем итерации по набо
ру данных во время градиентного спуска. Используя givens, мы можем держать на
бор данных на ГП, если он помещается в его памяти (например, если мы выполняем
тренировку мини - пакетами) . Исходный код следующий:
364 РаспаралJlеливание тренировкu нейронных сетей при помощи Тhеапо
>»
# инициализировать
data = np.array([[l , 2, 3]],
dtype=theaпo.coпfig.floatX)
х = T.fmatrix( ' x ')
w = theaпo.shared(пp.asarray([[O . O, О . О, О.О]],
dtype=theaпo . coпfig. floatX) )
z x.dot (w . T)
update = [[w , w + 1 . 0]]
# скомпилировать
net input theano.function(inputs=[],
updates=update,
givens=(x: data] ,
outputs=z)
# исполнить
for i in range(5) :
priпt ( 'z: ', пеt input ())
zO: [ [ О. J]
zl: [ [ 6.]]
z2: [ [ 12.]]
z3: [ [ 18. J J
z4: [ [ 24.]]
Глядя на приведенный выше пример исходного кода, мы также видим, что атри
бут givens - это словарь Python, который ставит в соответствие имени переменной
фактический объект Python. Здесь мы назначили это имя, когда определяли fmatrix.
[ 4 . О], [5 . О] ,
[6 . О] , [7. О] ,
(8. OJ , [9. О] J ,
dtype=theano . config. floatX)
у traiп np . asarray([l.0 , 1.3,
3 . 1, 2. О ,
5.О , 6 . 3,
6 . 6, 7 . 4,
8. О , 9. О],
dtype=theaпo. coпfig. floatX)
import t he ano
from t heano i mpor t t ensor as т
i mport numpy as np
costs = [ ]
# Инициализировать массивы
etaO = T. fscalar (' e t aO ' )
у T.fvector (name= ' y ' )
Х T. fmatri x( name= ' X')
w = theano.s hared (np . zeros (
shape= (X_train.s ha pe[l] + 1),
dt ype= theano . config .floatX ),
name= ' w ' )
# Вычислить стоимость
# Скомпилировать модель
train = theano . fun c tion(input s=[e t aOJ ,
outpu t s=cost ,
updates=upda te ,
give ns={ X: X_ t rain ,
у : y_ train , ) )
for i n range (e pochs ):
costs.append(train (eta ))
return costs , w
plt . show ()
Збб Распараллеливание тренировки нейронных сетей при помощи Тhеапо
Как видно на следующем ниже графике, алгоритм обучения сходился уже после
пятой эпохи:
300
Z50
t zoo
о
"
:s:
ь 150
100
50
4 10
Э поха
def predict_linreg(X , w) :
Xt = T.matrix(name='X')
net_input = T.dot(Xt, w[l:]) + w[O ]
predict = theano.function(inputs= [Xt ] ,
givens=(w: w),
outputs=net_input)
return predict(X)
plt.scatter(X_train ,
y_train ,
marker:::: ' s ',
s=50)
plt . plot(range(X_ train.shape[O ] ),
predict_linreg(X_train, w),
color= ' gray ',
marker=' о',
marke rsize=4,
linewidth=З)
plt.xlabel ( ' х')
plt . ylabel ( ' у')
plt.show()
>- 4
- 2'--~~~.........~~~~'--~~~.........~~~--''--~~~-'-~~~__.
-2 о 4 10
1
Флогистическая(z) = -
-z ·
1+е
Здесь скалярная переменная z определяется как чистый вход:
т
z =w0x 0 + ...+wmxm = Ix w = Wт х.
1 1
j=O
def lo gistic(z):
Выбор функций аюпивации для нейронных сетей с прюtы.м распространенuе.Аt сигналов 369
return 1. 0 / (1 . 0 + np . exp (- z ) )
Если выч ислить чистый вход и использовать его для активации логистического
н ей ро н а с этими отдел ь н о взятыми значе ниями признаков и в есовыми коэффициен
там и, то мы в р езул ьтат е пол учим з нач е ние 0.707, которо е можно инте рпретиров ать
как вероятность в размере 70.7%, что эта отдельно взятый образец х принадлежит
положител ьному классу. В главе 12 «Тренировка искусственных нейронных сетей для
распознавания изображений» мы использовали м етод прямого кодирования для вы
числения значений в выходном слое, состоящем из двух и более узлов логистиче
ской активации. Однако, как будет показано на следующем ниже примере исходного
кода, выходно й слой , состоящий из двух и более узлов логистической активации, не
произ водит содержательных, поддающ ихся интерпр етации значений вероятности :
»>
W: массив , форма = [n_output_units , n_hidde n units+l]
Весова я матри ц а дл я скрыто г о сл оя - > выход ной сл ой .
# Примечание: первый столбец (W[ : ] [0 ] ) содержит у злы смещения
W np.array ( [[ l.l , 1.2 , 1 . 3, 0 . 5] ,
(0 . 1, 0 . 2, 0.4 , 0 . 1] ,
[0 . 2, 0 . 5, 2.1 , 1 . 9]] )
[О . 3] ,
(0 . 7]] )
Вероя т ности :
[ [ 0.87653295]
[ 0 . 57688526 ]
о. 90114393]]
»>
y_ cla s s = np .argmax (Z, axis=O )
рr int ('Пр е д с каза нна я метка кл асс а: %d ' % y_class [O] )
Пре дска з анная метка класс а: 2
В е р оятности :
[[ 0 .4 038 64 93]
[ 0 . 07756222]
[ 0 . 5185 7284 ]]
y_proba s. sum()
1. 0
>»
y_class = np.argmax(Z , axis=O)
рrint ('Предска занна я метка класса :
%d' % y_class[OJ )
В скрытых слоях искусственных нейронных сетей часто используется еще одна сиг
моидальная функция - функция гиперболического тангенса (tanh), которую мож
но интерпретировать как приведенную версию логистической функции:
ez -e-z
Фtаnь (z) =Флогистическая(2 х z)-1 = z -z ;
е +е
1
Флогистическая (z) = - -z ·
1 +е
def tanh(z):
е_р = np.exp(z)
e_m = np.exp(-z)
ret urn (е_р - e_m) / (е_р + e_m)
z = np.arange(-5, 5, 0 . 005 )
l og_act = logistic (z)
tanh_act = tanh(z)
plt.ylim([ - 1 . 5, 1 . 5})
рlt.хlаЬеl( ' чистый вход $z$')
рlt.уlаЬеl( ' активация $\phi(z)$')
pl t.axh l ine( l , co l or= ' Ьlack ', lines t yle= '--')
plt . axhline(0.5 , color='Ьlack ', linestyle= '--')
plt . axhline (O, color= ' Ьlack ', linestyle= '--')
plt.axhline(-1, color= ' Ьlack ', linestyle= '--')
plt .plot(z, tanh_act,
l inewidth=2,
color = 1
Ыac k ',
color='lightgreen',
lаЬеl= ' логистическая')
plt . legend(loc = ' lower right')
plt.tight_layout()
plt. show ()
Как видно, формы двух сигмоидальных кривых выглядят очень похожими; од
нако функция tanh имеет выходное пространство в 2 раза больше, чем в логистиче
ской функции:
1.5
1.0
0.5
""'
~
:s:
о .о
~
"'
:s:
\;;;
"' -0.5
-1 .0
лоr и ст и чес ка я
-1.S
-6 -4 -2 4
ч и сты й вход z
tanh_act = np.tanh(z)
Единичная ступенчатая
1О, Вариант персептрона
-F
z< O,
(Хевисайд)
ф(z)= 0.5, z=O,
1, z> O
г
Кусочно-постоянная z<O, Вариант персептрош1
(сигнум)
Лине йная
ф(z) = о.'
ф(z) = z
1,
z= O,
z> O
---+=
Кусочно-лин ей ная 1 Метод опорных ве1<торов
1, z;?:2,
1 1 1
ф(z)= z+-
2' - -z<z<2,
о, 1
z:<;--
2
+
Ло ги стичес1<ая (сиrмоида) 1 Логистичес1<ая регрессия,
ф(z)=~ мно1·ослой11ая НС
+е
+
Гиnерболичес1<ий тангенс ф(z) = е' -e-z Многослойная НС
е' +e-z
<lf" train - images- idxЗ - ubyte . gz: тренировочный набор изображений (9 912 422 бай-
та);
<lf" train- labe ls-idxl-ubyte .gz: тренировочный набор меток (28 881 байт);
~ tlOk- images- idxЗ - ubyte . gz: тестовый набор изображений (1 648 877 байт);
<lf" tlOk-labels-idxl-ubyte .gz: тестовый набор меток ( 4542 байта).
После скачивания и разархивирования мы помещаем файлы в каталог mn ist в на
шем текущем рабочем каталоге, чтобы можно было загружать тренировочный и тес
товый наборы данных при помощи следующей ниже функции:
»>
import os
import struct
import numpy as np
impo rt t hea no
theano. config. floatX = ' float32 '
X_train = X_ train . as t ype( theano . config . floa t X)
X_test = X_test . astype(thea no . config . flo atX)
Далее нам нужно преобразовать метки классов (целые числа 0-9) в формат пря
мого кода. К счастью, библиотека Ке гаs обеспечивает для этого удобный инстру
м е нт :
»>
from keras . utils i mport np_u t ils
print( 'П epвыe 3 метки :', y_train[ : З] )
Первые 3 метки : [5 О 4]
np . random . seed(l )
model = Sequential ()
model.add(Dense (input di m=X_ t rain . s hape[l] ,
output_dim=SO ,
init= ' uniform 1 ,
activation= 'tanh '))
Наконец, число узлов в выходном слое должно быть равно числу уникальных
меток классов - числу столбцов в массиве меток классов в прямой кодировке .
Прежде чем мы сможем скомпилировать нашу модель, мы также должны задать
оптимизатор . В предыдущем примере мы выбрали оптимизацию на основе стохас
тического градиентного спуска, с которой мы уже знакомы из предыдущих глав.
Кроме того, мы можем установить значения для константы снижения весов и им
пульса обучения, чтобы скорректировать темп обучения в каждой эпохе, как об
суждалось в главе 12 «Тренировка искусственных нейронных сетей для распознава
ния изображений» . Наконец, мы устанавливаем функцию стоимости (или потерь)
в categorical_crossentropy. (Бинарная) перекрестная энтропия - это просто техни
ческий термин для функции стоимости в логистической регрессии, и категориаль
ная перекрестная энтропия - это ее обобщение для многоклассовых прогнозов
с использованием функции softmax. После компиляции модели теперь можно на
тренировать ее путем вызова метода fit. Здесь мы используем мини-пакетный сто
хастический градиент с размером пакета 300 тренировочных образцов из расчета
на пакет. Мы тренируем MLP на 50 эпохах и можем проследить за оптимизацией
функции стоимости во время тренировки, установив verbose=l . Параметр valida -
особенно удобен, поскольку он резервирует 10% тренировочных данных
tion_split
(в данном случае 6000 образцов) для проверки после каждой эпохи, с тем чтобы
во время тренировки мы могли удостовериться, что модель не находится в пере
подогнанном состоянии .
»>
mode l .fit (X_t r ain,
y_tra i n_ohe ,
nb_epoch=50 ,
b at c h_size = ЗOO ,
ve r bose=l ,
valida ti on_spli t= O. l ,
s how_a ccu r ac y=True)
Epoch 1
54000/54000 [==============================] - ls - loss: 1.8850 -
асс: 0 . 5279 - val - loss : 1.6098 - val - асс: 0 .56 17
Epoch 2
54000/54000 [==============================] - ls - loss : 1 . 3903 -
асс: 0.5884 - val loss : 1.1666 - val - асс: 0.6707
-
Epoch 3
54000/54000 [= ~ ============================] - ls - loss: 1.0592 -
асс : 0.6936 - val loss : 0 . 8961 - val асс : 0.7615
[".]
Epoch 49
54000/54000 [==============================] - ls - loss: 0.1907 -
асс : 0.9432 - val loss : 0 .1749 - val асс: 0.9482
- -
Распечатка значения функции стоимости чрезвычайно полезна во время трени
ровки, поскольку мы можем быстро установить, уменьшается ли стоимость во время
тренировки, и остановить алгоритм раньше, чтобы настроить значения гиперпара
метров.
»>
y_train_pred = model.predict_c lasses(X_train , verbose=O)
print ( ' Первые 3 предск азания: ', y_train_pred[ :3 ] )
Первые 3 предсказания : [5 О 4]
Отметим, что это всего лишь очень простая нейронная сеть без оптимизирован
ных настроечных параметров. Если вам интересно поэкспериментировать с Кегаs
дальше, то вы можете свободно попробовать поменять темп обучения, импульс, сни
жение весов и число скрытых узлов.
Резюме
Я надеюсь, что вы получили удовольствие от этой последней главы захватывающего
путешествия по машинному обучению. В этой книге мы затронули все существенные
темы, которые эта область может предложить, и теперь вы должны быть хорошо под
готовлены для приведения этих методов в действие для решения реальных задач.
Мы начали наше путешествие с краткого обзора разных типов обучения выпол
нять задачи: обучение с учителем, обучение с подкреплением и обучение без учителя.
Мы обсудили несколько различных алгоритмов обучения, которые могут исполь
зоваться для классификации данных, начиная с простых однослойных нейронных
сетей в zлаве2 «Тренировка алгоритмов машинного обучения для задачи классифи
кации». Затем мы обсудили более усовершенствованные алгоритмы классификации
в главе З « Обзор классификаторов с использованием библиотеки scikit-leam~. а в гла
ве 4 «Создание хороших тренировочных наборов - предобработка данных» и zла
ве 5 «Сжатие данных путем снижения размерности» вы узнали о самых важных
аспектах конвейера машинного обучения. Напомним, что даже самый продвинутый
алгоритм ограничен информацией в тренировочных данных, на которых он обуча
ется. В zлаве 6 «Анализ наиболее успешных приемов оценивания моделей и тонкой
настройки гиперпараметров» вы изучили наиболее успешные практические мето
ды построения и оценивания прогнозных моделей, т. е. еще одного важного аспекта
в приложениях с использованием машинного обучения. Если один-единственный
алгоритм обучения не достигает необходимого нам качества, то для выполнения
прогноза иногда целесообразно создать ансамбль экспертов. Мы обсудили это в гла
ве 7 « Объединение .моделей для методов ансамблевого обучения». В zлаве 8 «Приме
нение алгоритмов машинного обучения в анализе мнений» мы применили машинное
обучение для анализа, вероятно, самой интересной формы данных в наше время,
которая занимает доминирующее положение в платформах социальных медиа в Ин
тернете: текстовых документов. Однако машинные методы обучения не ограничива
ются офлайновым анализом данных, и в главе 9 «Встраивание алгоритма машинного
обучения в веб-приложение» мы увидели, как встраивать машиннообучаемую модель
в неб-приложение, с тем чтобы поделиться им с внешним миром. По большей части
наше внимание концентрировалось на алгоритмах для классификации данных, веро
ятно, наиболее популярном приложении методов машинного обучения. Однако на
этом они не заканчиваются! В главе 10 «Предсказание непрерывных целевых величин
при помощи регрессио~того анализа» мы разобрали несколько алгоритмов для задач
регрессионного анализа с предсказанием выходных значений непрерывной целевой
переменной. Еще одной захватывающей подобластью машинного обучения являет
ся кластерный анализ, который способен помочь найти в данных скрытые струк
туры, даже если наши тренировочные данные не содержат правильных ответов, на
Оценка моделей
делении данных; назовем ее генеральной верностью ACCpopu lati 0 11 (m). При этом, чтобы
оценить обобщающую способность, как правило, мы применяем методы перекрест
ной проверки и раздельный независимый тестовый набор данных.
Переобучение выполняется, если имеется альтернативная модель т' из простран
ста гипотез алгоритма, где точность на тренировочном наборе лучше и обобщающая
способность хуже, по сравнению с моделью т, - мы говорим, что т переобучена (из
лишне адаптирована или аппроксимирована) под тренировочные данные.
Исходя из эмпирического опыта, модель, скорее всего, переобучена, если она
слишком сложна при наличии фиксированного числа тренировочных образцов.
Приведенный ниже рисунок показывает тренировочную и перекрестно-провероч
ную кривые модели SVM на некотором наборе данных. Здесь верность показана как
функция обратного параметра регуляризации С - чем больше значение параметра
С, тем крупнее штраф за сложность.
Оцешса .моделей 381
1.00 ,_...............................,....__,__,.....,.........,....,..._~_..,....,..,....,.....-....-.......,.......,.......,..-....,.........................,
0.9S
- -" - ----
;
-"
ь
~ 0.90 .. . """ . . " " . ; " . . . " . . " . . " . " :. " ." . . "." .. . " , """ "" . . " " .. :· ·· · "
а.
(])
со
o.ss ..... "" .." .. .... .." ... .."".;". " .....
~
• • Проверочная верность
0.80 _________....__ _...__ _.__________.....______ _._.~.__-- ..................
10·! 10·2 10·1 100 101 102
Пара м етр С
Тренировка Оценка
~~~~~~~~~~~ ~~~~~~
Тренировочный набор
Тренировочный набор Jl j
Перый ряд обозначает сценарий 1; третий ряд описывает более «классический»
подход, когда вы далее подразделяете ваши тренировочные данные на тренировоч
1 -я итерация J
•••
грессионных моделей, одну для каждого из т внешних блоков, при этом внутренние
блоки используются для оптимизации гиперпараметров каждой модели (например,
с использованием поиска по сетке параметров в сочетании с k-блочной перекрест
ной проверкой. Если ваша модель стабильна, то все эти т моделей должны иметь
одинаковые значения гиперпараметров, и вы сообщаете о среднем качестве работы
модели, основываясь на внешних тестовых блоках. Затем вы переходите к следую
щему алгоритму, например SVM, и т. д.
384 Приложение А
Тренировочные блоки
Тестовый t
щ;еэ - 4ЗС: > ;
1 б2,, =I
1 о -· J _ щ, _ [ _, __pµl
1 """= ,_",,,. - -= _,[ " 1 ММ-- ] Внешний цикл
Тренировать
параметрами
Внутренний цикл
Перекрестно·
Тренировочный блок проверочныи блок
} Настроить параметры
1_."
Теперь можно быстро извлечь тренировочный набор, отложив 40% данных для
тестирования (оценки) нашего классификатора:
»>
X_ t r ain , X_ test, y_ tr a in , y_ test train_test_split(
iris.data , iris . target , test_size=0 . 4, random_state=O)
Оценка ..1t0делей 385
ных (как это происходит во время фиксации произвольного тестового набора). В этом
главное преимущество, в частности для таких задач, где число образцов очень мало.
Самый простой способ применения перекрестной проверки состоит в вызове
вспомогательной функции cross_val_score на оценщике и наборе данных .
Следующий ниже пример демонстрирует, как оценивать точность линейного ме
тода опорных векторов на наборе данных lгis, разбив данные, выполнив подгонку
модели и вычислив оценку 5 раз подряд (каждый раз с разными разбивками ) .
»>
from sklearn .model_selection import cross_val_score
cl f = svm.SVC(kernel = ' linear ', C=l )
scores = cross_val_score(clf , iris.data, iris .targe t , cv=S)
scor e s
array ( [ О. 96 ... , 1. 0 . 96 ... , 0 . 96 ... , 1. ])
386 Прuложе1ше А
»>
рrint("Точность: %0.2f (+/ - %0.2f)" % (scores . mean(), scores.std() * 2))
Точность: 0.98 (+ /- 0.03)
»>
from sklearn import metrics
scores cross_val_score(
clf, iris . data, iris.target, cv=S , scoring='fl_macro')
scores
array( [ О. 96"" 1 . 0.96"., 0.96"" 1 . ])
»>
from sklearn.model_selection import ShuffieSplit
n_samples = iris.data.shape[O]
cv = ShuffieSplit(n_splits=3, test_size=0 . 3, random_state=O)
cross_val_score(clf, iris.data, iris.target, cv=cv)
»>
from sklearn.model selection import LeaveOneOut
х = [1 , 2 , 3, 4]
100 = LeaveOneOut()
for train, test in loo.split(X):
print( "%s %s" % (train, test))
[1 2 3 ] [ О]
[О 2 3 ] [ 1]
[О 3] [2]
[О 1 2 ] [ 3]
Оценка .моделей 387
»>
from sklearn . model selection import StratifiedKFold
Х = np.ones(lO)
у = [О , О , О , О , 1, 1, 1, 1, 1, 1]
skf = StratifiedKFold ( n_ splits=З )
for train, test in skf . split (X, у ):
print(" %s %s" % (trai n, test))
[2 3 6 7 8 9 ] [ о 1 4 5]
[О 3 4 5 8 9] (2 6 7]
[ 0 1 2 4 5 6 7] [3 8 9 ]
# настроить конвейер
cls = SVC (C=l0.0 , kernel= ' rbf' , gamma=O.l, decision_ function_ shape= ' ovr ' )
kernel svm Pipeline([('std', StandardScaler()),
(
1
SVC ' ' cls ) ] )
gs svm GridSearchCV(estimator=kerпel_svm ,
param_grid=param_grid ,
scoriпg= ' accuracy ',
пjobs=-1,
cv=5 ,
verbose=O ,
refit =True ,
pre_dispatch= '2 *п_jobs ' )
>»
import пumpy as пр
params []
scores []
skfold Stra t1fiedKFold ( у=у traiп, п f olds=5, shuffie=False, raпdom _ sta te=l)
for traiп idx , test idx iп skfold:
gs svm .fit (X_tra1n [train_idx] , y_tra1n [tra1n_1dx])
y_pred = gs_svm.predict(X_train[test_idx])
асс= accuracy_score(y_true=y_train[test_idx] , y_pred=y_pred)
params.appeпd(gs_svm.best_params )
scores . appeпd(acc)
Модели SVM:
1. Точность : О . 96 , параме тры: {<svc_C >: 100 , >svc kernel >: >rЬf >, >svc_gamma >: О . 001)
2. Точность : l . 00 , параметры : {< svс_С>:lОО, >svc kernel>:>rЬf>, >svc_gamma>: 0 . 001]
3. Точность : О. 83, параметры: {<svc_C >: 1000 , >svc_kernel >: >rЬf >, >svc_gamma >: О. 001)
4. Точность : 1. 00 , параметры: {<svc _С >: 100 , >svc _ kernel >: >rЬf >, >svc_gamma >: О. 001)
5. Точность : О . 9б, параметры: {<svc_C >: 100 , >svc kernel >: >rЬf >, >svc_gamma >: О. 001)
»>
gs svm . fi t( X_ train , y_ t ra i n)
рrint( ' Наилучши е параметры %s ' % gs_ svm.best_params_ )
Наилучшие параметры {<svc_ C> : 100 , <svc_kernel> : <rbf> , <svc_ gamma> : 0 . 001)
Чтобы выполнить проверку качества модели, нам нужна оценочная функция (ко
ли чественная оценка качества предсказаний) , на приме р точность/правил ьность для
кл ассиф икаторов . Надлежащий способ выбора гиперпараметров оценщика - это,
разумеется, сеточный по иск либо подобные методы тонкой настройки гиперпараме
тров , которые отбирают гиперпараметр с максимальной оценкой на одном либо двух
и более проверочных наборах . Отметим, что если мы оптимизировали гиперпараме
тры , основыв ая сь на про ве рочно й (валидационно й ) оценке , то проверочная оц е нка
смещена и больше не я вляется хорошим оценочным показателем качества обобщ е
ния. Чтобы получить надлежащий показатель качества обобщения, мы должны вы
числить оценку на еще одном тестовом наборе .
Однако иногда, чтобы выяснить , переобучен ил и недообучен оценщ ик для не
скольких значений гип ерпараметров, помогает построение графика влияния оди
ночного ги перпар аметр а на тренировочную и пров ерочную оценки.
»>
import numpy as np
from sklearn. model_selection i mport valida t ion_curve
f r om sklearn . datasets import load_ iris
fr om skl earn . linear model import Ridge
np.random.seed (O)
ir i s = load_ iris()
Х , у = iris.data , i ris.target
indices = np . arange (y.shape[O] )
np . random.s huffie (indice s )
Х , у= X[indices ] , y[indices]
390 Приложение А
valid scores
array([[ 0 . 90 .. . , о. 92 ... ' о . 94 ... ) '
[ 0 . 90". ' о. 92 ... , о . 94 ... ) ,
[ о . 44. " , о . 39 " . ' о . 45 ". ]] )
1.0
0.8
ro
~0.6
о
0.4 •
0.2
- Тренировочная оценка
- Перекрестно-проверочная оценка
о . о ._ _________.......,_ _.____ __....___________ .....
10-6 10-s 10-4 1о-з 10-2
у
Оценка .1.tоделей 391
# настроить конвейер
cls ; SVC(C; l 0 . 0,
kernel; ' rЬf ' ,
gamma;0.1 ,
decision function shape; ' ovr ' )
gs GridSearchCV(estimator;kernel svm ,
param_grid;param_grid,
scoring; ' accuracy ',
n jobs; -1 ,
cv;S,
verbose;l ,
refit=Tr ue,
pre_d1spatch; ' 2*n jobs ' )
»>
gs .fit (X_ train , y_ train)
>»
рrint( ' Лучшая оценка в результате сеточного поиска %.2f ' % gs.best_score_)
рrint('Лучшие параметры в результате сеточного поиска %s' % gs.best_params
»>
from sklearn. cross_validation import StratifiedKFold, cross val score
from sklearn . linear_model import LogisticRegression
import numpy as np
np.random.seed(O)
np . set_printoptions(precision=6)
у [ np.random.randint(З) for i in range(25)]
Х = (у + np . random.randn(25)) .reshape( - 1, 1)
»>
from sklearn.grid_search import GridSearchCV
gs = GridSearchCV (LogisticRegression (), {) , cv=cv5 idx, verbose=З) .fit (Х, у)
Машинное обучение 393
[ CV ] .. .. .. . • . • . ..•...•....• • .••••........•.•••........••....••.••...
[CV] . .. .........•.•... ... .... " " .......... , score=0.600000 - O. Os
Как видно, оценки для 5 блоков точно такие же, что и те, которые были из cross_
val_score ране е .
Теперь атрибут bestscore объекта GridSearchCV, который становится доступным
после подгонки методом fit, возвращает среднюю оценку точности лучшей модели:
>»
gs.be s t _ score
0.4 8
Машинное обучение
[+ ]SON QavaScript Object Notation) - это легковесный формат обмена данными, легкий
для чтения и написания людьми и простой для разбора и порождения машинами. Он
основан на подмножестве языка программирования JavaScript стандарта ЕСМА-262, 3-е
изд. - декабрь 1999 г. JSON - это текстовый формат, который полностью независим от
языков программирования и одновременно использует обозначения, понятные для про
граммистов семейства языков С, в том числе С, С++, С#, Java, JavaScript, Perl, Python
и многих других. Эти особенности делают JSON идеальным языком обмена данными
[источник: http://www.json.org].
Одно из преимуществ JSON состоит в том, что этот формат удобен для восприя
тия человеком. Поэтому когда дело доходит до крайностей, мы все равно способ
ны прочитать файлы параметров и модельных коэффициентов «в ручном» режиме,
присвоить эти значения соответствующему оценщику scikit-leaгn или построить
нашу собственную модель, которая воспроизведет научные результаты.
Посмотрим, как это работает. Сначала натренируем простой логистический ре
грессионный классификатор на данных цветков ириса Iгis:
iris = load_iris()
у , Х = iris.target , iris.data[ :, [О , 2]] #использовать всего 2 признака
lr = LogisticRegression(C=lOO . O,
class_weight=None,
dual=False ,
fit_intercept=True ,
intercept_scaling=l,
max_ iter=lOO,
396 Пршюжен.uе А
.
pl t. xlabel ( ' длина чашели с тика' )
рlt . уlаЬ еl (' длина лепестка ' )
plt. s how()
8-
ш
7- 1
6- 2
"' 5 -
"t;
..,"'
с
с:;
"'
::z:
~ з
1 ...
••..•:i·1•···· •.
о ~------~
4 6 7 8
длина чашеnи<Тика
import json
path = ' ./sckit- model-to- json/params.json '
with open (path, ' w', encoding= ' utf - 8 ' ) as outfile:
json.dump(lr.get_params (), outfile)
При чтении файла можно увидеть, что файл JSON - это просто копия один-в
один нашего словаря Python в текстовом формате:
»>
path = '. /sckit - model -to-json/params.json '
wit h open (pa th , ' r ', encoding= ' utf - 8 ') as i nfile :
print(infile.read())
{
11
n_ jobs ": 1 , "random_state ": 1 , "intercept_scaling ": 1, "penalty ": "12 11 , 11
fit_intercept ":
true, "solver ": "newton - cg", "ve rbose ": О, "warm_start ": false, "max_i ter": 100 , "tol ":
0.0001 , "dual ": false , " С ": 100 . 0 , class_wei ght": null , "multi_class 11 "multinomial" }
11
:
Чтобы сериализовать массивы NumPy в объекты JSON, нам сначала нужно пре
образовать массивы в (вложенные) списки Python. Однако благодаря методу to list
это не особо хлопотно (помимо этого, для ясности можно подумать о сохранении
атрибутов в отдельных файлах JSON, например intercept.json и coef.json).
import nump y as np
for k in attr dict :
if isinstance(attr_dict[k] , np . ndarray) :
attr_dic t [k] = attr_dict[k] .tolist()
Если все прошло прекрасно, то наш файл JSON в простом текстовом формате
должен выглядеть следующим образом:
>»
path = '. /sckit -model-to-json /at tributes.json'
wi th open (path, ' r ', encoding=' utf-8') as infile:
print(infile.read())
11
classes_ 11 : [
о,
1,
2
] '
"coef 11
: [
0.426252363761105,
- 8.557501546501133
],
1.564423133644856 ,
- 1.678365901997047
],
[
- 1.9906754973974157 ,
10.235867448237142
],
"i ntercept_": [
27 . 5333848531875,
4.185099109107042,
- 31 . 718483962281997
],
"n iter_ ": [
27
iris load_iris()
у, Х iris . target, iris . data[ :, [О , 2]] #использовать всего 2 признака
lr = LogisticRegression()
lr.set_params(**params)
for k in attributes :
if isinstance(attributes[k] , list) :
setattr (lr , k, np . array(attributes[k] ))
else :
setattr(lr , k, attributes[k])
Термины
ADALINE (adaptive linear element, адаптивный линейный элемент) - частный
случай линейного классификатора или искусственной нейронной сети с одним
слоем.
ботки данных этот метод известен как нормализация данных, которая обычно вы
полняется на этапе предобработки.
Матрично-векторное умножение (matгix-vectш multiplication) - это последова
тельность вычисления скалярных произведений.
Мера неоднородности, или мера Джини (Gini impшity) - это вероятность, что с
случайный образец будет классифицирован правильно, если произвольно выбрать
метку согласно распределению в ветви дерева. Не следует путать с коэффициентом
Джини в статистике, где он представляет собой показатель степени расслоения об
щества ло отношению к какому-либо признаку.
Метка класса (class label) - выходная (независимая) переменная классифика
ционной модели. Метка класса всегда является дискретной и принимает значения
из некоторого ограниченного набора категорий - наименований классов. Напри
мер, если в задаче классификации требуется определить степень риска, связанного
с выдачей кредита клиенту, то для метки класса могут быть определены 3 значения:
«Высокий», «Средний» и «Низкий».
Метод эластичной сети (elastic net) - это метод регуляризованной регрессии,
компенсирующий недостатки метода LASSO добавлением L1 - и L2-штрафа ..
Метрические данные (metгic data) - измеряются по интервальной или относи
тельной шкале.
Многомерное шкалирование (scaling) - метод анализа и визуализации данных
с помощью расположения точек, соответствующих изучаемым (шкалируемым) объ
ектам, в пространстве меньшей размерности, чем пространство признаков объектов.
Точки размещаются так, чтобы попарные расстояния между ними в новом простран
стве как можно меньше отличались от эмпирически измеренных расстояний в про
странстве признаков изучаемых объектов.
Многослойный перцептрон (multilayered peгceptron, MLP) - сеть с одним вход
ным, одним выходным и одним или более скрытыми слоями нейронов.
Мода (mode) - значение во множестве наблюдений, которое встречается наибо
лее часто, т. е. значение признака, имеющее наибольшую частоту в статистическом
ряду распределения .
ление о том, как модель будет обобщаться на независимый набор данных (т. е. ранее
не встречавшихся данных, например из реальной задачи).
Перекрестная проверка с исключением по одному (leave-one-out CV) - это прос
тая перекрестная проверка, где каждый обучающий набор создается с использова
нием всех образцов, за исключением одного, и этот отложенный образец становится
тестовым набором. Так, для п образцов будет иметься п разных тренировочных и п
разных тестовых наборов. Эта процедура перекрестной проверки не тратит слишком
много данных, т. к. из тренировочного набора удаляется всего один образец.
Переобучение (oveгfitting) - чрезмерная аппроксимация целевой функции (чрез
мерно близкая подгонка), которая приводит к тому, что модель вычленяет шум.
Персеnтрон (регсерtгоn) - простейшая форма нейронной сети, под которой в ос
новном понимается элементарный нейрон, представляющий собой линейный сум
матор, каждый из входных сигналов которого умножается на некоторый весовой
множитель, а выходной суммарный сигнал является ненулевым, если сумма превы
шает некоторое пороговое значение.
компьютера.
щего только одну 1, т. е. где i-e значение признака равно 1, а все остальные равны О.
При обратном кодировании имеется только один О. Длина кода определяется коли
чеством кодируемых значений признака, или объектов.
Сеточный поиск (grid search), или поиск по сетке параметров - это просто исчер
пывающий поиск в заданном вручную подмножестве гиперпараметрического про
странства.
Слой (layer) - множество нейронов (узлов), имеющих общие входные или вы
ходные сигналы.
гипотеза.
нировки модели.
Сокращения
API - интерфейс программирования приложений, программный интерфейс.
BLAS - динамическая библиотека базовых подпрограмм линейной алгебры.
Breast Cancer Wisconsin - экспериментальный набор данных о раке груди по
шт. Висконсин, США.
С - язык программирования.
С++ - язык программирования.
CSV - файл значений, разделенных запятыми.
Facebook - социальная сеть.
Flask - библиотека Python, реализующая легковесную веб-платформу.
Fortran - язык программирования.
GLM (Gener·alized Linear Model) - обобщенная линейная модель (ОЛМ).
Housing - экспериментальный набор данных жилищного фонда с информацией
о зданиях в пригородах Бостона, США.
Iris - экспериментальный набор данных цветков ириса (ирисы Фишера).
JavaScript - язык программирования.
JSON QavaScript Object Notation) - объектная форма записи JavaScript, тексто
вый формат обмена данными.
Jupyter - интерактивная вычислительная среда.
Keras - высокоуровневая библиотека Python для работы с глубокими нейронны
ми сетями.
не встречавшихся экземпляров
г данных, 36
Гауссово ядро, 154 тренировка прогнозных
Гиперпараметры моделей, 35
описание,171,319
и Классификатор кинофильмов
Иерархическая и плотностная
обновление,258
превращение в веб- приложение, 249
кластеризация,289
Классификатор на основе ll ближайших
Иерархическая кластеризация
соседей (KNN), 103
выполнение на матрице
Классификатор на основе деревьев
расстояний, заз
решений, 93
описание, 302
Классификация
Импутация простым средним, 110
альтернативные классификации
Инерция кластера, 291
в scikit- leaш, 88
Интернет-база кинофильмов
интуитивное понимание
(IMDb), 222
с максимальным зазором, 85
Ирис виргинский (lris virginica), 69, 202 обработка нелинейно разделимых
Ирис разноцветный (Iris
случаев, 86, 87
versico loг) , 69, 202
Классификация документов, тренировка
Ирис щетинистый (lгis setosa), 69 логистической регрессионной модели
Искусственная нейронная сеть для нее, 232
вычисление логистической функции Кластеризация на основе
СТОИМОСТИ, 339 прототипов, 289
тренировка, 339 Кластеры, организация в виде
тренировка нейронных сетей иерархического дерева, 302
методом обратного распространения Конвейеры
ошибки, 341 оптимизация потока операций
получение,323 DBSCAN,309
реализация многослойного Области решений, 72
персептрона,328 Обработки естественного языка
Набор данных киноотзывов IMDb (NLP), 222
URL-aдpec, 223 Обратная частота документа, 226
получение, 222 Обратное распространение
Набор данных сортов вин Wine описание, 341
URL-aдpec, 116 развитие интуитивного
описание, 30 п
снижение размерности для сжатия
Параметр глубины, 181
данных, 31
Параметрические модели, 103
Обучение вне ядра, определение, 234 Параметр регуляризации, 82, 181
Обучение на основе деревьев решений
Персептрон , 69
максимизация прироста
Перекрестная проверка Sx2, 183
информации, 94
Перекрестная проверка с отложенными
объединение слабых учеников
данными, 170
для создания сильного при помощи
Переобучение,71,81, 119
случайных лесов, 100
Персептронная модель, тренировка
описание, 93
на наборе данных Iris, 50
построение дерева решений, 98
Персистентность модели, 239
Обучение на примерах, 103
Площадь под RОС-кривой
Обучение с подкреплением
(AUC), 188, 202
описание, 29
Плюралистическое голосование, 193
решение интерактивных задач, 29
Подвыборка, 353
Обучение с учителем
Подогнанные оценщики библиотеки
выполнение предсказаний, 26
задача классификации
scikit-learn, сериализация, 239
Поиск по сетке параметров.
для распознавания меток классов, 27
См. Сеточный поиск
описание, 26
Полиномиальная регрессия, 278
Обычный метод наименьших квадратов
Полиномиальное ядро, 154
(МНК), 267
Объединяющий слой, 353 Полная связь,
302
Полнота (REC), 187
Объекты
группирование по подобию Пороговая функция, 318
с использованием алгоритма
Портал разработчиков Google
k средних, 289 Developers, URL-aдpec, 229
Одиночная связь, 302 Порядковые признаки, 112
функция
softmax, 370 э
Функция logit, 74 Эластичная сеть, 277
Функция softmax, 370 Энтропия, 95
Функция гиперболического тангенса Эпоха, 317
(tanl1), 371
Функция хэширования MurmurHashЗ,
URL-aдpec , 236 Ядерные функции, 152
Ядерный метод SVM, 88
х
Ядерный метод РСА
Хранение данных, настройка базы использование для нелинейных
данных СУБД sqlite, 242 отображений, 151
применение в библиотеке
ч
scikit-leaш, 165
Частота термина, 226 реализация на Python, 156
Частота термина - обратная частота Ядерный трюк, 152
документа, 226 Ядро
полиноминальноеядро,154
ш ядро на основе гиперболического
Ширина лепестка, 69 тангенса (сиrмоида), 154
Ширина чашелистика, 202 ядро на основе функции радиального
Шумовые точки, 309 базиса (RBF), 154
Книги издательства «дМК Пресс>,> можно заказать
в торгово-издательском холдинге «Планета Альянс >.> наложенным платежом,
выслав открытку или письмо по почтовому адресу:
Себастьян Рашка
анализа ;
Если вы хотите узнать , как использовать Python, чтобы начать отвечать на крити
ческие вопросы в отношении ваших данных, возьмите книгу "Python и машинное
обучение " - и неважно, приступаете ли вы к изучению науки о данных с нуля или
просто намереваетесь расширить по ней свои знания .