[БЕЗ_ЗВУКА] Привет. В этом модуле мы начнем знакомиться с библиотекой Scikit-learn — удобным и эффективным инструментом для анализа данных и решения задач машинного обучения в Python. С помощью библиотеки Sklearn можно пройти практически все шаги по анализу данных, начиная от генерации модельных данных, заканчивая построением моделей и оценкой их качества. В этом модуле мы с вами научимся генерировать модельные данные, удовлетворяющие заданным свойствам, научимся строить разбиения данных с помощью кросс-валидации, научимся строить линейные модели, а также оценивать их качество на основе различных метрик. Начнем мы с задачи генерации модельных данных и рассмотрим модуль sklearn.datasets. По ссылке ниже приведена документация по этому модулю, мы с вами для начала его импортируем, и так как в процессе работы мы будем строить много графиков, сразу же подключим magic pylab. Модуль datasets предлагает нам набор удобных функций для построения модельных данных. Существуют такие функции как make_classification или make_regression — это такие функции, которые позволяют нам строить dataset общего вида и гибко специфицировать параметры, которые нас интересуют. А также есть довольно удобные функции, такие как make_circles или make_checkerboard и другие, которые позволяют нам генерировать красивые наборы данных, которые удобно отрисовывать на плоскости. Эти данные обычно состоят из двух признаков (x и y) и позволяют получить красивые фигуры. Вот мы именно с такой функции начнем и рассмотрим функцию make_circles. Она позволяет нам получить dataset, который на плоскости выглядит, как две вложенные окружности. Вот давайте такой объект создадим и посмотрим, как он выглядит. Ну в качестве объекта мы получили тьюпл, состоящий из двух элементов. Первый элемент — это непосредственно набор данных, список описаний наших объектов. И второй элемент — это метки классов. Вот давайте выведем их на экран и посмотрим, как они выглядят. Мы видим, что каждый объект представляется списком из двух элементов. Первый элемент — это x координата, второй элемент — это y координата. И наши метки проставляются как цифры 0 или 1 в случае бинарной классификации, с которой мы сейчас и работаем. Вот для того, чтобы наглядно посмотреть, как же наш набор данных выглядит, я предлагаю его визуализировать. Для этого нам понадобится импортировать дополнительно объект ListedColormap. Сейчас посмотрим, зачем же он нужен. Итак, чтобы отрисовать наш объект, мы воспользуемся функцией scatter. Эта функция позволяет нам отрисовывать точки, зная их x и y координаты. На входе нужно подать два списка: первый — список x координат, второй — список y координат. т Зная, как выглядит наш набор данных, мы понимаем, как же этот список получить. Очень простая комбинация функций map и lambda позволяет нам сначала достать x координаты, в данном случаем мы проходимся по нашим объектам и берем нулевой элемент из списка, и аналогично можно получить y координату, только в данном случае в lambda функции мы указываем первый элемент из списка. Дальше нам нужно указать, какими цветами отрисовывать объекты. Понятно, что для наглядности мы хотим объекты разных классов видеть разными цветами. Конечно, можно было бы сделать так: можно было бы дважды вызвать функцию scatter, первый раз отрисовать объекты нулевого класса одним цветом, второй раз отрисовать объекты первого класса другим цветом. Но представьте, если бы у нас было больше двух классов, например 20, тогда, конечно, нам было бы не очень удобно. Вот для того, чтобы этого не делать, можно воспользоваться так называемым Colormap. В таком случае в качестве цветов мы можем передать просто числа, последовательность номеров, и далее передать в эту функцию некоторый объект, который знает связь между номером и цветом. В данном случае это и есть объект Colormap. Мы создаем его вот здесь, мы говорим, что у нас есть всего 2 цвета, в данном случае я выбрала красный и желтый, и таким образом получается, что объекты с меткой 0 будут иметь красный цвет, с меткой 1 — желтый. Вот давайте все это запустим и посмотрим, что мы получим. Итак, видим, что мы получили наш рисунок, это как раз две окружности: вложенная окружность желтого цвета и внешняя окружность красного. Ну давайте сразу же обернем эти команды в одну функцию, потому что нам она в дальнейшем понадобится, чтобы запись была несколько короче. Итак, в принципе мы видим, что этот набор данных довольно простой. Что если нам захочется немножечко усложнить себе задачу и получить окружность не с такими четкими краями. Это тоже очень просто делается. Для этого нужно передать внутрь нашей функции параметр noise, означает некоторый шум, который мы добавим к нашим данным. Давайте это сделаем. Сначала добавим не очень сильный шум и посмотрим, как же изменится наша картинка. Ну вот видим, что задача с точки зрения классификации несколько усложнилась, наши границы не такие четкие. Давайте еще немножечко усложним задачу и видим, что да, теперь стало еще сложнее. Таким образом, с помощью этого параметра мы можем гибко влиять на то, насколько сложную задачу мы получили. Теперь давайте рассмотрим функцию make_classification, она позволяет гибко создавать набор данных, который нас интересует. В частности, мы можем указать количество объектов, которые мы хотим видеть в нашем dataset, количество признаков, также мы можем задать, сколько из этих признаков должно быть информативными, а сколько избыточными. Можем даже ввести повторяющиеся признаки. Для начала давайте построим очень простую задачу классификации. Для того чтобы визуализировать набор данных, а мы обязательно захотим это сделать, нам удобнее работать с двумя признаками. Самостоятельно вы, конечно, можете задать любое количество признаков. Вот давайте зададим два признака, скажем, что один из них будет информативным, и опять же будем решать задачу бинарной классификации, зададим число классов равное двойке. Теперь давайте посмотрим, как наш dataset выглядит. Ну вот видим, что получили достаточно простую задачу. Очевидно, где нужно провести разделяющую плоскость в случае классификации. Теперь давайте немножечко усложним. Будем решать задачу классификации на 4 класса. Для этого изменим значение параметра n_classes (number of classes) и скажем, что пусть в данном случае два признака будут информативными. Нам придется еще изменить объект colors, потому что нам нужно несколько больше цветов для отрисовки этого набора данных. Вот давайте на него посмотрим. Видим, что мы получили четыре облака точек. Таким образом, с помощью этой функции можно генерировать набор данных практически любой сложности и гибко решать интересующие вас задачи. А мы с вами двигаемся дальше. Помимо непосредственной генерации данных с помощью Sklearn.datasets можно загружать так называемые toy datasets или «игрушечные» наборы данных. Это такие стандартные наборы данных, которые часто используются для тестирования ваших алгоритмов. Их можно загрузить с помощью перечисленных ниже функций, например функций load_iris, load_boston или load_digits. Наверное, наиболее популярным набором данных является dataset ирисы Фишера, поэтому мы с вами на него и посмотрим. Для того чтобы его загрузить, воспользуемся функцией load_iris, и теперь давайте посмотрим на объект, который мы получили. Видим, что это некоторый dict-like object, у которого есть ключи и некоторые значения по этим ключам. Вот давайте посмотрим на набор ключей, чтобы понять, что же у нас есть в этом объекте. Вот мы видим, что нам доступны названия признаков, доступны сами данные, сама целевая переменная, также есть некоторое описание данных и известны имена классов. Давайте начнем с того, что выведем описание. Ну вот видим, что описание довольно подробное. Из него мы можем очень многое про наш набор данных понять. В частности, мы знаем, сколько нам доступно объектов, какие есть признаки, что они означают. В данном случае мы понимаем, что признаки — это метрические характеристики цветков. Нам даже доступна некоторая статистика по нашим признакам. И есть ссылка на UCI репозиторий, в котором хранится исходный набор данных, даже есть информация о некоторых references, авторы и так далее. Довольно удобно, вы практически все про набор данных можете из этой справки понять. Теперь давайте посмотрим на имена признаков и имена классов. Вот мы видим, что они также доступны. Здесь из этого мы можем понять, как называются наши виды ирисов. Теперь давайте посмотрим на данные. Чтобы запись была покороче, выведем данные по первым 10 объектам. Ну вот видим, что это ровно те 4 признака, о которых было сказано в описании, и можно посмотреть на целевую переменную. Вот видим, что классы представлены метками от 0 до 2 и объекты отсортированы по классам. В общем-то, сейчас мы уже довольно хорошо понимаем, что из себя представляет эта выборка. Ну вот давайте на нее посмотрим и визуализируем наши данные. Для этого нам будет удобно работать с данными, как с таблицей. Вспомним немножечко библиотеку pandas и давайте построим новый DataFrame. Строить будем его на основе наших данных, далее добавим в качестве названий колонок имена признаков, потому что они нам также доступны, и добавим дополнительный столбец target. Он нам нужен для того, чтобы анализировать данные, зная информацию о классах. Итак, DataFrame готов, давайте на него посмотрим с помощью команды head, и вот мы видим, что мы получили ровно то, что мы хотели — признаковое описание каждого объекта и в конце метка класса. В общем-то, в таком виде нам уже удобно работать с нашим набором данных, но так как мы хотим сейчас просто визуализировать dataset, то тогда давайте рассмотрим колонку target и заменим в ней метки классов на их имена. Это можно сделать очень просто с помощью функции apply. Передадим в нее lambda функцию, которая берет метку класса и вместо нее подставляет название. Снова посмотрим на наш набор данных. И видим, что, да, всё получилось: вместо метки класса мы теперь видим там его имя. Что же, работать с pandas очень удобно, потому что мы с вами можем очень просто строить различные графики, например, строить гистограммы распределения признаков. Вот давайте посмотрим, как первый признак выглядит... как выглядит гистограмма первого признака для класса setosa. Делаем это с помощью команды hist и получаем нужное распределение. На самом деле, мы понимаем, что нас интересует не только это распределение. В общем-то, нам интересно посмотреть на распределение всех признаков в рамках всех классов. Но нам хочется понять, вообще говоря, отличаются они между собой или нет, сможем ли мы по этим признакам каким-то образом разделить наши объекты. Вот давайте это сделаем. Понятно, что мы можем делать это аналогичным образом, построив отдельно каждый график. Но, наверное, это не очень удобно. Представьте, когда много признаков, много классов, это довольно трудоемкое занятие. Давайте получим одну картинку, на которой мы будем иметь следующий набор данных: мы хотим построить гистограммы всех признаков по всем классам. Когда у нас не очень много классов и не очень много признаков, это довольно просто делается. Для этого мы с вами снова воспользуемся pyplot'ом и в цикле по признакам и по классам будем строить некоторые графики. Для этого нам понадобится метод subplot, который позволяет нам строить такую матрицу из графиков. В этот метод мы можем передать количество строк и количество столбцов, которое мы хотим, и также для каждого конкретного графика нужно будет сдать его номер. Вот, собственно, это мы с вами и делаем. Теперь мы будем пользоваться функцией hist, но уже не функцией, не методом dataframe'а, а просто методом pyplot. И также помимо самого графика, мы будем задавать его название, будем задавать ось x и ось y, как они называются. Вот давайте построим, это занимает какое-то время, потому что графиков много, мы с вами получаем целых 12 графиков. Вот давайте посмотрим, как они выглядят. Видим, что по столбцам у нас идут графики, касающиеся разных классов, и по строчкам мы видим признаки. Вот можно проанализировать все графики. Вот, например, давайте посмотрим на последний. Видим, что распределения, в общем-то, везде получились разные и видим, что даже вот по последнему признаку у нас в классе setosa максимальное значение порядка 0,6, а в следующем классе минимальное значение 1. То есть в общем-то видно, что даже в пространстве этих признаков мы задачу решить сможем, данные можно разделить. И в качестве бонуса хочется рассказать вам еще одну библиотеку, с помощью которой довольно просто визуализировать данные. Эта библиотека называется seaborn. Ну давайте для начала ее импортируем и попробуем с ее помощью построить следующий график. Давайте отобразим наши объекты в координатах пар признаков и посмотрим, хорошо ли они разделяются. Это можно сделать с помощью метода pairplot. Передаем этому методу наши dataframe и говорим, в каком столбике находится наша целевая метка. Строим данный график и видим, что мы получили очень интересное изображение: по диагонали мы видим значение... мы видим графики с распределением наших признаков, разными цветами отмечены разные классы. В общем-то, это графики, аналогичные тем, которые мы с вами строили самостоятельно, но совмещенные на одну картинку. Возможно, кому-то так даже больше понравится. Все остальные графики показывают, отображают наши объекты в пространстве пар признаков. Вот мы можем посмотреть, что существуют такие пары признаков, в которых объекты довольно неплохо разделяются. Вот, например, по этому признаку видно, что объекты можно разделить. Но с другой стороны, есть такие признаки, по которым объекты разделяются не так хорошо. Вот график получился довольно неплохой, но мне кажется, шрифт мелкий и читать не очень удобно. Вот давайте посмотрим, как мы можем на это повлиять. Мы видим, что можно с помощью метода set задать очень много параметров. Например, мы можем указать, какой шрифт мы хотим использовать, мы можем увеличить шрифт, можем задать цвета и так далее. Вот давайте немножечко увеличим шрифт и хочется показать вам еще одну вещь. Так как dataset "iris" довольно популярен, это стандартная вещь. Библиотека seaborn содержит функцию, которая позволяет этот dataset импортировать непосредственно, поэтому вам необязательно нужно иметь уже готовую dataframe к тому моменту, когда вы хотите рисовать этот график. Следственно, теперь вы знаете два способа: можно иметь свой dataframe, можно воспользоваться функцией load_datasets из библиотеки seaborn. Вот давайте теперь для разнообразия сделаем так, единственное отличие в том, что столбец target теперь называется по-другому. Вот и заодно давайте увеличим шрифт. [ПУСТО] Вот видим, что получили практически такой же график. Шрифт достаточно... шрифт стал немножечко больше. И также можем проанализировать наши данные. Если вам библиотека seaborn понравилась, то обязательно посмотрите ссылки ниже. Эта библиотека не входит в стандартный дистрибутив анаконда, поэтому ее придется поставить отдельно. Но это очень просто. По ссылкам доступны подробные инструкции на установку и также на установку с помощью анаконды. Попробуйте, это очень легко делается. Также доступно множество примеров и руководство по этой библиотеке. А мы на этом заканчиваем. На этом уроке мы научились генерировать модельные данные и загружать стандартные dataset'ы с помощью модуля sklearn datasets. На следующем уроке мы научимся строить разбиение данных с помощью кросс-валидации.