Главная страница

однострочники пайтон. Однострочники Python лаконичный и содержательный код by Кристи. Однострочники


Скачать 4.44 Mb.
НазваниеОднострочники
Анкороднострочники пайтон
Дата22.04.2022
Размер4.44 Mb.
Формат файлаpdf
Имя файлаОднострочники Python лаконичный и содержательный код by Кристи.pdf
ТипКнига
#490720
страница15 из 21
1   ...   11   12   13   14   15   16   17   18   ...   21
Получение строки с минимальной дисперсией
в одной строке кода
Возможно, вы читали о пяти V больших данных: объем (
volume), скорость
(
velocity), разнообразие (variety), достоверность (veracity) и ценность
(
value)
1
. Дисперсия (
variance) — еще одно важное V: это мера ожидаемого
(квадратичного) отклонения данных от среднего значения. На практике дисперсия — важный показатель, нашедший свои приложения в финансах, прогнозе погоды и обработке изображений.
1
Иногда выделяют не пять, а три V, а порой даже семь: помимо перечисленных, еще и изменчивость (variability) и визуализацию (visualization).

162
Глава 4. Машинное обучение
Общее описание
Дисперсия — это мера того, насколько данные разбросаны вокруг их средне- го значения в одномерном или многомерном пространстве. Чуть ниже вы увидите наглядный пример. Фактически дисперсия — один из важнейших показателей в машинном обучении. Она захватывает обобщенные законо- мерности в данных, а машинное обучение прежде всего ориентировано на распознавание закономерностей.
В основе многих алгоритмов машинного обучения лежит дисперсия в той или иной форме. Например, подбор правильного соотношения систематиче-
ской ошибки и дисперсии — хорошо известная задача в машинном обучении: хитроумные модели машинного обучения подвержены риску переобуче- ния (высокая дисперсия), но очень точно отражают обучающие данные
(маленькая систематическая ошибка). С другой стороны, простые модели часто хорошо обобщаются (низкая дисперсия), но плохо отражают данные
(большая систематическая ошибка).
Что же такое дисперсия? Это простой статистический показатель, отража- ющий степень разбросанности данных относительно их среднего значения.
На рис. 4.22 приведен пример в виде графиков двух наборов данных: один с низкой дисперсией, а второй — с высокой.
Рис. 4.22. Сравнение дисперсии курсов акций двух компаний
В этом примере показаны курсы акций двух компаний. Курс акций техно- логического стартапа сильно отклоняется от среднего значения. Курс акций продовольственной компании довольно стабилен и лишь незначительно

Получение строки с минимальной дисперсией в одной строке кода
163
отклоняется от среднего значения. Другими словами, у технологического стартапа высокая дисперсия, а у продовольственной компании — низкая.
На математическом языке вычислить дисперсию var (X) множества число- вых значений X можно с помощью следующей формулы:
Величина представляет собой среднее значение данных во множестве X.
Код
По мере старения многие инвесторы стремятся сократить общий риск своего инвестиционного портфеля. Согласно доминирующей философии инвестиций, следует стремиться к акциям с более низкой дисперсией как менее рискованным инвестиционным активам. Проще говоря, риск поте- рять деньги будет ниже при инвестициях в стабильную, предсказуемую, крупную компанию, чем при вложениях в маленький технологический стартап.
Задача однострочника из листинга 4.7 — найти в портфеле акции с мини- мальной дисперсией. При инвестициях в эти акции можно ожидать более низкую общую дисперсию портфеля.
Листинг 4.7. Вычисление минимальной дисперсии в одной строке кода
## Зависимости import numpy as np
## Данные (строки: акции / столбцы: курсы акций)
X = np.array([[25,27,29,30],
[1,5,3,2],
[12,11,8,3],
[1,1,2,2],
[2,6,2,2]])
## Однострочник
## Поиск акций с наименьшей дисперсией min_row = min([(i,np.var(X[i,:])) for i in range(len(X))], key=lambda x: x[1])
## Результат print("Row with minimum variance: " + str(min_row[0]))
print("Variance: " + str(min_row[1]))
Какими же будут результаты выполнения этого фрагмента кода?

164
Глава 4. Машинное обучение
Принцип работы
Как обычно, мы сначала описываем данные, на которых будет работать наш однострочник (см. верхнюю часть листинга 4.7). Массив NumPy
X
содержит пять строк (по одной для каждого вида акций в портфеле), каждая из которых включает четыре значения (курсы акций).
Задача — найти идентификатор и дисперсию акций с минимальной дис- персией. Поэтому внешняя функция нашего однострочника — min()
. Мы выполняем ее для последовательности кортежей
(a,b)
, в которых первое значение кортежа a
представляет собой индекс строки (индекс вида акций), а второе значение кортежа b
— дисперсию строки.
Возможно, вы спросите: а что такое минимальное значение последовательности кортежей? Конечно, необходимо четко определить эту операцию перед ис- пользованием. Для этого мы обратимся к аргументу key функции min()
. В него должна передаваться функция, получающая последовательность и возвращаю- щая допускающее сравнение объектное значение. Опять же, значения в нашей последовательности — кортежи, и нам нужно найти кортеж с минимальной дисперсией (вторым значением кортежа). А поскольку дисперсия — второе значение в кортеже, мы возвращаем для сравнения x[1]
. Другими словами,
«победителем» становится кортеж с минимальным вторым значением.
Посмотрим, как создать эту последовательность значений-кортежей. Чтобы создать кортеж для индекса строки (вида акций), мы прибегнем к списково- му включению. Первый элемент кортежа — просто индекс строки i. Второй элемент кортежа — дисперсия этой строки. Для вычисления дисперсии стро- ки мы используем функцию var()
библиотеки NumPy в сочетании со срезом.
Результат выполнения нашего однострочника выглядит так:
"""
Row with minimum variance: 3
Variance: 0.25
"""
Хотелось бы добавить, что существует и альтернативный способ решения этой задачи. Если бы наша книга не была посвящена однострочникам Python, возможно, я предпочел бы следующее решение вместо упомянутого одно- строчника:
var = np.var(X, axis=1)
min_row = (np.where(var==min(var)), min(var))

