[БЕЗ_ЗВУКА] Привет. В этом модуле вы уже многое узнали про деревья решений, а значит настало время научиться их строить. В этом видео мы потренируемя строить деревья решений с помощью модуля tree из библиотеки Sklearn. По ссылкам ниже доступна документация этой библиотеки, а также множество примеров. Для начала давайте импортируем нужную функциональность, Здесь используются все привычные вам модули, из новых только модуль trees. Также мы с вами будем строить много графиков, поэтому мы сразу подключим magic pylab. Теперь можно переходить к генерации данных. Давайте решать задачу многоклассовой классификации, создадим с помощью функции make_classification dataset, состоящий из двух признаков: x и y координаты. Нам так удобней будет его отрисовывать. И сделаем задачу многоклассовой классификации с тремя классами. Итак, генерируем данные, теперь давайте сразу же получившийся набор данных отрисуем. Для этого нам понадобится создать Colormap, нам также понадобится еще один Colormap, когда мы будем строить разделяющие поверхности, поэтому давайте сразу объявим целых два. И теперь отрисуем наши объекты на плоскости. Мы их отрисовали в координатах признаков. В данном случае нам важно расположение каждой точки (мы еще будем к этому обращаться), поэтому сразу сделаем их такими большими. Это делается с помощью атрибута s. Так, точки мы получили. Видим, что у нас есть три облака точек, причем некоторые облака накладываются друг на друга, что только усложняет нашу задачу. Теперь давайте разобъем данные на обучение и тест и перейдем к построению модели. Разбивает данные с помощью функции train_test_split. Итак, теперь давайте строить модель. Для того чтобы построить модель, воспользуемся методом DecisionTreeClassifier из модуля tree, который возвращает нам объект classificator decision tree. Для начала мы с вами создаем объект с парамерами по умолчанию, и сразу же давайте наше дерево обучим с помощью метода fit. Итак, обученное дерево готово. Теперь давайте построим предсказания, для этого применим метод predict и передадим ему на вход тестовую выборку, и сразу же оценим качество с помощью метрики accuracy, передав функции accuracy_score на вход 2 аргумента: test_labels — правильные ответы на нашей тестовой выборке и наши предсказания, которые мы сейчас построим. Итак, видим, что с помощью данного алгоритма мы правильно оцениваем приблизительно 70 % объектов. Ну, довольно неплохо. Итак, мы научились строит базовое решающее дерево, а теперь давайте проанализируем, как меняется качество модели, а также вид разделяющей плоскости в зависимости от параметра дерева, например в зависимости от его глубины. Для того чтобы отрисовать разделяющую плоскость нашего алгоритма, нам понадобится реализовать ряд дополнительных функций. Первая вспомогательная функция будет называться get_meshgrid и на вход будет принимать данные шаг и граница. Давайте рассмотрим, что же она делает. Зная наши данные, мы можем легко оценить, как меняются значения каждого признака на наших объектах. В данном случае у нас всего два признака — x и y координата, поэтому по данным мы легко можем понять, в каких границах меняется x и y. Соответственно, зная эти границы, мы можем получить набор точек, находящихся внутри квадрата по x и y в соответствии с нашими данными. Вот давайте все эти точки получим, будем получать точки с некоторым шагом (пусть он будет 0,05) и вернем некоторый объект под называнием meshgrid — набор наших точек. Именно с помощью этого объекта мы будем отрисовывать разделяющую плоскость. Так. Следующая вспомогательная функция называется plot_decision_surface. Именно она отвечает непосредственно за отрисовку наших графиков. Функция принимает на вход целый ряд аргументов: во-первых, это модель, которую мы анализируем; это обучающая и тестовая выборка (как данные, так и метки); и это два объекта colormap: один нужен для того, чтобы отрисовывать объекты в плоскости признаков, другой нужен для того, чтобы отрисовывать разделяющую поверхность. Пусть наша разделяющая поверхность будет несколько светлее, для того чтобы на ней хорошо было видно объекты. Итак, для того чтобы получить разделяющую поверхность, первое, что нужно сделать — это обучить модель. Делаем это с помощью метода fit. Обучаем модель на обучающей выборке. Далее давайте зададим размер нашего рисунка. Так как нам интересно посмотреть и на обучающие объекты, и на объекты из тестовой выборки, то давайте сразу будем строить subplot. Рисунок будет состоять из двух рисунков, поэтому сделаем наш график чуть шире по горизонтали, чем по вертикали. Так, теперь переходим непосредственно к отрисовке разделяющей поверхности. Для начала рисуем график с обучающими объектами. Нам нужно получить наш meshgrid, делаем это с помощью функции, которую мы определили шагом ранее. Теперь как же нам раскрасить все эти точки в правильные цвета, чтобы наглядно увидеть разделяющую поверхность? Ну вот давайте сделаем следующий трюк: представим, что каждая точка является объектом, который подлежит классификации. Фактически что мы можем сделать? Мы можем взять нашу обученную модель и применить ее к каждой точке на плоскости. Так как эти точки находятся внутри границ изменения признаков, то таким образом мы получим квадрат, на котором легко сможем отобразить все наши обучающие объекты. Вот давайте этот объект получим. Далее с помощью метода predict будем классифицировать каждую из этих точек и таким образом получим набор меток. Эти метки мы будем использовать в качестве цветов для построения разделяющей плоскости. Далее с помощью метода pcolormesh давайте отрисуем нашу разделяющую плоскость. Заметьте, что сюда мы передаем сами точки xx, yy. Также мы передаем наши предсказания. Они нужны будут для того, чтобы отрисовать объекты разными цветами, и указываем, какой colormap мы используем. В данном случае мы используем light_colors — светлые цвета. Далее поверх нашей разделяющей поверхности мы можем отрисовать объекты. Делаем это с помощью уже известной нам функции scatter, и здесь используем другой colormap. Ну и дальше давайте зададим нашему графику название, и прямо в названии запишем качество получившейся модели. Оценим качество с помощью метрики accuracy, используем функцию accuracy_score. Аналогично давайте поступим с тестовыми данными. Так как разделяющая поверхность у нас не изменится, не будем заново ее получать, просто отрисуем еще раз готовую разделяющую поверхность и на ней отметим точки из нашей тестовой выборки. Вот ровно это сделает наша функция. Так, функцию мы определили, теперь давайте ее применим. Для начала создадим очень простое решающее дерево глубины 1. Фактически у нас может быть проверено только одно условие, и дальше должны следовать листья, так как глубина всего лишь 1. Вот давайте создадим такую модель, передадим ее на вход нашей функции plot_decision_surface и посмотрим, как будет выглядеть разделяющая поверхность. Смотрите, мы получили следующий график. Так как дерево глубины 1, очевидно, что мы не сможем использовать 2 признака для классификации. В данном случае выгоднее было использовать признак, разделяющий наши объекты по вертикали, поэтому мы видим, что мы получили соответствующую картинку. Точность классификации составила 66 % на обучающей выборке и 63 % на тестовой. Вот довольно простая разделяющая поверхность. Что если сделать немного более сложную модель, то есть построить дерево глубины 2? Вот давайте посмотрим, как изменится картинка. Создаем соответствующий объект и передаем его нашей функции. Видим, что картинка несколько усложнилась. Теперь мы делим объекты не только по горизонтали, но и по вертикали. Видим, что это больше соответствует нашим данным, и качество действительно растет. Растет качество как на обучении, так и на тесте. Отсюда можно сделать предположение, что глубина деревьев положительно сказывается на качестве, и чем глубже получается наше дерево, тем лучше мы обучаем модель. Ну что ж, давайте, исходя из этого предположения, продолжим увеличивать глубину деревьев. Построим дерево глубины 3. Видим, что качество на обучении продолжает расти, и при этом качество на тесте не падает. Возможно, имеет смысл еще сильнее увеличить глубину дерева? Давайте попробуем. Давайте вообще не будем ничем ограничивать глубину дерева. Будем его строить настолько глубоким, насколько это возможно, так чтобы на обучении не произошло ни одной ошибки. Тогда не будем ее никак ограничивать и посмотрим, что мы получили. Мы видим, что мы получили довольно сложную разделяющую поверхность, там очень много областей нескольких цветов, и качество на обучении равняется 1. Мы не ошиблись ни разу, видим, что все наши объекты находятся в области своего цвета, в правильной области. Теперь смотрим на тестовые данные. Что мы видим? Мы видим, что для тестовых данных такая разделяющая поверхность является неоптимальной, то есть многие точки находятся в чужой области. Как такое могло получиться? Получается, что мы с вами чрезмерно подстроились под обучающие данные, то есть фактически произошло переобучение. Чтобы бороться с проблемой переобучения, мы можем не только ограничивать дерево по глубине, но также накладывать ограничения на другие параметры. Например, давайте поступим следующим образом: ограничим количество объектов, которые необходимы для того, чтобы продолжать ветвление из некоторой вершины. То есть создадим параметр min_samples_leaf = 3. Это означает, что минимальное количество объектов в листе должно быть 3. По умолчанию этот параметр был равен 1. Вот давайте сделаем такое ограничение и посмотрим, как изменится вид нашей разделяющей поверхности. Ну да, видим, что разделяющая поверхность стала несколько проще, чем была на предыдущем графике. Это хорошо. С одной стороны, у нас немножко уменьшилось качество на обучении, но, с другой стороны, выросло качество на тесте, что и говорит о том, что мы построили более хорошую модель. На этом мы с вами заканчиваем урок по решающим деревьям. Мы научились строить этот алгоритм и проанализировали, как параметры дерева влияют на его качество и на вид разделяющей поверхности. На этом мы заканчиваем изучение решающих деревьев, и уже в следующем уроке вы познакомитесь с таким понятием как случайный лес.