Основные статистические показатели с помощью одной строки кода
165
В первой строке вычисляется дисперсия массива NumPy
X
по столбцам
(
axis=1
). Во второй создается кортеж. Первое значение кортежа представ- ляет собой индекс минимума в массиве дисперсий. А второе — сам этот минимум в массиве дисперсий. Обратите внимание, что одна и та же (ми- нимальная) дисперсия может быть у нескольких строк.
Такое решение удобнее для восприятия. Поэтому явно необходимо выбирать между лаконичностью и удобочитаемостью кода. Возможность втиснуть что-либо в одну строку кода не означает, что так следует поступать всегда.
При прочих равных условиях лучше писать лаконичный и удобочитаемый код, а не раздувать его ненужными определениями, комментариями или промежуточными шагами.
Теперь, изучив в этом разделе основы понятия дисперсии, вы готовы узнать, как вычислять основные статистические показатели.
Основные статистические показатели
с помощью одной строки кода
Исследователю данных, как и специалисту по машинному обучению, необ- ходимо знать основные статистические показатели. Некоторые алгоритмы машинного обучения базируются исключительно на статистических пока- зателях (например, байесовские сети).
Например, вычисление основных статистических показателей матриц (сред- него значения, дисперсии или стандартного отклонения) — неотъемлемая составляющая анализа разнообразных наборов данных, в том числе финан- совых, медицинских и данных соцсетей. По мере роста популярности ма- шинного обучения и науки о данных умение применять библиотеку NumPy, используемую в Python для исследования данных, статистики и линейной алгебры, становится все более востребованным на рынке.
Из этого однострочника вы увидите, как вычислять основные статистические показатели с помощью NumPy.
Общее описание
В этом разделе рассказывается, как вычислить среднее значение, стандартное отклонение и дисперсию по одной из осей координат. Вычисления этих трех

166
Глава 4. Машинное обучение величин очень похожи; если вы разберетесь с одной, то и остальные будут для вас понятны.
Вот что мы хотим сделать: по заданному массиву NumPy данных об акциях, в котором строки соответствуют различным компаниям, а столбцы — курсам их акций по дням, найти среднее значение и стандартное отклонение курса акций каждой из компаний (рис. 4.23).
Рис. 4.23. Среднее значение и дисперсия по оси 1
В примере показан двумерный массив NumPy, но на практике массивы бы- вают намного большей размерности.
Среднее арифметическое, дисперсия, стандартное отклонение
Прежде чем выяснить, как вычислить все это в NumPy, понемногу разберем- ся со всем, что нужно для этого знать. Пусть нам нужно вычислить просто среднее значение, дисперсию или стандартное отклонение по всем значениям массива NumPy. Вы уже видели в главе примеры вычисления среднего значе- ния и дисперсии. Стандартное отклонение равно просто квадратному корню дисперсии. Вычислить их все легко можно с помощью следующих функций:
import numpy as np
X = np.array([[1, 3, 5],
[1, 1, 1],
[0, 2, 4]])
print(np.average(X))
# 2.0
print(np.var(X))
# 2.4444444444444446
print(np.std(X))
# 1.5634719199411433

Основные статистические показатели с помощью одной строки кода
167
Наверное, вы обратили внимание, что функции применяются тут к дву- мерному массиву NumPy
X
. Но NumPy просто «сплющивает» этот массив и вычисляет функции на основе полученного результата. Например, среднее арифметическое «сплющенного» массива NumPy
X
вычисляется следующим образом:
(1 + 3 + 5 + 1 + 1 + 1 + 0 + 2 + 4) / 9 = 18 / 9 = 2.0.
Вычисление среднего значения, дисперсии, стандартного отклонения
по одной из осей
Иногда, впрочем, бывает нужно вычислить эти функции по одной из осей.
Для этого можно указать ключевое слово axis в качестве аргумента функций вычисления среднего значения, дисперсии или стандартного отклонения
(см. подробное описание аргумента axis в главе 3).
Код
В листинге 4.8 показано, как именно вычислить среднее значение, диспер- сию и стандартное отклонение по одной из осей. Наша задача: вычислить среднее значение, дисперсию и стандартное отклонение всех типов акций в двумерной матрице, строки которой соответствуют типам акций, а столб- цы — курсам по дням.
Листинг 4.8. Вычисление простейших статистических показателей по одной из осей
## Зависимости import numpy as np
## Данные о курсах акций: 5 компаний
## (строка=[курс_день_1, курс_день_2, ...])
x = np.array([[8, 9, 11, 12],
[1, 2, 2, 1],
[2, 8, 9, 9],
[9, 6, 6, 3],
[3, 3, 3, 3]])
## Однострочник avg, var, std = np.average(x, axis=1), np.var(x, axis=1), np.std(x, axis=1)
## Результат print("Averages: " + str(avg))
print("Variances: " + str(var))
print("Standard Deviations: " + str(std))
Угадайте, какой результат вернет этот код!

168
Глава 4. Машинное обучение
Принцип работы
В нашем однострочнике с помощью ключевого слова axis задается ось коор- динат, по которой вычисляется среднее значение, дисперсия и стандартное отклонение. Например, если выполнять эти три функции по оси axis=1
, то каждая из строк будет агрегирована в одно значение. А значит, размерность полученного в итоге массива NumPy уменьшится до единицы.
Приведенный выше код возвращает следующее:
"""
Averages: [10. 1.5 7. 6. 3. ]
Variances: [2.5 0.25 8.5 4.5 0. ]
Standard Deviations: [1.58113883 0.5 2.91547595 2.12132034 0. ]
"""
Прежде чем перейти к следующему однострочнику, я хотел бы продемон- стрировать применение той же идеи для массивов NumPy еще большей размерности.
При агрегировании по одной из осей многомерного массива NumPy всегда агрегируется ось, указанная в аргументе axis
. Вот пример:
import numpy as np x = np.array([[[1,2], [1,1]],
[[1,1], [2,1]],
[[1,0], [0,0]]])
print(np.average(x, axis=2))
print(np.var(x, axis=2))
print(np.std(x, axis=2))
"""
[[1.5 1. ]
[1. 1.5]
[0.5 0. ]]
[[0.25 0. ]
[0. 0.25]
[0.25 0. ]]
[[0.5 0. ]
[0. 0.5]
[0.5 0. ]]
"""
Здесь приведены три примера вычисления среднего значения, дисперсии и стандартного отклонения по оси 2 (самая внутренняя ось координат;

Классификация путем метода опорных векторов с помощью одной строки кода
169
см. главу 3). Другими словами, все значения оси 2 будут схлопнуты в одно, в результате чего ось 2 пропадет из итогового массива. Взгляните на эти три примера внимательнее и разберитесь, как именно ось 2 схлопывается в одно среднее значение, дисперсию или стандартное отклонение.
Резюмируя: умение извлекать хотя бы простейшую полезную информацию необходимо для широкого спектра наборов данных (в том числе финансовых, медицинских и данных соцсетей). Из этого раздела вы узнали больше о том, как быстро и эффективно вычислять основные статистические показатели для многомерных массивов, необходимый шаг предварительной обработки для многих алгоритмов машинного обучения.
Классификация путем метода опорных
векторов с помощью одной строки кода
Популярность метода опорных векторов (support-vector machines, SVM) сильно выросла за последние годы благодаря его высокой ошибкоустойчиво- сти при классификации, в том числе в пространствах высокой размерности.
Как ни удивительно, SVM работает, даже если количество измерений (при- знаков) превышает количество элементов данных, что необычно для алгорит- мов классификации из-за так называемого проклятия размерности: при росте размерности данные становятся очень разреженными, а это усложняет поиск закономерностей в наборе данных. Понимание основных идей SVM — необ- ходимый шаг становления опытного специалиста по машинному обучению.
Общее описание
Как функционируют алгоритмы классификации? На основе обучающих данных они ищут границу решений, отделяющую данные из одного класса от данных из другого (в разделе «Логистическая регрессия в одной строке» на с. 130 границей решений может служить пороговое значение 0.5 вероят- ности сигма-функции).
Общая картина классификации
На рис. 4.24 приведен пример классификатора.
Пусть нам нужно создать рекомендательную систему для студентов началь- ных курсов университета. На рис. 4.24 визуализированы обучающие данные,

170
Глава 4. Машинное обучение
Рис. 4.24. Различия наборов способностей специалистов по computer science и представителей искусства состоящие из пользователей, классифицированных по их способностям в двух сферах: логика и творчество. Одни студенты отличаются сильными логическими способностями и относительно низким уровнем творческих; другие — выраженными творческими способностями и относительно низким уровнем логических. Первую группу мы обозначили как специалисты по
computer science, а вторую — представители искусства.
Для классификации новых пользователей модель машинного обучения должна отыскать границу решений, разделяющую специалистов по computer science и представителей искусства. В общих чертах будем классифицировать пользователей в зависимости от того, по какую сторону границы решений они попадают. В нашем примере мы классифицируем пользователей слева от границы решений как специалистов по computer science, а справа — как представителей искусства.
В двумерном пространстве роль границы решений может играть либо пря- мая, либо кривая (более высокого порядка). В первом случае классификатор называется линейным (linear classifier), а во втором — нелинейным (nonlinear classifier). В этом разделе мы будем рассматривать только линейные клас- сификаторы.
На рис. 4.24 (см. выше) показаны три границы решений, вполне приемлемо разделяющие данные. В нашем примере невозможно определить, какая из этих границ решений лучше; они все обеспечивают абсолютную безошибоч- ность при классификации обучающих данных.

Классификация путем метода опорных векторов с помощью одной строки кода
171
Но какая же граница решений лучшая?
Метод опорных векторов дает уникальный и очень красивый ответ на этот вопрос. Вполне логично, что лучшая граница решений — та, которая обеспе- чивает максимальный «запас прочности». Другими словами, метод опорных векторов максимизирует расстояние между границей решений и ближайши- ми точками данных. Цель состоит в минимизации погрешности для новых точек, близких к границе решений.
Пример этого можно увидеть на рис. 4.25.
Рис. 4.25. Метод опорных векторов максимизирует допустимую погрешность
Классификатор SVM находит такие опорные векторы, чтобы область между ними была максимально широка. В данном случае роль опорных векторов играют точки данных, лежащие на двух пунктирных линиях, параллельных границе решений. Эти прямые называются отступами (margins). Граница решений — прямая посередине, расстояние от которой до отступов макси- мально. Вследствие максимизации области между отступами и границей решений можно ожидать, что допустимая погрешность также будет макси- мальной при классификации новых точек данных.
Код
Можно ли создать собственный алгоритм SVM с помощью одной строки кода на Python? Взгляните на листинг 4.9.

172
Глава 4. Машинное обучение
Листинг 4.9. Классификация с помощью SVM в одной строке кода
## Зависимости from sklearn import svm import numpy as np
## Данные: оценки студентов по (математика, языки, творческие
## способности) --> предмет для изучения
X = np.array([[9, 5, 6, "computer science"],
[10, 1, 2, "computer science"],
[1, 8, 1, "literature"],
[4, 9, 3, "literature"],
[0, 1, 10, "art"],
[5, 7, 9, "art"]])
## Однострочник svm = svm.SVC().fit(X[:,:-1], X[:,-1])
## Результат student_0 = svm.predict([[3, 3, 6]])
print(student_0)
student_1 = svm.predict([[8, 1, 1]])
print(student_1)
Угадайте, что вернет этот код.
Принцип работы
Из кода понятно, как применять (в простейшем варианте) метод опорных векторов на Python. В массиве NumPy содержатся маркированные обучаю- щие данные, по одной строке на пользователя и одному столбцу на признак
(способности студентов к математике, языкам и творческие способности).
Последний столбец — метка (класс).
Поскольку данные у нас — трехмерные, метод опорных векторов разделяет их с помощью двумерных плоскостей (линейный разделитель), а не одномерных прямых. Как вы, наверное, видите, можно также разделять три класса, а не два, как в предыдущих примерах.
Сам однострочник очень прост: сначала мы создаем модель с помощью конструктора класса svm.SVC
(SVC расшифровывается как support-vector classification — классификация с помощью опорных векторов). Далее мы вызываем функцию fit()
, производящую обучение на основе наших мар- кированных обучающих данных.
В части «Результат» фрагмента кода мы вызываем функцию predict()
, передавая ей новые наблюдения. Поскольку для student_0
указано

Классификация с помощью случайных лесов в одной строке кода
173
математика = 3, языки = 3 и творческие способности = 6, то метод опорных векторов предсказывает, что способностям студента соответствует метка art
. Аналогично, для student_1
с математика = 8, языки = 1 и творческие способности = 1 метод опорных векторов предсказывает, что способностям студента соответствует метка computer science
Вот как выглядят итоговые результаты нашего однострочника:
## Результат student_0 = svm.predict([[3, 3, 6]])
print(student_0)
# ['art']
student_1 = svm.predict([[8, 1, 1]])
print(student_1)
## ['computer science']
Резюмируя: SVM демонстрирует хорошие результаты даже в многомерных пространствах при количестве признаков, превышающем количество обу- чающих векторов данных. Идея максимизации «запаса прочности» вполне интуитивна и демонстрирует хорошие результаты даже при классификации
граничных случаев (boundary cases) — векторов, попадающих в рамки этого
«запаса прочности». В последней части данной главы мы рассмотрим более общую картину — метаалгоритм классификации: обучение ансамблей с по- мощью случайных лесов.
Классификация с помощью случайных лесов
в одной строке кода
Посмотрим теперь на замечательную методику машинного обучения: обу-
чение ансамблей (ensemble learning). Если степень безошибочности пред- сказаний вашей модели недостаточно высока, а срок сдачи проекта уже на носу — мой совет: попробуйте этот подход метаобучения, сочетающий пред- сказания (классификации) нескольких алгоритмов машинного обучения. Во многих случаях с его помощью вы сможете добиться в последнюю минуту лучших результатов.
Общее описание
В предыдущих разделах мы изучили несколько алгоритмов машинного обу- чения, с помощью которых можно быстро получить неплохие результаты.

174
Глава 4. Машинное обучение
Однако у разных алгоритмов — разные сильные стороны. Например, осно- ванные на нейронных сетях классификаторы способны давать великолепные результаты для сложных задач, однако подвержены риску переобучения именно вследствие своих потрясающих способностей к усвоению тонких закономерностей данных. Обучение ансамблей для задач классификации частично решает проблему, связанную с тем, что заранее неизвестно, какой алгоритм машинного обучения сработает лучше всего.
Как работает этот подход? Создается метаклассификатор, состоящий из не- скольких типов или экземпляров простых алгоритмов машинного обучения.
Другими словами, обучается несколько моделей. В целях классификации конкретного наблюдения входные данные передаются по отдельности всем моделям. А роль метапредсказания играет класс, который эти модели воз- вращали чаще всего при этих входных данных. Он и становится итоговым результатом алгоритма обучения ансамблей.
Случайные леса (random forests) — особая разновидность алгоритмов обу- чения ансамблей, использующая обучение на основе деревьев принятия решений. Лес состоит из множества деревьев. Аналогично, случайный лес состоит из множества деревьев принятия решений. Отдельные деревья принятия решений получаются путем внесения стохастичности в процесс генерации деревьев на этапе обучения (например, выбор в качестве первой различных вершин дерева). В результате получаются различные деревья принятия решений — как раз то, что нужно.
На рис. 4.26 показан процесс предсказания для обученного случайного леса при следующем сценарии. У Алисы выраженные способности к математике и языкам. Ансамбль состоит из трех деревьев принятия решений (составля- ющих случайный лес). Чтобы классифицировать Алису, мы просим все эти деревья ее классифицировать. Два из трех деревьев классифицируют Алису как специалиста по computer science. Этот класс как получивший максимум «го- лосов» и возвращается в качестве окончательного результата классификации.
Код
Продолжим работать с этим примером классификации изучаемых предме- тов на основе демонстрируемых студентами способностей в трех областях: математика, языки и творчество. Возможно, вам кажется, что реализовать метод обучения ансамблей на Python непросто. Но благодаря многогран- ности библиотеки scikit-learn это не так (листинг 4.10).

Классификация с помощью случайных лесов в одной строке кода
175
Лингвистик а
Ист ория
Ист ория
Иск усств о
Лингвистик а
Рис. 4.26.
Классификатор на основе случайного леса агрегирует результаты трех деревьев принятия решений

176
Глава 4. Машинное обучение
Листинг 4.10. Обучение ансамблей с помощью классификаторов на основе случайных лесов
## Зависимости import numpy as np from sklearn.ensemble import RandomForestClassifier
## Данные: оценки студентов по (математика, языки, творческие
## способности) --> предмет для изучения
X = np.array([[9, 5, 6, "computer science"],
[5, 1, 5, "computer science"],
[8, 8, 8, "computer science"],
[1, 10, 7, "literature"],
[1, 8, 1, "literature"],
[5, 7, 9, "art"],
[1, 1, 6, "art"]])
## Однострочник
Forest = RandomForestClassifier(n_estimators=10).fit(X[:,:-1], X[:,-1])
## Результат students = Forest.predict([[8, 6, 5],
[3, 7, 9],
[2, 2, 1]])
print(students)
Попробуйте догадаться, каковы будут результаты выполнения этого фраг- мента кода.
Принцип работы
Инициализировав массив маркированных обучающих данных в ли- стинге 4.10, код создает случайный лес с помощью конструктора класса
RandomForestClassifier с одним параметром — n_estimators
, — задающим количество деревьев в лесу. Далее мы вызываем функцию fit()
, заполняя данными полученную при инициализации модель (пустой лес). Исполь- зуемые для этого входные обучающие данные состоят из всех столбцов массива
X
, кроме последнего, а метки обучающих данных задаются в этом последнем столбце. Как и в предыдущих примерах, соответствующие столб- цы из массива данных
X
мы выделяем с помощью срезов.
Относящаяся к классификации часть этого фрагмента кода несколько отли- чается. Я хотел показать вам, как классифицировать много наблюдений, а не только одно. Это можно сделать тут путем создания многомерного массива, в котором каждому наблюдению соответствует одна строка.

Классификация с помощью случайных лесов в одной строке кода
177
Вот результаты работы нашего фрагмента кода:
## Результат students = Forest.predict([[8, 6, 5],
[3, 7, 9],
[2, 2, 1]])
print(students)
# ['computer science' 'art' 'art']
Обратите внимание, что результаты по-прежнему недетерминистичны (мо- гут отличаться при различных запусках этого кода), поскольку в алгоритме случайных лесов используется генератор случайных чисел, возвращающий различные числа в различные моменты времени. Детерминизировать этот вызов можно с помощью целочисленного аргумента random_state
. Напри- мер, можно задать параметр random_state=1
при вызове конструктора слу- чайного леса:
RandomForestClassifier(n_estimators=10,
random_state=1)
В этом случае при каждом создании классификатора на основе случайных лесов будут возвращаться одни и те же результаты, поскольку будут гене- рироваться одни и те же случайные числа: в основе их всех лежит начальное значение генератора 1.
Резюмируя: в этом разделе представлен метаподход к классификации: снижение дисперсии погрешности классификации за счет использования результатов работы нескольких различных деревьев решений — одна из версий обучения ансамблей, при котором несколько базовых моделей объ- единяется в одну метамодель, способную задействовать все сильные стороны каждой из них.
ПРИМЕЧАНИЕ
Использование двух различных деревьев принятия решений может при- вести к высокой дисперсии погрешности, если одно возвращает хорошие результаты, а второе
нет. Последствия этого эффекта можно уменьшить с помощью случайных лесов.
Различные вариации этой идеи очень распространены в машинном обучении.
Чтобы быстро повысить степень безошибочности модели, просто запустите несколько моделей машинного обучения и найдите среди их результатов лучшие (маленький секрет специалистов по машинному обучению). Мето- дики обучения ансамблей в некотором смысле автоматически выполняют задачи, часто возлагаемые на экспертов по конвейерам машинного обучения:

178
Глава 4. Машинное обучение выбор, сравнение и объединение результатов различных моделей машинного обучения. Основное преимущество обучения ансамблей — возможность выполнения его по отдельности для каждого значения данных во время выполнения.
Итоги главы
В этой главе мы рассмотрели десять простых алгоритмов машинного обу- чения, необходимых для успешной работы в данной сфере. Вы посмотрели на предсказание значений с помощью алгоритмов регрессии, в частности, линейной, KNN и нейронных сетей. Вы также узнали об алгоритмах клас- сификации: логистической регрессии, обучении с помощью деревьев при- нятия решений, SVM и случайных лесах. Более того, научились вычислять основные статистические показатели многомерных данных и использовать алгоритм k-средних для обучения без учителя. Эти алгоритмы и методы вхо- дят в число важнейших в сфере машинного обучения, и вам придется изучить еще очень много всего, чтобы стать специалистом по машинному обучению.
Подобные усилия окупятся с лихвой — такие специалисты зарабатывают в США шестизначные суммы (в этом можно легко убедиться с помощью простого поиска в Google). Студентам, желающим узнать больше о машин- ном обучении, я рекомендую замечательный бесплатный курс Coursera от Эндрю Энга. Вы легко можете найти материал этого курса в интернете.
В следующей главе вы научитесь работать с одним из важнейших (и самых недооцененных) инструментов эффективных программистов: регулярными выражениями. И если в этой главе материал излагался на довольно схе- матичном уровне (вы изучили основные идеи, а все сложные вычисления взяла на себя библиотека scikit-learn), то далее вас ждет немало технических подробностей. Так что засучите рукава и приступайте!

5
Регулярные выражения
Вы офисный работник, студент, разработчик про- граммного обеспечения, менеджер, блогер, исследова- тель, автор, составитель рекламных текстов, учитель или самозанятый фрилансер? Скорее всего, вы каждый день проводите немало времени за своим компьютером.
Возможность увеличить свою ежедневную выработку — даже всего на доли процента — позволит заработать (суммарно за все годы) лишние тысячи, а то и десятки тысяч долларов и освободить сотни часов времени.
В этой главе вы познакомитесь с часто недостаточно ценимой методикой, повышающей эффективность программистов при работе с текстовыми данными: регулярными выражениями. Глава продемонстрирует вам десять способов решения повседневных задач с помощью регулярных выражений, позволяющих экономить усилия, время и энергию. Очень внимательно изу- чите главу — ее материал оправдает ваши ожидания!
Поиск простых комбинаций символов
в строковых значениях
В этом разделе вам предстоит познакомиться с регулярными выражениями, используя модуль re и одну из важных его функций re.findall()
. Начнем с рассказа о нескольких простейших регулярных выражениях.

180
Глава 5. Регулярные выражения
Общее описание
Регулярное выражение (regular expression, или, сокращенно, regex) фор- мально описывает поисковый шаблон
1
(search pattern), на основе кото- рого можно находить соответствующие части текста. Простой пример на рис. 5.1 демонстрирует поиск слова
Juliet в тексте пьесы Шекспира
«Ромео и Джульетта».
Рис. 5.1. Поиск слова Juliet в тексте пьесы Шекспира
«
Ромео и Джульетта
»
1
В русскоязычной литературе часто встречается также термин «паттерн».

Поиск простых комбинаций символов в строковых значениях
181
Как показывает рис. 5.1, простейшее регулярное выражение — обычная символьная строка. Символьная строка 'Juliet'
— вполне допустимое регулярное выражение.
Возможности регулярных выражений очень широки, они подходят отнюдь не только для простого текстового поиска; в их основе лежит всего несколько основных команд. Изучите эти основные команды, и вы сможете разобраться в самых сложных регулярных выражениях и писать их самостоятельно. Мы сосредоточим свое внимание на трех важнейших командах регулярных выра- жений, значительно расширяющих возможности простого поиска шаблонов символов в заданном тексте.
Регулярное выражение «точка»
Во-первых, нужно разобраться, как найти произвольный символ с помощью
регулярного выражения «точка», то есть символа
. Регулярное выражение
«точка» соответствует любому символу (включая пробельные). С его по- мощью можно указать, что неважно, какой именно символ найден, лишь бы был найден ровно один:
import re text = '''A blockchain, originally block chain,
is a growing list of records, called blocks,
which are linked using cryptography.
'''
print(re.findall('b...k', text))
# ['block', 'block', 'block']
В этом примере используется метод findall()
модуля re
. Первый его аргу- мент — собственно, само регулярное выражение: мы ищем произвольную комбинацию символов, начинающуюся с символа 'b'
, за которым следуют три произвольных символа,
, за которыми следует символ 'k'
. Регулярному вы- ражению b...k соответствует не только строка символов 'block'
, но и 'boook'
,
'b erk'
и 'bloek'
. Второй параметр метода findall()
— текст, в котором произ- водится поиск. Строковая переменная text содержит три подходящих шаблона символов, как видно из выведенных оператором print результатов.
Регулярное выражение «звездочка»
Во-вторых, пусть требуется найти текст, который будет начинаться и закан- чиваться символом 'y'
, с произвольным количеством символов посередине.

182
Глава 5. Регулярные выражения
Как это сделать? С помощью регулярного выражения «звездочка», то есть символа
*
. В отличие от регулярного выражения «точка», регулярное вы- ражение «звездочка» не является самостоятельным, а лишь модифицирует смысл других регулярных выражений. Рассмотрим следующий пример:
print(re.findall('y.*y', text))
# ['yptography']
Оператор «звездочка» применяется к расположенному непосредственно перед ним регулярному выражению. В этом примере задаваемый регуляр- ным выражением шаблон начинается с 'y'
, далее следует произвольное количество символов,
.*
, за которыми снова следует символ 'y'
. Как видите, слово 'cryptography'
содержит одно вхождение этого шаблона:
'yptography'
Возможно, вы недоумеваете, почему этот код не находит длинную под- строку между 'originally'
и 'cryptography'
, которая тоже вроде бы соответствует шаблону регулярного выражения y.*y
. Дело в том, что оператор «точка» соответствует любому символу, кроме символа новой строки. В переменной text хранится многострочное строковое значение, включающее три символа новой строки. Оператор «звездочка» можно использовать и в сочетании с любым другим регулярным выражением.
Например, регулярному выражению abc*
соответствуют строки символов 'ab'
,
'abc'
,
'abcc'
и 'abccdc'
Регулярное выражение «один или ни одного»
В-третьих, нужно уметь находить соответствие типа «один или ни одного» с помощью регулярного выражения, символа
?
. Подобно оператору
*
, знак вопроса модифицирует какое-либо другое регулярное выражение, как можно видеть из следующего примера:
print(re.findall('blocks?', text))
# ['block', 'block', 'blocks']
Регулярное выражение «один или ни одного»,
?
, применяется к регулярно- му выражению, располагающемуся непосредственно перед ним, в данном случае к символу s
. Регулярное выражение «один или ни одного» означает, что модифицируемый им шаблон необязателен.

Поиск простых комбинаций символов в строковых значениях
183
В пакете re
Python знак вопроса может использоваться и по-другому, но к регулярному выражению «один или ни одного» это отношения не имеет: знак вопроса в сочетании с оператором «звездочка»,
*?
, служит для «нежад-
ного» (nongreedy) поиска соответствия шаблону. Например, при указании регулярного выражения
.*?
Python ищет минимальное количество произ- вольных символов. И наоборот, при указании оператора «звездочка»
*
без знака вопроса он «жадно» ищет соответствие как можно большего количе- ства символов.
Рассмотрим пример. При поиске в строке HTML-кода '
hello world
'
по регулярному выражению
<.*>
возвращается вся строка символов '
hello world
'
, а не только префикс '
'
. Если же нужен только префикс, необходимо воспользоваться «нежадным» регуляр- ным выражением
<.*?>
:
txt = '
hello world
'
print(re.findall('<.*>', txt))
# ['
hello world
']
print(re.findall('<.*?>', txt))
# ['
', '
']
Вооружившись знаниями этих трех инструментов — регулярных выражений
«точка»
, «звездочка»
*
и «один или ни одного»
?
, — вы уже сможете разо- браться в следующем однострочном решении.
Код
Роль входных данных играет строковое значение, а задача состоит в поиске с помощью «нежадного» подхода всех комбинаций символов, начинающихся с символа 'p'
, заканчивающихся символом 'r'
и включающих посередине между ними хотя бы одно вхождение символа 'e'
(и, возможно, произволь- ное количество других символов)!
Подобные текстовые запросы встречаются очень часто, особенно в ком- паниях, занимающихся обработкой текста, распознаванием речи или машинным переводом (например, компаниях, разрабатывающих по- исковые системы, социальные сети и видеоплатформы). Взгляните на листинг 5.1.

184
Глава 5. Регулярные выражения
Листинг 5.1. Однострочное решение для поиска (
«
нежадного
»
) конкретных шаблонов символов
## Зависимости import re
## Данные text = 'peter piper picked a peck of pickled peppers'
## Однострочник result = re.findall('p.*?e.*?r', text)
## Результат print(result)
Этот код выводит список всех подходящих фраз в тексте. Каких по-вашему?
Принцип работы
Поисковый запрос регулярного выражения — p.*?e.*?r
. Рассмотрим его по частям. Мы ищем фразу, начинающуюся с символа 'p'
и заканчивающуюся символом 'r'
. Кроме того, между ними должно встречаться хотя бы одно вхождение символа 'e'
. Кроме того, допускается произвольное количество символов (как пробельных, так и прочих). Поиск производится «нежадным» образом, с помощью
.*?
, поэтому Python будет искать минимальное коли- чество произвольных символов. Вот результат:
## Результат print(result)
# ['peter', 'piper', 'picked a peck of pickled pepper']
Сравните этот результат с получаемым при использовании «жадного» ре- гулярного выражения p.*e.*r
:
result = re.findall('p.*e.*r', text)
print(result)
# ['peter piper picked a peck of pickled pepper']
Первый «жадный» оператор «звездочка»
*
захватывает практически всю строку до конца.
Создание вашего первого веб-скрапера
с помощью регулярных выражений
Из предыдущего раздела вы узнали о самом эффективном способе поис- ка произвольных шаблонов текста в строковых значениях: регулярных

Создание вашего первого веб-скрапера с помощью регулярных выражений
185
выражениях. Этот раздел еще больше вдохновит вас на использование регулярных выражений и укрепит ваши знания с помощью практического примера.
Общее описание
Допустим, вы разработчик-фрилансер. Вашему заказчику — финансово- технологическому стартапу — постоянно нужны последние новости в сфере криптовалют. Они наняли вас для написания веб-скрапера, который бы ре- гулярно извлекал исходный HTML-код из новостных сайтов и искал в нем слова, начинающиеся с 'crypto'
(например,
'cryptocurrency'
,
'crypto-bot'
,
'crypto-crash'
и т. д.).
Первая наша попытка — следующий фрагмент кода:
import urllib.request search_phrase = 'crypto'
with urllib.request.urlopen('https://www.wired.com/') as response:
html = response.read().decode("utf8") # convert to string first_pos = html.find(search_phrase)
print(html[first_pos-10:first_pos+10])
Метод urlopen()
(из модуля urllib.request
) извлекает исходный HTML- код по указанному URL. Поскольку результат представляет собой байтовый массив, необходимо сначала преобразовать его в строковое значение с помо- щью метода decode()
. А затем воспользоваться строковым методом find()
для поиска позиции первого вхождения искомой строки. С помощью среза
(см. главу 2) мы извлекаем подстроку, содержащую непосредственное окру- жение искомого места. В результате получаем следующее строковое значение:
# ,r=window.crypto||wi
Ой, выглядит не очень. Как оказалось, поисковая фраза двусмысленна — большинство слов, содержащих 'crypto'
, с криптовалютами никак не связаны. Наш веб-скрапер генерирует ложноположительные результаты
(находит строковые значения, которые мы вовсе не хотели находить). Как же исправить эту ситуацию?
К счастью, вы как раз читаете эту книгу по Python, так что ответ очевиден: регулярные выражения! Возникает идея: исключить ложноположительные

186
Глава 5. Регулярные выражения результаты за счет поиска только тех вхождений, в которых за словом 'crypto'
следует до 30 произвольных символов, за которыми следует слово 'coin'
. Грубо говоря, поисковый запрос выглядит так: crypto
+
<до
30
про-
извольных
символов>
+
coin
. Рассмотрим следующие два примера:
'crypto-bot that is trading
Bitcoin'
— да;
'cryptographic encryption methods that can be cracked easily with quantum computers'
— нет.
Итак, проблема состоит в том, что регулярное выражение должно допускать до 30 произвольных символов между двумя строками символов. Как решить эту выходящую за пределы простого поиска строк задачу? Перебрать все ком- бинации символов не получится — их количество практически бесконечно.
Например, нашему поисковому шаблону должны соответствовать все следу- ющие строковые значения:
'cryptoxxxcoin'
,
'crypto coin'
,
'crypto bitcoin'
,
'crypto is a
currency.
Bitcoin'
, а также остальные сочетания до 30 символов между двумя строками. Даже если в алфавите всего 26 символов, количество теоретически удовлетворяющих нашему требованию строк символов превы- шает
1 26 30
= 2 813 198 901 284 745 919 258 621 029 615 971 520 741 376. Далее мы расскажем вам, как искать в тексте задаваемый регулярным выражением шаблон, которому соответствует большое количество возможных комбинаций символов.
Код
В этом коде мы ищем в заданном строковом значении вхождения, в которых за строкой символов 'crypto'
следует до 30 произвольных символов, за которыми следует слово 'coin'
. Посмотрим сначала на листинг 5.2, а затем обсудим, как этот код решает поставленную задачу.
Листинг 5.2. Однострочное решение для поиска фрагментов текста вида crypto(какой-то текст)coin
## Зависимости import re
## Данные text_1 = "crypto-bot that is trading Bitcoin and other currencies"
1
Поскольку помимо символов алфавита в них могут входить различные пробельные символы и знаки препинания. Впрочем, на деле далеко не все сочетания символов осмысленны, так что реальное количество сочетаний намного меньше.

Создание вашего первого веб-скрапера с помощью регулярных выражений
187
text_2 = "cryptographic encryption methods that can be cracked easily with quantum computers"
## Однострочник pattern = re.compile("crypto(.{1,30})coin")
## Результат print(pattern.match(text_1))
print(pattern.match(text_2))
Данный код производит поиск в двух строковых переменных, text_1
и text_2
Соответствуют ли они поисковому запросу (шаблону)?
Принцип работы
Во-первых, мы импортируем стандартный модуль для работы с регулярными выражениями в Python, re
. Самое интересное происходит в однострочнике, где компилируется поисковый запрос crypto(.{1,30})coin
. С помощью этого запроса мы и будем производить поиск в различных строковых значе- ниях. В нем используются специальные символы регулярных выражений.
Прочитайте их список внимательно, от начала до конца, и вы поймете смысл шаблона из листинга 5.2:
шаблон
()
предназначен для поиска соответствия указанному внутри него регулярному выражению;
шаблону соответствует любой произвольный символ;
шаблону
{1,30}
соответствует от 1 до 30 вхождений предыдущего регулярного выражения;
шаблону
({1,30})
соответствует строковое значение, включающее от 1 до 30 произвольных символов;
шаблону crypto(.{1,30})coin соответствует строковое значение, со- стоящее из трех частей: слова 'crypto'
, последовательности, включа- ющей от 1 до 30 символов, и следующего за ними слова 'coin'
Мы упомянули, что шаблон скомпилирован, поскольку Python создает объект шаблона, который можно повторно применять в разных местах — подобно тому, как скомпилированную программу можно использовать многократно. Теперь можно вызвать функцию match()
нашего скомпи- лированного шаблона, и будет произведен поиск по тексту. В результате получим следующее:

188
Глава 5. Регулярные выражения
## Результат print(pattern.match(text_1))
#
print(pattern.match(text_2))
# None
Строковая переменная text_1
соответствует шаблону (что видно из полу- ченного объекта
Match
), а text_2
— нет (что видно из результата
None
). И хотя текстовое представление первого объекта выглядит не слишком изящно, но ясно указывает, что заданная строка 'crypto-bot that is trading
Bitcoin'
соответствует регулярному выражению.
Анализ гиперссылок HTML-документов
В предыдущем разделе вы узнали, как искать в строке большое количество комбинаций символов с помощью шаблона регулярного выражения
.{x,y}
В этом разделе мы продвинемся еще на шаг и познакомимся со множеством других регулярных выражений.
Общее описание
Чем больше регулярных выражений вы знаете, тем быстрее и лаконичнее сможете решать реальные задачи. Какие же регулярные выражения наи- более важны? Изучите следующий список внимательно, поскольку все эти выражения будут применяться в данной главе. Можете считать те из них, которые уже видели выше, маленьким упражнением на повторение.
Регулярному выражению соответствует любой символ.
Регулярному выражению «звездочка»
<шаблон>*
соответствует произ- вольное количество вхождений, в том числе нулевое, соответствующих
<шаблон>
Регулярному выражению «по крайней мере один»
<шаблон>+
может со- ответствовать произвольное количество
<шаблон>
, но не менее одного.
Регулярному выражению «один или ни одного»
<шаблон>?
соответ- ствует один экземпляр
<шаблон>
или ни одного.
«Нежадному» регулярному выражению «звездочка»
<шаблон>*?
соот- ветствует как можно меньшее количество символов, необходимых для того, чтобы удовлетворить регулярному выражению в целом.

Анализ гиперссылок HTML-документов
189
Регулярному выражению
<шаблон>{m}
соответствует ровно m
копий
<шаблон>
Регулярному выражению
<шаблон>{m,n}
соответствует от m
до n
копий
<шаблон>
Регулярному выражению
<шаблон_1>|<шаблон_2>
соответствует как
<шаблон_1>
, так и
<шаблон_2>
Регулярному выражению
<шаблон_1><шаблон_2>
соответствует
<ша-
блон_1>
, за которым следует
<шаблон_2>
Регулярному выражению
(<шаблон>)
соответствует
<шаблон>
. Скоб- ки служат для группировки регулярных выражений с целью кон- троля порядка выполнения (например, регулярное выражение
(<шаблон_1><шаблон_2>)|<шаблон_3>
отличается от
<шаблон_1>
(<шаблон_2>|<шаблон_3>)
). Скобочные группы регулярного выраже- ния также служат для создания групп соответствий, как вы увидите далее в этом разделе.
Рассмотрим короткий пример. Пускай мы создали регулярное выражение b?(.a)*
. Каким комбинациям символов оно соответствует? Всем, начинаю- щимся с символа b
(он может и отсутствовать) и содержащим произвольное количество последовательностей из пар символов, заканчивающихся 'a'
Таким образом, ему соответствуют все следующие строковые значения:
'bcacaca'
,
'cadaea'
,
''
(пустая строка) и 'aaaaaa'
Прежде чем углубиться в следующий однострочник, вкратце поговорим о том, когда использовать ту или иную функцию регулярных выражений.
Три важнейшие функции регулярных выражений — re.match()
, re.search()
и re.findall()
. Две из них вы уже видели, но посмотрим на них вниматель- нее в следующем примере:
import re text = '''
"One can never have enough socks", said Dumbledore.
"Another Christmas has come and gone and I didn't get a single pair. People will insist on giving me books."
Christmas Quote
'''
regex = 'Christ.*'
print(re.match(regex, text))

190
Глава 5. Регулярные выражения
# None print(re.search(regex, text))
#
print(re.findall(regex, text))
# ["Christmas has come and gone and I didn't", 'Christmas Quote']
Все три эти функции принимают на входе регулярное выражение и строко- вое значение, в котором производится поиск. Функции match()
и search()
возвращают объект
Match
(или
None
, если соответствия регулярному вы- ражению не нашлось). В объекте
Match хранится позиция найденного соответствия и дополнительная метаинформация. Функция match()
не нашла соответствия регулярному выражению в нашей строке (вернула
None
). Почему? А потому, что эта функция ищет шаблон только с начала строки. Функция же search()
ищет первое вхождение шаблона в любом месте строки. А потому находит соответствие "Christmas has come and gone and
I
didn't"
Результаты работы функции findall()
наиболее интуитивно понятны, но и наименее удобны для дальнейшей обработки. Результаты работы функции findall()
представляет собой последовательность строковых значений, а не объект
Match
, поэтому точной информации о месте вхождения они не дают.
Тем не менее функция findall()
также бывает полезна: в отличие от методов match()
и search()
, функция findall()
извлекает все подходящие шаблоны символов. Это удобно, если требуется найти количество вхождений слова в тексте (например, строки символов 'Juliet'
в тексте «Ромео и Джульетты» или строки символов 'crypto'
в статье о криптовалютах).
Код
Представьте, что начальник попросил вас создать маленький веб-бот для сканирования веб-страниц и проверки того, содержится ли на них ссылка на домен finxter.com
. Кроме того, оно попросило вас убедиться, содержат ли описания гиперссылок строку символов 'test'
или 'puzzle'
. В HTML гиперссылки заключены в теги

. Сама гиперссылка задается в виде значения атрибута href
. Поэтому точная формулировка задачи (отражен- ной в листинге 5.3) звучит так: по заданному строковому значению найти все гиперссылки, указывающие на домен finxter.com и содержащие строки символов 'test'
или 'puzzle'
в описании ссылки.
1   ...   11   12   13   14   15   16   17   18   ...   21


написать администратору сайта