Text
                    

Санкт-Петербург «БХВ-Петербург» 2023
УДК 004.43 ББК 32.973-018.1 М31 М31 Масис С. Интерпретируемое машинное обучение на Python: Пер. с англ. — СПб.: БХВ-Петербург, 2023. — 640 с.: ил. ISBN 978-5-9775-1735-5 Книга поможет осознанно и эффективно работать с моделями машинного обучения. Дано введение в интерпретацию машинного обучения: раскрыты важность темы, ее ключевые понятия и проблемы. Рассмотрены методы интерпретации: модельно-агностические, якорные и контрфактические, для многопеременного прогнозирования, а также визуализации сверточных нейронных сетей. Раскрыты вопросы настройки на интерпретируемость: отбор и конструирование признаков, ослабление систематического смещения и причинно-следственный вывод, монотонные ограничения, настройка моделей и устойчивость к антагонизму. Показаны перспективы развития интерпретируемых моделей машинного обучения. Каждая глава книги включает подробные примеры исходного кода на языке Python. На сайте издательства размещен архив с цветными иллюстрациями. Для программистов в области машинного обучения УДК 004.43 ББК 32.973-018.1 Научный редактор: Инженер-разработчик компании КРОК Анвар Хафизов Группа подготовки издания: Руководитель проекта Зав. редакцией Перевод с английского Редактор Компьютерная верстка Корректор Оформление обложки Евгений Рыбаков Людмила Гауль Андрея Логунова Анна Кузьмина Натальи Смирновой Светлана Крутоярова Зои Канторович © Packt Publishing 2021. First published in the English language under the title ‘Interpretable Machine Learning with Python – (9781800203907)’ Впервые опубликовано на английском языке под названием ‘Interpretable Machine Learning with Python – (9781800203907)’ "БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20. ISBN 978-1-80020-390-7 (англ.) ISBN 978-5-9775-1735-5 (рус.) © Packt Publishing, 2021 © Перевод на русский язык, оформление. ООО "БХВ-Петербург", ООО "БХВ", 2023
Оглавление Об авторе ........................................................................................................................ 15 О рецензентах................................................................................................................. 17 Предисловие ................................................................................................................... 19 Для кого эта книга предназначена................................................................................. 20 Что эта книга охватывает ............................................................................................... 21 Как получить максимальную отдачу от этой книги .................................................... 23 Загрузка файлов с исходным кодом .............................................................................. 24 Загрузка цветных изображений ..................................................................................... 25 Используемые условные обозначения .......................................................................... 25 ЧАСТЬ I. ВВЕДЕНИЕ В ИНТЕРПРЕТАЦИЮ МАШИННОГО ОБУЧЕНИЯ ........................ 27 Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно?.................................................................................................. 29 Технические требования ................................................................................................ 30 Что такое интерпретация машинного обучения?......................................................... 30 Изучение простой модели предсказания веса ....................................................... 31 Понимание разницы между интерпретируемостью и объяснимостью...................... 37 Что такое интерпретируемость? ............................................................................. 37 Что такое объяснимость? ........................................................................................ 40 Деловое обоснование интерпретируемости ................................................................. 42 Более качественные решения.................................................................................. 42 Более надежные бренды .......................................................................................... 44 Более высокий уровень этичности ......................................................................... 46 Более высокая прибыльность ................................................................................. 49 Резюме.............................................................................................................................. 50 Источники изображений ................................................................................................ 50 Справочные материалы .................................................................................................. 50 Глава 2. Ключевые понятия интерпретируемости................................................. 52 Технические требования ................................................................................................ 52 Миссия ............................................................................................................................. 52 Подробности о сердечно-сосудистых заболеваниях ............................................ 53
6 Оглавление Подход.............................................................................................................................. 54 Подготовительные работы ............................................................................................. 54 Загрузка библиотек .................................................................................................. 54 Изучение проблемы и подготовка данных ............................................................ 54 Ознакомление с типами методов интерпретации и диапазонами интерпретируемости ....................................................................................................... 57 Типы методов модельной интерпретации ............................................................. 61 Диапазоны модельной интерпретируемости......................................................... 61 Интерпретирование отдельных предсказаний с помощью логистической регрессии .................................................................................................................. 62 Оценивание препятствий, мешающих интерпретируемости результатов машинного обучения ...................................................................................................... 67 Нелинейность ........................................................................................................... 69 Интерактивность ...................................................................................................... 72 Немонотонность....................................................................................................... 72 Миссия выполнена .......................................................................................................... 74 Резюме.............................................................................................................................. 75 Справочные материалы .................................................................................................. 75 Глава 3. Трудности интерпретации........................................................................... 76 Технические требования ................................................................................................ 76 Миссия ............................................................................................................................. 76 Подход.............................................................................................................................. 78 Подготовительные работы ............................................................................................. 78 Загрузка библиотек .................................................................................................. 78 Изучение проблемы и подготовка данных ............................................................ 79 Обзор традиционных методов модельной интерпретации ......................................... 84 Предсказывание минут задержки с помощью различных регрессионных методов........................................................................................... 84 Классифицирование рейсов как задержанных либо незадержанных с использованием различных классификационных методов............................... 89 Визуализация задержанных рейсов с помощью методов понижения размерности .............................................................................................................. 96 Ограничения традиционных методов модельной интерпретации ........................... 102 Изучение имманентно интерпретируемых моделей (типа белого ящика) .............. 103 Обобщенные линейные модели............................................................................ 103 Деревья решений.................................................................................................... 118 RuleFit ..................................................................................................................... 123 Метод ближайших соседей ................................................................................... 125 Наивный Байес ....................................................................................................... 127 Распознавание компромисса между результативностью и интерпретируемостью ............................................................................................... 130 Особые модельные свойства................................................................................. 130 Диагностика результативности ............................................................................ 131
Оглавление 7 Обнаружение более новых интерпретируемых (аквариумных) моделей ................ 134 Объяснимая бустинговая машина ........................................................................ 134 Skoped-Rules ........................................................................................................... 138 Миссия выполнена ........................................................................................................ 140 Резюме............................................................................................................................ 141 Источник набора данных.............................................................................................. 141 Справочные материалы ................................................................................................ 142 ЧАСТЬ II. ОСВОЕНИЕ МЕТОДОВ ИНТЕРПРЕТАЦИИ .................................................. 143 Глава 4. Основы важности признаков и их влияние ........................................... 145 Технические требования .............................................................................................. 145 Миссия ........................................................................................................................... 146 Личность и очередность рождения ...................................................................... 146 Подход............................................................................................................................ 147 Подготовительные работы ........................................................................................... 147 Загрузка библиотек ................................................................................................ 147 Изучение проблемы и подготовка данных .......................................................... 148 Как измерить влияние признака на исход .................................................................. 150 Важность признаков в древовидных моделях..................................................... 154 Важность признаков в логистической регрессии ............................................... 156 Важность признаков в линейном дискриминантном анализе ........................... 159 Важность признаков в многослойном персептроне ........................................... 161 Применение перестановочной важности признаков на практике ............................ 162 Недостатки метода перестановочной важности признаков ............................... 165 Интерпретирование графиков частичной зависимости............................................. 166 Интеракционные графики частичной зависимости ............................................ 171 Недостатки графиков частичной зависимости.................................................... 174 Объяснение графиков индивидуального условного ожидания ................................ 174 Недостатки графиков индивидуального условного ожидания.......................... 179 Миссия выполнена ........................................................................................................ 179 Резюме............................................................................................................................ 180 Источник набора данных.............................................................................................. 180 Справочные материалы ................................................................................................ 180 Глава 5. Модельно-агностические методы глобальной интерпретации.......... 182 Технические требования .............................................................................................. 182 Миссия ........................................................................................................................... 183 Подход............................................................................................................................ 184 Подготовительные работы ........................................................................................... 185 Загрузка библиотек ................................................................................................ 185 Изучение проблемы и подготовка данных .......................................................... 186 Значения Шепли............................................................................................................ 196
8 Оглавление Интерпретирование сводки SHAP и графиков зависимости .................................... 198 Генерирование сводных графиков SHAP ............................................................ 202 Изучение взаимодействий..................................................................................... 204 Графики зависимости SHAP ................................................................................. 207 Силовые графики SHAP ........................................................................................ 215 Графики накопленных локальных эффектов.............................................................. 217 Глобальные суррогаты.................................................................................................. 221 Подгонка суррогатов ............................................................................................. 221 Оценивание суррогатов......................................................................................... 222 Интерпретирование суррогатов............................................................................ 223 Миссия выполнена ........................................................................................................ 225 Резюме............................................................................................................................ 225 Справочные материалы ................................................................................................ 226 Глава 6. Модельно-агностические методы локальной интерпретации ........... 227 Технические требования .............................................................................................. 227 Миссия ........................................................................................................................... 227 Подход............................................................................................................................ 228 Подготовительные работы ........................................................................................... 229 Загрузка библиотек ................................................................................................ 229 Изучение проблемы и подготовка данных .......................................................... 230 Задействование ядерного объяснителя SHAP для локальных интерпретаций со значениями SHAP..................................................................................................... 236 Обучение модели C-SVC ...................................................................................... 237 Вычисление значений SHAP с помощью ядерного объяснителя...................... 239 Локальная интерпретация для группы предсказаний с использованием графиков решений ................................................................................................. 241 Локальная интерпретация по одному предсказанию за раз с использованием силового графика.................................................................... 244 Применение локально интерпретируемых модельно-агностических объяснений..................................................................................................................... 247 Что такое LIME? .................................................................................................... 247 Локальная интерпретация по одному предсказанию за раз с использованием табличного объяснителя на основе LIME ............................ 249 Использование метода LIME для NLP........................................................................ 251 Обучение модели LightGBM................................................................................. 253 Локальная интерпретация по одному предсказанию за раз с использованием текстового объяснителя на основе LIME ............................. 254 Опробование SHAP в обработке естественного языка.............................................. 257 Сравнение SHAP с LIME.............................................................................................. 260 Миссия выполнена ........................................................................................................ 261 Резюме............................................................................................................................ 262 Источник набора данных.............................................................................................. 262 Справочные материалы ................................................................................................ 262
Оглавление 9 Глава 7. Якорные и контрфактические объяснения............................................ 264 Технические требования .............................................................................................. 264 Миссия ........................................................................................................................... 264 Необъективная смещенность в диагностиках риска рецидивизма ................... 266 Подход............................................................................................................................ 267 Подготовительные работы ........................................................................................... 267 Загрузка библиотек ................................................................................................ 267 Изучение проблемы и подготовка данных .......................................................... 268 Якорные объяснения..................................................................................................... 278 Подготовительные работы для якорных и контрафактических объяснений с помощью библиотеки alibi ............................................................ 279 Локальные интерпретации якорных объяснений ............................................... 281 Анализ контрфактических объяснений....................................................................... 284 Контрфактические объяснения под руководством прототипов ........................ 285 Получение контрфактических экземпляров и многого другого с помощью инструмента What-If Tool (WIT) ...................................................... 289 Сравнение с помощью метода контрастивного объяснения..................................... 299 Миссия выполнена ........................................................................................................ 303 Резюме............................................................................................................................ 304 Источник набора данных.............................................................................................. 304 Справочные материалы ................................................................................................ 304 Глава 8. Визуализация сверточных нейронных сетей ......................................... 306 Технические требования .............................................................................................. 306 Миссия ........................................................................................................................... 307 Подход............................................................................................................................ 308 Подготовительные работы ........................................................................................... 309 Загрузка библиотек ................................................................................................ 309 Изучение проблемы и подготовка данных .......................................................... 310 Диагностика CNN-классификатора традиционными методами интерпретации........................................................................................................ 315 Визуализирование процесса усвоения с помощью активационных методов.......... 323 Промежуточные активации................................................................................... 325 Максимизация активации ..................................................................................... 328 Оценивание ошибочных классификаций с помощью градиентных методов атрибуции ...................................................................................................................... 332 Карты значимости.................................................................................................. 333 Метод градиентных карт активаций классов Grad-CAM................................... 336 Интегрированные градиенты................................................................................ 338 Окончательная сборка ........................................................................................... 341 Объяснение классификаций с помощью пертурбационных методов атрибуции ...................................................................................................................... 344 Окклюзивная чувствительность ........................................................................... 344 Объяснитель изображений методом LIME ......................................................... 347 Метод контрастивных объяснений ...................................................................... 349
10 Оглавление Окончательная сборка ........................................................................................... 354 Бонусный метод: глубокий объяснитель SHAP.................................................. 357 Миссия выполнена ........................................................................................................ 358 Резюме............................................................................................................................ 359 Источники данных и изображений.............................................................................. 359 Справочные материалы ................................................................................................ 360 Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа чувствительности .................................................... 361 Технические требования .............................................................................................. 362 Миссия ........................................................................................................................... 362 Подход............................................................................................................................ 364 Подготовительные работы ........................................................................................... 365 Загрузка библиотек ................................................................................................ 365 Изучение проблемы и подготовка данных .......................................................... 366 Диагностика моделей временного ряда с использованием традиционных методов интерпретации ................................................................................................ 375 Использование стандартных регрессионных метрик ......................................... 376 Агрегации предсказательных ошибок ................................................................. 378 Оценивание как классификационная задача ....................................................... 380 Генерирование LSTM-атрибуций с помощью интегрированных градиентов ........ 381 Вычисление глобальных и локальных атрибуций с помощью ядерного объяснителя SHAP ........................................................................................................ 387 Зачем использовать ядерный объяснитель? ........................................................ 387 Определение стратегии, позволяющей работать с моделью многопеременного временного ряда.................................................................... 388 Заложение основы для стратегии аппроксимации перестановок...................... 389 Выявление влияющих признаков с помощью факторной приоритизации.............. 394 Вычисление индексов чувствительности Морриса ............................................ 395 Анализирование элементарных эффектов........................................................... 398 Квантифицирование неопределенности и стоимостной чувствительности с помощью фиксирования факторов ........................................................................... 401 Генерирование и предсказывание на образцах Сальтелли ................................ 402 Выполнение анализа чувствительности по методу Соболя............................... 403 Встраивание реалистичной функции стоимости ................................................ 405 Миссия выполнена ........................................................................................................ 409 Резюме............................................................................................................................ 410 Источники данных и изображений.............................................................................. 411 Справочные материалы ................................................................................................ 411 ЧАСТЬ III. НАСТРОЙКА НА ИНТЕРПРЕТИРУЕМОСТЬ .............................................. 413 Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости................................................................................................... 415 Технические требования .............................................................................................. 416
Оглавление 11 Миссия ........................................................................................................................... 416 Подход............................................................................................................................ 417 Подготовительные работы ........................................................................................... 418 Загрузка библиотек ................................................................................................ 418 Изучение проблемы и подготовка данных .......................................................... 419 Изучение эффекта нерелевантных признаков ............................................................ 420 Построение базовой модели ................................................................................. 421 Оценивание модели ............................................................................................... 422 Обучение базовой модели на разных максимальных глубинах ........................ 425 Обзор фильтрационных методов отбора признаков .................................................. 427 Базовые фильтрационные методы........................................................................ 428 Корреляционные фильтрационные методы ........................................................ 430 Ранжирующие фильтрационные методы............................................................. 432 Сравнение фильтрационных методов .................................................................. 434 Анализ встроенных методов отбора признаков ......................................................... 435 Раскрытие потенциала оберточных, гибридных и продвинутых методов отбора признаков .......................................................................................................... 439 Оберточные методы............................................................................................... 439 Гибридные методы ................................................................................................ 441 Продвинутые методы ............................................................................................ 443 Оценивание всех моделей, построенных с применением отбора признаков................................................................................................................ 445 Обзор конструирования признаков ............................................................................. 447 Миссия выполнена ........................................................................................................ 455 Резюме............................................................................................................................ 457 Источники наборов данных ......................................................................................... 457 Справочные материалы ................................................................................................ 457 Глава 11. Ослабление систематического смещения и причинно-следственный вывод ............................................................................ 459 Технические требования .............................................................................................. 460 Миссия ........................................................................................................................... 460 Подход............................................................................................................................ 461 Подготовительные работы ........................................................................................... 462 Загрузка библиотек ................................................................................................ 462 Изучение проблемы и подготовка данных .......................................................... 463 Обнаружение систематического смещения................................................................ 467 Визуализирование систематического смещения набора данных ...................... 469 Квантифицирование систематического смещения набора данных................... 472 Квантифицирование систематического смещения модели................................ 476 Ослабление систематического смещения ................................................................... 479 Методы ослабления систематического смещения стадии предварительной обработки ................................................................................. 480
12 Оглавление Методы ослабления систематического смещения стадии промежуточной обработки.................................................................................... 487 Методы ослабления систематического смещения стадии последующей обработки ....................................................................................... 490 Окончательная сборка ........................................................................................... 493 Построение причинно-следственной модели............................................................. 495 Изучение результатов эксперимента ................................................................... 497 Изучение причинно-следственных моделей ....................................................... 500 Инициализация линейного дважды устойчивого ученика................................. 502 Обучение причинно-следственной модели ......................................................... 502 Гетерогенные эффекты экспериментальной процедуры........................................... 503 Выбор политики..................................................................................................... 507 Проверка устойчивости оценки ................................................................................... 510 Добавление случайной общей причины .............................................................. 510 Замена экспериментальной процедуры случайной переменной ....................... 511 Миссия выполнена ........................................................................................................ 512 Резюме............................................................................................................................ 513 Источник набора данных.............................................................................................. 513 Справочные материалы ................................................................................................ 513 Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость.............................................................................................. 515 Технические требования .............................................................................................. 516 Миссия ........................................................................................................................... 516 Подход............................................................................................................................ 518 Подготовительные работы ........................................................................................... 518 Загрузка библиотек ................................................................................................ 519 Изучение проблемы и подготовка данных .......................................................... 519 Установка ограничений с помощью конструирования признаков........................... 522 Упорядочение......................................................................................................... 523 Дискретизация........................................................................................................ 525 Члены взаимодействия и нелинейные преобразования ..................................... 526 Категориальное кодирование ............................................................................... 530 Другие подготовительные работы........................................................................ 531 Настройка моделей на интерпретируемость .............................................................. 532 Настройка нейронной сети Keras ......................................................................... 533 Настройка других популярных модельных классов........................................... 536 Оптимизация под объективность с помощью байесовой гиперпараметрической настройки и прикладных метрик.................................. 544 Имплементирование модельных ограничений........................................................... 550 Ограничения в XGBoost ........................................................................................ 551 Ограничения в TensorFlow Lattice........................................................................ 556 Миссия выполнена ........................................................................................................ 563
Оглавление 13 Резюме............................................................................................................................ 564 Источник набора данных.............................................................................................. 565 Справочные материалы ................................................................................................ 565 Глава 13. Устойчивость к антагонизму .................................................................. 566 Технические требования .............................................................................................. 567 Миссия ........................................................................................................................... 567 Подход............................................................................................................................ 569 Подготовительные работы ........................................................................................... 569 Загрузка библиотек ................................................................................................ 569 Изучение проблемы и подготовка данных .......................................................... 570 Загрузка базовой модели CNN ............................................................................. 573 Диагностика базового классификатора CNN ...................................................... 575 Эвазивные атаки............................................................................................................ 576 Атака быстрым методом на основе знака градиента.......................................... 578 Атака методом инфинитной нормы Карлини и Вагнера ................................... 581 Целенаправленная атака методом антагонистических заплат........................... 583 Защита от целенаправленных атак с помощью предобработки ............................... 585 Защита от любой эвазивной атаки с помощью антагонистического обучения устойчивого классификатора ...................................................................... 590 Оценивание и сертифицирование устойчивости к антагонизму .............................. 595 Сравнение устойчивости модели с силой атаки ................................................. 595 Сертифицирование устойчивости с помощью рандомизированного сглаживания............................................................................................................ 597 Миссия выполнена ........................................................................................................ 604 Резюме............................................................................................................................ 605 Источники наборов данных ......................................................................................... 605 Справочные материалы ................................................................................................ 606 Глава 14. Интерпретируемость машинного обучения: что дальше?................ 607 Современное состояние интерпретируемости машинного обучения ...................... 607 Связываем всё воедино! ........................................................................................ 607 Текущие тренды ............................................................................................................ 612 Размышления о будущем интерпретируемости машинного обучения.................... 614 Новое видение машинного обучения................................................................... 615 Междисциплинарный подход............................................................................... 616 Соответствующая требованиям стандартизация ................................................ 616 Исполнение регуляторных предписаний............................................................. 616 Бесшовная автоматизация машинного обучения со встроенной интерпретацией ...................................................................................................... 617 Более тесная интеграция с инженерами MLOps ................................................. 617 Справочные материалы ................................................................................................ 618 Предметный указатель .............................................................................................. 619
Об авторе Серг Масис (Serg Masís) в течение последних двух десятилетий трудится на стыке веб-разработки, разработки приложений и аналитики. В настоящее время он является специалистом по климатическим и агрономическим данным в Syngenta, ведущей агробизнес-компании с миссией по улучшению глобальной продовольственной безопасности. До этого стал одним из основателей стартапа, созданного Harvard Innovation Labs, который объединил возможности облачных вычислений и машинного обучения с принципами из науки принятия решений, чтобы знакомить пользователей с новыми местами и событиями. Независимо от того, к чему это относится: к досугу, болезням растений или пожизненной ценности клиента, — Серг увлечен нахождением часто недостающих связей между данными и процессом принятия решений, и интерпретация результатов работы моделей машинного обучения эффективно помогает преодолевать этот разрыв.
О рецензентах Шайлендра Кадре (Shailendra Kadre) — опытный специалист в области машинного и глубокого обучения, разработки продуктов и цифровой трансформации с более чем 20-летним отраслевым опытом работы в глобальных компаниях по производству ИТ-продуктов и услуг. В настоящее время Шайлендра является признанным лидером по аналитике продуктов в HP Inc., Бангалор. Он активно работает советником университетов и корпораций по искусственному интеллекту (ИИ). Является автором двух книг по искусственному интеллекту, опубликованных в издательстве Springer и McGrow-Hill, США. Он — профессиональный фотограф. Занимал лидирующие позиции в области машинного обучения, аналитики продуктов и цифровой трансформации в HP Inc., Citi Group/Oracle, Satyam и TCS. Шейлендра Кадре имеет степень магистра в области инженерного дела в Индийском технологическом институте (Indian Institute of Technology, IIT), Дели. Джеймс Ле (James Le) является поборником данных для Superb AI, компании, поддерживаемой венчурным фондом Y Combinator, которая ощутимо улучшает рабочие потоки производства ИИ с помощью платформы управления данными, служащей для повышения квалификации. До этого он получил степень магистра в области теории и практики вычислительных систем и машин в RIT, проводя исследования на стыке глубокого обучения и рекомендательных систем. Мухаммад Рехман Зафар (Muhammad Rehman Zafar) является кандидатом наук в Университете Райерсона (Ryerson University), Торонто, Канада, и связан с Лабораторией мультимедийных исследований Райерсона (Ryerson Multimedia Research Laboratory, RML). Его исследование сосредоточено на дизайне и разработке интерпретируемых моделей путем комбинирования интерактивных визуализаций и подходов к машинному обучению, которые помогают конечным пользователям понимать решения, принимаемые сложными моделями. Рехман также является членом команды Aggregate Intellect в Торонто в качестве владельца потока обеспечения интерпретируемости машинного обучения. Али Эль-Шариф (Ali El-Sharif) живет в Виндзоре, Онтарио, Канада, со своей женой, тремя детьми, матерью и кошкой. Являясь кандидатом наук Юго-Восточного университета Новы (Nova Southeastern University), Али проводит исследования в области интерпретации машинного обучения. Он также является частью команды Aggregate Intellect в Торонто и возглавляет отдел обеспечения интерпретируемости машинного обучения. Али имеет степень бакалавра в области вычислительного
18 О рецензентах машиностроения и степень магистра Университета штата Райт (Wright State University) в Дейтоне, штат Огайо. Он также имеет степень магистра в области информационной безопасности Юго-Восточного университета Нова. В настоящее время Али преподает аналитику данных и информационную безопасность в рамках Школы бизнеса и ИТ Зекельмана (Zekelman School of Business & IT) в колледже Сент-Клер (St. Clair College) в Виндзоре, Онтарио, Канада. Федерико Риверолл (Federico Riveroll) является экспертом в области машинного обучения и науки о данных. Он соучредитель OpenBlender.io и имеет 11-летний опыт работы с такими международными компаниями, как AB InBev. Федерико также работал со стартапами и сотрудничал с университетами над различными проектами ИИ.
Предисловие Из названия данной книги смело можно сделать вывод о том, что она посвящена трем вещам: интерпретации, машинному обучению и языку программирования Python. И их следует перечислять именно в таком порядке важности! — Почему? — возможно, спросите вы. Интерпретируемое машинное обучение, также именуемое объяснимым искуссственным интеллектом (ИИ — explainable AI, XAI), — это постоянно растущее семейство методов, которые можно задействовать для усвоения полезной информации из моделей и для того, чтобы делать их безопасными, объективными и надежными, чего, я надеюсь, мы все хотим для наших моделей. Однако, поскольку ИИ заменяет собой программно-информационное обеспечение (и людей), модели машинного обучения рассматриваются как более "интеллектуальная" его форма. Да, это единицы и нули, но они не являются программноинформационным обеспечением в том смысле, что их логика запрограммирована людьми и действует так, как задумано, по алгоритму. Значит, интерпретация связана с тем, как можно находить в них и их неточностях смысл, а затем исправлять их недостатки, в идеале до того, как они смогут причинить какой-либо вред. Следовательно, интерпретация имеет решающее значение для надежности и этичности моделей. Кроме того, довольно скоро мы даже не будем тренировать модели программно, а станем для этого использовать графические интерфейсы перетаскивания мышью! Таким образом, хоть мы все и любим Python, интерпретация машинного обучения является навыком, который выдержит испытание временем. А пока для подготовки и проведения предварительного анализа данных и затем обучения и внедрения моделей в производство по-прежнему требуется достаточный объем исходного кода, поэтому каждая глава этой книги включает подробные примеры исходного кода на языке Python. Тем не менее эта книга не была предназначена для использования в качестве "кулинарного справочника"(cookbook) по программированию, абстрагированного от области применения решений и понимания оснований для их выбора. Напротив, цель этой книги прямо противоположна указанной. Причина проста: чтобы интерпретируемое машинное обучение было эффективным, вопрос "почему?" должен предшествовать вопросу "как?". В конце концов, интерпретация является ответом на вопрос "почему". По этой причине большинство глав начинаются с миссии ("почему"), за которым следует подход ("как"). После этого цель состоит в выполнении миссии с использо-
20 Предисловие ванием методов (еще больше "как"), преподносимых на протяжении всей главы, и сосредоточением на интерпретации результатов (еще больше "почему"). Наконец, в ней будут приведены практические идеи, которые были усвоены при выполнении этой задачи. Сама книга также хорошо структурирована. Изложение в ней переходит от основ к более продвинутым темам. Все используемые инструменты с открытым исходным кодом созданы самыми передовыми исследовательскими лабораториями, такими как Microsoft, Google и IBM. Это очень обширная область исследований, бóльшая часть которой еще даже не покинула лабораторию и не стала широко использоваться. В этой книге у авторов не было намерения охватить абсолютно все сферы. Наоборот, целевая задача состоит в том, чтобы представить ряд инструментов обеспечения интерпретируемости с глубиной, достаточной для того, чтобы они стали полезными для практиков и многих профессионалов, работающих в области машинного обучения. Часть I книги представляет собой руководство для начинающих по интерпретируемости, охватывающее ее актуальность в бизнесе и выполняющее предварительный анализ ее ключевых аспектов и трудностей. Часть II проинформирует вас о полной коллекции методов интерпетации и способах их применения в различных вариантах использования, будь то для классификации или регрессии, для табличных данных, временны́х рядов, изображений или текста. В части III вы познакомитесь с настройкой моделей и тренировочных данных на интерпретируемость путем уменьшения сложности, ослабления систематического смещения, установления ограничений и повышения надежности. К концу этой книги вы будете использовать методы интерпретации, чтобы лучше понимать модели машинного обучения и совершенствовать их через интерпретируемость. Для кого эта книга предназначена Эта книга предназначена для следующих читателей:  для новичков и студентов, изучающих науку о данных, обладающих основопо- лагающими знаниями машинного обучения и языка программирования Python;  для профессионалов по работе с данными, на которых возлагается все более важная ответственность в объяснении хода работы систем ИИ, которые они разрабатывают и чью работу они сопровождают технически;  для инженеров по машинному обучению и исследователей данных, которые хо- тят расширить свой набор навыков, включив в него новейшие методы интерпретации и способы уменьшения систематических ошибок;  для специалистов по этике ИИ, чтобы углубить свое понимание имплементаци- онных сторон своей работы в целях более эффективного ориентирования этих усилий;
Предисловие 21  для менеджеров проектов ИИ и руководителей компаний, которые хотят вне- дрить интерпретируемое машинное обучение в свои производственные процессы, чтобы соответствовать принципам объективности, подотчетности и прозрачности. Что эта книга охватывает Глава 1 "Интерпретация, интерпретируемость и объяснимость: почему все это важно?" вводит методику интерпретации машинного обучения и связанные с ней понятия, такие как интерпретируемость, объяснимость, модели типа черного ящика и прозрачность, предоставляя для этих терминов определения во избежание двусмысленности. Затем мы подведем фундамент под ценность интерпретируемости машинного обучения для бизнеса. В главе 2 "Ключевые понятия интерпретируемости" используется пример с предсказанием сердечно-сосудистых заболеваний для введения двух фундаментальных понятий (важности признаков и участков решений) и наиболее важных таксономий, используемых для классифицирования методов интерпретации. Мы также подробно расскажем об элементах, препятствующих интерпретируемости машинного обучения, в качестве введения в темы, которые будут изложены в дальнейшем. В главе 3 "Трудности интерпретации" рассматриваются традиционные методы, используемые для интерпретации машинного обучения как для регрессии, так и для классификации, в контексте задачи о предсказании задержки авиарейса. Затем мы рассмотрим ограничения этих традиционных методов и дадим объяснение тому, что делает модели белого ящика имманентно интерпретируемыми и почему мы не всегда можем использовать модели белого ящика. В целях ответа на эти вопросы мы рассмотрим, как достичь компромисса между предсказательной результативностью и модельной интерпретируемостью. Наконец, мы раскроем несколько новых прозрачных, так называемых аквариумных, моделей, которые не пытаются идти на уступки в этом компромиссе. В главе 4 "Основы важности признаков и их влияние" задействуется пример классифицирования очередности рождения с целью обсуждения разных методов определения важности признаков, таких, которые задействуют внутренние параметры модели, и более надежный модельно-агностический метод, именуемый перестановочной важностью признаков. Затем, чтобы донести до читателя маргинальное влияние одного признака на предсказание, мы изучим вопрос визуализирования и интерпретирования графиков частичной зависимости (PDP) и графиков индивидуальных условных ожиданий (ICE). Глава 5 "Модельно-агностические методы глобальной интерпретации" проводит тщательное исследование аддитивных объяснений Шепли (SHAP), основанных на теории игр, с помощью регрессионных моделей топливной эффективности, а затем визуализирует графики условного маргинального распределения накопленных локальных эффектов (ALE). Наконец, мы коснемся глобальных суррогатных моделей, которые при правильном выборе бывают очень точными и эффективными инструментами интерпретации.
22 Предисловие Глава 6 "Модельно-агностические методы локальной интерпретации" охватывает методы локальной интерпретации, объясняющие одно предсказание или их группу. С этой целью в указанной главе рассматривается принцип использования SHAP и локально интерпретируемых модельно-агностических объяснений (LIME) для локальных интерпретаций посредством примера с назначением рейтинга шоколадным плиткам как с табличными, так и с текстовыми данными. Глава 7 "Якорные и контрфактические объяснения" продолжится локальными модельными интерпретациями, но только для классификационных задач. Мы воспользуемся примером с предсказанием риска рецидивизма, чтобы понять, каким образом можно объяснять необъективные предсказания в пригодном для интерпретации человеком ключе. В этой главе рассматриваются якоря, контрфакты и метод контрастивного объяснения (CEM), а также инструмент What-If Tool (WIT). В главе 8 "Визуализация сверточных нейронных сетей" рассматриваются методы интерпретации, которые работают исключительно с моделями на основе сверточных нейронных сетей (CNN), на примере модели — классификаторе фруктов. Разобравшись в том, как CNN учится с помощью активаций, мы изучим несколько градиентных методов атрибуции, таких как карты значимости, Grad-CAM и интегрированные градиенты для отлаживания атрибуции классов. Наконец, мы расширим наше понимание отладки атрибуций с помощью пертурбационных методов атрибуций, таких как окклюзивная чувствительность, LIME и CEM. В главе 9 "Методы интерпретации для многопеременного прогнозирования и анализа чувствительности" представлена задача о прогнозировании автомобильного трафика и создания модели на основе долгой кратковременной памяти (LSTM) с целью изучения принципа использования интегрированных градиентов и SHAP для этой задачи. В данной главе показано, каким образом прогнозирование и неопределенность внутренне связаны, а также осуждается анализ чувствительности — семейство методов, предназначенных для измерения неопределенности результата на выходе из модели относительно ее данных на входе. Мы изучим два таких метода: метод Морриса для факторной приоретизации и метод Соболя для факторной фиксации. В главе 10 "Отбор и конструирование признаков для обеспечения интерпретируемости" рассматривается сложная задача об оптимизации рассылки почты некомерчекой организации для анализа методов фильтрационного отбора признаков, таких как корреляция Спирмена, и вводятся встраиваемые методы, такие как LASSO. Вы откроете для себя оберточные методы, такие как последовательная селекция признаков и гибридные методы, такие как рекурсивное устранение признаков, а также более продвинутые, такие как генетические алгоритмы. Наконец, даже несмотря на то, что конструирование признаков обычно проводится до их отбора, по многим причинам существует своя ценность в проведении предварительного анализа методики конструирования признаков. В главе 11 "Ослабление систематического смещения и причинно-следственный вывод" рассматривается задача об уплате задолженности по кредитной карте, чтобы
Предисловие 23 продемонстрировать использование метрик объективности и визуализаций для обнаружения нежелательной систематической ошибки. Далее в данной главе рассматривается тема его уменьшения посредством методов предобработки, таких как перевесовка, и методов промежуточной обработки, таких как устранение разрозненных воздействий, и методов постобработки, таких как уравненные шансы. Затем мы тестируем процедуры для снижения вероятности дефолта по кредитным картам и используем причинно-следственное моделирование для определения их средних эффектов экспериментальных процедур (ATE) и условных средних эффектов экспериментальных процедур (CATE). Наконец, мы проверяем причинно-следственные допущения и устойчивость оценок. Глава 12 "Монотонные ограничения и настройка моделей на интерпретируемость" продолжается задачей о предсказании риска рецидивизма из главы 7. Мы узнаем, как размещать ограничения при конструировании признаков на стороне данных и монотонные и интеракционные ограничения на стороне модели для обеспечения объективности, а также узнаем о том, как выполнять настройку модели, когда существует несколько целевых задач. В главе 13 "Устойчивость к антагонизму" рассматривается задача об обнаружении, надета ли у человека одноразовая медицинская маска, чтобы охватить сквозное антагонистическое решение. Антагонист может мешать модели намеренно многими способами, но мы фокусируемся на эвазивных атаках, таких как инфинитная норма Карлини и Вагнера и антагонистические заплаты, и даем краткое объяснение других форм атак. Мы поясняем два защитных метода: пространственно-сглаживающую предобработку и антагонистическое обучение. Наконец, мы демонстрируем один метод оценивания устойчивости и один метод сертификации. В главе 14 "Интерпретируемость машинного обучения: что дальше?" обобщается все то, что было изучено в контексте экосистемы методов интерпретации машинного обучения, а затем высказываются мысли в отношении того, что будет дальше! Как получить максимальную отдачу от этой книги Вам понадобится среда Jupyter Notebook с языком Python 3.6+. Можно выполнить одно из следующих действий:  инсталлировать ее на свой компьютер локально через Anaconda Navigator либо с нуля с помощью пакетного менеджера pip;  использовать интерактивные облачные среды, такие как Google Colaboratory, Kaggle, Azure или Amazon Sagemaker. Рекомендации о том, как начинать работу, соответственно будут отличаться, поэтому мы настоятельно рекомендуем вам поискать в Интернете последние инструкции по их настройке.
24 Предисловие Для получения инструкций по установке многих используемых в книге пакетов перейдите в репозиторий Git, в котором обновленные инструкции будут находиться в файлах readme. Мы ожидаем, что они будут время от времени меняться, учитывая, как часто меняются сами пакеты. Мы также протестировали исходный код с указанными в книге версиями, подробно описанными в файле readme, поэтому, если что-то не удастся с более поздними версиями, то убедительно просим вас вместо этого инсталлировать указанную версию. Отдельные главы начинаются с инструкций по установке пакетов в таком виде: !pip install --upgrade nltk lightgbm lime Но в зависимости от того, как была настроена среда приложения Jupyter Notebook, установку пакетов лучше всего выполнять через командную строку либо с помощью среды conda, поэтому мы предлагаем вам адаптировать эти инструкции по инсталляции в соответствии с вашими потребностями. Если вы используете цифровую версию этой книги, то мы советуем вам набирать исходный код самостоятельно либо обращаться к нему через репозиторий GitHub (ссылка приводится в следующем разделе). Это поможет вам избегать любых потенциальных ошибок, связанных с копированием и вставкой исходного кода. Если вы не знакомы с машинным обучением либо являетесь новичком, советуем читать книгу последовательно, поскольку многие концепции подробно описаны только в предыдущих главах. Более продвинутым специалистам машинного обучения, не знакомым с интерпретируемостью, рекомендуем бегло ознакомиться с первыми тремя главами, чтобы получить базовое представление о контексте и усвоить понятия, необходимые для понимания всего остального, но следующие главы читать по порядку. Специалисты высокого уровня, которые уже разбираются в интерпретируемости, могут читать книгу в любом порядке. Что касается исходного кода, то книгу можно читать, не выполняя исходный код одновременно или строго по теории. Но если же вы планируете его выполнять, лучше всего делать это с помощью книги в качестве руководящей помощи в интерпретации результатов и укреплении вашего понимания теории. Во время чтения думайте о том, как можно было бы использовать усвоенные инструменты, и к концу книги, будем надеяться, вы воодушевитесь на применение этих только что полученных знаний на практике! Загрузка файлов с исходным кодом Скачать файлы с образцами исходного кода этой книги можно из репозитория книги на GitHub по адресу https://github.com/PacktPublishing/Interpretable-MachineLearningwith-Python/. В случае появления какого-либо обновления исходного кода он будет обновляться в существующем репозитории GitHub. Там же в файле README.MD вы также сможете найти список требований к оборудованию и программному обеспечению.
Предисловие 25 Загрузка цветных изображений Мы также предоставляем PDF-файл с цветными изображениями снимков экрана/диаграмм, используемых в этой книге. Его можно скачать отсюда: https://static.packt-cdn.com/downloads/9781800203907_ColorImages.pdf1. Используемые условные обозначения В этой книге используется ряд текстовых обозначений. Исходный код в тексте указывает кодовые слова в тексте, имена таблиц базы данных, вводимые пользователем данные и дескрипторы Twitter. Вот пример: "Далее можно натренировать модель антагонистически, сначала инициализировав новый библиотечный класс KerasClassifier устойчивой моделью (robust_model). Блок исходного кода оформляется следующим образом: base_classifier = KerasClassifier(model=base_model, clip_values=(min_, max_)) y_test_mdsample_prob = np.max(y_test_prob[sampl_md_idxs], axis=1) y_test_smsample_prob = np.max(y_test_prob[sampl_sm_idxs], axis=1) Если мы хотим обратить ваше внимание на конкретную часть блока исходного кода, то соответствующие строки или элементы выделяются жирным шрифтом.: robust_classifier = KerasClassifier(model=robust_model, clip_values=(min_, max_)) attacks = BasicIterativeMethod(robust_classifier, eps=0.3,\ eps_step=0.01, max_iter=20) trainer = AdversarialTrainer(robust_classifier, attacks, ratio=0.5) trainer.fit(X_train, ohe.transform(y_train), nb_epochs=30, batch_size=128) Любой ввод или вывод из командной строки записывается следующим образом: $ mkdir css cd $ css Жирный шрифт обозначает новый термин, важное слово или слова, которые вы видите на экране, интернет-ссылки или названия элементов интерфейса программ. Например, слова в меню или диалоговых окнах показываются в тексте следующим образом. Вот пример: "На панели администрирования выберите пункт System Info (Системная информация)". С ОВЕТЫ ИЛИ ВАЖНЫЕ ПРИМЕЧАНИЯ Выглядят вот так. 1 Для читателей книги подготовлен адаптированный файл иллюстраций по адресу: https://zip.bhv.ru/9785977517355.zip. — Прим. ред.
Часть I Введение в интерпретацию машинного обучения В этой части вы узнаете, как важна интерпретируемость в бизнесе, и поймете ее ключевые аспекты и трудности. Эта часть включает следующие главы.  Глава 1. Интерпретация, интерпретируемость и объяснимость: почему все это важно?  Глава 2. Ключевые понятия интерпретируемости.  Глава 3. Трудности интерпретации.
1 Интерпретация, интерпретируемость и объяснимость: почему всё это важно? Мы живем в мире, правила и процедуры которого регулируются данными и алгоритмами. Например, существуют правила разрешения на предоставление кредита или освобождения под залог, правила цензуры сообщений в социальных сетях. Существуют также процедуры, позволяющие определять, какая маркетинговая тактика является наиболее эффективной и какие рентгеновские снимки грудной клетки могут доказывать наличие пневмонии. Вы этого ожидаете, потому что в этом нет ничего нового! Еще совсем недавно подобные правила и процедуры было принято формализировать в программах, учебниках и на бумажных бланках, хотя последнее слово при принятии решения оставалось за людьми. Зачастую это решение полностью зависело от людей, потому что правила и процедуры были жесткими и, следовательно, не всегда применимыми. Всегда были исключения, поэтому для их принятия требовался человек. Например, если вы подадите заявку на ипотеку, ее одобрение будет зависеть от приемлемой и достаточно длительной кредитной истории. Эти данные позволят рассчитать кредитный балл с использованием алгоритма начисления баллов. В свою очередь, у банка есть правила, определяющие величину балла, который является достаточным для желаемой вами ипотеки. Ваш кредитный инспектор может дать ей ход или же отклонить ее. В наши дни финансовые учреждения тренируют нейросетевые модели на тысячах ипотечных результатов с десятками переменных. Эти модели можно использовать для определения вероятной возможности дефолта по ипотеке с допускаемой высокой точностью. Если есть кредитный инспектор, который визирует одобрение или отказ, то это уже не просто руководящий принцип, а алгоритмическое решение. Разве оно может быть неверным? А правильным? Стоит попридержать эту мысль, потому что на протяжении всей книги мы будем изучать ответы на эти и многие другие вопросы!
30 Часть I. Введение в интерпретацию машинного обучения Интерпретировать решения, принимаемые моделью машинного обучения, значит отыскивать в решении смысл, но, кроме того, можно прослеживать его источник и процесс, который его преобразовал. В этой главе рассматриваются интерпретация машинного обучения и связанные с ней понятия, такие как интерпретируемость, объяснимость, модели типа черного ящика и прозрачность. Данная глава предоставляет определения этих терминов, чтобы можно было избежать двусмысленности, и подкрепляет их объяснением ценности интерпретируемости машинного обучения. Вот главные темы, которые мы собираемся осветить:  что такое интерпретация машинного обучения;  понимание разницы между интерпретацией и объяснимостью;  бизнес-аспекты интерпретируемости. Давайте начнем! Технические требования В целях сверки с примерами по ходу чтения этой главы вам понадобится язык Python 3, работающий в среде Jupyter Notebook либо в вашей любимой интегрированной среде разработки (integrated development environment, IDE), такой как PyCharm, Atom, VSCode, PyDev или Idle. Для запуска примеров также потребуются установленные Python-библиотеки requests, BS4, pandas, sklearn, matplotlib и scipy. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/ Interpretable-Machine-Learning-with-Python/tree/master/Chapter01. Что такое интерпретация машинного обучения? Интерпретировать что-то — значит объяснять его смысл. В контексте машинного обучения это "что-то" является алгоритмом. Если конкретнее, то это математический алгоритм, который принимает данные на входе и выдает данные на выходе, как и в любой формуле. Давайте рассмотрим самую базовую из моделей — простую линейную регрессию, модель которой проиллюстрирована в следующем уравнении: ŷ  0  1 x1 . После аппроксимации данных этой моделью ее смысл заключается в том, что предсказания ŷ представляют собой взвешенную сумму признаков x с коэффициентами  . В данном случае существует только один признак x , или предсказательная переменная, а переменная ŷ обычно называется откликом, или целевой переменной. Простая линейно-регрессионная модель объясняет преобразование, которое выполняется на данных x1 на входе для порождения результата ŷ на выходе. Следующий ниже пример может проиллюстрировать эту концепцию подробнее.
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 31 Изучение простой модели предсказания веса Если вы зайдете на веб-страницу, поддерживаемую Калифорнийским университетом, http://wiki.stat.ucla.edu/socr/index.php/SOCR_Data_Dinov_020108_HeightsWeights, то сможете найти ссылку на скачивание набора данных из 25 000 синтетических записей о весе и росте 18-летних юношей и девушек. Мы не будем использовать весь набор данных целиком, а возьмем только образцовую таблицу на самой вебстранице с 200 записями. Мы извлекаем эту таблицу с веб-страницы и выполняем подгонку линейно-регрессионной модели под эти данные. В указанной модели рост используется для предсказания веса индивида. Другими словами, x1 — рост, а ŷ — вес (вернее, масса тела), поэтому формула линейно-регрессионной модели будет следующей: вес  0  1  рост . Исходный код этого примера находится по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter01/WeightPrediction.ipynb. В целях выполнения этого примера вам необходимо установить следующие библиотеки:  requests для получения запроса содержания веб-страницы;  bs4 (Beautiful Soup) для парсинга таблицы с веб-страницы;  pandas для загрузки таблицы в кадр данных;  sklearn (scikit-learn) для подгонки линейно-регрессионной модели и расчета ее ошибки;  matplotlib для визуализации модели;  scipy для проверки корреляции. Сначала необходимо импортировать все библиотеки следующим образом: import math import requests from bs4 import BeautifulSoup import pandas as pd from sklearn import linear_model from sklearn.metrics import mean_absolute_error import matplotlib.pyplot as plt from scipy.stats import pearsonr Библиотека requests используется для получения содержимого веб-страницы следующим образом: url = \ 'http://wiki.stat.ucla.edu/socr/index.php/SOCR_Data_Dinov_020108_HeightsWeights' page = requests.get(url)
32 Часть I. Введение в интерпретацию машинного обучения Затем надо взять это содержимое и извлечь только таблицу с помощью библиотеки BeautifulSoup следующим образом: soup = BeautifulSoup(page.content, 'html.parser') tbl = soup.find("table",{"class":"wikitable"}) Библиотека pandas может превратить содержимое таблицы из исходного источника на языке гипертекстовой разметки (HyperText Markup Language, HTML) в кадр данных, как показано ниже: height_weight_df = pd.read_html(str(tbl))[0][['Height(Inches)', 'Weight(Pounds)']] И вуаля! Теперь у нас есть кадр данных с ростом в дюймах (Heights(Inches)) в одном столбце и весом в фунтах (Weights(Pounds)) в другом. В качестве проверки исправности можно подсчитать число записей. Оно должно равняться 200. Исходный код показан ниже: num_records = height_weight_df.shape[0] print(num_records) Теперь, когда мы подтвердили, что у нас есть данные, необходимо преобразовать их так, чтобы они сочетались со спецификациями модели. Библиотека sklearn принимает их как в NumPy-массиве размерностью (200, 1), поэтому для начала необходимо извлечь числовые ряды Heights(Inches) и Weights(Pounds) из pandas. Следующий шаг — превратить их в NumPy-массив и, наконец, преобразовать их размерность для соответствия (200, 1). Следующий код выполняет все необходимые операции преобразования: x = height_weight_df['Height(Inches)'].values.reshape(num_records, 1) y = height_weight_df['Weight(Pounds)'].values.reshape(num_records, 1) Затем надо инициализировать линейно-регрессионную модель (LinearRegression) библиотеки scikit-learn и выполнить ее подгонку под тренировочные данные: model = linear_model.LinearRegression() _ = model.fit(x,y) Работая в библиотеке scikit-learn, для вывода на экран формулы линейнорегрессионной модели необходимо извлечь коэффициент 0 вертикального сдвига (т. е. y-координату точки пересечения линии регрессии с вертикальной осью системы координат) и коэффициент наклона линии 1 . Вот формула, которая объясняет, как она делает предсказания: print("ŷ =" + str(model.intercept_[0]) + " + " + str(model.coef_.T[0][0]) + " x₁ " Результат: ŷ = -106.02770644878132 + 3.432676129271629 x1 Он говорит о том, что в среднем на каждый дополнительный фунт приходится 3,4 дюйма роста. Однако объяснение алгоритма работы модели — это всего лишь один из способов объяснения данной линейно-регрессионной модели, и это только одна сторона ис-
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 33 тории. Модель не является идеальной, потому что фактические результаты, приведенные в таблице, и предсказанные моделью результаты для тренировочных данных не эквивалентны. Разница между ними называется ошибкой, или остатками. Понимать ошибку в модели можно совершенно по-разному. К примеру, можно применить функцию ошибки, такую как функция средней абсолютной ошибки (mean_absolute_error), используемую для измерения отклонения по модулю между предсказанными и фактическими значениями, как показано в следующем фрагменте исходного кода: y_pred = model.predict(x) mae = mean_absolute_error(y, y_pred) print(mae) Результат: 7.7587373803882205 Средняя абсолютная ошибка 7,8 означает, что в среднем предсказание отстоит на 7,8 фунта от фактической величины, но эта величина ошибки, возможно, не будет интуитивно понятной или в некоторых случаях даже не будет информативной. Визуализация линейно-регрессионной модели может пролить некоторый свет на то, насколько эти предсказания точны на самом деле. Это можно сделать с помощью точечной диаграммы библиотеки matplotlib и наложения линейной модели (синим цветом) и средней абсолютной ошибки (в виде двух параллельных полос серым цветом): plt.scatter(x, y, color='black') plt.plot(x, y_pred, color='blue', linewidth=3) plt.plot(x, y_pred + mae, color='lightgray') plt.plot(x, y_pred - mae, color='lightgray') plt.xlabel('Рост, дюймы') plt.ylabel('Вес, фунты') Если выполнить приведенный выше фрагмент кода, то на выходе будет получен график, показанный на рис. 1.1. Как следует из графика на рис. 1.1, во многих случаях фактические точки находятся на расстоянии 20–25 фунтов от предсказания. Тем не менее средняя абсолютная ошибка может вас дезинформировать, заставив думать, что ошибка всегда ближе к 8. Поэтому крайне важно выполнять визуализацию ошибки модели, чтобы понимать ее распределение. По этому графику можно сказать, что в нашем распределении нет красных флажков, которые выделялись бы, например, как в случае большей разбросанности остатков для одного диапазона роста, чем для других. Поскольку точки на графике распределены более-менее равномерно относительно синей прямой, мы говорим, что распределение величины гомоскедастично. В случае линейной регрессии гомоскедастичность — одно из многих модельных допущений, которые вы должны проверять наряду с линейностью, нормальностью, не-
34 Часть I. Введение в интерпретацию машинного обучения зависимостью и отсутствием мультиколлинеарности (если есть более одного признака). Эти допущения гарантируют, что вы используете модель, которая подходит для выполняемой работы. Другими словами, рост и вес можно объяснить линейной связью, и это объяснение будет неплохим с точки зрения статистики. Рис. 1.1. Линейно-регрессионная модель предсказания веса на основе роста С помощью этой модели мы пытаемся установить факт линейной связи между ростом и весом. Эта ассоциация называется линейной корреляцией. Измерить ее силу можно с помощью коэффициента корреляции Пирсона. Указанный статистический метод измеряет связь между двумя случайными величинами, используя их ковариацию, деленную на их среднеквадратичные отклонения. Ковариация — это число между 1 и 1, причем чем указанное число ближе к нулю, тем слабее связь между величинами. Если это число является положительным, существует положительная связь, а если оно является отрицательным — отрицательная связь. В Python коэффициент корреляции Пирсона можно вычислить с помощью функции pearsonr из библиотеки scipy: corr, pval = pearsonr(x[:,0], y[:,0]) print(corr) Результат: 0.5568647346122992 Данное число является положительным, что неудивительно, т. к. по мере увеличения роста вес тоже имеет тенденцию к увеличению, но оно также ближе к 1, чем к 0, а значит, вес сильно коррелирован. Второе число, произведенное функцией pearsonr, является p-значением, служащим для проверки некорреляции. Если протестировать уровень ошибки на значение менее 5%, можно уверенно сказать, что для этой корреляции существует достаточно оснований: print(pval < 0.05)
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 35 Результат: True Понимание поведения модели и соответстующих обстоятельств ее поведения помогает нам объяснять, почему, собственно говоря, она делает определенные предсказания, и когда она на них не способна. Давайте вообразим, что нас просят объяснить, почему кто-то с ростом 71 дюйм должен весить 134 фунта, но вместо этого весил на 18 фунтов больше. Судя по тому, что мы знаем о модели, это погрешность ошибки, т. к. существует целый ряд обстоятельств, при которых мы не можем ожидать надежности от модели. Что делать, если нас попросят использовать эту модель для предскания веса человека ростом 56 дюймов? Сможем ли мы обеспечить такой же уровень точности? Определенно нет, потому что мы выполняем подгонку модели под данные испытуемых не ниже 63 дюймов. То же самое было бы, если бы нас попросили предсказать вес 9-летнего ребенка, потому что тренировочные данные были предназначены для 18-летних. Несмотря на приемлемые результаты, этот пример модели предсказания веса не был реалистичным. Если вы хотите получать более точные результаты, но, что важнее, достоверно прогнозировать влияние разных факторов на вес человека, вам нужно будет добавить больше переменных. К примеру, можно добавить пол, возраст, рацион и уровень физической активности. Вот где ситуация станет интересней, потому что необходимо обеспечить объективность их включения или не включения. Например, если бы был включен пол, но большинство нашего набора данных состояло бы из мужчин, то как бы вы смогли обеспечить точность для женщин? Такая ситуация называется систематическим смещением вследствие отбора данных. А что, если вес больше связан с образом жизни и такими обстоятельствами, как бедность и беременность, нежели с полом? Если эти переменные не включены, это называется систематическим смещением вследствие пропущенных переменных. И тогда имеет ли вообще смысл включать чувствительную переменную пола с риском добавления в модель систематического смещения? Если у вас есть несколько признаков, и вы их проверили на предмет объективности, можно узнать и объяснить признаки, которые влияют на результативность модели. Мы называем эту процедуру определением важности признаков. Однако, по мере добавления большего числа переменных, мы увеличиваем сложность модели. Парадоксально, но эта проблема касается интерпретации, и мы рассмотрим ее подробнее в следующих главах. А пока ключевым выводом должно быть утверждение, что интерпретация модели во многом связана с объяснением следующего: 1. Можно ли объяснить, что предсказания были сделаны объективно? 2. Можно ли надежно проследить предсказания назад до чего-то или кого-то? 3. Можно ли объяснить, как были сделаны предсказания? Можно ли объяснить ход работы модели? И в конечном счете вопрос, на который мы пытаемся ответить, звучит так: можно мы доверять модели?
36 Часть I. Введение в интерпретацию машинного обучения Три главных понятия интерпретируемого машинного обучения напрямую связаны с тремя приведенными выше вопросами и имеют аббревиатуру FAT, что означает fairness (объективность)1, accountability (подотчетность) и transparency (прозрачность). Если можно объяснить, что предсказания были сделаны без заметного систематического смещения, тогда есть объективность. Если можно объяснить, почему, собственно, модель делает определенные предсказания, тогда есть подотчетность. И если можно объяснить, каким образом были сделаны предсказания и как модель работает, тогда есть прозрачность. С этими понятиями связано много вопросов, вызывающих этические соображения (рис. 1.2). Рис. 1.2. Три главных понятия интерпретируемого машинного обучения Некоторые исследователи и компании расширили показатель FAT, поместив его под более широким зонтиком этического искусственного интеллекта (ИИ), тем самым превратив FAT в FATE (где буква Е в аббревиатуре соответствует английскому слову ethical). Этический ИИ является частью еще более широкого обсуждения темы алгоритмического регулирования и регулирования на основе данных. Тем не менее оба понятия очень сильно пересекаются, поскольку интерпретируемое машинное обучение связано с тем, как в машинном обучении имплементируются принципы FAT и этические вопросы. В этой книге мы будем обсуждать этику именно в таком контексте. Например, глава 13 относится к надежности, безвредности и безопасности. В главе 11 обсуждается тема объективности. 1 Объективность (fairness), согласно Оксфордскому словарю английского языка, — это нейтральное и равное обращение с кем-либо/чем-либо или поведение без фаворитизма или дискриминации. В русском языке указанный термин может переводиться как "справедливость". В книге используется первый вариант, поскольку слово "справедливость" относится к поведению людей, а книга посвящена вычислительным моделям. К тому же термин justice, который тоже переводится как "справедливость", вступает в коллизию и вносит путаницу. — Прим. перев.
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 37 Понимание разницы между интерпретируемостью и объяснимостью Читая первые несколько страниц этой книги, вы, вероятно, кое-что заметили, а именно, что глаголы "интерпретировать" и "объяснять", а также существительные "интерпретация" и "объяснение" использовались взаимозаменяемо. Это неудивительно, учитывая, что интерпретировать — значит толковать, объяснять смысл чего-то. Несмотря на это, родственные термины "интерпретируемость" и "объяснимость" не следует использовать взаимозаменяемо, даже если их часто принимают за синонимы. Что такое интерпретируемость? Интерпретируемость позволяет уяснить, насколько люди, включая экспертов, не относящихся к обсуждаемому предмету, могут понимать причину и следствие, а также данные на входе в модель машинного обучения и на выходе из нее. Сказать, что модель обладает высоким уровнем интерпретируемости, означает, что можно описать ее вывод в доступном для толкования человеком ключе. Другими словами, почему данные на входе в модель дают определенный результат на выходе? Каковы требования и ограничения данных на выходе? Какими границами уверенности обладают предсказания? Или почему одна переменная оказывает более существенное влияние, чем другая? Говоря об интерпретируемости, следует учеть, что подробное описание хода работы модели имеет значение только в той степени, в которой она может объяснять свои предсказания и оправдывать свое применение. В приведенном в этой главе примере вы могли бы заметить, что между ростом и весом человека существует линейная связь, поэтому имеет смысл использовать линейно-регрессионную модель, а не нелинейную. Это можно доказать статистически, потому что участвующие переменные не нарушают допущения линейной регрессии. Даже когда статистика на нашей стороне, все равно следует учесть знания из предметной области, связанной с вариантом использования. В данном конкретном случае наша уверенность опирается на биологию, потому что наши знания физиологии человека не противоречат взаимосвязи между ростом и весом. Осторожно, сложность! Многие модели машинного обучения изначально сложнее понимать просто из-за математики, связанной с внутренним механизмом модели или конкретной архитектурой модели. В дополнение к этому выбирается много вариантов решений, которые могут увеличивать сложность и делать модели менее интерпретируемыми, от выбора набора данных до селекции и конструирования признаков, до выбора вариантов обучения и настройки модели. Эта сложность делает задачу объяснения хода ее работы чрезвычайно непростой. Интерпретируемость машинного обучения является весьма активной областью исследований, поэтому по-прежнему существует много споров о ее точном определении. Указанная полемика включает вопрос о необходимости полной прозрачности, чтобы мы имели возможность квалифициро-
38 Часть I. Введение в интерпретацию машинного обучения вать модель машинного обучения как достаточно интерпретируемую. В этой книге отдается предпочтение пониманию того, что определение понятия "интерпретируемость" не должно с неизбежностью исключать непрозрачные модели, которые по большей части являются сложными, если выбор не ставит под угрозу их надежность. Этот компромисс обычно называется постфактумной интерпретируемостью (post-hoc interpretability). В конце концов, невозможно объяснить точно, каким образом человеческий мозг, во многом подобный сложной модели машинного обучения, делает выбор, но мы часто доверяем его решению, потому что можем попросить человека рассказать о своем ходе рассуждений. Постфактумная интерпретация машинного обучения — это в точности то же самое, за исключением того, что здесь рассуждения объясняются человеком от имени модели. Использование конкретного понятия интерпретируемости выгодно тем, что можно интерпретировать непрозрачные модели и не жертвовать точностью наших предсказаний. Мы обсудим эту тему подробнее в главе 3. Когда важна интерпретируемость? Системы принятия решений не всегда требуют интерпретируемости. В исследованиях предлагаются два случая в качестве исключений.  Когда неправильные результаты не имеют существенных последствий. Напри- мер, что делать, если модель машинного обучения, натренированная находить и читать почтовый индекс на бандероли, иногда неправильно его читает и отправляет бандероль по другому адресу? Вероятность дискриминационного смещения невелика, а стоимость ошибочной классификации относительно низкая. Такие ситуация встречаются не слишком часто, чтобы можно было увеличивать стоимость.  Когда есть последствия, но они достаточно изучены и подтверждены в реальном мире, чтобы можно было принимать решения без участия человека. Так обстоит дело с системой оповещения о воздушном движении и предупреждения столкновений в воздухе (traffic-alert and collision-avoidance system, TCAS), которая предупреждает пилота другого самолета, который представляет угрозу столкновения в воздухе. С другой стороны, интерпретируемость необходима для того, чтобы эти системы имели следующие атрибуты.  Пригодность для получения научных знаний: метеорологам есть чему по- учиться на основе климатической модели, но только если ее легко интерпретировать.  Надежность и безвредность: решения, принимаемые беспилотным транспорт- ным средством, должны быть доступны для отлаживания, чтобы его разработчики могли понимать точки отказа.  Этичность: в модели перевода с языка на язык могут использоваться гендерно- смещенные вложения слов, которые приводят к дискриминационным переводам, но вы должны уметь легко находить эти примеры и исправлять их. Однако сис-
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 39 тема должна быть спланирована так, чтобы она могла извещать вас о проблеме до момента ее выпуска в публичное пространство.  Убедительность и выверенность: иногда модели машинного обучения могут иметь неполные и взаимоисключающие целевые задачи; например, система контроля холестерина может не учитывать степень вероятности того, что пациент действительно будет придерживаться диеты или режима приема лекарственных препаратов, либо между одной целевой задачей и другими, такими как безвредность и отсутствие дискриминации, может существовать компромисс. Объясняя решения модели, можно устранять пробелы нашего понимания задачи — ее неполноту. Одна из наиболее важных проблем заключается в том, что, учитывая высокую точность наших технических решений в области машинного обучения, мы склонны повышать уровень нашей уверенности до такой степени, что считаем, будто полностью понимаем задачу. И тогда мы оказываемся дезориентированными, полагая, что наше решение охватывает ее целиком! В начале этой книги мы затронули вопрос о том, что привлечение данных для порождения алгоритмических правил не является чем-то новым. Однако раньше мы судили об этих правилах задним числом, а теперь не можем. Следовательно, раньше был ответственен человек, а теперь алгоритм. В данном случае алгоритмом является модель машинного обучения, которая отвечает за все этические последствия, которые она влечет за собой. Указанный поворот во многом связан с точностью. Проблема в том, что, хотя модель машинного обучения может превосходить человеческую точность в целом, она еще должна интерпретировать свои результаты так, как это делал человек. Следовательно, она не судит о своих решениях постфактум, поэтому в качестве решения ей не хватает желаемого уровня полноты, и именно по этой причине нам нужно интерпретировать модели так, чтобы иметь возможность закрывать хотя бы часть этого пробела. Тогда почему интерпретация машинного обучения до сих пор еще не является стандартной частью конвейера? Дело в том, что в дополнение к нашему уклону в сторону сосредоточенности лишь на точности одним из самых больших препятствий является устращающая концепция моделей типа черного ящика. Что такое модели типа черного ящика? Это просто еще один термин для непрозрачных моделей. Черный ящик обозначает систему, в которой известны только входы и выходы и нет возможности увидеть, что именно преобразовывает входы в выходы. В случае машинного обучения модель типа черного ящика может быть открыта, но ее механизмы нелегко понять. Что такое модели типа белого ящика? Они противоположны моделям типа черного ящика (рис. 1.3). Их также называют прозрачными, поскольку они достигают полной или почти полной прозрачности интерпретации. В этой книге мы называем их имманентно интерпретируемыми и рассмотрим их подробнее в главе 3.
40 Часть I. Введение в интерпретацию машинного обучения Взгляните на сравнение этих моделей. Рис. 1.3. Визуальное сравнение моделей типа типа белого ящика и типа черного ящика Что такое объяснимость? Объяснимость охватывает все сущности, входящие в сферу интерпретируемости. Разница заключается в том, что она уходит глубже в требование прозрачности, чем интерпретируемость, т. к. требует удобных для человека объяснений внутреннего механизма работы модели и процесса обучения модели, а не только выведения модельного результата. В зависимости от приложения это требование может распространяться на различные степени модельной, конструктивной и алгоритмической прозрачности. Указанные три типа прозрачности описаны далее.  Модельная прозрачность — возможность объяснять, как выполняется пошаго- вое обучение модели. В случае нашей простой модели предсказания веса можно объяснить, как метод оптимизации, именуемый обычными наименьшими квадратами, находит коэффициент  , который минимизирует ошибки в модели.  Конструктивная прозрачность — возможность объяснять выбираемые вари- анты, такие как модельная архитектура и гиперпараметры. Например, мы могли бы обосновать эти варианты выбора на основе размера или природы тренировочных данных. Если бы мы прогнозирвали продажи и знали, что у них сезонность составляет 12 месяцев, то это могло бы быть правильным вариантом выбора параметров. В случае сомнений для отыскания правильной сезонности мы всегда могли бы использовать какой-нибудь хорошо зарекомендовавший себя статистический метод.  Алгоритмическая прозрачность — возможность объяснять автоматизирован- ные оптимизации, такие как поиск по сетке (grid search) гиперпараметров; но обратите внимание, что те, которые невозможно воспроизвести из-за их случайной природы, такие как случайный поиск гиперпараметрической оптимизации, ранняя остановка и стохастический градиентный спуск, делают алгоритм непрозрачным.
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 41 Непроницаемые модели называются непроницаемыми просто потому, что им не хватает модельной прозрачности, но для многих моделей это неизбежно, каким бы оправданным ни был выбор модели. Во многих сценариях, даже если вывести математику, участвующую, скажем, в обучении нейронной сети или случайного леса, она будет вызывать больше сомнений, чем доверие. Для этого есть по меньшей мере несколько причин, которые изложены ниже:  Статистическая необоснованность: непроницаемый процесс обучения модели соотносит данные на входе с оптимальными данными на выходе, основываясь на произвольных значениях параметров. Эти параметры были найдены путем оптимизации функции стоимости, но не основаны на статистической теории.  Неопределенность и невоспроизводимость: когда вы выполняете обучение прозрачной модели под одинаковые данные, вы всегда получаете одинаковые результаты. С другой стороны, непроницаемые модели не являются одинаково воспроизводимыми, потому что для инициализации своих весов или для регуляризации или оптимизации своих гиперпараметров в них используются случайные числа или задействуется стохастическая избирательность (так обстоит дело, например, с алгоритмом случайного леса).  Переобучение и проклятие размерности: многие из этих моделей оперируют на высокоразмерном пространстве. Это не вызывает доверия, потому что труднее делать обобщения на более крупное число размерностей. В конце концов, имеется тем больше риска для переобучения, чем больше размерностей вы добавляете.  Человеческое познание и проклятие размерности: прозрачные модели часто используются для малых наборов данных с меньшим числом размерностей, и даже если они не являются прозрачными, в них никогда не используется больше размерностей, чем необходимо. Они также, как правило, не усложняют взаимодействие между этими размерностями больше, чем это необходимо. Это отсутствие ненужной сложности облегчает визуализацию того, что модель делает, и ее исходов. Люди не особо справляются с комплексным учетом многочисленных факторов, поэтому использование прозрачных моделей, как правило, облегчает их понимание.  Бритва Оккама: это правило называется принципом простоты, или бережливо- сти. В нем говорится, что самое простое решение обычно является правильным. Правда это или нет, но люди тоже склонны к простоте, и прозрачные модели известны — если что — своей простотой. Почему и когда объяснимость имеет важное значение? Заслуживающее доверия и этичное принятие решений являются главными мотивами для интерпретируемости. Объяснимость имеет дополнительные мотивы, такие как причинно-следственная связь, переносимость и информативность. Поэтому существует много вариантов использования, в которых ценится полная или почти полная прозрачность, и это правильно. Некоторые из них описаны ниже:  Научные исследования: воспроизводимость имеет важное значение для науч- ного метода. Кроме того, использовать статистически обоснованные методы оп-
42 Часть I. Введение в интерпретацию машинного обучения тимизации особенно желательно, когда необходимо доказать причинно-следственную связь.  Клинические испытания: они также должны давать воспроизводимые резуль- таты и быть статистически обоснованными. В дополнение к этому, учитывая потенциальную тяжесть последствий переобучения, в них должны использоваться наименьшие возможные размерности и модели, которые их не усложняют.  Верификация безвредности потребительских товаров: как и в случае клини- ческих испытаний, когда речь идет о безвредности, касающейся жизни и смерти, простота по возможности бывает предпочтительнее.  Общественная политика и право: это более узкая тема обсуждения в рамках того, что ученые в области правоведения называют алгоритмическим регулированием, и они проводят различие между авквариумной прозрачностью и обоснованной прозрачностью. Первая из них ближе к строгости, необходимой для верификации безвредности потребительских товаров, а вторая — там, где будет достаточно постфактумной интерпретируемости. Когда-нибудь правительство будет полностью управляться алгоритмами. Сейчас трудно сказать, какая политика будет соответствовать какой форме прозрачности, но существует много областей общественной политики, таких как уголовное правосудие, где необходима абсолютная прозрачность. Однако всякий раз, когда полная прозрачность противоречит целевым задачам обеспечения конфиденциальности или безопасности, должна применяться менее строгая форма прозрачности.  Уголовное расследование и аудит соответствия регуляторным требовани- ям. Если что-то пойдет не так, например, произойдет авария на химическом заводе, вызванная неисправностью робота, или авария на автономном транспортном средстве, то следователю необходимо проследить цепочку принятых решений. Это делается для того, чтобы "облегчить возложение процедурной и юридической ответственности". Даже если никаких несчастных случаев не произошло, этот вид аудита может проводиться по требованию властей. Аудит соответствия требованиям применяется к регулируемым отраслям, таким как финансовые и коммунальные услуги, транспорт и здравоохранение. Во многих случаях "аквариумная" прозрачность является предпочтительной. Деловое обоснование интерпретируемости В этом разделе описывается несколько практических деловых выгод интерпретируемости машинного обучения, таких как более качественные решения, а также большее доверие, этичность и прибыльность. Более качественные решения Как правило, модели машинного обучения тренируются, а затем оцениваются в отношении желаемых метрик. Если они проходят контроль качества на тестовом наборе данных (т. е. на множестве данных, которые не использовались при обуче-
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 43 нии), они развертываются в производстве. Однако после проверки в реальных условиях всё принимает буйный характер, как в следующих ниже гипотетических сценариях.  Алгоритм высокочастотной торговли может в одиночку разрушить фондовый рынок.  Сотни устройств для умного дома могут необъяснимо разразиться необуздан- ным хохотом, приводя в ужас своих пользователей.  Системы распознавания номерных знаков могут начать считывать номерные знаки нового типа неправильно и штрафовать не тех водителей.  Расово смещенная система наблюдения может неправильно обнаружить зло- умышленника, и из-за этого охранники выстрелят в невинного офисного служащего.  Самоходный автомобиль может принять снег за тротуар, врезаться в скалу и травмировать пассажиров. Любая система подвержена ошибкам, поэтому нельзя сказать, что интерпретируемость есть средство от всех ошибок. Однако концентрация на простой оптимизации метрик может стать причиной катастрофы. В лабораторных условиях модель может и будет хорошо обобщать, но если вы не знаете, почему модель принимает те или иные решения, тогда существует вероятность упустить шанс ее улучшения. Например, знать, что именно самоходный автомобиль считает дорогой, недостаточно, но знания, как он это делает, помогут в совершенствовании модели. Если, скажем, одной из причин была светлая дорога, как снег, то это могло представлять опасность. Проверка допущений и выводов модели может приводить к улучшению модели путем введения в набор фотоснимков зимних дорог или потоковой подачи в модель реально-временны́х данных о погоде. Кроме того, если это не сработает, возможно, алгоритмическая отказоустойчивость способна помешать ей осуществлять действия по принятому решению, в котором она не совсем уверена. Одна из главных причин, по которой акцент на интерпретируемости машинного обучения приводит к более качественному принятию решений, была упомянута ранее, когда мы говорили о полноте. Если вы считаете, что модель является полной, то какой смысл делать ее лучше? Более того, если вы не подвергаете рассуждения модели сомнению, тогда ваше понимание задачи должно быть полным. Если это так, то, возможно, вам вообще не следует использовать машинное обучение для решения задачи! Машинное обучение создает алгоритм, который в иной ситуации было бы слишком сложно запрограммировать с использованием инструкций ifelse, именно для использования в тех случаях, когда наше понимание задачи является неполным! Оказывается, когда мы что-то предсказываем или оцениваем, в особенности с высокой степенью точности, мы полагаем, что имеем над этим контроль. Это как раз и называется систематическим смещением вследствие иллюзии контроля. Нельзя недооценивать сложность задачи только потому, что в совокупности модель почти все время выполняет ее правильно. Разница между снегом и бетонным покрытием
44 Часть I. Введение в интерпретацию машинного обучения бывает размытой и трудно объяснимой даже для человека. Откуда вообще начинать описывать это различие в таком ключе, чтобы она всегда была точной? Модель может усваивать эти различия, но это не делает ее менее сложной. Проверка модели на предмет точек отказа и постоянное наблюдение за выбросами требует другого взгляда, в соответствии с которым мы признаем, что не способны контролировать модель, но можем попытаться понять ее с помощью интерпретации. Ниже приведены несколько дополнительных систематических смещений при принятии решений, способные влиять на модель негативно и служить причинами, по которым интерпретируемость может приводить к более качественному принятию решений.  Систематическое смещение вследствие консерватизма. При получении новой информации мы не меняем своих прежних мнений. С таким систематическим смещением укоренившаяся ранее существовавшая информация одерживает верх над новой информацией, но модели должны эволюционировать. Следовательно, подход, который предпочитает ставить предыдущие допущения под сомнение, является здравым.  Систематическое смещение вследствие заметности. Некоторые выступающие наружу или более заметные детали могут выделяться больше других, но, говоря статистически, им следует уделять равное внимание наряду с другими. Это систематическое смещение может влиять на наш выбор признаков, поэтому менталитет на основе интерпретируемости может расширять наше понимание задачи за счет включения в нее других, менее воспринимаемых, признаков.  Фундаментальная ошибка атрибуции. Это систематическое смещение застав- ляет нас приписывать исходы поведению, а не обстоятельствам, характеру, а не ситуациям, природе, а не формированию/воспитанию. Интерпретируемость требует от нас проведения более глубокого исследования и поиска менее очевидных взаимосвязей между нашими переменными или теми, которые могут быть пропущены. Одной из важнейших выгод от интерпретации моделей является локализация выбросов. Выбросы, как аномальные значения, бывают потенциальным новым источником активов или пассивов, которые могут происходить в любой момент. Знание этого факта помогает нам соответствующим образом подготавливаться и вырабатывать стратегию. Более надежные бренды Доверие определяется как вера в надежность, способность или авторитетность чего-либо или кого-либо. В контексте организаций доверие — это ее репутация; и в неумолимом суде общественного мнения требуется всего один-единственный неуспешный случай, разногласие или фиаско, чтобы значительное доверие общественности было утрачено. А это, в свою очередь, может приводить к ослабеванию доверия инвесторов.
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 45 Давайте посмотрим, что случилось с Boeing после крушения 737 MAX или с Facebook после скандала с президентскими выборами 2016 года. В обоих случаях были приняты недальновидные решения исключительно с целью оптимизации одной метрики, будь то прогнозные продажи самолетов или продажи цифровой рекламы. Они недооценили известные потенциальные точки отказа и совершенно упустили очень большие точки. После этого нередко ситуация ухудшается, когда организации прибегают к ложной аргументации, чтобы оправдывать свои рассуждения, дезориентировать общественность или отвлечь внимание СМИ на что-то другое. Такое поведение может приводить к дополнительным промахам в связях с общественностью. Компании не только теряют авторитетность в том, что они делают со своей первой ошибкой, но и пытаются обманывать людей, теряя авторитетность в том, что говорят их представители. И это были примеры решений, принимаемых по большей части людьми. С решениями, принимаемыми исключительно моделями машинного обучения, все может ухудшиться, потому что легко бросить мяч и удерживать подотчетность на стороне модели. Например, если в вашей ленте Facebook стали появляться оскорбительные материалы, то Facebook мог бы сказать, что это потому, что его модель была натренирована на ваших данных, таких как ваши комментарии и лайки, поэтому социальная сеть на самом деле отражает то, что вы хотите увидеть. Это не ее вина, а ваша. Если полиция нацелилась на ваш район для проведения агрессивной охраны общественного порядка, так это потому, что она использует PredPol — алгоритм, который предсказывает, где и когда произойдут преступления, и представители охраны правопорядка могут обвинить в этом алгоритм. С другой стороны, создатели этого алгоритма могут обвинить полицию в том, что программно-информационное обеспечение было натренировано на их полицейских отчетах. Это генерирует потенциально тревожный и порочный цикл обратной связи, не говоря уже о разрыве в подотчетности. И если некоторые пранкеры или хакеры устранят разметку полос, то это может привести к тому, что беспилотный автомобиль Tesla свернет на неправильную полосу. Разве это вина Tesla в том, что разработчики не предвидели такой возможности, или хакеров, которые бросили гаечный ключ в эту модель? Это называется антагонистической атакой, и мы обсудим эту тему в главе 13. Несомненно, одна из целей интерпретируемости машинного обучения как таковой состоит в том, чтобы делать модели качественнее при принятии решений. Но даже когда они отказывают, можно продемонстрировать, что усилия прилагалсь. Доверие теряется не полностью из-за самого отказа, а из-за отсутствия подотчетности, и даже в тех случаях, когда принимать всю вину на себя было бы необъективно, некоторая подотчетность (или ответственность) лучше, чем никакая. Например, в предыдущем наборе примеров Facebook мог бы поискать подсказки о том, почему оскорбительные материалы показываются чаще, а затем посвятить себя поиску способов делать это реже, даже если это принесет меньше денег. Пользователи алгоритма PredPol могли бы найти другие источники наборов данных о преступности, которые потенциально менее смещены, даже если они меньше. Они также могут использовать методы для устранения систематического смещения в существующих наборах данных (см. главу 11). И Tesla может провести аудит своих систем на
46 Часть I. Введение в интерпретацию машинного обучения предмет антагонистических атак, даже если это задержит старт продаж автомобилей. Все эти решения касаются интерпретации. После того как это станет обычной практикой, они могут привести к повышению доверия не только общественности, например со стороны пользователей и клиентов, но и внутренних интересантов, таких как сотрудники и инвесторы. На рис. 1.4 показано несколько промашек ИИ в связях с общественностью, которые произошли за последние пару лет. Рис. 1.4. Инфографика института AI Now с промашками искусственного интеллекта в связях с общественностью за 2019 год Из-за проблем с доверием многие технологии, основанные на искусственном интеллекте, теряют общественную поддержку в ущерб как компаниям, монетизирующим ИИ, так и пользователям, которые могли бы извлекать из них выгоду (см. рис. 1.4). Это требует правовой базы отчасти на национальном или глобальном уровне, а также большей подотчетности в организационном плане для тех, кто внедряет эти технологии. Более высокий уровень этичности Существует три школы этики: утилитаристы сосредоточены на последствиях, деонтологи озабочены долгом, а телеологи больше интересуются совокупным мораль-
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 47 ным характером. Таким образом, это означает, что существуют разные способы изучения этических проблем. Например, из них всех можно извлекать полезные уроки. Бывают случаи, когда хочется произвести наибольшее количество "добра" несмотря на то, что по ходу причиняется и некоторый вред. В отдельных случаях этические границы должны трактоваться как линии на песке (иными словами, жесткие границы), которые нельзя пересекать. В других ситуациях речь идет о развитии праведного нрава, к чему стремятся многие религии. Независимо от этических воззрений, которых мы придерживаемся, наше представление о них со временем эволюционирует, потому что оно отражает наши текущие ценности. На данный момент в западных культурах к этим ценностям относятся следующие:  благосостояние человека;  владение и право собственности;  конфиденциальность;  свобода от предвзятости;  универсальное удобство;  доверие;  автономность;  информированное согласие;  подотчетность;  вежливость;  экологическая стабильность. Этические нарушения — это случаи, когда вы пересекаете моральные границы, которые указанные ценности стремятся поддерживать, будь то дискриминация по отношению к кому-либо или загрязнение окружающей среды, независимо от того, противоречит это закону или нет. Этические дилеммы возникают, когда у вас есть выбор между вариантами, которые приводят к нарушениям, поэтому вам приходится выбирать между одним и другим. Первая причина, по которой машинное обучение связано с этикой, заключается в том, что технологии и этические дилеммы имеют имманентно связанную историю. Начиная с первого широко распространенного созданного людьми инструмента, технологии несли прогресс, но также наносили вред; примерами могут служить несчастные случаи, войны и потери рабочих мест. Это не значит, что технологии всегда плохи, но нам не хватает предусмотрительности измерять и контролировать их последствия во временно́й динамике. В случае с ИИ совершенно не ясно, каковы вредные долгосрочные последствия. Чего можно ожидать, так это серьезной потери рабочих мест и огромного спроса на энергию для питания наших центров обработки данных, что может нанести вред окружающей среде. Существуют соображения, что ИИ может создать "алгократическое" надзорное государство, управляемое алгоритмами, нарушая такие ценности, как конфиденциальность, автономия и право собственности.
48 Часть I. Введение в интерпретацию машинного обучения Вторая причина еще более значима, чем первая. Дело в том, что машинное обучение технологически в первую очередь служит человечеству: машинное обучение — это технология, которая способна принимать решения за нас, и эти решения могут приводить к отдельным этическим нарушениям, которые трудно отслеживать. Проблема в том, что подотчетность необходима для соблюдения морально-нравственных принципов, потому что вы должны знать, кого винить в проявлении человеческого достоинства, возмещении ущерба, разрыве отношений или уголовном преследовании. Однако во многих технологиях проблемы с подотчетностью возникают с самого начала, потому что морально-нравственная подотчетность, понимаемая как ответственность, так или иначе, нередко является совместной. Например, возможно, причина автокатастрофы была вызвана частично водителем, механиком и производителем автомобилей. То же самое может произойти и с моделью машинного обучения, за исключением того, что она становится сложнее. В конце концов, получается программирование модели "без программиста", потому что "программирование" было усвоено из данных, и есть вещи, усваиваемые моделью из данных, которые могут приводить к этическим нарушениям. Главными среди них являются такие систематические смещения, как:  систематическое смещение вследствие отбора данных (sample bias), когда ваши данные (выборка) точно не отражают окружающую действительность, также именуемую популяцией;  систематическое смещение вследствие изъятия (exclusion bias), когда вы опускаете признаки или группы, которые в ином случае могли бы объяснять критически важное явление;  систематическое смещение вследствие предрасудков (prejudice bias), когда на ваши данные прямо или косвенно влияют стереотипные отношения;  систематическое смещение вследствие измерения (measurement bias), когда неправильные измерения искажают ваши данные. Интерпретируемость оказывается весьма кстати для ослабления систематического смещения, как будет видно из главы 11, или даже установления ограждений именно на тех признаках, которые могут быть источником систематического смещения. Об этом говорится в главе 12. Как указывалось ранее в этой главе, объяснения играют важную роль в формировании подотчетности, которая является моральным императивом. Кроме того, изучая лежащие в основе моделей утверждения, можно отыскивать этические проблемы до того, как они причинят какой-либо вред. Но существует еще больше способов, которыми можно держать потенциально тревожные этические последствия моделей под контролем, и это связано не столько с интерпретируемостью, сколько с дизайном. Существуют такие каркасы, как человекоцентрированный дизайн, ценностно-чувствительный дизайн и этика техноморальных добродетелей, которые можно использовать для встраивания этических соображений в каждый выбираемый вариант технологического дизайна. Статья Кирстен Мартин (Kirsten Martin, https://doi.org/10.1007/s10551-018-3921-3) также предлагает конкретный каркас для алгоритмов. В данной книге мы не будем углубляться слишком уж сильно в аспекты дизайна алгоритмов, но для тех читате-
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 49 лей, которых интересует более широкий спектр этического ИИ, указанная выше статья является отличным отправным пунктом. Предлагаемая Мартин модель моральности алгоритмов приведена на рис. 1.5. Рис. 1.5. Модель моральности алгоритмов от Кирстен Мартин Организации должны серьезно относиться к этике алгоритмического принятия решений, потому что этические нарушения имеют денежные и репутационные издержки. Но помимо этого предоставленный самому себе ИИ может подорвать те самые ценности, подкрепляющие демократию и экономику снизу, которые позволяют бизнесу процветать. Более высокая прибыльность Как уже отмечалось в этой главе, интерпретируемость улучшает алгоритмические решения, повышая доверие и смягчая этические нарушения. Когда вы используете ранее неизвестные возможности и смягчаете угрозы, такие как случайные сбои, за счет более эффективного принятия решений, вы можете только улучшить конечный результат; и если вы повышаете доверие к технологии, основанной на ИИ, вы можете только увеличить ее использование и повысить общую репутацию бренда, что также благотворно влияет на прибыль.С другой стороны, что касается этических нарушений, то они могут быть либо преднамеренными, либо случайными, но когда их обнаруживают, они влияют одинаково негативно как на прибыль, так и на репутацию. Когда предприятия встраивают интерпретируемость в свои рабочие потоки машинного обучения, рабочий цикл становится эффективным и приводит к более высокой прибыльности. В случае некоммерческой организации или правительства прибыль может и не быть мотивом. И, тем не менее, финансы, несомненно, здесь участвуют,
50 Часть I. Введение в интерпретацию машинного обучения потому что судебные процессы, никуда не годное принятие решений и запятнанная репутация обходятся дорого. В конечном счете технический прогресс зависит не только от технических и научных навыков и материалов, которые делают его возможным, но и от его добровольного принятия широкой общественностью. Резюме Прочитав эту главу, вы должны теперь четко понимать, что такое интерпретация машинного обучения, и чем она не является, и признавать важность интерпретируемости. В следующей главе мы узнаем о том, что может делать модели машинного обучения столь сложными для интерпретации и как классифицировать методы интерпретации по категориям и по диапазону. Источники изображений  Varoon Mathur. AI in 2019: A year in review — the growing pushback against harm- ful AI. AI Now Institute via Medium URL: Mathur, Varoon, 2019, AI in 2019: A Year in Review — The Growing Pushback Against Harmful AI. AI Now Institute via Medium. — URL: https://medium.com/@AINowInstitute/ai-in-2019-a-year-inreview-c1eba5107127. (Варун М. ИИ в 2019 году: год в обзоре — растущая оппозиция вредному ИИ.)  Martin K. Ethical implications and accountability of algorithms // Journal of Business Ethics. — 2019. — № 160. — P. 835–850. — URL: https://doi.org/10.1007/s10551018-3921-3. (Мартин К. Этические последствия и подотчетность алгоритмов.) Справочные материалы  Microsoft. Responsible AI principles from Microsoft). 2019. — URL: https://www.microsoft.com/en-us/ai/responsible-ai. (Microsoft. Ответственные принципы ИИ от Microsoft.)  Lipton Zachary. The Mythos of Model Interpretability: in machine learning, the con- cept of interpretability is both important and slippery. — URL: https://doi.org/10.1145/3236386.3241340. (Липтон З. Мифы об интерпретируемости моделей.)  Doshi-Velez F., Kim B. Towards A Rigorous Science of Interpretable Machine Learn- ing // arXiv:1702.08608. — 2017. — URL: http://arxiv.org/abs/1702.08608. (ДошиВелес Ф., Ким Б. К строгой науке интерпретируемого машинного обучения.)  Roscher R., Bohn B., Duarte M.F., Garcke J. Explainable Machine Learning for Sci- entific Insights and Discoveries // IEEE Access. — 2020. — № 8. — P. 42200– 42216. — URL: https://dx.doi.org/10.1109/ACCESS.2020.2976199. (Рошер Р., Бон
Глава 1. Интерпретация, интерпретируемость и объяснимость: почему всё это важно? 51 Б., Дуарте М. Ф., Гарке Дж. Объяснимое машинное обучение для научного понимания и открытий.)  Coglianese C., Lehr D. Transparency and algorithmic governance // Administrative Law Review. — 2019. — № 71. — P. 1–4). — URL: https://ssrn.com/abstract=3293008. (Кольянезе К., Джеф Д. Прозрачность и алгоритмическое регулирование.)  Weller Adrian. Transparency: Motivations and Challenges // arXiv:1708.01870 [Cs]. — 2019. — URL: http://arxiv.org/abs/1708.01870. (Уэллер А. Прозрачность: мотивы и вызовы.)
2 Ключевые понятия интерпретируемости В этой книге рассмотрены многочисленные методы модельной интерпретации: одни создают метрики, другие — визуальные эффекты, а третьи — и то и другое; некоторые изображают вашу модель в общих чертах, а другие — детально. В этой главе мы узнаем о двух методах: важности признаков и участков принятия решений, а также о таксономиях, используемых для описания этих методов. В качестве введения в темы, которые будут изложены далее, мы также подробно изучим элементы, препятствующие интерпретируемости машинного обучения. Вот главные темы, которые будут освещены в этой главе:  типы методов интерпретации и диапазоны интерпретируемости;  оценивание препятствий, мешающих интерпретируемости машинного обучения. Технические требования Хотя мы начали книгу с "игрушечного примера", на протяжении всей книги мы будем задействовать реально существующие наборы данных, чтобы использовать их в конкретных случаях модельной интерпретации. Они взяты из разных источников и часто используются только один раз. Во избежание ситуации, когда читателям приходится тратить много времени на скачивание, загрузку и подготовку наборов данных для одноразовых примеров, мы предлагаем библиотеку mldatasets, которая берет на себя бо́льшую часть этой работы. В дополнение к библиотеке mldatasets в примерах этой главы также используются библиотеки pandas, numpy, statsmodel, sklearn и matplotlib. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/InterpretableMachine-Learning-with-Python/blob/master/Chapter02/CVD.ipynb. Миссия Вообразите, что вы являетесь аналитиком национального Министерства здравоохранения, как раз в этот момент резко возросло количество случаев сердечнососудистых заболеваний (ССЗ). Министр здравоохранения считает приоритетным обратить этот рост вспять и снизить число случаев ССЗ до минимума, отмеченного за последние 20 лет. С этой целью была создана оперативная рабочая группа для анализа данных, чтобы установить следующее: 1. На какие факторы риска стоит обратить внимание?
Глава 2. Ключевые понятия интерпретируемости 53 2. Если есть возможность предсказывать будущие случаи ССЗ, то интерпретировать предсказания по каждому конкретному случаю. Вы вошли в состав этой оперативной рабочей группы! Подробности о сердечно-сосудистых заболеваниях Прежде чем углубиться в данные, необходимо собрать несколько важных подробностей о сердечно-сосудистых заболеваниях, чтобы:  понять контекст и релевантность задачи;  извлечь информацию, касающуюся знаний предметной области, которая помо- жет в анализе данных и модельной интерпретации;  соотнести полученную от экспертов информацию с признаками набора данных. Сердечно-сосудистые заболевания — это группа расстройств, наиболее распространенным из которых является коронарное заболевание сердца (ишемическая болезнь сердца). По данным Всемирной организации здравоохранения, сердечнососудистые заболевания являются ведущей причиной смерти во всем мире, приводя к летальному исходу около 18 млн человек ежегодно. Ишемическая болезнь сердца и инсульт являются наиболее значимыми способствующими ему факторами. По оценкам, 80% сердечно-сосудистых заболеваний связаны с модифицируемыми факторами риска. Другими словами, причиной ССЗ является несколько предотвратимых факторов, а именно:  плохое питание;  курение и употребление алкоголя;  ожирение;  отсутствие физической активности;  плохой сон. Однако многие факторы риска не подлежат модифицированию и, следовательно, признаются как неизбежные; среди них:  генетическая предрасположенность;  старость;  мужской пол (варьируется в зависимости от возраста). Мы не будем вдаваться в медицинские подробности ССЗ, т. к. они не требуются для четкого понимания примера. Правда, следует отметить, что для модельной интерпретации невозможно переоценить важность предметных знаний. Так что, если бы рассматриваемый пример был вашей работой, и многие жизни зависели бы от вашего анализа, было бы целесообразно прочитать последние научные исследования по данной проблеме либо проконсультироваться с экспертами в предметной области, чтобы наполнить информацией свои интерпретации.
54 Часть I. Введение в интерпретацию машинного обучения Подход Логистическая регрессия — один из самых распространенных способов ранжирования факторов риска в медицинской практике. В отличие от линейной регрессии, она не пытается предсказывать непрерывное значение по каждому вашему наблюдению и предсказывает вероятность того, что наблюдение принадлежит тому или иному классу. В данном случае мы пытаемся предсказать следующее: при наличии данных x о каждом пациенте какова вероятность y, от 0 до 1, что у некоего пациента есть сердечно-сосудистое заболевание? Подготовительные работы Исходный код этого примера находится по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter02/CVD.ipynb. Загрузка библиотек Для того чтобы запустить этот пример, вам необходимо установить следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования данными;  statsmodels для подгонки логистической регрессионной модели;  sklearn (scikit-learn) для разбиения данных;  matplotlib для визуализации интерпретаций. Загрузите их следующим образом: import math import mldatasets import pandas as pd import numpy as np import statsmodels.api as sm from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt Изучение проблемы и подготовка данных Затем используемые в этом примере данные необходимо загрузить в кадр данных, который мы назовем cvd_df: cvd_df = mldatasets.load("cardiovascular-disease") Должно быть 70 000 записей и 12 столбцов. Это можно подтвердить с помощью библиотечного метода info(): cvd_df.info()
Глава 2. Ключевые понятия интерпретируемости 55 Приведенная выше инструкция выводит на экран имя каждого столбца с его типом и числом содержащихся в нем записей, которые не имеют неопределенных значений null: <class 'pandas.core.frame.DataFrame'> RangeIndex: 70000 entries, 0 to 69999 Data columns (total 12 columns): age 70000 non-null int64 gender 70000 non-null int64 height 70000 non-null int64 weight 70000 non-null float64 ap_hi 70000 non-null int64 ap_lo 70000 non-null int64 cholesterol 70000 non-null int64 gluc 70000 non-null int64 smoke 70000 non-null int64 alco 70000 non-null int64 active 70000 non-null int64 cardio 70000 non-null int64 dtypes: float64(1), int64(11) memory usage: 6.4 MB Словарь данных В целях понимания того, что было загружено, далее приведен словарь данных в порядке, выводимом источником данных.  age — возраст пациента в днях (объективный признак);  gender — пол: 1 — женщина, 2 — мужчина (объективный признак);  height — рост в сантиметрах (объективный признак);  weight — масса тела в килограммах (объективный признак);  ap_hi — систолическое артериальное давление, т. е. артериальное давление при выбросе крови во время сокращения желудочков сердца. Нормальное значение: менее 120 мм рт. ст. (признак медицинского осмотра);  ap_lo — диастолическое артериальное давление, т. е. промежуточное артериаль- ное давление между сердцебиениями. Нормальное значение: менее 80 мм рт. ст. (признак медицинкого осмотра);  cholesterol — уровень холестерина: 1 — в норме, 2 — выше нормы, 3 — значи- тельно выше нормы (признак медицинского осмотра);  gluc — уровень глюкозы: 1 — в норме, 2 — выше нормы, 3 — значительно выше нормы (признак медицинского осмотра);  smoke — наличие привычки курения: 0 — некурящий, 1 — курящий (субъектив- ный признак);  alco — наличие алкогольной зависимости: 0 — непьющий, 1 — пьющий (субъ- ективный признак);
56 Часть I. Введение в интерпретацию машинного обучения  active — физическая активность: 0 — неактивный, 1 — активный (субъектив- ный признак);  cardio — наличие ССЗ: 0 — не имеет ССЗ, 1 — имеет ССЗ (целевой признак). Подготовка данных В целях интерпретируемости и модельной результативности можно выполнить несколько работ по подготовке данных, но прямо сейчас особняком среди них стоит возраст (age). Возраст обычно не измеряют в днях. Собственно, для подобного рода предсказаний, связанных со здоровьем, мы, возможно, даже захотим сгруппировать их по возрастным категориям, поскольку люди, как правило, стареют по-разному. А пока что мы конвертируем все возрасты в годы: cvd_df['age'] = cvd_df['age'] / 365.24 В результате получится более понятный столбец, т. к. ожидаемые значения возрастов будут в диапазоне от 0 до 120. Мы взяли существующие данные и преобразовали их. Это наглядный пример методики конструирования признаков, или выработки признаков, осуществляемой с использованием знаний предметной области о своих данных с целью создания признаков, которые отражают вашу задачу гораздо лучше, тем самым улучшая свои модели. Мы обсудим это далее в главах 10 и 12. Конструирование признаков ценно хотя бы тем, что можно сделать модельные результаты более интерпретируемыми при условии, что оно не вредит результативности модели. Нам это подходит, т. к. изменение столбца возраста (age) не сможет навредить, поскольку мы не ухудшили данные: количества лет по-прежнему содержат десятичные разделители, которые учитывают дни. Теперь мы взглянем на сводные статистические величины по каждому нашему признаку, используя блиотечный метод describe(): cvd_df.describe().transpose() На рис. 2.1 показаны сводные статистические величины, выведенные на экран приведенной выше инструкцией. Здесь возраст (age) представлен уже в привычном виде, и он колеблется в пределах 29–65 лет, что не является чем-то необычным, но есть несколько выбросов в систолическом (ap_hi) и диастолическом (ap_lo) артериальном давлении. Кровяное давление не может быть отрицательным, и самое высокое за всю историю наблюдений составило 370. Эти записи придется удалить, потому что они могут привести к слабой результативности и интерпретируемости модели. Для полноты картины необходимо обеспечить, чтобы систолическое артериальное давление (ap_hi) всегда было выше диастолического (ap_lo), поэтому любая запись с этим расхождением также должна быть удалена: cvd_df = cvd_df[(cvd_df['ap_lo'] <= 370) &\ (cvd_df['ap_lo'] > 0)].reset_index(drop=True) cvd_df = cvd_df[(cvd_df['ap_hi'] <= 370) &\ (cvd_df['ap_hi'] > 0)].reset_index(drop=True) cvd_df = cvd_df[cvd_df['ap_hi'] >=\ cvd_df['ap_lo']].reset_index(drop=True)
Глава 2. Ключевые понятия интерпретируемости 57 Рис. 2.1. Сводные статистические величины для набора данных Теперь в целях подгонки логистической регрессионной модели необходимо собрать все объективные признаки, признаки медицинского осмотра и субъективные признаки вместе как X и целевой признак отдельно как y. После этого X и y следует разбить на тренировочный и тестовый наборы данных, при этом ради воспроизводимости обязательно применить аргумент random_state (случайное состояние): y = cvd_df['cardio'] X = cvd_df.drop(['cardio'], axis=1).copy() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.15, random_state=9) Ознакомление с типами методов интерпретации и диапазонами интерпретируемости Теперь, когда мы подготовили данные и разбили их на тренировочный и тестовый наборы, можно выполнить обучение модели, используя тренировочные данные, и распечатать сводку результатов: log_model = sm.Logit(y_train, sm.add_constant(X_train)) log_result = log_model.fit() print(log_result.summary2()) Распечатка результата метода summary2() на обученной модели имеет следующий вид: Optimization terminated successfully. Current function value: 0.561557 Iterations 6 Results: Logit =================================================================
58 Часть I. Введение в интерпретацию машинного обучения Model: Logit Pseudo R-squared: 0.190 Dependent Variable: cardio AIC: 65618.3485 Date: 2020-06-10 09:10 BIC: 65726.0502 No. Observations: 58404 Log-Likelihood: -32797. Df Model: 11 LL-Null: -40481. Df Residuals: 58392 LLR p-value: 0.0000 Converged: 1.0000 Scale: 1.0000 No. Iterations: 6.0000 ----------------------------------------------------------------Coef. Std.Err. z P>|z| [0.025 0.975] ----------------------------------------------------------------const -11.1730 0.2504 -44.6182 0.0000 -11.6638 -10.6822 age 0.0510 0.0015 34.7971 0.0000 0.0482 0.0539 gender -0.0227 0.0238 -0.9568 0.3387 -0.0693 0.0238 height -0.0036 0.0014 -2.6028 0.0092 -0.0063 -0.0009 weight 0.0111 0.0007 14.8567 0.0000 0.0096 0.0125 ap_hi 0.0561 0.0010 56.2824 0.0000 0.0541 0.0580 ap_lo 0.0105 0.0016 6.7670 0.0000 0.0075 0.0136 cholesterol 0.4931 0.0169 29.1612 0.0000 0.4600 0.5262 gluc -0.1155 0.0192 -6.0138 0.0000 -0.1532 -0.0779 smoke -0.1306 0.0376 -3.4717 0.0005 -0.2043 -0.0569 alco -0.2050 0.0457 -4.4907 0.0000 -0.2945 -0.1155 active -0.2151 0.0237 -9.0574 0.0000 -0.2616 -0.1685 ================================================================= Приведенная выше сводка помогает нам понять, какие именно признаки внесли наибольший вклад в диагноз ССЗ y, с помощью модельных коэффициентов (помеченных как Coef.). Как и в случае с линейной регрессией, они подобны весу, примененному к каждому предсказателю. Однако экспонента, в аргументе которой находится линейная комбинация признаков, является логистической функцией. Это еще больше затрудняет интерпретацию. Мы объясним эту функцию далее в главе 3. Глядя на указанный столбец, можно только сказать, что признаками с абсолютными наивысшими значениями являются cholesterol и active, но не понятно, что это значит. Более интерпретируемыми эти значения становятся после вычисления экспоненты от этих коэффициентов: np.exp(log_result.params).sort_values(ascending=False) Приведенная строка исходного кода выводит на экран следующее: cholesterol ap_hi age weight ap_lo height gender gluc smoke 1.637374 1.057676 1.052357 1.011129 1.010573 0.996389 0.977519 0.890913 0.877576
Глава 2. Ключевые понятия интерпретируемости 59 alco 0.814627 active 0.806471 const 0.000014 dtype: float64 Почему именно экспонента? Коэффициенты — это логарифмические шансы (от англ. log odds). Кроме того, шансы — это вероятность положительного случая относительно вероятности отрицательного случая, когда положительный случай — это явление, которое мы пытаемся предсказать. Он не обязательно указывает на то, чему отдается предпочтение. Например, если мы пытаемся предсказать шансы дождя сегодня днем, то положительным случаем будет выпадение дождя независимо от того, что мы предсказывали: наличие либо отсутствие осадков. Шансы часто выражаются в виде соотношения. В новостях могут сообщить, что вероятность дождя сегодня днем составляет 60%, или сказать, что вероятность дождя равна 3:2, или 3 2  1,5 . В форме логарифмических шансов это будет 0,176, т. е. логарифмом 1,5. Эти цифры, в сущности, одинаковы, но выражены по-разному. Экспоненциальная функция является обратной функции логарифма, поэтому она может принимать любые логарифмические шансы и возвращать шансы. Вернемся к нашей задаче о сердечно-сосудистых заболеваниях. Теперь, когда у нас есть шансы, можно интерпретировать их смысл. Например, что означают шансы в случае учета холестерина? Это означает, что шансы ССЗ увеличиваются в 1,64 раза для каждой дополнительной единицы холестерина при условии, что все остальные признаки остаются неизменными. Возможность объяснить влияние признака на модель в таких ощутимых терминах — одно из преимуществ имманентно интерпретируемой модели, такой как логистическая регрессия. Хотя шансы и дают нам полезную информацию, они не говорят нам о том, какой признак важнее всего, и, следовательно, сами по себе не могут использоваться для измерения важности признаков. Но как такое может быть? Если у чего-то есть более высокие шансы, то это что-то должно иметь более высокую важность, верно? Дело в том, что, на минуточку, все они имеют разные шкалы, и в этом вся разница. Это связано с тем, что, если вы хотите измерить шансы того, насколько что-то увеличивается, то вы должны знать, насколько это что-то увеличивается в типичной ситуации, т. к. это обеспечивает контекст. Например, мы могли бы сказать, что шансы того, что определенный вид бабочек проживет на один день больше, составляют 0,66 после их вылупления из кокона. Это утверждение будет для вас бессмысленным, если вы не знаете продолжительность жизни и репродуктивный цикл этого вида. В целях предоставления шансам контекста можно легко вычислить среднее квадратичное отклонение признаков, используя соответствующую функцию: np.std(X_train, 0) Следующий вывод как раз и является результатом работы функции np.std: age gender height weight 6.757537 0.476697 8.186987 14.335173
60 ap_hi ap_lo cholesterol gluc smoke alco active dtype: float64 Часть I. Введение в интерпретацию машинного обучения 16.703572 9.547583 0.678878 0.571231 0.283629 0.225483 0.397215 Как можно судить по этой распечатке, двоичные и порядковые признаки в типичной ситуации изменяются не более чем на единицу, но непрерывные признаки, такие как вес (weight), или систолическое артериальное давление (ap_hi), могут изменяться в 10–20 раз больше, о чем свидетельствует среднее квадратичное отклонение этих признаков. Еще одна причина, по которой шансы нельзя использовать для измерения важности признаков, заключается в том, что несмотря на благоприятные шансы, иногда признаки не являются статистически значимыми. Они переплетены с другими признаками в таком виде, что могут выглядеть значимыми, но можно доказать, что они таковыми не являются. Это можно увидеть в сводной таблице модели под столбцом P>|z|. Это значение называется p-значением, и при его величине меньше 0,05 путем статистического тестирования гипотезы определяется наличие веских оснований для того, чтобы считать его значимым. Однако, когда p-значение превышает 0,05, в особенности с большим отрывом, то нет никаких статистических оснований считать, что оно будет влиять на предсказанный балл. Так обстоит дело с признаком пола (gender), по меньшей мере, в этом наборе данных. Если пытаться получить признаки, которые играют наиболее важную роль, то аппроксимировать это можно путем умножения коэффициентов на средние квадратичные отклонения признаков. Их анализ объясняет разницы в дисперсиях между признаками. Следовательно, будет лучше, если мы заодно выведем из игры и признак gender тоже: coefs = log_result.params.drop(labels=['const','gender']) stdv = np.std(X_train, 0).drop(labels='gender')abs(coefs * stdv).sort_values(ascending=False) Приведенный выше фрагмент исходного кода произвел следующий результат: ap_hi age cholesterol weight ap_lo active gluc alco smoke height dtype: float64 0.936632 0.344855 0.334750 0.158651 0.100419 0.085436 0.065982 0.046230 0.037040 0.029620
Глава 2. Ключевые понятия интерпретируемости 61 Эту таблицу можно интерпретировать как аппроксимацию факторов риска от высокого до низкого в соответствии с моделью. Это также модельно-специфический метод определения важности признаков, другими словами, метод глобальной модельной (модулярной) интерпретации. Здесь следует разъяснить ряд новых понятий, поэтому давайте разложим их по полочкам. Типы методов модельной интерпретации Существует два типа методов модельной интерпретации.  Модельно-специфические методы. Когда метод может использоваться только для определенного модельного класса, тогда он является модельно-специфическим. Метод, описанный в предыдущем примере, может работать только с логистической регрессией, потому что в нем используются ее коэффициенты.  Модельно-агностические методы. Это методы, которые могут работать с лю- бым модельным классом. Мы рассмотрим их в главе 4. Диапазоны модельной интерпретируемости Существует несколько диапазонов модельной интерпретируемости.  Глобальная холистическая интерпретация. То, как модель делает предсказа- ния, можно объяснить просто потому, что модель можно осмыслить сразу целиком с полным пониманием данных, и это натренированная модель. Например, простой пример линейной регрессии в главе 1 можно визуализировать на двумерном графике. Ее можно осмыслить в уме, но это осмысление возможно только потому, что его позволяет сделать сама простота модели, и это не очень распространено и не очень ожидаемо.  Глобальная модулярная интерпретация. Как весь процесс преобразования топлива в кинетическую энергию можно объяснить ролью частей двигателя внутреннего сгорания, так и модель можно рассматривать как совокупность ее составляющих. Например, в примере с фактором риска сердечно-сосудистых заболеваний наш метод определения важности признаков говорит о том, что систолическое артериальное давление (ap_hi), возраст (age), холестерин (cholesterol) и вес (weight) — это части, которые влияют на целое больше всего. Метод определения важности признаков является лишь одним из многих методов глобальной модулярной интерпретации, но, пожалуй, самым важным. В главе 4 о важности признаков рассказывается подробнее.  Локальная интерпретация отдельного предсказания. У вас есть возможность объяснить причину, почему было сделано отдельное предсказание. Следующий ниже пример проиллюстрирует эту концепцию.  Локальная интерпретация группы предсказаний. То же самое, что и отдель- ное предсказание, но относится она к группам предсказаний.
62 Часть I. Введение в интерпретацию машинного обучения Примите поздравления! Вы уже определили факторы риска с помощью метода глобальной модельной интерпретации, но министр здравоохранения также хочет знать, существует ли возможность использовать эту модель для интерпретации отдельных случаев. Итак, давайте обратимся к ним. Интерпретирование отдельных предсказаний с помощью логистической регрессии Что будет, если применить модель с целью предсказания сердечно-сосудистого заболевания для всего тестового набора данных целиком? Это можно было бы сделать вот так: y_pred = log_result.predict(sm.add_constant(X_test)).to_numpy() print(y_pred) Результирующий массив представляет собой вероятности того, что каждый тестовый случай является положительным случаем ССЗ: [0.40629892 0.17003609 0.13405939 ... 0.95575283 0.94095239 0.91455717] Давайте возьмем один из положительных случаев — тестовый случай № 2872: print(y_pred[2872]) Мы знаем, что он предсказал положительный результат ССЗ, т. к. вероятнось превышает 0,5: 0.5746680418975686 Детали тестового случая № 2872 выводятся следующим образом: print(X_test.iloc[2872]) И эти детали таковы: age 60.521849 gender 1.000000 height 158.000000 weight 62.000000 ap_hi 130.000000 ap_lo 80.000000 cholesterol 1.000000 gluc 1.000000 smoke 0.000000 alco 0.000000 active 1.000000 Name: 46965, dtype: float64 Таким образом, по внешнему виду приведенного выше ряда мы знаем, что к индивидууму относится следующее:  пограничное высокое систолическое артериальное давление (ap_hi);  нормальное диастолическое артериальное давление (ap_lo). Наличие высокого систолического артериального давления и нормального диастолического артериального давления известно как изолированная систолическая гипертензия.
Глава 2. Ключевые понятия интерпретируемости 63 Она может вызывать положительное предсказание, но систолическое артериальное давление (ap_hi) является пограничным (граница составляет 130 мм рт. ст.), следовательно, состояние изолированной систолической гипертензии является пограничным;  возраст (age) не слишком большой, но является одним из самых больших в набо- ре данных;  холестерин (cholesterol) в норме;  вес ( weight) также, по-видимому, находится в диапазоне для здоровых пациентов. Также нет никаких других факторов риска: уровень глюкозы в норме, отсутствие вредных привычек, курения, употребления алкоголя и ведение активного образа жизни. Не совсем понятно, почему результат является положительным. Достаточно ли возраста и пограничной изолированной систолической гипертензии, чтобы склонить чашу весов? Без контекста трудно понять причины предсказания, поэтому давайте попробуем их связать! Но каким образом поместить все в контекст одновременно? У нас не получится визуализировать соотношения одного предсказания с другими десятью тысячами по каждому отдельному признаку и соответствующему ему предсказанному диагнозу ССЗ. К сожалению, люди не способны справляться с таким уровнем размерности, даже если бы можно было визуализировать десятимерную гиперплоскость! Тем не менее мы сможем делать это попарно, в результате получив график, который доносит до нас места, где для этих признаков лежит модельная граница решения. Кроме того, можно наложить любые предсказания на тестовый набор данных, основываясь на всех признаках. Это делается для того, чтобы визуализировать расхождение между влияниями двух признаков и всеми одиннадцатью признаками. Такой метод графической интерпретации называется границей решения. Он демонстрирует границы классов, оставляя области, принадлежащие тому или иному классу. Такие области называются участками принятия решения. В данном случае у нас есть два класса, поэтому мы увидим график с одной-единственной границей между cardio=0 и cardio=1 в отношении лишь двух сравниваемых нами признаков. Нам удалось визуализировать сразу два признака, основанных на границе решения, за раз с одним большим допущением о том, что если все остальные признаки остаются постоянными, мы сможем наблюдать только два в отдельности. Это допущение также носит название ceteris paribus, т. е. при прочих равных условиях, и имеет решающее значение в научном исследовании, позволяя нам держать под контролем некоторые переменные, чтобы наблюдать за другими. Это можно делать путем заполнения их значением, которое не будет влиять на результат. Используя сгенерированную нами таблицу шансов, мы сможем судить об увеличения признака по мере того, как он будет увеличивать шансы ССЗ. Таким образом, в совокупности более низкое значение является менее рисковым для ССЗ. Например, age=30 является наименее рисковым значением из тех, что присутствуют в наборе данных для признака возраста. С другой стороны, active=1 представляет
64 Часть I. Введение в интерпретацию машинного обучения меньший риск, чем active=0. Оптимальные значения можно сформулировать и для остальных признаков:  height=165;  weight=57 (оптимальный для этого роста);  ap_hi=110;  ap_lo=70;  smoke=0;  chelesterol=1 (означает нормальный уровень);  gender может кодироваться для мужчины или женщины, что не играет роли, по- тому что шансы для пола (0.977519) очень близки к 1. Следующий словарь значений заполнительных признаков (filler_feature_values) является примером соотнесения индекса признаков с их наименее рисковыми значениями: filler_feature_values = {0:1, 1:30, 2:1, 3:165, 4:57, 5:110, 6:70, 7:1, 8:1, 9:0, 10:0, 11:1} В указанном словаре признаки не поименованы, а пронумерованы, потому что функция, которую мы будем использовать для построения графика участков решения, используется только на массивах NumPy. Кроме того, в библиотеке statsmodels необходимо определять константу (так называемое пересечение, или сдвиговый коэффициент) в явной форме. Для того чтобы ее указать, следует помнить, что в любую логистическую модель можно "искусственно ввести" дополнительный признак с нулевым значением, экспонента от которого всегда равна 1. Мы также намерены построить график фактических предсказаний для тестового набора данных. Для этой цели необходимо определить еще один словарь, наподобие filler_feature_values, но с диапазоном, чтобы, например, значение filler_feature_value для роста равнялось 165. Тогда мы сможем варьировать это значение в пределах величины, равной 220, чтобы рост включал все случаи в диапазоне 165  110 , или [55–275], содержащий все возможные значения роста в тестовом наборе данных: filler_feature_ranges = {0:1, 1:35, 2:2, 3:110, 4:150, 5:140, 6:70, 7:3, 8:3, 9:2, 10:2, 11:2} Следующим на очереди является создание массива NumPy размерности (1, 12) с тестовым случаем № 2872, чтобы функция построения графика смогла его выделить. С этой целью мы сначала конвертируем его в массив NumPy, а затем добавляем в его начало константу 1, которая должна быть первым признаком, после чего реформируем массив так, чтобы он удовлетворял размерности (1, 12): X_highlight = np.reshape( np.concatenate(([1], X_test.iloc[2872].to_numpy())), (1, 12)) print(X_highlight)
Глава 2. Ключевые понятия интерпретируемости 65 Результат: [[ 1. 130. 0. 60.52184865 80. 1. ]] 1. 1. 158. 1. 62. 0. Теперь мы готовы к работе! Давайте визуализируем несколько графиков участков принятия решения! Мы будем сравнивать признак, который считается самым высоким фактором риска, а именно систолическое артериальное давление (ap_hi), со следующими четырьмя наиболее важными факторами риска: возрастом (age), уровнем холестерина (cholesterol), весом (weight) и диастолическим артериальным давлением (ap_lo). Следующий исходный код будет генерировать графики на рис. 2.2: plt.rcParams.update({'font.size': 14}) fig, axarr = plt.subplots(2, 2, figsize=(12,8), sharex=True, sharey=False) mldatasets.create_decision_plot(X_test, y_test, log_result, [5,1], ['ap_hi мм рт. ст.', 'возраст, годы'], X_highlight, filler_feature_values, filler_feature_ranges, ax=axarr.flat[0]) mldatasets.create_decision_plot(X_test, y_test, log_result, [5,7], ['ap_hi, мм рт. ст.', 'холестерин [1-3]'], X_highlight, filler_feature_values, filler_feature_ranges, ax=axarr.flat[1]) mldatasets.create_decision_plot(X_test, y_test, log_result, [5,6], ['ap_hi, мм рт. ст.', 'ap_lo, мм рт. ст.'], X_highlight, filler_feature_values, filler_feature_ranges, ax=axarr.flat[2]) mldatasets.create_decision_plot(X_test, y_test, log_result, [5,4], ['ap_hi, мм рт. ст.', 'вес, кг'], X_highlight, filler_feature_values, filler_feature_ranges, ax=axarr.flat[3]) plt.subplots_adjust(top = 1, bottom=0, hspace=0.2, wspace=0.2) plt.show() На графике рис. 2.2 "окружность" представляет тестовый случай № 2872. На всех частях графика под номером один тестовый случай находится в отрицательном (левом) участке решения, представляющем классификацию cardio=0. Пограничного высокого систолического артериального давления (ap_hi) и относительно высокого возраста (age) едва ли хватает для положительного предсказания на левом верхнем
66 Часть I. Введение в интерпретацию машинного обучения графике. Тем не менее для тестового случая № 2872 мы предсказали 57%-ю вероятность ССЗ, так что это значение вполне сможет объяснить бо́льшую часть тестового случая. Неудивительно, что систолического артериального давления (ap_hi) и уровня холестерина (cholesterol) для здорового пациента самих по себе недостаточно, чтобы принять решение о наличии ССЗ в соответствии с моделью, потому что результат явно находится на участке отрицательного решения, и ни один из результатов не имеет нормального значения диастолического артериального давления (ap_lo). По этим трем диаграммам можно судить о том, что, хотя в распределении квадратов и треугольников и есть некоторое наложение, в направлении увеличения значений по оси y существует тяготение большего числа треугольников к положительной стороне, в то время как этот участок заполняется меньшим числом квадратов. Рис. 2.2. Участки решения для систолического артериального давления (ap_hi) и других верхних факторов риска с тестовым случаем № 2872 Наложение границы решения вполне ожидаемо, потому что, в конце концов, эти точки данных в виде квадратов и треугольников отражают влияние всех признаков. Тем не менее можно ожидаемо найти довольно устоявшийся шаблон. Диаграмма с
Глава 2. Ключевые понятия интерпретируемости 67 систолическим артериальным давлением (ap_hi) в зависимости от веса (weight) не растет вертикально вертикально по мере увеличения веса, что намекает на то, что в этой истории чего-то не хватает... Запомните эту идею, потому что мы вернемся к данному вопросу в следующем разделе! Примите поздравления! Вы выполнили вторую часть поручения министра. Построение графиков участков решения методом локальной модельной интерпретации предоставило Министерству здравоохранения инструмент для интерпретирования предсказаний отдельных случаев. Теперь можно его расширить, чтобы объяснять по несколько случаев за раз, либо строить графики всех комбинаций важных признаков, которые находятся на участке положительного решения. Некоторые переменные можно также изменять по одной за раз, чтобы видеть их влияние на ситуацию. Например, что будет, если увеличить возраст до медианного возраста 54 года или даже до возраста тестового случая № 2872. Хватит ли сейчас погранично высокого уровня систолического артериального давления (ap_hi) и уровня холестерина для здорового пациента, чтобы принять решение? Мы ответим на этот вопрос позже, но сначала давайте разберемся в том, что затрудняет интерпретацию машинного обучения. Оценивание препятствий, мешающих интерпретируемости результатов машинного обучения В последнем разделе мы задавались вопросом: почему диаграмма с систолическим артериальным давлением (ap_hi) в сопоставлении с весом (weight) не имела убедительного паттерна? Вполне возможно, что, хотя вес является фактором риска, повышенный риск ССЗ могут объяснять и другие критические опосредующие переменные. Опосредующая переменная — это переменная, которая влияет на силу между независимой и целевой (зависимой) переменной. Пожалуй, нам не придется долго размышлять над тем, чего не хватает. В главе 1 мы выполнили линейную регрессию на весе (weight) и росте (height), потому что между этими переменными существует линейная связь. В контексте здоровья человека вес не так содержателен без роста, поэтому вам нужно обратиться к обоим параметрам. Возможно, если построить график участков решения для этих двух переменных, то мы получим какие-то подсказки. Их график можно построить с помощью следующего фрагмента исходного кода: fig, ax = plt.subplots(1,1, figsize=(12,8)) mldatasets.create_decision_plot(X_test, y_test, log_result, [3, 4], ['рост, см', 'вес, кг'], X_highlight, filler_feature_values, filler_feature_ranges, ax=ax) plt.show()
68 Часть I. Введение в интерпретацию машинного обучения Приведенный выше фрагмент сгенерирует график на рис. 2.3. Рис. 2.3. Участки решения для веса и роста с тестовым случаем № 2872 На рис. 2.3 никакой границы решения выявлено не было, поскольку, если все остальные переменные остаются постоянными (при менее рисковых значениях), никакой комбинации роста и веса недостаточно для предсказания ССЗ. Тем не менее можно сказать, что у оранжевых треугольников есть шаблон, заключающийся в том, что они в основном сосредоточены в одной овальной области. Это дает захватывающее осознание того, что, хотя вес ожидаемо и будет увеличиваться по мере увеличения роста, вес заведомо нездорового пациента не увеличивается линейно вместе с ростом. Собственно, в течение почти двух столетий эта связь математически понималась под названием "индекс массы тела" (ИМТ): ИМТ  вес , рост 2 где вес измеряют в килограммах (фактически это масса тела), а рост — в метрах. Прежде чем мы обсудим индекс массы тела дальше, необходимо рассмотреть сложность. Помимо размерности, сложность, которая затрудняет интерпретацию, привносится главным образом тремя компонентами:  нелинейностью;  интерактивностью;  немонотонностью.
Глава 2. Ключевые понятия интерпретируемости 69 Нелинейность Линейные уравнения, такие как y  a  bx , понять легко. Они аддитивны, поэтому легко по отдельности исследовать влияние каждого их члена ( a и bx ) на выход модели (y). Многие модельные классы имеют линейные уравнения, объяснимые с точки здения математики. Эти уравнения можно использовать как для подгонки модели под данные, так и для описания модели. Однако существуют модельные классы, которые по сути являются нелинейными, т. к. они привносят в процесс обучения модели нелинейность. Так обстоит дело с моделями глубокого обучения, поскольку они имеют нелинейные активационные функции, такие как сигмоида. Однако логистическая регрессия считается обобщенной линейной моделью (generalized linear model, GLM), потому что она является аддитивной. Другими словами, исход представляет собой сумму взвешенных данных на входе и параметров. Мы обсудим обобщенные линейные модели далее в главе 3. Однако, даже если ваша модель и является линейной, взаимосвязи между переменными бывают нелинейными, что может приводить к ее низкой результативности и интерпретируемости. В таких ситуациях можно принимать любой из следующих ниже подходов.  Использовать класс нелинейных моделей, который будет намного лучше вписы- вать эти нелинейные взаимосвязи, возможно, улучшая результативность модели. Однако это может делать ее менее интерпретируемой (см. главу 3).  Использовать предметные знания для конструирования признака, который по- может ее "линеаризировать". Например, если у вас был признак, который увеличивался экспоненциально по сравнению с еще одним, то можно сконструировать новую переменную с логарифмом этого признака. В случае предсказания ССЗ мы знаем, что подход на основе индекса массы тела позволяет лучше понять взаимосвязь веса и роста. Приятно, этот признак не выдуман произвольно, и поэтому его легче интерпретировать. Этот аргумент можно доказать, сделав копию набора данных, сконструировав в нем признак индекса массы тела, натренировав модель с использованием этого нового признака и выполнив локальную модельную интерпретацию. Следующий ниже фрагмент исходного кода как раз делает это: X2 = cvd_df.drop(['cardio'], axis=1).copy() X2["bmi"] = X2["weight"] / (X2["height"]/100)**2 X2_train, X2_test,__,_ = train_test_split(X2, y, test_size=0.15, random_state=9) В целях иллюстрации этого нового признака давайте построим график индекса массы тела в сопоставлении с весом и ростом, используя следующий исходный код: fig, axs = plt.subplots(1,3, figsize=(15,4)) axs[0].scatter(X2["weight"], X2["bmi"], color='black', s=2) axs[0].set_xlabel('вес [кг]') axs[0].set_ylabel('ИМТ') axs[1].scatter(X2["height"], X2["weight"], color='black', s=2) axs[1].set_xlabel('рост, см') axs[1].set_ylabel('вес, кг')
70 Часть I. Введение в интерпретацию машинного обучения axs[2].scatter(X2["bmi"], X2["height"], color='black', s=2) axs[2].set_xlabel('ИМТ') axs[2].set_ylabel('рост, см') plt.subplots_adjust(top = 1, bottom=0, hspace=0.2, wspace=0.3) plt.show() Рис. 2.4 выполнен с использованием приведенного выше исходного кода. Рис. 2.4. Двупеременное сравнение веса, роста и индекса массы тела Как можно понять из графиков на рис. 2.4, между индексом массы тела (bmi) и весом (weight) имеется более определенная линейная взаимосвязь, чем между ростом (height) и весом (weight) и даже между индексом массы тела и ростом. Давайте применим новую модель с новым признаком, используя следующий фрагмент исходного кода: log_model2 = sm.Logit(y_train, sm.add_constant(X2_train)) log_result2 = log_model2.fit() Теперь посмотрим, относится ли тестовый случай № 2872 к участку положительного решения при сравнении с систолическим артериальным давлением (ap_hi) с индексом массы тела (bmi): filler_feature_values2 = {0:1, 1:60, 2:1, 3:165, 4:57, 5:110, 6:70, 7:1, 8:1, 9:0, 10:0, 11:1, 12:20} filler_feature_ranges2 = {0:1, 1:35, 2:2, 3:120, 4:150, 5:140, 6:70, 7:3, 8:3, 9:2, 10:2, 11:2, 12:250} X2_highlight = np.reshape(np.concatenate(([1], X2_test.iloc[2872].to_numpy())), (1,13)) fig, ax = plt.subplots(1,1, figsize=(12,8)) mldatasets.create_decision_plot(X2_test, y_test, log_result2, [5, 12], ['ap_hi, мм рт.ст.', 'ИМТ'], X2_highlight, filler_feature_values2, filler_feature_ranges2, ax=ax) plt.show()
Глава 2. Ключевые понятия интерпретируемости 71 Приведенный выше фрагмент исходного кода показывает участки решения на рис. 2.5. Рис. 2.5. Участки решения с учетом систолического артериального давления (ap_hi) и индекса массы тела (bmi) для тестового случая № 2872 На рис. 2.5 показано, что систолическое артериальное давление (ap_hi) и индекс массы тела (bmi) способны помочь объяснить положительное предсказание для ССЗ, т. к. воображаемая окружность, охватывающая точки данных, находится на участке положительного решения. Обратите внимание на несколько вероятных аномальных выбросов индекса массы тела (самый высокий когда-либо зарегистрированный индекс массы тела составил 204), поэтому в наборе данных, возможно, есть неправильные показатели веса или роста. В ЧЕМ ПРОБЛЕМА С ВЫБРОСАМИ ? Выбросы бывают влиятельными или сильными рычагами воздействия и, следовательно, могут влиять на модель во время ее обучения со своим участием. Даже если они этого не делают, они способны усложнять интерпретацию. Если они аномальные, вам следует их исключать, как мы сделали с параметром артериального давления в начале этой главы. Иногда они могут маскироваться, поскольку воспринимаются как аномальные только в контексте других признаков. В любом случае, есть объяснения тому, почему выбросы создают проблемы, например, делая графики, подобные предыдущему, слишком "растянутыми", если записывать в них эти выбросы, и не позволяя вам оценивать границу решения там, где это немаловажно. И к тому же есть более серьезные основания, такие как потеря доверия к данным, подрываюшая доверие к моделям, которые были натренированы на этих данных. Такого рода проблемы
72 Часть I. Введение в интерпретацию машинного обучения возможны и с реально существующими данными. Несмотря на то что в данной главе мы этого не сделали, важно каждый проект начинать с тщательного исследования данных, обработки пропущенных значений и выбросов и выполнения других служебно-организующих задач обработки данных. Интерактивность Когда мы ввели в рассмотрение индекс массы тела, мы не только линеаризовали нелинейную связь, но и легализовали взаимодействие между двумя признаками. Следовательно, индекс массы тела является признаком взаимодействия, или интеракционным признаком1, но об этом сообщают знания предметной области. Однако многие модельные классы делают это автоматически, перетасовывая все виды операций между признаками. В конце концов, признаки имеют скрытые взаимосвязи друг с другом, такие как высота (height) и ширина (width), а также систолическое (ap_hi) и диастолическое (ap_lo) артериальное давление. Следовательно, автоматизирование процесса их отыскания не всегда является бессмысленным занятием. На самом деле оно даже бывает абсолютно необходимым. Это относится ко многим задачам глубокого обучения, в которых данные бывают неструктурированными, и поэтому часть задачи обучения модели заключается в отыскании скрытых связей, чтобы их понять. Однако в случае структурированных данных, даже если взаимодействия важны для результативности модели, они могут вредить интерпретируемости, добавляя в модель потенциально ненужную сложность, а также обнаруживая скрытые связи, которые ничего не значат (так называемую мнимую связь, или корреляцию). Немонотонность Нередко переменная имеет содержательную и устоявшуюся взаимосвязь между признаком и целевой переменной. Так, мы знаем, что с возрастом риск сердечнососудистых заболеваний должен возрастать. Нет никакой контрольной возрастной точки, при достижении которой этот риск падает. Возможно, рост риска замедляется, но сам риск не снижается. Мы называем это монотонностью, и функции, которые являются монотонными, всегда демонстрируют рост либо всегда снижение параметров по всей их области определения. Обратите внимание, что все линейные связи являются монотонными, но не все монотонные связи с неизбежностью являются линейными. Это обусловлено тем, что они не обязательно имеют вид прямой линии. Распространенная проблема машинного обучения заключается в том, что модель не знает о монотонных связях, которые мы ожидаем из-за нашего опыта работы в предметной области. И тогда из-за шума и пропусков в данных модель тренируется в ключе неожиданных взлетов и падениий. 1 В данном переводе термины "признак взаимодействия" и "интеракционный признак" используются взаимозаменяемо. — Прим. перев.
Глава 2. Ключевые понятия интерпретируемости 73 Давайте рассмотрим гипотетический пример и вообразим, что из-за отсутствия данных о людях в возрасте 57–60 лет и из-за того, что несколько имевшихся у нас для этого возрастного диапазона случаев были ССЗ-отрицательными, модель могла бы усвоить, что именно там можно ожидать снижение риска ССЗ. Некоторые модельные классы, такие как логистическая регрессия, изначально являются монотонными, поэтому данной проблемы у них быть не может, но у многих других она есть. Мы рассмотрим это подробнее в главе 12. Рис. 2.6. График частичной зависимости между целевой переменной (yhat) и предсказателем с монотонными и немонотонными моделями На рис. 2.6 показан так называемый график частичной зависимости (partial dependence plot, PDP) из примера, не относящегося к данной теме. Мы изучим концепцию графиков частичной зависимости подробнее в главе 4, но из указанной концепции важно понять важную вещь: предсказание yhat предположительно должно уменьшаться по мере увеличения признака quantity_indexes_for_real_gdp_by_state
74 Часть I. Введение в интерпретацию машинного обучения (количественные индексы для реального ВВП по штатам). Как можно судить по линиям, в монотонной модели зависимость неуклонно снижается; в немонотонной она по мере своего уменьшения имеет зубчатые пики, а затем увеличивается в самом конце. Миссия выполнена Первая часть нашей миссии состояла в том, чтобы понять факторы риска сердечнососудистых заболеваний, и вы определили, что первейшими четырьмя факторами риска в соответствии с логистической регрессионной моделью являются систолическое артериальное давление (ap_hi), возраст (age), уровень холестерина (cholesterol) и вес (weight), из которых немодифицируемым является только возраст. Тем не менее вы также поняли, что систолическое артериальное давление (ap_hi) само по себе не играет особо важную роль для интерпретации, поскольку оно зависит от диастолического артериального давления (ap_lo). То же самое касается веса и роста. Мы узнали, что взаимодействие признаков играет решающую роль в интерпретации, как и их взаимосвязь друг с другом и целевой переменной, будь то линейная или монотонная. Кроме того, данные — это лишь представление истины, которое может быть неверным. В конце мы обнаружили аномалии, которые без проверки могут систематически влиять на нашу модель. Еще одним источником систематического смещения является то, как данные были собраны. В конце концов, можно задаться вопросом: почему все четыре признака модели были объективными и получаемыми при медицинском осмотре пациента. Почему ни курение, ни употребление алкоголя не являются более серьезными факторами? В целях подтверждения присутствия систематического смещения вследствие отбора данных вам придется выполнить сравнение с другими более надежными наборами данных и проверить, что в вашем наборе данных пьющие и курящие представлены в недостаточной мере. Или же, возможно, систематическое смещение было привнесено вопросом о том, курит ли пациент сейчас, а не о том, курил ли он когда-либо в течение длительного периода. Мы могли бы устранить еще один тип систематического смещения, а именно систематическое смещение вследствие изъятия — в наших данных может отсутствовать информация, объясняющая истину, которую модель пытается изобразить. Например, благодаря медицинским исследованиям мы знаем, что проблемы с артериальным давлением, такие как изолированная систолическая гипертензия, которая увеличивает риск ССЗ, обусловливаются такими патологическими состояниями, как сахарный диабет, гипертиреоз, артериальная гипертензия и ожирение. Из всех этих состояний можно исключить лишь одно из данных — ожирение, но не другие. Если мы хотим иметь возможность хорошо интерпретировать предсказания модели, нам нужно обладать всеми релевантными признаками. В противном случае будут иметь место пробелы, которые мы не сможем объяснить. Возможно, что после того, как мы добавим эти признаки, они не будут играть важной роли, но именно для этого предназначены методы, которые мы изучим в главе 10.
Глава 2. Ключевые понятия интерпретируемости 75 Вторая часть миссии заключалась в том, чтобы иметь возможность интерпретировать отдельные модельные предсказания. Это можно делать достаточно хорошо, строя графики участков решения. Данный метод прост, но у него много ограничений, в особенности в ситуациях, когда существует несколько признаков, и они, как правило, активно влияют друг на друга. Главы 6 и 7 будут посвящены более качественным методам локальной интерпретации. Однако метод с использованием графиков участков решения помогает иллюстрировать многие концепции, связанные с границами решения, которые мы обсудим в этих главах. Резюме Прочитав эту главу, вы должны были познакомиться с двумя методами модельной интерпретации: определения важности признаков и границ решения. Вы также узнали о типах и диапазонах методов модельной интерпретации, а также о трех элементах, которые влияют на интерпретируемость в машинном обучении. Мы продолжим упоминать эти фундаментальные концепции в последующих главах. Для практика машинного обучения крайне важно уметь их локализовывать, чтобы знать, какие инструменты можно использовать для преодоления трудностей интерпретации. В следующей далее главе мы углубимся в эту тему. Справочные материалы  Molnar Christoph. Interpretable Machine Learning. A Guide for Making Black Box Models Explainable. — 2019. — URL: https://christophm.github.io/interpretable-mlbook/. (Мольнар К. Интерпретируемое машинное обучение. Руководство по обеспечению объяснимости моделей типа черного ящика.)  Mlextend Documentation. Plotting Decision Regions. — URL: http://rasbt.github.io/mlxtend/user_guide/plotting/plot_decision_regions/. (Документация по Mlextend. Строительство графиков участков решения.)
3 Трудности интерпретации В этой главе мы обсудим классические методы интерпретации машинного обучения для регрессии и классификации. К ним относятся такие методы оценивания модельной результативности, как RMSE, R-квадрат, AUC, кривая ROC и многие метрики, получаемые из матриц путаницы. Мы также освоим несколько технических приемов визуализации понижения размерности, которые можно задействовать для целей интерпретации. Затем мы исследуем пределы этих традиционных методов и дадим объяснение тому, что придает моделям типа белого ящика имманентную интерпретируемость и почему мы не всегда можем использовать такие модели. Для того чтобы ответить на эти вопросы, мы рассмотрим, как достигается компромисс между предсказательной результативностью и модельной интерпретируемостью. Наконец, мы выявим несколько новых аквариумных моделей, таких как EBM и Skope-Rules, которые препятствуют достижению этого компромисса. Вот главные темы, которые будут охвачены в этой главе:  обзор традиционных методов модельной интерпретации;  пределы традиционных методов модельной интерпретации;  имманентно интерпретируемые (типа белого ящика) модели;  компромисс между результативностью и интерпретируемостью;  выявление современных интерпретируемых ("аквариумных") моделей. Технические требования Начиная с главы 2, мы используем конкретную прикладную библиотеку mldatasets, чтобы загружать наши наборы данных. В дополнение к ней в примерах этой главы также используются библиотеки pandas, numpy, sklearn, rulefit, cvae, interpret, statsmodels, matplotlib и skope-rules. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-withPython/blob/master/Chapter03/FlightDelays.ipynb. Миссия Вообразите себя консультантом по науке о данных в зале для переговоров в ФортУэрте, штат Техас, в начале января 2019 года. В этом зале руководители одной из крупнейших авиакомпаний мира, American Airlines (AA), инструктируют вас об их производительности своевременной работы (on-time performance, OTP). OTP является широко признанным ключевым индикатором эффективности обеспечения
Глава 3. Трудности интерпретации 77 пунктуальности полетов. Он измеряется в процентах рейсов, прибывающих в пределах 15 минут от запланированного прибытия. Оказывается, авиакомпания достигала OTP чуть более 80% в течение 3 лет подряд, что уже приемлемо и намного лучше, чем раньше, но она по-прежнему занимает девятое место в мире и пятое место в Северной Америке. Для того чтобы в следующем году в рекламе этот показатель выставить как достоинство, руководители авиакомпании стремятся занять в 2019 году по меньшей мере первое место в Северной Америке, превзойдя своих крупнейших конкурентов. На финансовом фронте, по их оценкам, задержки рейсов обходятся авиакомпании почти в 2 млрд долларов, поэтому сокращение этого показателя даже на 25–35%, чтобы быть на равных со своими конкурентами, может привести к значительной экономии. Кроме того, подсчитано, что пассажирам такие задержки обходятся в те же суммы из-за десятков миллионов потерянных часов. Сокращение задержек сделает клиентов счастливее, что может привести к увеличению продаж билетов. Ваша оперативная задача состоит в создании моделей, которые могут предсказывать задержки только для внутренних рейсов. При этом руководители авиакомпании надеются получить от моделей следующее:  понять, какие факторы влияли на задержки прибытия внутренних рейсов в 2018 году больше всего;  предвидеть задержку по причине авиакомпании еще в воздухе с точностью, дос- таточной, чтобы можно было ослаблять некоторые из этих факторов в 2019 году. Однако не все задержки одинаковы. Международная ассоциация воздушного транспорта (International Air Transport Association, IATA) имеет более 80 числовых кодов задержки в диапазоне от 14 (перепродажи, ошибки бронирования) до 75 (обледенение воздушных судов, удаление льда/снега, предотвращение замерзания). Кое-какие из них можно предотвратить, другие же неизбежны. Руководители авиакомпании сообщили вам, что на данный момент нет необходимости предсказывать задержки, вызываемые событиями, выходящими из-под их контроля, такими как экстремальные погодные условия, события по обеспечению безопасности и проблемы с управлением воздушным движением. Они также не заинтересованы в задержках, вызываемых поздним прибытием предыдущих рейсов одного и того же самолета, т. к. это не является первопричиной. Тем не менее они хотели бы знать влияние загружости авиахаба на предотвратимые задержки, даже если это связано с перегруженностью, потому что, в конце концов, возможно, удастся что-то сделать с планированием рейсов или скоростью полета или даже выбором выходов на посадку. И хотя все понимают, что международные рейсы иногда влияют на внутренние рейсы, руководители авиакомпании в первую очередь надеются выйти на крупный локальный рынок. Руководители предоставили вам набор данных из Бюро статистики грузопассажирских перевозок (Bureau of Transportation Statistics) Министерства транспорта США со всеми внутренними рейсами American Airlines за 2018 год.
78 Часть I. Введение в интерпретацию машинного обучения Подход После тщательного обдумывания вы решили подойти к поставленной задаче как с регрессионной, так и с классификационной позиций. Поэтому вы будете строить модели, которые предсказывают минуты задержки, а также модели, которые классифицируют рейсы как относимые и неотносимые к задержке, превышающей 15 минут. В плане интерпретации использование обоих подходов позволит вам применить более широкий спектр методов и расширить свою интерпретацию. Кроме того, понижение размерности модели поможет еще больше обогатить возможности интерпретации. Поэтому для данного примера мы предпримем следующие шаги: 1. Предсказывание минут задержки с помощью различных регрессионных методов. 2. Классифицирование рейсов как задержанных либо незадержанных с использованием различных классификационных методов. 3. Визуализирование задержанных рейсов с помощью методов понижения размерности. Указанные шаги в разд. "Обзор традиционных методов модельной интерпретации" сопровождаются выводами, распространяемыми на остальные разделы этой главы. Подготовительные работы Исходный код этого примера можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter03/FlightDelays.ipynb. Загрузка библиотек В целях выполнения этого примера вам необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для работы с данными;  sklearn (scikit-learn), rulefit, cvae, statsmodels, interpret и skope-rules для обучения моделей и расчета метрик результативности;  matplotlib и seaborn для создания визуализаций. Загрузите эти библиотеки, как показано в следующем фрагменте исходного кода: import math import mldatasets import pandas as pd import numpy as np from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures,
Глава 3. Трудности интерпретации 79 StandardScaler from sklearn.model_selection import train_test_split from sklearn import(metrics, linear_model, tree, naive_bayes, neighbors, ensemble, neural_network, svm, decomposition, manifold) from rulefit import RuleFit import statsmodels.api as sm from interpret.glassbox import ExplainableBoostingClassifier from interpret import show from interpret.perf import ROC import matplotlib.pyplot as plt import seaborn as sns from cvae import cvae from skrules import SkopeRules Изучение проблемы и подготовка данных Затем мы загружаем данные, как показано ниже: aad18_df = mldatasets.load("aa-domestic-delays-2018") Должно быть около 900 000 записей и 23 столбца. Это можно подтвердить следующим образом: aad18_df.info() Вот результат: <class 'pandas.core.frame.DataFrame'> RangeIndex: 899527 entries, 0 to 899526 Data columns (total 23 columns): FL_NUM 899527 non-null int64 ORIGIN 899527 non-null object DEST 899527 non-null object PLANNED_DEP_DATETIME 899527 non-null object CRS_DEP_TIME 899527 non-null int64 DEP_TIME 899527 non-null float64 DEP_DELAY 899527 non-null float64 DEP_AFPH 899527 non-null float64 DEP_RFPH 899527 non-null float64 TAXI_OUT 899527 non-null float64 WHEELS_OFF 899527 non-null float64 : : : : WEATHER_DELAY 899527 non-null float64 NAS_DELAY 899527 non-null float64 SECURITY_DELAY 899527 non-null float64 LATE_AIRCRAFT_DELAY 899527 non-null float64 dtypes: float64(17), int64(3), object(3) memory usage: 157.8+ MB Похоже, все в порядке, потому что все столбцы есть, и значений null нет.
80 Часть I. Введение в интерпретацию машинного обучения Словарь данных Давайте рассмотрим словарь данных. Общие признаки  FL_NUM — номер рейса.  ORIGIN — код аэропорта происхождения (IATA).  DEST — код аэропорта назначения (IATA). Признаки вылета  PLANNED_DEP_DATETIME — запланированные дата и время рейса.  CRS_DEP_TIME — запланированное время отправления.  DEP_TIME — фактическое время отправления.  DEP_AFPH — количество фактических рейсов в час, выполняемых между заплани- рованным и фактическим вылетами из аэропорта отправления (с учетом 30-минутной временно́й "подушки"). Этот признак показывает степень загруженности аэропорта отправления во время взлета.  DEP_RFPH — относительные рейсы вылета в час. Это соотношение фактических рейсов в час к среднему числу рейсов в час в аэропорту отправления в это время суток, дня недели и месяца года. Данный признак показывает степень загруженности аэропорта отправления во время взлета.  TAXI_OUT — время, прошедшее между выездом самолета на взлетную полосу аэро- порта отправления и отрывом колес самолета от земли.  WHEELS_OFF — момент времени, когда колеса самолета отрываются от земли. Полетные признаки  CRS_ELAPSED_TIME — запланированное время, необходимое для полета.  PCT_ELAPSED_TIME — отношение фактического времени полета к запланированно- му времени полета для измерения относительной скорости самолета.  DISTANCE — расстояние между двумя аэропортами. Признаки прибытия  CRS_ARR_TIME — запланированное время прибытия.  ARR_AFPH — число фактических рейсов в час, выполняемых между запланирован- ным и фактическим временем прибытия в аэропорт назначения (с учетом 30-минутной временно́й "подушки"). Этот признак показывает степень загруженности аэропорта назначения во время посадки самолета.  ARR_RFPH — относительное число рейсов прилета в час. Это отношение фактиче- ских рейсов в час к среднему числу рейсов в час в аэропорту назначения в это время суток, дня недели и месяца года. Данный признак сообщает о степени загруженности аэропорта назначения во время посадки.
Глава 3. Трудности интерпретации 81 Признаки задержки  DEP_DELAY — суммарная задержка отправления в минутах.  ARR_DELAY — суммарная задержка прибытия в минутах, может быть подразделена на любое количество или все из следующих параметров:  CARRIER_DELAY — задержка в минутах, вызванная обстоятельствами, находящимися в пределах контроля авиакомпании (например, проблемы с техобслуживанием или экипажем, чистка воздушного судна, загрузка багажа, заправка топливом и т. д.);  WEATHER_DELAY — задержка в несколько минут, вызванная значительными метеорологическими условиями (фактическими или прогнозируемыми);  NAS_DELAY — задержка в минутах, предусмотренная национальной авиационной системой, такая как неэкстремальные погодные условия, операции в аэропортах, интенсивное движение и управление воздушным движением;  SECURITY_DELAY — задержка в несколько минут, вызванная эвакуацией терминала или зала ожидания, зала повторной посадки в самолет из-за нарушения безопасности, неисправного досмотрового оборудования или длинных очередей с превышением ожидания более чем на 29 минут в зонах досмотра;  LATE_AIRCRAFT_DELAY — задержка в несколько минут, вызванная предыдущим рейсом того же самолета, который прибыл с опозданием. Подготовка данных Прежде всего, признак PLANNED_DEP_DATETIME должен иметь тип datetime: aad18_df['PLANNED_DEP_DATETIME'] = pd.to_datetime(aad18_df['PLANNED_DEP_DATETIME']) Точный день и время рейса не существенны, но, возможно, месяц и день недели важны из-за погоды и сезонных особенностей, которые можно оценивать только на этом уровне детализации. Кроме того, руководители отметили, что для задержек особенно плохи выходные дни и зимы. Следовательно, мы создадим признаки для месяца и дня недели: aad18_df['DEP_MONTH'] = aad18_df['PLANNED_DEP_DATETIME'].dt.month aad18_df['DEP_DOW'] = aad18_df['PLANNED_DEP_DATETIME'].dt.dayofweek Столбец PLANNED_DEP_DATETIME нам не нужен, поэтому давайте его исключим; это делается вот так: aad18_df = aad18_df.drop(['PLANNED_DEP_DATETIME'], axis=1) Крайне важно регистрировать факт того, что аэропорт прибытия или назначения является хабом. В 2019 году у AA было 10 хабов: Шарлотт, Чикаго-ОʼХара, Даллас/Форт-Уэрт, Лос-Анджелес, Майами, Нью-Йорк — Дж. Ф. Кеннеди, НьюЙорк — Ла Гуардия, Филадельфия, Феникс-Скай-Харбор и Вашингтон-Нэшнл. Поэтому можно закодировать аэропорты ORIGIN и DEST, которые являются хабами AA, используя их коды IATA, и избавиться от столбцов с кодами, поскольку они слишком специфичны (FL_NUM, ORIGIN и DEST): # Создать список с 10 хабами (с их кодами IATA) hubs = ['CLT', 'ORD', 'DFW', 'LAX', 'MIA', 'JFK', 'LGA', 'PHL', 'PHX', 'DCA']
82 Часть I. Введение в интерпретацию машинного обучения # Булев ряд для обозначения того, что ORIGIN либо DEST являются хабами is_origin_hub = aad18_df['ORIGIN'].isin(hubs) is_dest_hub = aad18_df['DEST'].isin(hubs) # Использовать булев ряд для задания столбцов ORIGIN_HUB и DEST_HUB aad18_df['ORIGIN_HUB'] = 0 aad18_df.loc[is_origin_hub, 'ORIGIN_HUB'] = 1 aad18_df['DEST_HUB'] = 0 aad18_df.loc[is_dest_hub, 'DEST_HUB'] = 1 # Отбросить столбцы с кодами aad18_df = aad18_df.drop(['FL_NUM', 'ORIGIN', 'DEST'], axis=1) После всех этих операций у нас есть немало влияющих признаков, но нам еще предстоит определить целевой признак. Для этого рассмотрим два столбца. У нас есть задержка прибытия (ARR_DELAY), т. е. общая сумма минут задержки независимо от причины, и еще имеется задержка из-за перевозчика (CARRIER_DELAY), т. е. общая сумма этих минут, которые могут быть отнесены только к авиакомпании. Например, посмотрите на следующую выборку рейсов, задержанных более чем на 15 минут (которые считаются опоздавшими в соответствии с определением авиакомпании): aad18_df.loc[aad18_df['ARR_DELAY'] > 15, ['ARR_DELAY','CARRIER_DELAY']].head(10) Приведенный выше фрагмент исходного кода выводит таблицу на рис. 3.1. Рис. 3.1. Образцы наблюдений с задержками прибытия, превышающими 15 минут Из всех приведенных на рис. 3.1 задержек одна (№ 26) вообще не входила в компетенцию авиакомпании. Четыре были частичной ответственностью авиакомпании (№ 8, 16, 33, 40), две из них указывают на опоздание, превышающее 15 минут, из-за авиакомпании (№ 8, 40). Вина за остальные полностью лежала на авиалайнере. Можно сказать, что хотя информация о суммарной задержке и является полезной, руководители авиакомпании были заинтересованы только в тех задержках, которые были вызваны авиакомпанией, поэтому признак ARR_DELAY можно отбросить. Кроме
Глава 3. Трудности интерпретации 83 того, еще одна более важная причина, по которой следует отбросить данный признак, заключается в том, что если задача состоит в предсказании задержек, то мы не можем использовать практически ту же самую задержку (за вычетом фрагментов данных, не связанных с авиакомпанией), чтобы ее предсказывать. Смахивает на подтасовку данных. Именно по этой самой причине лучше всего ARR_DELAY удалить: aad18_df = aad18_df.drop(['ARR_DELAY'], axis=1) Наконец, можно поместить целевой признак отдельно как y, а все остальные — как X. После этого мы разобъем y и X на тренировочный и тестовый наборы данных. Обратите внимание, что для регрессии целевой признак (y) остается тем же, поэтому мы подразделяем его на y_train_reg и y_test_reg. Однако для классификации необходимо создать двоичные версии этих меток, обозначающих опоздание либо неопоздание более чем на 15 минут, назвав их y_train_class и y_test_class. Обратите внимание и на то, что для воспроизводимости результатов мы задаем фиксированное случайное состояние (random_state): rand = 9 y = aad18_df['CARRIER_DELAY'] X = aad18_df.drop(['CARRIER_DELAY'], axis=1).copy() X_train, X_test, y_train_reg, y_test_reg = train_test_split(X,y, test_size=0.15, random_state=rand) y_train_class = y_train_reg.apply(lambda x: 1 if x > 15 else 0) y_test_class = y_test_reg.apply(lambda x: 1 if x > 15 else 0) Для определения степени линейной коррелированности признаков с целевым признаком CARRIER_DELAY мы вычисляем коэффициент корреляции Пирсона, превращаем коэффициенты в абсолютные значения (потому что нас не интересует их положительная либо отрицательная коррелированность) и сортируем их в убывающем порядке: corr = aad18_df.corr() abs(corr['CARRIER_DELAY']).sort_values(ascending=False) Как можно судить из результата, сильно коррелирует только один признак (DEP_DELAY). Остальные — нет: CARRIER_DELAY 1.000000 DEP_DELAY 0.703935 ARR_RFPH 0.101742 LATE_AIRCRAFT_DELAY 0.083166 DEP_RFPH 0.058659 ARR_AFPH 0.035135 DEP_TIME 0.030941 NAS_DELAY 0.026792 : : WEATHER_DELAY 0.003002 SECURITY_DELAY 0.000460 Name: CARRIER_DELAY, dtype: float64 Однако это только линейная коррелированность и на основе связи "один-к-одному". Это не значит, что у признаков нет нелинейных взаимосвязей или несколько
84 Часть I. Введение в интерпретацию машинного обучения взаимодействующих вместе признаков не влияют на цель. В следующем далее разделе мы обсудим этот вопрос подробнее. Обзор традиционных методов модельной интерпретации В целях проведения исследования как можно большего числа модельных классов и методов интерпретации мы будем выполнять подгонку регрессионных и классификационных моделей под данные, а также использовать методы понижения размерности. Предсказывание минут задержки с помощью различных регрессионных методов В целях сравнения и сопоставления регрессионных методов сначала создадим словарь с именем reg_models. Каждая модель — это отдельный словарь и функция, которая его создает в атрибуте model. Указанная структура будет использоваться позже для хранения учебной модели и достигнутых ими показателей метрик. Модельные классы в этом словаре были выбраны для представления нескольких модельных семейств и для иллюстрации важных концепций, которые мы обсудим позже: Reg_models = { # Обобщенные линейные модели (GLM-модели) 'linear':{'model': linear_model.LinearRegression()}, 'linear_poly':{'model': make_pipeline(PolynomialFeatures(degree=2), linear_model.LinearRegression(fit_intercept=False)) 'linear_interact':{'model': make_pipeline(PolynomialFeatures(interaction_only=True), linear_model.LinearRegression(fit_intercept=False)) }, 'ridge':{'model': linear_model.RidgeCV(alphas=[1e-3, 1e-2, 1e-1, 1]) }, # Деревья 'decision_tree':{'model': tree.DecisionTreeRegressor(max_depth=7, random_state=rand)}, # RuleFit 'rulefit':{'model': RuleFit(max_rules=150, rfmode='regress', random_state=rand)}, # Ближайшие соседи 'knn':{'model': neighbors.KNeighborsRegressor(n_neighbors=7)}, # Ансамблевые методы 'random_forest':{'model':ensemble.RandomForestRegressor(max_depth=7, random_state=rand)}, # Нейронные сети 'mlp':{'model':neural_network.MLPRegressor(hidden_layer_sizes=(21,), max_iter=500, early_stopping=True, random_state=rand)} }
Глава 3. Трудности интерпретации 85 Прежде чем начать обучение этих моделей на данных, кратко объясним каждую по очереди.  linear. Линейная регрессия была первым модельным классом, который мы об- суждали. В ней используется несколько допущений о данных. Главным среди них является допущение, что предсказание должно быть линейной комбинацией признаков X. Это, естественно, ограничивает способность обнаруживать нелинейные связи и взаимодействия между признаками.  linear_poly. Полиномиальная регрессия расширяет линейную регрессию путем добавления степенных зависимостей от признаков. В этом случае, как указано в degree=2, полиномиальная степень равна двум, поэтому она является квадратической. Это означает, что помимо представления всех признаков в линейной форме (например, DEP_FPH), эти признаки присутствуют в квадратичной форме (например, DEP_FPH2), а также много перекрестных членов для всех 21 признаков. Другими словами, для DEP_FPH будут существовать такие члены взаимодействия, как DISTANCE, DEP_FPH, задержка DEP_FPH и т. д. в отношении остальных признаков.  linear_interact. Регрессия похожа на полиномиально-регрессионную модель, но без квадратичных членов. Другими словами, присутствуют только перекрестные члены (члены взаимодействия признаков), как предполагает аргумент interaction_only=True. Эта регрессия полезна тем, что нет никаких оснований полагать, будто какой-либо признак имеет завивисимость, которая может быть лучше аппроксимирована квадратичными членами. Тем не менее вполне возможно, что влияние оказывается именно взаимодействием с другими признаками.  ridge. Гребневая регрессия — это вариация линейной регрессии. Однако, не- смотря на то что метод линейной регрессии, называемый методом наименьших квадратов (МНК — ordinary least squares, OLS), довольно хорошо справляется с минимизацией ошибки при обучении модели на данных, он делает это без переобучения. Проблема здесь в том, что МНК трактует все признаки одинаково, поэтому модель усложняется по мере добавления каждой новой переменной. Как следует из слова "переобучение", результирующая модель аппроксимирует тренировочные данные весьма точно, что приводит к минимальному систематическому смещению (bias), но к максимальной дисперсии. В этом компромиссе между систематическим смещением и дисперсией есть золотая середина, и попасть в эту середину можно путем уменьшения сложности, добавляемой введением слишком большого числа признаков. Линейная регрессия сама по себе для этого не приспособлена. Именно здесь на выручку приходит гребневая регрессия вместе с регуляризацией. Она делает это путем уменьшения коэффициентов, которые не вносят вклад в работоспособность модели, при оптимизации с использованием функции потерь с добавлением штрафного члена, именуемого нормой L2. В данном примере мы используем кросс-валидированную версию гребня (RidgeCV), варьируя гиперпараметры (alphas).  decision_tree. Дерево решений точно соответствует своему названию. Вообра- зите древовидную структуру, в которой в каждой точке расщепления ветвей выполняется "тест" на признаке, расщепляющий наборы данных на ветви. Когда
86 Часть I. Введение в интерпретацию машинного обучения ветви перестают расщепляться, они становятся листьями, и на каждом листе принимается решение, будь то отнесение к классу в случае классификации или же фиксированное значение в случае регрессии. В этом дереве мы задаем предел max_depth=7, чтобы предотвращать переобучение, т. к. чем дерево больше, тем ближе оно к тренировочным данным.  rule_fit. RuleFit — регуляризованная линейная регрессия, расширенная за счет включения признаковых взаимодействий в форме правил. Правила формируются путем обхода дерева решений за исключением того, что эта регрессия отбрасывает листья и сохраняет признаковые взаимодействия, обнаруженные при обходе ветвей при продвижении к этим листьям. Здесь используется лассорегрессия, в которой, как и гребневой, применяется регуляризация, но вместо нормы L2 в ней берется норма L1. Как следствие, бесполезные признаки в итоге получают нулевой коэффициент, а не просто сходятся к нулю, как в случае с L2. Мы лимитируем число правил значением 150 (max_rules=150), а атрибут rfmode='regress' сообщает RuleFit, что эта задача является регрессионной, поскольку указанная модель также может использоваться для классификации. В отличие от всех других используемых здесь моделей, эта модель не входит в состав экосистемы scikit-learn, она была создана Кристофом Мольнаром (Christoph Molnar), который адаптировал ее на основе исследовательской статьи.  kNN. k ближайших соседей (kNN) — простой метод, основанный на допущении о локальности, которое заключается в том, что близко расположенные друг к другу точки данных похожи. Другими словами, они должны иметь одинаковые значения предсказания, и на практике эта догадка выглядит неплохой, поэтому данный метод берет k точек данных, ближайших к точке, которую вы хотите предсказать, и на основе этого выводит предсказание. В нашем случае n_neighbors=7, и поэтому k  7 . Указанная модель принадлежит классу моделей машинного обучения на основе экземпляров, а также называется ленивым учеником, потому что она всего-навсего хранит тренировочные данные. Во время вывода она задействует тренировочные данные для расчета сходства с точками и на основе этого генерирует предсказание. Это противоречит тому, что делают модельно-ориентированные приемы машинного обучения, или усердные ученики, использующие тренировочные данные для усвоения формул, параметров, коэффициентов или систематического смещения/весов, которые затем используются для предсказания во время выведения результата.  random_forest (случайный лес). Вообразите не одно, а сотни деревьев решений, натренированных на случайных признаковых комбинациях и случайных выборках данных. Случайный лес берет средний или средневзвешенный ответ, полученный от этих случайно сгенерированных деревьев решений, чтобы создавать более надежную предсказательную модель. Эта концепция обучения менее эффективных моделей в параллельном режиме и их комбинирование с использованием процесса усреднения называется бэггингом, или модельным усреднением. Указанный метод является ансамблевым, потому что он объединяет более одной модели (обычно именуемых слабыми учениками) в сильного ученика. Помимо бэггинга, есть еще два ансамблевых метода — бустинг и стэкинг. Для про-
Глава 3. Трудности интерпретации 87 ведения более глубокого бэггинга деревья подходят лучше, т. к. они уменьшают дисперсию ошибок. В нашем случае мы примем значение max_depth равным 7.  mlp. Многослойный персептрон — классическая ("ванильная") нейронная сеть (последовательного) прямого и обратного распространения, и в ней используются нелинейные активационные функции (в MLPRegressor по умолчанию используется активация ReLU), для обучения применяются стохастический градиентный спуск и обратное распространение. В данном случае мы используем 21 нейрон в первом и единственном скрытом слое, отсюда hidden_layer_sizes=(21,), проводим обучение в течение 500 эпох (max_iter=500) и заканчиваем обучение, когда валидационный балл перестанет улучшаться (early_Stop=True). Если вы не знакомы с некоторыми из этих моделей, не переживайте! Мы рассмотрим их подробнее далее в этой главе либо позже в книге. Кроме того, обратите внимание, что некоторые модели полностью стохастичны. В целях обеспечения воспроизводимости мы задали значение аргумента random_state. Следует всегда стремиться устанавливать его значение, иначе этот аргумент всякий раз будет устанавливаться случайно, что затруднит воспроизводимость ваших результатов. Теперь давайте прокрутим наш словарь моделей (reg_models) в цикле, проведем их подгонку под тренировочные данные, а также выполним предсказания и вычислим две метрики, основываясь на качестве этих предсказаний. Затем сохраним обученную модель, тестовые предсказания и метрики в словаре для последующего использования. Обратите внимание, что модель rulefit принимает только массивы NumPy, поэтому мы не сможем выполнять их подгонку в одинаковом ключе. Кроме того, обратите внимание, что обучение моделей rulefit и mlp занимает больше времени, чем остальных, поэтому на их выполнение уйдет несколько минут: for model_name in reg_models.keys(): if model_name != 'rulefit': fitted_model = reg_models[model_name]['model'].fit(X_train, y_train_reg) else: fitted_model = reg_models[model_name]['model'].fit(X_train.values, y_train_reg.values, X_test.columns) y_train_pred = fitted_model.predict(X_train.values) y_test_pred = fitted_model.predict(X_test.values) reg_models[model_name]['fitted'] = fitted_model reg_models[model_name]['preds'] = y_test_pred reg_models[model_name]['RMSE_train'] = math.sqrt( metrics.mean_squared_error(y_train_reg, y_train_pred)) reg_models[model_name]['RMSE_test'] = math.sqrt( metrics.mean_squared_error(y_test_reg, y_test_pred)) reg_models[model_name]['R2_test'] = metrics.r2_score(y_test_reg, y_test_pred) Теперь можно конвертировать словарь в кадр данных pandas (DataFrame) и вывести метрики на экран в отсортированном виде и с цветовой кодировкой: reg_metrics = pd.DataFrame.from_dict(reg_models, 'index')[['RMSE_train', 'RMSE_test', 'R2_test']]
88 Часть I. Введение в интерпретацию машинного обучения reg_metrics.sort_values(by='RMSE_test').style.background_gradient( cmap='viridis', low=1, high=0.3, subset=['RMSE_train', 'RMSE_test']).background_gradient( cmap='plasma', low=0.3, high=1, subset=['R2_test']) Приведенный выше исходный код выводит на экран рис. 3.2. Обратите внимание, что цветовая кодировка работает не во всех имплементациях приложения Jupyter Notebook. Рис. 3.2. Регрессионные метрики для наших моделей В целях интерпретирования метрик рис. 3.2 сначала необходимо понять, что они означают, как в целом, так и в контексте этого регрессионного упражнения.  Корень из средней квадратичной ошибки (root mean square error, RMSE) оп- ределяется как среднее квадратичное отклонение остатков. Это квадратный корень из квадратов остатков, деленных на число наблюдений, в данном случае — рейсов. В среднем указанная метрика сообщает об отдаленности предсказания от фактических данных, и, как можно судить по цветовой кодировке, чем она меньше, тем лучше, потому что мы хотим, чтобы предсказания были как можно ближе к фактическим данным в тестовом (отложенном) наборе данных. Мы также включили эту метрику для тренировочного набора данных, чтобы увидеть, насколько хорошо модель обобщается. Ожидается, что тестовая ошибка будет выше, чем тренировочная, но не намного. Если это так, то, как и в случае с моделью random_forest, нужно отрегулировать несколько параметров. В данном случае следует сократить максимальную глубину деревьев, увеличить число деревьев (так называемых оценщиков) и уменьшить максимальное число используемых признаков. С другой стороны, в случае с моделью knn можно скорректировать k, но она, ожидаемо, вследствие природы ленивого ученика, покажет более высокую результативность на тренировочных данных. В любом случае эти числа довольно хороши, потому что даже наша наихудшая модель показывает результативность ниже тестовой RMSE, равной 10, и около
Глава 3. Трудности интерпретации 89 половины из них имеют тестовую RMSE менее 7,5, что, вполне возможно, эффективно предсказывает задержку, если брать в среднем, поскольку порог задержки составляет 15 минут. Обратите внимание, что модель linear_poly идет второй, а модель linear_interact — четвертой по результативности, значительно опережая модель linear, и этот факт позволяет предположить, что нелинейность и интерактивность являются важными факторами для обеспечения более высокой предсказательной результативности.  Метрика R-квадрат (R2), также именуемая коэффициентом детерминации, определяется как доля дисперсии в целевой переменной (исходе) y, которая может быть объяснена признаками (предсказателями) X в модели. И, как можно судить по цветовой кодировке, чем она больше, тем лучше. При этом наши модели, по всей видимости, включают X значительных признаков, о чем свидетельствуют коэффициенты корреляции Пирсона. Поэтому, если бы значение R2 было низким, возможно, помогло бы добавление дополнительных признаков, таких как журналы полетов, условия в терминалах и даже те элементы, в рассмотрении которых, по словам руководителей авиакомпаний, они не заинтересованы прямо сейчас, например это влияние вторичных событий и международные рейсы. Млжно было бы заполнить пробелы и понизить дисперсию ошибки предсказаний. Давайте теперь посмотрим, сможем ли мы получить приемлемые значения метрик для задачи классификации. Классифицирование рейсов как задержанных либо незадержанных с использованием различных классификационных методов Как и в случае с регрессией, для сравнения и сопоставления классификационных методов мы сначала создадим для них словарь с именем class_models. Каждая модель — это отдельный словарь и функция, которая его создает в атрибуте model. Указанная структура будет использоваться позже для хранения обученной модели и показателей метрик. Модельные классы в этом словаре были выбраны для представления нескольких модельных семейств и для иллюстрации важных концепций, которые мы обсудим позже. Некоторые из них будут нам знакомы, потому что это те же методы, которые используются в регрессии, но применяются к классификации: Class_models = { # Обобщенные линейные модели (GLM-модели) 'logistic':{'model': linear_model.LogisticRegression()}, 'ridge':{'model': linear_model.RidgeClassifierCV(cv=5, alphas=[1e-3, 1e-2, 1e-1, 1], class_weight='balanced')}, # Дерево 'decision_tree':{'model': tree.DecisionTreeClassifier(max_depth=7, random_state=rand)},
90 Часть I. Введение в интерпретацию машинного обучения # Ближайшие соседи 'knn':{'model': neighbors.KNeighborsClassifier(n_neighbors=7)}, # Наивный Байес 'naive_bayes':{'model': naive_bayes.GaussianNB()}, # Ансамблевые методы 'gradient_boosting':{'model': ensemble.GradientBoostingClassifier(n_estimators=210)}, 'random_forest':{'model':ensemble.RandomForestClassifier(max_depth=11, class_weight='balanced', random_state=rand)}, # Нейронные сети 'mlp':{'model': make_pipeline(StandardScaler(), neural_network.MLPClassifier(hidden_layer_sizes=(7,), max_iter=500, early_stopping=True, random_state=rand))} } Перед тем как приступить к подгонке этих моделей под данные, кратко объясним каждую из них по очереди.  logistic. Логистическая регрессия была представлена в главе 2. При работе с данной моделью наблюдаются те же преимущества и недостатки, что и при работе с линейной регрессей. Например, отсутствие корреляций между признаками должно учитываться вручную. Как и другие классификационные модели, логистическая регрессия возвращает вероятность от 0 до 1, которая при ее большей близости к 1 обозначает вероятное соответствие положительному классу, а при большей близости к 0 — невероятное соответствие положительному классу и, следовательно, вероятное соответствие отрицательному классу. Значение 0,5 может использоваться в качестве порога для точного определения класса, но оно не обязательно должно быть именно таким. Как мы рассмотрим позже, с интерпретацией и результативностью связаны причины, которые позволяют корректировать указанный порог. Обратите внимание, что мы имеем дело с задачей бинарной классификации, поэтому и выбираем только между задержкой (положительный класс) и отсутствием задержки (отрицательный класс), но этот метод можно обобщить и на мультиклассовую классификацию. Тогда он будет называться мультиномиальной классификацией.  ridge. Гребневая классификация использует тот же регуляризационный под- ход, который присутствует в гребневой регрессии, но применительно к классификации. Это делается путем изменения кодировки целевых переменных в 1 (для отрицательного класса) и оставления 1 для положительного класса, а затем выполнения гребневой регрессии. "За кулисами" ее маскированная регрессия будет предсказывать значения между 1 и 1, а затем конвертировать их обратно в шкалу 0–1. Как и в случае с RidgeCV для регрессии, RidgeClassifierCV использует кросс-валидацию с исключением по одному, т. е. сначала она разбивает данные на разные равноразмерные наборы — в нашем случае мы используем пять наборов (cv=5), а затем удаляет признаки по одному, чтобы посмотреть, насколько высокой будет модельная результивность без них, в среднем во всех пяти набо-
Глава 3. Трудности интерпретации 91 рах. Те признаки, которые имеют наименьшее влияние на итоговый результат, исключаются с помощью регуляризации, причем поиск ее гиперпараметров (alphas) осуществляется путем их варьирования и анализа изменения метрики качества модели на кросс-валидации. Как и во всех регуляризационных технических приемах, суть состоит в том, чтобы упростить модель, минимизируя влияние менее заметных признаков.  decision_tree. Подобного рода классическое дерево решений также называется классификационно-регрессионным деревом (classification and regression tree, CART), потому что его можно использовать для регрессионных либо классификационных задач. Для обеих задач оно имеет одинаковую архитектуру, но функционирует немного по-разному, как алгоритм, используемый для решения вопроса о том, в каком месте "расщеплять" ветвь. В этом случае мы позволяем нашим деревьям иметь глубину, только равную 7.  knn. Метод k ближайших соседей тоже может применяться к классификацион- ным задачам, за исключением того, что вместо усреднения значений категориальных целевых переменных (или меток) ближайших соседей указанный метод выбирает наиболее частый (т.е. моду). Для классификации мы тоже используем число k, равное 7 (n_neighbors).  naive_bayes. Гауссов наивный Байес является частью семейства наивных байе- совых классификаторов, которые называются наивными из-за того, что они принимают некоторые допущения о независимости признаков друг от друга, что обычно не совсем так. Это сильно затрудняет способность указанного метода предсказывать, если принятое допущение неверно. А байесовым метод называется потому, что он основан на теореме Байеса об условных вероятностях, которая гласит, что условная вероятность класса есть вероятность класса, умноженная на вероятность признаков при условии наличия класса. Гауссов наивный Байес принимает дополнительное допущение о том, что непрерывные значения имеют нормальное распределение, т. е. гауссово распределение.  gradient_boosting. Как и случайный лес, градиентно-бустированные деревья тоже являются ансамблевым методом, но в нем задействуется бустинг (модельное усиление) вместо бэггинга (модельного усреднения). Бустинг работает не в параллельном, а в последовательном режиме, итеративно тренируя слабых учеников и интегрируя их силы в более сильном ученике, при этом адаптируя еще одного слабого ученика, берущего на себя их слабости. Хотя ансамбли и бустинг, в частности, могут выполняться с помощью модельного класса, в этом конкретном методе используются деревья решений. Мы ограничили число деревьев знчением 210 (n_estimators=210).  random_forest. Тот же случайный лес, что и в случае регрессии, за исключением того, что в нем используются не регрессионные, а классификационные деревья решений.  mlp. Тот же многослойный персептрон, что и в случае регрессии, но в выход- ном слое по умолчанию используется логистическая функция, получающая вероятности, которые в свою очередь конвертируются в 1 либо 0 на основе порога
92 Часть I. Введение в интерпретацию машинного обучения 0,5. Еще одно отличие состоит в том, что в первом и единственном скрытом слое (hidden_layer_sizes=(7,)) используется семь нейронов, потому что для достижения оптимального результата двоичная классификация, как правило, требует меньшего их числа. Обратите внимание, что в некоторых из этих моделей для классов используются сбалансированные веса (class_weight='balanced'), что очень важно, т. к. эта задача является несбалансированной классификационной. Под этим мы подразумеваем, что отрицательные классы значительно превосходят положительные классы по численности. Можно посмотреть, как эта несбалансированность выглядит для наших тренировочных данных: y_train_class[y_train_class==1].shape[0] / y_train_class.shape[0] А вот результат: 0.061283264255549 Хорошо видно, что доля положительных классов в наших тренировочных данных составляет всего 6% общего числа. Модели, которые это учитывают, будут достигать более объективных результатов. Существуют разные способы учета дисбаланса классов, и мы займемся их подробным обсуждением в главе 11, но аргумент class_weight='balanced' применяет вес обратно пропорционально частотам классов, что дает недостаточному по численности положительному классу возможность подниматься вверх. Обучение и оценивание классификационных моделей Теперь давайте прокрутим словарь моделей (class_models) в цикле, выполним их обучение на тренировочных данных и предскажем обе вероятности и класс, за исключением гребневой модели ridge, которая не выдает вероятности на выходе. Затем мы вычислим пять метрик, основываясь на качестве этих предсказаний. Наконец, мы сохраним обученную модель, тестовые предсказания и метрики в словаре для последующего использования. Пока будет выполняться следующий ниже фрагмент исходного кода, можно пойти попить кофе, потому что обучение градиентно-бустированной (gradient_boosting) модели из библиотеки sklearn занимает больше времени, чем остальных, и выполнение этого фрагмента может занять несколько минут: for model_name in class_models.keys(): fitted_model = class_models[model_name]['model'].fit(X_train, y_train_class) y_train_pred = fitted_model.predict(X_train.values) if model_name == 'ridge': y_test_pred = fitted_model.predict(X_test.values) else: y_test_prob = fitted_model.predict_proba(X_test.values)[:,1] y_test_pred = np.where(y_test_prob > 0.5, 1, 0) class_models[model_name]['fitted'] = fitted_model class_models[model_name]['probs'] = y_test_prob class_models[model_name]['preds'] = y_test_pred
Глава 3. Трудности интерпретации 93 class_models[model_name]['Accuracy_train'] = metrics.accuracy_score( y_train_class, y_train_pred) class_models[model_name]['Accuracy_test'] = metrics.accuracy_score( y_test_class, y_test_pred) class_models[model_name]['Recall_train'] = metrics.recall_score( y_train_class, y_train_pred) class_models[model_name]['Recall_test'] = metrics.recall_score( y_test_class, y_test_pred) if model_name != 'ridge': class_models[model_name]['ROC_AUC_test'] = metrics.roc_auc_score( y_test_class, y_test_prob) else: class_models[model_name]['ROC_AUC_test'] = 0 class_models[model_name]['F1_test'] = metrics.f1_score( y_test_class, y_test_pred) class_models[model_name]['MCC_test'] = metrics.matthews_corrcoef( y_test_class, y_test_pred) Теперь можно конвертировать словарь во фрейм данных и вывести метрики на экран в отсортированном виде и с цветовой кодировкой: class_metrics = pd.DataFrame.from_dict(class_models,'index')[['Accuracy_train', 'Accuracy_test', 'Recall_train', 'Recall_test', 'ROC_AUC_test', 'F1_test', 'MCC_test']] class_metrics.sort_values( by='ROC_AUC_test', ascend-ing=False).style.background_gradient( cmap='plasma', low=0.3, high=1, subset=['Accuracy_train', 'Accura-cy_test']).background_gradient(cmap='viridis', low=1, high=0.3, subset=['Recall_train', 'Recall_test', 'ROC_AUC_test', 'F1_test', 'MCC_test']) Приведенный выше исходный код выводит на экран рис. 3.3. В целях интерпретирования метрик рис. 3.3 сначала необходимо понять, что они означают как в целом, так и в контексте этого классификационного упражнения.  Точность (accuracy) — самая простая мера эффективности классификационной задачи, которая является процентом правильных предсказаний по сравнению со всеми предсказаниями. Другими словами, в двоично-классификационной задаче ее можно рассчитать, сложив число истинноположительных (true positives, TP) и истинноотрицательных (true negatives, TN) классификаций и разделив их на численность всех сделанных предсказаний. Как и в случае с регрессионными
94 Часть I. Введение в интерпретацию машинного обучения метриками, точность измеряется как для обучение, так и для валидации, чтобы вовремя обнаружить переобучение. Рис. 3.3. Классификационные метрики наших моделей  Полнота. Несмотря на то что метрика точности выглядит отлично, в данном случае полнота (recall) намного лучше, т. к. у вас точность может быть 94%, и это (на первый взгляд) вполне приемлемо, а на самом деле оказывается, что вы всегда предсказываете отсутствие задержки! Другими словами, даже получении высокого значения метрики точности она не несет никакого смысла, если вы с достаточной точностью не предсказываете минорные классы в дисбалансированном наборе данных. Более правильным критерием в этом случае будет полнота (т. е. чувствительность или частота истинноположительных классификаций), а именно TP TP  FN  , и ее можно интерпретировать как число возвращенных релевантных результатов. Другими словами, в данном случае предсказан процент фактических задержек. Еще одной хорошей мерой, включающей истинноположительные классификации, является точность (precision, или глубина резкости), т. е. степень релевантности наших предсказанных образцов, а именно TP TP  FP  . В данном случае она будет составлять процент предсказанных задержек, которые оказались фактическими задержками. В случае несбалансированных классов рекомендуется использовать обе метрики, но, в зависимости от ваших предпочтений FN 1 или FP2, вы будете предпочитать полноту вместо точности, или наоборот.  ROC-AUC. ROC — это аббревиатура receiver operating characteristic (обозна- чающая операционную характеристику приемника), которая была придумана для отделения сигнала от шума. Указанная метрика в графическом виде отражает зависимость частоты истинноположительных классификаций (полноты) по оси x и частоты ложноположительных классификаций по оси y. AUC (area under the curve) обозначает площадь под кривой, т. е. число от 0 до 1, которое диагностирует предсказательную способность классификатора, где 1 — совершенная; 0,5 не лучше подбрасывания монеты, а все, что ниже, означает, что если бы 1 2 FN (false negative) — ложноотрицательный (исход). — Прим. ред. FP (false positive) — ложноположительный (исход). — Прим. ред.
Глава 3. Трудности интерпретации 95 мы перевернули результаты предсказания, то получили бы более точное предсказание. В целях иллюстрации давайте сгенерируем кривую ROC для нашей модели с худшей результативностью, наивного Байеса, в соответствии с метрикой AUC: plt.tick_params(axis='both', which='major', labelsize=12) fpr, tpr, _ = metrics.roc_curve(y_test_class, class_models['naive_bayes']['probs']) plt.plot(fpr, tpr, label='ROC-кривая (площадь = %0.2f)' % class_models['naive_bayes']['ROC_AUC_test']) plt.plot([0, 1], [0, 1], 'k–') # линия подбрасывания монеты plt.xlabel('Частота ложноположительных классификаций', fontsize=14) plt.ylabel('Частота истинноположительных классификаций', fontsize=14) plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.0]) plt.legend(loc="lower right") Приведенный выше исходный код выводит на экран рис. 3.4. Обратите внимание, что диагональная линия обозначает половину площади, другими словами, это место, в котором модель обладает предсказательными качествами, такими же как при подбрасывании монеты.  Оценка F1 также называется гармоническим средним значением точности и пол- ноты, потому что она рассчитывается следующим образом: 2TP  2TP  FP  FN  . Поскольку она включает и точность, и полноту, которые относятся к доле истинноположительных классификаций, этот вариант метрики можно эффективно использовать при несбалансированном наборе данных и отсутствии предпочтения точности либо полноты.  Коэффициент корреляции Мэтьюса (Matthews correlation coefficient, MCC) — это взятая из биостатистики метрика, которая набирает популярность в более широком сообществе исследователей данных, т. к. обладает способностью демонстрировать высокие баллы с объективным учетом TP, FN, TN и FP благодаря учету пропорции классов. Это свойство делает его оптимальным для несбалансированных классификационных задач. В отличие от всех других использовавшихся до этого метрик MCC варьирует не между 0 и 1, а между 1 (полное несогласие) и 1 (полное согласие между предсказаниями и фактическими данными). Точка 0 посередине эквивалентна случайному предсказанию. Наши классификационные метрики в основном очень хороши, если они превышают точность 96% и полноту 75%. Однако даже полнота — это еще не всё. Например, RandomForest из-за балансировки классов весами получил самую высокую полноту, но плохо справился с F1 и MCC, что намекает на не очень хорошую точность. Гребневая классификация тоже имела такую же настройку и показала столь слабую оценку F1, что точность оказалась критически малой. Это не значит, что указанный технический прием взвешивания органически неверен, но он нередко требует большего контроля. Эта книга как раз посвящена техническим приемам достижения правильного баланса между объективностью и точностью, точностью и надежностью, надежностью и валидностью и т. д. Такой балансирующий акт требует
96 Часть I. Введение в интерпретацию машинного обучения большого числа метрик и визуализаций. Ключевым выводом из этого упражнения должно быть следующее: одна-единственная метрика не расскажет вам всю историю целиком, а интерпретация заключается в том, чтобы рассказывать наиболее релевантную и достаточно полную историю. Рис. 3.4. Кривая ROC для алгоритма наивного Байеса Теперь, в завершение общего обзора, мы собираемся попробовать несколько методов понижения размерности. Визуализация задержанных рейсов с помощью методов понижения размерности Визуализация и интерпретация, собственно, не всегда имеют дело с материальными сущностями. Работая с машинным обучением, мы часто имеем дело с неявными взаимосвязями между признаками, которые, учитывая их сложность, трудно оты-
Глава 3. Трудности интерпретации 97 скивать и еще труднее описать или визуализировать. Заметно уменьшить сложность в их визуализировании можно посредством применения методов понижения размерности, которые могут помочь составить некоторые представления о задаче и натолкнуть на мысли о дальнейших действиях. В целях сравнения и сопоставления методов понижения размерности мы сначала создадим для них словарь dimred_methods. Каждый метод — это отдельный словарь и функция, которая его создает в атрибуте method. Указанная структура будет использоваться позже для хранения данных после понижения размерности или, в случае cvae, обученной модели. Методы в словаре были выбраны так, чтобы представлять несколько семейств методов с целью иллюстрации важных концепций, которые мы обсудим позже. Учитывая потенциально ресурсоемкий характер некоторых методов, мы используем сокращенную девятистолбцовую версию нашего набора данных как для обучения (X_train_abbrev), так и для валидации (X_test_abbrev). И мы также извлекаем только 10%-ю выборку из тестового набора данных, используя случайно сгенерированный индекс (sample_idx). Это просто массив чисел в формате numpy, которые говорят о том, какие наблюдения были отобраны случайно. Если вы располагаете большими вычислительными ресурсами для работы, можете свободно изменить размер выборки (sample_size) по своему усмотрению на бо́льшую величину процента: X_train_abbrev = X_train.iloc[:,[0, 1, 2, 4, 8, 9, 11, 17, 20]] X_test_abbrev = X_test.iloc[:,[0, 1, 2, 4, 8, 9, 11, 17, 20]] np.random.seed(rand) sample_size = 0.1 sample_idx = np.random.choice(X_test.shape[0], math.ceil(X_test.shape[0]*sample_size), replace=False) dimred_methods = { # Разложение 'pca':{'method': decomposition.PCA(n_components=3, random_state=rand)}, # Усвоение проекций в топологическом многообразии 't-sne':{'method': manifold.TSNE(n_components=3, random_state=rand)}, # Вариационные автокодировщики 'vae':{'method': cvae.CompressionVAE(X_train_abbrev.values, dim_latent=3, tb_logging=False)} } Прежде чем приступить к применению этих методов к нашим данным, кратко объясним каждый по очереди.  pca. Анализ главных компонент (principal component analysis, PCA) является одним из старейших методов понижения размерности, и обычно он выполняется путем разложения матрицы ковариаций данных на собственные значения. В отличие от других методов, обзор которых мы здесь проводим, данный алгоритм является вычислительно быстрым. Так, в ходе разложения на собственные значения мы отыскиваем ортогональные векторы: векторы неидентичны друг другу с точки зрения геометрии. Это делается для того, чтобы анализ главных компонент мог понизить размерность задачи до того, что признаки перестанут
98 Часть I. Введение в интерпретацию машинного обучения коррелировать друг с другом. Термин "главные компоненты" в названии указанного метода используется ввиду того, что собственные векторы также называются главными направлениями. Это имеет смысл, поскольку уменьшение признаков данных происходит путем их проецирования на меньшее число размерностей так, чтобы потеря информации была сведена к минимуму в допущении, что направления с наибольшими дисперсиями в значениях являются наиболее важными.  t-sne. T-распределенное стохастическое вложение соседей (T-distributed stochastic neighbor embedding, t-SNE) считается одним из новых методов понижения размерности и, в отличие от анализа главных компонент (PCA), является нелинейным, поэтому хорошо сохраняет нелинейные соотношения. Кроме того, в отличие от PCA, в основе t-SNE лежит другая математическая теория: не линейная алгебра, а теория вероятностей. Метод t-SNE основан на минимизиции разницы между попарными сходствами распределений между высокоразмерным (наши входные данные) и низкоразмерным представлением с использованием дивергенции Кульбака — Лейблера (т. е. меры расстояния). В отличие от PCA, который фокусируется на размещении непохожих точек как можно дальше друг от друга, t-SNE нацелен на размещение похожих точек близко друг к другу.  vae. Вариационные автокодировщики (variational autoencoder, VAE) — метод глубокого обучения, который усваивает наилучший способ кодирования данных из высокой размерности в низкую, а затем выполняет декодирование их обратно из низкой размерности в высокую. Поскольку в нем для нейронной сети используется линейная алгебра и измеряется дивергенция Кульбака — Лейблера между распределениями значений вероятности, в него встроены элементы как PCA, так и t-SNE. В то время как VAE минимизирует ошибку реконструкции между исходными и восстановленными данными, он не сохраняет расстояния между похожими точками на низком уровне, как это делает t-SNE. В отличие от PCA и t-SNE, VAE обеспечивает обратимость между низкими и более высокими размерностями и даже способен сгенерировать новые данные. Обратите внимание, что во всех методах мы сводим данные к трем компонентам (n_components=3) или размерностям (dim_latent=3). Кроме того, vae не просто метод понижения размерности, а класс моделей машинного обучения, поэтому он изначально будет обучаться на данных. Таким образом, в отличие от других, мы будем для этой цели использовать тренировочные данные X_train_abbrev. Теперь давайте пройдем через наш словарь методов (dimred_methods) в цикле и выполним редукцию размерности с каждым из вышеперечисленных методов. В случае с vae также будет обученная модель. Наконец, мы сохраняем редуцированные данные и обученную модель vae в словаре для последующего использования. На выполнение двух методов из этого комплекта уйдет по несколько минут, так что не переживайте, если работа займет некоторое время: for method_name in dimred_methods.keys(): if method_name != 'vae': lowdim_data = dimred_methods[method_name]['method'].fit_transform( X_test_abbrev.values[sample_idx])
Глава 3. Трудности интерпретации 99 else: fitted_model = dimred_methods[method_name]['method'].train() lowdim_data = fitted_model.embed(X_test_abbrev.values[sample_idx]) dimred_methods[method_name]['fitted'] = fitted_model dimred_methods[method_name]['lowdim'] = lowdim_data Итак, что же можно сделать с низкоразмерными данными, которые у нас сейчас получились? Для начала можно их визуализировать! К примеру, можно сделать одну аккуратную визуализацию, а именно, нарисовать три размерности — назовем их x, y и z — по две размерности за раз, при этом показывая наши классификации разными цветами. Это будет похоже на просмотр трех размерностей с разных точек зрения (сверху, сбоку и спереди). Мы будем использовать графопостроительную функцию plot_3dim_decomposition, которая берет наши низкоразмерные данные z и строит три их размерности при кодировании цветовых меток y_labels. Первоначально наши метки могут быть фактическими значениями Y (кодированными 0 для "не задержан" и 1 для "задержан"), но для того чтобы график мог отображать легенду, мы также включим имена y_names, т. е. словарь, который помогает перевести их в легенду графика: Y_names = {0:'Не задержан', 1:'Задержан'} Теперь давайте построим график низкоразмерных данных PCA в сопоставлении с отобранными данными y_test_class: mldatasets.plot_3dim_decomposition(dimred_methods['pca']['lowdim'], y_test_class.values[sample_idx], y_names) По рис. 3.5 можно судить, что в некоторых частях метки "Задержан" можно отделить от меток "Не задержан", и всё становится яснее при сравнении одних размерностей нежели других. Рис. 3.5. PCA с тремя компонентами, нанесенными по две размерности за раз, и с кодированными по цвету метками Как насчет того, чтобы сделать то же самое для t-SNE и VAE? mldatasets.plot_3dim_decomposition(dimred_methods['t-sne']['lowdim'], y_test_class.values[sample_idx], y_names) mldatasets.plot_3dim_decomposition(dimred_methods['vae']['lowdim'], y_test_class.values[sample_idx], y_names)
100 Часть I. Введение в интерпретацию машинного обучения Приведенный выше фрагмент исходного кода выводит на экран рис. 3.6 и 3.7 соответственно для t-SNE и VAE. Рис. 3.6. t-SNE с тремя компонентами, нанесенными по две размерности за раз, и с кодированными цветом метками Рис. 3.7. VAE с тремя размерностями, нанесенными по две размерности за раз, и с кодированными по цвету метками Скопление на графике модели t-SNE (см. рис. 3.6) очень плотное, но все еще можно найти кластеры, в которых преобладают задержки, а с VAE (см. рис. 3.7) кластеры идентифицировать труднее, особенно в области, где сосредоточена бо́льшая часть фиолетового. Судя по этим начальным шагам, указанные методы можно использовать для выявления областей наибольшей концентрации ваших классов. Но разве это все, что тут есть? Понижение размерности можно задействовать самыми разными способами. Некоторые из них совершенно наглядны в визуальном плане, а другие могут быть расширены для улучшения отбора и конструирования признаков, обнаружения аномалий и даже моделирования, где их можно использовать для понимания промежуточных шагов. Но вернемся к визуализации, которую на данный момент можно использовать даже для отладки моделей. Например, если вместо фактических двоичных классов вы по каждому наблюдению изображали бы классификационные ошибки (FP, FN) или их отсутствие (TP, TN), то вы могли бы визуализировать места, где находится большинство ваших ошибок в той или иной модели. С этой целью мы будем использовать функцию encode_classification_error_vector, которая принимает наши актуаль-
Глава 3. Трудности интерпретации 101 ные данные и модельные предсказания и возвращает массив классификационных ошибок (error_vector), а также соответствующий ему словарь для легенды графика error_labels. Затем можно вставить всё это в ту же самую функцию plot_3dim_decomposition. Ее можно использовать для визуализации классификационных ошибок гребневого классификатора, обучение которого мы выполнили ранее, одного из наших классификаторов с наихудшим уровнем результативности: Y_test_class_samp = y_test_class.values[sample_idx] y_test_pred_samp = class_models['ridge']['preds'][sample_idx] error_vector, error_labels = encode_classification_error_vector(y_test_class_samp, y_test_pred_samp) Теперь можно визуализировать эти классификационные ошибки, используя все три метода понижения размерности: mldatasets.plot_3dim_decomposition(dimred_methods['pca']['lowdim'], error_vector, error_labels) mldatasets.plot_3dim_decomposition(dimred_methods['t-sne']['lowdim'], error_vector, error_labels) mldatasets.plot_3dim_decomposition(dimred_methods['vae']['lowdim'], error_vector, error_labels) Приведенный выше фрагмент исходного кода выводит на экран рис. 3.8. На рис. 3.8 по всем трем методам понижения размерности можно выявить "слабые" области, в которых преобладают ложноположительные (FP) и ложноотрицательные (FN) классификации. В эти области можно углубиться и попробовать разные комбинации признаков на понижение размерности, чтобы увидеть, имеет ли это какоелибо значение, или даже выполнить некоторые преобразования на ваших признаках. Если вы обнаружите, что три размерности не имеют достаточной выразительности, чтобы понять закономерности, то попробуйте больше размерностей. Здесь есть над чем поэкспериментировать. Если методы понижения размерности позволяют улавливать суть взаимосвязей ваших наборов данных, то почему бы не начать тренировать на них? В некоторых случаях именно так и следует поступать, но x, y и z лишены изначально заложенного в данные смысла, а смысл является неотъемлемой частью интерпретации. Но эти связи можно отыскать в кластерах, где модели классифицируют неправильно, и это можно распространить на все модели. На самом деле, вы могли бы задать вопрос и дать на него ответ: где у всех моих моделей стабильно есть ложноположительные (FP) либо ложноотрицательные (FN) классификации? Можно найти кластеры, в которых это происходит, и сделать некоторые выводы по тому, как улучшить свои модели. При использовании визуализации для проверки качества моделей технические решения не лимитированы методами понижения размерности. Некоторые модельные классы легко визуализируются, и об этом мы расскажем в данной главе позже. Теперь же давайте рассмотрим некоторые ограничения традиционных методов, которые мы учились применять на практике.
102 Часть I. Введение в интерпретацию машинного обучения Рис. 3.8. PCA, t-SNE и VAE, каждый с тремя компонентами, нанесенными по две размерности за раз, и с кодированными по цвету классификационными ошибками Ограничения традиционных методов модельной интерпретации В двух словах, традиционные методы интерпретации охватывают только поверхностные вопросы о ваших моделях, такие как:  Показывают ли они хорошую результативность в целом?  Какие изменения в гиперпараметрах могут повлиять на предсказательную ре- зультативность?  Какие скрытые шаблоны, регулярности или закономерности можно найти между признаками и их предсказательной результативностью? Эти вопросы имеют весьма ограничительный характер, если пытаться понять не только то, что ваша модель работает как надо, но и почему и как она это делает.
Глава 3. Трудности интерпретации 103 Непонимание в этих вопросах может привести к неожиданным проблемам с вашей моделью, которые не обязательно будут сразу очевидны. Давайте учтем, что модели, будучи уже развернутыми на серверах для решения какой-то задачи, не являются статичными, а являются динамичными. Они сталкиваются с иными трудностями, нежели те, с которыми они сталкивались в "лабораторных" условиях, когда вы их тренировали. Они могут сталкиваться не только с трудностями, связанными с результативностью, но и с трудностями, связанными с систематическим смещением, например с дисбалансом из-за недостаточной представленности классов или безопасностью из-за антагонистических атак. Распознав, что в реально существующей среде признаки изменились, нам, возможно, придется добавить какие-то новые признаки вместо того, чтобы просто перетренировывать модель с тем же набором признаков. И если в вашей модели было сделано несколько тревожащих допущений, то вам, вероятно, придется перепроверить весь конвейер. Но как вообще распознавать существование этих проблем? Именно с этого момента вам понадобится совершенно новый набор инструментов интерпретации, которые помогут копать глубже и отвечать на более конкретные вопросы касательно вашей модели. Эти инструменты обеспечивают интерпретации, которые и вправду могут учитывать объективность, подотчетность и прозрачность (FAT), обсуждавшиеся нами в главе 1. Изучение имманентно интерпретируемых моделей (типа белого ящика) До этого в данной главе мы уже выполняли обучение модельных классов, представляющих каждое семейство моделей типа белого ящика, под тренировочные данные. Цель этого раздела состоит в том, чтобы продемонстрировать причину, почему собственно говоря они поддаются интерпретации имманентно, т. е. задействуя внутренние параметры. Мы сделаем это, используя модели, которые были обучены ранее. Обобщенные линейные модели Обобщенные линейные модели (generalized linear models, GLM) — это большое семейство модельных классов, которые имеют модель для каждого статистического распределения. Точно так же, как линейная регрессия, которая исходит из допущения о том, что ваш целевой признак и остатки имеют нормальное распределение, логистическая регрессия исходит из распределения Бернулли. Обобщенные линейные модели существуют для каждого распределения, к примеру, пауссонова регрессия для распределения Пуассона и мультиномиальный отклик для мультиномиального распределения. GLM-модель выбирается на основе распределения вашей целевой переменной и на основе соответствия ваших данных другим допущениям GLM (они варьируются). В дополнение к опорному распределению GLMмодели связываются вместе в единое семейство тем фактом, что все они имеют линейный предсказатель. Другими словами, переменная цели ŷ (или предсказатель)
104 Часть I. Введение в интерпретацию машинного обучения может выражаться математически как взвешенная сумма признаков X, где веса представляют собой коэффициенты b. Так, линейно-предсказательная функция, которая используется совместно всеми GLM-моделями, выглядит следующим образом в виде очень простой формулы, где b являются компонентами вектор-столбца  : yˆ  X . Однако, хотя они используют одну и ту же формулу, у каждой из них есть своя функция, которая обеспечивает связь между линейно-предсказательной функцией и средним значением статистического распределения обобщенной линейной модели (GLM). За счет этого в результирующую модельную формулу может привноситься некоторая нелинейность при сохранении линейной комбинации между коэффициентами b и входными данными X, что может становиться источником путаницы. Тем не менее она является линейной вследствие линейной комбинации. Существует также много вариаций конкретных GLM-моделей. Например, полиномиальная регрессия — это линейная регрессия с многочленами ее признаков, а гребневая регрессия — это линейная регрессия с регуляризацией L2. В этом разделе мы не будем охватывать все обобщенные линейные модели, потому что они не нужны для примера этой главы, но у всех есть убедительные варианты использования. Кстати, существует также аналогичная концепция — обобщенные аддитивные модели (generalized additive model, GAM), т. е. обобщенные линейные модели, которые не требуют линейных комбинаций признаков и коэффициентов и вместо этого сохраняют часть формулы со сложением, но при этом к признакам применяются произвольные функции. GAM-модели также поддаются интерпретации, но они не столь распространены и обычно адаптированы к конкретным случаям использования в нерегламентированном порядке. Линейная регрессия В главе 1 мы рассмотрели формулу простой линейной регрессии, которая имеет только один признак X. Множественная линейная регрессия расширяет ее и имеет любое число признаков, поэтому вместо ŷ  0  1 X 1 она записывается вот так: yˆ  0  1 X 1  2 X 2  ...  n X n , где n — это число признаков; 0 — пересечение (или сдвиговый коэффициент). И благодаря линейной алгебре при x0  1 она может принимать форму простого матричного умножения: yˆ  βX. Метод, используемый для получения оптимальных коэффициентов  , обычных наименьших квадратов, хорошо изучен и понятен. Также, в дополнение к коэф-
Глава 3. Трудности интерпретации 105 фициентам, по каждому из них можно извлекать интервалы уверенности. Правильность модели зависит от соответствия входных данных принятым допущениям: о линейности, нормальности, независимости, (в основном) отсутствии мультиколлинеарности и гомоскедастичности. Линейность уже довольно много обсуждалась ранее, поэтому мы кратко объясним остальное.  Нормальность — это свойство, благодаря которому каждый признак нормально распределен. Это можно проверить с помощью квантильно-квантильного графика (или Q-Q-графика), гистограммы или теста Колмогорова — Смирнова, а ненормальность может быть исправлена нелинейными преобразованиями. Если признак не является нормально распределенным, то это сделает доверительные интервалы его коэффициентов невалидными.  Независимость — это свойсто, которое указывает, что ваши наблюдения (стро- ки в наборе данных) не зависят друг от друга, как разные и несвязанные события. Если ваши наблюдения не являются независимыми, это может повлиять на вашу интерпретацию результатов. Вернемся к примеру этой главы: если бы к одному и тому же рейсу относилось несколько строк, это могло бы нарушить указанное допущение и затруднить понимание результатов. Независимость можно проверить, проведя поиск повторяющихся номеров рейсов.  Отсутствие мультиколлинеарности является желательным в связи с тем, что в противном случае у вас были бы неточные коэффициенты. Мультиколлинеарность возникает, когда признаки сильно коррелированы друг с другом. Ее можно проверить с помощью матрицы корреляций, меры допуска или коэффициента "раздувания" дисперсии (variance inflation factor, VIF), и ее можно исправить, удалив один признак из каждого сильно коррелированного признака.  Гомоскедастичность была кратко затронута в главе 1, и она возникает, когда остатки (ошибки) более или менее одинаковы вдоль линии регрессии. Ее можно проверить с помощью теста Гольдфельда — Квандта, а гетероскедастичность (отсутствие гомоскедастичности) можно исправить нелинейными преобразованиями. Указанное допущение нередко нарушается на практике. Если вы собираетесь использовать линейную регрессию, то всегда полезно выполнить проверку этих допущений перед началом подгонки линейно-регрессионной модели под свои данные (хотя мы еще этого не делали). В этой книге мы не будем описывать подробно, как это делается, потому что речь идет, скорее, о модельноагностических и основанных на глубоком обучении методах интерпретации, нежели о разборе того, как обеспечивать соответствие специфического модельного класса допущениям, таким как нормальность и гомоскедастичность. Тем не менее в главе 2 мы рассмотрели характеристики, которые лучше всего способствуют интерпретации, и мы продолжим искать эти характеристики: нелинейность, немонотонность и интерактивность. Мы будем делать это главным образом потому, что линейность и корреляция признаков и между ними по-прежнему остаются релевантными, независимо от используемого для предсказания модельного класса, а также потому, что эти характеристики можно легко протестировать в методах, используемых для линейной регрессии.
106 Часть I. Введение в интерпретацию машинного обучения Интерпретация Итак, каким образом интерпретировать линейно-регрессионную модель? Легко! Надо просто взять коэффициенты перед признаками и пересечение (intercept). В наших моделях из библиотеки scikit-learn в каждую обученную модель встроены вот эти атрибуты: coefs_lm = reg_models['linear']['fitted'].coef_ intercept_lm = reg_models['linear']['fitted'].intercept_ print('коэффициенты:%s' % coefs_lm) print('пересечение:%s' % intercept_lm) Приведенный выше исходный код выводит на экран следующий результат: коэффициенты: [ 4.54955677e-03 -5.25032459e-03 8.94123625e-01 1.25274473e-01 -6.46799581e-04 ...] пересечение: -37.860211953237275 Таким образом, теперь вы знаете формулу, которая выглядит примерно так: ŷ = -37.86 + 0.0045X1 + -0.0053X2 + 0.894X3 + ... Эта формула должна дать некоторое интуитивное понимание принципа интерпретирования модели в глобальном плане. Интерпретирование каждого коэффициента модели может осуществляться для множественной линейной регрессии, как это делалось с примером простой линейной регрессии в главе 1. Коэффициенты действуют как веса, но они также рассказывают историю, которая изменяется в зависимости от вида признака. В целях придания интерпретации более управляемого характера давайте поместим наши коэффициенты во фрейм данных вместе с названиями каждого признака: coef_df = pd.DataFrame({'feature':X_train.columns.values.tolist(), 'coef': coefs_lm}) coef_df Приведенный выше фрагмент исходного кода создает фрейм данных pandas, показанный на рис. 3.9. Вот как надо интерпретировать признак с использованием коэффициентов рис. 3.9.  Непрерывный: аналогичен ARR_RFPH. Вы знаете, что для каждого увеличения на одну единицу (относительные полеты в час) предсказываемая задержка возрастает на 0,373844 минуты, если все остальные признаки остаются неизменными.  Двоичный: аналогичен ORIGIN_HUB. Вы знаете, что разница между аэропортом отпрвления, являющимся хабом, и аэропортом, таковым не являющимся, выражается коэффициентом –1,029088. Другими словами, поскольку это число является отрицательным, аэропорт происхождения является хабом. Он (коэффициент) уменьшает задержку чуть более чем на 1 минуту, если все остальные признаки остаются неизменными.  Категориальный: у нас нет категориальных признаков, но у нас есть порядко- вые, которые могли бы быть и на самом деле должны были быть категориальными признаками. Например, DEP_MONTH и DEP_DOW являются целыми числами соответственно в интервалах 1–12 и 0–6. Если их рассматривать как порядковые
Глава 3. Трудности интерпретации 107 числа, то вследствие линейной природы линейной регрессии мы исходим из допущения, что увеличение или уменьшение количества месяцев влияет на исход. То же самое и с днем недели. Но его воздействие незначительное. Если бы мы трактовали признаки как фиктивные либо как признаки, кодированные с одним активным состоянием3, мы могли бы измерить, в какой степени именно по пятницам возникают задержки рейсов по причинам, связанным с перевозчиком, по сравнению с субботами и средами, или в июлях по сравнению с октябрями и июнями. Смоделировать порядок было бы невозможно, потому что такие признаки не имеют никакого отношения к этому порядку (и да, он нелинейный!). Рис. 3.9. Коэффициенты линейно-регрессионных признаков  К примеру, у нас был признак DEP_FRIDAY, и еще один под названием DEP_JULY. Они трактуются как двоичные признаки и могут сказать точно, какое влияние на модель оказывает вылет в пятницу или в июле. Некоторые признаки были пред- 3 Словосочетание "кодирование с одним активным состоянием" (англ. one-hot encoding) пришло из терминологии цифровых интегральных микросхем, в которой оно описывает конфигурацию микросхемы, допускающую, чтобы только один бит был положительным (активным). — Прим. перев.
108 Часть I. Введение в интерпретацию машинного обучения намеременно сохранены как порядковые или непрерывные несмотря на то, что они были хорошими кандидатами на категориальность, чтобы продемонстрировать, как невнесение правильных корректировок в ваши признаки может влиять на выразительную силу модельной интерпретации. Было бы неплохо рассказать руководителям авиакомпании побольше о том, как день и время отправления влияли на задержки. Кроме того, в некоторых случаях (но не в данном) подобный недосмотр может сильно влиять на результативность линейнорегрессионной модели. Пересечение (–37.86) не является признаком, но оно и вправду имеет смысл, а именно, если бы все признаки были равны 0, то каким было бы предсказание? На практике этого не происходит, если только у всех ваших признаков не будет веской причины быть равными 0. Как и в главе 1, где вы не ожидали бы, что кто-то будет иметь рост 0, в этом примере вы не рассчитываете, что рейс будет иметь расстояние 0. Однако, если бы вы стандартизировали признаки, сделав их среднее значение равным 0, то вы бы изменили интерпретацию пересечения как предсказания, которое вы ожидаете, если все признаки являются своим средним значением. Важность признаков Коэффициенты также могут использоваться для расчета важности признаков. К сожалению, линейный регрессор библиотеки scikit-learn не очень пригоден для этого, потому что он не выводит среднюю квадратичную ошибку коэффициентов  . Для ранжирования признаков в соответствии с их важностью всего лишь требуется разделить  на относящиеся к ним средние квадратичные ошибки. Этот результат называется t-статистикой: ti  i . SE  i  Затем надо взять абсолютное значение этого результата и отсортировать числа от большего значения к меньшему. Вычислить t-статистику достаточно легко, но вам потребуется средняя квадратичная ошибка. Методы линейной алгебры, участвовшие в ее извлечении с помощью пересечения, и коэффициенты, возвращаемые библиотекой scikit-learn, можно восстановить. Однако, пожалуй, намного проще выполнить подгонку линейно-регрессионной модели заново, но на этот раз с помощью библиотеки statsmodels, в которой есть сводка со всеми статистическими величинами, в том числе и с t-статистикой! Кстати, в statsmodels ее линейный регрессор называется OLS, что логично, поскольку OLS (обычные наименьшие квадраты) — это имя математического метода, который выполняет подгонку модели: linreg_mdl = sm.OLS(y_train_reg, sm.add_constant(X_train)) linreg_mdl = linreg_mdl.fit() linreg_mdl.summary() Приведенный выше фрагмент исходного кода формирует результат, приведенный на рис. 3.10.
Глава 3. Трудности интерпретации Рис. 3.10. Сводка линейной регрессии библиотеки statsmodels 109
110 Часть I. Введение в интерпретацию машинного обучения Судя по сводке на рис. 3.10, многое следует разъяснить. В этой книге мы не будем касаться всех приведенных там величин, за исключением t-статистики, которая позволит вам сделать выводы о степени важности признаков по отношению друг к другу. Существует еще одна, более уместная статистическая интерпретация: если бы вы выдвинули гипотезу о том, что коэффициент b равен 0, другими словами, что этот признак не влияет на модель, то расстояние t-статистики от 0 помогло бы отклонить эту нулевую гипотезу. Именно для этого предназначено p-значение (столбец в сводке справа от t-статистики). Неслучайно самое близкое значение t к 0 (для ARR_AFPH) имеет единственное p-значение, которое превышает 0,05. Благодаря этому указанный признак является незначимым, поскольку в соответствии с этим методом статистической проверки гипотез все, что меньше 0,05, может считаться статистически значимым. Рис. 3.11. Сводная таблица линейной регрессии, отсортированная по абсолютному значению t-статистики Таким образом, в целях ранжирования наших признаков давайте извлечем фрейм данных из сводки, полученной с помощью библиотеки statsmodels. Затем отбрасим const (пересечение), потому что это не признак. Нам нужны имена признаков, чтобы понять что к чему, поэтому мы превращаем этот массив признаков во фрейм данных. Затем мы конкатенируем (concat) фрейм данных names с фреймом данных summary. Наконец, создаем новый столбец с абсолютным значением t-статистики и
Глава 3. Трудности интерпретации 111 сортируем его. В целях демонстрации обратной связи абсолютного значения t-статистики и p-значения выделям эти столбцы цветом: summary_df = linreg_mdl.summary2().tables[1] summary_df = summary_df.drop(['const']).reset_index().rename(columns={'index':'feature'}) summary_df['t_abs'] = abs(summary_df['t']) summary_df.sort_values(by='t_abs', ascending=False).style.\ background_gradient(cmap='plasma_r', low=0, high=0.1, subset=['P>|t|']).\ background_gradient(cmap='plasma_r', low=0, high=0.1, subset=['t_abs']) Приведенный исходный код выводит на экран результат, представленный на рис. 3.11. Особенно интересно в отношении важности признаков на рис. 3.11, что разные виды задержек занимают пять из шести верхних позиций. Конечно, это может быть связано с тем, что линейная регрессия путает разные нелинейные эффекты или, возможно, здесь есть нечто, достойное нашего внимания, тем более что сводка библиотеки statsmodels в разделе "Warnings" (Предупреждения) предостерегает: [2] число условий велико, 5.69е+04. Оно может указывать на наличие сильной мультиколлинеарности или других численных проблем. Это странно. Запомните это утверждение. Мы обсудим его позже. Гребневая регрессия Гребневая регрессия является частью подсемейства штрафуемой или регуляризуемой регрессии наряду с методами типа LASSO4 и ElasticNet, потому что, как объяснялось ранее в этой главе, она штрафует с использованием нормы L2. Это подсемейство также называется разреженными линейными моделями, т. к. благодаря регуляризации оно устраняет часть шума, помогая модели определить нерелевантные функции менее значимыми при обучении. Разреженность в этом контексте означает, что чем меньше сложность модели, тем лучше, потому что уменьшенная сложность приводит к меньшей дисперсии результатов и улучшению обобщения. В целях иллюстрации этой концепции посмотрите на таблицу важности признаков (см. рис. 3.11), которую мы выводим для линейной регрессии. Думаем, вам понятно, почему первые несколько строк в столбце t_abs окрашены в разные цвета, а затем целая их группа имеет один и тот же оттенок желтого. Из-за различий в интервалах уверенности абсолютное t-значение нельзя взять в качестве пропорционального критерия оценки и утверждать, что ваш верхний признак в сотни раз релевантнее, чем каждый из нижних 10 признаков. Тем не менее такая цветовая разметка должна указывать на существование значительно более важных призна4 LASSO (least absolute shrinkage and selection operator) — оператор наименьшего абсолютного сжатия (усадки) и отбора. — Прим. перев.
112 Часть I. Введение в интерпретацию машинного обучения ков, нежели другие, до такой степени, что они становятся нерелевантными и, возможно, вызывающими затруднения для понимания, следовательно, создавая шум. Существует обширное исследование, в котором изучалась тенденция, когда малое подмножество признаков оказывает наиболее существенное влияние на исход модели. Оно называется ставкой на принцип разреженности. Независимо от того, верно это или нет для ваших данных, всегда полезно опробовать теорию, применив регуляризацию, в особенности в тех случаях, когда данные очень обширны (много признаков) или мультиколлинеарны. Эти технические приемы регуляризованной регрессии могут встраиваться в процессы отбора признаков или информировать вас о том, какие признаки являются ключевыми. Существует специальный технический прием адаптирования гребневой регрессии к классификационным задачам. Мы его кратко рассмотрели ранее. Он нормализует метки по шкале от –1 до 1 с целью обучения модели предсказывать значения от –1 до 1, а затем конвертирует их обратно по шкале от 0 до 1. Однако в этом приеме для подгонки модели под данные используется регуляризованная линейная регрессия, и она может интерпретироваться в таком же ключе. Интерпретация Гребневую регрессию можно интерпретировать так же, как и линейную регрессию, т. е. глобально и локально, потому что после того, как модель была обучена, разницы нет. Формула остается прежней: yˆ  гребень X , за исключением того, что коэффициенты гребень отличаются, т. к. они были оштрафованы параметром  , который контролирует уровень применяемого сжатия (так называемого штрафа). Коэффициенты можно быстро сравнить, извлекая гребневые коэффициенты из их обученной модели и помещая их во фрейм данных рядом с линейно-регрессионными коэффициентами: coefs_ridge = reg_models['ridge']['fitted'].coef_ coef_ridge_df = pd.DataFrame({'feature':X_train.columns.values.tolist(),\ 'coef_linear': coefs_lm, 'coef_ridge': coefs_ridge}) coef_ridge_df.style.\ background_gradient(cmap='viridis_r', low=0.3, high=0.2, axis=1) Как следует из рис. 3.12, коэффициенты всегда слегка различаются, но иногда они ниже, а иногда выше. Мы не сохранили параметр  (именуемый в scikit-learn альфой), который кроссвалидация гребневой регрессии сочла оптимальным. Тем не менее можно провести небольшой собственный эксперимент, чтобы выяснить, какой параметр был лучшим. Это делается путем перебора 100 возможных значений альфа между 100 (1) и 1013 (10 000 000 000 000), выполнением подгонки гребневой модели под данные с использованием каждого значения альфа, а затем добавления коэффициентов в ко-
Глава 3. Трудности интерпретации 113 нец массива. Мы исключаем один коэффициент из массива просто потому, что он намного больше остальных и затруднит визуализацию эффектов сжатия: num_alphas = 100 alphas = np.logspace(0, 13, num_alphas) alphas_coefs = [] for alpha in alphas: ridge = linear_model.Ridge(alpha=alpha).fit(X_train, y_train_reg) alphas_coefs.append(np.concatenate((ridge.coef_[:8],ridge.coef_[9:]))) Рис. 3.12. Линейно-регрессионные коэффициенты в сопоставлении с гребневыми коэффициентами Теперь, когда у нас есть массив коэффициентов, можно построить график прогрессии коэффициентов: plt.gca().invert_xaxis() plt.tick_params(axis='both', which='major') plt.plot(alphas, alphas_coefs) plt.xscale("log") plt.xlabel('Альфа') plt.ylabel('Гребневые коэффициенты') plt.grid() plt.show() Приведенный выше исходный код генерирует грифик на рис. 3.13.
114 Часть I. Введение в интерпретацию машинного обучения Рис. 3.13. Значение гиперпараметров альфа в сопоставлении со значениями коэффициентов гребневой регрессии На рис. 3.13 следует отметить, что более высокое значение альфы означает более высокую регуляризацию. Вот почему, когда значение альфы было равно 1012 , все коэффициенты сошлись к 0, по мере уменьшения альфы они расходятся и в определенный момент более или менее стабилизируются. В данном случае точка стабилизации примерно соответствует альфе, равной 102. На это можно посмотреть и по-другому: когда все коэффициенты равны 0, это означает, что регуляризация настолько сильна, что все признаки являются нерелевантными. Когда они достаточно разошлись и стабилизировались, регуляризация делает их все релевантными, что противоречит цели. Остановимся на этом и вернемся к нашему исходному коду. Мы обнаружим, что в нашей регрессии мы используем вот такие значения альфы RidgeCV: alphas=[1e-3, 1e-2, 1e-1, 1]. Как видно из предыдущего графика, когда альфы достигли 1 и менее, коэффициенты уже стабилизировались, хотя все еще немного колеблются. Это может объяснить причину, по которой гребневая регрессия показала результативность, не превышающую линейную регрессию. Обычно ожидается, что регуляризованная модель будет показывать более высокую результативность, чем нерегуляризованная, если только ваши гиперпараметры выбраны верно. И НТЕРПРЕТАЦИЯ И ГИПЕРПАРАМЕТРЫ Хорошо отлаженная регуляризация помогает устранять шум и, следовательно, повышать интерпретируемость, но выбранные для RidgeCV альфы были отобраны преднамеренно, чтобы донести суть: регуляризация может работать только в том случае,
Глава 3. Трудности интерпретации 115 если вы правильно выбрали гиперпараметры. В другом случае, когда настройка регуляризационных гиперпараметров выполняется автоматически, метод должен быть оптимальным для вашего набора данных. Важность признаков Здесь всё так же, как и в линейной регрессии, но опять же нам нужна средняя квадратичная ошибка коэффициентов, которую невозможно извлечь из модели библиотеки scikit-learn. Для этого можно использовать метод fit_regularized() библиотеки statsmodels. Полиномиальная регрессия Полиномиальная регрессия является частным случаем линейной или логистической регрессии, в которой признаки были расширены до более высоких степеней. До этого в упражнении данной главы мы выполнили лишь полиномиальную линейную регрессию, поэтому обсудим только ее вариацию. Однако она применяется аналогичным образом. Двухпризнаковая множественная линейная регрессия будет выглядеть следующим образом: ŷ  0  1 X 1  2 X 2 . Но в полиномиальной регрессии каждый признак расширяется, чтобы получить более высокие степени и взаимодействия между всеми признаками. Поэтому, если этот двухпризнаковый пример расширить до многочлена второй степени, то линейно-регрессионная формула будет выглядеть так: ŷ  0  1 X 1  2 X 2  3 X 12  4 X 1 X 2  2 X 22 . Это по-прежнему линейная регрессия во всех отношениях, за исключением того, что она имеет дополнительные признаки, члены более высокой степени и взаимодействия. Хотя полиномиальное разложение можно ограничить только одним или несколькими признаками, мы использовали библиотечный класс PolynomialFeatures, который проделывает это со всеми признаками. Следовательно, 21-й признак, скорее всего, умножался многократно. Из обученной модели можно извлечь коэффициенты и, используя свойство shape массива библиотеки NumPy, вернуть число сгенерированных коэффициентов. Это число соответствует числу сгенерированных признаков: print(reg_models['linear_poly']['fitted'].get_params()['linearregression'].\ coef_.shape[0]) Приведенная выше строка исходного кода выводит на экран 253. То же самое можно сделать с версией полиномиальной регрессии, которая была только с членами взаимодействия: print(reg_models['linear_interact']['fitted'].get_params()['linearregression'].\ coef_.shape[0]) Приведенная выше строка исходного кода выводит на экран 232. Реальность такова, что большинство членов в сгенерированном таким образом многочлене отражают взаимодействие всех признаков.
116 Часть I. Введение в интерпретацию машинного обучения Интерпретация и важность признаков Полиномиальную регрессию можно интерпретировать как глобально, так и локально точно так же, как и линейную регрессию. В данном случае непрактично записывать формулу с 253 линейно-комбинированными членами, поэтому из нее выпало то, что мы определили в главе 2, как глобальную холистическую интерпретацию. Однако ее все еще можно интерпретировать в других диапазонах, и она сохраняет многие свойства линейной регрессии. Например, поскольку модель является аддитивной, можно легко отделить степени влияния признаков. Также можно задействовать те же самые испытанные и проверенные статистические методы, которые применяются для линейной регрессии. Например, можно использовать t-статистику, p-значение, границы уверенности, R-квадрат, а также многочисленные тесты для диагностирования качества или отсутствия подгонки, анализ остатков, линейную корреляцию и анализ дисперсии. Перечисленное обилие статистически доказанных методов валидации и интерпретирования моделей — это не то, на что может рассчитывать большинство модельных классов. К сожалению, многие из них являются модельно-специфическими, зависящими от линейной регрессии и ее особых случаев. Кроме того, мы не будем здесь выполнять полиномиальную регрессию из-за большого числа членов. Тем не менее вы, несомненно, могли бы ранжировать признаки для полиномиальной регрессии так же, как и для линейной регрессии, используя библиотеку statsmodels. Вся трудность состоит в том, чтобы выяснить очередность сгенерированных библиотечным классом PolynomialFeatures признаков, чтобы дать им соответствующее имя в столбце имен признаков. После того как это будет сделано, вы сможете определить важность любых членов второй степени или взаимодействий. Результат вам покажет, имеют ли эти признаки нелинейную природу и сильно ли они зависят от других признаков. Логистическая регрессия Мы обсудили логистическую регрессию, а также ее интерпретацию и важность признаков в главе 2. Здесь мы лишь чуть-чуть расширим эту тему в контексте классификационного упражнения этой главы и подчеркнем причину, по которой она поддается интерпретации. Обученная логистическая регрессионная модель имеет коэффициенты и пересечение так же, как и линейно-регрессионная модель: coefs_log = class_models['logistic']['fitted'].coef_ intercept_log = class_models['logistic']['fitted'].intercept_ print('коэффициенты:%s' % coefs_log) print('пересечение:%s' % intercept_log) Приведенный выше исходный код выводит следующее: coefficients: [[-1.22697824e-03 -2.15999672e-03 -4.40890391e-03 -1.76485228e-03 5.61883080e-03 -2.74751192e-03 -1.53628575e-01 -1.65326323e-01 -1.31302994e-02 -2.07293774e-03]] intercept: [-0.0023103] 2.81998160e-04 1.27656536e-04 -2.43056085e-04 -1.21284703e-01 -5.33432883e-03 1.61515846e-01 1.87614278e-03 -5.18584183e-02 -6.91254912e-03 -5.99230501e-03 4.79739711e-04
Глава 3. Трудности интерпретации 117 Однако в формуле конкретного предсказания yˆ  i  эти коэффициенты совершенно по-другому представлены: (i ) P  yˆ (i )  1  e0 1 X1  X 2( i ) ...n X n( i ) (i ) 1  e0 1 X1  X 2( i ) ...n X n( i ) . Другими словами, вероятность того, что yˆ i   1 (является положительным случаем), выражается логистической функцией, которая предусматривает экспоненциалы (т. е. экспоненциальные функции или члены) линейной комбинации коэффициентов  и признаков X. Наличие экспоненциалов объясняет, почему извлеченные из модели коэффициенты являются логарифмическими шансами: потому что для выделения коэффициентов необходимо взять логарифм от обеих частей уравнения. Интерпретация Интерпретирование каждого коэффициента делают точно так же, как и в линейной регрессии. За исключением каждого отдельного увеличения значения признаков, вы повышаете шансы получения положительного случая на фактор, выраженный экспоненциалом коэффициента — при прочих равных условиях (вспомните обсуждавшееся в главе 2 допущение ceteris paribus). Экспоненциал e нужно применять к каждому коэффициенту, потому что он выражает увеличение не просто шансов, а логарифмических шансов. Помимо встраивания логарифмических шансов в интерпретацию, к логистической регрессии относится все вышесказанное о непрерывности, двоичности и категориальности в интерпретации линейной регрессии. Важность признаков Как бы это ни было удручающе, статистическое сообщество еще не пришло к единому мнению о том, каким образом лучше всего получать важность признаков в логистической регрессии. Существует ряд методов, в том числе метод, основанный прежде всего на стандартизировании всех признаков, метод псевдо-R2, методы ROC AUC с обработкой по одному признаку за раз, метод частичной статистики 2 и самый простой метод, который умножает средние квадратичные отклонения каждого признака на коэффициенты. Мы не будем охватывать все эти методы, но следует отметить, что выверенное и надежное вычисление важности признаков является проблемой для большинства модельных классов, даже для классов моделей типа черного ящика. Мы углубимся в этот вопрос в главе 4. В логистической регрессии, пожалуй, самый популярный метод выполняет стандартизирование всех признаков перед обучением модели, т. е. обеспечивая их сосредоточение в районе нуля и деля на их среднее квадратичное отклонение. Но мы этого не сделали, потому что, хотя стандартизация и имеет другие выгоды, она усложняет интерпретацию коэффициентов; поэтому здесь мы используем довольно грубый метод, примененный в главе 2, который заключается в умножении средних квадратичных отклонений каждого признака на коэффициенты: stdv = np.std(X_train, 0) abs(coefs_log.reshape(21,) * stdv).sort_values(ascending=False)
118 Часть I. Введение в интерпретацию машинного обучения Приведенный выше фрагмент исходного кода выведет следующее: DEP_DELAY CRS_ELAPSED_TIME DISTANCE LATE_AIRCRAFT_DELAY NAS_DELAY WEATHER_DELAY TAXI_OUT SECURITY_DELAY ARR_AFPH : WHEELS_OFF PCT_ELAPSED_TIME dtype: float64 8.918590 6.034794 5.309037 4.985519 2.387845 2.155292 1.311593 0.383242 0.320974 : 0.006806 0.003410 По-прежнему сохраняется способность довольно хорошо аппроксимировать важность признаков. И точно так же, как и в случае с линейной регрессией, по ним можно судить, что признаки задержки занимают довольно высокое "положение". Пять из них входят в число восьми верхних признаков. И на это стоит обратить внимание. Мы обсудим данную тему подробнее, когда займемся методами типа белого ящика. Деревья решений Деревья решений использовались в течение длительного времени, еще до того, как их превратили в алгоритмы. Для их понимания вряд ли требуются какие-либо математические способности, и этот низкий барьер в постижении делает их чрезвычайно интерпретируемыми в их простейших представлениях. Однако на практике существует много видов деревьев решений, и большинство из них не очень поддаются интерпретации, потому что в них используются ансамблевые методы (бустинг, бэггинг и стэкинг) или даже задействуется анализ главных компонент (PCA) либо какой-нибудь другой встроенный метод. Даже неансамблированные деревья решений нередко становятся чрезвычайно сложными по мере их "разрастания" в глубину. Независимо от сложности дерева решений, их всегда можно переработать с целью получения сущностной информации о ваших данных и ожидаемых предсказаниях, и их всегда можно вписать как в регрессионные, так и в классификационные задачи. Деревья решений CART Алгоритм классификационных и регрессионых деревьев (classification and regression trees, CART) является классическим ("ванильным") незатейливым деревом решений в большинстве случаев использования. И, как уже отмечалось, большинство деревьев решений не являются моделями типа белого ящика, но это потому, что они выражаются в виде математической формулы, визуализируются и распечатываются в виде набора правил, которые подразделяют дерево на ветви и, в конечном счете, на листья.
Глава 3. Трудности интерпретации 119 Вот эта математическая формула: M yˆ    m I  x  Rm  . m 1 Она означает, что если x в соответствии с функцией тождественности I находится в подмножестве Rm , то функция возвращает 1, а если нет, то 0. Этот двоичный член умножается на средние значения всех элементов в подмножестве Rm , обозначаемые как  m . Поэтому, если xi находится в подмножестве, принадлежащем листовому узлу Rk , то предсказание yˆi   k . Другими словами, предсказание представляет собой среднее значение всех элементов в подмножестве Rk . Такая ситуация характерна для регрессионных задач, а в двоичной классификации просто нет такого  m , которое можно было бы умножать на функцию тождественности I. В основе каждого алгоритма дерева решений лежит метод генерирования подмножеств Rm . В случае CART это достигается с помощью так называемого индекса Джини, позволяющего рекурсивно расщеплять в местах, где две ветви отличаются максимально. Интерпретация Дерево решений можно интерпретировать визуально в глобальном и локальном плане. Рассмотрим пример: мы установили максимальную глубину равной 2 (max_depth=2), потому что могли бы сгенерировать все 7 слоев, но текст может оказаться слишком коротким, чтобы его можно было оценить. Одним из ограничений этого метода является то, что его бывает сложно выполнить визуализцию с глубинами более 3 или 4. Однако всегда есть возможность обойти ветви дерева программно и визуализировать лишь по нескольку ветвей за раз: fig, axes = plt.subplots(nrows = 1, ncols = 1, figsize = (16,8), dpi=600) tree.plot_tree(class_models['decision_tree']['fitted'],\ feature_names=X_train.columns.values.tolist(), filled = True,\ max_depth=2) fig.show() Приведенный выше фрагмент исходного кода выводит на экран дерево, показанное на рис. 3.14. Из этого дерева следует, что самая первая ветвь расщепляет дерево решений на основе значения DEP_DELAY, равного или меньшего 20,5. В этом узле показаны индекс Джини, который послужил источником информации для указанного решения, и размер выборки samples (это просто еще один способ обозначить наблюдения, точки данных или строки). Обход этих ветвей можно выполнять до тех пор, пока не будет достигнут лист. В указанном дереве есть один листовой узел, и он находится в крайнем левом углу. Это дерево является классификационным, поэтому по значению [629167, 0] можно судить о том, что все оставшиеся в этом узле 629 167 образцов были классифицированы как 0 (Not delay).
120 Часть I. Введение в интерпретацию машинного обучения Рис. 3.14. График дерева решений нашей модели Визуализировать дерево можно еще лучше, но с меньшим числом деталей, таких как индекс Джини и размер выборки, а именно путем распечатки решений, принимаемых в каждой ветви, и класса в каждом узле: text_tree = tree.export_text(class_models['decision_tree']['fitted'],\ feature_names=X_train.columns.values.tolist()) print(text_tree) И приведенный фрагмент исходного кода выводит следующее: |--- DEP_DELAY <= 20.50 | |--- DEP_DELAY <= 15.50 | | |--- class: 0 | |--- DEP_DELAY > 15.50 | | |--- PCT_ELAPSED_TIME <= 0.99 | | | |--- PCT_ELAPSED_TIME <= 0.98 | | | | |--- PCT_ELAPSED_TIME <= 0.96 | | | | | |--- CRS_ELAPSED_TIME <= 65.50 | | | | | | |--- PCT_ELAPSED_TIME <= 0.94 | | | | | | | |--- class: 0 | | | | | | |--- PCT_ELAPSED_TIME > 0.94 | | | | | | | |--- class: 0 | | | | | |--- CRS_ELAPSED_TIME > 65.50 | | | | | | |--- PCT_ELAPSED_TIME <= 0.95 | | | | | | | |--- class: 0 | | | | | | |--- PCT_ELAPSED_TIME > 0.95 | | | | | | | |--- class: 0 | | | | |--- PCT_ELAPSED_TIME > 0.96 | | | | | |--- CRS_ELAPSED_TIME <= 140.50 | | | | | | |--- DEP_DELAY <= 18.50 | | | | | | | |--- class: 0
Глава 3. Трудности интерпретации | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 121 | | | | |--- DEP_DELAY > 18.50 | | | | | |--- class: 0 | | | |--- CRS_ELAPSED_TIME > 140.50 | | | | |--- DEP_DELAY <= 19.50 | | | | | |--- class: 0 | | | | |--- DEP_DELAY > 19.50 | | | | | |--- class: 0 | |--- PCT_ELAPSED_TIME > 0.98 | | |--- DEP_DELAY <= 18.50 | | | |--- DISTANCE <= 326.50 | | | | |--- LATE_AIRCRAFT_DELAY <= 0.50 | | | | | |--- class: 1 | | | | |--- LATE_AIRCRAFT_DELAY > 0.50 | | | | | |--- class: 0 | | | |--- DISTANCE > 326.50 | | | | |--- DEP_DELAY <= 17.50 | | | | | |--- class: 0 | | | | |--- DEP_DELAY > 17.50 | | | | | |--- class: 0 | | |--- DEP_DELAY > 18.50 | | | |--- LATE_AIRCRAFT_DELAY <= 1.50 | | | | |--- DISTANCE <= 1358.50 | | | | | |--- class: 1 | | | | |--- DISTANCE > 1358.50 | | | | | |--- class: 0 | | | |--- LATE_AIRCRAFT_DELAY > 1.50 | | | | |--- class: 0 |--- PCT_ELAPSED_TIME > 0.99 | |--- LATE_AIRCRAFT_DELAY <= 1.50 | |--- ... (продолжается еще на протяжении 6 страниц!) С помощью дерева решений можно проделать гораздо больше операций, и библиотека scikit-learn предлагает API для проведения предварительного анализа дерева. Важность признаков Вычисление важности признаков в дереве решений CART выполняется достаточно просто. Как можно понять из визуализаций, некоторые признаки возникают в решениях чаще, но их появление зависит от того, насколько признаки способствовали совокупному уменьшению в индексе Джини по сравнению с предыдущим узлом. Подсчитывается вся сумма относительного уменьшения индекса Джини по всему дереву, и вклад каждого признака является процентной долей этого уменьшения: dt_imp_df = pd.DataFrame({'feature':X_train.columns.values.tolist(),\ 'importance': class_models['decision_tree']['fitted'].feature_importances_}).\ sort_values(by='importance', ascending=False) dt_imp_df
122 Часть I. Введение в интерпретацию машинного обучения Фрейм данных dt_imp_df, выведенный с помощью приведенного выше фрагмента исходного кода, можно оценить на рис. 3.15. Рис. 3.15. Важность признаков дерева решений Таблица важности признаков на рис. 3.15 увеличивает подозрения по поводу признаков задержки. Они снова занимают пять из шести верхних позиций. Возможно ли такое, что все пять признаков оказывают такое огромное влияние на модель? И НТЕРПРЕТАЦИЯ И ПРЕДМЕТНЫЕ ЗНАНИЯ Целевой признак задержки из-за перевозчика (CARRIER_DELAY) еще называется зависимой переменной, потому что он зависит от всех других признаков, независимых переменных. Несмотря на то что из статистической связи не вытекает причинноследственная связь, мы хотим обеспечивать нашу процедуру отбора признаков информацией, основываясь на нашем понимании того, какие независимые переменные могут убедительно влиять на зависимую. Вполне резонно, что задержка отправления (DEPARTURE_DELAY) влияет на задержку прибытия (которую мы удалили) и, следовательно, CARRIER_DELAY. Точно так же признак задержки из-за опоздавшего авиалайнера (LATE_AIRCRAFT_DELAY) имеет смысл в качестве предсказателя, потому что она известна до взлета авиалайнера, если предыдущий авиалайнер опоздал на несколько минут, становясь причиной повышения риска того, что этот рейс может опоздать, но не в качестве причины текущего рейса (исключая этот вариант).
Глава 3. Трудности интерпретации 123 Однако, несмотря на то что веб-сайт Бюро статистики грузопассажирских перевозок определяет задержки в таком ключе, что они кажутся дискретными категориями, некоторые из них можно определять задолго до отправления рейса. Например, предсказывая задержку в середине рейса, можем ли мы выдать предсказание, основываясь на задержке по погодным условиям (WEATHER_DELAY), если плохая погода еще не наступила? И можем ли мы предсказывать, основываясь на задержке по причинам безопасности (SECURITY_DELAY), если нарушение безопасности еще не произошло? Вероятно, ответы на эти вопросы будут такими: мы не должны этого делать, потому что основанием включения этих признаков является их обязательность для устранения CARRIER_DELAY, но это работает только в том случае, если они являются дискретными категориями, которые предшествуют зависимой переменной по дате! Прежде чем прийти к дальнейшим выводам, вам нужно поговорить с руководителями авиакомпании, чтобы определить хронологический график, согласно которому каждая категория задержки неукоснительно устанавливается и (гипотетически) доступна пилоту во время полета или из командного центра авиакомпании. Даже если вы вынуждены удалять категории из моделей, возможно, другие данные заполнят пустоту в содержательном плане, например первыми 30 минутами журналов полетов и историческими погодными условиями. Интерпретация не всегда выводится из данных и моделей машинного обучения напрямую, но она выводится в тесном сотрудничестве с экспертами в предметной области. Однако иногда и эксперты в предметной области могут вас дезориентировать. Есть еще один момент, связанный со всеми метриками на основе времени и категориальными признаками, которые мы сконструировали в начале главы (DEP_DOW, DEST_HUB, ORIGIN_HUB и т. д.). Выясняется, они стабильно не оказывали на модели практически никакого влияния. Несмотря на то что руководители авиакомпании намекали на важность дней недели, хабов и перегруженности, нам, прежде чем конструировать данные, следовало бы продолжить анализ данных, отыскивая корреляции. Но даже если мы и в правду сконструируем несколько бесполезных признаков, то поможет модель типа белого ящика, которая применяется для диагностирования их воздействия. Практики в сфере науки о данных нередко узнают наиболее результативные модели машинного обучения в таком же ключе — путем проб и ошибок! RuleFit RuleFit — это семейство модельных классов, представляющее собой гибрид линейной LASSO-регрессии для получения регуляризованных коэффициентов по каждому признаку и их объединения с правилами принятия решения, для регуляризации которых тоже используется LASSO. Эти правила принятия решения извлекают путем обхода дерева решений, отыскания степени взаимодействия между признаками и назначения им коэффициентов, основываясь на их влиянии на модель. Для выполнения этой задачи используемая в данной главе имплементация применяет градиентно-бустированные деревья решений. В этой главе мы не рассматривали правила принятия решения в явной форме, но они служат еще одним семейством имманентно интерпретируемых моделей. Мы их не рассматриваем, потому что на момент написания книги единственная библиотека Python под названием Skater, поддерживающая правила принятия решения, именуемые списком байесовых правил (Bayesian rule list, BRL), все еще находится на экспериментальной стадии. В любом случае концепция, лежащая в основе правил принятия решения, очень похожа. Указанная модель извлекает признаковые
124 Часть I. Введение в интерпретацию машинного обучения взаимодействия из дерева решений, но не отбрасывает листовой узел, и вместо назначения коэффициентов она использует предсказания в листовом узле, которые служат для конструирования правил. Последнее правило является всеохватывающим, подобно инструкции ELSE языка программирования. В отличие от RuleFit, BPL можно понять только последовательно, потому что она очень похожа на любую конструкцию типа IF-THEN-ELSE, но в этом ее главное преимущество. Интерпретация и важность признаков Все, что вам нужно знать о правиле, можно поместить в один фрейм данных (rulefit_df). Затем следует удалить правила, которые имеют коэффициент 0. У модели они есть, потому что в LASSO, в отличие от гребневой регрессии, оценки коэффициентов сходятся к нулю. Фрейм данных можно отсортировать по важности в убывающем порядке, чтобы увидеть признаки или взаимодействия признаков (в форме правил), которые важны больше всего: rulefit_df = reg_models['rulefit']['fitted'].get_rules() rulefit_df = rulefit_df[rulefit_df.coef !=0].sort_values(by="importance",\ ascending=False) rulefit_df Правила фрейма данных rulefit_df можно увидеть на рис. 3.16. Рис. 3.16. Правила модели Rulefit На рис. 3.16 для каждого признака в модели RuleFit имеется тип (type). Признаки, которые имеют тип linear, интерпретируются так же, как и любой линейнорегрессионный коэффициент. Признаки, которые имеют type=rule, также следует трактовать как двоичные признаки в линейно-регрессионной модели. Например, если правило WEATHER_DELAY > 255.0 & DEP_DELAY > 490.5 является истинным, то к предсказанию применяется коэффициент –333,579026. Правила улавливают степени взаимодействия, поэтому добавлять члены взаимодействия в модель вручную
Глава 3. Трудности интерпретации 125 либо использовать какой-либо нелинейный метод их отыскания не требуется. Кроме того, всё делается в простом для понимания ключе. Модель RuleFit можно использовать в качестве средства, которое улучшит ваше понимание взаимодействий признаков, даже если вы решите внедрить в производство другие модели. Метод ближайших соседей Ближайшие соседи — это семейство моделей, которое включает в свой состав даже неконтролируемые методы. Во всех этих методах для обеспечения предсказаний используется близость между точками данных. Из всех этих методов интерпретации поддаются отчасти только метод контролируемых k ближайших соседей (kNN) и его двоюродный брат — радиусный метод ближайших соседей (т. е. в заданном радиусе). k ближайших соседей Идея в основе модели kNN проста. Она берет k точек, ближайших к точке данных, в тренировочном наборе и использует их метки (y_train) для информирования предсказаний. Если эта задача является классификационной, то используется мода всех меток, а если задача является регрессионной, то используется среднее значение. Указанная модель является ленивым учеником, потому что "обученная модель" — это не намного больше, чем тренировочные данные и параметры, такие как k, и список классов (если это классификация). Она мало что делает до момента умозаключения. И именно там она задействует тренировочные данные, вводя их напрямую, а не извлекая параметры, веса/смещения или коэффициенты, усвоенные моделью, как это делают усердные ученики. Интерпретация Модель kNN обладает только локальной интерпретируемостью, потому что из-за отсутствия обученной модели у вас нет глобальной модулярной или глобальной холистической интерпретируемости. В случае классификационных задач можно попытаться понять ее, используя границы и участки решения, которые мы изучили в главе 2. Тем не менее она всегда основана на локальных экземплярах. В целях интерпретирования локальной точки из нашего тестового набора данных мы запрашиваем фрейм данных pandas, используя его индекс. Мы будем пользоваться рейсом № 721043: print(X_test.loc[721043,:]) Приведенная выше строка исходного кода выведет следующий ряд pandas: CRS_DEP_TIME DEP_TIME DEP_DELAY TAXI_OUT WHEELS_OFF CRS_ARR_TIME 655.000000 1055.000000 240.000000 35.000000 1130.000000 914.000000
126 Часть I. Введение в интерпретацию машинного обучения CRS_ELAPSED_TIME 259.000000 DISTANCE 1660.000000 WEATHER_DELAY 0.000000 NAS_DELAY 22.000000 SECURITY_DELAY 0.000000 LATE_AIRCRAFT_DELAY 221.000000 DEP_AFPH 90.800000 ARR_AFPH 40.434783 DEP_MONTH 10.000000 DEP_DOW 4.000000 DEP_RFPH 0.890196 ARR_RFPH 1.064073 ORIGIN_HUB 1.000000 DEST_HUB 0.000000 PCT_ELAPSED_TIME 1.084942 Name: 721043, dtype: float64 Из меток y_test_class по рейсу № 721043 следует, что он был задержан, потому что приведенная ниже строка исходного кода выводит 1: print(y_test_class[721043]) Однако наша модель kNN предсказала, что рейс не был задержан, потому что вот эта строка исходного кода выводит 0: print(class_models['knn']['preds'][X_test.index.get_loc(721043)]) Обратите внимание, что предсказания выводятся как массив NumPy, поэтому мы не можем обращаться к предсказанию рейса № 721043, используя его индекс pandas (721043). Для его извлечения необходимо использовать последовательное местоположение этого индекса в тестовом наборе данных с помощью библиотечного метода get_loc(). В целях выяснения причины возникновения несответствия, можно использовать kneighbors на нашей модели, чтобы найти 7 ближайших соседей этой точки. Для этого необходимо реформировать (reshape) данные, потому что kneighbors будет принимать их только в той же форме, что и в тренировочном наборе, т. е. (n, 21), где n — это число наблюдений (строк). В этом случае n=1, потому что нам нужны ближайшие соседи только для одной точки данных. И, как можно судить из того, что было выведено инструкцией X_test.loc[721043,:], числовой ряд библиотеки pandas имеет форму (21,1), поэтому необходимо инвертировать эту форму: print(class_models['knn']['fitted'].\ kneighbors(X_test.loc[721043,:].values.reshape(1,21), 7)) kneighbors выводит два массива: (array([[143.3160128, 173.90740076, 192.66705727, 211.57109221, 243.57211853, 259.61593993, 259.77507391]]), array([[105172, 571912, 73409, 89450, 77474, 705972, 706911]])) Первый — это расстояние от каждой из семи ближайших тренировочных точек до нашей точки в тестовых данных.
Глава 3. Трудности интерпретации 127 Второй — это местоположение указанных точек данных в тренировочном наборе: print(y_train_class.iloc[[105172, 571912, 73409, 89450, 77474, 705972, 706911]]) Приведенная выше строка исходного кода выводит следующий числовой ряд pandas: 3813 0 229062 1 283316 0 385831 0 581905 1 726784 1 179364 0 Name: CARRIER_DELAY, dtype: int64 Хорошо видно, что предсказание отражает моду, потому что наиболее распространенным классом в семи ближайших точках был 0 (Not delayed). Число k можно увеличить или уменьшить, чтобы увидеть, что это соблюдается. Кстати, при использовании двоичной классификации рекомендуется выбирать нечетное число k, чтобы не было равенства голосов за кандидатов. Еще одним важным аспектом является метрика расстояния, которая участвовала в отборе ближайших точек данных. Используемую в модели метрику можно легко узнать с помощью: print(class_models['knn']['fitted'].effective_metric_) Результатом будет euclidean, что для этого примера имеет смысл. В конце концов, евклидово расстояние оптимально подходит для вещественного векторного пространства, потому что большинство признаков являются непрерывными. Можно также протестировать альтернативные метрики расстояния, такие как minkowski, seuclidean или mahalanobis. Когда большинство признаков являются двоичными и категориальными, у вас целочисленное векторное пространство. Таким образом, расстояния нужно рассчитывать с помощью алгоритмов, подходящих для этого пространства, таких как hamming или canberra. Важность признаков Важность признаков является методом глобальной модельной интерпретации, а kNN имеет гиперлокальную природу, поэтому из модели kNN просто невозможно извлечь важность признаков. Наивный Байес Как и обобщенные линейные модели (GLM), наивный Байес — это семейство модельных классов, в котором модель скроена для разных статистических распределений. Однако, в отличие от допущения обобщенных линейных моделей о том, что целевой признак y имеет выбранное распределение, все модели на основе наивного Байеса исходят из допущения о том, что ваши признаки X имеют это распределение. Еще важнее то, что они были основаны на теореме Байеса об условной вероятности, поэтому они выводят вероятность и, следовательно, являются исключи-
128 Часть I. Введение в интерпретацию машинного обучения тельно классификаторами. Но они трактуют вероятность влияния каждого признака на модель независимо, что является сильным допущением. Вот почему их называют наивными. Есть модель для распределения Бернулли, именуемая бернуллиевым наивным Байесом; модель для мультиномиального распределения, именуемая мультиномиальным наивным Байесом, и, конечно же, модель для гауссова распределения, которая распространена больше всего. Гауссов наивный Байес Теорема Байеса определяется вот такой формулой: P  A | B  P  B | A P  A P B . Другими словами, для того чтобы определить вероятности наступления события A, при условии, что наступило B, вы берете условную вероятность наступления А, деленую на вероятность B. В контексте классификатора машинного обучения эту формулу можно переписать следующим образом: P y | X   P X | y P y P X  . Это обусловлено тем, что мы хотим иметь вероятность y при условии истинности X. Но наш X состоит более чем из одного признака, поэтому указанную формулу можно расширить следующим образом: P  y | x1 , x2 , ..., xn   P  x1 | y  P  x2 | y  P  xn | y  P  y  P  x1  P  x2  P  xn  . Для того чтобы вычислять предсказания ŷ , необходимо учитывать, что мы должны вычислять и сравнивать вероятности по каждому классу (вероятность задержки против вероятности отсутствия задержки) и выбирать класс с наибольшей вероятностью: n yˆ  P  y | X   arg max P  y  Ck   P  xi | y  Ck . Ck i 1 Вычисление вероятности каждого класса P  y  Ck  (также известной как априорная вероятность класса) выполняется относительно тривиально. На практике обученная модель сохраняет ее в атрибуте под названием class_prior_: print(class_models['naive_bayes']['fitted'].class_prior_) Приведенная выше строка исходного кода выводит следующее: array([0.93871674, 0.06128326]) Естественно, поскольку задержки из-за перевозчика происходят только в 6% случаев, существует маргинальная вероятность наступления этого события.
Глава 3. Трудности интерпретации В формуле есть произведение  129 n i1 условных вероятностей того, что каждый при- знак принадлежит классу P  xi | y  Ck  ). Поскольку он является двоичным, вычислять вероятности многочисленных классов не требуется, потому что они обратно пропорциональны. Значит, можно отбросить Ck и заменить его на 1, как вот тут: n yˆ  P  y  1| X   P  y  1  P  xi | y  1 . i 1 Это связано с тем, что мы пытаемся предсказать именно вероятность задержки. Кроме того, P  xi | y  1 является отдельной формулой, которая отличается в соответствии с принятым как допущение распределением модели, в данном случае гауссовым: P  xi | y  1  1 2i2  xi i 2 e 2 i2 . Эта формула называется плотностью гауссова распределения значений вероятности. Интерпретация и важность признаков Тогда что это за сигмы ( i ) и теты ( i ) в формуле? Это, соответственно, дисперсия и среднее значение признака xi при y  1 . Дело в том, что признаки имеют разные дисперсии и средние значения в одном классе по сравнению с другим, что может влиять на классификацию. Эта задача является двоично-классификационной, но вы можете рассчитать i и i для обоих классов. К счастью, в обученной модели сохранено следующее: print(class_models['naive_bayes']['fitted'].sigma_) На выходе получатся два массива, первый из которых соответствует отрицательному классу, а второй — положительному. Массивы содержат  (дисперсии) для каждого 21 признака при заданном классе: array([[2.50123026e+05, 2.61324730e+05, ..., 1.13475535e-02], [2.60629652e+05, 2.96009867e+05, ..., 1.38936741e-02]]) Из модели также можно извлечь  (средние значения): print(class_models['naive_bayes']['fitted'].theta_) Приведенная выше строка исходного кода тоже выводит два массива, по одному для каждого класса: array([[1.30740577e+03, 1.31006271e+03, ..., 9.71131781e-01], [1.41305545e+03, 1.48087887e+03, ..., 9.83974416e-01]]) Указанные два массива — это все, что нужно для отлаживания и интерпретирования результатов наивного Байеса, потому что их можно использовать для вычисления условной вероятности того, что проявится признак xi при заданном положительном классе P  xi | y  1 . Эту вероятность можно использовать для ранжирова-
130 Часть I. Введение в интерпретацию машинного обучения ния признаков по важности на глобальном уровне либо интерпретирования конкретного предсказания на локальном уровне. Наивный Байес считается быстрым алгоритмом с несколькими хорошими вариантами использования, такими как фильтрация спама и рекомендательные системы, но принятое сильное допущение о независимости мешает его работе в большинстве ситуаций. Говоря о результативности, давайте обсудим эту тему в контексте интерпретируемости. Распознавание компромисса между результативностью и интерпретируемостью Мы уже кратко затрагивали эту тему раньше, но высокая результативность часто требует сложности, а сложность препятствует интерпретируемости. Как было рассмотрено в главе 2, эта сложность исходит в основном из трех источников: нелинейности, немонотонности и интерактивности. Если модель добавляет какую-либо сложность, то она усугубляется числом и природой признаков в наборе данных, что само по себе является источником сложности. Особые модельные свойства Перечисленные далее особые свойства помогают делать модель более интерпретируемой. Ключевое свойство: объяснимость В главе 1 мы обсудили причину, по которой возможность заглядывать под капот модели и интуитивно понимать, как все ее движущиеся части выводят свои предсказания в выверенном ключе, в сущности является разграничительной линией, отделяющей объяснимость от интерпретируемости. Это свойство также называется прозрачностью, или просвечиваемостью. Модель может быть интерпретируемой без этого, но в том же самом ключе, в котором можно интерпретировать решения человека, потому что невозможно понять, что происходит "под капотом". Это часто носит название постфактумной интерпретируемости, и именно на такой интерпретируемости данная книга сконцентрирована в первую очередь, за некоторыми исключениями. При этом необходимо признать, что если модель можно понять с помощью привлечения ее математической формулы (основанной на статистической теории и теории вероятностей), как мы это делали с линейной регрессией и наивным Байесом, или путем визуализации интерпретируемой человеком структуры, как с деревьями решений, или набора правил, как с RuleFit, то она гораздо более интерпретируема, чем классы моделей машинного обучения, в которых все это практически невозможно. В этом отношении модели типа белого ящика всегда будут иметь преимущество, и, как указано в главе 1, существует масса вариантов использования, в ко-
Глава 3. Трудности интерпретации 131 торых наличие модели типа белого ящика является обязательным. Но даже если вы не внедряете модели белого ящика в производство, они всегда сослужат службу в интерпретации, если позволяет размерность данных. Это свойство является ключевым по той причине, что коль скоро модель объяснима, нет никакой разницы — соответствует размерность другим свойствам или нет; модель всё равно будет более интерпретируемой, чем те, которые не объяснимы. Корректировочное свойство: регуляризуемость В этой главе мы узнали, что регуляризация уменьшает сложность, добавляемую введением слишком большого числа признаков, и это может делать модель более интерпретируемой, не говоря уже о более высокой результативности. Некоторые модели включают регуляризацию в тренировочный алгоритм, например RuleFit и градиентно-бустированные деревья; другие могут ее интегрировать, например многослойный персептрон или линейная регрессия, а третьи не имеют возможности ее включать в свой состав, например kNN. Регуляризация бывает во многих формах. Деревья решений имеют метод, именуемый подрезанием, который помогает уменьшать сложность путем удаления несущественных ветвей. В нейронных сетях есть метод, именуемый отсевом, который случайно отбрасывает узлы нейронной сети из слоев во время обучения. Регуляризация является корректировочным свойством, поскольку она помогает ослаблять сложность даже наименее интерпретируемым моделям и, таким образом, улучшать интерпретируемость. Диагностика результативности К настоящему времени в этой главе вы уже провели диагностику результативности всех моделей типа белого ящика, рассмотренных в предыдущем разделе, а также нескольких моделей типа черного ящика. Возможно, вы уже заметили, что метрики результативности моделей типа черного ящика превосходят таковые модели белого ящика, и для большинства вариантов использования это обычно так. Невозможно точно выяснить, какие классы моделей более интерпретируемы, но таблица на рис. 3.17 отсортирована по этим моделям с наиболее желательными свойствами, т. е. они не вводят нелинейность, немонотонность и интерактивность. Разумеется, объяснимость сама по себе является свойством, которое независимо ни от чего, меняет правила игры, и регуляризация оказывает помощь. Есть также случаи, в которых проводить диагностику свойств очень трудно. Например, полиномиальная (линейная) регрессия имплементирует линейную модель, но она выполняет подгонку нелинейных связей, поэтому имеет другую цветовую кодировку. Как вы узнаете в главе 12, некоторые библиотеки поддерживают добавление монотонных ограничений в градиентно-бустированные деревья и нейронные сети, а значит, их можно делать монотонными. Однако методы для моделей типа черного ящика, которые мы использовали в этой главе, монотонные ограничения не поддерживают. Столбцы Задача (регрессия и классификация) сообщают о том, можно ли их использовать для регрессии либо классификации. А столбцы Ранг результативности
132 Часть I. Введение в интерпретацию машинного обучения показывают, насколько хорошо эти модели оцениваются по RMSE (для регрессии) и по ROC AUC (для классификации), причем более низкие ранги лучше. Обратите внимание, что хотя ради простоты в этой таблице для диагностирования результативности мы и использовали только одну метрику, обсуждение результативности должно быть гораздо тоньше, чем тут. Следует отметить еще одну вещь — гребневая регрессия показала слабую результативность, но это связано с тем, что, как объяснялось в предыдущем разделе, мы использовали неправильные гиперпараметры. Белый ящик Модельный класс Свойства, увеличивающие интерпретируемость объяснимость Линейная регрессия Регуляризованная регрессия Логистическая регрессия Гауссов наивный Байес Полиномиальная регрессия линейность монотонность неинтерактивность Задача Ранг результативности регуляри- регрес- класси- регрес- классизация сия фикация сия фикация 6 7 8 5 7 2 RuleFit 8 Дерево решений 5 3 9 6 3 4 k ближайших соседей Случайный лес Градиентнобустированные деревья Многослойный персептрон 2 1 1 Рис. 3.17. Таблица, оценивающая интерпретируемость и результативность нескольких моделей типа белого ящика и черного ящика, предварительный анализ которых мы провели в этой главе Поскольку она соответствует требованиям всех пяти свойств, легко определить, почему линейная регрессия является для интерпретируемости "золотым стандартом". Кроме того, если мы признаем, что это неофициальные оценки, то сразу станет очевидно, что большинство лучших рангов принадлежат моделям типа черного ящика. Это не случайно! Математика в основе нейронных сетей и градиентнобустированных деревьев чрезвычайно эффективна в достижении наилучших метрик. Тем не менее как показывают красные кружки, они обладают всеми свойствами, которые делают модель менее интерпретируемой, что превращает их самую большую силу (сложность) в потенциальную слабость.
Глава 3. Трудности интерпретации 133 Именно поэтому модели типа черного ящика в этой книге представляют для нас первостепенный интерес, хотя многие из методов, которые вы узнаете, применимы и к моделям типа белого ящика. В части II, которая включает главы 4–9, мы усвоим особые методы, модельно-агностические и специфические для машинного обучения, которые помогают в интерпретации. А в части III, которая включает главы 10–14, мы научимся настраивать модели и наборы данных с целью повышения интерпретируемости. И НТЕРПРЕТАЦИЯ И БЫСТРОДЕЙСТВИЕ Предсказательная результативность не единственный показатель качества, которому следует уделять повышенное внимание. Обсуждая вопрос результативности ранее, мы прямо не рассматривали важность другого вида производительности — быстродействия (так называемые скорости исполнения или времени вычислений). Предсказательная результативность, как правило, обратно пропорциональна интерпретируемости и быстродействию. Точно так же, как модели типа черного ящика, как правило, предсказывают лучше, модели типа белого ящика более интерпретируемы и быстрее, чем черные ящики. И зачастую не только с точки зрения обучения, но и при выведении результата. Эта проблема раньше была серьезным сдерживающим фактором. Несмотря на то что методы глубокого обучения существуют уже более полувека, они по сути начали набирать обороты только лет десять назад из-за нехватки ресурсов в предыдущие периоды! Тогда почему предсказательная результативность по-прежнему релевантна? Потому что исследователи данных, инженеры данных и инженеры машинного обучения постоянно увеличивают сложность своих моделей, размер наборов данных и использование настройки гиперпараметров с целью повышения предсказательной результативности. Поэтому им требуется больше ресурсов для обучения и, возможно, для ускорения выведения результата. Однако модель с медленным выведением результата непрактична для многих случаев использования, потому что она становится неэффективной по стоимости или требует выведения результата в реальном времени, при осуществлении которого задержка будет слишком велика. Следовательно, должен существовать компромисс между предсказательной результативностью и быстродействием. И хотя исследователи ИИ раздвигают границы интерпретируемости моделей, будут возникать ситуации, требующие учитывать компромиссы между всеми тремя производительностями: предсказательной результативностью, быстродействием и интерпретируемостью (рис. 3.18). Более высокая интерпретируемость при сохранении высокой предсказательной результативности может сопровождаться значительной потерей в быстродействии. Так обстоит дело с аквариумными моделями, которые мы рассмотрим в следующем разделе. Однако кто знает, может, мы когда-нибудь сможем усидеть на двух стульях! Белый ящик Аквариум Черный ящик Интерпретируемость Высокая Средневысокая Низкая Предсказательная результативность Средняя Высокая Высокая Быстродействие Высокая Низкая Средняя Рис. 3.18. Сравнение моделей типа белого ящика, черного ящика и аквариумных (по меньшей мере, то, что известно о них на данное время)
134 Часть I. Введение в интерпретацию машинного обучения Обнаружение более новых интерпретируемых (аквариумных) моделей В последнее время предпринимаются значительные усилия как в промышленности, так и в академических кругах по созданию новых моделей, которые могут иметь достаточную сложность, обеспечивающую баланс между недообучением и переобучением, именуемую компромиссом между систематическим смещением и дисперсией, но при этом сохранять достаточный уровень объяснимости. Многие модели укладываются в это описание, но большинство из них предназначены для конкретных вариантов использования; они еще надлежащим образом не протестированы либо для них пока не выпущены библиотеки или открытый исходный код. Тем не менее две общецелевых модели, к которым мы сейчас обратимся, уже набирают обороты. Объяснимая бустинговая машина Объяснимая бустинговая машина (explainable boosting machine, EBM) является частью платформы Microsoft InterpretML, которая включает многие модельноагностические методы, которые мы будем использовать в книге позже. В объяснимой бустинговой машине задействованы упоминавшиеся ранее обобщенные аддитивные модели (GAM), которые похожи на линейные модели, но выглядят вот так: yˆ  g  E  y   0  f1  x1   f 2  x2   ...  f j  x j  . Отдельные функции f1 до f p подгоняются под каждый признак с помощью сплайновых функций. Затем связная функция g адаптирует GAM-модель под выполнение разных задач, таких как классификация или регрессия, или приспосабливает предсказания к разным статистическим распределениям. GAM-модели являются моделями типа белого ящика, тогда что делает объяснимую бустинговую машину (EBM) "аквариумной"? Она включает в свой состав бэггинг и градиентный бустинг, что, как правило, делает модели результативнее. Бустинг выполняется по одному признаку за раз с использованием низкой скорости усвоения, чтобы не вносить спутанность. GAM-модель также автоматически находит практические члены взаимодействия, что повышает результативность при сохранении интерпретируемости. Ее формула такова: yˆ  g  E  y   0   f j  x j    f ji  x j , xi  . После подгонки эта формула состоит из сложных нелинейных членов, поэтому глобальная холистическая интерпретация вряд ли возможна. Однако, поскольку влияние каждого признака или членов попарного взаимодействия являются аддитивными, они легко отделимы, и глобальная модулярная интерпретация вполне возможна. Локальная интерпретация одинаково проста, учитывая, что математическая формула помогает в отладке любого предсказания.
Глава 3. Трудности интерпретации 135 Один из недостатков EBM-машины кроется в том, что она бывает намного медленнее, чем градиентно-бустированные деревья и нейронные сети, из-за принятого за основу подхода с обработкой по одному признаку за раз, низкой скорости усвоения, не влияющей на очередность признаков, и сплайновых методов подгонки. Тем не менее ее рассчеты можно распараллелить, и в средах с достаточным объемом ресурсов и несколькими ядрами или машинами она будет работать намного быстрее. Для того чтобы не ждать результаты в течение одного-двух часов, тот же технический прием лучше всего применять и для понижения размерности, используя сокращенные версии X_train и X_test. Однако на этот раз мы будем использовать только восемь признаков, которые, как оказалось, являются наиболее важными: DEP_DELAY, LATE_AIRCRAFT_DELAY, PCT_ELAPSED_TIME, WEATHER_DELAY, NAS_DELAY, SECURITY_DELAY, DISTANCE, CRS_ELAPSED_TIME. Поместим их в массив feature_samp, а затем из фрейма данных X_train и X_test возьмем подмножества, включающие только конкретный признак. Установим значение sample2_size равным 10%, но если вы чувствуете, что у вас достаточно обрабатыващих ресурсов, отрегулируйте этот параметр соответствующим образом: # Создать новые сокращенные версии наборов данных feature_samp = ['DEP_DELAY', 'LATE_AIRCRAFT_DELAY',\ 'PCT_ELAPSED_TIME', 'DISTANCE', 'WEATHER_DELAY', 'NAS_DELAY',\ 'SECURITY_DELAY', 'CRS_ELAPSED_TIME'] X_train_abbrev2 = X_train[feature_samp] X_test_abbrev2 = X_test[feature_samp] # Для извлечения образцов из наблюдений np.random.seed(rand) sample2_size = 0.1 sample2_idx = np.random.choice(X_train.shape[0], math.ceil(X_train.shape[0]*sample2_size), replace=False) В целях обучения своей EBM-машины вам нужно лишь создать экземпляр библиотечного класса ExplainableBoostingClassifier, а затем выполнить обучение модели на тренировочные данных. Так же, как и в работе с редукцией размерности, мы используем sample2_idx для взятия выборки из части данных: ebm_mdl = ExplainableBoostingClassifier() ebm_mdl.fit(X_train_abbrev2.iloc[sample2_idx], y_train_class.iloc[sample2_idx]) Глобальная интерпретация Глобальная интерпретация предельно проста. Она входит в комплект приборной панели explain_global, которую можно попробовать. Сначала она загружается с графиком важности признаков, и можно отобрать отдельные признаки для вывода того, что было усвоено из каждого из них: show(ebm_mdl.explain_global()) Приведенная выше строка исходного кода создает приборную панель, похожую на изображенную на рис. 3.19.
136 Часть I. Введение в интерпретацию машинного обучения Рис. 3.19. Приборная панель с глобальной интерпретацией EBM-машины Локальная интерпретация Локальная интерпретация использует приборную панель как глобальную, за исключением того, что вы выбираете конкретные предсказания для их интерпретации, используя explain_local. Рис. 3.20. Приборная панель с локальной интерпретацией EBM-машины
Глава 3. Трудности интерпретации 137 В данном случае мы выбираем предсказание № 76, которое, как можно судить, было предсказано неправильно. Но его понять помогает LIME-подобный график, который мы рассмотрим в главе 6: ebm_lcl = ebm_mdl.explain_local(X_test_abbrev2.iloc[76:77],\ y_test_class[76:77], name='EBM') show(ebm_lcl) Как в случае с глобальной приборной панелью, приведенный выше фрагмент исходного кода генерирует еще одну панель, изображенную на рис. 3.20. Результативность Результативность, измеряемая с помощью ROC AUC, EBM-машины, близка к результативности, достигнутой с помощью двух рассмотренных ранее классификационных моделей, и можно лишь рассчитывать на то, что она улучшится при 10-кратном увеличении тренировочных и тестовых данных! ebm_perf = ROC(ebm_mdl.predict_proba).\ explain_perf(X_test_abbrev2.iloc[sample_idx],\ y_test_class.iloc[sample_idx], name='EBM') show(ebm_perf) Вы можете оценить приборную панель результативности, произведенную с помощью показанного выше фрагмента исходного кода, на рис. 3.21. Приборная панель результативности также может сравнивать несколько моделей одновременно, поскольку ее объяснители являются модельно-агностическими. И есть даже четвертая приборная панель, которую можно использовать для проведения предварительного анализа данных. Рис. 3.21. Одна из приборных панелей мониторинга результативности EBM-машины
138 Часть I. Введение в интерпретацию машинного обучения Skoped-Rules В случае модели Skoped-Rules правила извлекаются из ансамбля деревьев так же, как это делается с моделью RuleFit и регуляризацией L1 (LASSO). Однако в ней вместо градиентно-бустированных деревьев используется случайный лес, и в нее не встраиваются линейно-регрессионные коэффициенты. Вместо них в этой модели используются только двоичные правила, но они применяются лишь в том случае, если соблюдены условия точности и полноты, а веса пропорциональны внепакетному баллу (баллу OOB — out-of-bag)5, используемому в случайном лесу. Кстати, балл OOB похож на валидационную точность, но при его вычислении используется случайно отбираемое подмножество деревьев решений. Благодаря своей ориентации на точность и полноту модель Skoped-Rules бывает полезной для несбалансированных наборов данных, сохраняя при этом интерпретируемость нетронутой. Для обучения модели надо создать экземпляр библиотечного класса SkopeRules и непосредственно выполнить обучение модели на тренировочных данных. Мы используем тот же размер выборки sample2_idx, что и с EBM-машиной, потому что модель тоже может работать медленно, но не слишком. К счастью, n_jobs=-1 сообщает ей задействовать все ваши процессорные ядра. На результативность могут влиять несколько параметров: n_estimators — число деревьев решений, max_depth — глубина дерева. В то же время precision_min и recall_min — это минимальные величины точности и полноты для отбираемого правила. Аргумент random_state предназначен только для воспроизводимости. Как и в случае с EBM-машиной, выполнение приведенного ниже фрагмента исходного кода для обучения модели может занимать несколько минут: sr_mdl = SkopeRules(n_estimators=200, precision_min=0.2,\ recall_min=0.01, n_jobs=-1, random_state=rand, max_depth=7,\ feature_names=X_train_abbrev2.columns) sr_mdl.fit(X_train_abbrev2.iloc[sample2_idx],\ y_train_class.iloc[sample2_idx]) В следующем исходном коде вероятность задержки каждого рейса возвращается в score_top_rules, и это значение, в свою очередь, можно использовать для создания предсказаний с помощью функции np.where с порогом, заданным на уровне 0,5: sr_y_test_prob = sr_mdl.score_top_rules(X_test_abbrev2.iloc[sample_idx]) sr_y_test_pred = np.where(sr_y_test_prob > 0.5, 1, 0) Глобальная интерпретация Атрибут rules_ имеет список кортежей с каждым правилом. Их можно подсчитать следующим образом: print(len(sr_mdl.rules_)) 5 Бэггинг (bagging) как агрегирование бутстраповских выборок предусматривает взятие выборки с возвратом образцов обратно в набор данных. Такая выборка называется внутрипакетной (in-the-bag). Внепакетная (out-of-bag) выборка содержит все образцы, не участвовавшие в отборе. — Прим. перев.
Глава 3. Трудности интерпретации 139 Судя по всему, сгенерировано 1517 правил, но из-за того, как алгоритм использует точность и полноту, правила не всегда рассматриваются. Это замедляет выведение результата. Правила сортируются по уровню результативности. Давайте посмотрим на пять наиболее результативных сгенерированных правил: print(sr_mdl.rules_[0:5]) Приведенная выше строка исходного кода печатает следующее: [('DEP_DELAY > 39.5 and LATE_AIRCRAFT_DELAY <= 12.5 and WEATHER_DELAY <= 12.0 and NAS_DELAY <= 27.5 and SECURITY_DELAY <= 16.5', (0.9579037047855509, 0.47316836019772934, 4)), ('DEP_DELAY > 39.5 and LATE_AIRCRAFT_DELAY <= 11.5 and WEATHER_DELAY <= 12.0 and NAS_DELAY <= 27.5 and SECURITY_DELAY <= 8.5', (0.9594577495919502, 0.47085055043737395, 10)), ('DEP_DELAY > 39.5 and LATE_AIRCRAFT_DELAY <= 12.5 and WEATHER_DELAY <= 12.5 and NAS_DELAY <= 27.5 and SECURITY_DELAY <= 16.5', (0.9569012547735952, 0.4712520150456744, 2)), ('DEP_DELAY > 39.5 and LATE_AIRCRAFT_DELAY <= 11.5 and WEATHER_DELAY <= 12.0 and NAS_DELAY <= 29.5 and SECURITY_DELAY <= 16.5', (0.9564531654942614, 0.4705427055644734, 4)), ('DEP_DELAY > 39.5 and LATE_AIRCRAFT_DELAY <= 11.5 and WEATHER_DELAY <= 12.0 and NAS_DELAY <= 27.5 and SECURITY_DELAY <= 16.5', (0.9599182584158368, 0.46956357202280874, 12))] По мере продвижения вниз по списку вы начнете понимать, что именно имеет наибольшую важность для модели, т. к. единичные инструкции IF, если они являются истинными, указывают на положительный класс. Локальная интерпретация Давайте опробуем один модельно-специфический метод локального предсказания — предсказание о том, что рейс № 76 не был задержан, хотя в действительности это не так: print('фактически: %s, предсказано: %s' % (y_test_class.iloc[76], sr_y_test_pred[76])) Приведенная выше строка исходного кода печатает следующее: фактически: 1, предсказано: 0 Можно понять, почему функция принятия решения сообщает аномальный балл для входного образца. Этот балл представляет собой взвешенную сумму двоичных правил, в которой каждый вес является прецизионностью каждого правила. Таким образом, чем ниже балл, тем больше вероятность того, что образец является положительным, а если он нулевой, то определенно является положительным: print(sr_mdl.decision_function(X_test_abbrev2.iloc[76:77])) Результат равен 18,23, что не близко ни к 0, ни к null.
140 Часть I. Введение в интерпретацию машинного обучения Результативность Результативность оказалась неплохой, учитывая, что модель была натренирована на 10% тренировочных данных и оценивалась только на 10% тестовых данных. В особенности это касается балла полноты, который вошел в тройку лучших мест: print('точность: %.3g, полнота: %.3g, roc auc: %.3g, f1: %.3g, mcc: %.3g' % (metrics.accuracy_score(y_test_class.iloc[sample_idx], sr_y_test_pred), metrics.recall_score(y_test_class.iloc[sample_idx], sr_y_test_pred), metrics.roc_auc_score(y_test_class.iloc[sample_idx], sr_y_test_prob), metrics.f1_score(y_test_class.iloc[sample_idx], sr_y_test_pred), metrics.matthews_corrcoef(y_test_class.iloc[sample_idx],sr_y_test_pred))) Приведенный исходный код печатает следующие метрики: точность: 0.969, полнота: 0.981, roc auc: 0.989, f1: 0.789, mcc: 0.787 Миссия выполнена Миссия состояла в том, чтобы натренировать модели, которые могли бы предсказывать предотвратимые задержки с достаточной точностью и затем понять факторы, влияющие на эти задержки, согласно этим моделям, для улучшения производительности своевременной работы. Результирующая регрессия моделирует все предсказанные задержки, в среднем, значительно ниже 15-минутного порога в соответствии с RMSE. И большинство классификационных моделей набрали балл F1 значительно выше 50% — одна из них даже достигла 98,8%! Во всех моделях типа белого ящика нам также удалось найти факторы, влияющие на задержки; некоторые из этих факторов продемонстрировали достаточно хорошую результативность. Таким образом, похоже, что мы добились оглушительного успеха! Но рано праздновать. Несмотря на высокие метрики, эта миссия провалилась. Благодаря методам интерпретации мы поняли, что модели были точными в основном по ложным причинам. Это осознание помогает подкрепить критически важный для миссии урок о том, что модель запросто бывает правильной по ложным причинам, поэтому вопрос "почему?" следует задавать не только тогда, когда она показывает слабую результативность, а всегда. И мы задаем этот вопрос, используя методы интерпретации. Никак иначе. Но если миссия провалилась, почему этот раздел называется "Миссия выполнена"? Хороший вопрос! Оказывается, существовала секретная миссия. Подсказка: она — в названии этой главы. Ее смысл заключался в том, чтобы узнать о распространенных трудностях интерпретации через провал открытой миссии. На случай, если вы их пропустили, то вот те трудности интерпретации, на которые мы натолкнулись.  Традиционные методы модельной интерпретации охватывают только поверхно- стные вопросы о ваших моделях. Обратите внимание, что нам пришлось прибегнуть к модельно-специфическим методам глобальной интерпретации, чтобы обнаружить, что модели были правильными по ложным причинам.
Глава 3. Трудности интерпретации 141  Допущения могут сорвать любой проект машинного обучения, поскольку допу- щение есть информация, которую вы предполагаете без каких-либо доказательств. Обратите внимание, что очень важно тесно сотрудничать с экспертами в предметной области, чтобы принимать решения в течение всего рабочего потока машинного обучения, но иногда и они могут вас дезориентировать. Обеспечьте проверку неувязок между данными и тем, что вы считаете истиной об этих данных. Поиск и исправление этих проблем лежит в основе интерпретируемости.  Многие модельные классы, даже модели типа белого ящика, имеют проблемы с выверенным и надежным вычислением важности признаков.  Неправильная настройка модели может приводить к тому, что модель будет по- казывать достаточно хорошую результативность, но будет менее интерпретируемой. Обратите внимание, что регуляризованная модель имеет меньшее переобучение, при это оставаясь более интерпретируемой. Мы рассмотрим методы решения этой проблемы в главе 12. Отбор и конструирование признаков также могут иметь тот же эффект, о котором можно прочитать в главе 10.  Существует компромисс между предсказательной результативностью и интер- претируемостью. И этот компромисс распространяется на быстродействие. По этим причинам данная книга в первую очередь посвящена моделям типа черного ящика, которые обладают желаемой предсказательной результативностью и разумным быстродействием, но помогают с интерпретируемостью. Если вы усвоили эти трудности, примите поздравления! Миссия выполнена! Резюме Прочитав эту главу, вы должны были разобраться в нескольких традиционных методах обеспечения интерпретируемости и их пределах. Вы узнали об имманентно интерпретируемых моделях и о том, как их использовать и интерпретировать для регрессии и для классификации. Вы изучили компромисс между результативностью и интерпретируемостью и несколько моделей, которые пытаются не идти на уступки в этом компромиссе. Вы также обнаружили целый ряд практических трудностей интерпретации, связанных с ролью отбора и конструирования признаков, гиперпараметрами, экспертами в предметной области и быстродействием. В следующей главе мы узнаем больше о других методах интерпретации, служащих для измерения влияния признака на модель. Источник набора данных  Бюро статистики грузопассажирских перевозок Министерства транспорта США. (2018). Данные производительности своевременной работы авиакомпаний. Первоначально получено по адресу https://www.transtats.bts.gov.
142 Часть I. Введение в интерпретацию машинного обучения Справочные материалы  Friedman J., Popescu B. Predictive Learning via Rule Ensembles // The Annals of Applied Statistics. — 2008. — № 2 (3). — P. 916–954. — URL: http://doi.org/ 10.1214/07-AOAS148. (Фридман Дж. Попеску Б. Предсказательное усвоение посредством ансамблей правил.)  Hastie T., Tibshirani R., Wainwright M. Statistical Learning with Sparsity. The Lasso and Generalizations. — New York : Chapman and Hall/CRC, 2015. — 367 p. (Хасти Т., Тибширани Р., Уэйнрайт М. Статистическое усвоение с разреженностью: Lasso и обобщения.)  Thomas D. R., Hughes E., Zumbo B. D. On variable importance in linear regression // Social Indicators Research. — 1998. — № 45. — P. 253–275. — URL: https://doi.org/10.1023/A:1006954016433. (Томас Д. Р., Хьюз Э., Зумбо Б. Д. О важности переменных в линейной регрессии.)  Nori H., Jenkins S., Koch P., Caruana R. InterpretML: A unified framework for machine learning interpretability. — 2019. — URL: https://arxiv.org/pdf/ 1909.09223.pdf. (Нори Х., Дженкинс С., Кох П., Каруана Р. InterpretML: унифицированный каркас для обеспечения интерпретируемости машинного обучения.)  Hastie T., Tibshirani R. Generalized additive models: some applications // J. Am. Stat. Association. — 1987. — № 82 (39). — P. 371–386. — URL: http://doi.org/ 10.2307%2F2289439. (Хасти Т., Тибширани Р. Обобщенные аддитивные модели: несколько приложений.)
Часть II Освоение методов интерпретации В этой части вы научитесь интерпретировать модели, используя модельно-агностические методы и методы глубокого обучения. Эта часть включает следующие главы.  Глава 4. Основы важности признаков и их влияние.  Глава 5. Модельно-агностические методы глобальной интерпретации.  Глава 6. Модельно-агностические методы локальной интерпретации.  Глава 7. Якорные и контрфактические объяснения.  Глава 8. Визуализация сверточных нейронных сетей.  Глава 9. Методы интерпретации для многопеременного прогнозирования и ана- лиза чувствительности.
4 Основы важности признаков и их влияние В части I мы представили концепции, трудности и цели интерпретации машинного обучения. С этой главы начинается часть II, которая погрузит вас в многообразный мир методов, используемых для диагностики моделей и понимания их опорных данных. Методы интерпретации отвечают на один из самых важных вопросов: что для модели представляет наибольшую важность и как эта важность проявляется? В частности, методы интерпретации могут проливать свет на совокупную важность признаков и на то, как они — индивидуально или в сочетании — влияют на исход модели. Эта глава предоставит теоретическую и практическую основу для подхода к ответам на эти вопросы. Сначала мы будем использовать несколько внутренних параметров моделей scikitlearn с целью выведения наиболее важных признаков. Затем, разобравшись в противоречивости этих результатов, мы научимся использовать перестановочную важность признаков (permutation feature importance, PFI), чтобы ранжировать признаки интуитивно и надежно. Кроме того, чтобы продемонстрировать незначительное влияние одного признака на предсказание, мы изучим способы визуализации и интерпретации графиков частичной зависимости (partial dependence plot, PDP). Наконец, мы рассмотрим графики индивидуального условного ожидания (individual conditional expectation, ICE), чтобы объяснить изменения в предсказании при изменении признака. Вот главные темы, которые будут охвачены в этой главе:  измерение влияния признака на исход;  применение метода перестановочной важности признаков (PFI) на практике;  интерпретирование графиков частичной зависимости (PDP);  объяснение графиков индивидуального условного ожидания (ICE). Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, matplotlib и PDPbox. Инструкции по установке всех этих библиотек приведены в предисловии к книге. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-withPython/tree/master/Chapter04.
146 Часть II. Освоение методов интерпретации Миссия Мы все слышали стереотипные утверждения: первенцы бывают очень ответственными и властными; самый младший ребенок обычно избалован и беззаботен, а средний — ревнивый интроверт! Итак, выдающиеся исследователи психологии обратились в вашу консультационную фирму по науке о данных и провели несколько небольших эмпирических исследований о влиянии на личность очередности рождения. Но они только что заполучили набор данных, состоящий из более чем 40 000 записей ответов на вопросы онлайновых опросников, из проекта "Психометрия" с открытым исходным кодом. Психологи настроены скептически, потому что эти опросники выполнялись онлайн, и они никогда не проводили исследования такого масштаба, поэтому данная территория для них неразведана. По этим причинам они хотели бы, чтобы третья сторона, хорошо разбирающаяся в машинном обучении, подошла к задаче со свежим взглядом. Ваши заказчики надеются узнать, в частности, о наличии некой взаимосвязи между ответами на вопросы опросника и очередностью рождения, а также хотят определить, есть ли какие-либо вопросы, которые они могли бы использовать в своих эмпирических исследованиях, или даже являются ли вообще онлайновые опросники надежным методом. Ваша фирма согласилась пролить свет на эти вопросы. Личность и очередность рождения На протяжении более ста лет распространялись теории о том, как динамика родства — и, отчасти, стили воспитания, которые сами по себе в значительной степени определяются очередностью рождения — влияют на различные черты личности. Большинство этих теорий были сформулированы и изучены в западных странах, начиная с англичанина Фрэнсиса Гальтона (1874), связывающего первенцев с бо́льшим интеллектом, до исследования голландца Брэма Буанка (1997), связывающего родившихся последними с большей ревностью. В последнее время более детальные исследования учитывают пол, возрастные различия и социально-экономический статус. Однако указанные теории редко имеют широкое признание. Кроме того, известно, что на стили воспитания и динамику развития братьев и сестер влияет культура, поэтому западные теории плохо переносятся в другие культуры. Хорошо известно, что существует ряд психометрических методологий, которые используются для диагностирования личности с привлечением анкет для группировки людей в отдельные категории и шкалы. Набор данных содержит ответы на одну из этих методологий — тест "Большая пятерка" международного личностного предметного пула (international personality item pool, IPIP). Тест "Большая пятерка" является общепринятой моделью диагностирования личности в академической психологии. Набор данных также включает в себя 26 вопросов, специально разработанных для поиска черт, связанных с различными очередностями рождения, и хотя у исследователей есть точные очередности рождения, их интересуют только следующие три категории:  первый ребенок: участник является первым среди нескольких детей в семье;
Глава 4. Основы важности признаков и их влияние 147  средний ребенок: участник не является ни первым, ни последним среди не- скольких детей в семье;  последний ребенок: участник является последним среди нескольких детей в семье. Изначальный набор данных включает в свой состав записи со всего мира, поэтому исследователи попросили сосредоточиться конкретно на странах, в которых большинство населения говорит на английском языке, т. к. вопросы составлены на английском. Они не могут гарантироать, что вопросы не смещены систематически в культурном плане. Подход Задача состоит в том, чтобы выяснить, какие признаки — будь то ответы на вопросы опросника, технические и демографические данные — сигнализируют об очередности рождения больше всего, и надежны ли они для этой цели. Это можно сделать путем создания классификационных моделей для предсказания очередности рождения, а затем выполнить следующее.  Использовать внутренние модельные параметры для выяснения признаков, которые влияют на модель больше всего. Эта концепция называется важностью признаков, а подход — методом глобальной модулярной интерпретации. Данная тема затрагивалась в главе 2, но в этой главе мы рассмотрим ее подробнее.  Провести дальнейший анализ важности признаков с помощью более надежного метода, именуемого перестановочной важностью признаков (PFI).  Рассмотреть маргинальное влияние на исход наиболее важных признаков с помощью графиков частичной зависимости (PDP). Благодаря им мы сможем определить значения признаков, которые коррелируют с предсказаниями больше всего.  Получить более гранулярную визуализацию того, как отдельные признаки влияют на предсказания моделей с помощью графиков индивидуальных условных ожиданий (ICE). Давайте начнем! Подготовительные работы Исходный код этого примера можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter04/BirthOrder.ipynb. Загрузка библиотек В целях выполнения этого примера вам необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования данными;
148 Часть II. Освоение методов интерпретации  sklearn (scikit-learn) для разбивки данных и обучения моделей;  matplotlib и pdpbox для визуализации интерпретаций. Сначала необходимо их все загрузить, используя следующий ниже фрагмент исходного кода: import math import mldatasets import pandas as pd import numpy as np from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn import (metrics, linear_model, tree, discriminant_analysis, ensemble,\ neural_network, inspection) import matplotlib.pyplot as plt from pdpbox import pdp Теперь можно продолжить подготовку данных и разобраться с необходимыми действиями. Изучение проблемы и подготовка данных Загрузим данные в кадр данных, который мы назовем birthorder_df. Это делается вот так: birthorder_df = mldatasets.load("personality-birthorder", prepare=True) Аргумент prepare=True обеспечивает некоторую подготовку данных, такую как фильтрация по странам, в которых большинство населения говорит на английском языке, и категориальное кодирование. Указанный аргумент сэкономит нам некоторое время. В наборе должно быть около 26 000 записей и 97 столбцов. В этом можно убедиться, воспользовавшись инструкцией print(birthorder_df.shape), которая должна вернуть (25813, 97), что соответствует нашим ожиданиям. Словарь данных Мы не будем описывать здесь каждый столбец словаря данных, потому что их очень много и в основном они касаются конкретных личностных вопросов. Тем не менее если вам эти конкретные вопросы интересны, их можно найти в файле FBPSValidationData-Codebook.txt по адресу: https://www.kaggle.com/lucasgreenwell/ firstborn-personality-scale-responses. Вместе с тем мы предоставим краткий обзор 76 психологических вопросов, 6 демографических признаков и 5 технических признаков словаря данных. Психологические признаки (ответы на вопросы)  Q1, Q2, ..., Q26 — порядковый; ответы на 26 вопросов исследования очередности рождения (на основе пятибалльной шкалы Лайкерта: 1 — не согласен, ..., 3 — нейтрален, ..., 5 — согласен, а также 0 — нет ответа).
Глава 4. Основы важности признаков и их влияние 149  EXT1, EXT2, ..., EXT10; EST1, EST2, ..., EST10; AGR1, AGR2, ..., AGR10; CSN1, CSN2, ..., CSN10; OPN1, OPN2, ..., OPN10 — порядковый; опросник IPIP "Большая пятерка". Он состоит из 50 вопросов (ответы тоже по пятибалльной шкале Лайкерта: 1 — не согласен, ..., 3 — нейтрален, ..., 5 — согласен, а также 0 — нет ответа). Демографические признаки  age — порядковый; возраст участника в годах.  engnat — двоичный; является ли английский язык родным языком (1 — да, 2 — нет).  gender — категориальный; пол (мужской, женский, другой, неопределенный1);  birthn — порядковый; общее число родившихся у родителей детей от 1 до 10, 11 (для других).  country — категориальный; страна участника (по двухбуквенному коду).  birthorder — порядковый; целевая очередность рождения (1 — первенец, 2 — средний ребенок и 3 — последний ребенок). Технические признаки  source — категориальный; как пользователь попал на тестирование личности, основываясь на источнике запроса по протоколу передачи гипертекста (HTTP) (1 — непосредственно из Google, 2 — главная страница веб-сайта, 3 — любой другой).  screensize — порядковый; размер экрана, используемого для прохождения теста (2 — больше 600 пикселов (px) с каждой стороны, 1 — меньше).  introelapse — непрерывный; время, затрачиваемое на изучение целевой страни- цы личностного теста (в секундах).  testelapse — непрерывный; время, затрачиваемое на главную часть личностного теста (в секундах).  endelapse — непрерывный; время, затрачиваемое на изучение завершающей страницы личностного теста (в секундах). Как вы только что поняли, признаки в словаре данных (87) не соответствуют суммарному числу столбцов (97) в наборе данных. Это обусловлено тем, что три категориальных признака уже были категориально кодированы с использованием кодировки с одним активным состоянием. Этот процесс создает индивидуальные признаки по каждой категории с целью обеспечения их представленности в модели машинного обучения, добавляя выразительность и точность. Кодирование их в таком ключе также означает, что можно интерпретировать их независимо. 1 Трудно (вернее, невозможно) предствить себе "другой" или "неопределенный" пол, когда природой заданы только два пола — мужской и женский. Однако это западный опросник. А как известно, в настоящее время в этом плане в западных странах к этому безумию относятся толератно. — Прим. ред.
150 Часть II. Освоение методов интерпретации Подготовка данных Поскольку бо́льшая часть подготовки данных была выполнена автоматически, сейчас нам нужно лишь разбить их на тренировочные/тестовые. Но сначала мы инициализируем константу rand, которая будет использоваться для хранения случайного состояния (random_state) на протяжении всего этого упражнения. Далее мы определяем y как столбец очередности рождения (birthorder) и X — как всё остальное, а затем разбиваем данные на тренировочный и тестовый наборы данных с помощью функции train_test_split, как показано в следующем фрагменте исходного кода: rand = 9 y = birthorder_df['birthorder'] X = birthorder_df.drop(['birthorder'], axis=1).copy() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33,\ random_state=rand) Мы завершили все шаги алгоритма дальнейших действий и подготовки данных, поэтому теперь можно перейти к темам, упомянутым в обзоре главы. Как измерить влияние признака на исход В этом упражнении нам предстоит обучить шесть разных модельных классов: деревья решений, градиентно-бустированные деревья, случайный лес, логистическую регрессию, многослойный персептрон и линейный дискриминантный анализ (linear discriminant analysis, LDA), — на тренировочных данных. О первых пяти мы узнали в главе 3, поэтому уделим некоторое время тому, чтобы ознакомиться с последней из перечисленных, которая подробно описана ниже.  lda (линейный дискриминантный анализ, LDA) — очень разносторонний метод. Он принимает несколько тех же самых допущений, которые имеет линейная регрессия в отношении нормальности и гомоскедастичности; однако он берет свое начало в понижении размерности и тесно связан с неконтролируемым методом анализа главных компонент (PCA). В его обязанности входит вычисление расстояния между средним значением разных классов, именуемого межклассовой дисперсией, и дисперсией внутри каждого класса, именуемой внутриклассовой дисперсией. Затем он проецирует данные в низкоразмерное пространство таким образом, чтобы максимизировать расстояния между классами и минимизировать расстояние внутри классов. Если у вас более трех признаков, то представить концепцию разделимости классов очень трудно. Но, допустим, что вы взяли все свои точки данных и свели их к двум размерностям. Тогда есть способ спроецировать их в это низкоразмерное пространство, где ваши точки данных организованы в таком ключе, чтобы у вас было достаточно разделения между классами. Между ними можно провести линию (максимизируя межклассовую дисперсию) и сделать это, сближая точки каждого класса (минимизируя внутриклассовую дисперсию). Помимо классификации, LDA можно использовать для понижения размерности и визуализации разделения классов.
Глава 4. Основы важности признаков и их влияние 151 Теперь мы помещаем модели библиотеки scikit-learn в словарь Python (class_models), чтобы их прокрутить в цикле, натренировать, оценить и сохранить наши результаты в той же самой словарной структуре следующим образом: class_models = { 'decision_tree':{'model':\ tree.DecisionTreeClassifier(max_depth=6, random_state=rand,\ class_weight='balanced')}, 'gradient_boosting':{'model':\ ensemble.GradientBoostingClassifier(n_estimators=200, max_depth=4,\ subsample=0.5, learning_rate=0.05)}, 'random_forest':{'model':\ ensemble.RandomForestClassifier(max_depth=11, n_estimators=300,\ max_features='sqrt', random_state=rand)}, 'logistic':{'model':\ linear_model.LogisticRegression(multi_class='ovr', solver='lbfgs',\ class_weight='balanced', max_iter=500)}, 'lda':{'model':\ discriminant_analysis.LinearDiscriminantAnalysis(n_components=2)}, 'mlp':{'model':\ make_pipeline(StandardScaler(), neural_network.MLPClassifier(\ hidden_layer_sizes=(11,), early_stopping=True, random_state=rand,\ validation_fraction=0.25, max_iter=500))} } Каждая модель имеет гиперпараметры, которые уже были отрегулированы по определенным причинам. Например, LDA выполняет редукцию размерности на двух размерностях (n_components=2), потому что существует три класса, и это число не должно превышать или равняться числу классов, а для улавливания дисперсии в 96 признаках одного недостаточно. Эти классы распределены не поровну, поэтому некоторые из них имеют аргумент class_weight='balanced', чтобы во время обучения применять взвешивание классов обратно пропорционально их частотам. Балансировка помогает повышать точность и полноту менее представленных классов. Логистическая регрессия сопровождается пятью разными решателями. Каждый решатель имеет свой подход к отысканию весов параметров с целью минимизирования стоимостной функции (отрицательного логарифмического правдоподобия, понимаемого как вероятная возможность события). Тот, который используется, называется лимитированным по памяти методом Бройдена — Флетчера — Гольдфарба — Шанно (limited-memory Broyden — Fletcher — Goldfarb — Shanno, L-BFGS) (solver='lbfgs'). Этот подход был выбран в силу его эффективности, и ни по какой другой причине. Почти все остальные параметры были выбраны для предотвращения переобучения, например максимальная глубина (max_depth), число оценщиков (n_estimators), подвыборка (subsample), скорость обучения (learning_rate) и максимальное число признаков (max_features).
152 Часть II. Освоение методов интерпретации Далее мы прокручиваем в цикле каждую модель в словаре class_models. Мы выполняем обучение моделей на тренировочных данных и используем метод predict для генерирования предсказаний как для тренировочного, так и для тестового наборов данных. Затем можно сохранить обученную модель в наборе данных и применить несколько метрик результативности, таких как точность, полнота, балл F1 и коэффициент корреляции Мэтьюса (MCC). Указанные метрики были рассмотрены в главе 3, но на этот раз, поскольку мы имеем дело с мультиклассовой классификационной задачей, мы используем аргумент average='weighted' для взвешивания метрики в соответствии с частотами классов. Например, существует не одна метрика Recall_score, а три (по одной для каждого класса), поэтому она вычисляет средневзвешенное значение. Исходный код проиллюстрирован в следующем фрагменте: for model_name in class_models.keys(): fitted_model = class_models[model_name]['model'].fit(X_train, y_train) y_train_pred = fitted_model.predict(X_train) y_test_pred = fitted_model.predict(X_test) class_models[model_name]['fitted'] = fitted_model class_models[model_name]['preds'] = y_test_pred class_models[model_name]['Accuracy_train'] =\ metrics.Accuracy_score(y_train, y_train_pred) class_models[model_name]['Accuracy_test'] =\ metrics.Accuracy_score(y_test, y_test_pred) class_models[model_name]['Recall_train'] =\ metrics.Recall_score(y_train, y_train_pred, average='weighted') class_models[model_name]['Recall_test'] =\ metrics.Recall_score(y_test, y_test_pred, average='weighted') class_models[model_name]['Precision_train'] =\ metrics.Precision_score(y_train, y_train_pred, average='weighted') class_models[model_name]['Precision_test'] =\ metrics.Precision_score(y_test, y_test_pred, average='weighted') class_models[model_name]['F1_test'] =\ metrics.f1_score(y_test, y_test_pred, average='weighted') class_models[model_name]['MCC_test'] =\ metrics.matthews_corrcoef(y_test, y_test_pred) Имея все наши метрики в словаре class_models, мы может конвертировать этот словарь в кадр данных с помощью функции from_dict. Этот кадр данных можно отсортировать по MCC, используя библиотечный метод sort_values, и закодировать цветом все остальное, а затем вызвать метод style.background_gradient, используя следующий фрагмент исходного кода: class_metrics = pd.DataFrame.from_dict(class_models, 'index')\ [['Accuracy_train', 'Accuracy_test', 'Recall_train', 'Recall_test',\ 'Precision_train', 'Precision_test', 'F1_test', 'MCC_test']] with pd.option_context('display.Precision', 3): html = class_metrics.sort_values(by='MCC_test',\ ascending=False).style.background_gradient(cmap='plasma', low=0.43,\
Глава 4. Основы важности признаков и их влияние 153 high=0.63, subset=['Accuracy_train', 'Accuracy_test']).\ background_gradient(cmap='viridis', low=0.63, high=0.43,\ subset=['F1_test']) html Приведенный выше фрагмент генерирует таблицу, показанную на рис. 4.1. Рис. 4.1. Метрики результативности классификационной модели На рис. 4.1 тестовая точность не кажется такой уж впечатляющей, но обратите внимание, что для надлежащей интерпретации точности необходимо обратиться к частоте отсутствия информации (no information rate, NIR), или так называемой частоте появления ошибки null. Рассматривая NIR в рамках конкретного примера, допустим, что мы имеем дело с задачей классифицирования изображений, и 85% набора данных содержит изображения собак, а 15% — изображения кошек. Следовательно, собаки являются мажоритарным классом. Если бы нам было лень, мы бы предсказали, что все изображения представляют собак, и при этом достигли бы 85%-ной точности. NIR — это точность, которую мы бы получили, если бы лениво предсказывали, что все наблюдения принадлежат к мажоритарному классу. В целях вычисления NIR нам лишь нужно разделить число наблюдений в мажоритарном классе (y_train[y_train==1].shape[0]) на суммарное число наблюдений (y_train.shape[0]), как показано в следующей строке исходного кода: print('NIR: %.4f' % (y_train[y_train==1].shape[0]/y_train.shape[0])) Приведенная выше строка должна напечатать следующее: NIR: 0.4215 Нам следует стремиться к достижению большей точности, хотя отрыв большей точности от меньшей незначительный. Учитывая, что модели были настроены на повышенную предсказательную результативность, это разочаровывает, но мы к этому и не стремились. Было важно превзойти NIR, потому что иначе модели были бы ничем не лучше нашей наилучшей "ленивой" догадки. В противном случае это означает, что необходимо подвергнуть сомнению сложность наших моделей, выбранные методы регуляризации и подбор признаков, не говоря уже о качестве наших данных и валидности нашей гипотезы. Однако здесь мы пытаемся задействовать способность модели выводить на поверхность скрытые взаимосвязи между переменными, чтобы помочь нам связать ответы на вопросы опросника с очередностью рождения, если вообще такая связь существует.
154 Часть II. Освоение методов интерпретации В любом случае точность не единственная метрика, которая имеет немалую важность. У нас еще есть взвешенная полнота, точность и балл F1. Они не особенно впечатляют, но поскольку мы не отдаем предпочтение ложноположительным классификациям перед ложноотрицательными классификациями, точность и полнота имеют для нас одинаковую ценность, поэтому хорошо, что они более-менее равны. Более высокую разность между ними имеют только деревья решений. В остальных моделях, поскольку балл F1 является средним значением гармонического среднего точности и полноты, он — что неудивительно — имеет аналогичное число. С другой стороны, MCC очень хорошо описывает предсказательную результативность, потому что говорит о том, что наши модели занимают приближенно 20% интервала между почти случайным и идеальным предсказанием. Вспомните, что MCC колеблется между 1 , если каждое предсказание было неправильным, и 1 , если они были правильными, где 0 означает, что они были почти случайными. Следует отметить еще одно: более крупный размер тренировочного набора по сравнению с тестовым по каждой из этих метрик говорит нам о том, насколько наша модель переобучена. Нередко бывает трудно найти ту золотую середину, где вы максимизируете тестовую точность, не допустив слишком большого переобучения, как это случается с градиентно-бустированными деревьями (gradient_boosting) и случайным лесом (random_forest). Если бы мы намеревались эти модели внедрить в производство, то нам нужно было бы уделить этому пристальное внимание, но это не цель данного упражнения. Наша цель — использовать эти модели в качестве инструментов открытия знаний. Важность признаков в древовидных моделях В трех моделях это делается проще простого. Во всех древовидных моделях (даже ансамблированных) важность признаков уже рассчитана с использованием взвешенной суммы уменьшений загрязненности узлов. Загрязненность узла (node impurity) является одной из метрик, используемых для решения вопроса о том, как расщеплять ветвь. Она сообщает, какая именно часть узла принадлежит одному классу, начиная со 100%-ной загрязненности, когда расщепление происходит поровну, и до 0%-ной загрязненности, когда вся ветвь принадлежит одному классу. В целях понимания важности признаков всех трех моделей нам нужно лишь сослаться на атрибут feature_importances_ в обученной модели. Мы возьмем эти значения и сохраним их вместе с именами их признаков в кадре данных по каждой модели: дерева решений (dt_imp_df), градиентно-бустированных деревьев (gb_imp_df) и случайного леса (rf_imp_df), — следующим образом: dt_imp_df = pd.DataFrame({ 'name': X_train.columns,\ 'dt_imp': class_models['decision_tree']['fitted'].feature_importances_}) gb_imp_df = pd.DataFrame({ 'name': X_train.columns,\ 'gb_imp': class_models['gradient_boosting']['fitted'].feature_importances_}) rf_imp_df = pd.DataFrame({ 'name': X_train.columns,\ 'rf_imp': class_models['random_forest']['fitted'].feature_importances_})
Глава 4. Основы важности признаков и их влияние 155 Имеется 96 признаков, и важность признаков для всех трех моделей неодинакова из-за различий в древовидных структурах. Лучше всего интерпретировать важность признаков как относительную меру, позволяющую сравнивать один признак с другими, но не в разных моделях. Следовательно, вместо того чтобы сравнивать эти меры, можно сравнивать их ранг. Можно использовать функцию rank библиотеки pandas, чтобы вычислить ранг мер важности в каждой модели по каждому признаку и сохранить их в виде кадра данных. Это делается без изменения порядка следования признаков, поскольку они поступают неотсортированными. Соответствующий фрагмент исходного кода проиллюстрирован ниже: dt_rank_df = pd.DataFrame({'dt_rank': dt_imp_df['dt_imp'].\ rank(method='first', ascending=False).astype(int)}) gb_rank_df = pd.DataFrame({'gb_rank': gb_imp_df['gb_imp'].\ rank(method='first', ascending=False).astype(int)}) rf_rank_df = pd.DataFrame({'rf_rank': rf_imp_df['rf_imp'].\ rank(method='first', ascending=False).astype(int)}) Теперь давайте выполним конкатенацию каждого кадра данных важности признаков с соответствующим ему кадром данных рангов и объединим (merge) их в кадр данных tree_ranks_df, который имеет меру важности признака и ранг этой важности для каждой модели. Все ранги можно усреднить (avg_rank), а затем отсортировать по этому принципу, чтобы увидеть наиболее важные признаки в среднем. Соответствующий фрагмент исходного кода проиллюстрирован ниже: tree_ranks_df = pd.merge(\ pd.merge(\ pd.concat((dt_imp_df, dt_rank_df), axis=1),\ pd.concat((gb_imp_df, gb_rank_df), axis=1), 'left'),\ pd.concat((rf_imp_df, rf_rank_df), axis=1), 'left') tree_ranks_df['avg_rank'] = (tree_ranks_df['dt_rank'] +\ tree_ranks_df['gb_rank'] + tree_ranks_df['rf_rank'])/3 tree_ranks_df.sort_values(by='avg_rank') Приведенный выше фрагмент генерирует кадр данных, показанный на рис. 4.2. Как видно из рис. 4.2, между деревом решений есть несколько схожестей между рангами дерева решений (dt_rank), рангами градиентно-бустированных деревьев (gb_rank) и рангами случайного леса (rf_rank), в особенности между последними двумя. И действительно, меры важности, похоже, не находятся в одной шкале, поэтому вместо этого мы использовали подход на основе сравнения рангов. Еще одним подходом было бы минимаксное нормирование мер важности, вследствие которого их самые низкие значения были бы равны 0, а самые высокие — 1, но это позволило бы больше узнать об относительном расстоянии в важности между признаками и меньше — о порядке следования. Сейчас нас больше интересует порядок следования. В дополнение к своей модельной специфичности, методы определения важности признаков для древовидных моделей основаны на загрязненности. Это также является недостатком, потому что загрязненность делает их заведомо систематически
156 Часть II. Освоение методов интерпретации смещенными к признакам с более высокой кардинальностью. Признаки, которые имеют более высокую кардинальность, — это признаки, которые имеют более уникальные значения. Например, в данном примере в нашем наборе данных представлено 72 разных возраста, тогда как каждый вопрос имеет пять или шесть уникальных значений, а все значения страны и пола, такие как county_GB и gender_undefined, являются двоичными, поэтому имеют два уникальных значения. Вы должны задаться вопросом: не важнее ли причина возраста (age) по среднему рангу, чем любой вопрос, и не важнее ли каждый вопрос, чем двоичные признаки, вследствие этого систематического смещения? Рис. 4.2. Важность признаков для древовидных моделей Важность признаков в логистической регрессии В предыдущих двух главах мы уже рассмотрели важность признаков для логистической регрессии. Вы узнали, что обученная логистическая регрессионная модель имеет коэффициенты, и эти коэффициенты бывают полезными подсказками относительно того, какой признак важнее. Впрочем, на этот раз всё было по-другому. Давайте распечатаем форму (shape) свойства coef_ обученной модели следующим образом: print(class_models['logistic']['fitted'].coef_.shape) Эта строка исходного кода выводит следующее: (3, 96) Оказывается, существует три набора коэффициентов! Но почему?! Три набора существует потому, что эта модель состоит не из одного, а из трех классификаторов в одном. Если вы вернетесь к определению модели, то увидите аргумент multi_class='ovr'. OvR означает "один против остальных" (от англ. One-
Глава 4. Основы важности признаков и их влияние 157 vs-Rest), и скрывая от нас детали, она предсказывает классы первенцев, средних детей и последних детей независимо друг от друга. Другими словами, у каждого есть своя двоичная классификационная задача. Затем модель сравнивает предсказанные вероятности каждого класса по каждому наблюдению, и класс, который имеет наибольшую вероятность, становится предсказанным классом. Собственно подход OvR в итоге и приводит к трем наборам коэффициентов, и эти коэффициенты могут рассказывать о наиболее важных признаках только в предсказании каждого класса. Как объяснялось в главе 2, коэффициенты — это логарифмические шансы, увеличиваемые на каждую дополнительную единицу признака, того, что класс является положительным совпадением при условии, что все остальные признаки остаются неизменными. В данном примере у нас есть три набора коэффициентов, соответствующих предсказаниям каждого класса. Следовательно, первый набор коэффициентов сообщает посредством увеличения логарифмических шансов на каждую дополнительную единицу по каждому признаку, что участник является первенцем. Если параметр отрицательный, то это сигнализирует об уменьшении логарифмических шансов для каждой дополнительной единицы. Поскольку мы не выполняли обучение нашей модели под нормализованные данные, все наши признаки имеют разные шкалы, и именно по этой причине в целях учета такой ситуации мы умножаем каждый коэффициент на его среднее квадратичное отклонение, аппроксимируя важность признаков. В главе 3 обсуждается вопрос: почему это всего лишь аппроксимация и нет консенсуса по наилучшему методу получения важности признаков для логистической регрессии? Зная ответ, можно сначала вычислить средние квадратичные отклонения (stdv) и создать новый кадр данных lr_imp_df, куда рядом с именем признака мы помещаем коэффициенты каждого класса, умноженные на средние квадратичные отклонения. Вот соответствующий фрагмент исходного кода: stdv = np.std(X_train, 0) lr_imp_df = pd.DataFrame({\ 'name': X_train.columns,\ 'first_coef_norm': class_models['logistic']['fitted'].coef_[0] * stdv,\ 'middle_coef_norm': class_models['logistic']['fitted'].coef_[1] * stdv,\ 'last_coef_norm': class_models['logistic']['fitted'].coef_[2] * stdv}\ ).reset_index(drop=True) В целях аппроксимирования степени влияния каждого признака на модель можно их взвесить на априорные степени, т. е. степени представленности каждого класса в наборе данных. К счастью, обученная модель для линейного дискриминантного анализа (LDA) сохраняет их как атрибут priors_. Их можно сохранить в нашей собственной переменной class_ priors вот так: class_priors = class_models['lda']['fitted'].priors_ print(class_priors) Как следует из массива class_priors, первенцы составляют 42% всех участников, средние дети — 24%, а последние дети — остальные 34%. Этот массив можно использовать для создания средневзвешенного значения с применением абсолютного
158 Часть II. Освоение методов интерпретации значения коэффициентов с именем coef_weighted_avg. В следующем ниже фрагменте исходного кода мы используем абсолютное значение этого средневзвешенного значения, потому что нас не интересует, увеличивает ли оно или уменьшает логарифмические шансы, для нас важна только степень, с которой это происходит: lr_imp_df['coef_weighted_avg'] =\ (abs(lr_imp_df['first_coef_norm']) * class_priors[0]) +\ (abs(lr_imp_df['middle_coef_norm']) * class_priors[1]) +\ (abs(lr_imp_df['last_coef_norm']) * class_priors[2]) Средневзвешенное значение, которое мы только что произвели, является лишь аппроксимацией важности признаков, которая позволит нам сортировать признаки от самой высокой важности до самой низкой. Мы сделаем это далее с помощью библиотечного метода sort_values() и закодируем столбцы коэффициентов цветом с помощью метода background_gradient(), чтобы было легче оценивать различия в значениях в каждом столбце, следующим образом: lr_imp_df.\ sort_values(by='coef_weighted_avg', ascending=False).style.\ background_gradient(cmap='viridis', low=-0.1, high=0.1,\ subset=['first_coef_norm', 'middle_coef_norm', 'last_coef_norm']) Приведенный выше фрагмент исходного кода производит кадр данных, показанный на рис. 4.3. Рис. 4.3. Важность признаков для логистической регрессионной модели
Глава 4. Основы важности признаков и их влияние 159 На рис. 4.3 точный порядок следования не всегда имеет значение сам по себе, как то: какие именно признаки находятся вверху (в большей степени релевантны), какие находятся в самом низу (не релевантны), а какие лежат где-то посередине (частично релевантны). Коэффициенты каждого класса можно интерпретировать по тому, какие из них являются положительными либо отрицательными, и более или менее по некоторой величине — например, мы знаем, что общее число родившихся у родителей детей (birthn) отрицательно коррелирует с положительным совпадением для первенца. Это понимание интуитивно имеет смысл. Чем больше количество детей в семье, тем меньше вероятность того, что наугад выбранный из них является первенцем. То же самое касается и последних детей — только шансы среднего ребенка растут по мере увеличения количества детей. С увеличением возраста (age) шансы на то, что он родится последним, снижаются. Этот вывод также имеет смысл, потому что раньше семьи были больше, но не ясно, почему он увеличивается для первенцев. Однако нам понадобится другой инструмент, который поможет проинспектировать это получше. Мы также можем сказать, что согласие с утверждением в вопросе 1 (Q1), в котором говорится: "я прочитал абсурдное количество книг", и вопросом 13 (Q13), в котором говорится: "я командую окружающими людьми", увеличивает вероятность того, что участник является первенцем. Кроме того, вопрос 20 (Q20), в котором говорится: "мне не нужна похвала других", увеличивает вероятность того, что это будет средний ребенок. Можно сказать, что классы в основном противоположны друг другу, несмотря на то что их обучение выполняется по отдельности, и, естественно, очень мало случаев, когда коэффициенты по всем трем классам для признака являются положительными или отрицательными. Этот модельно-специфический метод определения важности признаков не очень надежен для диагностирования важности всех признаков холистически (целостно) по всем классам. Кроме того, поскольку эта модель является логистической регрессией, она принимает несколько допущений о данных, которые могут не соответствовать действительности, таких как малая или нулевая мультиколлинеарность между признаками и линейная зависимость от логарифмических шансов. Однако, если эти допущения более или менее верны, преимущество логистической регрессии с независимым представлением классов на основе подхода OvR заключается в разделении между классами. Соотнесенность каждого признака с каждым классом можно рассмотреть независимо. Важность признаков в линейном дискриминантном анализе Как и в случае логистической регрессии с использованием подхода "один против остальных" (OvR), в линейном дискриминантном анализе (LDA) мы тоже можем извлечь три набора коэффициентов по каждому признаку. В целях подтверждения надо рассмотреть форму (shape). Это делается вот так: print(class_models['lda']['fitted'].coef_.shape) Должно быть выведено (3, 96). Разница заключается в смысле этих коэффициентов. Они говорят о весе каждого признака в разделимости класса. Чем выше абсо-
160 Часть II. Освоение методов интерпретации лютное значение коэффициента, тем больше конкретный признак помогает в разделении этого класса. С другой стороны, более низкое абсолютное значение коэффициента указывает на то, что признак не способствует разделимости класса. В конце концов, LDA похож на PCA, но он раскладывает признаки не по критерию корреляции, а по разделимости. Для того чтобы взглянуть на эти коэффициенты, можно создать новый кадр данных lda_imp_df, где рядом с именем признака мы разместим коэффициенты по каждому классу, умноженные на среднее квадратичное отклонение, следующим образом: lda_imp_df = pd.DataFrame({\ 'name': X_train.columns,\ 'first_coef_norm': class_models['lda']['fitted'].coef_[0] * stdv,\ 'middle_coef_norm': class_models['lda']['fitted'].coef_[1] * stdv,\ 'last_coef_norm': class_models['lda']['fitted'].coef_[2] * stdv}).\ reset_index(drop=True) Теперь можно сделать то же самое, что и с логистической регрессией, и взять средневзвешенное значение абсолютного значения коэффициентов (coef_weighted_avg), используя переменную class_priors. Мы делаем это с единственной целью отсортировать таблицу и приблизительно понять, какие именно признаки наиболее важны, признавая при этом, что имеем дело не с точной наукой. Вот соответствующий фрагмент исходного кода: lda_imp_df['coef_weighted_avg'] =\ (abs(lda_imp_df['first_coef_norm']) * class_priors[0]) +\ (abs(lda_imp_df['middle_coef_norm']) * class_priors[1]) +\ (abs(lda_imp_df['last_coef_norm']) * class_priors[2]) Теперь можно использовать средневзвешенное значение (coef_weighted_avg) для сортировки признаков и их цветового кодирования так же, как и для логистической регрессии, следующим образом: lda_imp_df.\ sort_values(by='coef_weighted_avg', ascending=False).style.\ background_gradient(cmap='viridis', low=-0.1, high=0.1,\ subset=['first_coef_norm', 'middle_coef_norm', 'last_coef_norm']) Как следует из рис. 4.4, сгенерированного приведенным выше фрагментом исходного кода, многие из тех же признаков, которые входили в верхние 10 для логистической регрессии, тоже находятся в числе верхних 10 для линейного дискриминантного анализа (LDA). И также можно увидеть похожие закономерности между классами, например средний ребенок гораздо более близок к birthn, чем кто-либо еще, тогда как у двух других классов больше баланса в признаках, которые помогают их предсказывать. Подобно логистической регрессии с независимым представлением классов на основе OvR, важность признаков в линейном дискриминантном анализе (LDA) имеет недостатки, заключающиеся в модельной специфичности и принимаемых моделью на основе LDA-допущений. Модель LDA исходит из наличия малой или нулевой мультиколлинеарности между признаками и многопеременной нормальности,
Глава 4. Основы важности признаков и их влияние 161 т. е. признаки распределены нормально по каждому классу. Она также обладает тем же главным преимуществом логистической регрессии с OvR в отношении возможности наблюдать, как каждый признак соотносится с каждым классом. Однако модель LDA более устойчива к нарушениям допущений и, таким образом, может использоваться с более шумными данными. При этом квадратичный дискриминантный анализ (quadratic discriminant analysis, QDA) в таких случаях показывает себя еще лучше. QDA похож на LDA, но не принимает допущения о нормальности и разбивает классы не линейной границей решения, а квадратичной. Рис. 4.4. Важность признаков в LDA Важность признаков в многослойном персептроне Нейронным сетям не хватает внутренних атрибутов, которые способны без особых усилий помогать в определении важности признаков, как в других модельных классах. Всё усложняется даже для данного примера с одним скрытым слоем, потому что каждому слою соответствует два набора весовых матриц, как показано в следующем фрагменте исходного кода: print(class_models['mlp']['fitted'][1].coefs_[0].shape) print(class_models['mlp']['fitted'][1].coefs_[1].shape) Размерности этих двух массивов выглядят следующим образом: (96, 11) (11, 3) Веса в каждой матрице могут дезориентировать, поскольку могут усиливаться или подавляться друг другом. Если перемножить эти две матрицы, то получится одна матрица знакомой размерности (96, 3), в которой ячейки соответствуют каждой комбинации признаков и классов, использованных нами для логистической регрессии и линейного дискриминантного анализа. Однако это не совсем то, как веса используются для предсказания во время прямого распространения. Прежде всего,
162 Часть II. Освоение методов интерпретации между этими матричными операциями и после них существуют нелинейные активационные функции, такие как relu и softmax. Исходя из того, что обучение проводилось с нормализованными данными, поступали предложения брать сумму абсолютных произведений весов и сумму произведений весов без абсолютных значений. Существуют более изощренные схемы с привлечением взвешивания и нормализации весов, но они игнорируют влияние активационной функции скрытого слоя. Подводя итог, следуют сказать, что консенсус в отношении того, как извлекать важность признаков из внутренних параметров нейронной сети, отсутствует. Как мы узнаем позже в этой книге, существуют и другие имманентно интерпретируемые аспекты нейронной сети, например карты значимости (saliency maps) в главе 8 и интегрированные градиенты (integrated gradients) в главе 9. Хотя мы и смогли задействовать внутренние параметры для получения важности признаков по всем другим моделям, используемые методы были противоречивыми. Следовательно, результаты отличались не только из-за различий в моделях, но и изза различий в методах. Тогда каким же будет надежный метод расчета важности признаков для произвольной модели? Он называется перестановочной важностью признаков (PFI), и мы рассмотрим его далее. Применение перестановочной важности признаков на практике Концепцию перестановочной важности признаков (permutation feature importance, PFI) гораздо проще объяснить, чем любой модельно-специфический метод определения важности признаков! Этот метод просто измеряет увеличение в ошибке предсказания после того, как значения каждого признака были перетасованы. Теория в основе PFI базируется на логике, согласно которой, если признак имеет связь с целевой переменной, перетасовка ее нарушит и увеличит ошибку. С другой стороны, если признак не имеет тесной связи с целевой переменной, ошибка предсказания не будет увеличиваться намного, если вообще будет. Затем, если ранжировать признаки по тем, чья перетасовка увеличивает ошибку максимально, то можно понять, какие из них наиболее важны для модели. В дополнение к тому, что перестановочная важность признаков (PFI) является модельно-агностическим методом, его можно использовать с ранее не встречавшимися данными, такими как тестовый набор данных, что является огромным преимуществом. В этом случае, поскольку PFI подвержен переобучению со случайными лесами и градиентно-бустированными деревьями, насколько надежными будет важность признаков, полученная из внутренних параметров? Метод говорит нам о том, что именно модель считает важным в соответствии с тем, что было усвоено из тренировочных данных, но он не сможет сообщить о том, что именно является наиболее важным, если внести ранее не встречавшиеся данные. Кристоф Мольнар в своей книге "Интерпретируемое машинное обучение" (Molnar C. Interpretable machine learning) приводит аргументы в пользу использования трени-
Глава 4. Основы важности признаков и их влияние 163 ровочных данных взамен тестовых данных, хотя первые могут рассказывать больше о зависимости от каждого признака тренируемой модели, а не от его индивидуального вклада в обобщаемую предсказательную результативность. Нас больше интересует последнее, поэтому мы используем тестовый набор данных. Для вычисления перестановочной важности на всех наших моделях можно снова задействовать словарь class_models, перебирая каждую из них в цикле, а затем вызывая функцию permutation_importance() библиотеки scikit-learn для вычисления PFI. Главными параметрами функции permutation_importance являются обученная модель (fitted_model), а также признаки (X_test) и метки (y_test) набора данных. Мы также определяем точность как метрику ошибки предсказания или метрику оценивания, которую мы хотим использовать (scoring='Accuracy') для сравнения снижения точности после того, как признаки были переставлены. Вот соответствующий фрагмент исходного кода: for model_name in class_models.keys(): fitted_model = class_models[model_name]['fitted'] permutation_imp = inspection.permutation_importance(\ fitted_model, X_test, y_test, n_jobs=-1,\ scoring='Accuracy', n_repeats=8, random_state=rand) class_models[model_name]['importances_mean'] =\ permutation_imp.importances_mean Метод перестановочной важности признаков (PFI) перетасовывает признаки более одного раза, а затем усредняет ошибки предсказания, поэтому крайне важно определить число перетасовок признака (n_repeats=8), а также случайное состояние (random_state) для воспроизводимости. Метод PFI можно выполнять в параллельном режиме, используя все процессоры вашей системы (n_jobs=-1). Наконец, после того как метод PFI будет выполнен для каждой модели, он сохранит средние значения ошибок предсказания (importances_mean). Теперь можно взять вычисленные для каждой модели средние значения и поместить их в отдельные столбцы нового кадра данных perm_imp_df вместе с именем каждого признака, как показано в следующем фрагменте исходного кода: perm_imp_df = pd.DataFrame({\ 'name': X_train.columns,\ 'dt_imp': class_models['decision_tree']['importances_mean'],\ 'gb_imp': class_models['gradient_boosting']['importances_mean'],\ 'rf_imp': class_models['random_forest']['importances_mean'],\ 'log_imp': class_models['logistic']['importances_mean'],\ 'lda_imp': class_models['lda']['importances_mean'],\ 'mlp_imp': class_models['mlp']['importances_mean']}).reset_index(drop=True) Исключительно в целях сортировки кадра данных perm_imp_df по некоему критерию давайте усредним значения всех шести моделей, поместив результат в новый столбец, который назовем avg_imp, следующим образом: perm_imp_df['avg_imp'] = (\ perm_imp_df['dt_imp'] + perm_imp_df['gb_imp'] + perm_imp_df['rf_imp'] +\ perm_imp_df['log_imp'] + perm_imp_df['lda_imp'] + perm_imp_df['mlp_imp'])/6
164 Часть II. Освоение методов интерпретации Теперь можно округлить (round), отсортировать по avg_imp и сохранить perm_imp_df в новом кадре данных с именем perm_imp_sorted_df, а затем вывести на экран с цветовой кодировкой, следующим образом: perm_imp_sorted_df = perm_imp_df.round(5).\ sort_values(by='avg_imp', ascending=False) perm_imp_sorted_df.style.\ background_gradient(cmap='viridis_r', low=0, high=0.2,\ subset=['dt_imp', 'gb_imp', 'rf_imp', 'log_imp', 'lda_imp', 'mlp_imp']) Приведенный выше исходный код порождает кадр данных, показанный на рис. 4.5. Рис. 4.5. Тестовая перестановочная важность признаков (PFI) для всех моделей На рис. 4.5 показана перестановочная важность признаков (PFI) тестового набора данных для всех обученных в этой главе моделей. Она подтверждает, что модели имманентно сильно опираются на количество рождений (birthn), но также и то, что этот признак гораздо важнее, чем следующий наиболее важный признак. Фактически, birthn имеет для моделей настолько высокую важность, что если бы мы вычли среднее увеличение ошибки предсказания (которое в данном случае соответствует снижению точности) из тестовой точности каждой модели, то модели опустились бы ниже уровня отсутствия информации!2 Это достаточно легко доказать, взяв атрибут Accuracy_test из словаря class_models, в котором хранятся все тестовые точности каждой модели, и вычтя первые шесть значений (1:7) из первой строки в (0) в отсортированном кадре данных важностей (perm_imp_sorted_df): pd.DataFrame.\ from_dict(class_models, 'index')[['Accuracy_test']] -\ perm_imp_sorted_df.iloc[0, 1:7].to_numpy().reshape((6,1)) 2 Уровень отсутствия информации (no-information rate) — это наивная модель, которую необходимо превысить, чтобы доказать, что построенная модель является значимой. — Прим. перев.
Глава 4. Основы важности признаков и их влияние 165 На рис. 4.6, сгенерированном приведенным выше фрагментом, хорошо видно, что после вычитания PFI из birthn ни одна модель не имеет уровня точности выше NIR (0,4215). Рис. 4.6. Тестовая точность всех моделей после вычитания перестановочной важности признаков (PFI) из признака birthn Исходя из отсутствия содержательного уровня мультиколлинеарности, подавляющее влияние этого единственного признака означает, что всех психологических вопросов вместе взятых недостаточно для предсказания очередности рождения. К сожалению, это один из демографических вопросов, который делает модели частично результативными, что, безусловно, расходится с ожиданиями исследователей. Однако этот вывод вовсе не означает, что из этого упражнения ничего нельзя усвоить. В модельной интерпретации есть больше, чем понимание того, какие признаки побуждают или не побуждают модель работать. Но почему? Дело в том, что даже когда модель работает по ложным причинам, мы все равно можем учиться на ее работе. С этой целью необходимо углубиться в причину того, почему признак birthn столь хорошо справляется, и что можно усвоить из остальных признаков. Рассматриваемые далее методы, такие как график частичной зависимости (PDP) и графики индивидуальных условных ожиданий (ICE), помогут пролить некоторый свет на конкретные признаки и их взаимосвязь с целевой переменной и друг с другом. Недостатки метода перестановочной важности признаков Главным недостатком метода перестановочной важности признаков (PFI), который не относится к особенным методам модельной интерпретации, является то, что указанный метод не учитывает влияние признаков, коррелированных друг с другом. Другими словами, мультиколлинеарность будет сильно влиять на важность признаков. Когда вы перетасовываете один признак, то коррелированный признак (или признаки) остается неизменным, сохраняя частоты ошибок относительно неизменными, а значит, кластеры коррелированных признаков будут иметь меньшие важности, чем должны. Существует стратегия решения этой проблемы, и мы ее обсудим в главе 12.
166 Часть II. Освоение методов интерпретации Интерпретирование графиков частичной зависимости График частичной зависимости (partial dependence plot, PDP) доносит до нас маргинальный эффект признака на предсказание во всех (или интерполированных) возможных значениях этого признака. Это метод глобальной модельной интерпретации, который может наглядно демонстрировать влияние признака и природу связи с целевой переменной (линейную, экспоненциальную, монотонную и т. д.). Его также можно расширить, включив в него два признака, чтобы проиллюстрировать влияние их взаимодействия на модель. Один график признака показывает предсказываемый исход на оси y, или относительное изменение этого исхода, а ось x отражает все возможные значения этого признака. Вычерчиваемая линия рассчитывается путем изменения значения признака по оси x для всех наблюдений и усреднения предсказаний, если этот отдельный признак должен был изменяться, получая координату по оси y. Одна из вариаций графика частичной зависимости вычитает ожидаемое значение по всем наблюдениям из оси y, таким образом центрируя маргинальный эффект на ожидаемое значение. Еще одна вариация графика частичной зависимости будет показывать распределение признака с помощью гистограммы или одномерного графика (rug plot). Поскольку линия графика частичной зависимости вычисляется с помощью среднего значения, это немаловажно, т. к. и в областях графика, где признак распределен более разреженно, среднее значение является не столь уж надежным. Сначала давайте создадим два списка — список имен признаков, которые мы хотим интерпретировать (feature_names), и список соответствующих им меток (feature_labels), которые будут выводиться в метках оси x и заголовке, следующим образом: feature_names = ['birthn', 'Q1', 'Q13', 'age'] feature_labels = ['Количество рождений', 'Вопрос № 1', 'Вопрос № 13', 'Возраст'] Теперь можно перебрать имя каждого признака в цикле и вызвать функцию pdp_isolate библиотеки PDPbox для получения кадра данных со всеми средними значениями PDP (pdp_feat_df), используя обученную модель (model), набор данных (dataset), имена всех столбцов признаков (model_features) и признак, который вы хотите показать по оси x (feature). Для обученной модели мы используем градиентно-бустированные деревья, потому что ни одна модель не находится ближе всего к средней перестановочной важности признаков (PFI) для первых четырех важных признаков. Однако это можно изменить, чтобы увидеть насколько признаки в среднем имеют разную связь с целевой переменной в зависимости от модели. Вы обнаружите, что некоторые из них имеют зубчатый контур, некоторые — гладкий, некоторые — линейный, и т. д. Для набора данных (dataset) мы будем использовать тестовый набор данных по той же причине, по которой мы использовали его для PFI. Следует отметить, что dataset ожидает весь набор данных (признаки и метки), и поскольку мы разделяем их на X_test и y_test, необходимо их конкатенировать с помощью функции concat библиотеки pandas. Получив кадр данных, нам остается лишь построить его график; и в
Глава 4. Основы важности признаков и их влияние 167 библиотеке PDPbox есть функция pdp_plot, которая генерирует графики matplotlib. Она берет ранее сгенерированный кадр данных (pdp_isolate_out) и несколько дополнительных графических параметров, подробно описанных ниже.  center=True сдвигает ось y относительной самого большого или самого маленько- го значения.  x_quantile=True делает интервал между делениями оси x соответствующим кван- тилям. Библиотека PDPbox не включает гистограмму или одномерный график (rug plot), чтобы показывать распределение признаков, поэтому является неплохим способом преодоления трудностей интерпретации, связанных с разреженным или неровным распределением.  ncols=3 помещает все три класса в один ряд данных.  plot_lines=True будет выводить линии, соответствующие выборке наблюдений.  frac_to_plot=100 сообщает функции о том, что на графике нужно вывести 100 ото- бранных наблюдений.  feature_name — это метка признака по оси x. Следующий исходный код прокручивает все четыре признака в цикле, генерируя кадр данных pdp_isolate, а затем строит с ним графики частичной зависимости: for i in range(len(feature_names)): pdp_feat_df = pdp.pdp_isolate(\ model=class_models['gradient_boosting']['fitted'],\ dataset=pd.concat((X_test, y_test), axis=1),\ model_features=X_test.columns, feature=feature_names[i]) fig, axes = pdp.pdp_plot(\ pdp_isolate_out=pdp_feat_df, center=True, x_quantile=True, ncols=3,\ plot_lines=True, frac_to_plot=100, figsize=(15,6),\ feature_name=feature_labels[i]) Приведенный выше исходный код строит графики, показанные на рис. 4.7–4.10. Рис. 4.7. Графики частичной зависимости для признака birthn Теперь у нас есть наглядное представление того, что мы ранее замечали в отношении важности признаков в логистической регрессии (см. рис. 4.7). Вероятность по-
168 Часть II. Освоение методов интерпретации явления первенца (class 0) и последнего ребенка (class 2) неуклонно снижается по мере увеличения количества рождений (birthn). Вероятности среднего ребенка (class 1) идут в противоположном направлении, начиная почти с 0%, потому что не может быть среднего ребенка с двумя детьми! Всё это имеет смысл. Из того, насколько стабильно близко находятся более тонкие линии к более толстой (средней), можно также судить о том, что это сильный признак, с небольшой дисперсией во всех предсказаниях класса. Рис. 4.8. Графики частичной зависимости для вопроса Q1 Рис. 4.8 соответствует шкале Лайкерта для вопроса Q1: "я прочитал абсурдное количество книг", поэтому для первенцев вероятность уменьшается между отсутствием (0) и несогласием (1), но подъем после этого превышает нулевую отметку (без изменений) и решительно увеличивается к тому времени, когда она миновала нейтральность (3). Вопрос Q1 для последних детей имеет прямо противоположный эффект. Результат для среднего ребенка вызывает бо́льший интерес, поскольку хорошо видно, что выборочные наблюдения (тонкие линии) разбросаны повсюду, поэтому следует принимать его со скепсисом. А вот их среднее значение указывает на смешение результатов между первенцами после метки 3 и последними детьми до метки 3. Другими словами, как полное несогласие, так и согласие с вопросом Q1 предполагают более высокую вероятность того, что средние дети окажутся между этими двумя крайностями. Рис. 4.9. Графики частичной зависимости для вопроса Q13
Глава 4. Основы важности признаков и их влияние 169 Графики частичной зависимости (PDP) для вопроса Q13 ("я командую окружающими людьми") на рис. 4.9 имеют сходные с вопросом Q1 связи с целевой переменной для первенцев и последних детей, но более выраженные на конце несогласия по шкале Лайкерта и слегка менее выраженные на другом конце. В вопросе Q13 со средними детьми было гораздо меньше двойственности, чем в вопросе Q1, причем этот класс был менее вероятным по мере повышения уровня согласия. Рис. 4.10. Графики частичной зависимости для возраста Рис. 4.10 включает PDP-графики возрастного признака (age). Можно сказать, что в среднем по мере увеличения возраста вероятность быть первенцем медленно и неуклонно возрастает. Хотя мы и можем это проинтерпретировать, трудно найти этому логическое объяснение, т. к. раньше семьи были большими. Именно поэтому мы, пожалуй, ожидаем, что вероятность будет с возрастом уменьшаться. К счастью, квантили могут давать подсказки. Обратите внимание, что шаг делений от 16 до 22 лет составляет 2 года, но затем этот шаг увеличивается до 4, 6, 10 и, наконец, до 38 лет. Это означает, что возрастное распределение сдвинуто вправо, что не обязательно плохо, ведь распределение по классам, находящимся среди каждой возрастной группы, также бывает неровным. Для доказательства этой гипотезы давайте сначала поместим возраст (age) и очередность рождения (birthorder) в отдельный кадр данных (birthorder_abbrev_df). А затем задействуем функцию cut библиотеки pandas, чтобы установить индекс равным одинаковой возрастной группе в квантилях. Далее мы сохраним числовой ряд (agegroup_ birthorder_counts_s), сгруппированный по этому индексу возрастной группы и очередность рождений (birthorder), и еще один (agegroup_counts_s), сгруппированный по индексу. Теперь можно разделить итог возрастных групп и итог очередности рождений на итог возрастных групп, получив процентный числовой ряд (agegroup_pct_birthorder_s). Наконец, можно применить библиотечный метод unstack(), чтобы конвертировать числовой ряд в кадр данных, и функцию plot.bar библиотеки pandas, чтобы превратить его в составную столбиковую диаграмму, как показано в следующем фрагменте исходного кода: birthorder_abbrev_df = birthorder_df[['age', 'birthorder']] birthorder_abbrev_df.set_index(pd.cut(birthorder_abbrev_df['age'], [12, 16, 18, 20, 22, 26, 30, 36, 46, 88]), inplace=True)
170 Часть II. Освоение методов интерпретации agegroup_birthorder_counts_s = birthorder_abbrev_df.\ groupby([birthorder_abbrev_df.index, 'birthorder']).size() agegroup_counts_s = birthorder_abbrev_df.\ groupby(birthorder_abbrev_df.index)['birthorder'].count() agegroup_pct_birthorder_s =\ agegroup_birthorder_counts_s.div(agegroup_counts_s, axis=0, level=0) agegroup_pct_birthorder_s.unstack().plot.bar(stacked=True, figsize=(15,8)) Приведенный выше фрагмент строит график, показанный на рис. 4.11. Рис. 4.11. Априорные степени представленности классов в расчете на возрастные группы одинакового размера На рис. 4.11 показано, как каждый класс (birthorder) представлен в разных возрастных группах. В возрасте от 16 до 46 лет представленность первенцев (class 1) подскочила с 38 почти до 49%, тогда как последние дети (class 3) опустились с 38 до 29%, хотя такое изменение может показаться несущественным. Между тем, доля средних детей (class 2) изменилсь всего на 3%. Все это противоречит тому, что мы знаем о демографии, потому что известно, что за 75 лет, охватывающих эти возрастные группы, среднее число детей в семье в представленных странах сократилось как минимум на двух детей и почти на одного ребенка за последние 50 из этих 75 лет. Теоретически это означает, что вероятность быть первенцем или последним ребенком должна была уменьшаться с увеличением возраста, тогда как вероятность быть средним ребенком должна возрастать. Убедительная гипотеза могла бы состоять в том, что первенцы чрезмерно представлены в старших возрастных группах, потому что прежде всего они все чаще участвуют в этих онлайновых опросниках, и модель подхватила это систематиче-
Глава 4. Основы важности признаков и их влияние 171 ское смещение. Независимо от того, есть ли систематическое смещение или нет, необходимо уметь работать с релевантными классовыми дисбалансами. Систематические смещения мы рассмотрим подробнее в главе 11, в которой научимся использовать демографические данные для сокращения классового дисбаланса и, следовательно, вызываемых им модельных систематических смещений. Интеракционные графики частичной зависимости Графики частичной зависимости (PDP) также можно применять сразу к нескольким признакам, что бывает полезно при проверке связанности взаимодействия двух признаков с целевой переменной. Библиотеку PDPbox можно также использовать для генерирования интеракционного графика (графика, отражающего взаимодействия) частичной зависимости (PDP). Библиотечная функция pdp_interact очень похожа на показанную выше функцию pdp_plot и имеет все те же параметры, за исключением того, что аргумент feature является списком признаков. В дополнение к выбору birthn и Q1 в качестве наших признаков у нас есть параметр n_jobs=-1, который задействует все процессоры для выполнения параллельных вычислений. Функция pdp_interact порождает кадр данных pdp_birthn_Q1_df. Теперь необходимо построить его график с помощью функции pdp_interact_plot. В указанной функции вы увидите похожие параметры, как и в функции pdp_plot. Например, функция pdp_interact_out аналогична функции pdp_isolate_out и берет кадр данных, произведенный на предыдущем шаге и список имен признаков (feature_names) похож на список feature_name, но содержит список меток признаков, а не одну метку. Аргумент plot_type='grid' сообщает, что функция генерирует сетку, которая отлично подходит для признаков с низкой кардинальностью либо порядковых признаков, таких как birthn и Q1. Соответствующий фрагмент исходного кода проиллюстрирован ниже: pdp_birthn_Q1_df = pdp.pdp_interact(\ model=class_models['random_forest']['fitted'],\ dataset=pd.concat((X_test, y_test), axis=1),\ model_features=X_test.columns,\ features=['birthn','Q1'],\ n_jobs=-1) fig, axes = pdp.pdp_interact_plot(\ pdp_interact_out=pdp_birthn_Q1_df, plot_type='grid',\ x_quantile=True, ncols=2, figsize=(15,15),\ feature_names=['Количество рождений','Вопрос № 1']) На рис. 4.12, выведенном на экран в результате исполнения приведенного выше исходного кода, по кодированной цветом сетке можно судить, что средняя вероятность первенцев (class 0) увеличивается по мере уменьшения количества рождений (birthn) и увеличения согласия с вопросом Q1. Для последних детей (class 2) наблюдается то же самое, что и для первенцев, но с точностью до наоборот для вопроса Q1. Пока эти взаимодействия не должны вызывать удивления, потому что это похоже на комбинирование индивидуальных PDP-графиков для каждого из этих признаков. Однако у средних детей (class 1) диаграмма Q1 была чуть-чуть двойствен-
172 Часть II. Освоение методов интерпретации ной, но важно отметить, как один признак способен противодействовать среднему эффекту другого. Как только проявится его взаимодействие с признаком birthn, вы обнаружите, что его вероятность будет меняться, в данном случае увеличиваться, вместе с признаком birthn. Рис. 4.12. Интеракционные графики частичной зависимости в виде решеток для признаков birthn и Q1 В библиотеке PDPbox есть еще один тип интеракционного графика частичной зависимости (PDP), который называется контурным (contour), и он больше подходит для признаков с более высокой кардинальностью или непрерывных признаков, и значит, на этот раз мы будем использовать возраст (age) и время, затрачиваемое на сдачу теста (testelapse). Исходный код для вывода на экран графика точно такой же, как и для предыдущего, за исключением других аргументов: features, feature_names и plot_type. Соответствующий фрагмент исходного кода проиллюстрирован ниже: pdp_age_testelapse_df = pdp.pdp_interact(\ model=class_models['random_forest']['fitted'],\ dataset=pd.concat((X_test, y_test), axis=1),\
Глава 4. Основы важности признаков и их влияние 173 model_features=X_test.columns,\ features=['age','testelapse'],\ n_jobs=-1) fig, axes = pdp.pdp_interact_plot(\ pdp_interact_out=pdp_age_testelapse_df,\ plot_type='contour', x_quantile=True, ncols=2, figsize=(15,15),\ feature_names=['Возраст', 'Время на тест, мин']) Приведенный выше фрагмент производит результат, показанный на рис. 4.13. Рис. 4.13. Контурные интеракционные графики частичной зависимости для возраста и времени, затрачиваемого на сдачу теста На рис. 4.13 показано, что вероятность первенцев (class 0) возрастает по мере уменьшения времени, затрачиваемого на сдачу теста, и увеличения возраста, поэтому, если вы старше и быстрее проходите тест, то вероятность того, что вы являетесь первенцем, выше. У последних детей (class 2) наблюдается противоположность зависимости: если вы моложе, то чем медленнее вы отвечаете на вопросы теста, тем больше у вас шансов быть родившимся последним. Вероятность для средних детей (class 1) увеличивается медленно в одном направлении по мере роста скорости прохождения теста (testelapse), за исключением случаев, когда возраст превышает 46 лет, и вероятность быстро возрастает с возрастом.
174 Часть II. Освоение методов интерпретации Недостатки графиков частичной зависимости Главными недостатками графиков частичной зависимости (PDP) является то, что они могут выводить на экран лишь до двух признаков за раз, и в них принимается допущение о независимости признаков, хотя на самом деле признаки могут быть коррелированными друг с другом. Для решения этой проблемы с независимостью в следующей главе мы рассмотрим графики накопленного локального эффекта (accumulated local effect, ALE). Как мы узнали из этого раздела, PDP-графики отлично подходят для ознакомления с тем, как в среднем признаки соотносятся с целевой переменной. А если мы хотим визуализировать взаимосвязь дезагрегированно, в разбивке (другими словами, видеть каждое отдельное наблюдение, а не среднее значение)? Эта агрегация является еще одним недостатком, и от его избавления предназначены графики индивидуального условного ожидания (individual conditional expectation, ICE), которые мы кратко рассмотрим далее. Объяснение графиков индивидуального условного ожидания Графики индивидуального условного ожидания (individual conditional expectation, ICE) — это ответ на вопрос: что делать, если мои графики частичной зависимости (PDP) затуманивают дисперсию в моих взаимосвязях между признаками и целевой переменной? Действительно, когда вы пытаетесь понять, как признак связан с модельным предсказанием, многое можно потерять за счет усреднения. Если внимательно посмотреть на графики PDP индивидуальных признаков, можно заметить, что многие из них имеют тонкие линии, которые не только далеки от средней толстой линии, но даже не следуют ее общему направлению. Эти вариации могут давать дополнительное разъяснение. И, кстати, тонкие линии легко интерпретировать, посмотрев на графики ICE. Графики ICE могут содержать все ваши наборы данных, но построение многочисленных линий на графиках может обходиться дорого в вычислительном плане и, что важнее, их нагромождение трудно понять. Вот почему рекомендуется либо брать выборки из своего набора данных, либо строить графики с прозрачными линиями. Мы будем использовать оба подхода, но сначала выберем набор данных. Первым делом зададим начальное значение для генерации случайного числа с целью обеспечения воспроизводимости, используя для этого np.random.seed, а далее установим размер выборки (sample_size) равным 10% набора данных и используем sample_idx для случайного отбора 10% индексов, которые будут представлены на наших графиках ICE. Затем сохраним отобранные наблюдения в новом кадре данных (X_test_samp).
Глава 4. Основы важности признаков и их влияние 175 Вот соответствующий фрагмент исходного кода: np.random.seed(rand) sample_size = 0.1 sample_idx = np.random.choice(X_test.shape[0],\ math.ceil(X_test.shape[0]*sample_size), replace=False) X_test_samp = X_test.iloc[sample_idx,:] В имплементации индивидуальных условных ожиданий (ICE) на Python, которую мы применяем по умолчанию, используется функция predict, отлично подходящая для регрессионных задач. Тем не менее в случае классификации получаются прямые линии друг над другом, которые идут по направлению к одному из трех возможных классов. Для исправления этой ситуации можно использовать функцию predict_proba, которая возвращает предсказанные вероятности. Однако она возвращает три набора предсказанных вероятностей, и данная имплементация не способна это понять. Поэтому можно создать по одной функции predict на класс следующим образом: def predict_prob_first_born(test_df): return class_models['random_forest']['fitted'].predict_proba(test_df)[:,0] def predict_prob_middle_child(test_df): return class_models['random_forest']['fitted'].predict_proba(test_df)[:,1] def predict_prob_last_born(test_df): return class_models['random_forest']['fitted'].predict_proba(test_df)[:,2] Как можно судить по трем функциям predict_prob_, для того чтобы проиллюстрировать график ICE, мы используем подогнанную (fitted) модель для случайного леса (random_forest) и тестовый набор данных. Теперь можно вызвать функцию plot_data_vs_ice библиотеки mldatasets, которая может вычислять и строить графики ICE с данными, используемыми для генерирования графиков. По оси x можно отложить верхний признак birthn. Для того чтобы сделать это упражнение увлекательнее, мы даже закодируем линии цветом в соответствии с ответами на вопрос Q1. С этой целью давайте сначала создадим словарь со шкалой Лайкерта (legend_key), который мы будем использовать в качестве легенды для вопроса Q1, следующим образом: legend_key = {0:'Отсутствует', 1:'Не согласен', 2:'Отчасти не согласен', 3:'Нейтрален', 4:'Отчасти согласен', 5:'Согласен'} Затем вызовем функцию plot_data_vs_ice для генерирования графиков. Если вам интересно, то обратите внимание, что для построения графика ICE она скрыто использует библиотеку pycebox. Мы не будем вдаваться в подробности того, как использовать эту библиотеку напрямую, потому что наше внимание сосредоточено на интерпретации, но можно ознакомиться с руководством по адресу: https://github.com/AustinRochford/PyCEbox/. Первыми двумя аргументами, необходимыми функции plot_data_vs_ice, являются функция предсказания (predict) и метка для размещения на оси y. Метка относится
176 Часть II. Освоение методов интерпретации к тому, что предсказывается функцией predict. Для этого также требуются данные X, которые используются для предсказаний, имя признака, который должен быть нанесен на ось x (feature_name), и его метка (feature_name). При необходимости можно указать признак, используемый для цветового кодирования (color_by), и легенду для этого признака (legend_key). Сначала мы сгенерируем график предсказанной вероятности для первенцев: mldatasets.plot_data_vs_ice(predict_prob_first_born,\ 'Вероятность первенца', X=X_test_samp,\ feature_name='birthn',\ feature_label='Количество рождений',\ color_by='Q1', legend_key=legend_key) Приведенный исходный код генерирует графики, показанные на рис. 4.14 и 4.15. Рис. 4.14. Вероятность получения точек данных о первенцах по мере увеличения признака birthn с цветовой кодировкой для ответов Q1 Хорошо видно, что рис. 4.15 соединяет точки на рис. 4.14, модифицируя значения birthn по каждому наблюдению так, чтобы они совпадали со значениями на оси x. Кроме того, линии графика ICE иллюстрируют образец вариации в взаимосвязи между признаками birthn и birthorder для первенцев. Цветовая кодировка, проявляемая после выполнения исходного кода, улучшает интерпретацию. Можно сказать, что многочисленные фиолетовые и синие линии беспорядочны, даже немоно-
Глава 4. Основы важности признаков и их влияние 177 тонны и, как правило, имеют более низкие вероятности в целом, тогда как желтые и зеленые стабильнее и выше. Кажется, что чем больше вы не согласны с утверждением в вопросе Q1 ("я прочитал абсурдное количество книг"), тем менее надежным является предсказание первенца. Рис. 4.15. График ICE первенца по мере роста величины признака birthn и с цветовой кодировкой для ответов на вопрос Q1 Теперь можно сделать то же самое для среднего ребенка (middle_child) с тем же исходным кодом, но заменив первые два аргумента в функции plot_data_vs_ice следующим образом: mldatasets.plot_data_vs_ice(predict_prob_middle_child,\ 'Вероятность среднего ребенка', X=X_test_samp,\ feature_name='birthn',\ feature_label='Количество рождений',\ color_by='Q1', legend_key=legend_key) Приведенный исходный код генерирует два графика. Один из них показан на рис. 4.16. Линии графика ICE для среднего ребенка на рис. 4.16 стабильнее, чем линии для первенцев. Все линии резко увеличиваются с 2 до 3, далее плавно с 3 до 6, а затем выходят на плато. Цветовая кодировка предполагает, что чем больше респонденты согласны с утверждением в вопросе Q1, тем меньше вероятность того, что они являются средними детьми, независимо от их возраста.
178 Часть II. Освоение методов интерпретации Рис. 4.16. Вероятность среднего ребенка на графике ICE по мере увеличения величины признака birthn и с цветовой кодировкой для ответов на вопрос Q1 Рис. 4.17. Вероятность последнего ребенка на графике ICE по мере увеличения величины признака birthn и с цветовой кодировкой для ответов на вопрос Q1
Глава 4. Основы важности признаков и их влияние 179 И наконец, попробуем сделать то же самое для последних детей: mldatasets.plot_data_vs_ice(predict_prob_last_born,\ 'Вероятность последнего ребенка', X=X_test_samp,\ feature_name='birthn',\ feature_label='Количество рождений',\ color_by='Q1', legend_key=legend_key) Показанный рис. 4.17 график был выведен в результате исполнения приведенного выше исходного кода. График ICE для вероятности последних детей на рис. 4.17 имеет еще больше вариаций, чем для первенцев. Однако, как и в случае с первенцами, чем меньше согласие с вопросом Q1, тем неустойчивее могут быть линии, но, в отличие от первенцев, согласие с вопросом Q1 совпадает с меньшей вероятностью, независимо от количества рождений (birthn). Недостатки графиков индивидуального условного ожидания Кривые графиков индивидуального условного ожидания (ICE), такие как на графиках частичной зависимости (PDP), исходят из независимости признаков, поэтому у них одни и те же недостатки. Кроме того, на ICE-гафиках невозможно отразить взаимодействие двух непрерывных признаков либо признаков с высокой кардинальностью. Например, мы смогли получить цветовое кодирование для вопроса Q1, но только потому, что существует шесть возможных значений Q1. Еще одним недостатком является то, что трудно установить связь между признаком и средним значением целевой переменной, но для этого служат PDP-графики. В конечном счете графики ICE преуспевают именно в поиске подсказок в вариации этой связи, а не в ее совокупности. Миссия выполнена Миссия состояла в том, чтобы определить, что, собственно, машинное обучение может обнаруживать в наборе данных из 40 000 записей ответов на вопросы опросников. Исследователи в области психологии хотели узнать, можно ли доверять этим данным, чтобы обеспечивать себе дальнейшие исследования на их основе. Они также хотели узнать, покажет ли интерпретация машинного обучения признаки и значения признаков, которые влияют на исход максимально. Используя графики частичной зависимости (PDP), мы обнаружили, что существуют некоторые расхождения с распределением возраста и очередности рождения, поскольку доля средних детей должна увеличиваться с возрастом. Если какое-либо упражнение по моделированию запланировано для работы в реально существующих сценариях, то тренировочные данные должны совпадать с реально существующими распределениями. Однако еще не все потеряно. Можно принять кор-
180 Часть II. Освоение методов интерпретации ректирующие меры, сбалансировав эти распределения. Вероятно, необходимо внести существенные изменения в данные, чтобы сделать их более надежными для исследовательских целей. В связи с этим, поскольку мы имеем дело с онлайновым опросником, собирающим данные анонимных респондентов, можно ожидать, что ложь будет обычным делом, поэтому должна быть установлена соответствующая погрешность ошибки. Что касается вопросов прозрачности, то согласно методу перестановочной важности признаков (PFI) количество рождений (birthn) на сегодняшний день является наиболее важным признаком. Тем не менее упражнение было успешным в выявлении таких вопросов, как Q1 ("я прочитал абсурдное количество книг") и Q13 ("я командую окружающими людьми"), которые стабильно коррелируют с очередностью рождения, и в подтверждении их влияния с помощью графиков индивидуального условного ожидания (ICE). Мы также раскрыли некоторые захватывающие взаимодействия между возрастом и количеством времени, затрачиваемым на прохождение теста (testelapse), с использованием PDP-графиков. После того как в отношении возраста проблемы с распределением будут устранены, возможно, это станет очевиднее. Резюме Прочитав эту главу, вы должны понять недостатки использования внутренних модельных параметров для оценки важности признаков. Мы также ответили на вопрос: каковы модельно-агностические полезные альтернативы ранжированию признаков по важности и визуализации их предсказательного воздействия? В следующей главе мы рассмотрим еще более устойчивые модельно-агностические методы глобальной интерпретации, преодолевающие некоторые трудности, с которыми сталкиваются рассмотренные в данной главе методы. Источник набора данных  Проект "Психометрия" с открытым исходным кодом (Open-Source Psychometrics Project, 2019). Сырые данные онлайновых личностных тестов находятся по адресу: https://openpsychometrics.org/_rawdata/. Справочные материалы  Klecka W. R. Discriminant analysis // Quantitative applications in the social sciences series. — 1980. — № 19. — Thousand Oaks, CA: Sage Publications. (Клека В. Р. Дискриминантный анализ.)  Cardell N. S., Joerding, W., Li, Y. 1994. Why some feedforward networks cannot learn some polynomials // Neural Computation. — 1994. — № 6. — P. 763–768. —
Глава 4. Основы важности признаков и их влияние 181 URL: https://doi.org/10.1162/neco.1994. 6.4.761. (Карделл Н. С., Йердинг У., Ли Ю. Почему некоторые сети прямого распрострарения не способны усваивать некоторые многочлены.)  Boger Z., Guterman H. Knowledge extraction from artificial neural network models // 1997 IEEE International Conference on Systems, Man, and Cybernetics. Computational Cybernetics and Simulation. — 1997. — URL: https://doi.org/10.1109/ ICSMC.1997.633051. (Богер З., Гутерман Х. Извлечение знаний из моделей нейросетевых моделей.)  Molnar C. Interpretable Machine Learning. A guide for making black box models ex- plainable. — URL: https://christophm.github.io/interpretable-ml-book/. (Мольнар С. Интерпретируемое машинное обучение. Руководство по наделению моделей типа "черный ящик" объяснимостью.)
5 Модельно-агностические методы глобальной интерпретации В главе 4 мы продемонстрировали, что перестановочная важность признаков является более качественной альтернативой использованию внутренних модельных параметров для ранжирования признаков по их влиянию на модельные исходы. Мы также научились применять графики частичной зависимости (PDP) и графики индивидуальных условных ожиданий (ICE) для оценивания изменений модельных исходов в зависимости от значений признаков и взаимодействий. Однако, несмотря на то что все эти глобальные модельно-агностические методы чрезвычайно популярны, у них есть нечто общее — они чувствительны к коллинеарным признакам. В этой главе мы продолжим рассматривать глобальные модельно-агностические методы, два из которых были разработаны в основном для ослабления воздействия мультиколлинеарности с помощью очень прочного статистического фундамента. Первый из них — это аддитивные объяснения Шепли (SHapley Additive exPlanations, SHAP), которые в основном придерживаются математических принципов использования значений Шепли, выведенных из теории коалиционных игр. Второй базируется на графиках накопленных локальных эффектов (accumulated local effects, ALE), которые, используя условные маргинальные распределения, обеспечивают более качественную альтернативу графикам частичной зависимости (partial dependence plots, PDP). Наконец, объяснение моделей черного ящика осуществляется еще одним распространенным способом — посредством моделей белого ящика, которые их аппроксимируют, поэтому мы рассмотрим широкую тему глобальных суррогатов, которые при правильном выборе бывают очень точными и эффективными инструментами интерпретации. Вот главные темы, которые будут освещены в этой главе:  знакомство со значениями Шепли;  интерпретирование сводки аддитивных объяснений Шепли (SHAP) и графиков зависимости;  исследование графиков накопленных локальных эффектов (ALE);  объяснение моделей черного ящика с помощью глобальных суррогатов. Технические требования В примере, рассматриваемом в данной главе, используются библиотеки mldatasets, pandas, numpy, sklearn, tensorflow, xgboost, rulefit, matplotlib, seaborn, scipy, shap и
Глава 5. Модельно-агностические методы глобальной интерпретации 183 alepython. Исходный код главы находится по адресу: https://github.com/ PacktPublishing/Interpretable-Machine-Learning-with-Python/tree/master/ Chapter05. Миссия Энергоэффективность серьезно беспокоит потребителей, которые хотят меньше потреблять энергии или загрязнять окружающую среду. Следовательно, она входит в компетенцию политиков, регулирующих органов, экологических активистов, функционеров общественного здравоохранения и производителей энергопотребляющих технологий. В 2019 году только в США на долю транспортного сектора приходилось 28% (https://www.eia.gov/energyexplained/use-of-energy/transportation.php) общего энергопотребления, из которых более половины относится к легковым пассажирским транспортным средствам. И хотя за последнее десятилетие увеличился парк электромобилей США, бóльшая часть их электроэнергии по-прежнему поступает от электростанций, работающих на ископаемом топливе. В конечном счете это означает, что все пассажирские транспортные средства имеют углеродный след независимо от типа топлива. В примере данной главы допустим, что американская некоммерческая организация по защите прав потребителей, в которой вы работаете, традиционно сосредоточена на безвредности автомобилей, а мошеннические методы продаж сдвигают их внимание в сторону энергоэффективности. Законы о безопасносности, принятые за последние несколько десятилетий, значительно снизили ответственность производителя за счет улучшения контроля качества и регулирования. Хотя безвредность по-прежнему вызывает беспокойство, оно в основном связано с безрассудным вождением и плохими атмосферными условиями. Механическая неисправность является причиной только 2–3% всех автомобильных аварий. Лишь в очень редких случаях их можно отнести на счет производителей транспортных средств или комплектующих, таких как тормоза, подвеска, трансмиссия или шины. Некоммерческая организация может похвастаться тем, что она очень успешно пресекает мошенничество и дискриминационную практику со стороны автосалонов. Понимая, что молодое поколение заботится как об окружающей среде, так и о ресурсах, руководители некоммерческой организации хотят идти в ногу со временем, отстаивая эффективность использования топлива, которую можно измерять в милях на галлон израсходованного топлива (miles per gallon, MPG). Чем больше это число, тем выше эффективность. К счастью, Министерство энергетики США (https://www.fueleconomy.gov/feg/ws/) регистрирует это число для всех транспортных средств в стране начиная с 1984 года. Некоммерческая организация хочет объяснить в своих брошюрах, как эти разные переменные влияли на MPG за последние несколько десятилетий. Ваши заказчики хотели бы, чтобы вы в качестве их постоянного исследователя данных отыскали наиболее зна-
184 Часть II. Освоение методов интерпретации чимые предсказатели топливной эффективности и, возможно, проиллюстрировали их в понятном для людей ключе. Подход Вам предоставлен набор данных с тысячами моделей автомобилей. Он включает общие сведения, а также сведения о двигателе, загрязнении окружающей среды, трансмиссии, шасси и технические детали по каждой модели. В целях отыскания предсказателей для пробега в милях на галлон израсходованного топлива можно использовать испытанные и проверенные статистические методы, например статистическую проверку гипотез, анализ корреляций и имманентно интерпретируемые модели, такие как обобщенные линейные модели, с целью получения достоверного понимания данных. Тем не менее в каждом конкретном случае вам нужно будет обеспечить использование верных статистических методов и проверить соответствие ваших данных их опорным допущениям. И даже после всего этого вашим внутренним моделям будет не хватать достаточной точности предсказания, чтобы подкреплять любые выводы. Многие практики доверяют этому классическому подходу. Однако эта книга поддерживает мнение о том, что модели черного ящика могут извлекать из данных больше знаний и делать это надежнее и эффективнее, чем при классическом подходе. Интерпретируемое машинное обучение предоставляет для этого свой набор инструментов. С этой целью давайте воспользуемся сокращенной методикой из семи пунктов, которая не преподается на занятиях по прикладной статистике! 1. Подготовить все признаки так, чтобы у них не было значений null и все они были числовыми. 2. Убедиться, что с помощью этих признаков можно хорошо прогнозироовать MPG на основе модели черного ящика. В этом примере мы будем использовать нейронные сети и XGBoost. 3. Выполнить оценивание на тестовом наборе данных, чтобы убедиться, что модель не переобучена. 4. Использовать SHAP, чтобы понять, каким образом удалось прийти к полученным выводам. 5. Выполнить несколько статистических тестов для дальнейшей оценки двупеременных ассоциаций и исключить любые мнимые корреляции и систематические смещения. 6. Подробнее исследовать эффекты признаков на моделях с помощью графиков ALE. 7. Постараться детально понять опорные правила модели с помощью глобальных суррогатов. Давайте начнем!
Глава 5. Модельно-агностические методы глобальной интерпретации 185 Подготовительные работы Исходный код этого примера можно найти по адресу: https://github.com/ PacktPublishing/Interpretable-Machine-Learning-with-Python/blob/master/Chapter05/ FuelEfficiency.ipynb. Загрузка библиотек Для того чтобы отслеживать ход выполнения этого примера, вам необходимо установить следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования данными;  sklearn (scikit-learn), tensorflow, xgboost и rulefit для разбивки данных и подгонки моделей;  scipy для статистического тестирования;  matplotlib, seaborn, shap и alepython для визуализации интерпретаций. Сначала необходимо их загрузить следующим образом: import math import os import mldatasets import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn import metrics, tree import as tf import tensorflow_docs as tfdocs import tensorflow_docs.plots import xgboost as xgb from rulefit import RuleFit from scipy import stats import matplotlib.pyplot as plt import seaborn as sns import shap from alepython import ale_plot Теперь с помощью инструкции print(tf.__version__) давайте проверим, что используется нужная версия TensorFlow. Версия должна быть 2.0 или выше.
186 Часть II. Освоение методов интерпретации Изучение проблемы и подготовка данных Загрузим данные в кадр данных, который назовем fueleconomy_df. Обратите внимание, что мы используем аргумент prepare=True, который подготавливает признаки автоматически, избавляя вас от необходимости заниматься этим самостотельно: fueleconomy_df = mldatasets.load("vehiclefueleconomy", prepare=True) Должно быть более 43 000 записей и 84 столбца. Это можно подтвердить с помощью библиотечного метода info(): fueleconomy_df.info() Результат должен пройти проверку успешно. Все признаки являются числовыми без пропущенных значений, а категориальные признаки заранее были закодированы с одним активным состоянием за нас, потому что мы использовали аргумент prepare=True. Словарь данных В наборе данных имеется всего 25 признаков, но из-за категориального кодирования они становятся 84 столбцами, из которых 3 относятся к общим, 6 — к двигателю, 3 — к загрязнению окружающей среды, 3 — к трансмиссии, 7 — к шасси, 2 — к электронике и один является целевым признаком. Словарь данных можно очертить в разбивке по указанным категориям. Признаки из общей категории  make — категориальный; марка или производитель транспортного средства (поч- ти из 140 различных).  model — категориальный; модель транспортного средства (более чем из 4000 раз- личных).  year — порядковый; год модели (с 1984 по 2021 год). Признаки двигателя  fuelType — категориальный; первичный тип используемого двигателем топлива.  cylinders — порядковый; число цилиндров двигателя (от 2 до 16). Как правило, чем больше цилиндров, тем выше мощность.  displ — непрерывный; объем двигателя (в литрах от 0,6 до 8,4).  eng_dscr — текстовый; описание двигателя, состоящее из одного или нескольких буквенно-цифровых кодов, объединенных вместе (коды можно найти по адресу: https://www.fueleconomy.gov/feg/findacarhelp.shtml#engine).  phevBlended — двоичный; аббревиатура PHEV (от англ. plug-in-hybrid vehicle) означает транспортное средство с гибридной силовой установкой, а Blended указывает на то, что автомобиль будет питаться от аккумулятора и только дополняться топливом. Если значение признака истинно, то транспортное средство использует этот режим работы, именуемый режимом истощения заряда.
Глава 5. Модельно-агностические методы глобальной интерпретации 187  atvtype — категориальный; альтернативные виды топлива или технологии, ис- пользуемые в двигателе (из 8 различных). Признаки загрязнения  co2TailpipeGpm — непрерывный; выбросы углекислого газа (CO2) из выхлопной трубы в граммах на милю.  co2 — непрерывный; выбросы CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года. Для указанных моделей он основан на тестах EPA. В предыдущие годы CO2 оценивался с использованием коэффициента выбросов EPA ( 1 — отсутствует).  ghgScore — порядковый; балл выбросов парниковых газов EPA (от 0 до 10; 1 — отсутствует). Признаки трансмиссии  drive — категориальный; тип транспортного средства с ведущей осью (из 7 разных).  trany — категориальный; дескриптор передачи в основном в форме "{тип}, {скорость}-скор", где тип может быть только ручным (Manual) или автоматическим (Automatic).  trans_dscr — текстовый; более подробное описание передачи, состоящее из одно- го либо нескольких буквенно-цифровых кодов, объединенных вместе (коды можно найти по адресу: https://www.fueleconomy.gov/feg/findacarhelp.shtml#trany). Признаки шасси  VClass — категориальный; тип транспортного средства (из 34 разных).  pv4 — непрерывный; 4-дверный объем пассажирского салона (в кубических футах).  lv4 — непрерывный; 4-дверный объем багажного отделения (в кубических футах).  lv2 — непрерывный; 2-дверный объем багажного отделения (в кубических футах).  pv2 — непрерывный; 2-дверный объем пассажирского салона (в кубических футах).  hlv — непрерывный; объем пассажирского салона хэтчбека (в кубических футах).  hpv — непрерывный; объем багажного отделения хэтчбека (в кубических футах). Признаки электроники  startStop — категориальный; технология запуска-остановки, встроенная в транс- портное средство (Y — да, N — нет, пусто — старые транспортные средства).  tCharger — категориальный; транспортное средство с турбонаддувом (T — да, пусто — в противном случае). Целевой признак  comb08 — непрерывный; комбинированные мили на галлон израсходованного топлива. Для электромобилей и транспортных средств на сжатом природном газе это число равняется милям в эквиваленте бензина на галлон израсходованного топлива (gasoline-equivalent miles per gallon, MPGe).
188 Часть II. Освоение методов интерпретации Теперь, когда мы взглянули на данные, можно быстро подготовить их для конструирования моделей, а затем выполнить обучение нескольких из них! Подготовка данных Судя по сводке библиотечного метода info(), значений null не осталось; у нас много категориально кодированных столбцов, и все столбцы либо числовые, либо логические, за исключением столбцов make и model, которые мы отбросим. Выборка почти готова. Сейчас нам осталось лишь разбить набор данных на тренировочную, тестовую и валидационную выборки. Для этого мы сначала поместили наш одинединственный целевой признак в y, а все остальные, кроме признаков make и model, — в X, затем разбили X и y на тренировочную (85%) и тестовую (15%) выборки, а потом X_train и Y_train на тренировочную (80%) и валидационную (20%). Как обычно, важно задать начальное значение для генерации случайного числа и поместить его в переменную под названием rand для аргумента random_state, чтобы обеспечить воспроизводимость: rand = 9 y = fueleconomy_df['comb08'] X = fueleconomy_df.drop(['comb08','make','model'], axis=1).copy() X_train, X_test, y_train, y_test =\ train_test_split(X, y, test_size=0.15, random_state=rand) X_train, X_val, y_train, y_val =\ train_test_split(X_train, y_train, test_size=0.2, random_state=rand) Отлично! Теперь мы готовы предпринять шаги по построению моделей и их оцениванию! Построение глубокой нейросетевой модели и оценивание ее результативности Нестабильности в модельных результатах усугубляются трудностями интерпретации, поэтому воспроизводимость имеет первостепенное значение. Однако обеспечить воспроизводимость нейронных сетей, как известно, очень трудно и, учитывая их стохастический характер, еще важнее задать начальное значение для генерации случайного числа, чтобы иметь до некоторой степени стабильные результаты. Следующий ниже исходный код работает, как правило, в TensorFlow 2.0+: os.environ['PYTHONHASHSEED']=str(rand) tf.random.set_seed(rand) np.random.seed(rand) Мы создадим нейронную сеть прямого распространения (Sequential) с двумя скрытыми слоями с 64 скрытыми узлами в каждом. В Keras это можно сделать путем добавления входного (Input), скрытых (Dense(64)) и выходного (Dense(1)) слоев. Между входным и первым скрытым слоем мы также добавляем слой нормализации (Normalization), который нормализует все признаки, чтобы среднее значение равнялось 0, а среднее квадратичное отклонение было 1. Это преобразование обычно вы-
Глава 5. Модельно-агностические методы глобальной интерпретации 189 полняется на отдельном шаге предобработки, но гораздо выгоднее иметь модельный конвейер либо сделать так, чтобы модель сама заботились об этом шаге, включая более чистый исходный код и повышенную надежность. После того как мы построим нашу модель (fitted_nn_model), можно ее скомпилировать, используя в качестве нашей функции потерь среднюю квадратичную ошибку (mean squared error, MSE) и единственную метрику (metrics=['mse']). Воспользуемся оптимизатором Adam с очень низкой скоростью усвоения (lr=0.0005). Это замедлит обучение, но с таким значением гиперпараметра она сходится на более низком уровне MSE. fitted_nn_model = tf.keras.Sequential([ tf.keras.Input(shape=[len(X_train.keys())]), tf.keras.layers.experimental.preprocessing.Normalization(), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(1) ]) fitted_nn_model.compile(loss='mean_squared_error',\ optimizer=tf.keras.optimizers.Adam(lr=0.0005),\ metrics=['mse']) fitted_nn_model.summary() Библиотечный метод summary() может применять для печати всех слоев с их формами и параметрами по каждому из них: Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= normalization (Normalization) (None, 81) 163 _________________________________________________________________ dense (Dense) (None, 64) 5248 _________________________________________________________________ dense_1 (Dense) (None, 64) 4160 _________________________________________________________________ dense_2 (Dense) (None, 1) 65 ================================================================= Total params: 9,636 Trainable params: 9,473 Non-trainable params: 163 _________________________________________________________________ Сеть должна сходиться где-то между 700-й и 1300-й эпохами. Вместо того чтобы стремиться к фиксированному числу эпох, можно установить его равным 3000 и в функции подгонки (fit) имплементировать раннюю остановку в качестве функции обратного вызова. Указанная функция обратного вызова (EarlyStopping) будет от-
190 Часть II. Освоение методов интерпретации слеживать валидационное значение loss-функции и проверять последние 200 эпох (patience) с чрезвычайно низким минимумом, который будет квалифицироваться как улучшение (min_delta=0.0001). Мы также обеспечим восстановление весов эпохи с наилучшим показателем функции потерь на валидационной части (restore_best_weights=True). Когда мы выполняем подгонку модели, можно сохранять историю обучения (nn_history): es = tf.keras.callbacks.EarlyStopping(monitor='val_loss', mode='min', verbose=1,\ patience=200, min_delta=0.0001, restore_best_weights=True) nn_history = fitted_nn_model.fit(X_train.astype(float), y_train.astype(float),\ epochs=3000, batch_size=128,\ validation_data=(X_val.astype(float), y_val.astype(float)),\ verbose=1, callbacks=[es]) Библиотека tensorflow_docs идет в комплекте с графопостроителем, специально разработанным для поддержания истории обучения (HistoryPlotter), который имеет просто фантастическую функциональность, позволяя строить график истории в одной-двух строках исходного кода: nn_plotter = tfdocs.plots.HistoryPlotter(smoothing_std=2) nn_plotter.plot({'Ранняя остановка': nn_history}, metric="mse") plt.show() Приведенный выше фрагмент исходного кода выводит на экран рис. 5.1. Он показывает, как после 750 эпох валидационная метрика MSE колеблется около 0,75 на минимуме, поэтому запускается ранняя остановка. Теперь давайте оценим предсказательную результативность. В целях ознакомления с качеством обучения модели, т. е. с тем, насколько хорошо подогнанная модель предсказывает тестовый набор данных, всегда можно использовать функцию Model.evaluate, но она будет делать это с помощью функции потерь и метрик, в обоих случаях MSE. Вместо нее мы будем использовать RMSE, которую легче интерпретировать. В целях вычисления RMSE необходимо выполнить функцию Model.predict, чтобы получить предсказания как на тренировочном, так и на тестовом наборах данных, а затем, используя метод mean_squared_error библиотеки scikitlearn с аргументом squared=False, можно получить RMSE для обоих. Нам также потребуется R-квадрат как мера качества подгонки, указывающая на процент изменчивости, объясняемый моделью: y_train_nn_pred = fitted_nn_model.predict(X_train.astype(float)) y_test_nn_pred = fitted_nn_model.predict(X_test.astype(float)) RMSE_nn_train = metrics.mean_squared_error(y_train, y_train_nn_pred,\ squared=False) RMSE_nn_test = metrics.mean_squared_error(y_test, y_test_nn_pred,\ squared=False) R2_nn_test = metrics.r2_score(y_test, y_test_nn_pred) print('RMSE_train: %.4f_test: %.4f: %.4f' % (RMSE_nn_train, RMSE_nn_test, R2_nn_test))
Глава 5. Модельно-агностические методы глобальной интерпретации 191 Рис. 5.1. История обучения нейронной сети для показания MSE на тренировочной (train) и валидационной (val) выборках Значения RMSE при обучении и валидации достаточно близки, говоря о минимальном переобучении, и любая RMSE менее единицы является превосходной, т. к. это означает, что в среднем предсказанные комбинированные мили на галлон израсходованного топлива составляют (MPG) не более 1MPG от наблюдаемой комбинированной MPG. Кроме того, метрика R-квадрат, равная 99%, означает, что модель объясняет подавляющую часть изменчивости: RMSE_train: 0.7012 RMSE_test: 0.7878 r2: 0.9907 Качество обучения модели также можно визуализировать, построив график наблюдаемых комбинированных милей на галлон относительно предсказанных в y с помощью линии регрессии. Для этого используется функция regplot библиотеки seaborn, которая может генерировать этот график в одной строке кода. Мы выполним индивидуальную настройку графика рассеяния, чтобы каждая точка данных выглядела на 30% прозрачной ('alpha':0.3) и зеленой (color="g"), а также зададим метки осей и размер шрифта: plt.ylabel('Предсказанные комбинированные MPG', fontsize=14) sns.regplot(x=y_test, y=y_test_nn_pred,\ color="g", scatter_kws={'alpha':0.3}) plt.xlabel('Наблюдаемые комбинированные MPG (comb08)', fontsize=14)
192 Часть II. Освоение методов интерпретации Рис. 5.2. График наблюдаемых комбинированных MPG относительно предсказанных MPG с линией регрессии для нейросетевой модели Приведенный выше исходный код выводит на экран рис. 5.2. График на рис. 5.2 показывает качество предсказания моделью фактических комбинированных милей на галлон израсходованного топлива. Точки расположены к линии регрессии ближе всего в левом нижнем квадранте; такое распределение позволяет предположить, что чем ниже значение, тем лучше модель их предсказывает. Это неудивительно, учитывая, насколько больше точек находится в левом нижнем квадранте, нежели в правом верхнем. Теперь можно приступить к обучению модели XGBoost! Построение модели XGBoost и оценивание ее результативности XGBoost — это библиотека, в которой имплементированы градиентно-бустированные деревья решений, очень похожие на библиотечный класс GradientBoostingRegressor библиотеки scikit-learn, ансамблевый метод которой мы использовали в предыду-
Глава 5. Модельно-агностические методы глобальной интерпретации 193 щих главах. Однако в то время как scikit-learn следует изначальному алгоритму градиентного бустирования для дерева, в XGBoost имплементировано несколько оптимизаций, которые делают указанную библиотеку быстрее и масштабируемее, повышают предсказательную результативность и потенциально делают ее еще менее подверженной переобучению. Официальная имплементация XGBoost требует, чтобы вы хранили свои данные в объектах DMatrix с целью еще большего повышения эффективности. Это делается следующим образом: dtrain = xgb.DMatrix(X_train, label=y_train) dval = xgb.DMatrix(X_val, label=y_val) dtest = xgb.DMatrix(X_test, label=y_test) В нашем примере мы будем использовать исходный код библиотеки scikit-learn, служащий оберткой для библиотеки XGBoost. Указанная обертка конвертирует данные в объекты DMatrix автоматически. Причина в том, что многие модельноагностические методы интерпретации ожидают от вас использования функций, которые следуют стереотипу, популяризированному экосистемой scikit-learn, как например методы fit(X, y) и predict(X), где X и y — это массивы или разреженные матрицы, обычно массивы NumPy или кадры данных pandas. Однако в XGBoost всё происходит иначе, потому что DMatrix — это совершенно другой тип данных. Тем не менее методы интерпретации по-прежнему остаются технически модельноагностическими, т. к. они не зависят от внутренних модельных параметров. И все же, на практике вам придется программировать класс, который выступает в качестве промежуточного звена, во многом как в данном случае делает обертка библиотеки scikit-learn. Единственный недостаток — библиотека XGBoost имеет массу параметров, не доступных в обертке. К счастью, в данном случае нам не нужно было использовать многие из них. Нам только требуется установить максимальную глубину дерева (max_depth) равной 7, скорость усвоения (learning_rate) равной 0,6 и нашу целевую задачу как регрессию, минимизирующую ошибку в квадрате (reg:squarederror), а затем выполнить 4 задания в параллельном режиме (n_jobs). Подгонка модели с оцениванием RMSE выполняется легко, используя как тренировочный, так и валидационный наборы данных: fitted_xgb_model = xgb.XGBRegressor(max_depth=7, learning_rate=0.6, n_jobs=4,\ objective='reg:squarederror', random_state=rand, n_estimators=50).\ fit(X_train, y_train, eval_metric='rmse', eval_set=[(X_train, y_train),\ (X_val, y_val)]) После завершения подгонки модели можно построить график истории обучения с помощью библиотеки matplotlib. Эту историю можно извлечь из подогнанной модели XGBoost с помощью функции evals_result(), которая возвращает словарь. Поскольку мы поместили два валидационных набора данных в eval_set, в этом словаре есть два элемента с именами validation_0 и validation_1. Первый соответствует оценке на тренировочном наборе, а второй — на валидационном наборе: plt.plot(fitted_xgb_model.evals_result()['validation_0']['rmse']) plt.plot(fitted_xgb_model.evals_result()['validation_1']['rmse']) plt.ylabel('RMSE [комбинированные MPG]', fontsize=14)
194 Часть II. Освоение методов интерпретации plt.xlabel('Раунд', fontsize=14) plt.legend(['Тренировка', 'Валидация'], loc='upper right') Как следует из рис. 5.3, модель быстро сходится при значении валидационной RMSE, близкой к 0,75. Хотя разрыв между тренировочной и валидационной RMSE можно потенциально сократить путем корректировки гиперпараметров, он по-прежнему указывает на относительно малое переобучение. Это не является вопросом первостепенной важности для модели, которую мы не планируем внедрять в производство, и ошибка остается низкой с ранее не встречавшимися данными. Рис. 5.3. История обучения XGBoost — показания RMSE на тренировочной и валидационной частях данных Давайте выполним оценивание результата относительно тестового набора данных, чтобы убедиться, что он соответстует нашим ожиданиям так же, как в случае с нейронной сетью: y_train_xgb_pred = fitted_xgb_model.predict(X_train) y_test_xgb_pred = fitted_xgb_model.predict(X_test) RMSE_xgb_train = metrics.mean_squared_error(y_train, y_train_xgb_pred,\ squared=False) RMSE_xgb_test = metrics.mean_squared_error(y_test, y_test_xgb_pred, squared=False) R2_xgb_test = metrics.r2_score(y_test, y_test_xgb_pred)
Глава 5. Модельно-агностические методы глобальной интерпретации 195 print('RMSE_train: %.4f_test: %.4f: %.4f' %\ (RMSE_xgb_train, RMSE_xgb_test, R2_xgb_test)) И тестовая, и тренировочная RMSE сейчас еще ниже, а R-квадрат еще выше, чем в случае с нейросетевой моделью, которая уже была приемлемой для нас. Предсказательная результативность этой модели тоже является достаточной, чтобы быть полезной для целей глобальной модулярной интерпретации: RMSE_train: 0.2974 RMSE_test: 0.6809 r2: 0.9930 Что касается интерпретации, то как и большинство древовидных моделей, XGBoost идет в комплекте с возможностями по определению важности признаков. В XGBoost есть три разных алгоритма вычисления важности признаков: частота появления признака в дереве (weight), среднее уменьшение в ошибке из-за признака (gain) и число наблюдений, на которые влияет разбивка ветви с участием признака (cover). По умолчанию используется аргумент importance_type="weight". Выполнив следующий ниже исходный код для функции plot_importance, можно испытать все три признака и посмотреть, насколько они различаются: sns.set() fig, ax = plt.subplots(figsize=(12, 8)) xgb.plot_importance(fitted_xgb_model, max_num_features=12, ax=ax,\ importance_type="weight") plt.show() Рис. 5.4. Важность признаков (feature importance), рассчитанная библиотекой XGBoost с использованием веса (weight) или частоты появления признака в дереве
196 Часть II. Освоение методов интерпретации Приведенный выше фрагмент исходного кода генерирует рис. 5.4, на котором изображен один из многих способов расчета важности признаков в XGBoost, но какому из них следует доверять? Если взять верхние признаки для всех этих способов, то можно найти общие признаки, и весьма вероятно, что именно они действительно играют в модели самую важную роль. Однако мы по-прежнему хотели бы качественно оценить величину важности и обстоятельства, при которых она проявляется, и в идеале — с помощью надежного метода, который обоснован статистически и является модельно-агностическим... Самое время познакомиться со значениями Шепли! Значения Шепли В нескольких главах этой книги мы будем регулярно обращаться к одному методу особенно: SHAP. Поэтому будет лучше, если мы проведем обзор математического фундамента и его базовых свойств. Мы сделаем это по аналогии с баскетболом. Вообразите, что у вас завязаны глаза на баскетбольном матче, где комментатор через громкоговоритель объявляет всякий раз о выходе игрока вашей команды на поле или ухода с него либо сообщает, когда команда увеличивает счет. Комментатор не говорит, кто именно забросил мяч в корзину, и у вас завязаны глаза, поэтому вы не знаете, ни кто забил, ни даже кто провел результативную передачу! К игрокам обращаются только по номерам, и вы все равно не знаете, кто они. Эти игроки могут быть хорошими либо плохими. В любой момент времени можно угадывать, что тот, кто вступил в игру последним, имел какое-то отношение к последнему исходу, хорошему либо плохому. Поэтому со временем вы начинаете понимать, какие игроки коррелируют с более высокими результатами сильнее всего, а для каких наблюдается противоположный эффект или вообще отсутствует. А смогли бы мы просимулировать эту игру со всеми возможными комбинациями игроков, вступающих в игру в разных очередностях много раз, и усреднить все разницы в очках, когда каждый игрок вступил в игру? И даже если бы результат не был точным по отношению к одной игре, то для многочисленных игр мы получили бы более точное представление о том, кто был самым ценным игроком. На высоком уровне в теории коалиционных игр, также именуемой теорией кооперативных игр, различные комбинации игроков являются коалициями, разницы в баллах (а в нашем случае очках) являются маргинальными (предельными) вкладами, а значение Шепли — это среднее значение этих вкладов во многих симуляциях. Для модели признаки являются игроками, разные подмножества признаков — коалициями игроков, разницы в предсказательных ошибках — маргинальными вкладами, и у вас завязаны глаза, потому что, конечно же, модель является черным ящиком или, по меньшей мере, трактуется как таковая! Участвующая в вычислении значений Шепли для модели математика становится сложнее, чем может предложить аналогия с баскетболом, потому что она предусматривает множества и факториалы, и мы не будем вдаваться во все алгоритмические детали, описанные в статьях, в которых значения Шепли были адаптированы
Глава 5. Модельно-агностические методы глобальной интерпретации 197 к машинному обучению. Однако ее можно объяснить просто и интуитивно. У вас есть полная коалиция со всеми признаками и все возможные подмножества признаков за вычетом признака, который вы оцениваете. Вклад признака, также именуемый отдачей, представляет собой уменьшение в предсказательной ошибке для регрессии или увеличение вероятности для классификации. Таким образом, в целях вычисления значения Шепли для признака и конкретного подмножества вы вычисляете вклад при добавлении этого признака в это подмножество. Всё это взвешивается на вероятность случайного взятия этого подмножества признаков из всех возможных подмножеств. Эти взвешенные вклады суммируются по всем возможным подмножествам, и вуаля! У вас на руках ваше значение Шепли. По сути, это средний маргинальный вклад по признаку во всех возможных подмножествах. Однако на практике время вычисления значений Шепли должно неуклонно расти экспоненциально по мере увеличения признаков, поэтому подход с использованием грубой силы будет очень ресурсоемким. Существует несколько стратегий минимизации вычислений. Наиболее распространенной является отбор только некоторых возможных подмножеств признаков с использованием метода, именуемого выборками Монте-Карло, который случайно извлекает выборки из распределения значений вероятности. Кроме того, игрока из игры можно удалить, но удалить признак из уже обученной модели невозможно. Тогда как представить результативность модели с признаком и без него? Подход на основе перестановочной важности делает это путем перетасовки признаков, но алгоритм Шепли вместо этого вычисляет ожидаемое значение признаков по всему набору данных. Такой подход имеет смысл, потому что он был бы наилучшей догадкой о значении признака, и это допущение разумно. Возможно, этот подход не идеален, но он служит лишь базовым уровнем для сравнения вкладов конкретного признака. Главное — согласованность. Говоря о согласованности, следует отметить, что значения Шепли имеют несколько свойств, выведенных непосредственно из теории коалиционных игр, которые делают метод SHAP идеальным в качестве метода определения важности признаков.  Свойство фиктивности: если признак i никогда не вносит маргинального зна- чения, то Шеплиi  0 .  Свойство замещаемости: если два заданных признака i и j вносят вклад во все их возможные подмножества в равной мере, то Шеплиi  Шепли j .  Свойство аддитивности: если модель P является ансамблем из k подмоделей, то вклады признака k P Шеплиi   n1 Шеплиin . i в подмодели должны складываться:  Свойство эффективности: аналогичным образом, все значения Шепли должны складываться как разность между предсказаниями и ожидаемыми значениями. На момент написания книги в Python не было "чистых" имплементаций значений Шепли. Даже в имплементациях R используется взятие выборок для сокращения времени вычислений. Однако самая популярная имплементация на Python — SHAP — "срезает углы" еще больше с целью обеспечения модельно-агностическо-
198 Часть II. Освоение методов интерпретации го подхода, задействуя внутренние параметры некоторых модельных классов, а именно древовидных моделей и моделей глубокого обучения, а также линейных суррогатных моделей. Интерпретирование сводки SHAP и графиков зависимости Аддитивные объяснения Шепли (SHapley Additive exPlanations, SHAP) — это коллекция методов, или объяснителей, которые аппроксимируют значения Шепли, по большей части придерживаясь их математических свойств. Вообще, указанные значения называются значениями SHAP, но в данной книге название SHAP будет использоваться взаимозаменяемо с термином "Шепли". Тем не менее следует отметить, что авторы SHAP позволили себе несколько вольностей со свойствами. Например, некоторые объяснители не соответствуют требованиям, предъявляемым к свойству фиктивности, и задействуют опорные фоновые данные для симулирования пропущенных значений. Несмотря на эти проблемы, ввиду того что SHAP основан на других солидных свойствах, он все же лучше, чем альтернативы, изученные в главе 4. В нем есть три свойства, которые слабо основаны на свойствах Шепли:  локальная точность — эквивалентна свойству эффективности Шепли;  согласованность — охватывает аксиомы аддитивности и замещаемости, а так- же, теоретически, фиктивности;  пропущенность — означает, что если признак пропущен, то его значение Шеп- ли равно нулю. Это свойство служит для проверки исправности и на практике необходимо только тогда, когда признаки постоянны. Многие объяснители унифицируют другие методы интерпретации в целях выполнения аппроксимации эффективным образом. По этой причине четыре объяснителя не являются модельно-агностическими (рис. 5.5), т. к. унифицированные методы приводят к привлечению структур или параметров модели. Особенности этих алгоритмов обсуждаются в главной статье по SHAP — "A Unified Approach to Interpreting Model Predictions" ("Унифицированный подход к интерпретированию модельных предсказаний"), а также в статье, написанной для древовидных моделей, TreeSHAP, которые позже были переименованы в двевесный объяснитель TreeExplainer. В дополнение к этому два метода работают исключительно с каркасами глубокого обучения TensorFlow/Keras и PyTorch, которые основаны соответственно на методах определения важных для глубокого обучения признаков (Deep Learning Important FeaTures, DeepLift) и ожидаемых градиентов. И есть один, который будет работать только с линейными моделями библиотеки scikitlearn. Помимо этих модельно-специфических объяснителей, ядерный объяснитель, выборочный объяснитель и другие являются модельно-агностическими с некоторыми оговорками.
Глава 5. Модельно-агностические методы глобальной интерпретации 199 Рис. 5.5. Сводная информация об объяснителях SHAP Теперь мы кратко представим каждый приведенный на рис. 5.5 стержневой объяснитель SHAP.  Древовидный объяснитель (TreeExplainer) был специально разработан для эф- фективного аппроксимирования значений SHAP для древовидных моделей, таких как древовидные ансамбли наподобие XGBoost или случайный лес, или деревья решений CART. Поскольку в нем вместо функции маргинального ожидаемого значения используется функция условного ожидаемого значения, он может придавать значения невлиятельным признакам, отличные от нуля, нарушая тем самым свойство фиктивности Шепли. Как обсуждалось в главе 4, такое поведение влечет последствия, когда признаки являются коллинеарными.  Глубокий объяснитель (DeepExplainer) работает только с моделями глубокого обучения, и унифицированным методом здесь является DeepLIFT. Для этого метода важность признака может быть связана с разницей на выходе, когда на вход передается опорное "нейтральное" значение, или базовый уровень. Это значение базового уровня на входе может быть произвольным, но оно в любом случае означает отсутствие признака. В библиотеке SHAP был выбран безопасный маршрут, а именно, использование среднего значения по всем признакам набора данных. Затем во время обратного распространения определяется множитель для каждого слоя как входы сверх выходов после вычета базового уровня:  y  yб.у   x  xб.у  . К множителям применяется цепное правило, и это делается по аналогии с примененим к градиентам. Важность признаков для конкретного экземпляра равна разности между входом и базовым уровнем, умноженной на частичный "наклон", производимый во время обратного распространения для множителей. Затем SHAP берет эти выходы и адаптирует их в соответствии со свойствами SHAP.  Градиентный объяснитель (GradientExlainer) объединяет несколько унифици- рованных методов, но основным из них являются ожидаемые градиенты, от-
200      1 Часть II. Освоение методов интерпретации ветвление от интегрированных градиентов и гладких градиентов (метод SmoothGrad)1. Мы здесь не будем вдаваться в подробности, отложив объяснение до главы 8. Так же как и в DeepLIFT, в интегрированных градиентах используется базовый уровень, который отражает отсутствие признаков, и он проводит интегрирование с базовым уровнем между данными на выходе и на входе. В библиотеке SHAP используется аналогичная концепция, именуемая ожидаемыми градиентами, которая заменяет интеграл математическим ожиданием. Затем она использует для этого ожидания фоновый набор данных в качестве опорных значений черпаемой выборки, что приводит к комбинированному ожиданию градиентов, которые сходятся в виде атрибутов, складывемых во многом так же, как требуется от значений SHAP. Линейный объяснитель (LinearExplainer) — очень простой объяснитель, навеянный исключительно одной из первых работ по Шепли в контексте моделей контролируемого обучения. Он лимитирован линейными моделями только из библиотеки scikit-learn. Ядерный объяснитель (KernelExplainer) — самый популярный модельноагностический метод, и он основан на локально-интерпретируемых модельноагностических объяснениях (LIME), которые мы обсудим в главе 6. Так же как и LIME, он задействует, например, линейные модели с подогнанными весами, но использует коалиции выборок Шепли и применяет другое ядро, которое возвращает значения SHAP в качестве коэффициентов. Кроме того, поскольку ядерный объяснитель заменяет отсутствующие признаки случайными данными при создании коалиций выборок, у него возникают проблемы со свойством фиктивности и, следовательно, с коллинеарными признаками. Мы это тоже обсудим далее в главе 6. Выборочный объяснитель (SamplingExplainer) основан исключительно на той первой статье, в которой был предложен подход на основе выборки для аппроксимирования значений Шепли, и является модельно-агностическим, но он исходит из допущения о независимости признаков. Это довольно хорошая альтернатива ядерному объяснителю, когда у вас есть обширный фоновый набор данных, который, например, был бы необходим для разреженных данных. Перестановочный объяснитель (PermutationExplainer) — самое близкое, что можно получить, чтобы апроксимировать значение Шепли методом грубой силы. Он работает путем перестановки всех признаков как в прямом, так и в обратном направлениях. Если это делается один раз, он улавливает значения SHAP вплоть до взаимодействий второго порядка, но может выполняться много раз для достижения еще более высокой точности. Разбивочный объяснитель (PartitionExplainer) вычисляет значения SHAP на дереве, определяющем иерархию признаков. Рекомендуется применять его, если многие ваши признаки принадлежат группе или категории или имеют сильно коррелированные признаки. Метод SmoothGrad вычисляет средние карты заметности над случайной перестановкой подаваемых на вход данных. См. https://arxiv.org/pdf/1905.12105.pdf. — Прим. перев.
Глава 5. Модельно-агностические методы глобальной интерпретации 201  Аддитивный объяснитель (AdditiveExplainer) будет принимать любую произ- вольную функцию предсказания, поэтому он является модельно-агностическим, но откажет, если модель не относится к классу обобщенных аддитивных моделей (generalized additive model, GAM). В этой главе мы будем использовать градиентный объяснитель для модели Keras и древесный объяснитель для модели XGBoost. Однако эти объяснители не являются модельно-агностическими. Зачем же их использовать в главе под названием "Модельно-агностические методы глобальной интерпретации"? Затем, что SHAP в целом является модельно-агностическим, т. к. все объяснители могут коллективно охватывать любой модельный класс и вариант использования для табличных, графовых или текстовых наборов данных. Еще важнее то, что они могут инициализироваться более-менее одинаково и иметь устоявшийся набор графиков, которые можно использовать для интерпретации. В этой главе мы научимся интерпретировать три таких графика. А в последующих главах мы задействуем ядерный объяснитель и глубокий объяснитель и распространим их применение на другие графики SHAP. Прежде чем приступить к интерпретации, необходимо выполнить два простых шага — в некоторых случаях они бывают сложными, поэтому мы рассмотрим их детально. 1. Инициализация объяснителей. Первым шагом для любого объяснителя SHAP является его инициализация. Древовидному объяснителю требуется только подогнанная древовидная модель (fitted_xgb_model): shap_xgb_explainer = shap.TreeExplainer(fitted_xgb_model) С другой стороны, градиентному объяснителю нужен фоновый набор данных. Можно либо взять выборку, либо обобщить набор данных: shap.kmeans(data, K). Мы возьмем выборку из 150 элементов, используя np.choice. Теперь инструкция print(background.shape) должна подтвердить, что мы отобрали 150 образцов из тестового набора данных, и, естественно, выборка имеет 81 признак. Затем, в целях инициализации объяснителя мы подключим нашу модель (fitted_nn_model) и фоновые данные (background). Обратите внимание, что мы конвертируем кадр данных pandas в массив NumPy чисел с плавающей запятой, чтобы он работал с TensorFlow: background =\ X_train.iloc[np.random.choice(X_train.shape[0], 150, replace=False)] print(background.shape) shap_nn_explainer = shap.GradientExplainer(fitted_nn_model,\ background.astype(float).values) После того как мы инициализировали объяснители SHAP, их можно использовать для вычисления значений SHAP. 2. Вычисление значений SHAP. Все объяснители имеют функцию shap_values, которая берет любое число наблюдений, если они соответствуют размерностям признаков, и вычисляют для них значения SHAP. Мы сделаем это для трениро-
202 Часть II. Освоение методов интерпретации вочного и тестового наборов данных модели XGBoost. Как обсуждалось в главе 4, интерпретация производительности работы модели относительно тренировочных и тестовых данных может нести разные выгоды. И хотя мы заинтересованы в понимании того, что именно модель находит в ранее не встречавшихся данных, неплохой проверкой исправности будет сравнение обоих, чтобы убедиться, что они почти полностью согласуются: shap_xgb_values_train = shap_xgb_explainer.shap_values(X_train) print(shap_xgb_values_train.shape) shap_xgb_values_test = shap_xgb_explainer.shap_values(X_test) print(shap_xgb_values_test.shape) Приведенный выше фрагмент исходного кода должен распечатать кортежи, соответствующие размерностям тренировочного ((29389, 81)) и тестового ((6484, 81)) наборов данных. Для каждого признака по каждому наблюдению должно быть одно значение SHAP. В отличие от других методов модельной интерпретации выведенные с помощью SHAP значения детализированы в достаточной мере, чтобы их можно было использовать для всех видов глобальной и локальной интерпретации без последующего обучения или постобработки. Теперь давайте вычислим нейросетевые значения SHAP для тестового набора данных и выведем на экран тип объекта, возвращаемого shap_values. Обратите внимание, что это список, а не массив. Теоретически список порождается только моделями с несколькими выходами, такими как классификаторы, но регрессионные нейросетевые модели с одним выходом также возвращают список. В этом случае искомые вами значения SHAP являются первым элементом в списке (shap_nn_values_test[0]): shap_nn_values_test =\ shap_nn_explainer.shap_values(X_test.astype(float).values) print(type(shap_nn_values_test)) print(shap_nn_values_test[0].shape) Приведенный выше фрагмент исходного кода должен распечатать размерности значений SHAP для нейросетевой модели и тестового набора данных ((6484, 81)). Генерирование сводных графиков SHAP Первое, что можно сделать со значениями SHAP, — это сгенерировать экземпляр сводного графика (summary_plot). Первым параметром являются значения, за которыми следуют данные, используемые для вывода на график, и, при необходимости, тип графика (plot_type). Мы построим сводный график XGBoost для тренировочных и тестовых данных, чтобы иметь возможность их сравнить: shap.summary_plot(shap_xgb_values_train, X_train, plot_type="dot") shap.summary_plot(shap_xgb_values_test, X_test, plot_type="dot") Приведенный выше фрагмент исходного кода произвел графики на рис. 5.6. Хорошо видно, что они очень похожи и более-менее согласуются вплоть до девятого
Глава 5. Модельно-агностические методы глобальной интерпретации 203 признака сверху донизу. Признаки ранжированы по важности сверху донизу. И линия разделяет влияние этих признаков, отделяя отрицательные от положительных. С обеих сторон есть точки для всех признаков, а число точек указывает на то, насколько признак влияет на модель отрицательно (слева) или положительно (справа). Рис. 5.6. Сводный график значений SHAP модели XGBoost для тренировочных и тестовых данных Можно сказать, что самые левые точки относятся к году (year), поэтому они больше всего коррелируют с более низкими комбинированными милями на галлон израсходованного топлива. Вместе с тем они также являются аномальными значениями, т. к. большинство точек для года сконцентрированы вокруг средней области. Точки имеют цветовую кодировку, поэтому можно отнести их к высоким, средним или низким значениям признаков. Например, можно сказать, что все аномальные значения — это среднезначные годы. Другими словами, в 37 годах, охватывающих годы с 1984 по 2021, средней областью будут 1996–2009 годы. Эти временны́е интервалы имеют смысл, потому что они соотносятся с годами, приходящимися на период самой дешевой нефти и бума экономики США и закончившимися финансовым кризисом и самыми высокими ценами на нефть за всю историю. В ту пору обычным явлением были большие "пожирающие" газ спортивные внедорожники. Затем можно вывести на экран сводный график (summary_plot) для нейросетевых значений SHAP и сравнить его с моделью XGBoost: shap.summary_plot(shap_nn_values_test[0], X_test, plot_type="dot") Приведенный выше фрагмент исходного кода выводит на экран рис. 5.7. Первый признак — выбросы CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) согласуется с соответствующим признаком XGBoost. Высокие значения коррелируют отрицательно, а низкие — положительно, производя почти идеальный градиент между двумя экстремумами. Большинство значений распределены посередине. Кроме того, не должно никого удивлять, что ни один из других признаков не соответствует тому же порядку следования. Даже год (year) не только менее важен, чем
204 Часть II. Освоение методов интерпретации для модели XGBoost в целом, но, похоже, и не оказывает очень отрицательного влияния ни на одно предсказание. Рис. 5.7. Сводный график значений SHAP нейросетевой модели только для тестовых данных Различия между рис. 5.6 и 5.7 можно отнести на счет усвоения моделями разной информации о предсказательных переменных для предсказания исхода. Это всё равно что попросить двух отправившихся на урок учеников объяснить ответ на вопрос, основываясь на том, что они узнали на том уроке. Допустив, что мы имеем дело с хорошими учениками, следует ожидать, что их объяснения будут иметь смысл с точки зрения связи вопроса с ответом. Они будут включать в свои рассуждения много одинаковых элементов, которые, правда, будут расставлены по приоритетам и взаимосвязаны по-разному. В конце концов, эти ученики мыслят поразному, потому что они — разные люди. В этом упражнении мы используем две модели именно потому, что учиться у двух учеников лучше, чем у одного! Изучение взаимодействий Работать со взаимодействиями признаков в моделях типа "черный ящик" — дело хлопотное, но если копнуть достаточно глубоко, то можно найти ответы на несколько вопросов о том, как и почему они взаимодействуют. Поскольку мы учимся на двух моделях машинного обучения, имеет смысл извлечь выгоду из обеих. И между нашими 81 признаками существует несколько тысяч возможных двупеременных взаимодействий. Тем не менее подавляющее большинство среднего влияния на результат работы модели на выходе кроется в первом признаке. Другие че-
Глава 5. Модельно-агностические методы глобальной интерпретации 205 тыре-пять верхних признаков отстают далеко позади. И если поменять summary_plot на plot_type="bar", то заметить это будет легче. Весьма вероятно, что среди этого верхнего яруса признаков мы найдем наиболее заметные взаимодействия. С этой целью давайте рассмотрим взаимодействие между пятью верхними признаками для XGBoost (co2TailpipeGpm, fuelType_Diesel, year, cylinders и ghgScore) и тем, который является вторым по важности для нейронной сети (co2), но седьмым по важности для XGBoost. Мы создаем список (top_features_l) с этими признаками и добавляем в него переменную отклика comb08. Затем мы берем подмножество тренировочного кадра данных по этим признакам и сохраняем его как top_df: top_features_l = ['comb08'] + \ ['co2TailpipeGpm', 'fuelType_Diesel', 'co2', 'year', 'ghgScore', 'cylinders'] top_df = fueleconomy_df.loc[X_train.index, top_features_l] Далее можно визуализировать коэффициенты корреляции Спирмена признаков, представленных в этом кадре данных (top_df), с помощью теплокарты. Этот метод измеряет монотонность между двумя признаками. Он выдает число в интервале между 1 и 1, указывающее и силу, и направление связи. Значения, наиболее близкие к обоим экстремумам, являются самыми сильными, отрицательными либо положительными, в то время как значения, близкие к нулю, — наименее сильными. Коэффициенты Спирмена могут отражать нелинейные связи при условии их монотонности. Хотя этот метод служит хорошей отправной точкой для приоретизации того, какие взаимодействия следует оценить дальше, нужно предупредить, что нелинейные связи, которые не являются монотонными, не будут считаться этим методом сильными. Параболическая кривая будет иметь нулевой коэффициент Спирмена, потому что она является симметрично немонотонной несмотря на то, что явно существует значительная связь: corrs = stats.spearmanr(top_df).correlation mask = np.zeros_like(corrs) mask[np.triu_indices_from(mask)] = True ax = sns.heatmap(corrs, vmin=-1, vmax=1, center=0, mask=mask, square=True,\ cmap=sns.diverging_palette(20, 220, n=200),\ xticklabels=top_df.columns, yticklabels=top_df.columns ) Приведенный выше исходный код порождает график, показанный на рис. 5.8. На нем хорошо видно, что число цилиндров (cylinders) и выбросы CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) имеют наибольшую монотонную корреляцию с откликом — комбинированные мили на галлон израсходованного топлива (comb08) и что они оба отрицательны. Остальные признаки имеют более слабую положительную монотонную корреляцию. Используя функцию spearmanr, можно также извлекать p-значения — значения параметра статистической значимости гипотезы, который проверяет корреляцию на валидность.
206 Часть II. Освоение методов интерпретации Рис. 5.8. График корреляций Спирмена для верхних признаков обеих моделей В целях статистической строгости можно использовать точечно-двурядовый2 коэффициент вместо дизельного типа топлива (fuelType_Diesel), потому что он является дихотомическим признаком. Точечно-двурядовый коэффициент корреляции подобен коэффициенту Спирмена, но берется между дихотомической и непрерывной переменной. Он не исходит из монотонности, но принимает другие допущения, соблюдение которых можно протестировать. Мы не будем здесь вдаваться в подробности, но обычно этот индикатор является более прочным для такого рода связи: print('spearman2TailpipeGpm→comb08: %.3f-val: %.4f' %\ (stats.spearmanr(X_train.co2TailpipeGpm.values, top_df.comb08.values))) print('point-biserial_Diesel→comb08: %.3f-val: %.4f' %\ (stats.pointbiserialr(top_df.fuelType_Diesel.values, top_df.comb08.values))) print('spearman→comb08: %.3f-val: %.4f' %\ (stats.spearmanr(X_train.co2.values, top_df.comb08.values))) 2 Точечно-двурядовый (point-serial) коэффициент — технический термин, применяемый в тесте на точечнодвурядовую корреляцию, при котором баллы по непрерывной шкале сравниваются с одним-единственным элементом, который имеет только два возможных значения: правильное и неправильное. — Прим. перев.
Глава 5. Модельно-агностические методы глобальной интерпретации 207 print('spearman→comb08: %.3f-val: %.4f' %\ (stats.spearmanr(X_train.year.values, top_df.comb08.values))) print('spearman→comb08: %.3f-val: %.4f' %\ (stats.spearmanr(top_df.ghgScore.values, top_df.comb08.values))) print('spearman→comb08: %.3f-val: %.4f' %\ (stats.spearmanr(X_train.cylinders.values, top_df.comb08.values))) Приведенный выше исходный код печатает приведенные ниже данные, при этом p-значения менее 0,05 подтверждают корреляционную гипотезу для всех: spearman co2TailpipeGpm→comb08 corr: -0.994 p-val: 0.0000 point-biserial fuelType_Diesel→comb08 corr: 0.062 p-val: 0.0000 spearman co2→comb08 corr: 0.223 p-val: 0.0000 spearman year→comb08 corr: 0.255 p-val: 0.0000 spearman ghgScore→comb08 corr: 0.374 p-val: 0.0000 spearman cylinders→comb08 corr: -0.785 p-val: 0.0000 Теплокарта коэффициентов корреляции Спирмена помогла нам указать несколько интересных направлений.  Согласно значениям SHAP модели XGBoost, число цилиндров (cylinders) зани- мает лишь четвертое место по важности, но, по-видимому, этот параметр очень монотонно коррелирует с целевой переменной и выбросами CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) и в меньшей степени — с выбросами парниковых газов (ghgScore).  Согласно значениям SHAP нейросетевой модели, параметр выбросов CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года (co2) входит только в пятерку лучших, но у него более высокий уровень корреляции Спирмена, чем у дизельного типа топлива (fuelType_Diesel). Почему это так?  Нейронная сеть также, похоже, оценивала выбросы парниковых газов (ghgScore) усерднее, и в корреляционной теплокарте отражены высокие значения выбросов CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года (co2) и лет (year). Похоже, между этими тремя признаками что-то происходит. Есть отличный способ оценить взаимодействия этих признаков, а именно использовать графики зависимости SHAP, при этом измеряя корреляции с помощью библиотеки scipy. Мы также построим несколько диаграмм рассеяния, чтобы сравнить наши результаты с опорными данными. Графики зависимости SHAP График зависимости SHAP строится между значением SHAP для признака на оси y и значениями признака на оси х. По сути, он демонстрирует влияние изменений значений по оси x на значения по оси y. Одна функция (dependence_plot) будет строить график зависимости. Для этого ей просто требуется имя (co2TailpipeGpm) или индекс признака, за которым следуют значения SHAP (shap_xgb_values_test) и соответствующие им данные (X_test). При
208 Часть II. Освоение методов интерпретации необходимости можно указать член взаимодействия (interaction_index). Мы не показываем график сразу (show=False), потому что хотим сделать его больше (fig.set_size_inches(12,8)), а затем использовать plt.show(), чтобы вывести целиком. Мы также делаем точки полупрозрачными (alpha=0.3), чтобы облегчить выявление областей с меньшим числом точек. После этого можно вывести точки Спирмена для взаимодействия, как это делалось раньше, а затем построить еще один график зависимости, но на этот раз для числа цилиндров (cylinders) с выбросами парниковых газов (ghgScore). Обратите внимание, что число цилиндров (cylinders) является порядковым, поэтому x_jitter=0.4 помогает лучше оценивать распределение, так как, например, без джиттера все шестицилиндровые транспортные средства с нулевым значением SHAP будут появляться как одна точка: shap.dependence_plot("co2TailpipeGpm", shap_xgb_values_test,\ X_test,interaction_index="cylinders", alpha=0.3) print('spearman→co2TailpipeGpm: %.3f-val: %.4f' %\ (stats.spearmanr(X_train.cylinders.values, X_train.co2TailpipeGpm.values))) shap.dependence_plot("cylinders", shap_xgb_values_train, X_train,\ interaction_index="ghgScore", alpha=0.3, x_jitter=0.4) print('spearman→cylinders: %.3f-val: %.4f' %\ (stats.spearmanr(top_df.ghgScore.values, top_df.cylinders.values))) Рис. 5.9. Графики зависимости SHAP для модели XGBoost и статистические величины, изображающие взаимодействия выбросов CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) с числом цилиндров (cylinders)
Глава 5. Модельно-агностические методы глобальной интерпретации 209 Приведенный выше исходный код генерирует результат на рис. 5.9 и 5.10. Цветовое кодирование справа относится к значениям члена взаимодействия. Первый график говорит о том, что увеличение в числе цилиндров (cylinders) коррелирует с увеличением выброса CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm), а, в свою очередь, более высокие выбросы CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) коррелируют с более низкими значениями SHAP. Коэффициент Спирмена подтверждает монотонность этого взаимодействия. Второй график интерпретировать сложнее, но он показывает, что более высокий выброс парниковых газов (ghgScore) коррелирует с меньшим числом цилиндров (cylinders) и слегка более высокими значениями SHAP. График на рис. 5.9 намекает на то, что число цилиндров (cylinders) настолько идеально согласуется с выбросами CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm), что указанный признак модели не нужен, несмотря на его сильные корреляции. Другими словами, он по большей части является избыточным, за исключением того, что, вероятно, используется в качестве члена взаимодействия признаком выбросов парниковых газов (ghgScore). Рис. 5.10. Графики зависимости SHAP для модели XGBoost и статистические величины, изображающие взаимодействия числа цилиндров (cylinders) с выбросами парниковых газов (ghgScore) Обратите внимание, что на рис. 5.10 есть аномальные значения в случаях, когда значения SHAP очень низкие либо очень высокие, когда уровень выбросов парниковых газов (ghgScore) высок. Эти выбросы располагаются, когда число цилиндров
210 Часть II. Освоение методов интерпретации (cylinders) имеет конкретные значения. Например, модель могла бы усвоить, что когда число цилиндров (cylinders) равно нулю, а уровень выбросов парниковых газов (ghgScore) превышает пять, то влияние на исход должно быть выше. Графики SHAP являются наглядным представлением того, что, собственно говоря, модель усвоила из наших данных. Однако, если у нас есть какие-либо сомнения, мы всегда можем перейти непосредственно к источнику — к данным. В главе 4 мы генерировали одновременно графики индивидуальных условных ожиданий (ICE) и диаграммы рассеяния. В этом примере мы могли бы сказать, что модель "соединила точки" на графике рассеяния, т. е. составила полную картину, наглядно представив то, что она усвоила из графиков ICE. Теперь можно сделать то же самое, построив диаграмму рассеяния числа цилиндров (cylinders) относительно выбросов CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) и целевого признака — комбинированные мили на галлон израсходованного топлива (comb08). Эти графики могли бы показать нам нечто такое, о чем модель нам не рассказывает, или подтвердить свою историю. Следующий ниже исходный код создает два подграфика с диаграммами рассеяния regplot. Объект regplot библиотеки seaborn предназначен для графопостроения данных с помощью линейной регрессионной линии. Несмотря на то, что мы не ожидаем линейности, часто бывает полезно нарисовать линию, чтобы показать направление или тренд: fig, axs = plt.subplots(1, 2, figsize = (13,6)) sns.regplot(x=X_train.cylinders, y=X_train.co2TailpipeGpm,\ ax=axs[0], scatter_kws={'alpha':0.3}, line_kws={'color':'g'}) axs[0].set_ylabel('Выбросы CO2, г/миль', fontsize=13) axs[0].set_xlabel('Cylinders', fontsize=13) sns.regplot(x=X_train.cylinders, y=y_train, ax=axs[1], marker="+",\ scatter_kws={'alpha':0.3}, line_kws={'color':'g'}) axs[1].set_ylabel('Комбинированный MPG (comb08)', fontsize=13) axs[1].set_xlabel('Cylinders', fontsize=13) Приведенный выше исходный код генерирует рис. 5.11. Он подтверждает, что число цилиндров (cylinders) и выбросы CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) коррелированы положительно, тогда как число цилиндров (cylinders) и комбинированные мили на галлон израсходованного топлива (comb08) коррелированы отрицательно. Теперь давайте построим график зависимости dependence_plot для выбросов CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm), взаимодействующих с выбросами CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года (co2), как мы это делали для числа цилиндров (cylinders): shap.dependence_plot("co2TailpipeGpm", shap_nn_values_test[0], X_test,\ alpha=0.3, interaction_index="co2") print('spearman→co2TailpipeGpm: %.3f-val: %.4f' %\ (stats.spearmanr(X_train.co2.values, X_train.co2TailpipeGpm.values)))
Глава 5. Модельно-агностические методы глобальной интерпретации 211 Рис. 5.11. Диаграммы рассеяния, показывающие взаимосвязь между признаками cylinders и co2TailpipeGpm и признаками cylinders и comb08, т. е. целью Рис. 5.12. Графики зависимости SHAP для нейросетевой модели и статистические величины, отражающие взаимодействия признака co2TailpipeGpm с признаком co2 Результаты работы приведенного выше исходного кода на рис. 5.12 демонстрируют, как средние и высокие значения признака co2 имеют положительную монотонную связь с признаком co2TailpipeGpm, но, как ни странно, низкие значения co2
212 Часть II. Освоение методов интерпретации не имеют никакой связи. Они повсюду! И из-за этого коэффициент Спирмена указывает на отрицательную монотонную корреляцию. Опять же, построение графика данных с помощью regplot может пролить свет на то, что происходит со взаимодействиями. Мы будем строить график выбросов CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года (co2) относительно выбросов CO2 из выхлопной трубы в граммах на милю вплоть до 2013 года (co2TailpipeGpm) и комбинированных миль на галлон израсходованного топлива (comb08): fig, axs = plt.subplots(1, 2, figsize = (13,6)) sns.regplot(x=X_train.co2, y=X_train.co2TailpipeGpm, ax=axs[0],\ scatter_kws={'alpha':0.3}, line_kws={'color':'g'}) axs[0].set_ylabel('Выхлопы CO2, г/миль (co2TailpipeGpm)', fontsize=13) axs[0].set_xlabel('Выхлопы CO2, г/миль (co2)', fontsize=13) sns.regplot(x=X_train.co2, y=y_train, ax=axs[1],\ marker="+", scatter_kws={'alpha':0.3}, line_kws={'color':'g'}) axs[1].set_ylabel('Комбинированный MPG (comb08)', fontsize=13) axs[1].set_xlabel('Выхлопы CO2, г/миль (co2)', fontsize=13) Приведенный выше фрагмент исходного кода выводит рис. 5.13. На нем показано, что признаки co2 и co2TailpipeGpm в основном равны, за исключением случаев, когда co2=-1. Если вы хотите это подтвердить, выполните ячейку с X_train[X_train.co2TailpipeGpm!=X_train.co2].co2, которая выведет числовой ряд pandas, состоящий из признака co2, когда указанные признаки не равны. Копнув глубже, вы поймете, что все отрицательные единицы соответствуют годам вплоть до 2013 года. Эта находка не должна удивлять, учитывая то, что записано в словарь данных! И действительно, признак co2 равен признаку co2TailpipeGpm, но с пропущенной информацией. Это не существенно, но нейронная сеть, похоже, сочла это важным, потому что это сильно коррелирует с целью, т. е. при наличии этих данных. Другими словами, вообразите, что произойдет с регрессионной линией на обоих графиках на рис. 5.13, если убрать 1 . Однако если признак co2 равен признаку co2TailpipeGpm, но с пропущенными значениями, разве нейронная сеть не должна была счесть это неважным? Возможно, признаки ghgScore и year содержат какие-то ответы на этот вопрос, поэтому давайте построим пару экземпляров графиков зависимости (dependence_plot) между этими признаками, как это делалось раньше: shap.dependence_plot("co2", shap_nn_values_test[0], X_test,\ alpha=0.3, interaction_index="ghgScore", x_jitter=10) print('spearman→co2: %.3f-val: %.4f' %\ (stats.spearmanr(top_df.ghgScore.values, top_df.co2.values))) shap.dependence_plot("ghgScore", shap_nn_values_test[0], X_test,\ alpha=0.3, interaction_index="year", x_jitter=0.4) print('spearman→year: %.3f-val: %.4f' %\ (stats.spearmanr(top_df.ghgScore.values, top_df.year.values)))
Глава 5. Модельно-агностические методы глобальной интерпретации 213 Рис. 5.13. Диаграммы рассеяния, показывающие взаимосвязь между признаками co2 и co2TailpipeGpm и признаками co2 и comb08, т. е. целью Рис. 5.14. Графики зависимости SHAP для нейросетевой модели и статистические величины, отражающие взаимодействие признака co2 с признаком ghgScore Приведенный выше фрагмент исходного кода выводит рис. 5.14 и 5.15. На первом графике видно, что по мере увеличения признака co2 признак ghgScore имеет тен-
214 Часть II. Освоение методов интерпретации денцию уменьшаться, а значение SHAP увеличивается. Как и в случае с признаком cylinders, на рис. 5.14 присутствуют аномальные значения, поэтому признак co2 становится существенным как признак взаимодействия при co2=-1. С другой стороны, на рис. 5.15 показано, что все значения от низкого до среднего для признака year имеют значение признака ghgScore, равное 1 . Этот признак, повидимому, переполнен значениями "отсутствует", подобно признаку co2. Кроме того, по мере его увеличения значение SHAP слегка уменьшается, но также резко уменьшается и его дисперсия. Рис. 5.15. Графики зависимости SHAP для нейросетевой модели и статистических величин, отражающие взаимодействие признака ghgScore с признаком year Можно построить график признака ghgScore относительно самого важного признака (co2TailpipeGpm) и цели, чтобы лучше понять, что происходит: fig, axs = plt.subplots(1, 2, figsize = (13,6)) sns.regplot(x=X_train.ghgScore, y=X_train.co2TailpipeGpm,\ ax=axs[0], scatter_kws={'alpha':0.3}, line_kws={'color':'g'}) axs[0].set_ylabel('Выхлопы CO2, г/мили (co2TailpipeGpm)', fontsize=13) axs[0].set_xlabel('EPA GHG Score (ghgScore)', fontsize=13) sns.regplot(x=X_train.ghgScore, y=y_train,\ ax=axs[1], marker="+", scatter_kws={'alpha':0.3}, line_kws={'color':'g'}) axs[1].set_ylabel('Комбинированный MPG (comb08)', fontsize=13) axs[1].set_xlabel('EPA GHG Score (ghgScore)', fontsize=13)
Глава 5. Модельно-агностические методы глобальной интерпретации 215 Приведенный выше исходный код выводит пару графиков на рис. 5.16, на котором показано, как признак ghgScore идеально выравнивается с признаком co2TailpipeGpm, за исключением случаев, когда он равен 1 . Показатель ghgScore, вероятно, является формулой, полученной из выбросов выхлопных газов, за исключением случаев до 2013 года, когда он был недоступен, и это означает, что он имеет некоторую видимую корреляцию с целевой переменной. Однако единственная целевая задача признака ghgScore в модели — служить признаком взаимодействия. Рис. 5.16. Диаграммы рассеяния, показывающие взаимосвязь между признаками ghgScore и co2TailpipeGpm и признаками ghgScore и comb08, т. е. целью Далее мы изучим еще один график, который бывает полезным для оценки эффектов взаимодействия между признаками. Силовые графики SHAP В главе 6 мы рассмотрим эту тему подробнее, но пока достаточно знать, что силовые графики обычно используются для объяснения одного-единственного предсказания. Силовые графики изображают континуум, в котором синие признаки представляют силы, толкающие предсказания в отрицательном направлении, а красные — силы, толкающие предсказания в положительном направлении. В данном случае положительные силы представляют собой более высокие комбинированные мили на галлон израсходованного топлива (comb08), а отрицательные силы — более низкие. Если мы сложим локальные интерпретации, вертикально отображенные рядом друг с другом, то сможем использовать эту концепцию для глобальной интерпретации. Силовой график SHAP это как раз и делает, когда вы передаете в функцию force_plot более одного значения SHAP и наблюдения. Этот график визуализируется медленнее, потому что это не просто один график, а динамическая приборная панель. Его можно сгенерировать быстрее, если сделать одну вещь, а именно, использовать
216 Часть II. Освоение методов интерпретации выборку из тестового набора данных. Мы отберем только 5% (sample_test_size) индексов (sample_test_idx) в тестовом наборе данных. И поскольку эта приборная панель является динамической, перед выполнением force_plot вам нужно будет инициализировать JavaScript с помощью функции shap.initjs(). Рис. 5.17. Силовые графики SHAP для всех признаков, сгруппированных по сходству объяснений, за которыми следуют результаты, отфильтрованные и упорядоченные по годам, относительно эффектов признака co2TailpipeGpm Силовой график требует ожидаемого значения (expected_value), которое в данном случае является средним значением целевой переменной, за которым следуют значения SHAP и тестовые данные: sample_test_size = 0.05 sample_test_idx = np.random.choice(\
Глава 5. Модельно-агностические методы глобальной интерпретации 217 X_test.shape[0], math.ceil(X_test.shape[0]*sample_test_size), replace=False) shap.initjs() shap.force_plot(shap_xgb_explainer.expected_value,\ shap_xgb_values_test[sample_test_idx], X_test.iloc[sample_test_idx]) Приведенный выше фрагмент исходного кода генерирует приборную панель. Начальный экран кластеризует все наблюдения, сгруппированные по сходству и центрированные по ожидаемому значению (около 21 мили на галлон). Синие силы толкают мили на галлон вниз и подталкивают красные силы вверх. Силы, конечно, являются конкретными значениями признаков. И если навести указатель мыши на график, появятся всплывающие подсказки. На рис. 5.17 это видно на первом снимке экрана. Однако этот начальный экран часто слишком заполнен, чтобы быть полезным, поэтому можно отфильтровать и отсортировать средние эффекты по признакам (в верхнем раскрывающемся списке) и посмотреть, как они взаимодействуют с другими признаками (в левом раскрывающемся списке). На втором снимке экрана на рис. 5.17 показано, как после 2013 года среднее влияние на co2TailpipeGpm тяготеет к более высоким значениям. Также имелось два года, в течение которых наблюдались увеличения: в 2001 и 2012 годах. Какими бы прекрасными ни были графики зависимости SHAP и визуализации взаимодействий, их бывает сложнее интерпретировать, чем другие менее подробные графики. А иногда хочется лишь уловить суть чего-то путем демонстрации общего направления того, как один-два признака взаимодействуют вместе с целевой переменной. Именно тут можно задействовать графики ALE. Графики накопленных локальных эффектов Графики накопленных локальных эффектов (accumulated local effects, ALE) похожи на графики частичной зависимости (PDP), которые мы изучали в главе 4, за исключением того, что они не имеют систематического смещения и визуализируются намного быстрее. Несмещенность предполагает, что для графиков не принимается допущение, которое редко соблюдается: признаки не коррелированы. Как мы уже заметили, выбросы CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года (co2) и выбросы парниковых газов (ghgScore) были выведены из выбросов CO2 из выхлопной трубы в граммах на милю для моделей вплоть до 2013 года (co2TailPipeGpm). Следовательно, они являются в основном избыточными, за исключением случаев, когда они равны 1 ("отсутствут"). Тогда, как же опираться на метод интерпретации, который спутывает свои эффекты? Благодаря своим свойствам, атрибуции SHAP отличаются сильной согласованностью, потому что симуляции проводятся на основе разумных ожиданий — даже учитывая, по большей части, коллинеарные признаки. Графики частичной зависимости (PDP) отражают средние значения предсказаний по всем значениям признаков (и интерполяциям) независимо от того, имеют ли они смысл или нет, при этом исходя из допущения о независимости признаков. С другой стороны, в графиках ALE используется разумный подход, факторизующий распределения данных при расчете эффектов признаков. Это выполняется
218 Часть II. Освоение методов интерпретации путем разбиения объекта на интервалы одинакового размера (как правило, квантили). Затем происходит вычисление величины изменения предсказаний в среднем в каждом таком интервале, следовательно, локальном. Далее эти эффекты суммируются по всем интервалам — другими словами, накапливаются. Эффекты трактуются относительно среднего значения, поэтому затем они центрируются на нуле. Их простота заслоняет их гениальность. Средние значения интервальных разностей являются производными, а накопление — интегралом, скрытым от всех. Мы не будем здесь вдаваться в математические подробности, но в результате это приводит к изолированию эффекта одного признака от других! На момент написания книги пакет, который мы будем использовать для порождения графиков ALE (https://github.com/blent-ai/ALEPython), требует данных в формате библиотеки pandas. Это требование делает его несовместимым с нейросетевой моделью. Мы в любом случае будем работать с моделью XGBoost. Но если вы хотите использовать нейросетевую модель, можно решить эту проблему, создав оберточный класс, который абстрагирует модель и конвертирует кадр данных pandas в формат NumPy для указанной модели. Многие библиотеки модельноагностической интерпретации изо всех сил стараются сделать их совместимыми с каждым модельным классом, поэтому вам приходится прибегать к хитростям, чтобы сделать их "подходящими". При этом не хватает стандартизации (о которой мы поговорим в главе 14), нехватка которой затрудняет более простую имплементацию и широкое принятие их на вооружение. Теперь мы будем использовать цикл for для верхних непрерывных признаков и построим графики ALE для каждого из них. Функция ale_plot очень проста. Первым аргументом является модель (fitted_xgb_model). Затем следует кадр данных (X_test) библиотеки pandas, за которым указывают массив признаков для вывода на графике. Можно опционально задать корзины (bins), т. е. число квартилей, используемых в качестве интервалов. Кроме них, существует еще один вариант, который рекомендуется, но замедляет процесс, а именно использовать выборки Монте-Карло (monte_carlo). Если установить соответствующий аргумент равным истине, то указанная функция создаст много симуляций-реплик (monte_carlo_rep), где она берет долю случайно черпаемых выборок из данных и вычисляет по ним накопленные локальные эффекты ALE (monte_carlo_ratio). В итоге вы получаете тонкие синие линии, представляющие каждую реплику. Идея в основе такого подхода заключается в том, чтобы увидеть, насколько ваш график ALE может варьироваться в валидационном наборе данных, берущемся из распределения аналогичного тому, из которого взят тестовый набор данных: for feature_name in ['co2TailpipeGpm', 'co2', 'ghgScore', 'year', 'cylinders']: ale_plot(fitted_xgb_model, X_test, [feature_name], bins=10,\ monte_carlo=True, monte_carlo_rep=50, monte_carlo_ratio=0.4) plt.show() Приведенный выше фрагмент исходного кода генерирует пять графиков ALE. На рис. 5.18 представлен один из них. Видно, как признак co2 влияет на модель XGBoost в соответствии с тестовым набором данных. Полностью изолированный,
Глава 5. Модельно-агностические методы глобальной интерпретации 219 он оказывает незначительное воздействие в интервале от 0,05 до 0,30 миль на галлон израсходованного топлива (MPG). Обратите внимание, что значение 1 на графике даже не представлено, потому что ALE считает, что оно само о себе не несет никакой информации. Рис. 5.18. График ALE для признака ghgScore в соответствии с моделью XGBoost Далее мы сгенерируем графики ALE для двух признаков сразу. Всё вычисляется аналогично за исключением того, что квантили оперируют в двух размерностях, и они накапливают эффекты в этих двух размерностях, в результате чего получается контурный график с цветовой кодировкой. Мы будем повторять это в цикле, но на этот раз относительно пар признаков. Функция ale_plot может принимать два признака в третьем аргументе, но симуляции Монте-Карло недоступны для двух сразу: for interaction in [['co2TailpipeGpm', 'co2'],\ ['co2TailpipeGpm', 'ghgScore'],\ ['cylinders', 'co2TailpipeGpm'],\ ['year', 'co2TailpipeGpm']]: ale_plot(fitted_xgb_model, X_test, interaction, bins=[10,10]) plt.show()
220 Часть II. Освоение методов интерпретации Приведенный выше исходный код выводит четыре графика взаимодействия ALE. Первые три демонстрируют незначительные эффекты взаимодействия (менее 1 MPG). Последний изображен на рис. 5.19. Он демонстрирует значительный эффект взаимодействия между годом (year) и выбросами CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm), в особенности отрицательного между 1985 и 2004 годами, в сочетании с выбросами CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm) ниже 300. Но подождите, разве более низкий уровень выхлопов CO2 не указывает на более высокие значения MPG? Рис. 5.19. График взаимодействия ALE между признаками year и co2TailpipeGpm Оказывается, модель усвоила, что она не должна доверять признаку co2tailpippm за эти годы, и тяготеет к тому, чтобы штрафовать самые низкие значения. Вполне вероятно, что существуют проблемы либо с качеством данных, либо с тем, как расчет признака co2TailpipeGpm менялся с годами, что и делало его неровным. Что делать, если вы хотите выжать немного сведений о своей модели машинного обучения, которую слишком трудно интерпретировать другими средствами, такие как, например, некоторые правила, объясняющие опорную логику принятия решений? Или же коэффициенты, которые улавливают для модели магнитуду и направ-
Глава 5. Модельно-агностические методы глобальной интерпретации 221 ление признака? Изначально интерпретируемые модели содержат эти элементы в качестве встроенных частей, но мы предпочитаем модели типа "черный ящик", потому что они показывают более высокую результативность. Существует компромисс, и он состоит в использовании глобально-суррогатных моделей, о которых мы узнаем в следующем далее разделе! Глобальные суррогаты Термин "суррогатная модель" весьма многосторонний. Он используется в машиностроении, статистике, экономике и физике, и это лишь несколько областей, часто в контексте метамоделей, математических оптимизаций или симуляций. В контексте методов интерпретации машинного обучения глобально-суррогатная модель обычно относится к модели белого ящика, которую вы тренируете с помощью предсказаний модели типа "черный ящик". Мы делаем это, чтобы извлекать знания из внутренних параметров модели белого ящика, как и в главе 3. Суррогатные модели можно использовать еще одним способом: задействуя модель черного ящика для аппроксимирования и оценивания другой модели, к которой у вас нет доступа, но есть ее предсказания. Как раз этим мы и займемся в главе 7, но для такого рода суррогатов мы предпочитаем термин "косвенная модель" (или проксимодель). Для создания глобального суррогата не нужны никакие причудливые библиотеки. Можно использовать любую модель типа "белый ящик", которую мы обсуждали в главе 3. В конечном счете было разработано несколько моделей для использования специально в качестве суррогатов, таких как TREPAN. Библиотека Skater имеет регрессионную имплементацию (https://oracle.github.io/Skater/reference/ interpretation.html#tree-surrogates-using-decisiontrees), которую можно использовать, и одну для классификатора на основе списка байесовых правил (Bayesian rule list classifier, BRLC), которая очень похожа на модель RuleFit из главы 3, за исключением того, что она работает только с классификационными задачами. В этом примере мы хотим извлечь из наших нейросетевых моделей несколько правил и иерархию. Поэтому имеет смысл использовать деревья решений и модель RuleFit. Деревья решений помогают понимать иерархию, а модель RuleFit — понимать правила. Подгонка суррогатов Первый шаг состоит в подгонке суррогатов; единственное отличие состоит в том, что тренировочные данные представлены предсказаниями нейросетевой модели как y. После подгонки регрессора DecisionTreeRegressor мы выполняем метод predict(), чтобы получить предсказания для тренировочного и тестового наборов: fitted_dt_surrogate = tree.DecisionTreeRegressor(max_depth=7, random_state=rand).\ fit(X_train, y_train_nn_pred) y_train_dt_pred = fitted_dt_surrogate.predict(X_train) y_test_dt_pred = fitted_dt_surrogate.predict(X_test)
222 Часть II. Освоение методов интерпретации То же самое можно сделать и для модели RuleFit. Обратите внимание, что функция fit модели RuleFit требует данных в вещественном формате NumPy: fitted_rf_surrogate = RuleFit(max_rules=150, rfmode='regress', random_state=rand,\ tree_size=8).fit(X_train.astype(float).values,\ np.array(y_train_nn_pred).squeeze(), X_train.columns) y_train_rf_pred = fitted_rf_surrogate.predict(X_train.astype(float).values) y_test_rf_pred = fitted_rf_surrogate.predict(X_test.astype(float).values) Мы получаем предсказания суррогата, чтобы измерить качество подгонки каждой суррогатной модели под нейросетевую модель и степень ее переобучения. Оценивание суррогатов Если предсказания суррогатной модели слишком далеки от предсказаний нейросетевой модели, то любые интерпретации будут бесполезны. Кроме того, если она переобучена, это означает, что нейросетевая модель хорошо аппроксимирует только тренировочные данные, но не тестовые, и когда это происходит, использовать суррогат не следует. Сначала давайте оценим дерево решений, вычислив значение метрик RMSE и R-квадрат: # Измерить качество, с которым дерево решений # воспроизводит предсказания нейронной сети RMSE_dt_nn_train = metrics.mean_squared_error(\ y_train_nn_pred, y_train_dt_pred, squared=False) RMSE_dt_nn_test = metrics.mean_squared_error(\ y_test_nn_pred, y_test_dt_pred, squared=False) R2_dt_nn_test = metrics.r2_score(y_test_nn_pred, y_test_dt_pred) # Распечатать все метрики print('RMSE_train: %.4f_test: %.4f: %.4f' %\ (RMSE_dt_nn_train, RMSE_dt_nn_test, R2_dt_nn_test)) Приведенный выше исходный код печатает следующее: RMSE_train: 0.5036 RMSE_test: 0.5518 r2: 0.9952 Величина метрики R-квадрат высока, а разница в значениях метрики RMSE вообще не указывает на переобучение. Теперь давайте выполним модель RuleFit: # Измерить качество, с которым RuleFit # воспроизводит предсказания нейронной сети RMSE_rf_nn_train = metrics.mean_squared_error(\ y_train_nn_pred, y_train_rf_pred, squared=False) RMSE_rf_nn_test = metrics.mean_squared_error(\ y_test_nn_pred, y_test_rf_pred, squared=False) R2_rf_nn_test = metrics.r2_score(y_test_nn_pred, y_test_rf_pred)
Глава 5. Модельно-агностические методы глобальной интерпретации 223 # Распечатать все метрики print('RMSE_train: %.4f_test: %.4f: %.4f' %\ (RMSE_rf_nn_train, RMSE_rf_nn_test, R2_rf_nn_test)) Приведенный выше исходный код печатает следующее: RMSE_train: 0.8211 RMSE_test: 0.6416 r2: 0.9935 Модель RuleFit проходит проверку. Ее метрики хуже, чем у дерева решений, но она всё еще очень хороша. Далее давайте применим глобальные суррогаты для интерпретации. Интерпретирование суррогатов Можно изобразить дерево решений на графике, чтобы визуализировать иерархию, как мы научились делать в главе 3: fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(16,8), dpi=600) tree.plot_tree(fitted_dt_surrogate, filled=True, max_depth=2,\ feature_names=X_train.columns.values.tolist()) fig.show() Приведенный выше фрагмент исходного кода сгенерировал двевовидную структуру на рис. 5.20. Рис. 5.20. Суррогатная структура дерева решений глубиной 2 Нейросетевая модель не имеет аналогичной структуры. Тем не менее если мы сможем воспроизвести предсказания с ее помощью с высокой степенью, то это означает, что, хотя электрическая энергия в качестве источника питания (fuelType_Electricity) не является одним из наиболее важных признаков, в совокупности он имеет решающее значение в качестве отправной точки. И действительно, дерево решений бывает полезно для понимания того, как наилучшим образом подойти к проблеме. Например, возможно, имеет смысл сделать две модели: одну для электромобилей и одну для автомобилей на ископаемом топливе. Копая глубже, можно использовать
224 Часть II. Освоение методов интерпретации как мы описывали в главе 3, либо попробовать построить дерево с помощью API библиотеки scikit-learn. tree.export_tree, Для модели RuleFit можно использовать функцию get_rules(), чтобы извлечь правила, отфильтровать правила с нулевым коэффициентом, а затем отсортировать их по важности (importance): rulefit_df = fitted_rf_surrogate.get_rules() rulefit_df = rulefit_df[rulefit_df.coef != 0] rulefit_df.sort_values(by="importance", ascending=False) Приведенный выше фрагмент исходного кода выводит кадр данных на рис. 5.21. Он показывает, что линейный признак co2TailPipeGpm является наиболее важным, тогда как правило fuelType_Electricity > 0.5 является вторым, за которым следует более длинное правило, также включающее fuelType_Electricity > 0.5! Тогда почему мы не подхватили важность признака fuelType_Electricity раньше? Рис. 5.21. Десять верхних правил суррогатной модели RuleFit Графики SHAP и ALE показали нам несколько интересных сторон признаков и продемонстрировали, как они связаны с целевой переменной и друг с другом. Однако, учитывая сложность моделей XGBoost и нейронных сетей, они скрывают более простые истины, которые можно извлечь наилучшим образом в других терминах, что могут продемонстрировать только модели типа "белый ящик" с помощью предсказаний модели типа "черный ящик". П РИМЕЧАНИЕ Находки суррогатной модели могут быть убедительными только в отношении изначальной модели, а не в отношении данных, используемых для обучения модели. О данных из самих моделей и их суррогатов можно узнать гораздо больше. Например, можно оценить каждое взаимодействие в верхних правилах списка правил с помощью графиков ALE или рассмотреть дерево решений, обращаясь к соответст-
Глава 5. Модельно-агностические методы глобальной интерпретации 225 вующим графикам зависимостей по каждому признаку, в особенности признаку fuelType_Electricity, что кажется многообещающим. Миссия выполнена Миссия состояла в том, чтобы понять, как потенциальные предсказатели в наборе данных влияли на топливную эффективность на протяжении многих лет. Мы определили, что наиболее значимые предсказатели топливной эффективности безоговорочно связаны с загрязнением окружающей среды, при этом особо выделяются выбросы CO2 из выхлопной трубы в граммах на милю (co2TailpipeGpm). Загрязнение окружающей среды и топливная неэффективность уменьшаются с каждым годом. С другой стороны, они растут с увеличением числа цилиндров и в ситуациях, когда используется дизельный тип топлива (fuelType_Diesel). Ничто из этого не должно удивлять тех, кто знает об эволюции автомобилей на протяжении нескольких последних десятилетий. Тем не менее было несколько идей, открывающих завесу неизведанного. Например, графики зависимости SHAP (см. рис. 5.12 и 5.14) помогли нам понять причину, почему признаки выбросов CO2 из выхлопной трубы в граммах на милю для моделей после 2013 года (co2) и выбросов парниковых газов (ghgScore) являются избыточными. И, как показано на графике взаимодействия (см. рис. 5.19), могут возникать некоторые проблемы с качеством данных с признаком выбросов CO2 из выхлопной трубы в граммах на милю вплоть до 2013 года (co2TailpipeGpm), которые подлежат дальнейшему расследованию. Глобальные суррогаты развили чувство иерархии, невидимое в других методах интерпретации. Несмотря на то что в совокупности признаки электродвигателя (fuelType_Electricity, atvType_EV) и признаки трансмиссии (trany_Manual, trans_spd) не выглядят важными, они располагаются высоко в дереве решений (см. рис. 5.20) и в правилах модели RuleFit (см. рис. 5.21). Иерархия показывает, что они являются хорошими начальными точками разбивки для предсказания MPG. У нас есть несколько отличных находок и захватывающих графиков. Можно назвать эту миссию выполненной. Тем не менее при интерпретации вместе с хорошими ответами вы нередко получаете и хорошие вопросы! Можно продолжить путь вниз по кроличьей норе и посмотреть, куда она приведет. Например, после удаления избыточных признаков следующим шагом может стать обучение отдельной модели для ископаемого топлива и электромобилей, чтобы можно было узнать, какие факторы влияют отдельно на мили на галлон израсходованного топлива (MPG) и мили в эквиваленте бензина на галлон израсходованного топлива (MPGe). Можно сгенерировать графики SHAP для обоих, сопоставляя их верхние признаки, и, возможно, это поможет создать для вашей организации более убедительную историю! Резюме Прочитав эту главу, вы должны были разобраться в значениях Шепли и в том, как они связаны с библиотекой SHAP. Вы также узнали о графиках накопленных ло-
226 Часть II. Освоение методов интерпретации кальных эффектов (ALE) и о том, почему и как они являются более качественной альтернативой графикам частичной зависимости (PDP). Наконец, вы должны были понять, как использовать глобальные суррогаты, чтобы узнавать о своих моделях больше. В следующей далее главе мы изучим методы локальной интерпретации с использованием локально-суррогатной модели (Local Surrogate Model, LIME) и SHAP. Справочные материалы  Shapley L. S. A value for n-person Games / H. W. Kuhn, A. W. Tucker (eds.). Contri- butions to the Theory of Games. Annals of Mathematical Studies. 28. — Princeton University Press, 1953. — P. 307–317. — URL: https://doi.org/ 10.1515/9781400881970-018. (Шепли Л. С. Ценность для игр с n участниками.)  Lundberg S., Lee S.-I. A Unified Approach to Interpreting Model Predictions // Ad- vances in Neural Information Processing Systems. — 2017. — № 30. — URL: https://arxiv.org/abs/1705.07874 (документация по SHAP: https://github.com/ slundberg/shap). (Лундберг С., Ли С.-И. Единый подход к интерпретации модельных предсказаний.)  Lundberg S. M., Erion G., Lee S. Consistent Individualized Feature Attribution for Tree Ensembles. ICML Workshop. — 2018. — URL: https://arxiv.org/abs/ 1802.03888 (Лундберг С. М., Эрион Г. и Ли С. Согласованная индивидуализированная атрибуция признаков для древесных ансамблей.)  Shrikumar A., Greenside P., Kundaje A. Learning Important Features Through Propagating Activation Differences). — 2017. — URL: https://arxiv.org/abs/ 1704.02685 (Шрикумар А., Гринсайд П., Кундадже А. Усвоение важных признаков путем распространения активационных разниц.)  Sturmfels P., Lundberg S., Lee S. Visualizing the Impact of Feature Attribution Baselines). — 2020. — URL: https://www.doi.org/10.23915/distill.00022 (Штурмфельс П., Лундберг С., Ли С. Визуализация влияния базовых уровней атрибуции признаков.)  Apley D. W., Zhu J. Visualizing the Effects of Predictor Variables in Black Box Su- pervised Learning Models). — 2019. — URL: https://arxiv.org/abs/1612.08468. (Апли Д. У., Чжу Дж. Визуализация эффектов предсказательных переменных в моделях черно-ящичного контролиремого усвоения.)
6 Модельно-агностические методы локальной интерпретации В предыдущих двух главах мы работали исключительно с методами глобальной интерпретации. В этой главе мы коснемся методов локальной интерпретации, объясняющих причину, из-за которой было сделано одно предсказание или группа предсказаний. В ней будет рассказано, как использовать ядерный объяснитель (KernelExplainer) аддитивных объяснений Шепли (SHapley Additive exPlanations, SHAP), а также еще один метод, именуемый локально-интерпретируемыми модельно-агностическими объяснениями (local interpretable model-agnostic explanations, LIME) для локальных интерпретаций. Мы также проведем анализ использования этих методов как с табличными, так и с текстовыми данными. Вот главные темы, которые будут охвачены в этой главе:  применение ядерного объяснителя (KernelExplainer) SHAP для локальных ин- терпретаций со значениями SHAP;  применение LIME;  использование LIME для обработки естественного языка (natural language processing, NLP);  применение SHAP для обработки естественного языка;  сравнение SHAP с LIME. Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, nltk, lightgbm, rulefit, matplotlib, seaborn, shap и lime. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/Interpretable-MachineLearning-with-Python/tree/master/Chapter06. Миссия Кто не любит шоколад?! Это всеобщий фаворит, девять из десяти человек его любят, и около миллиарда человек едят его каждый день. Одной из популярных форм его употребления является шоколадная плитка. Однако даже всеми любимые ингредиенты можно переработать способами, которые понравятся не всем, и поэтому качество шоколадных плиток может варьировать от элитных до посредственных и далее до совершенно отвратительных. Нередко это определяется исключительно
228 Часть II. Освоение методов интерпретации качеством какао или дополнительных ингредиентов, а иногда шоколад получается необыкновенным только в сочетании с экзотическими вкусовыми добавками. Стремясь к совершенству производитель французского шоколада обратился к вам с проблемой. Все плитки его компании были оценены критиками высоко, но у критиков особые вкусовые рецепторы. И некоторые понравившиеся им плитки имеют необъяснимо посредственные продажи, но потребителям в фокус-группах и на дегустациях, похоже, они нравятся, поэтому критики озадачены тем, почему продажи не совпадают с их рыночными исследованиями. Они нашли набор данных о шоколадных плитках, оцененных настоящими гурманами, и, как оказалось, эти оценки совпали с их продажами. В целях получения несмещенного мнения производитель обратился к вам. Что касается набора данных, то члены Манхэттенского шоколадного общества встречаются с 2007 года с единственной целью попробовать и оценить прекрасный шоколад, обучить потребителей и вдохновить производителей шоколада на производство более качественного продукта. С тех пор собран набор данных из более чем 2200 плиток шоколада, оцененных участниками экспертиз по следующей шкале:  4,0–5,00 — выдающийся шоколад;  3,5–3,99 — настоятельно рекомендуемый;  3,0–3,49 — рекомендуемый;  2,0–2,99 — разочаровывающий;  1,0–1,90 — неприятный. Эти оценки основаны на рубрике, которая принимает в расчет аромат, внешний вид, текстуру, вкус, послевкусие и совокупное мнение, а оцениваемые плитки в основном представляют более темные шоколадные плитки, поскольку цель состоит в том, чтобы оценить вкусовые разновидности какао. В дополнение к указанным оценкам, набор данных Манхэттенского шоколадного общества включает целый ряд характеристик, таких как страна, в которой выращивали какао-бобы, масса ингредиентов в плитке, содержание соли, а также слова, используемые для его описания. Цель состоит в том, чтобы понять, почему одни плитки производителя шоколада считаются выдающимся шоколадом, но продаются плохо, в то время как другие, продажи которого впечатляют, оцениваются как разочаровывающий шоколад. Подход Вы решили использовать локальную модельную интерпретацию, чтобы объяснить причину, почему каждая плитка оценивается так, как она оценивается. С этой целью вы подготовите набор данных, а затем натренируете классификационные модели, чтобы предсказать, будут ли оценки шоколадных плиток выше или равны оценке "настоятельно рекомендуемый", потому что клиент хотел бы, чтобы все оценки его плиток оказались выше этого порога. Вам нужно будет обучить две модели: одну для табличных данных, а другую на основе обработки естественного
Глава 6. Модельно-агностические методы локальной интерпретации 229 языка (NLP) на словах, используемых для описания шоколадных плиток. Для этих задач мы будем использовать соответственно опорно-векторные машины (SVMмашины) и легкую градиентно-бустинговую машину (LightGBM). Если указанные модели типа "черный ящик" вами еще не использовались ранее, не переживайте — мы дадим их краткие объяснения. После того как вы натренируете эти модели, начнется самое интересное: мы задействуем два модельно-агностических метода локальной интерпретации, чтобы понять, что делает оценку конкретной шоколадной плитки меньше оценки "настоятельно рекомендуемый", а что — нет. Указанными методами являются SHAP и LIME, которые в сочетании дадут более глубокое объяснение, которое можно будет донести до вашего клиента. Затем мы проведем сравнительный анализ обоих методов, чтобы понять их сильные стороны и пределы. Подготовительные работы Исходный код этого примера можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter06/ChocoRatings.ipynb. Загрузка библиотек В целях выполнения этого примера вам необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas, numpy и nltk для манипулирования данными;  sklearn (scikit-learn) и lightgbm для разбивки данных и обучения моделей;  matplotlib, seaborn, shap и lime для визуализации интерпретаций. Сначала необходимо их все загрузить следующим образом: import math import mldatasets import pandas as pd import numpy as np import re import nltk from nltk.probability import FreqDist from sklearn.model_selection import train_test_split from sklearn.pipeline import make_pipeline from sklearn import metrics, svm from sklearn.feature_extraction.text import TfidfVectorizer import lightgbm as lgb import matplotlib.pyplot as plt
230 Часть II. Освоение методов интерпретации import seaborn as sns import shap import lime import lime.lime_tabular from lime.lime_text import LimeTextExplainer Вам также обязательно нужно скачать стоп-слова stopwords и лексемизатор punkt до их загрузки, например, следующим образом: nltk.download('stopwords') nltk.download('punkt') from nltk.corpus import stopwords from nltk.tokenize import word_tokenize Изучение проблемы и подготовка данных Мы загружаем данные в кадр данных, который назовем chocolateratings_df, следующим образом: chocolateratings_df = mldatasets.load("chocolate-bar-ratings") Должно быть более 2200 записей и 18 столбцов. Это можно подтвердить, просто оценив содержимое кадра данных, например, так: chocolateratings_df Результат, показанный на рис. 6.1, соответствует тому, что мы ожидали. Рис. 6.1. Содержимое набора данных о шоколадных плитках Словарь данных Словарь данных содержит следующее.  company — категориальный; производитель шоколадной плитки (из более чем 500 разных).  company_location — категориальный; страна-производитель (66 разных стран).
Глава 6. Модельно-агностические методы локальной интерпретации 231  review_date — непрерывный; год, в котором на плитку был оставлен отзыв (с 2006 по 2020 год).  country_of_bean_origin — категориальный; страна, в которой был собран урожай какао-бобов (62 разные страны).  cocoa_percent — категориальный; процент содержания какао в плитке.  rating — непрерывный; оценка, данная Манхэттенским шоколадным обществом (возможные значения: 1–5).  counts_of_ingredients — непрерывный; величина ингредиентов в плитке.  Beans — двоичный; была ли она приготовлена с использованием какао-бобов (have_bean) либо без использования какао-бобов (have_not_bean)?  cocoa_butter — двоичный; была ли она изготовлена с использованием какао-масла (have_cocoa_butter) либо без использования какао-масла (have_not_cocoa_butter)?  vanilla — двоичный; была ли она изготовлена с использованием ванили (have_vanilla) либо без использования ванили (have_not_vanilla)?  lecithin — двоичный; была ли она изготовлена с использованием лецитина (have_lecithin) либо без использования лецитина (have_not_lecithin)?  salt — двоичный; была ли она изготовлена с использованием соли (have_salt) либо без использования соли (have_not_salt)?  sugar — двоичный; была ли она изготовлена с использованием сахара (have_sugar) либо без использования сахара (have_not_sugar)?  sweetener_without_sugar — двоичный; была ли она изготовлена с использованием подсластителя без сахара (have_sugar) либо без использования подсластителя без сахара (have_not_sugar)?  first_taste — текст; слово (слова), используемое для описания первого вкуса.  second_taste — текст; слово (слова), используемое для описания второго вкуса.  third_taste — текст; слово (слова), используемое для описания третьего вкуса.  fourth_taste — текст; слово (слова), используемое для описания четвертого вкуса. Теперь, когда мы взглянули на данные, можно быстро их подготовить, а затем заняться построением моделей и интерпретацией! Подготовка данных Прежде всего необходимо отложить в сторону текстовые признаки, чтобы иметь возможность их обрабатывать по отдельности. Можно начать с создания кадра данных tastes_df, а затем удалить их из chocolateratings_df. Затем можно взглянуть на tastes_df, используя методы head() и tail(), как показано в следующем ниже фрагменте исходного кода: tastes_df = chocolateratings_df[['first_taste', 'second_taste', 'third_taste', 'fourth_taste']]
232 Часть II. Освоение методов интерпретации chocolateratings_df = chocolateratings_df.\ drop(['first_taste', 'second_taste', 'third_taste', 'fourth_taste'], axis=1) tastes_df.head(90).tail(10) Приведенный выше фрагмент производит кадр данных, показанный на рис. 6.2. Рис. 6.2. Столбцы вкусов имеют довольно много пустых значений Теперь давайте категориально закодируем категориальные признаки. В столбцах company_location и country_of_bean_origin слишком много стран, поэтому давайте установим порог. Скажем, если для какой-либо страны существует менее 3,333% (или 74 строки), давайте будем относить ее в корзину с категорией Other (Прочие), а затем закодируем категории. Это можно легко сделать с помощью функции make_dummies_with_limits, которую мы использовали в предыдущей главе, и указанный процесс снова показан в следующем фрагменте исходного кода: chocolateratings_df = mldatasets.make_dummies_with_limits(\ chocolateratings_df, 'company_location', 0.03333) chocolateratings_df = mldatasets.make_dummies_with_limits(\ chocolateratings_df, 'country_of_bean_origin', 0.03333) Все двоичные признаки должны быть преобразованы в единицы и нули, за исключением признака какао-бобов (beans), который всегда имеет одно и то же значение (have_bean). Будем использовать следующий исходный код: chocolateratings_df = chocolateratings_df.drop(['beans'], axis=1) binary_features = ['cocoa_butter', 'vanilla', 'lecithin',\ 'salt', 'sugar', 'sweetener_without_sugar'] chocolateratings_df[binary_features] = chocolateratings_df[\ binary_features].apply(lambda x: np.where(x.str.contains('not'), 0, 1)) Теперь, в целях обработки содержимого tastes_df, необходимо отметить, что в идеале мы хотели бы видеть в основном прилагательные, которые означают вкус
Глава 6. Модельно-агностические методы локальной интерпретации 233 или, по меньшей мере, вызывают ассоциацию со вкусом. Поэтому стоп-слова — т. е. распространенные слова, такие как and, of и with — можно безопасно отбросить, а нам нужно будет загрузить список английских стоп-слов из библиотеки nltk следующим образом: stop = stopwords.words('english') Если рассмотреть содержимое tastes_df, можно найти другие элементы, которые могут добавлять в модель шум помимо стоп-слов. Вы найдете знаки препинания и специальные знаки, такие как &, наречия, такие как overly, орфографические ошибки, такие как astringnet, и даже сочетания прилагательных и существительных, такие как full body. Их можно удалить или заменить одним прилагательным. Для этого можно использовать регулярные выражения, все одним махом. С этой целью давайте сначала создадим словарь (trans_dict) с нашими заменами и скомпилируем их в регулярное выражение следующим образом: trans_dict = {'?':'','&':'', 'overly intense':'intensest', 'overly sweet':'sweetest', 'overly tart':'tartest', 'sl. bitter':'bitterness', 'sl. burnt':'burntness', 'sl. sweet':'sweetness', 'sl. dry':'dryness', 'sl. chalky':'chalkiness', 'sl. Burnt':'burntness', 'hints fruit':'fruitiness', 'hint fruit':'fruitiness', 'high acid':'acidic', 'high acidity':'acidic', 'moderate acidity':'acid', 'high roast':'roast', 'astringcy':'astringent', 'astringnet':'astringent', 'full body':'robust', 'astringency':'astringent', 'high astringent':'acidic', 'rich cocoa':'rich', 'mild bitter':'bitterish', 'fruit long':'fruit', 'base cocoa':'basic', 'basic cocoa':'basic', '-like':'', 'smomkey':'smokey', 'true':'real', '(n)':'','/':' ', '-':' ',' +':' ' } trans_regex = re.compile("|".join(map(re.escape, trans_dict.keys( )))) Следующий ниже исходный код заменяет все nan-значения пустыми строковыми литералами, затем соединяет все столбцы в tastes_df вместе, образуя единый ряд. Затем он удаляет ведущие и концевые пробелы и конвертирует весь текст в нижний регистр. У него есть два экземпляра apply: первый выполняет все замены в регулярных выражениях, а второй удаляет стоп-слова. Вот этот код: tastes_s = tastes_df.replace(np.nan, '', regex=True).\ agg(' '.join, axis=1).str.strip().str.lower().\ apply(lambda s: trans_regex.sub(lambda match: trans_dict[match.group(0)], s)).\ apply(lambda s: ' '.join([word for word in s.split() if word not in (stop)]))
234 Часть II. Освоение методов интерпретации И вуаля! Подтвердить результат, т. е. ряд библиотеки pandas (tastes_s) (в основном) с прилагательными, связанными со вкусом, например, можно следующим образом: print(tastes_s) Как и ожидалось, этот ряд имеет ту же длину, что и кадр данных chocolateratings_df, как показано в следующем фрагменте: 0 cocoa blackberry robust 1 cocoa vegetal savory 2 rich fatty bready 3 fruity melon roasty 4 vegetal nutty ... 2221 muted roasty accessible 2222 fatty mild nuts mild fruit 2223 fatty earthy cocoa Length: 2224, dtype: object Но давайте выясним его число уникальных фраз с помощью следующей строки кода: print(np.unique(tastes_s).shape) На основе полученного результата можно судить, что дублируется менее 50 фраз, поэтому лексемизация по фразам была бы плохой идеей: (2178,) Здесь можно было бы использовать целый ряд подходов, например, лексемизацию по биграммам (последовательностям из двух слов) или даже по подсловам (деля слова на логические части). Однако, несмотря на то что порядок следования играет незначительную роль (потому что первые слова имели отношение к первому вкусу и вторые — ко второму и т. д.), наш набор данных слишком мал и имеет слишком много пустот (в особенности в третьем и четвертом вкусах), чтобы выводить смысл из порядка следования. Вот почему хороший вариант состоял в конкатенировании всех "вкусов" вместе. Так мы удалим их различимое разделение. Следует отметить еще одну вещь, а именно: наши слова (в основном) являются прилагательными. Мы предприняли небольшие усилия, чтобы удалить наречия, но всё еще есть несколько существительных, таких как "fruit" (фрукт) и "nuts" (орехи), в противовес прилагательным, таким как "fruity" (фруктовый) и "nutty" (ореховый). Мы не можем быть уверены, что ценители шоколада, выразившие свое мнение о плитках, имели в виду что-то другое, используя "fruit", вместо "fruity". Однако, если бы мы были в этом уверены, мы бы могли выделить основы или леммы слов, чтобы превратить все примеры "fruit", "fruity" и "fruitiness" в сопоставимые "fru" (основа) или "fruiti" (лемма). Мы не будем этим заниматься, потому что многие вариации наших прилагательных в любом случае не столь распространены во фразах. Давайте выясним наиболее распространенные слова, сначала выполнив их лексемизацию word_tokenize и используя FreqDist для подсчета их частоты. Затем можно поместить результирующий словарь tastewords_fdist в кадр данных (tastewords_df).
Глава 6. Модельно-агностические методы локальной интерпретации 235 Можно сохранить только эти слова с более чем 74 экземплярами в виде списка (commontastes_l). Соответствующий фрагмент исходного кода показан ниже: tastewords_fdist = FreqDist(word for word in\ word_tokenize(tastes_s.str.cat(sep=' '))) tastewords_df = pd.DataFrame.from_dict(tastewords_fdist,\ orient='index').rename(columns={0:'freq'}) commontastes_l = tastewords_df[tastewords_df.freq > 74].index.to_list() print(commontastes_l) Как можно судить по следующей ниже распечатке commontastes_l, наиболее распространенные слова в основном разные (за исключением spice и spicy): ['cocoa', 'rich', 'fatty', 'roasty', 'nutty', 'sweet', 'sandy', 'sour', 'intense', 'mild', 'fruit', 'sticky', 'earthy', 'spice', 'molasses', 'floral', 'spicy', 'woody', 'coffee', 'berry', 'vanilla', 'creamy'] С целью улучшения табличного набора данных можно превратить эти общие слова в двоичные признаки. Другими словами, для каждого "общего вкуса" (commontastes_l) был бы столбец, и если бы "вкусы" шоколадной плитки его включали, то в столбце была бы 1, в противном случае — 0. К счастью, это можно легко сделать с помощью двух строк кода. Сначала мы создаем новый столбец с текстовым рядом вкусов (tastes_s). Затем используем функцию make_dummies_from_dict, которую применяли в предыдущей главе, для генерирования фиктивных признаков путем поиска каждого "общего вкуса" в содержимом нового столбца следующим образом: chocolateratings_df['tastes'] = tastes_s chocolateratings_df = mldatasets.make_dummies_from_dict(\ chocolateratings_df, 'tastes', commontastes_l) Теперь, когда мы закончили с конструированием признаков, можно применить метод info(), чтобы посмотреть краткое описание информации в нашем кадре данных, следующим образом: chocolateratings_df.info() В распечатке можно видеть все числовые непустые (non-null) признаки, кроме компании (company). В ряду имеется более 500 компаний, поэтому категориально кодировать этот признак будет сложно, и поскольку целесообразно отнести большинство компаний к корзине Other, это, скорее всего, внесет систематическое смещение в сторону нескольких компаний, которые представлены больше всего. Данный столбец лучше удалить вообще. Результат показан ниже: <class 'pandas.core.frame.DataFrame'> RangeIndex: 2224 entries, 0 to 2223 Data columns (total 46 columns): # Column Non-Null Count Dtype --- ------ -------------- -----
236 0 Часть II. Освоение методов интерпретации company 2224 non-null object 1 review_date 2224 non-null int64 2 cocoa_percent 2224 non-null float64 3 rating 2224 non-null float64 4 counts_of_ingredients 2224 non-null int64 : : : : : 43 tastes_berry 2224 non-null int64 44 tastes_vanilla 2224 non-null int64 45 tastes_creamy 2224 non-null int64 dtypes: float64(2), int64(30), object(1), uint8(13) memory usage: 601.7+ KB Наш последний шаг по подготовке данных для построения моделей начинается с инициализации константы rand, которая на протяжении всего этого примера будет служить "случайным состоянием". Затем мы определяем y как столбец rating, конвертированный в единицы, если он больше или равен 3,5, и в 0 в противном случае. X — это все остальное (исключая компанию). Затем мы разбиваем X и y на тренировочную и тестовую выборки с помощью функции train_test_split, как показано в следующем фрагменте исходного кода: rand = 9 y = chocolateratings_df['rating'].apply(lambda x: 1 if x >= 3.5 else 0) X = chocolateratings_df.drop(['rating','company'], axis=1).copy() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33,\ random_state=rand) В дополнение к табличным тестовому и тренировочному наборам данных, для наших моделей на основе NLP понадобятся наборы данных только с текстовыми признаками, которые согласуются с нашей функцией train_test_split, чтобы мы получили возможность использовать те же самые метки y. С этой целью мы умножаем наш вкусовой ряд (tastes_s), используя индекс (index) наборов X_train и X_test, чтобы получить конкретные версии ряда NLP следующим образом: X_train_nlp = tastes_s[X_train.index] X_test_nlp = tastes_s[X_test.index] Отлично! Теперь все готово. Начнем строить и интерпретировать наши модели! Задействование ядерного объяснителя SHAP для локальных интерпретаций со значениями SHAP В этом разделе и для последующего использования мы сначала натренируем модель на основе опорно-векторного классификатора (support vector classifier, SVC).
Глава 6. Модельно-агностические методы локальной интерпретации 237 Обучение модели C-SVC SVM — это семейство модельных классов, которые оперируют на многомерном пространстве, отыскивая оптимальную гиперплоскость, в которой они пытаются отделить классы с максимальной маржой (или полосой, зазором) между ними. Опорные векторы — это точки, наиболее близкие к границе решения (разделяющей гиперплоскости), которые изменили бы ее, если бы были удалены. В целях отыскания наилучшей гиперплоскости они используют стоимостную функцию, именуемую функцией потерь на шарнирах (или кусочно-линейной loss-функцией), и вычислительно дешевый метод оперирования на многомерном пространстве, именуемый ядерным трюком; и хотя гиперплоскость предполагает линейную разделимость, указанная модель не всегда ограничивается линейным ядром. Имплементация в библиотеке scikit-learn, которая нам понадобится, называется C-SVC. SVC использует регуляризационный параметр L2, именуемый C, и по умолчанию использует ядро, именуемое радиально-базисной функцией (radial basis function, RBF), которое однозначно является нелинейным. Для RBF гиперпараметр гамма определяет радиус влияния каждого тренировочного примера в ядре, но обратно пропорционально. Следовательно, низкое значение увеличивает радиус, а высокое его уменьшает. Семейство SVM включает несколько вариаций классифицирования и даже регрессионные классы посредством опорно-векторной регрессии (support vector regression, SVR). Наиболее существенным преимуществом моделей на основе SVM является то, что они, как правило, работают результативно и эффективно при наличии большого числа признаков по сравнению с наблюдениями, и даже в случаях, когда признаки превышают наблюдения по численности! Указанная модель также стремится отыскать скрытые нелинейные связи в данных, без преобучения и не становясь нестабильной. Однако SVM не так масштабируема на более крупные наборы данных, и наладка ее гиперпараметров затруднена. Поскольку мы будем использовать стилизацию библиотеки seaborn для графиков, которая активируется функцией set(), в некоторых графиках этой главы мы сначала будем сохранять изначальные настройки библиотеки matplotlib (rcParams), чтобы иметь возможность восстанавливать их позже. В отношении SVC следует отметить одну вещь — она не порождает вероятности нативно, поскольку это линейная алгебра. Однако, если probability=True, то имплементация в библиотеке scikit-learn использует кросс-валидацию, а затем выполняет подгонку логистической регрессионной модели под баллы SVC для порождения вероятностей. Мы также используем gamma=auto. Эта настройка означает, что для нее установлено значение 1/число признаков — а значит 1/44. Как всегда, в целях воспроизводимости рекомендуется задать аргумент random_state. После подгонки модели можно применить метод evaluate_class_mdl для оценивания предсказательной результативности модели, как показано в следующем ниже фрагменте исходного кода: orig_plt_params = plt.rcParams sns.set() svm_mdl = svm.SVC(probability=True, gamma='auto', random_state=rand)
238 Часть II. Освоение методов интерпретации fitted_svm_mdl = svm_mdl.fit(X_train, y_train) y_train_svc_pred, y_test_svc_prob, y_test_svc_pred =\ mldatasets.evaluate_class_mdl(fitted_svm_mdl, X_train,\ X_test, y_train, y_test) Приведенный выше фрагмент порождает результат, показанный на рис. 6.3. Рис. 6.3. Предсказательная результативность модели на основе SVC Достигнутая результативность (см. рис. 6.3) неплоха, если учитывать, что мы имеем дело с малым несбалансированным набором данных в уже сложной области пользовательских оценок в моделях машинного обучения. В любом случае площадь под кривой (area under the curve, AUC) находится выше пунктирной линии подбрасывания монеты, а коэффициент корреляции Мэтьюса (Matthews correlation coeffi cient, MCC) располагается безопасно выше 0. Еще важнее, что точность значительно пре-
Глава 6. Модельно-агностические методы локальной интерпретации 239 вышает полноту, и это очень хорошо, учитывая гипотетическую стоимость неправильного классифицирования негодной шоколадной плитки, как настоятельно рекомендуемой. Мы предпочитаем точность полноте, потому что предпочли бы иметь меньше ложноположительных классификаций, чем ложноотрицательных. Вычисление значений SHAP с помощью ядерного объяснителя Учитывая вычислительную интенсивность, связанную с получением значений SHAP методом грубой силы, мы можем воспользоваться целым рядом статистически валидных сокращений из библиотеки SHAP. Как мы узнали в главе 5, указанные сокращения варьируются от задействования структуры дерева решений (двевесноподобный объяснитель) до разностей в нейросетевых активациях и базовой линии (глубокий объяснитель) и нейросетевого градиента (градиентный объяснитель). Эти сокращения делают объяснители значительно менее модельно-агностическими, поскольку они ограничены семейством модельных классов. Тем не менее в SHAP есть по-настоящему модельно-агностический объяснитель, именуемый ядерным объяснителем (KernelExplainer). В ядерном объяснителе (KernelExplainer) есть два подхода: он выбирает подмножество всех перестановок признаков для коалиций и использует схему взвешивания в соответствии с размером коалиции для вычисления значений SHAP. Первый подход является рекомендуемым методом для снижения времени вычислений. Второй взят из схемы взвешивания и используется методом локально-интерпретируемых модельно-агностических объяснений (LIME), который мы рассмотрим в следующей главе, и авторы SHAP сделали это так, чтобы он оставался совместимым с Шепли. Однако для признаков, которые в коалиции "пропущены", он отбирает значения признаков случайно в фоновом тренировочном наборе данных, что нарушает свойство фиктивности в значениях Шепли. Еще важнее, как и с перестановочной важностью признаков, что при существовани мультиколлинеарности он наделяет маловероятные случаи излишним весом. Несмотря на этот почти фатальный недостаток, ядерный объяснитель обладает всеми другими преимуществами значений Шепли и выступает одним из главных преимуществ метода LIME. Прежде чем мы займемся ядерным объяснителем, важно отметить, что для классификационных моделей он порождает список из нескольких значений SHAP. К ним можно обращаться по индексу для каждого класса. Путаница может возникать, если этот индекс находится не в том порядке, в котором вы ожидаете, потому что он находится в порядке, предусмотренном моделью. Поэтому важно убедиться в очередности классов в вашей модели, выполнив следующую инструкцию: svm_mdl.classes_ Результат на выходе говорит о том, что оценка "не очень рекомендуемый", как и ожидается, имеет индекс 0, а оценка "настоятельно рекомендуемый" имеет индекс 1. Нас интересуют значения SHAP для последнего из двух, потому что это собственно то, что мы и пытаемся предсказать. Результат показан ниже: array([0, 1])
240 Часть II. Освоение методов интерпретации Ядерный объяснитель (KernelExplainer) использует предсказательную функцию модели (fitted_svm_mdl.predict_proba) и немного фоновых тренировочных данных (X_train_summary). KernelExplainer настоятельно предлагает и другие меры по минимизированию вычислений. Одной из них является использование k средних для резюмирования фоновых тренировочных данных вместо того, чтобы использовать сразу все. Еще одним методом может быть взятие выборок из тренировочных данных. В этом случае наш выбор пал на кластеризацию k средних на 10 центроидов. После инициализации объяснителя выборки из тестового набора данных (nsamples=200) можно использовать для того, чтобы получить значения SHAP. Во время процесса обучения он применяет регуляризацию L1 (l1_reg). Таким образом мы хотим регуляризовать его до такой степени, чтобы он имел только 20 релевантных признаков. Наконец, можно применить сводный график (summary_plot), чтобы построить график значений SHAP для класса 1. Соответствующий фрагмент исходного кода показан ниже: np.random.seed(rand) X_train_summary = shap.kmeans(X_train, 10) shap_svm_explainer =\ shap.KernelExplainer(fitted_svm_mdl.predict_proba, X_train_summary) shap_svm_values_test = shap_svm_explainer.shap_values(X_test, nsamples=200,\ l1_reg="num_features(20)") shap.summary_plot(shap_svm_values_test[1], X_test, plot_type="dot") Приведенный выше фрагмент порождает результат, показанный на рис. 6.4. Несмотря на то что темой этой главы является локальная модельная интерпретация, важно начать с ее глобальной формы, чтобы обеспечить интуитивность результатов. Если они такими не являются, то, возможно, что-то не так. Из рис. 6.4 следует, что самый высокий (красный) процент какао (cocoa_percent), как правило, коррелирует с уменьшением вероятности оценки "настоятельно рекомендуемый", но средние значения (фиолетовый), как правило, увеличивают ее. Эта находка имеет интуитивный смысл, потому что самые темные шоколадные плитки имеют приобретенный вкус в большей мере, чем менее темный шоколад. Низкие значения (синий) разбросаны повсюду, поэтому они не показывают никакого тренда, но это бывает от того, что их не столь много. С другой стороны, дата оценки предполагает, что шоколад, вероятно, имел оценку "настоятельно рекомендуемый" в предыдущие годы. По обе стороны от 0 есть значительные участки красного и фиолетового цветов, поэтому здесь определить тренд трудно. Для этой цели лучше использовать график зависимости, такой как те, которые представлены в главе 5. Однако с двоичными признаками очень легко визуализировать то, как высокие и низкие значения, единицы и нули влияют на модель. Например, можно сказать, что наличие какао, сливочного (creamy), насыщенного (rich) и ягодного (berry) вкусов увеличивает вероятную возможность того, что шоколад будет рекомендован, тогда как сладкий (sweet), земляной (earthy), кислый (sour) и жирный (fatty) вкус делают всё наоборот. Аналогичным образом, шансы на оценку "настоятельно рекомендуемый" снижаются, если шоколад был произведен в США! Извините, США.
Глава 6. Модельно-агностические методы локальной интерпретации 241 Рис. 6.4. Глобальная модельная интерпретация с помощью SHAP, используя сводный график Локальная интерпретация для группы предсказаний с использованием графиков решений В случае локальной интерпретации нет необходимости визуализировать по одной точке за раз — вместо этого можно интерпретировать одновременно несколько точек. Самое главное — обеспечить некоторый контекст для адекватного сравнения точек, а их может оказаться не столь много, и различить их вполне возможно. Скорее всего, вы найдете выбросы или только те точки, которые соответствуют конкретным критериям. В этом примере мы выберем лишь те шоколадные плитки, которые были изготовлены вашим клиентом, следующим образом: sample_test_idx = X_test.index.get_indexer_for([5,6,7,18,19,21,24,25,27]) У значений Шепли есть одна замечательная особенность, и это их свойство аддитивности, которое можно легко продемонстрировать. Если добавить все значения SHAP к ожидаемому значению, которое используется для их вычисления, то получится предсказание. Конечно, мы имеем дело с классификационной задачей, и поэтому предсказание является вероятностью; значит, чтобы вместо этого получить массив булевых значений, необходимо делать проверку на превышение вероятно-
242 Часть II. Освоение методов интерпретации стью значения 0,5. Можно осуществить проверку на совпадение этого булева массива с предсказаниями нашей моделью на тестовом наборе данных (y_test_ svc_pred), выполнив следующий исходный код: expected_value = shap_svm_explainer.expected_value[1] y_test_shap_pred = (shap_svm_values_test[1].sum(1) + expected_value) > 0.5 print(np.array_equal(y_test_shap_pred, y_test_svc_pred)) Так и должно быть, и так оно и есть! Подтверждение можно увидеть ниже: True График решения SHAP показывает высвеченный признак, который можно использовать, чтобы выделять ложноотрицательные классификации (FN). Теперь давайте выясним, какие из наших выборочных наблюдений являются FN, следующим образом: FN = (~y_test_shap_pred[sample_test_idx]) & (y_test.iloc[sample_test_idx] == 1).to_numpy() Теперь можно быстро сбросить наш стиль графопостроения обратно в стиль библиотеки matplotlib, используемый по умолчанию, и построить график решения (decision_plot). Он принимает ожидаемое значение (expected_value), значения SHAP и фактические значения тех элементов, которые мы хотим вывести на графике. Можно опционально предоставить массив булевых элементов, которые мы хотим выделить пунктирными линиями — в данном случае ложноотрицательных классификаций (FN), как показано в следующем фрагменте исходного кода: sns.reset_orig() plt.rcParams.update(orig_plt_params) shap.decision_plot(expected_value, shap_svm_values_test[1][sample_test_idx],\ X_test.iloc[sample_test_idx], highlight=FN) Изображенный на рис. 6.5 график имеет одну кодированную цветом линию для каждого наблюдения. Цвет каждой линии представляет не значение какого-либо элемента, а результат работы модели. Поскольку в ядерном объяснителе (KernelExplainer) мы использовали функцию predict_proba, то мы имеем дело с вероятностью, в противном же случае график изображал бы значения SHAP, а значение, которое имеют признаки, когда они достигают верхней оси x, является предсказанным значением. Признаки отсортированы по степени важности, но только среди наблюдений на графике, и можно сказать, что в зависимости от каждого признака линии смещаются к краю или центру по горизонтали. Степень их варьирования и направление зависят от вклада признака в исход. Серая вертикальная линия представляет ожидаемое значение класса, похожее на пересечение, которое мы встречаем в линейной модели. Фактически, по аналогии, все линии начинаются с этого значения, обеспечивая возможность рассматривать график снизу вверх. На рис. 6.5 изображены три ложноотрицательные классификации; они изображены штрих-пунктирными линиями. Используя этот график, можно легко визуализировать признаки, которые заставили линии отклониться влево больше всего, потому что именно это сделало их отрицательными предсказаниями. Например, мы знаем, что крайняя левая ложноотрицательная классификация находится справа от линии
Глава 6. Модельно-агностические методы локальной интерпретации 243 ожидаемого значения вплоть до лецитина (lecithin), а затем продолжет отклоняться влево (значения признаков уменьшаются) вплоть до местоположения компании во Франции (company_location_France), а дата оценки (review_date) увеличивала вероятную возможность того, что шоколад будет иметь оценку "настоятельно рекомендуемый", но этого было недостаточно. Можно сказать, что другая страна происхождения какао-бобов (county_of_bean_origin_Other) снизила вероятность двух ошибочных классификаций. Возможно, это решение будет необъективным, потому что страна может быть одной из более чем 50 стран, которые не получили собственных признаков. Вполне вероятно, что между какао-бобами этих сгруппированных вместе стран существует много вариаций. Рис. 6.5. Локальная модельная интерпретация с помощью SHAP для выборки предсказаний с выделением ложноотрицательных классификаций График решения также может изолировать одно наблюдение. При этом он выводит значение каждого элемента рядом с пунктирной линией. Давайте построим график решения относительно той же самой компании (истинноположительное наблюдение № 696) следующим образом: shap.decision_plot(expected_value, shap_svm_values_test[1][696], X_test.iloc[696], highlight=0) Приведенный на рис. 6.6 график порожден приведенным выше исходным кодом.
244 Часть II. Освоение методов интерпретации Рис. 6.6. Локальная модельная интерпретация с помощью SHAP для одиночного истинноположительного результата в выборке предсказаний На рис. 6.6 хорошо видно, что лецитин (lecithin) и количество ингредиентов (counts_of_ingredients) уменьшили вероятную возможность оценки "настоятельно рекомендуемый" до такой степени, что это могло поставить ее под угрозу. К счастью, все признаки находятся выше по рейтингу тех, которые явно отклонили линию вправо, потому что атрибуты company_location_France=1, cocoa_percent=70 и tastes_berry=1 являются благоприятными. Локальная интерпретация по одному предсказанию за раз с использованием силового графика Ваш клиент, производитель шоколада, хочет, чтобы вы сравнили две шоколадные плитки. Плитка № 5 — выдающийся шоколад, а № 24 — разочаровывающий шоколад. Обе находятся в вашем тестовом наборе данных. Сравнить их можно, поместив их значения рядом друг с другом в кадр данных, чтобы понять, чем именно они отличаются. Мы конкатенируем оценку, фактическую метку y и предсказанную метку y_pred со значениями этих наблюдений следующим образом: eval_idxs = (X_test.index==5) | (X_test.index==24) X_test_eval = X_test[eval_idxs]
Глава 6. Модельно-агностические методы локальной интерпретации 245 eval_compare_df = pd.concat([\ chocolateratings_df.iloc[X_test[eval_idxs].index].rating,\ pd.DataFrame({'y':y_test[eval_idxs]}, index=[5,24]),\ pd.DataFrame({'y_pred':y_test_svc_pred[eval_idxs]},\ index=[24,5]), X_test_eval], axis=1).transpose() eval_compare_df Рис. 6.7. Наблюдения № 5 и 24, различия данных этих признаков выделены желтым цветом Приведенный выше исходный код порождает кадр данных, показанный на рис. 6.7. С помощью этого кадра данных можно подтвердить, что выбранные плитки не имеют ошибочной классификации, потому что y=y_pred. Ошибочная классификация может сделать модельные интерпретации ненадежными в понимании причины, почему одна плитка шоколада людям нравится больше другой. Затем можно оценить признаки, чтобы выявить различия, например, можно сказать, что дата оценки
246 Часть II. Освоение методов интерпретации (review_date) имеет разницу в 2 года. Кроме того, какао-бобы для выдающейся плитки были из Венесуэлы, а какао-бобы разочаровывающей плитки — из другой, менее представленной страны. У выдающейся был ягодный вкус, а у разочаровывающей — земляной. Силовой график может рассказать полную историю того, что повлияло на решения модели (и, предположительно, рецензентов), и дает нам подсказки относительно предпочтений потребителей. Для построения силового графика (force_plot) требуются ожидаемое значение интересующего вас класса (expected_value), значение SHAP интересующего вас наблюдения и фактические значения этого наблюдения. Мы начнем с наблюдения № 5, как показано в следующем фрагменте исходного кода: shap.force_plot(expected_value, shap_svm_values_test[1][X_test.index==5],\ X_test[X_test.index==5], matplotlib=True) Приведенный выше фрагмент порождает график на рис. 6.8. Этот силовой график показывает вес, которым обладают дата оценки (review_date), процент какао (cocoa_percent) и ягодный вкус (tastes_berry) в предсказании, тогда как единственным признаком, который, похоже, смещает вес в противоположном направлении, является количество ингредиентов (counts_of_ingredients). Рис. 6.8. Силовой график для наблюдения № 5 (выдающийся) Давайте сравним это с силовым графиком наблюдения № 24 следующим образом: shap.force_plot(expected_value, shap_svm_values_test[1][X_test.index==24],\ X_test[X_test.index==24], matplotlib=True) Рис. 6.9. Силовой график для наблюдения № 24 (разочаровывающий) Приведенный выше исходный код создает график на рис. 6.9. На нем хорошо видно, что земляной вкус (tastes_earthy) и другая страна происхождения какао-бобов (country_of_bean_origin_Other) считаются очень отрицательными атрибутами нашей модели. Исход может в основном объясняться разницей в "ягодном" и "земляном" вкусах шоколада. Несмотря на наши находки, страна происхождения какао-бобов
Глава 6. Модельно-агностические методы локальной интерпретации 247 нуждается в дальнейшем расследовании. В конце концов, вполне возможно, что фактическая страна происхождения не коррелирует с плохими оценками. В этом разделе мы рассмотрели ядерный объяснитель (KernelExplainer), в котором используется несколько приемов, позаимствованных им из локально-интерпретируемых модельно-агностических объяснений (LIME). Но что такое локальноинтерпретируемые модельно-агностические объяснения? Мы узнаем это далее! Применение локально интерпретируемых модельно-агностических объяснений До сих пор все рассматриваемые нами модельно-агностические методы интерпретации пытались согласовывать совокупность результатов на выходе из модели с ее данными на входе. В целях получения этими методами хорошего представления о том, как и почему X становится y_pred, им сначала нужны какие-то данные. Затем с этими данными они выполняют симуляции, вводя их вариации и оценивая то, что получается на выходе из модели. Иногда они даже задействуют глобальный суррогат, чтобы составить полную картину. Используя то, что они усвоили в этом процессе, они порождают важности, баллы, правила или значения, которые квантифицируют влияние признака, его взаимодействия или решения на глобальном уровне. В рамках многих методов, таких как SHAP, их также можно наблюдать локально. Однако, даже при возможности локального наблюдения, то, что было количественно определено глобально, может не применяться локально. По этой причине должен существовать еще один подход, который квантифицирует локальные эффекты признаков исключительно для локальной интерпретации — такой, как метод LIME! Что такое LIME? Метод локально интерпретируемых модельно-агностических объяснений (LIME) тренирует локальные суррогаты объяснять одно-единственное предсказание. С этой целью он начинается с вопроса о том, какую точку данных вы хотите интерпретировать. Вы также предоставляете ему свою модель черного ящика и выборку из набора данных. Затем метод делает предсказания на переставленной версии набора данных с помощью модели, создавая схему, в которой он отбирает и взвешивает точки выше, если они находятся ближе к выбранной вами точке данных. Область вокруг вашей точки называется окрестностью. Затем, используя отобранные точки и предсказания типа черного ящика в этой окрестности, метод формирует взвешенную внутренне интерпретируемую суррогатную модель. Наконец, он интерпретирует суррогатную модель. Здесь следует раскрыть целый ряд ключевых слов, поэтому давайте дадим им определение.  Выбранная точка данных. LIME называет точку данных, строку таблицы или наблюдение, которые вы хотите проинтерпретировать экземпляром. Это просто еще одно слово для указанного понятия.
248 Часть II. Освоение методов интерпретации  Пертурбация. LIME симулирует новые образцы, пертурбируя каждый признак, извлекая из соответствующего ему распределения тренировочной выборки данных в случае категориальных признаков и из нормального распределения в случае непрерывных признаков.  Схема взвешивания. LIME использует экспоненциальное сглаживающее ядро как для определения радиуса окрестности, так и для определения того, как взвешивать точки, наиболее отдаленные от ближайших.  Ближе. LIME использует евклидово расстояние для табличных и графических данных и косинусное сходство — для текста. Его трудно представить в высокоразмерных признаковых пространствах, но между точками для любого числа размерностей можно рассчитать расстояние и найти точки, которые расположены к интересующей точке ближе всего.  Внутренне интерпретируемая суррогатная модель. LIME использует разре- женную линейную модель со взвешенной гребневой регуляризацией. Однако он может использовать любую внутренне интерпретируемую модель, если точки данных могут быть взвешены, для этого есть две причины. Ему нужна модель, которая может выдавать надежные внутренние параметры, такие как коэффициенты, сообщащие ему об уровне, с которым каждый признак влияет на предсказание. Он также должен в большей степени учитывать точки данных, наиболее близкие к выбранной точке, потому что они релевантнее. Как и в случае с k ближайшими соседями (k-Nearest Neighbors, k-NN), лежащая в основе LIME интуитивная идея заключается в том, что точки в окрестности имеют общность, потому что близкие друг к другу точки ожидаемо будут иметь похожие, если не одинаковые, метки. Для классификаторов существуют границы решения, и значит, эта идея может быть очень наивным допущением, когда близкие точки делятся на единицу. Подобно еще одному модельному классу в семействе ближайших соседей, радиусным ближайшим соседям, LIME учитывает расстояние в пределах некоего радиуса и взвешивает точки соответствующим образом, хотя и делает это экспоненциально. Однако LIME не модельный класс, а метод интерпретации, а значит, на этом сходство и заканчивается. Вместо того чтобы "голосовать" за предсказания среди соседей, он выполняет подгонку взвешенной суррогатной разреженной линейной модели, потому что исходит из допущения, что каждая сложная модель является линейной локально, и поскольку он не является модельным классом, предсказания суррогатной модели не важны. Фактически, суррогатная модель даже не должна выполнять обучение под данные, потому вам от нее нужны лишь коэффициенты. Разумеется, при этом лучше всего, если она хорошо подогнана, чтобы иметь высокую лояльность в интерпретации. LIME работает для табличных, графических и текстовых данных и, как правило, обладает высокой локальной верностью, а значит, он может довольно хорошо аппроксимировать модельные предсказания на локальном уровне. Однако это зависит от правильности определения окрестности, что связано с выбором правильной ширины ядра и допущением о соблюдении локальной линейности.
Глава 6. Модельно-агностические методы локальной интерпретации 249 Локальная интерпретация по одному предсказанию за раз с использованием табличного объяснителя на основе LIME В целях объяснения одиночного предсказания сначала создают экземпляр табличного LIME-объяснителя (LimeTabularExplainer), предоставив ему выборку из набора данных в числовом двумерном массиве (X_test.values), список с именами признаков (x_test.columns), список с индексами категориальных признаков (только первые три признака не являются категориальными) и имена классов. Несмотря на то что требуется только выборка из набора данных, рекомендуется предоставлять имена своих признаков и классов, чтобы интерпретация имела смысл. В случае табличных данных важно сообщить методу LIME, какие именно признаки являются категориальными (categorical_features), потому что он трактует категориальные признаки иначе, чем непрерывные, и, не указав их, можно потенциально получить плохой локальный суррогат. Еще одним параметром, который может сильно повлиять на локальный суррогат, является ширина ядра (kernel_width). Метод задает диаметр окрестности, тем самым отвечая на вопрос о том, что считать локальным. В нем используется значение по умолчанию, способное давать или не давать интерпретации, которые имеют смысл для вашего экземпляра. Можно отрегулировать этот параметр по каждому экземпляру, чтобы оптимизировать свои объяснения. Вот исходный код: lime_svm_explainer = lime.lime_tabular.LimeTabularExplainer(\ X_test.values, feature_names=X_test.columns,\ categorical_features=list(range(3,44)),\ class_names=['Не очень рекомендуемый', 'Настоятельно рекомендуемый']) Создав экземпляр объяснителя, можно использовать метод explain_instance() для подгонки локальной суррогатной модели под наблюдение № 5. Мы также будем использовать классификаторную функцию нашей модели (predict_proba) и ограничимся восемью признаками (num_features=8). Можно взять возвращенное "объяснение" и сразу же его визуализировать с помощью метода show_in_notebook(). В то же время параметр predict_proba обеспечивает включение в него графика, показывающего наиболее вероятный класс согласно локальной суррогатой модели. Соответствующий фрагмент исходного кода проиллюстрирован ниже: lime_svm_explainer.explain_instance(\ X_test[X_test.index==5].values[0], fitted_svm_mdl.predict_proba, num_features=8).show_in_notebook(predict_proba=True) Приведенный выше фрагмент обеспечивает результат, показанный на рис. 6.10. Согласно локальному суррогату, значение процента какао (cocoa_percent) меньшее или равное 70 является благоприятным атрибутом, как и ягодный вкус. Нехватка кислого, сладкого и мелассового вкусов тоже сказывается на этой модели благоприятно. Тем не менее нехватка насыщенного и сливочного вкусов, а также вкуса какао делает всё наоборот, но этого недостаточно, чтобы подтолкнуть чашу весов к оценке "не очень рекомендуемый".
250 Часть II. Освоение методов интерпретации Рис. 6.10. Табличное объяснение LIME для наблюдения № 5 (выдающийся) При помощи небольшой корректировки приведенного исходного кода можно создать тот же график, но для наблюдения № 24, следующим образом: lime_svm_explainer.explain_instance(\ X_test[X_test.index==24].values[0], fitted_svm_mdl.predict_proba, num_features=8).show_in_notebook(predict_proba=True) Здесь, на рис. 6.11, мы ясно видим, почему локальный суррогат считает, что наблюдение № 24 имеет оценку "не очень рекомендуемый". Рис. 6.11. Табличное объяснение для наблюдения № 24 (разочаровывающий) При сравнении объяснения № 24 (см. рис. 6.11) с объяснением № 5 (см. рис. 6.10) проблемы станут очевидными. Оба объяснения отличаются одним-единственным признаком — ягодным вкусом (tastes_berry). Конечно, мы лимитировали его восемью верхними признаками, так что, вероятно, за этим скрывается нечто большее. Тем не менее вы ожидаете, что в восьмерку верхних признаков войдут те, которые имеют наибольшую разницу. В соответствии с SHAP именно знание того, что tastes_earthy=1, глобально объясняет разочаровывающую природу шоколадной плитки № 24, но это выглядит нелогичным. Высним, что случилось. Оказывается, наблюдения № 5 и 24 относительно похожи и, следовательно, находятся в одной и той же окрестности. Эта окрестность также включает в себя многочисленные шоколадные плитки с ягодными вкусами и очень немногие — с земляными. Однако земляных недостаточно, чтобы считать его заметным признаком, поэтому метод приписывает разницу между оценкой "настоятельно рекомендуемый" и оценкой "не очень рекомендуемый" другим признакам, которые, похоже, различаются чаще, по меньшей мере, локально. Причина этого двояка: локальная окрестность может быть слишком малой, а линейные мо-
Глава 6. Модельно-агностические методы локальной интерпретации 251 дели, учитывая их простоту, находятся на стороне компромисса между систематическим смещением и дисперсией. Эта смещенность только усугубляется тем фактом, что некоторые признаки, такие как ягодный вкус (tastes_berry), могут появляться относительно чаще, чем земляной вкус (tastes_earthy). Существует подход, который позволяет это исправлять, и мы рассмотрим его в следующем разделе. Использование метода LIME для NLP В начале главы мы отложили тренировочный и тестовый наборы данных в сторону с очищенным содержимым всех "вкусовых" столбцов для метода на основе обработки естественного языка (natural language processing, NLP). Можно взглянуть на тестовый набор данных для NLP следующим образом: print(X_test_nlp) Эта строка исходного кода печатает следующее: 1194 77 121 411 1259 roasty nutty rich roasty oddly sweet marshmallow balanced cherry choco sweet floral yogurt creamy burnt nuts woody ... 327 sweet mild molasses bland 1832 intense fruity mild sour 464 roasty sour milk note 2013 nutty fruit sour floral 1190 rich roasty nutty smoke Length: 734, dtype: object Ни одна модель машинного обучения не может поглощать данные в виде текста, поэтому нам нужно превратить их в числовой формат — другими словами, векторизовать их. Для этого существует целый ряд методов. В нашем случае нас не интересует ни положение слов в каждой фразе, ни семантика. Однако нас интересует их относительное возникновение — в конце концов, это было для нас проблемой в предыдущем разделе. По этим причинам метод частоты термина — обратной частоты документа (term frequency — inverse document frequency, TF-IDF) является идеальным, потому что он предназначен для оценивания частоты появления термина (каждого слова) в документе (каждой фразе). Однако он взвешивается в соответствии с его частотой во всем корпусе (все фразы). Используя метод TF-IDF, можно легко векторизовать наши наборы данных с помощью векторизатора TfidfVectorizer из библиотеки scikit-learn. Однако, когда нужно получить баллы TD-IDF, проводят обучение на тренировочном наборе данных только потому, что благодаря этому преобразованные тренировочный и тестовый наборы данных имеют сопоставимую процедуру оценивания каждого термина.
252 Часть II. Освоение методов интерпретации Взгляните на следующий фрагмент исходного кода: vectorizer = TfidfVectorizer(lowercase=False) X_train_nlp_fit = vectorizer.fit_transform(X_train_nlp) X_test_nlp_fit = vectorizer.transform(X_test_nlp) В целях получения представления о том, как выглядит балл TF-IDF, можно поместить имена всех признаков в один столбец кадра данных, а соответствующие им баллы для одного наблюдения — в другой. Поскольку векторизатор создает разреженную матрицу scipy, необходимо конвертировать ее в матрицу NumPy с помощью метода todense(), а затем в массив NumPy с помощью функции asarray(). Этот кадр данных можно отсортировать в убывающем порядке по баллам TD-IDF. Соответствующий фрагмент исходного кода показан ниже: pd.DataFrame({'taste':vectorizer.get_feature_names(),\ 'tf-idf':np.asarray(X_test_nlp_fit[X_test_nlp.index==5].todense())[0]}).\ sort_values(by='tf-idf', ascending=False) Приведенный выше фрагмент порождает результат, показанный на рис. 6.12. Рис. 6.12. Баллы TF-IDF для слов, представленных в наблюдении № 5 И, как видно из рис. 6.12, баллы TD-IDF являются нормализованными значениями в интервале между 0 и 1, и те, которые распространены в корпусе больше всего, имеют более низкое значение. Интересная вещь: мы понимаем, что наблюдение № 5 в нашем табличном наборе данных имело berry=1 из-за малины (raspberry). Использованный нами метод категориального кодирования искал появления слова berry (ягода) независимо от того, совпадало ли оно со всем словом или нет. Это
Глава 6. Модельно-агностические методы локальной интерпретации 253 не проблема, потому что малина — это вид ягоды, и малина не была одним из наших общих вкусов со своим собственным двоичным столбцом. Теперь, когда мы векторизовали наши наборы данных NLP, можно приступить к моделированию. Обучение модели LightGBM LightGBM, как и XGBoost, является еще одним очень популярным и эффективным градиентно-бустинговым каркасом, в котором задействуются ансамбли бустированных деревьев и отыскание расщепления на основе гистограмм. Главные отличия заключаются в алгоритмах метода расщепления. В LightGBM используются градиентная односторонняя выборка (gradient-based one-side sampling, GOSS) и комплектование разреженных признаков с помощью комплектования эксклюзивными признаками (exclusive feature bundling, EFB) по сравнению с принятым в XGBoost более строгим взвешенным квантильным наброском и отысканием расщеплений с учетом разреженности. Еще одна разница заключается в том, как деревья строятся, т. е. сперва в глубину (сверху вниз) в случае XGBoost и сперва лучшие (по всем листьям дерева) в случае LightGBM. Мы не будем вдаваться в подробности принципов работы этих алгоритмов, потому что это может нарушать обсуждаемую тему. Однако важно отметить, что благодаря GOSS модель LightGBM обычно работает даже быстрее, чем модель XGBoost, и, хотя она может терять предсказательную результативность из-за аппроксимаций GOSSрасщеплений, она до некоторой степени ее возвращает благодаря своему подходу на основе трассировки сперва лучших. С другой стороны, объяснимая бустинговая машина (explainable boosting machine, EBM) делает LightGBM идеальной для результативного и эффективного обучения на разреженных признаках, таких как в нашей разреженной матрице X_train_nlp_fit! Это в значительной степени подводит итог тому, почему в данном упражнении мы используем LightGBM. В целях обучения модели LightGBM мы сначала инициализируем модель, установив максимальную глубину дерева (max_depth), скорость усвоения (learning_rate), число бустированных деревьев для обучения (n_estimators), целевую задачу (objective), которая является двоичной классификацией, и — что не менее важно — random_state для обеспечения воспроизводимости. С помощью метода fit() мы тренируем модель на нашем векторизованном наборе тренировочных данных NLP (X_train_nlp_fit) и тех же метках, которые используются для модели SVM (y_train). После тренировки можно оценить качество обучения с помощью метода evaluate_class_mdl(), который мы использовали с SVM. Соответствующий фрагмент исходного кода показан ниже: lgb_mdl = lgb.LGBMClassifier(max_depth=13, learning_rate=0.05, n_estimators=100,\ objective='binary', random_state=rand) fitted_lgb_mdl = lgb_mdl.fit(X_train_nlp_fit, y_train) y_tuple = mldatasets.evaluate_class_mdl(fitted_lgb_mdl, X_train_nlp_fit,\ X_test_nlp_fit, y_train, y_test) y_train_lgb_pred, y_test_lgb_prob, y_test_lgb_pred = y_tuple
254 Часть II. Освоение методов интерпретации Приведенный фрагмент порождает рис. 6.13. Рис. 6.13. Предсказательная результативность модели LightGBM Достигаемая моделью LightGBM результативность (см. рис. 6.13) слегка ниже, чем у SVM (см. рис. 6.3), но она все еще довольно хороша, безопасно выше линии подбрасывания монеты. Комментарии относительно SVM о предпочтении точности перед полнотой в данной модели тоже применимы. Локальная интерпретация по одному предсказанию за раз с использованием текстового объяснителя на основе LIME В целях интерпретирования любого предсказания моделью типа "черный ящик" с помощью LIME необходимо указать соответствующую вашей модели классифика-
Глава 6. Модельно-агностические методы локальной интерпретации 255 торную функцию, такую как predict_proba, и модель будет использовать эту функцию для предсказания переставленных данных в окрестности вашего экземпляра, а затем обучит на нем линейную модель. Экземпляр должен быть в своей числовой форме — другими словами, векторизован. Однако было бы проще, если бы можно было подавать любой произвольный текст, а затем векторизовать его на лету. Это именно то, что может делать для вас конвейер. С помощью функции make_pipeline библиотеки scikit-learn можно определить последовательность оценщиков, преобразовывающих данные, за которой следует функция, способная выполнить с их помощью подгонки модели. В этом случае нам просто нужен векторизатор (vectorizer) для преобразования данных, за которым следует наша модель LightGBM (lgb_mdl), берущая преобразованные данные, как показано в следующем фрагменте исходного кода: lgb_pipeline = make_pipeline(vectorizer, lgb_mdl) Инициализация текстового объяснителя на основе метода LIME (LimeTextExplainer) довольно проста. Все параметры опциональны, но рекомендуется указывать имена ваших классов. Как и в случае с табличным объяснителем на основе LIME (LimeTabularExplainer), необязательный параметр ширины ядра (kernel_width) бывает критически важным, поскольку он определяет размер окресности, и принятое значение по умолчанию бывает не оптимальным, но может быть отрегулировано под каждый экземпляр. Соответствующий исходный код проиллюстрирован ниже: lime_lgb_explainer = LimeTextExplainer(class_names=['Не очень рекомендуемый', 'Настоятельно рекомендуемый']) Объяснение экземпляра с помощью текстового объяснителя с использованием метода LIME (LimeTextExplainer) выполяется по аналогии с соответствующим табличным объяснителем (LimeTabularExplainer). Разница в том, что мы используем конвейер (lgb_pipeline), а предоставляемые нами данные (первый параметр) являются текстовыми, поскольку конвейер может преобразовывать их за нас. Соответствующий фрагмент исходного кода показан ниже: lime_lgb_explainer.explain_instance(X_test_nlp[X_test_nlp.index==5].values[0],\ lgb_pipeline.predict_proba, num_features=4).show_in_notebook(text=True) Рис. 6.14. Текстовое объяснение для наблюдения № 5 (выдающийся) Согласно текстовому объяснителю на основе метода LIME (рис. 6.14), модель LightGBM предсказывает оценку "настоятельно рекомендуемый" для наблюдения № 5 из-за слова caramel (карамельный). По меньшей мере, согласно локальной окрестности raspberry (малиновый) не является существенным фактором.
256 Часть II. Освоение методов интерпретации Теперь давайте сравним интерпретацию для наблюдения № 5 с интерпретацией для наблюдения № 24, как это делалось раньше. Можно использовать тот же исходный код, но просто заменить 5 на 24 следующим образом: lime_lgb_explainer.explain_instance(X_test_nlp[X_test_nlp.index==24].values[0],\ lgb_pipeline.predict_proba, num_features=4).show_in_notebook(text=True) Согласно рис. 6.15, можно сказать, что наблюдение № 24, описанное со вкусом burnt wood earthy choco (жженный древесный земляной шоколадный), имеет оценку "не очень рекомендуемый" из-за слов earthy (земляной) и burnt (жженный). Рис. 6.15. Табличное объяснение для наблюдения № 24 (разочаровывающий) Учитывая, что мы используем конвейер, который может векторизовывать любой произвольный текст, давайте немножко развлечемся! Сначала мы попробуем фразу, состоящую из прилагательных, которые, как мы подозреваем, нашей модели нравятся, затем протестируем фразу с неблагоприятными прилагательными и, наконец, попытаемся использовать слова, с которыми наша модель, должно быть, не знакома: lime_lgb_explainer.explain_instance('creamy rich complex fruity',\ lgb_pipeline.predict_proba, num_features=4).show_in_notebook(text=True) lime_lgb_explainer.explain_instance('sour bitter roasty molasses',\ lgb_pipeline.predict_proba, num_features=4).show_in_notebook(text=True) lime_lgb_explainer.explain_instance('nasty disgusting gross stuff',\ lgb_pipeline.predict_proba, num_features=4).show_in_notebook(text=True) На рис. 6.16 объяснения оказываются верными для слов creamy rich complex fruity (сливочный насыщенный сложный фруктовый) и sour bitter roasty molasses (кислый горький жареный мелассовый), поскольку модель знает, что эти слова являются либо очень благоприятными, либо неблагоприятными. Указанные слова также достаточно распространенные, чтобы их можно было оценивать на локальном уровне. Однако вы ошибаетесь, думая, что предсказание "не очень рекомендуемый" для слов nasty disgusting gross stuff (неприятный отвратительный грубый дряной) имеет какое-то отношение к этим словам. Модель LightGBM не встречала этих слов раньше, поэтому предсказание больше связано с тем, что оценка "не очень рекомендуемый" является мажоритарным классом, т. е. хорошей догадкой, а разреженная матрица для этой фразы имеет одни нули. Следовательно, LIME, вероятно, обнаружил несколько отдаленных точек — если они вообще были — в его окрестности, и поэтому нулевые коэффициенты локальной суррогатной модели метода LIME это отражают.
Глава 6. Модельно-агностические методы локальной интерпретации 257 Рис. 6.16. Произвольные фразы, не входящие в тренировочный или тестовый наборы данных, могут легко объясняться с помощью LIME, если слова находятся в корпусе Опробование SHAP в обработке естественного языка Большинство объяснителей SHAP будут работать с табличными данными. Глубокий объяснитель (DeepExplainer) может работать с текстом, но ограничен моделями глубокого обучения, и, как мы рассмотрим в главе 8, три из них работают с изображениями, включая ядерный объяснитель (KernelExplainer). На самом деле, ядерный объяснитель SHAP был разработан как общецелевой по-настоящему модельноагностический метод, но он не продвигался как вариант для обработки естественного языка (NLP). Легко понять, почему: он медленный, а модели NLP, как правило, очень сложны и имеют сотни — если не тысячи — загружаемых признаков. В подобного рода случаях, когда очередность слов не является фактором, и у вас есть несколько сотен признаков, но верхние 100 присутствуют в большинстве ваших наблюдений, ядерный объяснитель (KernelExplainer) может работать. В дополнение к преодолению медлительности вам нужно будет преодолеть несколько технических препятствий. Одним из них является то, что ядерный объяснитель совместим с конвейером, но ожидает возврата одиночного набора предсказаний. Но LightGBM возвращает два набора, по одному для каждого класса: "не очень рекомендуемый" и "настоятельно рекомендуемый". В целях преодоления этой проблемы можно создать лямбда-функцию (predict_fn), которая включает в свой состав функцию predict_proba, возвращающую эти предсказания только для оценки "настоятельно рекомендуемый". Это показано в следующем ниже фрагменте исходного кода: predict_fn = lambda X: lgb_mdl.predict_proba(X)[:,1]
258 Часть II. Освоение методов интерпретации Второе техническое препятствие связано с несовместимостью SHAP с разреженными матрицами SciPy, и для нашего объяснителя нам понадобятся образцы векторизованных тестовых данных, которые находятся в этом формате. В целях преодоления указанной проблемы можно конвертировать наши данные, находящиеся в формате разреженной матрицы SciPy, в матрицу NumPy, а затем в кадр данных pandas (X_test_nlp_samp_df). Для преодоления любой медлительности можно использовать тот же трюк kmeans, к которому мы прибегали в прошлый раз. Помимо поправок, внесенных для преодоления препятствий, следующий ниже исходный код точно такой же, как и в случае с SHAP, выполнявшимся с моделью SVM: X_test_nlp_samp_df = pd.DataFrame(shap.sample(X_test_nlp_fit, 50).todense()) shap_lgb_explainer = shap.KernelExplainer(\ predict_fn, shap.kmeans(X_train_nlp_fit.todense(), 10)) shap_lgb_values_test = shap_lgb_explainer.shap_values(X_test_nlp_samp_df,\ l1_reg="num_features(20)") shap.summary_plot(shap_lgb_values_test, X_test_nlp_samp_df,\ plot_type="dot", feature_names=vectorizer.get_feature_names()) Рис. 6.17. Сводный график SHAP для модели LightGBM NLP Используя сводный график SHAP на рис. 6.17, можно сказать, что глобально слова creamy, rich, cocoa, fruit, spicy, nutty и berry (сливочный, насыщенный, какао,
Глава 6. Модельно-агностические методы локальной интерпретации 259 фруктовый, пряный, ореховый и ягодный) оказывают положительное влияние на модель в сторону предсказания "настоятельно рекомендуемый". С другой стороны, слова sweet, sour, earthy, hammy, sandy и fatty (сладкий, кислый, земляной, молотый, песчаный и жирный) имеют противоположный эффект. Эти результаты не должны быть совершенно неожиданными, учитывая то, что мы усвоили с помощью нашей предыдущей модели SVM с табличными данными и локальными интерпретациями LIME. С учетом сказанного значения SHAP были выведены из выборок разреженной матрицы, и в них могли отсутствовать детали и, возможно, даже быть частично неправильными, в особенности для недопредставленных признаков. Следовательно, необходимо отнестись к выводам с недоверием, особенно в нижней половине графика. В целях повышения верности интерпретации лучше всего увеличить размер выборки, но, учитывая медлительность ядерного объяснителя, следует стремиться к компромиссу. Теперь, когда мы проверили наши значения SHAP глобально, можно использовать их для локальной интерпретации с помощью силового графика. В отличие от LIME, мы не можем использовать для этого произвольные данные. С SHAP мы ограничены теми точками данных, для которых мы генерировали значения SHAP ранее. Например, давайте возьмем наблюдение № 18 из выборки тестового набора данных следующим образом: print(shap.sample(X_test_nlp, 50).to_list()[18]) Приведенная выше строка исходного кода печатает следующую фразу: woody earthy medicinal Важно отметить, какие слова представлены в наблюдении № 18, потому что кадр данных X_test_nlp_samp_df содержит векторизованное представление. 18-я строка наблюдения в этом кадре данных содержит данные, используемые для генерирования силового графика вместе со значениями SHAP для этого наблюдения и ожидаемым значением для класса, как показано в следующем ниже фрагменте исходного кода: shap.force_plot(shap_lgb_explainer.expected_value,\ shap_lgb_values_test[18,:], X_test_nlp_samp_df.iloc[18,:],\ feature_names=vectorizer.get_feature_names()) На рис. 6.18 представлен силовой график для слов woody earthy medicinal (древесный земляной лекарственный). Как можно судить, earthy и woody сильно влияют на предсказание против оценки "настоятельно рекомендуемый". Слово medicinal в силовом графике не фигурирует, и вместо этого вы получаете нехватку creamy (сливочный) и cocoa (какао) в качестве отрицательных факторов. Как нетрудно себе представить, слово medicinal (лекарственный) является не тем словом, которое используется для описания шоколадных плиток часто, поэтому в выборочном наборе данных было только одно наблюдение, которое его включало. Следовательно, его средний маргинальный вклад в возможные коалиции будет значительно уменьшен. Давайте попробуем еще раз следующим образом: print(shap.sample(X_test_nlp, 50).to_list()[9])
260 Часть II. Освоение методов интерпретации Наблюдение № 9 имеет следующую фразу: intense spicy floral Рис. 6.18. Силовой график SHAP для наблюдения № 18 выборки из тестового набора данных Генерирование силового графика (force_plot) для этого наблюдения такое же, как и раньше, за исключением того, что 18 заменяется на 9. Если выполнить этот исходный код, то будет получен результат, показанный на рис. 6.19. Рис. 6.19. Силовой график SHAP для наблюдения № 9 выборки из тестового набора данных Как следует из рис. 6.19, все слова из указанной фразы представлены на силовом графике: floral (цветочный) и spicy (пряный), подталкивающие в сторону "настоятельно рекомендуемый", и intense (интенсивный) в сторону "не очень рекомендуемый". Итак, теперь зная, как выполнять табличные интерпретации и NLP-интерпретации с помощью SHAP, как это соотносится с LIME? Сравнение SHAP с LIME Как вы уже заметили, и SHAP, и LIME имеют пределы, но у них также есть сильные стороны. SHAP основан на теории игр и аппроксимирует значения Шепли, поэтому его значения SHAP имеют какой-то смысл. Они обладают отличными свойствами, такими как аддитивность, эффективность и замещаемость, которые делают его сопоставимым, но нарушают свойство фиктивности. Однако он более подходит для глобальных интерпретаций, и один из его самых модельно-агностических объяснителей, KernelExplainer, является болезненно медлительным. Ядерный объяснитель (KernelExplainer) также имеет дело с пропущенными значениями, используя случайные значения, которые могут придавать маловероятным наблюдениям слишком большой вес. Метод LIME является быстрым, модельно-агностическим и адаптируемым ко всем видам данных. Однако он не основан на строгих и сопоставимых принципах, зато базируется на интуитивно воспринимаемом факте, что соседи похожи. По этой
Глава 6. Модельно-агностические методы локальной интерпретации 261 причине для оптимального определения размера окрестности может потребоваться сложная регулировка параметров, и даже тогда метод подходит только для локальных интерпретаций. Миссия выполнена Миссия состояла в том, чтобы понять, почему одна плитка шоколада вашего клиента оценивается как выдающийся шоколад, а другая — как разочаровывающий. Подход состоял в применении интерпретации моделей машинного обучения, чтобы прийти к следующим выводам.  Согласно SHAP на табличной модели выдающийся шоколад обязан этой оценке своим ягодным вкусом и процентным содержанием какао, равным 70%. С другой стороны, неблагоприятная оценка разочаровывающего шоколада обусловлена в основном его земляным вкусом и страной происхождения какао-бобов (Other). Дата оценки играет меньшую роль, но похоже, что шоколадные плитки, рассмотренные в период 2013–2015 годов, имели преимущество.  LIME подтверждает, что cocoa_percent<=70 является желательным свойством и что помимо ягодного (berry), сливочный (creamy), какао (cacao) и насыщенный (rich) вкусы являются благоприятными, тогда как сладкий (sweet), кислый (sour) и мелассовый (molasses) — неблагоприятными.  Общность между обоими методами, использующими табличную модель, заклю- чается в том, что, несмотря на большое число атрибутов, не связанных со вкусом, вкусовые признаки являются одними из наиболее заметных. Поэтому уместно интерпретировать слова, используемые для описания каждой плитки шоколада, с помощью модели NLP.  Выдающаяся плитка шоколада была представлена фразой oily nut caramel raspberry (маслянистый ореховый карамельный малиновый), из которых, согласно текстовому объяснителю на основе LIME (LIMETextExplainer), слово caramel имеет положительный эффект, а oily — отрицательный. Два других слова нейтральны. С другой стороны, разочаровывающая плитка была представлена словами burnt wood earthy choco (жженный древесный земляной шоколад), из которых burnt и earthy — неблагоприятны, а два других — благоприятны.  Несоответствия между вкусами в табличной и NLP-интерпретациях связаны с наличием менее представленных вкусов, включая raspberry (малиновый), который не столь распространен, как berry (ягодный).  Согласно глобальному объяснению SHAP модели NLP, слова creamy, rich, cocoa, fruit, spicy, nutty и berry (сливочный, насыщенный, какао, фруктовый, пряный, ореховый и ягодный) оказывают положительное влияние на модель в сторону предсказания оценки "настоятельно рекомендуемый". С другой стороны, слова sweet, sour, earthy, hammy, sandy и fatty (сладкий, кислый, земляной, молотый, песчаный и жирный) имеют противоположный эффект.
262 Часть II. Освоение методов интерпретации С учетом этих представлений о том, какие характеристики и вкусы шоколадных плиток считаются членами Манхэттенского шоколадного общества менее привлекательными, клиент может внести изменения в свои формулы шоколадных плиток, чтобы привлечь более широкую аудиторию, при условии, что допущение о том, что эта группа представляет их целевую аудиторию, верна. С совершенной очевидностью можно утверждать, что такие слова, как earthy и burnt (земляной и жженный), не являются благоприятными словами для ассоциирования с шоколадными плитками, тогда как caramel (карамельный) таким является. Следовательно, мы могли бы прийти к такому выводу без машинного обучения! Но, во-первых, вывод, не подтвержденный данными, был бы мнением, а, вовторых, контекст — это самое главное. Кроме того, на людей нельзя всегда полагаться в том, они будут ставить одну точку в свой контекст объективно — в особенности учитывая, что это делается среди тысяч записей! Кроме того, локальная модельная интерпретация связана не только с объяснением одного предсказания, потому что она связана с тем, как модель делает все предсказания, но, что важнее, с тем, как она делает предсказания для аналогичных точек — другими словами, в локальной окрестности! В следующей главе мы подробно расскажем о том, что значит быть в локальной окрестности, рассмотрев общности (якоря) и неувязки (контрфакты), которые можно там найти. Резюме Прочитав эту главу, вы должны научиться применять ядерный объяснитель (KernelExplainer) SHAP, а также его график решения и силовой график для проведения локальных интерпретаций. Вы также должны научиться делать то же самое с экземпляром объяснителя LIME для табличных и для текстовых данных. Наконец, вы должны понимать сильные и слабые стороны ядерного объяснителя SHAP и метода LIME. В следующей далее главе мы узнаем о том, как создавать еще более человеко-интерпретируемые объяснения модельных решений, такие как "если условия X удовлетворяются, тогда исходом является Y". Источник набора данных  Brelinski B. Manhattan Chocolate Society. — URL: http://flavorsofcacao.com/ mcs_index.html. (Брелински Б. Манхэттенское шоколадное общество.) Справочные материалы  Platt J. C., 1999. Probabilistic Outputs for Support Vector Machines and Comparisons to Regularized Likelihood Methods / Advances in Large Margin Classifiers. — MIT Press, 1999. — URL: https://www.cs.colorado.edu/~mozer/Teaching/syllabi/6622/ papers/Platt1999.pdf. (Платт Дж. С. Вероятностные результаты для опорновекторных машин и сравнения с регуляризованными методами правдоподобия. Достижения в классификаторах с большой маржой.)
Глава 6. Модельно-агностические методы локальной интерпретации 263  Lundberg S., Lee S. A Unified Approach to Interpreting Model Predictions // Ad- vances in Neural Information Processing Systems, 30. — 2017 — URL: https://arxiv.org/abs/1705.07874 (documentation for SHAP https://github.com/ slundberg/shap). (Лундберг С., Ли С. Единый подход к интерпретации модельных предсказаний.)  Ribeiro M. T., Singh S., Guestrin C. Why Should I Trust You? Explaining the Predic- tions of Any Classifier. Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. — 2016. — URL: http://arxiv.org/abs/1602.04938. (Рибейро М. Т., Сингх С., Гострин С. Почему я должен вам доверять? Объяснение предсказаний любого классификатора.)  Ke G., Meng Q., Finley T., Wang T., Chen W., Ma W., Ye Q., Liu T. LightGBM: a highly efficient gradient boosting decision tree // Advances in Neural Information Processing Systems. — 2017. — VOL. 30. — P. 3149–3157. — URL: https://papers.nips.cc/paper/6907-lightgbm-a-highly-efficient-gradient-boostingdecision-tree. (Кэ Г., Мэн К., Финли Т., Ван Т., Чен В., Ма В., Е К., Лю Т. LightGBM: высокоэффективное градиентно-бустированное дерево решений)
7 Якорные и контрфактические объяснения В предыдущих главах мы узнали, как приписывать модельные решения признакам и их взаимодействию с помощью современных методов глобальной и локальной модельной интерпретации. Однако определить или интерпретировать границы решений с помощью этих методов не всегда легко. Разве плохо иметь возможность выводить человеко-интерпретируемые правила из методов модельной интерпретации? В этой главе мы рассмотрим несколько методов человекоинтерпретируемой, локальной модельной интерпретации только для классификации. Сначала мы научимся использовать правила с ограниченным диапазоном, именуемые якорями, для объяснения сложных моделей с утверждениями, наподобие "если условия X удовлетворяются, тогда исходом является Y". Затем рассмотрим контрфактические объяснения, которые следуют форме "если условия Z не соблюдаются, тогда исходом является не Y". Наконец, мы объясним, как контрастивные объяснения объединяют якоря и контрфакты при помощи чего-то наподобие "Y является исходом, если соблюдаются условия X, а условия Z не соблюдаются". Вот главные темы, которые будут охвачены в этой главе:  что такое якорные объяснения;  анализ контрфактических объяснений;  сравнение с методом контрастивных объяснений. Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, catboost, tensorflow, rulefit, matplotlib, seaborn, alibi, shap и witwidget. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/InterpretableMachine-Learning-with-Python/tree/master/Chapter07. Миссия В США последние два десятилетия частные компании и некоммерческие организации разрабатывают инструменты диагностирования криминальных рисков, в большинстве которых используются статистические модели. Поскольку многие штаты больше не могут позволить себе содержать многочисленных заключенных; эти ме-
Глава 7. Якорные и контрфактические объяснения 265 тоды приобрели популярность, ориентируя судей и коллегии по условно-досрочному освобождению на каждом этапе тюремной системы. Однако они нередко делают больше, чем просто ориентируют решения. Они формируют их для лиц, принимающих решения в системе правосудия, потому что исходят из правильности. Хуже всего то, что при этом точно неизветно, как проводилась диагностика. Риск обычно рассчитывают с помощью модели белого ящика, но на практике используется модель типа "черный ящик", потому что она является проприетарной. Предсказательная результативность тоже относительно низка: средние баллы AUC по девяти инструментам варьируются от 0,57 до 0,74. При этом валидность и систематические смещения редко рассматриваются, в особенности учреждениями уголовного правосудия. Несмотря на то что традиционные статистические методы по-прежнему являются нормой для моделей уголовного правосудия, некоторые исследователи в целях повышения результативности предлагают использовать более сложные модели, такие как случайный лес, с более крупными наборами данных. Будучи далеко не научной фантастикой, почерпнутой из кинофильма "Особое мнение" ("Minority Report") или телесериала "Черное зеркало" ("Black Mirror"), в других странах оценивание людей на основе вероятности проявления их антисоциального или даже антипатриотического поведения с помощью больших данных и машинного обучения уже стало реальностью. Поскольку все больше решений на основе искусственного интеллекта пытаются делать судьбоносные предсказания о нас с помощью наших данных, объективность должна диагностироваться надлежащим образом, а все ее этические и практические последствия необходимо адекватно обсуждать. Глава 1 была посвящена теме объективности и тому, каким образом она является неотъемлемой концепцией интерпретации машинного обучения. Объективность можно оценивать в любой модели, но делать это особенно сложно тогда, когда она связана с человеческим поведением. Взаимодействие между психологическими, неврологическими и социологическими факторами человека чрезвычайно сложное. В контексте предсказания преступного поведения всё сводится к установлению факторов, которые потенциально могут быть ведущими в преступлении, и выяснению, как эти факторы взаимодействуют друг с другом. Квантитативные криминалисты всё еще обсуждают лучшие предсказатели преступности и их первопричины. Они также спорят, этично ли вообще обвинять преступника в этих факторах. К счастью, демографические черты, такие как раса, пол и национальность, больше не используются в диагностике криминального риска. Но это не означает, что указанные методы больше не являются систематически смещенными по расовому признаку. Ученые признают проблему и предлагают решения. В этой главе будут рассмотрены расовые смещенности в одном из наиболее широко используемых инструментов диагностики риска. Учитывая чувствительную и релевантную природу указанной тематики, было важно обеспечить толику контекста, связанного с инструментами диагностики криминального риска и тем, как машинное обучение и объективность со всем этим связаны. Мы не будем вдаваться в под-
266 Часть II. Освоение методов интерпретации робности, но нельзя недооценивать жизненную важность контекста, чтобы оценить то, как машинное обучение могло бы увековечивать структурное неравенство и необъективные смещенности. Теперь давайте познакомимся с вашей миссией в этой главе! Необъективная смещенность в диагностиках риска рецидивизма Криминальная журналистка пишет статью о том, как некий обвиняемый, афроамериканец, был задержан до суда. Система под названием Профилирование исправительного управления правонарушителями для альтернативных санкций (Correctional Offender Management Profiling for Alternative Sanction, COMPAS) посчитала, что существует риск рецидивизма. Рецидивизм — в уголовном праве это повторяемость преступного поведения. И балл этой системы убедил судью в том, что обвиняемого нужно было задержать до суда, настолько, что судья даже не стал рассматривать никаких других аргументов или свидетельских показаний. Обвиняемый находился в заключении в течение многих месяцев и в ходе судебного разбирательства был признан невиновным. Прошло более 5 лет со времени суда, и оправданный ни разу не был обвинен в каком-либо преступлении. Можно сказать, что предсказание рецидивизма было ложноположительным. Журналистка обратилась к вам, потому что она хотела бы выяснить с помощью науки о данных, существовала ли в данном конкретном случае необъективная смещенность. Диагностика риска проводится системой COMPAS с использованием 137 вопросов (https://www.documentcloud.org/documents/2702103-Sample-RiskAssessment-COMPAS-CORE.html). К ним относятся такие вопросы, как:  Основываясь на наблюдениях специалиста, проводящего досмотр, можно ли сделать вывод, является этот человек подозреваемым или признанным членом банды?  Как часто вы переезжали за последние 12 месяцев?  Как часто у вас едва хватает денег, чтобы сводить концы с концами?  Вопросы психометрической шкалы Лайкерта, такие как "я никогда ни о чем не грустил в своей жизни", подобные тем, которые рассматриваются в главе 4. Несмотря на то что ни один из вопросов не связан с расовой принадлежностью, многие из этих вопросов могут с ней коррелировать. Не говоря уже о том, что в некоторых случаях они бывают скорее вопросом мнения, чем фактом, и, следовательно, возможна систематическая смещенность. Журналистка не может предоставить вам ни ответы на 137 вопросов, ни модель COMPAS, потому что эти данные для общественности недоступны. Тем не менее данные о демографических показателях и рецидивизме всех ответчиков по одному и тому же округу штата Флорида имеются.
Глава 7. Якорные и контрфактические объяснения 267 Подход Вы решили сделать следующее.  Обучить косвенные модели. У вас нет ни изначальных признаков, ни модели, но не всё потеряно, потому что у вас есть баллы системы COMPAS — метки. И у нас также есть релевантные признаки для задачи, которые можно соединить с этими метками с помощью моделей. Аппроксимируя модель системы COMPAS через косвенные модели (или прокси-модели), можно диагностировать необъективность меток. В этой главе мы будем обучать модель CatBoost и нейросетевую модель.  Использовать якорные объяснения. Этот метод позволит понять, почему кос- венная модель делает те или иные предсказания, применив ряд правил, именуемых якорями, которые определяют расположение границ решений. Указанные границы имеют отношение к нашей миссии, т. к. мы хотим знать причину, по которой ответчику был ошибочно предсказан рецидивизм. Мы имеем дело с приближенной границей изначальной модели, но в ней все еще есть доля правды.  Применить контрфактические объяснения. Концепция, альтернативная яко- рям, заключается в понимании того, почему похожие точки данных находятся на противоположных сторонах границы решения, что особенно заметно при обсуждении вопросов необъективности. Мы будем использовать несмещенный метод, чтобы отыскивать контрфакты, а затем прибегнем к помощи инструмента WIT (What-If Tool) для дальнейшего анализа контрфактов и объективности.  Использовать метод контрастивных объяснений (contrastive explanations method, CEM). В дополнение к якорному и контрфактическому методам вы займетесь методом CEM, который позволяет понять минимальные требования к тому, чтобы считать ответчика подверженным высокому риску рецидивизма, проливая некоторый свет не только на объективность, но и на надежность, необходимую для того, чтобы объективность была достижимой. Подготовительные работы Исходный код примера этой главы можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter07/Recidivism_part1.ipynb. Загрузка библиотек В целях выполнения этого примера вам необходимо инсталлировать следующие ниже библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования набором данных;
268 Часть II. Освоение методов интерпретации  sklearn (scikit-learn), catboost и tensorflow для разбивки данных на подвыборки и обучения моделей;  matplotlib, seaborn, alibi, shap и witwidget для визуализации интерпретаций. Сначала необходимо их все загрузить следующим образом: import math import mldatasets import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn import metrics from catboost import CatBoostClassifier import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import matplotlib.pyplot as plt import seaborn as sns from alibi.utils.mapping import ohe_to_ord, ord_to_ohe from alibi.explainers import AnchorTabular from alibi.explainers import CEM from alibi.explainers import CounterFactualProto import shap import witwidget from witwidget.notebook.visualization import WitWidget, WitConfigBuilder Давайте проверим, что TensorFlow загрузил правильную версию, воспользовавшись инструкцией print(tf.__version__). Версия должна быть 2.0 или выше. Мы также должны отключить немедленное исполнение и убедиться, что это сработало, воспользовавшись приведенной ниже инструкцией. Результат должен показать False: tf.compat.v1.disable_eager_execution() print('Немедленное исполнение включено:', tf.executing_eagerly()) Изучение проблемы и подготовка данных Вот так мы загружаем данные в кадр данных, который назовем recidivism_df: recidivism_df = mldatasets.load("recidivism-risk", prepare=True) Должно быть почти 15 000 записей и 23 столбца. Это можно подтвердить с помощью библиотечного метода info(): recidivism_df.info() Следующий ниже результат соответствует требованиям. Все признаки являются числовыми без пропущенных значений, а категориальные признаки уже были закодированы за нас с одним активным состоянием: <class 'pandas.core.frame.DataFrame'> Int64Index: 14788 entries, 0 to 18315
Глава 7. Якорные и контрфактические объяснения 269 Data columns (total 23 columns): Column Non-Null Count Dtype --- ------ # -------------- ----- 0 age 14788 non-null int8 1 juv_fel_count 14788 non-null int8 2 juv_misd_count 14788 non-null int8 3 juv_other_count 14788 non-null int64 4 priors_count 14788 non-null int8 5 is_recid 14788 non-null int8 6 sex_Female 14788 non-null uint8 7 sex_Male 14788 non-null uint8 8 race_African-American 14788 non-null uint8 9 race_Asian 14788 non-null uint8 10 race_Caucasian 14788 non-null uint8 11 race_Hispanic 14788 non-null uint8 12 race_Native American 14788 non-null uint8 13 race_Other 14788 non-null uint8 14 c_charge_degree_(F1) 14788 non-null uint8 15 c_charge_degree_(F2) 14788 non-null uint8 16 c_charge_degree_(F3) 14788 non-null uint8 17 c_charge_degree_(F7) 14788 non-null uint8 18 c_charge_degree_(M1) 14788 non-null uint8 19 c_charge_degree_(M2) 14788 non-null uint8 20 c_charge_degree_(MO3) 14788 non-null uint8 21 c_charge_degree_Other 14788 non-null uint8 22 compas_score 14788 non-null int64 dtypes: int64(2), int8(5), uint8(16) memory usage: 649.9 KB Словарь данных Существует только девять признаков, но из-за категориальной кодировки они становятся 22 столбцами.  age — непрерывный; возраст ответчика (между 8 и 9).  juv_fel_count — непрерывный; число уголовных преступлений в несовершенно- летнем возрасте (от 0 до 2).  juv_misd_count — непрерывный; число малозначительных проступков в несовер- шеннолетнем возрасте (от 0 до 1).  juv_other_count — непрерывный; число осуждений в несовершеннолетнем воз- расте, которые не являются ни уголовными преступлениями, ни малозначительными проступками (от 0 до 1).
270 Часть II. Освоение методов интерпретации  priors_count — непрерывный; число предшествующих судимостей или ранее со- вершенных преступлений (от 0 до 13).  is_recid — двоичный; наличие рецидивизма у ответчика в течение 2 лет (1 — "да", 0 — "нет").  sex — категориальный; пол ответчика.  race — категориальный; раса ответчика.  c_charge_degree — категориальный; степень преступления, в котором ответчику предъявлено обвинение в настоящее время. В США классифицируются преступные посягательства как фелонии (уголовные преступления), мисдиминоры (малозначительные проступки) и инфракции (нарушения), от самых серьезных до самых незначительных. Их классифицируют в виде степеней, которые варьируются от 1-й (наиболее серьезных) до 3-й или 5-й (наименее тяжких). Однако, несмотря на то что это является стандартом для федеральных фелоний, признак адаптируется к законодательству штата на уровне штата. В отношении фелоний штат Флорида (http://www.dc.state.fl.us/pub/scoresheet/cpc_manual.pdf) имеет систему уровней, которая определяет тяжесть преступления независимо от степени, и она варьируется от 10 (наиболее тяжких) до 1 (наименее тяжких). Категории этого признака имеют префикс F для фелоний и M для мисдиминоров. За ними следует число, которое определяет уровень для фелоний и степень для мисдиминоров.  compas_score — двоичный, система COMPAS оценивает ответчиков как имеющих "низкий", "средний" либо "высокий" риск. На практике лица, принимающие решения, нередко трактуют "средний" как "высокий", поэтому данный признак был конвертирован в двоичный, дабы отразить такое поведение: 1 — высокий/ средний риск, 0 — низкий риск. Оценка предсказательного смещения с помощью матриц путаницы В наборе данных есть два двоичных признака. Первый — это сделанное системой COMPAS предсказание риска рецидивизма (compas_score). Второй — наличие рецидивизма ответчика в течение 2 лет (is_recid) — является эмпирической истиной, потому что показывает, что произошло в течение 2 лет после ареста ответчика. Точно так же, как при генерировании предсказаний любой моделью относительно ее тренировочных меток, с этими двумя признаками можно строить матрицы путаницы. В библиотеке scikit-learn их можно генерировать с помощью функции confusion_matrix(cf_matrix), а затем на их основе в библиотеке seaborn можно создавать теплокарту. Вместо того чтобы строить график числа истинноотрицательных (true negatives, TN), ложноположительных (false positives, FP), ложноотрицательных (false negatives, FN) и истинноположительных (true positives, TP) классификаций, можно построить график процентов, используя для этого простое деление (cf_matrix/np.sum(cf_matrix)).
Глава 7. Якорные и контрфактические объяснения 271 Другие параметры функции heatmap помогают только с форматированием: cf_matrix = metrics.confusion_matrix(recidivism_df.is_recid,\ recidivism_df.compas_score) sns.heatmap(cf_matrix/np.sum(cf_matrix), annot=True, fmt='.2%',\ cmap='Blues', annot_kws={'size':16}) Приведенный выше исходный код производит рис. 7.1. Правый верхний угол равен ложноположительным классификациям (FP), которые составляют почти одну пятую всех предсказаний, а вместе с ложноотрицательными классификациями (FN) в левом нижнем углу они составляют более двух третей. Рис. 7.1. Матрица путаницы между предсказываемым риском рецидивизма (compas_score) и эмпирической истиной (is_recid), т. е. наличием рецидивизма ответчика в течение 2 лет после ареста Рисунок 7.1 говорит о том, что предсказательная результативность модели COMPAS не очень хороша, в особенности если исходить из того, что лица, принимающие решения в области уголовного правосудия, принимают диагностические величины среднего или высокого риска за чистую монету. Он также говорит о том, что ложноположительные и ложноотрицательные классификации происходят с одинаковой частотой. Тем не менее простые визуализации, такие как матрица путаницы, затуманивают предсказательные диспаритеты между подгруппами популяции. Можно быстро сравнить диспаритеты между двумя подгруппами, которые исторически трактовались системой уголовного правосудия США по-разному. С этой целью мы сначала подразделяем наш кадр данных на два кадра: один для кавказцев (recidivism_c_df), а другой для афроамериканцев (recidivism_aa_df). Затем можно сгенерировать матрицы путаницы для каждого кадра и вывести их на графике одновременно, используя следующий исходный код: recidivism_c_df = recidivism_df[recidivism_df['race_Caucasian'] == 1] recidivism_aa_df = recidivism_df[recidivism_df['race_African-American'] == 1]
272 Часть II. Освоение методов интерпретации _ = mldatasets.compare_confusion_matrices(recidivism_c_df.is_recid,\ recidivism_c_df.compas_score,\ recidivism_aa_df.is_recid,\ recidivism_aa_df.compas_score,\ 'Caucasian', 'African-American', compare_fpr=True) Приведенный выше фрагмент исходного кода сгенерировал рис. 7.2. При беглом взгляде можно сказать, что матрица путаницы для кавказцев, похоже, была перевернута на 90 градусов и сформировала матрицу путаницы для афроамериканцев, и даже при таком раскладе она имеет меньшую необъективность. Обратите пристальное внимание на разницу между ложноположительными и ложноотрицательными классификациями. Вероятность того, что классификация для ответчика из числа кавказцев будет ложноположительной, более чем на половину выше вероятности того, что она будет истинноотрицательной, но для афроамериканца она на несколько процентных пунктов более вероятна. Другими словами, чернокожий ответчик, который не проявляет рецидивов, предсказывается как источник риска рецидивизма более чем в половине случаев. Рис. 7.2. Сравнение матриц путаницы для предсказываемого риска рецидива (compas_score) и эмпирической истины (is_recid) между афроамериканцами (African-American) и кавказцами (Caucasian) в наборе данных Вместо изучения матрицы путем пристального рассмотрения графиков можно измерить частоту ложноположительных (false positive rate, FPR) классификаций, т. е. соотношение между этими двумя мерами ( FP  FP  FN  ). Затем можно сравнить частоту ложноположительных классификаций для обеих групп и разделить между двумя, чтобы оценить относительную разницу. Чем выше это соотношение
Глава 7. Якорные и контрфактические объяснения 273 между частотами ложноположительных классификаций, тем больше будет необъективность, т. к. это означает, что одна группа классифицируется ошибочно, как проявляющая рецидивизм чаще. Подготовка данных Прежде чем перейти к построению моделей и их интерпретации, мы должны сделать последний шаг. Поскольку мы имеем аргумент prepare=True для загрузки данных, от нас сейчас требуется лишь разбить данные на тренировочный/тестовый наборы. Как обычно, очень важно установить случайные состояния, чтобы все ваши находки были воспроизводимыми. Затем мы устанавливаем целевую переменную y (compas_score) и переменную X как любой другой признак, за исключением наличия рецидивизма ответчика в течение 2 лет (is_recid), потому что это эмпирическая истина. Наконец, как и раньше, мы разбиваем y и X на тренировочную и тестовую выборки: rand = 9 np.random.seed(rand) tf.random.set_seed(rand) y = recidivism_df['compas_score'] X = recidivism_df.drop(['compas_score', 'is_recid'], axis=1).copy() X_train, X_test, y_train, y_test =\ train_test_split(X, y, test_size=0.2, random_state=rand) А теперь начнем! Построение моделей Давайте быстро натренируем пару моделей, которые мы будем использовать на протяжении всей этой главы. Косвенные модели (или прокси-модели) — это средство для эмулирования результата на выходе из модели черного ящика, подобно глобально-суррогатным моделям, которые мы рассмотрели в главе 5. Разве это одно и то же? В машинном обучении термины "суррогатный" и "косвенный" (прокси) часто используются взаимозаменяемо. Однако семантически суррогатность относится к подмене, а косвенность больше относится к представлению. Поэтому мы называем эти модели косвенными, чтобы подчеркнуть, что у нас нет точных тренировочных данных. Следовательно, вы представляете изначальную модель только по той причине, что не можете ее подменить. По той же причине, в отличие от интерпретации с суррогатами, которая лучше всего обеспечивается более простыми моделями, косвенная модель лучше всего подходит для сложных моделей, которые могут компенсировать разницу в тренировочных данных сложностью. Сначала давайте рассмотрим классификатор CatBoost. Для тех из вас, кто не знаком с методом CatBoost, сообщим, что он представляет собой метод на основе эф-
274 Часть II. Освоение методов интерпретации фективных бустированных ансамблированных деревьев. Он похож на LightGBM, за исключением того, что в нем используется новый технический прием, именуемый минимально-дисперсной выборкой (minimal variance sampling, MVS) вместо градиентной односторонней выборки (gradient-based one-side sampling, GOSS). В отличие от LightGBM, он выращивает деревья в сбалансированном ключе. Указанный классификатор называется CatBoost потому, что он может кодировать категориальные признаки автоматически и особенно устойчив с точки зрения переобучения, с несмещенной трактовкой категориальных признаков и классовых дисбалансов. Не вдаваясь в подробности, отметим, что он был выбран для этого примера по указанным выше причинам. Для библиотечного класса CatBoostClassifier, который является древовидным модельным классом, можно указать максимальное значение глубины (depth). Мы устанавливаем относительно высокое значение скорости усвоения (learning_rate) и более низкое значение итераций (iterations), по умолчанию 1000. После применения метода fit() на модели можно оценить результаты с помощью функции evaluate_class_mdl: cb_mdl = CatBoostClassifier(iterations=500, learning_rate=0.5, depth=8) fitted_cb_mdl = cb_mdl.fit(X_train, y_train, verbose=False) y_train_cb_pred, y_test_cb_prob, y_test_cb_pred =\ mldatasets.evaluate_class_mdl(fitted_cb_mdl, X_train, X_test, y_train, y_test) Результат работы функции evaluate_class_mdl для нашей модели CatBoost можно оценить на рис. 7.3. Справедливости ради надо сказать, что мы больше заботимся о ложноположительных классификациях (FP), чем о ложноотрицательных (FN), потому что необъективнее сажать в тюрьму невинного человека, чем оставлять виновного на свободе. Поэтому необходимо стремиться к более высокой точности, чем полноте. На рис. 7.3 этот факт подтверждают ROC-кривая и ROC-AUC и MCC. Давайте теперь обучим нейронную сеть прямого распространения. Прежде всего, мы ее создаем (keras.Sequential) с одним скрытым слоем (layers.Dense) с семью узлами и сигмоидой (sigmoid) в выходном слое, потому что эта задача является двоично-классификационной. Затем мы применяем методы compile() и fit() на этой модели. Наконец, мы используем функцию evaluate_class_mdl, чтобы оценить предсказания: fitted_nn_mdl = keras.Sequential([\ tf.keras.Input(shape=[len(X_train.keys())]),\ layers.Dense(7, activation='relu'),\ layers.Dense(1, activation='sigmoid') ]) fitted_nn_mdl.compile(loss='mean_squared_error', optimizer='adam') nn_history = fitted_nn_mdl.fit(X_train.values, y_train.values, epochs=12,\
Глава 7. Якорные и контрфактические объяснения 275 batch_size=32, validation_split=0.2, verbose=0) y_tuple = mldatasets.evaluate_class_mdl(fitted_nn_mdl, X_train, X_test,\ y_train, y_test) y_train_nn_pred, y_test_nn_prob, y_test_nn_pred = y_tuple Рис. 7.3. Предсказательная результативность модели CatBoost На рис. 7.4 показан результат работы приведенного выше исходного кода. Предсказательная результативность обеих моделей неплоха, учитывая, что они являются косвенными моделями, предназначенными только для аппроксимирования реальной ситуации с помощью разных, но связанных между собой данных.
276 Часть II. Освоение методов интерпретации Рис. 7.4. Предсказательная результативность нейросетевой модели Знакомство с "интересующим нас экземпляром" Журналистка обратилась к вам с одним случаем: ответчик — из числа афроамериканцев, которому был ложно предсказан рецидивизм. Это случай № 5231, и он является главным интересующим вас экземпляром. Поскольку наше внимание сосредоточено на расовых систематических смещениях, нам бы хотелось сравнить их с аналогичными примерами, но с разными расами. С этой целью мы нашли случай № 10127 (кавказец) и случай № 2726 (латиноамериканец). Можно взглянуть на данные по всем трем случаям. Поскольку мы будем продолжать ссылаться на эти случаи на протяжении всей главы, давайте сначала сохраним индексы вариантов с афроамериканцем (idx1), латиноамериканцем (idx2) и кавказцем (idx3). Затем возьмем подмножество тестового набора данных по этим индек-
Глава 7. Якорные и контрфактические объяснения 277 сам. Поскольку необходимо убедиться, что наши предсказания совпадают, мы выполним конкатенацию этого подмножества тестовых данных с истинными метками (y_test) и предсказаниями CatBoost (y_test_cb_pred): idx1 = 5231 idx2 = 2726 idx3 = 10127 eval_idxs = X_test.index.isin([idx1, idx2, idx3]) X_test_evals = X_test[eval_idxs] eval_compare_df = pd.concat([\ pd.DataFrame({'y':y_test[eval_idxs]}, index=[idx3, idx2, idx1]),\ pd.DataFrame({'y_pred':y_test_cb_pred[eval_idxs]}, index=[idx3, idx2, idx1]),\ X_test_evals], axis=1).transpose() eval_compare_df Приведенный выше исходный код создает кадр данных на рис. 7.5. По нему можно судить, что предсказания соответствуют истинным меткам, и интересующий нас главный экземпляр был единственным, предсказанным как средний или высокий риск рецидивизма. Помимо расы, другими различиями являются степень преступления, в котором ответчику предъявлено обвинение в настоящее время (c_charge_degree), и одна незначительная разница в возрасте. Рис. 7.5. Наблюдения № 5231, 10127 и 2726 с выделенными значениями признаков, в которых имеются различия
278 Часть II. Освоение методов интерпретации На протяжении всей этой главы мы будем уделять пристальное внимание этим разницам, чтобы выяснить, насколько большую роль они сыграли в формировании различий предсказаний. Все методы, которые мы будем рассматривать, внесут свой вклад в концепцию определяющих или изменяющих факторов получения решения косвенной модели и, возможно, модели COMPAS в расширенном смысле. Теперь, когда мы завершили настройку, продолжим движение вперед с привлечением методов интерпретации. Якорные объяснения В главе 6 мы узнали, что метод LIME тренирует локальную суррогатную модель (в частности, взвешенную разреженно-линейную модель) на перетасованной версии вашего набора данных в окрестности с интересующим вас экземпляром. В результате вы аппроксимируете границу локального решения, которая помогает вам интерпретировать модельное предсказание для нее. Как и метод LIME, якоря тоже выводятся из модельно-агностической пертурбационной стратегии. Однако они касаются не границы решения, а участка решения. Якоря также известны как правила с ограниченным диапазоном, потому что в них перечисляется несколько правил принятия решения, применимых к вашему экземпляру и его перетасованной окрестности. Эта окрестность также называется пертурбационным пространством (или пространством возмущений). Важной деталью является то, в какой степени к нему применяются правила. Эта степень называется прецизионностью. Представьте себе окрестность вокруг вашего экземпляра. Вы ожидаете, что чем ближе вы будете подходить к своему экземпляру, тем более похожие предсказания будут иметь точки, верно? Так, если у вас есть правила принятия решения, определяющие эти предсказания, то чем меньше область вокруг вашего экземпляра, тем прецизионнее ваши правила. Эта концепция называется охватом, который представляет собой процент вашего пертурбационного пространства, который дает конкретную прецизионность. В отличие от метода LIME, якоря не выполняют подгонку локальной суррогатной модели, чтобы объяснять предсказание вашего выбранного экземпляра. Вместо этого они исследуют возможные кандидатные правила принятия решения, используя алгоритм нижней и верхней границ уверенности дивергенции Кульбака — Лейблера (Kullback — Leibler divergence lower and upper confidence bounds, KLLUCB), который выведен из алгоритма многорукого бандита (multi-armed bandit, MAB). Алгоритмы многорукого бандита — это семейство алгоритмов обучения с подкреплением, позволяющих максимизировать выигрыш при ограниченных ресурсах с целью поиска всех неизвестных возможностей. Указанный алгоритм возник из понимания того, как игроки могут максимизировать свои выигрыши, играя на нескольких автоматах в казино. Этот алгоритм называется многоруким бандитом, потому что игровые автоматы получили известность как однорукие бандиты. Тем не
Глава 7. Якорные и контрфактические объяснения 279 менее игроки не знают, какая именно машина принесет наибольший выигрыш, не могут пробовать играть на всех сразу и имеют конечные средства. Хитрость заключается в том, чтобы научиться балансировать между исследованием (пробовать неизвестные игровые автоматы) и эксплуатацией (используя те, которые, по вашему мнению, перспективнее для выигрыша). В случае с якорями каждый игровой автомат является потенциальным правилом принятия решения, и выигрыш заключается в величине прецизионности, которую оно продуцирует. Алгоритм KL-LUCB использует участки уверенности, основанные на дивергенции Кульбака — Лейблера между распределениями, чтобы последовательно, но эффективно отыскивать правило принятия решения с наивысшей прецизионностью. Подготовительные работы для якорных и контрфактических объяснений с помощью библиотеки alibi Для того чтобы помочь библиотеке alibi производить дружественные для человека объяснения, необходимо выполнить несколько небольших шагов. Первый относится к предсказанию, поскольку модель может выводить 1 или 0, но предсказание легче понимать по его имени. С этой целью нам нужен список с именами классов, где позиция 0 соответствует имени отрицательного класса, а 1 — имени положительного класса: class_names = ['Low Risk', 'Medium/High Risk'] Далее давайте создадим массив numpy с главным интересующим нас экземпляром и выведем его. Обратите внимание, что для того чтобы библиотека alibi его поняла, одномерный массив должен быть расширен (np.expand_dims): X_test_eval = np.expand_dims(X_test.values[X_test.index.get_loc(idx1)], axis=0) print(X_test_eval) Приведенный выше исходный код выведет массив с 21 признаком, из которых 12 были результатом кодирования с одним активным состоянием (one-hot encoding, OHE): [[23 0 0 0 2 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0]] Проблема с дружественными человеку объяснениями возникает, когда у вас есть категории в кодировке с одним активным состоянием (OHE). Как для модели машинного обучения, так и для объяснителя, каждый признак OHE отделен от других. Однако для интерпретирующего результаты человека они кластеризованы как категории своих изначальных признаков. Библиотека alibi имеет несколько утилитных функций для решения этой проблемы, таких как ohe_to_ord, которая берет экземпляр, кодированный с одним активным состоянием, и переводит его в порядковый формат. В целях применения этой функции мы сначала определяем словарь (cat_vars_ohe), который сообщает библиотеке alibi, где в наших признаках находятся категориальные переменные и сколько
280 Часть II. Освоение методов интерпретации категорий в каждой из них. Например, в наших данных пол начинается с 5-го индекса и имеет две категории, поэтому словарь cat_vars_ohe начинается с 5:2. Имея этот словарь, функция ohe_to_ord может взять ваш экземпляр (X_test_eval) и вывести его в порядковом формате, в котором каждая категориальная переменная занимает один признак. Такая утилитная функция библиотеки alibi окажется полезной для контрфактических объяснений, в которых объяснителю понадобится этот словарь для соотнесения категориальных признаков вместе: cat_vars_ohe = {5:2, 7:6, 13:8} print(ohe_to_ord(X_test_eval, cat_vars_ohe)[0]) Приведенный выше исходный код печатает следующий массив: [[23 0 0 0 2 1 0 3]] Поскольку элементы массива имеют порядковый формат, библиотеке alibi понадобится словарь, в котором указаны имена для каждой категории и список имен признаков1: category_map = { 5: ['Female', 'Male'],\ 6: ['African-American', 'Asian', 'Caucasian',\ 'Hispanic', 'Native American', 'Other'],\ 7: ['Felony 1st Degree', 'Felony 2nd Degree',\ 'Felony 3rd Degree', 'Felony 7th Degree',\ 'Misdemeanor 1st Degree', 'Misdemeanor 2nd Degree',\ 'Misdemeanor 3rd Degree', 'Other Charge Degree'] } feature_names = ['age', 'juv_fel_count', 'juv_misd_count', 'juv_other_count', 'priors_count', 'sex', 'race', 'c_charge_degree'] Однако якорные объяснения библиотеки alibi используют данные в том виде, в котором они поставляются в наши модели. Мы используем данные в кодировке OHE, поэтому для указанного формата нам нужен категориальный словарь. Конечно, все признаки OHE являются двоичными, поэтому у них есть только две "категории" в каждом признаке: category_map_ohe = {5: ['Not Female', 'Female'],\ 6: ['Not Male', 'Male'],\ 7:['Not African American', 'African American'],\ 8:['Not Asian', 'Asian'],\ 9:['Not Caucasian', 'Caucasian'],\ 10:['Not Hispanic', 'Hispanic'],\ 11:['Not Native American', 'Native American'],\ 12:['Not Other Race', 'Other Race'],\ 1 Сокращенный перевод имен признаков: 5 (женщина, мужчина), 6 (афроамериканец, азиат, кавказец, латиноамериканец, коренной американец, другой), 7 (фелония 1-й степени, ..., мисдиминор 1-й степени, ..., проступок другой степени). — Прим. перев.
Глава 7. Якорные и контрфактические объяснения 281 13:['Not Felony 1st Level', 'Felony 1st Level'],\ 14:['Not Felony 2nd Level', 'Felony 2nd Level'],\ 15:['Not Felony 3rd Level', 'Felony 3rd Level'],\ 16:['Not Felony 7th Level', 'Felony 7th Level'],\ 17:['Not Misdemeanor 1st Deg', 'Misdemeanor 1st Deg'],\ 18:['Not Misdemeanor 2nd Deg', 'Misdemeanor 2nd Deg'],\ 19:['Not Misdemeanor 3rd Deg', 'Misdemeanor 3rd Deg'],\ 20:['Not Other Charge Degree', 'Other Charge Degree']} Локальные интерпретации якорных объяснений Всем объяснителям alibi требуется функция predict, поэтому для нашей модели CatBoost мы создаем lambda-функцию под названием predict_cb_fn. Обратите внимание, что мы используем predict_ proba для вероятностей классификатора. Затем в целях инициализации AnchorTabular мы также передаем ей имена признаков, как они есть в нашем наборе данных, и категорийный словарь (category_map_ohe), кодированный с одним активным состоянием. После инициализации модели мы выполняем ее подгонку под тренировочные данные: predict_cb_fn = lambda x: fitted_cb_mdl.predict_proba(x) anchor_cb_explainer = AnchorTabular(predict_cb_fn, X_train.columns,\ categorical_names=category_map_ohe) anchor_cb_explainer.fit(X_train.values) Прежде чем воспользоваться объяснителем, рекомендуется проверить, что якорь "соблюдается". Другими словами, необходимо проверить, что алгоритм многорукого бандита (MAB) нашел правила принятия решения, которые помогают объяснить предсказание. В целях подтверждения этого факта используется функция предсказания predictor, чтобы проверить совпадение полученного предсказания с ожидаемым для этого экземпляра. Прямо сейчас мы используем idx1, т. е. случай с ответчиком из числа афроамериканцев: print('Предсказание: %s' % class_names[anchor_cb_explainer.\ predictor(X_test.loc[idx1].values)[0]]) Приведенный выше исходный код печатает следующее: Предсказание: Medium/High Risk Теперь можно перейти к использованию функции explain генерирования объяснения для нашего экземпляра. Можно установить порог точности равным 0,85. Указанная величина порога означает, что мы ожидаем, что предсказания на заякоренных наблюдениях будут такими же, как и у нашего экземпляра, по меньшей мере, в 85% случаев. Получив объяснение, мы сможем распечатать якоря, а также их точность и охват: anchor_cb_explanation = anchor_cb_explainer.explain(\ X_test.loc[idx1].values, threshold=0.85, seed=rand) print('Якорь: %s' % (' AND'.join(anchor_cb_explanation.anchor)))
282 Часть II. Освоение методов интерпретации print('Точность: %.3f' % anchor_cb_explanation.precision) print('Охват: %.3f' % anchor_cb_explanation.coverage) Следующий ниже результат был сгенерирован приведенным выше исходным кодом. Хорошо видно, что возраст (age), число предшествующих судимостей (priors_count) и афроамериканская раса (race_African-American) являются факторами с точностью на уровне 86%. Впечатляюще! Это правило применимо почти к трети всех экземпляров пертурбационного пространства: Якорь: age <= 25.00 AND priors_count > 0.00 AND race_African-American = African American Точность: 0.863 Охват: 0.290 Можно попробовать тот же исходный код, но с 5%-ным увеличением порога точности. В результате будут получены те же первые три якоря, что и с более низким порогом точности, но теперь мы имеем еще два: Якорь: age <= 25.00 AND priors_count > 0.00 AND race_African-American = African American AND c_charge_degree_(M1) = Not Misdemeanor 1st Deg AND c_charge_degree_(F3) = Not Felony 3rd Level AND race_Caucasian = Not Caucasian Точность: 0.903 Охват: 0.290 Интересно, что, хотя точность действительно увеличилась на несколько процентных пунктов, охват остался прежним, поэтому дополнительные якоря применяются к аналогичному подмножеству пертурбаций с повышенной точностью. На этом уровне точности можно подтвердить, что раса — важный фактор, потому что принадлежность к афроамериканцам является якорем, а вот принадлежность к кавказцам им не является. Еще одним фактором была степень преступления, в котором ответчику предъявлено обвинение в настоящее время (c_charge_degree). Объяснение показывает, что наличие обвинения в мисдиминоре 1-й степени или фелонии 3-го уровня было бы лучше. Вполне понятно, что фелония 7-го уровня является более серьезным обвинением, чем эти два. Теперь давайте создадим якорный объяснитель типа черного ящика для нашей нейронной сети. Следует отметить, что lambda-функция отличается тем, что сетевая функция предсказания predict выводит один набор предсказаний для положительного класса, но нам-то нужны два набора, в том числе один для отрицательного класса. Это ограничение легко преодолеть, т. к. вероятности для обоих классов должны в сумме составлять 100%, следовательно, отрицательная вероятность должна дополнять положительную. Все остальное, касаемое инициализации и обучение объяснителя, остается прежним: predict_nn_fn = lambda x: np.concatenate(\ (1 - fitted_nn_mdl.predict(x), fitted_nn_mdl.predict(x)), axis=1)
Глава 7. Якорные и контрфактические объяснения 283 anchor_nn_explainer = AnchorTabular(predict_nn_fn, X_train.columns,\ categorical_names=category_map_ohe) anchor_nn_explainer.fit(X_train.values) Понять причину, которая повлияла на конкретное предсказание, можно еще одним способом, а именно, проведя поиск похожей точки данных, имевшей противоположное предсказание, и выяснить, почему было сделано это предсказание. Граница решения пересекает обе точки, поэтому полезно противопоставлять объяснения решений с обеих сторон границы. На этот раз мы воспользуемся случаем с ответчиком из числа кавказцев: anchor_nn_explanation = anchor_nn_explainer.explain(\ X_test.loc[idx3].values, threshold=0.85, seed=rand) print('Якорь: %s' % (' AND'.join(anchor_nn_explanation.anchor))) print(Точность: %.3f' % anchor_nn_explanation.precision) print('Охват: %.3f' % anchor_nn_explanation.coverage) Приведенный выше исходный код выводит якоря следующим образом: Якорь: priors_count <= 2.00 AND race_African-American = Not African American AND c_charge_degree_(F3) = Not Felony 3rd Level Точность: 0.911 Охват: 0.578 Первым якорем является priors_count < = 2.00, но по другую сторону границы первыми двумя якорями были age <= 25.00 и priors_count > 0.00. Другими словами, для афроамериканца в возрасте 25 лет или младше любого количества предшествующих судимостей (priors_count) достаточно, чтобы классифицировать его как имеющего средний/высокий риск рецидивизма (86% случаев). С другой стороны, для белого человека, до тех пор, пока число предшествующих судимостей не превышает 2 и он не обвиняется в фелонии 3-го уровня, предсказание в его отношении будет демонстрировать низкий риск (91% случаев и с охватом 58%). Эти правила принятия решения предполагают не только систематическую смещенность по признаку расы (race), но и применение двойных стандартов к другим признакам. Двойной стандарт — это применение разных правил тогда, когда в принципе ситуация одна и та же. В данном случае разные правила для числа предшествующих судимостей (priors_count) и отсутствие возраста (age) как фактора для кавказцев составляют двойные стандарты. Теперь можно обратиться к ответчику из числа латиноамериканцев (idx2), чтобы выяснить, следует ли в этом случае тоже применять двойные стандарты. Мы просто выполняем тот же самый исходный код, что и раньше, но заменяем idx3 на idx2: Якорь: priors_count <= 2.00 AND race_African-American = Not African American AND race_Hispanic = Hispanic Точность: 0.908 Охват: 0.578
284 Часть II. Освоение методов интерпретации Объяснения для ответчика из числа латиноамериканцев подтверждают двойной стандарт с числом предшествующих судимостей (priors_count) и демонстрируют, что раса (race) продолжает оставаться сильным фактором, поскольку существует один якорь для принадлежности к афроамериканцам, а другой — для принадлежности к латиноамериканцам. Для конкретных модельных решений якорные объяснения отвечают на вопрос "почему?" Однако, ища ответы на вопрос, почему наша точка не была на той стороне, мы пересекли границу решения. Поступая таким образом, мы поверхностно коснулись вопроса: "а что делать, если?.." В следующем далее разделе мы рассмотрим этот вопрос подробнее. Анализ контрфактических объяснений Контрфакты (или альтернативные суждения) являются неотъемлемой частью человеческого мышления. Сколько из нас бормотали слова "если бы я сделал это вместо того, то всё было бы совсем по-другому"? Всегда есть одна или две вещи, которые, если бы они были сделаны иначе, могли бы привести к предпочтительным результатам! В исходах машинного обучения этот способ рассуждения можно использовать, чтобы находить чрезвычайно дружественные для человека объяснения, позволяющие понять, что нужно изменить, чтобы получить противоположный исход (контрфактический класс). В конце концов, интересно знать, как сделать неблагоприятный исход лучше. Например, как получить одобрение заявки на выдачу кредита, в котором было отказано, или изменить риск сердечно-сосудистых заболеваний с высокого на низкий? Однако хотелось бы надеяться, что ответы на эти вопросы не предусматривают огромный список изменений. Желательно, чтобы число изменений вашего исхода было наименьшим. Что касается объективности, то контрфакты являются важным средством интерпретации, в частности, когда участвуют элементы, которые мы не можем изменить либо не должны изменять. Например, если вы выполняете одинаковую работу и имеете тот же уровень профессионализма, что и ваш коллега, то рассчитываете получать одинаковую зарплату, верно? Если у вас и вашего супруга одинаковые активы и кредитная история, но разные кредитные баллы, то вам нужно задаться вопросом: почему? С чем это связано: с полом, расой, возрастом или даже политической принадлежностью? Будь то компенсационная, кредитно-рейтинговая модель или модель риска рецидивизма, вы надеетесь, что аналогичные баллы будут иметь схожие результаты на выходе. Отыскивать контрфакты не очень сложно. Нам нужно лишь понемногу изменять интересующий нас экземпляр до тех, пока исход не станет иным. И, может быть, в наборе данных уже есть как раз такой экземпляр! На самом деле можно сказать, что три экземпляра, которые мы рассмотрели с якорями в предыдущем разделе, вполне могут быть противоположными по отношению друг к другу, за исключением случаев с кавказцем и латиноамериканцем, которые
Глава 7. Якорные и контрфактические объяснения 285 имеют одинаковый исход. Но кавказский и латиноамериканский экземпляры были "подобраны специально" путем поиска точек данных с одинаковой криминальной историей, но с другой расой, нежели интересующий экземпляр. Возможно, сравнивая схожие точки, главным образом за исключением расы, мы ограничили диапазон так, чтобы удостовериться в том, что раса важна для принимаемых моделью решений. Это яркий пример систематического смещения вследствие отбора данных. В конце концов, контрфактические объяснения органически избирательны, потому что они сосредоточены на нескольких изменениях признаков. И даже при наличии всего нескольких признаков существует очень много возможных перестановок, которые изменяют результат, а это означает, что отдельная точка может иметь сотни контрфактов. И не все из них расскажут выверенную историю. Это явление называется эффектом Расёмона (Rashomon effect) в честь известного японского кинофильма о таинственном убийстве. И, как мы и ожидали от таинственных убийств, свидетели интерпретируют по-разному то, что произошло. Но по аналогии с тем, как трудно полагаться на одного свидетеля, нельзя полагаться на один контрфакт. Кроме того, аналогично тому, как великие детективы обучены повсюду искать улики, связанные с местом преступления (даже если это противоречит их инстинктам), контрфакты нельзя "подбирать специально", потому что они удобно рассказывают историю, которую мы хотим узнать. К счастью, существуют алгоритмические способы отыскания контрфактических экземпляров несмещенным образом. В типичной ситуации они предусматривают отыскание ближайших точек с разными исходами, но при этом существуют разные способы измерения расстояния между точками. Для начала, есть расстояние L1 (манхэттенское расстояние) и расстояние L2 (евклидово расстояние) среди многих других. Но существует также вопрос нормализации расстояний, потому что не все признаки имеют одинаковую шкалу. В противном случае они будут систематически смещены в сторону признаков с меньшей шкалой, таких как признаки в кодировке с одним активным состоянием. Существует много схем нормализации, из которых тоже можно выбирать. Можно использовать среднее квадратичное отклонение, минимаксное шкалирование или даже медианное абсолютное отклонение [9]. В этом разделе мы дадим объяснение одному продвинутому методу отыскания контрфактов и его применим. Затем мы проведем анализ инструмента Google WIT. Он имеет простой L1- и L2-ориентированный поисковик контрфактов, который лимитирован набором данных, но дополняет его другими полезными функциональностями интерпретации. Контрфактические объяснения под руководством прототипов Самые изощренные алгоритмы отыскания контрфактов делают следующее.  Потери. Алгоритмы задействуют функцию потерь, которая помогает оптимизиро- вать отыскание контрфактов, наиболее близких к интересующему нас экземпляру.  Пертурбация. Как правило, эти алгоритмы действуют на пертурбационном про- странстве так же, как якоря, изменяя как можно меньше признаков. Обратите
286 Часть II. Освоение методов интерпретации внимание, что контрфакты не обязательно должны быть реально существующими точками в вашем наборе данных. Это бы слишком ограничило нас. Контрфакты существуют не в сфере безусловно известного, а в сфере возможного.  Распределение. Эти алгоритмы должны быть реалистичными и, следовательно, интерпретируемыми. Например, функция потерь помогает определять, что одного условия age < 0 достаточно, чтобы сделать любой средне-высокорисковый экземпляр низкорисковым. Вот почему контрфакты должны лежать близко к статистическим распределениям ваших данных, в особенности к специфическим для классов распределениям. Они также не должны быть систематически смещены относительно признаков с меньшей шкалой, а именно категориальным переменным.  Скорость важна, т. к. алгоритмы работают достаточно быстро, чтобы быть по- лезными в реально существующих сценариях. Библиотечный класс контрфактических объяснений под руководством прототипов (CounterFactualProto) библиотеки alibi имеет все эти свойства. Он имеет функцию потерь, которая включает регуляризацию как L1 (лассо), так и L2 (гребневую) в виде линейной комбинации, точно так же, как это делает наивная эластичная сеть ( L2  L2 ), но с весом (  ) только в члене L1. Хитрая часть этого алгоритма заключается в том, что он может (опционально) использовать автокодировщик для понимания распределений. Не будем возвращаться к объяснению принципа его работы, потому что мы рассмотрели вариационный автокодировщик (variational autoencoder, VAE) в главе 3. Однако здесь важно отметить, что автокодировщики в целом представляют собой нейронные сети, которые усваивают сжатое представление ваших тренировочных данных. Указанный метод включает в себя члены потери из автокодировщика, такие как потеря для ближайшего прот отипа. Прототипом является размерно-редуцированное представление контрфактического класса. Если автокодировщик недоступен, то вместо него алгоритм использует дерево, часто применяемое для мультиразмерного поиска (k-d-деревья). С помощью этого дерева алгоритм может эффективно улавливать распределения классов, а также выбирать ближайший прототип. После того как получен прототип, он начинает руководить пертурбациями. Включение относящегося к прототипу члена потери в функцию потерь обеспечивает достаточную близость результирующих пертурбаций к прототипу, т. е. присущих распределению контрфактического класса. Многие методы модельного класса и интерпретации упускают из виду важность трактовать непрерывные и категориальные признаки по-разному. Библиотечный класс CounterFactualProto библиотеки alibi может использовать две разные метрики расстояния для вычисления попарных расстояний между категориями категориальной переменной: метрику модифицированной разности значений (Modified value difference metric, MVDM) и метрику расстояния на основе ассоциаций (association-based distance metric, ABDM), и даже способен комбинировать обе. Библиотечный класс CounterFactualProto обеспечивает содержательные контрфакты еще одним способом — путем лимитирования переставленных признаков предопреде-
Глава 7. Якорные и контрфактические объяснения 287 ленными диапазонами. Можно использовать минимальное и максимальное значения признаков для генерирования кортежа массивов (feature_range): feature_range = (X_train.values.min(axis=0).reshape(1,21).astype(np.float32),\ X_train.values.max(axis=0).reshape(1,21).astype(np.float32)) print(feature_range) Приведенный выше исходный код выводит два массива — первый с минимумом, а второй с максимумом всех признаков: (array([[18., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32), array([[96., 20., 13., 11., 38., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32)) Теперь можно создать экземпляр объяснителя с помощью библиотечного класса CounterFactualProto библиотеки alibi. В качестве аргументов для этого требуются используемая в модели типа черного ящика функция предсказания (predict_nn_fn); форма экземпляра, который вы хотите объяснить (X_test_eval.shape); максимальное число выполняемых итераций оптимизации (max_iterations) и признаковый диапазон для перетасованных экземпляров (feature_range). Многие гиперпараметры можно отрегулировать, включая вес , применяемый к регуляризационной поправке функии потерь L1 (beta), и вес , применяемый к функции потерь прототипа (theta). Кроме того, вы должны указать, следует ли использовать k-d-дерево (use_kdtree) в случаях, когда автокодировочная модель не предусмотрена. После создания экземпляра объяснителя вы выполняете его обучение с тестовым набором данных. Метрика расстояния для категориальных признаков (d_type) задается как комбинация ABDM и MVDM: cf_nn_explainer = CounterFactualProto(\ predict_nn_fn, X_test_eval.shape, max_iterations=100,\ feature_range=feature_range, beta=.1, theta=5, use_kdtree=True) cf_nn_explainer.fit(X_test.values, d_type='abdm-mvdm') Создание объяснения с помощью объяснителя похоже на то, как это было с якорями. Надо просто передать экземпляр (X_test_eval) в функцию explain. Однако вывод результатов не так прост (в основном из-за конвертирования признаков из кодировки с одним активным состоянием в порядковый формат и взаимодействия между этими признаками). Документация по библиотеке alibi (https://docs.seldon.io/ projects/alibi/) предлагает подробный пример того, как это делается. Мы же будем использовать утилитную функцию describe_cf_instance, которая делает это за нас, используя интересующий нас экземпляр (X_test_eval), объяснение (cf_nn_explanation), имена классов (class_names), местоположения кодированных с одним активным состоянием категорий (cat_vars_ohe), словарь категорий (category_map) и имена признаков (feature_names): cf_nn_explanation = cf_nn_explainer.explain(X_test_eval) mldatasets.describe_cf_instance(X_test_eval,\ cf_nn_explanation, class_names, cat_vars_ohe, category_map, feature_names)
288 Часть II. Освоение методов интерпретации Следующий результат был получен с помощью приведенного выше исходного кода: Instance Outcomes and Probabilities -----------------------------------------------original: Medium/High Risk [0.46732193 0.53267807] counterfactual: Low Risk [0.50025815 0.49974185] Categorical Feature Counterfactual Perturbations -----------------------------------------------sex: Male --> Female race: African-American --> Asian c_charge_degree: Felony 7th Degree --> Felony 1st Degree Numerical Feature Counterfactual Perturbations -----------------------------------------------priors_count: 2.00 --> 1.90 Как следует из результата, интересующий экземпляр ("оригинал") имеет средний/высокий риск с вероятностью 53,26%, но контрфакт еле-еле находится на стороне низкого риска с вероятностью 50,03%! Мы как раз хотели бы увидеть контрфакт, который находится немного с другой стороны, потому что это, возможно, означает, что он лежит максимально близко к интересующему нас экземпляру. Между ними есть четыре отличающихся признака, три из которых категориальные (sex, race и c_charge_degree). Четвертым отличающимся признаком является числовой признак — число предшествующих судимостей (priors_count), который трактуется как непрерывный, поскольку объяснитель не знает, что он является дискретным. В любом случае он должен быть монотонным, и, следовательно, меньшее число предшествующих судимостей всегда должно означать более низкий риск, а значит, можно интерпретировать 1,90 как 1, потому что если на 0,1 меньшее число предшествующих судимостей помогало снижать риск, то так же должно делать и целое их число. Более глубокий смысл, заложенный в результате CounterFactualProto, состоит в том, что два демографических признака присутствовали в наиболее близком к этому признаку контрфакте. Один из них был найден с помощью метода, который предназначен для отслеживания статистических распределений наших классов и не смещен в противоположную сторону от конкретных типов признаков или, наоборот, в их сторону. И хотя удивительно увидеть в нашем альтернативном суждении женщину-азиатку, потому что она не вписывается в сюжет о том, что белые мужчины имеют бóльшие привилегии, тревожно осознавать, что раса вообще появляется в контрфакте.
Глава 7. Якорные и контрфактические объяснения 289 Получение контрфактических экземпляров и многого другого с помощью инструмента What-If Tool (WIT) Инструмент WIT (What-If Tool, WIT) компании Google очень универсален. Он требует очень малого объема данных на входе или подготовки и открывается в вашем блокноте приложения Jupyter Notebook или Colab в виде интерактивной приборной панели с тремя вкладками:  Datapoint editor (Редактор точек данных): для визуализациии ваших точек дан- ных, их редактирования и объяснения их предсказаний;  Performance (Результативность): просмотр высокоуровневых метрик модельной результативности (для всех регрессионных и классификационных моделей). Для двоичной классификации эта вкладка называется Performance & Fairness (Результативность и объективность), потому что в дополнение к высокоуровневым метрикам можно сравнивать предсказательную объективность между признаковоориентированными срезами вашего набора данных;  Features (Признаки): просмотр общей статистики признаков. Учитывая, что вкладка Features (Признаки) не относится к модельным интерпретациям, в этом разделе мы рассмотрим только первые две. Конфигурирование инструмента WIT Можно насыщать наши интерпретации в WIT опционально путем создания атрибуций, т. е. значений, объясняющих степень, с которой каждый признак вносит вклад в каждое предсказание. Для генерирования атрибуций можно применять любой метод, но мы будем использовать SHAP. Мы рассмотрели SHAP в главе 5. Поскольку мы будем интерпретировать модель CatBoost на приборной панели WIT, наиболее подходящим объяснителем SHAP является древовидный объяснитель (TreeExplainer), но глубокий объяснитель (DeepExplainer) будет работать для нейронной сети (и ядерный объяснитель KernelExplainer для обоих). Для инициализации объяснителя TreeExplainer нам нужно лишь передать подогнанную модель (fitted_cb_mdl): shap_cb_explainer = shap.TreeExplainer(fitted_cb_mdl) WIT требует всех признаков набора данных (включая метки). Мы будем использовать тестовый набор данных, чтобы вы могли конкатенировать X_test и y_test, но даже эти два подмножества исключают эмпирически истинный признак (is_recid). Получить их все можно путем взятия подмножества recidivism_df с индексами тестового набора данных (y_test.index). Инструменту WIT также нужны ваши данные и столбцы в формате списка, чтобы иметь возможность сохранять их в качестве переменных для последующего использования (test_np и cols_l). Наконец, для предсказаний и атрибуций нам нужно будет удалить эмпирическую истину (is_recid) и классификационную метку (compas_score), поэтому давайте сохраним индекс этих столбцов (delcol_idx): test_df = recidivism_df.loc[y_test.index] test_np = test_df.values
290 Часть II. Освоение методов интерпретации cols_l = test_df.columns delcol_idx = [cols_l.get_loc("is_recid"), cols_l.get_loc("compas_score")] У инструмента WIT есть несколько полезных функций для индивидуальной настройки приборной панели, таких как настройка метрики расстояния (set_custom_distance_fn), вывод на экран имен классов вместо чисел (set_label_vocab), настройка прикладной функции предсказания (set_custom_predict_fn) и второй функции предсказания для сравнения двух моделей (compare_custom_predict_fn). В дополнение к set_label_vocab мы собираемся использовать только прикладную функцию предсказания (custom_predict_with_shap). Для работы инструмента нужно лишь взять массив с вашим набором данных examples_np и сгенерировать несколько предсказаний (preds). Однако сначала необходимо удалить признаки, которые нам нужны на приборной панели, но не использовались для обучения (delcol_idx). От этой функции требуется, чтобы на выходе она дала словарь с предсказаниями, хранящимися в ключе predictions. Но мы также хотели бы иметь несколько атрибуций. Вот почему в этом словаре нам нужен ключ attributions. Следовательно, мы берем наш объяснитель SHAP и генерируем значения SHAP (shap_values) в виде массива NumPy. Однако, для того чтобы быть понятными для приборной панели WIT, атрибуции должны иметь форму списка словарей. С этой целью мы прокручиваем shap_output в цикле и конвертируем массив значений SHAP каждого наблюдения в словарь (attrs), а затем добавляем его в список (attributions): def custom_predict_with_shap(examples_np): # Для значений SHAP нам нужны только те же признаки, # которые использовались для тренировки inputs_np = np.delete(np.array(examples_np), delcol_idx, axis=1) # Получить модельные предсказания классов preds = predict_cb_fn(inputs_np) # С помощью тестовых данных сгенеририровать значения SHAP, # которые конвертируются в формат списка словарей keepcols_l = [c for i, c in enumerate(cols_l)\ if i not in delcol_idx] shap_output = shap_cb_explainer.shap_values(inputs_np) attributions = [] for shap in shap_output: attrs = {} for i, col in enumerate(keepcols_l): attrs[col] = shap[i] attributions.append(attrs) # Функция предсказания должна иметь на выходе # предсказания/атрибуции в словаре output = {'predictions': preds, 'attributions': attributions} return output
Глава 7. Якорные и контрфактические объяснения 291 Перед построением приборной панели WIT важно отметить, что для того, чтобы найти интересующий нас экземпляр на приборной панели, нам нужно знать его позицию в передаваемом в WIT массиве NumPy, потому что в массиве нет индексов, как в кадрах данных pandas. В целях отыскания позиции нам нужно лишь предоставить функцию get_loc с индексом: print(y_test.index.get_loc(5231)) Приведенный выше исходный код печатает 2910, поэтому можно взять на заметку данное число. Построить приборную панель инструмента WIT сейчас довольно просто. Сначала мы инициализируем конфигурацию (WitConfigBuilder) с нашим тестовым набором данных в формате NumPy (test_np) и списком признаков (cols_l). Оба конвертируются в списки с помощью tolist(). Затем мы задаем прикладную функцию предсказания с помощью set_custom_predict_fn и целевой признак (is_recid) и передаем имена классов. На этот раз мы будем использовать эмпирическую истину, чтобы оценить объективность с точки зрения того, что действительно произошло. После того конфигурация будет инициализирована, виджет (WitWidget) строит с ее помощью приборную панель. Можно опционально указать высоту (по умолчанию она равна 1000 пикселам): wit_config_builder = WitConfigBuilder(test_np.tolist(),\ feature_names=cols_l.tolist()).\ set_custom_predict_fn(custom_predict_with_shap).\ set_target_feature("is_recid").set_label_vocab(class_names) WitWidget(wit_config_builder, height=800) Редактор точек данных На рис. 7.6 показана приборная панель инструмента WIT с тремя вкладками. Сначала мы рассмотрим первую вкладку (редактор точек данных). Слева на ней расположены панели визуализации и редактирования, а справа редактор может показывать либо точки данных (Datapoints), либо графики частичной зависимости (Partial dependence plots). Если вы установите переключатель Datapoints, то точки данных можно визуализировать различными способами, используя элементы управления в правом верхнем углу (выделенная область A). На рис. 7.6 мы сделали следующее2:  Binning | X-axis (Группировка в корзины | ось X): c_charge_degree_(F7);  Binning | Y-axis (Группировка в корзины | ось Y): compas_score;  Color By (Цвет по): race_African-American;  все остальное остается без изменений. Эти настройки привели к тому, что все наши точки данных были аккуратно организованы в 2 строки и 2 столбца и закодированы цветом по двоичному признаку: афроамериканец или нет. Правый столбец предназначен для лиц с фелонией 7-го уровня, а верхняя строка — для лиц с баллом системы COMPAS равным среднему/ 2 Для справки: binning — группировка в интервальные корзины. — Прим. перев.
292 Часть II. Освоение методов интерпретации высокому риску. Точку данных 2910 можно найти в этой подгруппе (выделенная область B), нажав на самый правый верхний элемент. Он должен появиться на панели Edit (выделенная область C). Интересно, что атрибуции SHAP для этой точки в три раза выше для возраста (age), чем для афроамериканской расы (race_AfricanAmerican). Но все же раса в целом уступает возрасту по важности, находясь на втором месте. Кроме того, обратите внимание на то, что на панели логического вывода (Infer) приведена предсказанная вероятность, которая для среднего/ высокого риска примерно равна 89%. Рис. 7.6. Приборная панель инструмента WIT с интересующим нас экземпляром Инструмент WIT может отыскивать ближайший контрфакт на основе расстояния L1 или L2. И для расчета расстояний он может использовать либо значения признаков, либо атрибуции. Как упоминалось ранее, WIT также может включать прикладную функцию отыскания расстояния, если добавить ее в конфигурацию. Пока что мы выберем L2 со значением Feature value (Признак). На рис. 7.7 эти параметры появляются в выделенной области А. После того как будет выбрана метрика расстояния и включен переключатель Nearest counterfactual (Ближайший контрфакт), она появится рядом с интересующим нас экземпляром (область B), и инструмент сравнит их предсказания, как показано в области C. Признаки можно отсортировать по абсолютной атрибуции (Absolute attribution) с целью более четкого понимания важности признаков на локальном уровне. Контрфакт всего на 3 года старше, но имеет ноль предшествующих судимостей (priors_count) вместо двух, но этого было достаточно, чтобы снизить средний/высокий риск почти до 5%.
Глава 7. Якорные и контрфактические объяснения 293 Рис. 7.7. Как отыскивать ближайший контрфакт в инструменте WIT Пока интересующий нас экземпляр и контрфакт остаются выбранными, можно визуализировать их вместе со всеми остальными точками. При этом вы получаете информацию из локальных интерпретаций и можете создавать контекст, достаточный для глобального понимания. Например, давайте изменим наши настройки визуализации так:  Binning | X-axis (Группировка в корзины | ось X): Inference label (метка логиче- ского вывода);  Binning | Y-axis (Группировка в корзины | ось Y): (none);  Scatter | X-axis (Рассеяние | ось Х): age;  Scatter | Y-axis (Рассеяние | ось Y): priors_count;  все остальное остается без изменений. Результат этой визуализации показан на рис. 7.8. Как следует из рисунка, точки корзин Low Risk (Низкий риск), как правило, пролегают в нижней области признака priors_count. Обе корзины показывают, что число предшествующих судимостей (priors_count) и возраст (age) имеют небольшую корреляцию, хотя она значительно более выражена в корзине среднего/высокого риска. Однако наибольший интерес вызывает явная плотность афроамериканских точек данных, считающихся средним/высоким риском в возрасте (age), варьирующемся между 18 и 25, причем число
294 Часть II. Освоение методов интерпретации предшествующих судимостей (priors_count) ниже трех по сравнению с теми, которые находятся в корзине Low Risk (Низкий риск). Это говорит о том, что меньший возраст (age) и более высокое число предшествующих судимостей (priors_count) увеличивает риск для афроамериканцев в большей степени, чем для других. Рис. 7.8. Визуализация возраста (age) по сравнению с числом предшествующих судимостей (priors_count) в инструменте WIT Можно попробовать создавать собственные контрфакты, редактируя точку данных. Что происходит, когда мы уменьшаем число предшествующих судимостей (priors_count) до единицы? Ответ на этот вопрос показан на рис. 7.9. Внеся изменение и нажав кнопку Predict (Предсказать) на панели Infer (Логический вывод), мы увидим, что инструмент добавил запись в историю предсказания, которая будет последней на панели Infer. Как следует из результатов в прогоне № 2 (Run #2), риск снижается почти до 33,5%, что почти на 50% меньше! Что же произойдет, если возраст (age) всего на 2 года больше, но есть два совершенных ранее преступления (priors_count)? На рис. 7.10 в прогоне № 3 говорится, что результат еле-еле достиг балла низкого риска. Еще одной функциональностью на вкладке Datapoint editor (Редактор точек данных) являются графики частичной зависимости, которые мы рассмотрели в главе 4. Если установить переключатель Partial dependence plots, то инструмент изменит правую панель так, чтобы она выглядела как на рис. 7.11. По умолчанию, если выбрана точка данных, графики частичной зависимости (PDP) являются локальными, т. е. они относятся к выбранной точке данных. Но можно переключиться на глобальные.
Глава 7. Якорные и контрфактические объяснения 295 Рис. 7.9. Редактирование точки данных после уменьшения числа предшествующих судимостей (priors_count) в инструменте WIT Рис. 7.10. Редактирование точки данных после увеличения возраста (age) в инструменте WIT В любом случае, лучше всего отсортировать графики по вариации, как показано на рис. 7.11, где возраст (age) и число предшествующих судимостей (priors_count) имеют самую высокую вариацию. Интересно, что ни одна из этих переменных
296 Часть II. Освоение методов интерпретации не является монотонной, что не имеет смысла. Модель должна усвоить, что увеличение в числе предшествующих судимостей (priors_count) должно стабильно увеличивать риск. То же самое должно быть и со снижением возраста (age). В конце концов, научные исследования показывают, что преступность имеет тенденцию к пику в середине второй декады жизни и бóльшие значения случаев предшествующих судимостей повышают вероятную возможность рецидивизма. Взаимосвязь между этими двумя переменными также хорошо изучена, поэтому вместо того, чтобы усваивать нестыковки в данных, которые приводят к необъективности, возможно, конструирование данных и монотонные ограничения обеспечат согласованность модели с известными явлениями. Эта тема будет рассмотрена в главе 12. Рис. 7.11. Локальные графики частичной зависимости для возраста (age) и числа предшествующих судимостей (priors_count) А можно ли еще что-то сделать для повышения объективности в уже обученной модели? Действительно, можно. Нам потребуется вкладка Performance & Fairness (Результативность и объективность). Результативность и объективность Щелкнув по вкладке Performance & Fairness (Результативность и объективность), вы увидите, что слева на ней есть панели Configure (Сконфигурировать) и Fairness (Объективность). А справа можно изучить общую результативность модели (рис. 7.12). Вверху этой панели содержатся поля ложноположительных классификаций (False Positives, %), ложноотрицательных классификаций (False Negatives, %),
Глава 7. Якорные и контрфактические объяснения 297 точности (Accuracy, %) и метрики F1. Если панель развернуть, она покажет кривую ROC, кривую PR (Precision-Recall), матрицу путаницы и средние атрибуции — средние значения Шепли. Мы рассмотрели все эти метрики прямо или косвенно ранее в этой книге, за исключением кривой PR. Метрика точности — полноты (Precision-Recall, PR) очень похожа на кривую ROC, за исключением того, что она отражает зависимость точности относительно полноты, а не частоты истинноположительных классификаций (TPR) относительно частоты ложноположительных классификаций (FPR). На этом графике ожидается, что точность будет уменьшаться по мере уменьшения полноты. В отличие от кривой ROC, она считается хуже линии подбрасывания монеты, когда линия находится близко к оси x, и лучше всего подходит для решения задач несбалансированного классифицирования. Рис. 7.12. Первоначальный вид вкладки Performance & Fairness Классификационная модель будет показывать вероятности того, что наблюдение относится к тому или иному классу. Обычно мы относим каждое наблюдение с вероятностью 0,5 и более к положительному классу. В противном случае мы предсказываем, что оно относится к отрицательному классу. Этот порог называется классификационным порогом, и вам не всегда нужно использовать стандартное значение 0,5. Существует много ситуаций, в которых целесообразно выполнить настройку порога. Чаще всего к настройке прибегают в задачах несбалансированного классифицирования, поскольку нередко модели оптимизируют свою результативность только на точности, но в итоге остаются с плохой полнотой или точностью. Корректировка порога будет улучшать метрику, которая вас интересует больше всего. Еще одна первостепенная причина корректировки порогов состоит в обеспечении объективности. Для этого вам необходимо рассмотреть метрику, которая вас инте-
298 Часть II. Освоение методов интерпретации ресует больше всего, в разных срезах ваших данных. В нашем случае ложноположительные классификации (%) позволяют оценить необъективность лучше всего. Например, взгляните на рис. 7.13. С помощью панели Configure (Сконфигурировать) можно нарезать данные по признаку race_African-American, а справа от нее можно увидеть то, что мы наблюдали в начале этой главы, а именно, что ложноположительные классификации для афроамериканцев существенно выше, чем для других сегментов. Это можно исправить с помощью методов автоматической оптимизации, таких как демографический паритет или равные возможности. Если вы собираетесь использовать один из них, лучше всего скорректировать стоимостное соотношение (Cost Ratio: FP FN ), чтобы сообщить оптимизатору, что ложноположительные (FP) классификации стоят больше, чем ложноотрицательные (FN). Рис. 7.13. Нарезка метрик результативности по признаку race_African-American Рис. 7.14. Корректировка классификационного порога для набора данных, нарезанного по признаку race_African-American Корректировать пороги также можно вручную, используя применяемую по умолчанию настройку Custom Thresholds (Прикладные пороги), рис. 7.14. Если мы хотим приближенного паритета для этих срезов с ложноположительными классификациями, необходимо использовать 0,78 в качестве порога для случая, когда race_African-American=1. Недостатком является то, что ложноотрицательные классификации для этой группы увеличатся, не достигнув паритета на том конце. Стоимостное соотношение помогло бы определить, оправдывает ли 14,7% в ложноположительных классификациях 24,4% в ложноотрицательных классификациях, но
Глава 7. Якорные и контрфактические объяснения 299 для этого нам нужно было бы понять задействуемые средние стоимости. Мы рассмотрим методы калибровки шансов далее в главе 11. Теперь, когда у нас есть представление о том, как якоря и контрфакты могут использоваться для объяснения предсказаний, давайте опробуем метод, который сочетает в себе оба этих элемента. Сравнение с помощью метода контрастивного объяснения Метод контрастивного объяснения (contrastive explanation method, CEM) похож как на якоря, так и на контрфакты, поскольку он объясняет предсказания, используя то, что присутствует (например, якоря) и отсутствует (например, контрфакты). В нем то, что присутствует, называется уместными положительными результатами (pertinent positive, PP), а то, что отсутствует, уместными отрицательными результатами (pertinent negative, PN). Однако разница между ними состоит в том, что уместные (пертинентные) положительные результаты квалифицируются как минимально и достаточно присутствующие, чтобы предсказывать один и тот же класс. Аналогично, уместные отрицательные результаты минимально и необходимо отсутствуют, чтобы предсказывать противоположный класс. CEM лучше всего работает с непрерывными и порядковыми признаками, поскольку он ожидает вычитания из признаков до тех пор, пока желаемый результат не будет достигнут. По этой причине метод не знает, как работать с немонотонными непрерывными, неупорядоченными, категориальными или даже двоичными признаками, если на то пошло, и наш набор данных о рецидивизме имеет только такой признак! По общему признанию, пример этой главы не подходит для идеального варианта использования метода CEM. Мы коснемся указанного метода в последующих главах. А пока важно связать его с якорями и контрфактами и дать краткое объяснение принципа его использования. Метод CEM имеет пертурбационную стратегию, регуляризатор на основе эластичной сети и дополнительный автокодировщик, помогающий управлять функцией потерь. Звучит знакомо? Создатели CounterFactualProto основали его на статье, посвященной CEM. Однако CEM не опирается на k-d-деревья, поэтому настоятельно рекомендуется использовать дополнительный автокодировщик, чтобы сделать объяснения более реалистичными, когда набор данных относительно мал или зашумлен. Наш набор данных ни в коем случае не является большим или ясным, как день. И, следовательно, направляющая функциональность может быть полезной, поэтому мы создадим простой автокодировщик. Обучение автокодировщика не так страшно, как может показаться. Это всего лишь нейронная сеть, напоминающая песочные часы, цель которой состоит в обеспечении совпадения входного слоя (input_layer) и выходного слоя (output_layer). Между этими слоями есть кодировщик (coder) и декодировщик (decoder), которые сходятся в слое, который мы называем бутылочным горлышком (bottleneck). Этот слой появ-
300 Часть II. Освоение методов интерпретации ляется на выходе из кодировщика и подается на вход декодировщика. Вся суть состоит в том, чтобы сжать данные в меньшей размерности бутылочное горлышко, тем самым сократив ошибку восстановления между входным и выходным слоями: input_layer = tf.keras.Input(shape=(21)) encoder = tf.keras.layers.Dense(10, activation='relu')(input_layer) bottleneck = tf.keras.layers.Dense(3, activation='relu')(encoder) decoder = tf.keras.layers.Dense(10, activation='relu')(bottleneck) output_layer = tf.keras.layers.Dense(21, activation='linear')(decoder) autoencoder_mdl = tf.keras.Model(input_layer, output_layer) autoencoder_mdl.summary() Приведенный выше исходный код строит автокодировочную модель послойно в целях обеспечения ее более легкого понимания. Если посмотреть на ее сводку (summary()), то можно увидеть, как в процессе кодирования размерности переходят от 21 к 10, а затем к 3. Естественно, при декодировании они переходят назад от 3 к 10, а затем к 21: _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_29 (InputLayer) [(None, 21)] 0 _________________________________________________________________ dense_81 (Dense) (None, 10) 220 _________________________________________________________________ dense_82 (Dense) (None, 3) 33 _________________________________________________________________ dense_83 (Dense) (None, 10) 40 _________________________________________________________________ dense_84 (Dense) (None, 21) 231 ================================================================= Total params: 524 Trainable params: 524 Non-trainable params: 0 _________________________________________________________________ Затем выполняются компиляция и обучение автокодировщика, причем это делается так же, как и для любой нейросетевой модели, за исключением того, что первый аргумент (X) в функции fit и второй аргумент (y) одинаковы: X_train. Ожидается, что сеть произведет на выходе то, что будет максимально близким тому, что было в нее подано: autoencoder_mdl.compile(loss='mean_squared_error', optimizer='adam') autoencoder_history = autoencoder_mdl.fit(X_train.values, X_train.values,\ epochs=16, batch_size=32, validation_split=0.2, verbose=0)
Глава 7. Якорные и контрфактические объяснения 301 Для того чтобы сгенерировать класс PN (класс уместного отрицательного результата), мы выполняем функцию CEM со следующими необходимыми аргументами:  функцией предсказания (predict_nn_fn);  режимом ('PN');  формой, которую мы ожидаем получить обратно (x_test_eval.shape). В дополнение к этим аргументам мы указываем признаковый диапазон и максимальное число итераций, как это делалось с CounterFactualProto. Однако поскольку на этот раз используется автокодировщик (ae_model), мы также включаем гиперпараметр gamma, увеличивающий потерю от ошибки восстановления. После инициализации объяснителя нужно выполнить его обучение на тренировочных данных и создать объяснение с помощью explain для интересующего нас экземпляра (x_test_eval). Далее следует выдать изначальный экземпляр и класс PN, а затем значения признаков и вероятности предсказания для PN: cem_nn_explainer_pn = CEM(predict_nn_fn, 'PN', X_test_eval.shape,\ feature_range=feature_range, gamma=100,\ max_iterations=100, ae_model=autoencoder_mdl) cem_nn_explainer_pn.fit(X_train.values, no_info_type='median') cem_nn_explanation_pn = cem_nn_explainer_pn.explain(X_test_eval, verbose=False) print("%s -> %s" % (class_names[cem_nn_explanation_pn.X_pred],\ class_names[cem_nn_explanation_pn.PN_pred])) print("Вероятности: %s" % predict_nn_fn(cem_nn_explanation_pn.PN)[0]) print("Значения: %s" % cem_nn_explanation_pn.PN[0]) Следующий ниже результат был произведен приведенным выше исходным кодом. Можно сказать, что PN для интересующего вас экземпляра классифицируется как низкий риск. Уместные отрицательные (PN) результаты похожи на контрфакты, так что на это можно было рассчитывать. Результат также содержит значения признаков PN, а вероятности показывают, что PN еле-еле достиг низкого риска с вероятностью 50,11%: Medium/High Risk -> Low Risk Вероятности: [0.50112426 0.49887577] Значения: [23. 0. ... 0.45720586 ... 0. ] Уместные положительные (PP) результаты генерируются точно так же, как уместные отрицательные (PN) результаты, поэтому можно взять тот же самый исходный код и просто заменить все экземпляры PN на PP: cem_nn_explainer_pp = CEM(predict_nn_fn, 'PP', X_test_eval.shape,\ feature_range=feature_range, gamma=100,\ max_iterations=100, ae_model=autoencoder_mdl) cem_nn_explainer_pp.fit(X_train.values, no_info_type='median') cem_nn_explanation_pp = cem_nn_explainer_pp.explain(X_test_eval, verbose=False)
302 Часть II. Освоение методов интерпретации print("%s -> %s" % (class_names[cem_nn_explanation_pp.X_pred],\ class_names[cem_nn_explanation_pp.PP_pred])) print("Вероятности: %s" % predict_nn_fn(cem_nn_explanation_pp.PP)[0]) print("Значения: %s" % cem_nn_explanation_pp.PP[0]) Приведенный выше исходный код произвел следующий ниже результат: Medium/High Risk -> Medium/High Risk Вероятности: [0.29793483 0.70206517] Значения: [0. 0. 0 ... 0. 0. 0.] Из результата видно, PP классифицируется как средний/высокий риск, что и следовало ожидать. И это происходит с вероятностью 70,2%. Однако все значения признаков для PP имеют нули. Оказывается, PP ищет то, что минимально и достаточно присутствует, и обнаруживает, что все нули всё равно будут приводить к классификации средний/высокий риск. Для этого случая использования значения PP не очень понятны. Чтобы всё это осмыслить, давайте создадим кадр данных (salients_df) со значениями признаков для интересующего нас экземпляра (x) PN, тем, что отсутствует в PN (PN-x), а также со значениями PP. Затем обеспечим появление только тех признаков, у которых нет нулей в PP и PN: salients_df = pd.DataFrame({\ 'Feature': X_test.columns,\ 'x': cem_nn_explanation_pn.X[0],\ 'PN': cem_nn_explanation_pn.PN[0],\ 'PN-x': cem_nn_explanation_pn.PN[0] - cem_nn_explanation_pn.X[0],\ 'PP': cem_nn_explanation_pp.PP[0]}) salients_df = salients_df[(salients_df.PP != 0) | (salients_df.PN != 0)] salients_df Приведенный выше исходный код сгенерировал рис. 7.15. Рис. 7.15. Кадр данных, сравнивающий PN, PP и интересующий экземпляр На рис. 7.15 PN можно интерпретировать как контрфакт, но внимание уделяется тому, чего не хватает. Столбец PN-x показывает именно это: больше sex_Female и меньше race_African-American. Метод CEM не руководствуется понятием признаков,
Глава 7. Якорные и контрфактические объяснения 303 кодированных с одним активным состоянием, поэтому он не понимает, что sex_Male и sex_Female являются взаимоисключающими или, если на то пошло, двоичными. Тем не менее согласно PN, из результата можно понять, что раса и пол определяют решение для интересующего нас экземпляра. Метод CEM является контрастивным, потому что обычно противопоставляется то, что должно отсутствовать, путем указания того, что должно в достаточной мере присутствовать. Но наш столбец PP с нулями не имеет смысла. Это всё равно, что заявить, что наличие пустого холста является достаточным фактом считать картину картиной! Говоря о картинах, из-за непрерывно убывающей природы PP наш вариант использования метода CEM не подходит. Однако изображения являются идеальным вариантом использования. Каждый признак представляет собой непрерывное значение, пиксел, которое может интерпретироваться как отсутствие или присутствие света или первичного цвета. В главе 8 мы научимся интерпретировать нейронные сети, обученные с использованием изображений. Миссия выполнена Миссия этой главы состояла в том, чтобы выяснить наличие необъективного систематического смещения в предсказании того, будет ли конкретный ответчик совершать преступления повторно. Мы продемонстрировали, что частота ложноположительных классификаций (FPR) для ответчиков из афроамериканцев в 1,87 раза выше, чем для ответчиков из кавказцев. Это несоответствие было подтверждено инструментом WIT, указывающим на то, что рассматриваемая модель с гораздо большей вероятностью будет неправильно классифицировать положительный класс по признаку расы. Однако это метод глобальной интерпретации, поэтому он не отвечает на наш вопрос относительно конкретного ответчика. Кстати, в главе 11 мы рассмотрим другие методы глобальной интерпретации в отношении необъективности. Чтобы выяснить, была ли модель расово систематически смещена по отношению к рассматриваемому ответчику, мы использовали якоря и контрфакты — оба подхода в своих объяснениях на выходе указывают на расу в качестве первичного признака. Якорь сделал это с относительно высокими точностью и охватом, а руководимые прототипами контрфактические объяснения обнаружили, что у ближайшего из них другая раса. Тем не менее в обоих случаях раса была не единственным признаком в объяснениях. Признаки, как правило, включали любое или все из следующего: число предшествующих судимостей (priors_count), возраст (age), степень преступления (charge_degree) и пол (sex). Несопоставимые правила, касающиеся первых трех в отношении расы, предполагают двойные стандарты, а участие пола предполагает интерсекциональность. Двойные стандарты проявляются, когда правила необъективно применяются к разным группам. Интерсекциональность связана с тем, как пересекающиеся идентичности создают разные системы взаимосвязанных режимов дискриминации. Однако мы знаем, что женщины всех рас, согласно академическим исследованиям, менее склонны к рецидивизму. Тем не менее необходимо себя спросить: есть ли у них какое-либо структурное преимущество, которое делает их с неизбежностью привилегированными в этом контексте? Здесь про-
304 Часть II. Освоение методов интерпретации исходит более сложная динамика, чем кажется на первый взгляд. Суть в том, что, несмотря на все другие факторы, которые взаимодействуют с расой, и при условии, что у нас нет соответствующей криминологической информации, которую мы упускаем, действительно, в этом конкретном предсказании присутствует расовое систематическое смещение. Резюме После прочтения этой главы вы должны научиться использовать якоря, чтобы понимать правила принятия решения, влияющие на классификацию, и контрфакты, чтобы понимать, что собственно нужно скорректировать для изменения предсказываемого класса. Вы также научились диагностировать объективность, используя матрицы путаницы и инструмент Google What-If (WIT). Наконец, мы рассмотрели метод контрастивных объяснений (CEM), чтобы объяснять решение тем, что конкретно минимально присутствует и отсутствует. В следующей главе мы изучим методы интерпретации сверточных нейронных сетей (CNN-сетей). Источник набора данных Хранилище данных ProPublica (2019). Оценочные данные и анализ риска рецидива в системе COMPAS. Изначально взято из https://www.propublica.org/datastore/ dataset/compas-recidivism-risk-score-data-and-analysisv. Справочные материалы  Desmarais S. L., Johnson K. L., Singh J. P. Performance of recidivism risk assessment instruments in U.S. correctional settings // Psychol. Serv. — 2016. — Vol. 13 (3). — P. 206–222. — URL: https://doi.org/10.1037/ser0000075. (Демаре С. Л., Джонсон К. Л., Сингх Дж. П. Результативность инструментов диагностирования риска рецидивизма в исправительных учреждениях США.)  Berk R., Heidari H., Jabbari S., Kearns M., Roth A. Fairness in Criminal Justice Risk Assessments: The State of the Art // Sociological Methods & Research. — 2017. — URL: http://arxiv.org/abs/1703.09207v2. (Олух Р., Хейдари Х., Джаббари С., Кернс М., Рот А. Объективность в диагностировании риска в уголовном правосудии: современное положение дел.)  Angwin J., Larson J., Mattu S., Kirchner L. Machine Bias. There is software that is used across the county to predict future criminals. — URL: https://www.propublica.org/article/machine-bias-risk-assessments-in-criminalsentencing. (Ангвин Дж., Ларсон, Дж., Матту С., Киршнер Л. Машинное систематическое смещение. Существует программно-информационное обеспечение, которое используется по всей стране для предсказания будущих преступников.)
Глава 7. Якорные и контрфактические объяснения 305  Ribeiro M. T., Singh S., Guestrin C. Anchors: High-Precision ModelAgnostic Expla- nations // Proceedings of the AAAI/ACM Conference on AI, Ethics, and Society. — 2018. — URL: https://doi.org/10.1145/3375627.3375830. (Рибейро М. Т., Сингх С., Гестрин С. Якоря: высокопрецизионные модельно-агностические объяснения.)  Rocque M., Posick C., Hoyle J. Age and Crime. The encyclopedia of crime and pun- ishment. — URL: https://doi.org/10.1002/9781118519639. (Рок М., Посик К., Хойл Дж. Возраст и преступность. Энциклопедия преступления и наказания.)  Dhurandhar A., Chen P., Luss R., Tu C., Ting P., Shanmugam K., Das P. Explanations based on the Missing: Towards Contrastive Explanations with Pertinent Negatives. — URL: https://arxiv.org/abs/1802.07623. (Дурандхар А., Чен П., Люсс Р., Ту С., Тинг П., Шанмугам К., Дас, П. Объяснения, основанные на отсутствующем: к контрастивным объяснениям с пертинентными отрицательными результатами.)
8 Визуализация сверточных нейронных сетей До сих пор мы рассматривали только табличные данные и вкратце текстовые данные в главе 6. В данной главе мы обсудим методы интерпретации, исключительно работающие с изображениями и, в частности, вместе с моделями на основе сверточных нейронных сетей (convolutional neural network, CNN), которые тренируют классификаторы изображений. В типичной ситуации модели глубокого обучения рассматриваются как воплощение черного ящика. Однако одним из преимуществ CNN-сети является ее легкость, с которой она достигает визуализации, поэтому можно визуализировать не только исходы, но и каждый этап процесса обучения с использованием карт активаций. Возможность интерпретирования этих шагов редко встречается среди так называемых моделей типа черного ящика. Поняв принцип, на основе которого CNN-сеть усваивает знания, мы научимся использовать современные градиентные методы атрибуции, такие как карты значимости (saliency maps) и градиентное картирование активаций классов (Grad-CAM), для отладки атрибуции классов. Наконец, мы расширим наши практические знания об отладке атрибуции с помощью пертурбационных методов атрибуции, таких как окклюзивная чувствительность, локально-интерпретируемые модельно-агностические объяснения (LIME) и контрастивные объяснения (CEM). Вот основные темы, которые будут охвачены в этой главе:  диагностирование CNN-классификатора с помощью традиционных методов ин- терпретации;  визуализация процесса усвоения с помощью активационных методов;  оценивание ошибочных классификаций с помощью градиентных методов атри- буции;  классификация с помощью пертурбационных методов атрибуции. Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, и shap. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/ Interpretable-Machine-Learning-with-Python/tree/master/Chapter08. skimage, tensorflow, matplotlib, seaborn, cv2, tf-explain, tf-keras-vis, lime, alibi
Глава 8. Визуализация сверточных нейронных сетей 307 Миссия Кассы самообслуживания, позволяющие покупателям оплачивать покупки, были изобретены в 1984 году, но начали появляться в большинстве сетей супермаркетов только на рубеже веков. Однако, несмотря на целый ряд преимуществ, которые эти машины дают как розничным торговцам, так и покупателям, они далеки от совершенства — они подвержены кражам в магазинах, механическим поломкам, дефициту общедоступности и неадекватному опыту обслуживания клиентов. В последнее десятилетие многие компании пытались решать эти проблемы с помощью глубокого обучения. Например, камеры могут отслеживать позу тела, движение продукта и мимику лица. Они могут обнаруживать случаи кражи в магазинах или даже автоматически опускать терминал вниз, чтобы тот был более доступным для клиентов в инвалидных колясках, с помощью обученных моделей глубокого обучения. Еще один новый тренд заключается в том, что в большинстве развитых стран сети магазинов быстрого ослуживания типа минимаркета переживают фазу быстрого роста. Директора таких магазинов изо всех сил стараются не отставать от спроса и платят низкую заработную плату, которая позволяет работать торговой точке допоздна, когда большинство магазинов уже закрыты. Япония в этом тренде идет впереди, поскольку там минимаркеты уже давно доминируют в розничной торговле, а зарплаты сотрудников относительно высоки, поэтому магазины приспособлены к тому, чтобы все было удобно. Клиенты могут получить в этом виде розничной торговли не только круглосуточную доступность, но и очень быструю оплату покупок. Самостоятельное оплачивание покупки бывает медленнее традиционного способа, поэтому оно не было внедрено повсеместно в минимаркетах по всему миру. Тем не менее в некоторых японских сетях оно уже доступно и весьма успешно применяется. Находящаяся за пределами Азии крупная сеть минимаркетов приобрела систему самообслуживания в Японии, чтобы повторить этот успех. Руководители этой компании были в восторге, но быстро поняли, что для работы этой системы все товары должны иметь штрихкоды, что является проблемой только для одного типа товаров. В отличие от японских потребителей, которые не возражают против покупки фруктов в пластиковой упаковке, на рынках, где работает эта сеть, потребители не доверяют цельным фруктам, если они упакованы. К сожалению, пластик необходим для того, чтобы стикер со штрихкодом не отклеивался и не повреждался; к тому же клиенты не доверяют большим стикерам с большим количеством клея непосредственно на своих фруктах. Существует возможность вводить название плода вручную, как это делается в супермаркетах (рис. 8.1), но это замедляет процесс по меньшей мере на 15 секунд на каждый товар, что недопустимо в обстановке минимаркета. Руководители решили использовать видеокамеры в системе самообслуживания для автоматического обнаружения фруктов, чтобы клиентам не приходилось их вводить. С этой целью они определили 16 фруктов и овощей, которые нельзя прода-
308 Часть II. Освоение методов интерпретации вать в упаковке, и заплатили консалтинговой компании по искусственному интеллекту (ИИ) за разработку модели для их классификации. Компания заверила в достижении самых многообещающих результатов: колоссальной точности 99,9%, что было удивительно, учитывая, что разработчики не запрашивали ни знаний предметной области, ни данных. Однако, как только розничный продавец протестировал систему с помощью своих автоматов самообслуживания, он понял, что правильно классифицировалась лишь малая часть, где-то между 1/5 и 1/3, фруктов и овощей. Когда руководители торговой фирмы предъявили претензии консультантами, те были непреклонны в том, что их модель была почти идеальной, и именно камеры системы самообслуживания требовали калибровки. Рис. 8.1. Японская система самообслуживания с выделенной кнопкой "Фрукты" (только для целей иллюстрации) В целях получения второго мнения и честного оценивания модели сеть минимаркетов обратилась в еще одну консалтинговую фирму по ИИ — в вашу фирму! Подход Ни один метод интерпретации не совершенен, и даже в лучшем случае он сможет рассказать вам только одну часть истории. Поэтому вы решили сначала диагностировать предсказательную результативность модели с использованием традиционных методов интерпретации, включая следующие:  кривые ROC и ROC-AUC;  матрицы путаницы и все метрики, выводимые из них (точность, прецизион- ность, полнота, F1).
Глава 8. Визуализация сверточных нейронных сетей 309 Затем вы хотите оценить модель с помощью двух активационных методов:  промежуточной активации;  максимизации активации. За этим шагом следует оценивание решений с помощью трех градиентных методов:  карты значимости (saliency maps);  градиентного картирования активаций классов (Grad-CAM);  интегрированных градиентов. Затем следуют три пертурбационных метода:  окклюзивная чувствительность;  локально интерпретируемые модельно-агностические объяснения (LIME);  контрастивные объяснения (CEM). И, наконец, бонусный метод, основанный на обратном распространении:  глубокий объяснитель SHAP. Будем надеяться, что к концу этого процесса вы поймете, почему модель не демонстрирует ту результативность, какую она должна демонстрировать, и как это исправить. Вы также можете задействовать многочисленные графики и визуализации, которые вы создадите, чтобы донести эту историю до руководителей компанииоператору сети минимаркетов. Подготовительные работы Бóльшую часть исходного кода этого примера можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter08/FruitClassifier_part1.ipynb, вплоть до разд. "Объяснение Классификаций с помощью перестановочных методов атрибуции". Материалы по указанному разделу находятся по адресу: https://github.com/PacktPublishing/ Interpretable-Machine-Learning-with-Python/blob/master/Chapter08/ FruitClassifier_part2.ipynb. Загрузка библиотек Для выполнения этого примера необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas, numpy и sklearn (scikit-learn) для манипулирования наборами данных;  tensorflow для обучения моделей и предсказания с помощью них;  matplotlib, seaborn, cv2, skimage, tf-explain, tf-keras-vis, lime, alibi и shap для ви- зуализации интерпретаций.
310 Часть II. Освоение методов интерпретации Сначала необходимо их все загрузить следующим образом: import math import os import mldatasets import pandas as pd import numpy as np from sklearn import preprocessing, metrics import tensorflow as tf from tensorflow import keras from keras.utils.data_utils import get_file import matplotlib.pyplot as plt from matplotlib import cm import seaborn as sns import cv2 # Только ЧАСТЬ 1 from tf_explain.core.activations import ExtractActivations from tf_keras_vis.activation_maximization import ActivationMaximization from tf_keras_vis.saliency import Saliency from tf_keras_vis.utils import normalize from tf_keras_vis.gradcam import GradcamPlusPlus from tf_explain.core.integrated_gradients import IntegratedGradients # Только ЧАСТЬ 2 from skimage.segmentation import mark_boundaries from tf_explain.core.occlusion_sensitivity import OcclusionSensitivity import lime from lime import lime_image from alibi.explainers import CEM import shap Давайте проверим, что TensorFlow загрузил правильную версию с помощью вот этой инструкции. Версия должна быть 2.0 или выше: print(tf.__version__) Изучение проблемы и подготовка данных Использованные для обучения модели данные были созданы в ходе академических исследований и находятся в открытом доступе на веб-сайте Kaggle (https://www.kaggle.com/moltean/fruits). Набор данных называется Fruit 360, потому что фрукты вращались электромотором в то время, как камера делала снимки со всех сторон и более чем на одной оси. Снимки были сделаны при стабильном освещении на белом листе бумаги, но фон был заменен белым с помощью алгоритма, поэтому на изображениях отсутствуют тени. Набор данных Fruit 360 содержит более 100 классов фруктов и овощей. Вы будете загружать те же самые данные, за исключением того, что они имеют только 16 классов, соответствующих тем, которые руководители сети минимаркетов хотели классифицировать. Он также содержит в своем составе небольшую валидационную выборку с несколькими фотогра-
Глава 8. Визуализация сверточных нейронных сетей 311 фиями, на которых модель тестировалась и которые по общему согласию похожи на фрукты и овощи, поставляемые в магазины сети. Валидационные изображения были предоставлены в размерностях, необходимых для модели, и с изначально более высоким разрешением. Мы загружаем данные следующим образом в четыре набора данных, соответствующих обучению, тестированию, валидации, а также изначальной валидации: (X_train, X_test, X_val, X_val_orig, y_train, y_test,\ y_val, y_val_orig) = mldatasets.load("fruits-360", prepare=True) Следующий ниже исходный код позволяет подтвердить соответствие размеров массивов NumPy нашим ожиданиям: print('X_train:%s' % (X_train.shape,)) print('X_test:%s' % (X_test.shape,)) print('X_val:%s' % (X_val.shape,)) print('X_val_orig:%s' % (X_val_orig.shape,)) print('y_train:%s' % (y_train.shape,)) print('y_test:%s' % (y_test.shape,)) print('y_val:%s' % (y_val.shape,)) print('y_val_orig:%s' % (y_val_orig.shape,)) Приведенный выше исходный код выводит размер каждого массива. Из распечатки следует, что первое число в каждом кортеже X совпадает с соответствующим кортежем y. Второе число в кортеже y указывает на то, что метки еще не кодированы с одним активным состоянием и находятся в текстовой либо порядковой форме, потому что в противном случае вместо 1 было бы 16. Кроме того, все массивы X имеют одинаковые размеры: шириной 100 и высотой 100, — и 3 канала, за исключением изначальной валидации (x_val_orig), которая ожидаемо будет иметь более высокое разрешение. Для генерирования результатов набор данных изначальной валидации нам не понадобится, поэтому ничего страшного, что он не соответствует требованиям модели к размерности: X_train: X_test: X_val: X_val_orig: y_train: y_test: y_val: y_val_orig: (7872, 100, 100, 3) (2633, 100, 100, 3) (64, 100, 100, 3) (64, 400, 400, 3) (7872, 1) (2633, 1) (64, 1) (64, 1) Если применить инструкцию print(x_train[0]), то можно увидеть цепочку чисел 255. Это максимальное число, используемое для выражения красного, зеленого и синего цвета в изображении. Однако в целях эффективности и надежности CNN-сеть обычно тренируют значениями в виде чисел с плавающей точкой в интервале между нулем и единицей. С этой целью нам придется нормализовать массивы X_train, X_test и X_val следующим образом: X_train = X_train.astype('float32')/255 X_test = X_test.astype('float32')/255 X_val = X_val.astype('float32')/255
312 Часть II. Освоение методов интерпретации Нам нужно будет выполнить еще один шаг предобработки, а именно, кодирование меток y с одним активным состоянием (one-hot encode, OHE), потому что указанная форма понадобится для оценивания предсказательной результативности модели. После инициализации библиотечного класса OneHotEncoder нам нужно выполнить его обучение, используя тренировочные данные (X_train). Из кодировщика также можно извлечь категории (categories) и поместить их в список (fruits_l), чтобы подтвердить, что все 16 элементов там есть: ohe = preprocessing.OneHotEncoder(sparse=False) ohe.fit(y_train) fruits_l = ohe.categories_[0].tolist() print(fruits_l) Приведенный выше исходный код должен напечатать следующий ниже список. Он должен быть в алфавитном порядке, т. к. папки с изображениями расположены в таком порядке. Обычно можно с уверенностью допустить, что кодировщик использовал этот порядок. Однако, если это допущение неверно, то мы сможем узнать об этом, когда будем оценивать модельную результативность. Например, если бы кодирование было выполнено с категориями в обратном алфавитном порядке, то предсказания классов также были бы в обратном порядке1: ['Apple Golden', 'Apple Granny Smith', 'Apple Red', 'Avocado', 'Banana', 'Clementine', 'Grapefruit Pink', 'Mango Red', 'Nectarine', 'Onion Red', 'Onion White', 'Orange', 'Peach', 'Pear', 'Pomegranate', 'Tomato'] Ради воспроизводимости всегда следует инициализировать свои случайные начальные числа следующим образом: rand = 9 os.environ['PYTHONHASHSEED']=str(rand) np.random.seed(rand) tf.random.set_seed(rand) Общеизвестно, что в глубоком обучении выявлять детерминизм очень трудно, и он нередко зависит от сеанса, платформы и архитектуры. Если вы используете графический процессор NVIDIA, то можно инсталлировать библиотеку tensorflowdeterminism, которая находится по адресу: https://github.com/NVIDIA/frameworkdeterminism. Теперь давайте взглянем на изображения, которые имеются в наборах данных. Мы знаем, что тренировочный и тестовый наборы очень похожи, поэтому начнем с тестового набора данных. Можно прокрутить все классы fruits_l в цикле, случайно отбирая по одному из тестового набора данных с помощью функции np.random.choice. Мы помещаем каждое изображение в ячейку сетки 4  4 с меткой класса над ним: plt.subplots(figsize=(10,10)) for f, fruit in zip([*range(len(fruits_l))], fruits_l): 1 Перевод для справки: яблоко Голден, яблоко Грэнни Смит, яблоко красное, авокадо, банан, клементин, розовый грейпфрут, манго красный, нектарин, лук красный, лук белый, апельсин, персик, груша, гранат, помидор. — Прим. перев.
Глава 8. Визуализация сверточных нейронных сетей 313 plt.subplot(4, 4, f+1) plt.title(fruits_l[f], fontsize=12) idx = np.random.choice(np.where(y_test[:,0] == fruit)[0], 1)[0] plt.imshow(X_test[idx], interpolation='spline16') plt.axis("off") plt.show() Приведенный выше исходный код генерирует рис. 8.2. Видно, что по краям изображений плодов наблюдается значительная пикселизация; некоторые плоды выглядят темнее других, и некоторые изображения сделаны под странными углами. Рис. 8.2. Случайная выборка из тестового набора данных Теперь давайте сделаем то же самое для валидационного набора данных, чтобы сравнить его с тестовым/тренировочным наборами данных. Можно использовать тот же исходный код, что и раньше, за исключением замены y_test на yval: plt.subplots(figsize=(10,10)) for f, fruit in zip([*range(len(fruits_l))], fruits_l): plt.subplot(4, 4, f+1) plt.title(fruits_l[f], fontsize=12) idx = np.random.choice(np.where(y_val[:,0] == fruit)[0], 1)[0] plt.imshow(X_val[idx], interpolation='spline16') plt.axis("off") plt.show() Приведенный выше исходный код генерирует рис. 8.3. Из рисунка видно, что в валидационном наборе меньше пикселизации, и лучше освещенные фрукты и овощи в основном запечатлены под углом сверху и сбоку.
314 Часть II. Освоение методов интерпретации Рис. 8.3. Случайная выборка из валидационного набора данных В этой главе нам не нужно будет обучать CNN-сеть. К счастью, она была предоставлена нам клиентом. Загрузка CNN-модели Быстро загрузить модель и показать сводную информацию о ней можно следующим образом: url = '''https://github.com/PacktPublishing/ Interpretable-Machine-Learning-withPython/ blob/master/models/CNN_fruits_final.hdf5?raw=true''' model_path = get_file('CNN_fruits_final.hdf5', url) cnn_fruits_mdl = keras.models.load_model(model_path) cnn_fruits_mdl.summary() Приведенный выше фрагмент исходного кода выводит на экран следующую ниже сводку. В ней есть практически все, что нам нужно знать о модели. Она имеет четыре сверточных слоя (Conv2D), за каждым из которых следует слой сведения на основе максимума (MaxPooling2D)2. Затем она имеет первый слой отсева (Dropout), который служит для регуляризации, за которым следует уплощающий (Flatten) слой и полносвязный (Dense) слой. Затем перед выходом есть еще один слой отсева 2 Сведение на основе максимума (max pooling), или сведение по максимуму — взятие максимального значения окна окрестных признаков с целью редукции числа активаций; дает инвариантность сдвига в изображении. Операция сведения может также выполняться на основе других функций: минимума, среднего значения, медианы и пр. — Прим. перев.
Глава 8. Визуализация сверточных нейронных сетей 315 (Dropout). Естественно, 16 нейронов в этом последнем слое соответствуют каждому классу: Model: "CNN_fruits" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 99, 99, 16) 208 _________________________________________________________________ maxpool2d_1 (MaxPooling2D) (None, 49, 49, 16) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 48, 48, 32) 2080 _________________________________________________________________ maxpool2d_2 (MaxPooling2D) (None, 24, 24, 32) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 23, 23, 64) 8256 _________________________________________________________________ maxpool2d_3 (MaxPooling2D) (None, 11, 11, 64) 0 _________________________________________________________________ conv2d_4 (Conv2D) (None, 10, 10, 128) 32896 _________________________________________________________________ maxpool2d_4 (MaxPooling2D) (None, 5, 5, 128) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 5, 5, 128) 0 _________________________________________________________________ flatten (Flatten) (None, 3200) 0 _________________________________________________________________ dense_1 (Dense) (None, 150) 480150 _________________________________________________________________ dropout_2 (Dropout) (None, 150) 0 _________________________________________________________________ dense_2 (Dense) (None, 16) 2416 ================================================================= Total params: 526,006 Trainable params: 526,006 Non-trainable params: 0 _________________________________________________________________ Диагностика CNN-классификатора традиционными методами интерпретации Можно легко вывести точности для всех трех наборов данных, используя собственную модельную функцию оценивания evaluate, следующим образом: train_score = cnn_fruits_mdl.evaluate(X_train, ohe.transform(y_train), verbose=0) test_score = cnn_fruits_mdl.evaluate(X_test, ohe.transform(y_test), verbose=0) val_score = cnn_fruits_mdl.evaluate(X_val, ohe.transform(y_val), verbose=0) print('Тренировочная точность:\t{:.1%}'.format(train_score[1])) print('Тестовая точность:\t{:.1%}'.format(test_score[1])) print('Валидационная точность:\t{:.1%}'.format(val_score[1]))
316 Часть II. Освоение методов интерпретации Приведенный выше фрагмент исходного кода печатает следующий результат: Тренировочная точность: 100.0% Тестовая точность: 99.9% Валидационная точность: 31.2% Действительно, можно ожидать, что модель всегда будет достигать 100%-ной точности обучения, если обучать ее в течение достаточного числа эпох с использованием оптимальных гиперпараметров. Почти совершенной тестовой точности достичь труднее, и все зависит от того, насколько эти два набора различаются. Мы знаем, что тестовый набор данных представляет собой просто выборку изображений из одной и той же коллекции, поэтому не особо удивительно то, что была достигнута такая высокая точность (99,9%). При обсуждении классификационных моделей в деловой обстановке нередко непрофессионалы заинтересованы только в одном числе: точности. Легко позволить этой метрике возглавить дискуссию, но в ней есть гораздо больше нюансов. Например, разочаровывающая валидационная точность (31,2%) может означать многое. Она может означать, что пять классов получают идеальную классификацию, а все остальные — нет, или что 10 классов классифицируются неправильно только наполовину. Есть много объяснений того, что могло бы произойти. Рис. 8.4. Матрица путаницы для тестового набора данных
Глава 8. Визуализация сверточных нейронных сетей 317 В любом случае при решении многоклассовой классификационной задачи точность ниже 50% бывает не такой плохой, как кажется. При более или менее равной разбивке 16 классов необходимо принять к сведению, что уровень отсутствия информации, вероятно, составит около 7%, поэтому 31,2% все еще на порядки выше этого показателя. На самом деле скачок до 100% меньше! Для практика машинного обучения это означает, что если мы судим, основываясь исключительно на результатах валидационной точности, то модель все еще усваивает что-то ценное, что можно улучшить. Сначала мы оценим модель, используя тестовый набор данных с помощью функции evaluate_multiclass_mdl. Аргументы включают модель (cnn_fruits_mdl), наши тестовые данные (x_test) и соответствующие метки (y_test), а также имена классов (fruits_l) и кодировщик (ohe). Наконец, нам не нужен параметр для построения графика кривых ROC, т. к. они будут идеальными (plot_roc=False). Эта функция возвращает предсказанные метки и вероятности, которые можно хранить в переменных для последующего использования: y_test_pred, y_test_prob = mldatasets.evaluate_multiclass_mdl(\ cnn_fruits_mdl, X_test, y_test, fruits_l, ohe, plot_roc=False) Рис. 8.5. Почти совершенные метрики предсказательной результативности для тестового набора данных
318 Часть II. Освоение методов интерпретации Приведенный выше исходный код генерирует рис. 8.4 с матрицей путаницы и рис. 8.5 с метриками результативности по каждому классу. Несмотря на то что матрица путаницы на рис. 8.4, по-видимому, предлагает совершенную классификацию, увидев разбивку точности и полноты на рис. 8.5, вы сразу же определите, что в модели были проблемы с двумя сортами яблок, нектаринами и грушами. Теперь давайте повторим тот же фрагмент исходного кода, но для валидационного набора данных. На этот раз мы хотим видеть кривые ROC (plot_roc=True), но только средние значения, а не по классам (plot_roc_class=False), потому что в каждом классе всего четыре изображения. Учитывая малое число образцов, в матрице путаницы можно показывать не проценты, а числа (pct_matrix=False): y_val_pred, y_val_prob = mldatasets.evaluate_multiclass_mdl(\ cnn_fruits_mdl, X_val, y_val, fruits_l, ohe,\ plot_roc=True, plot_roc_class=False, pct_matrix=False) Приведенный выше фрагмент исходного кода сгенерировал кривую ROC на рис. 8.6, матрицу путаницы на рис. 8.7 и таблицу классификации на рис. 8.8. Рис. 8.6. Кривая ROC для валидационного набора данных
Глава 8. Визуализация сверточных нейронных сетей 319 График валидационной ROC (см. рис. 8.6) показывает макросреднюю и микросреднюю кривые ROC. Разница между ними заключается в том, как они рассчитываются. Макрометрики рассчитываются по каждому классу независимо, а затем усредняются. С другой стороны, микросредние учитывают вклад или представленность каждого класса. Как правило, микросредние более надежны. Рис. 8.7. Матрица путаницы для тестового набора данных Если мы взглянем на матрицу путаницы на рис. 8.7, то сможем сказать, что только бананы и грейпфруты получают классификацию четыре из четырех. Однако многие плоды классифицируются как бананы и грейпфруты ошибочно, в особенности грейпфруты! С другой стороны, многие плоды никогда не классифицируются надлежащим образом, например все сорта яблок и помидоры. Многие из них похожи по форме или цвету на другие плоды, поэтому можно понять, как это происходит, но каким образом авокадо классифицируется ошибочно как банан или красный лук?
320 Часть II. Освоение методов интерпретации Рис. 8.8. Предсказательная результативность для тестового набора данных Метрики предсказательной результативности на рис. 8.8 для валидационного набора данных согласуются с тем, что мы видели в матрице путаницы. Грейпфруты и бананы имеют высокую полноту, но низкую точность, и половина классов имеет 0% для обоих. Определение ошибочных классификаций, на которых следует сосредоточиться Мы уже заметили некоторые интересные ошибочные классификации, на которых можно сосредоточиться.  Ложноположительные классификации грейпфрутов: 15 из 64 образцов в ва- лидационном наборе данных были классифицированы ошибочно как грейпфруты. Это почти четверть! Что такого в грейпфруте, что его так легко спутать с другими плодами в соответствии с моделью?  Ложноотрицательные классификации авокадо: авокадо имеет форму груши, но более темный цвет и кожуру с уникальной текстурой аллигатора. Нелегко понять, как этот плод мог быть классифицирован ошибочно.
Глава 8. Визуализация сверточных нейронных сетей 321 Чтобы понять, как произошла ошибочная классификация, необходимо оценить количество истинноположительных предсказаний классификации тех же самых фруктов. В целях визуализирования предстоящих задач можно создать кадр данных (preds_df) с истинными метками (y_true) в одном столбце и предсказанными метками в другом (y_pred). И в целях понимания степени уверенности моделей в отношении этих предсказаний, можно создать еще один кадр данных с вероятностями (probs_df). Можно генерировать итоговые значения столбцов для этих вероятностей, чтобы сортировать столбцы в соответствии с плодами, в которых модель наиболее уверена по всем образцам. Затем можно конкатенировать кадр предсказаний с первыми восемью столбцами из кадра вероятностей, поскольку мы знаем, что только половина плодов вообще классифицируется: preds_df = pd.DataFrame({'y_true':y_val[:,0], 'y_pred':y_val_pred}) probs_df = pd.DataFrame(y_val_prob*100).round(1) probs_df.loc['Total']= probs_df.sum().round(1) probs_df.columns = fruits_l probs_df = probs_df.sort_values('Total', axis=1, ascending=False) probs_df.drop(['Total'], axis=0, inplace=True) probs_final_df = probs_df.iloc[:,0:8] preds_probs_df = pd.concat([preds_df, probs_final_df], axis=1) Теперь давайте выведем кадр данных с цветовой кодировкой для экземпляров предсказания, которые мы заинтересованы продиагностировать. С одной стороны, у нас есть ложноположительные классификации грейпфрута, а с другой — ложноотрицательные классификации авокадо. Но у нас также есть и истинноположительные классификации. Мы все их выделили, но нас интересуют только те, которые относятся к ошибочным классификациям. Наконец, мы выделили жирным шрифтом все вероятности более 50% и скрыли все вероятности 0%, чтобы было легче определять любую более высокую вероятность: pd.set_option('precision', 1) preds_probs_df.style.apply(\ lambda x: ['background: lightgreen' if (x[0] == x[1]) else '' for i in x], axis=1).apply(\ lambda x: ['background: orange' if (x[0] != x[1] and x[1] == 'Grapefruit Pink') else '' for i in x], axis=1).apply(\ lambda x: ['background: yellow' if (x[0] != x[1] and x[0] == 'Avocado') else '' for i in x], axis=1).apply(\ lambda x: ['font-weight: bold' if isinstance(i, float) and i >= 50 else '' for i in x],
322 Часть II. Освоение методов интерпретации axis=1).apply(\ lambda x: ['color:transparent' if i == 0.0 else '' for i in x], axis=1) Рис. 8.9. Таблица со всеми 64 образцами в валидационном наборе данных и их истинными и предсказанными метками, а также их предсказанными вероятностями
Глава 8. Визуализация сверточных нейронных сетей 323 Приведенный выше фрагмент исходного кода создает рис. 8.9. По выделенным позициям можно определить, какие являются ложноположительными классификациями грейпфрута и ложноотрицательными классификациями авокадо, а также какие для этих плодов будут истинноположительными классификациями: № 36–39 для грейпфрута и № 5 и 7 для авокадо. Индексы этих экземпляров можно легко сохранить в списках, применив следующий ниже исходный код. Благодаря этому в будущем мы сможем перебирать эти списки в цикле, чтобы проводить диагностику отдельных предсказаний или брать с их помощью подмножества массивов для выполнения интерпретационных задач для всей группы целиком. Как можно заметить, у нас есть списки для всех четырех групп: avocado_FN_idxs = preds_df[(preds_df['y_true'] != preds_df['y_pred']) & (preds_df['y_true'] == 'Avocado')].index.to_list() avocado_TP_idxs = preds_df[(preds_df['y_true'] == preds_df['y_pred']) & (preds_df['y_true'] == 'Avocado')].index.to_list() grapefruit_FP_idxs = preds_df[(preds_df['y_true'] != preds_df['y_pred']) & (preds_df['y_pred'] == 'Grapefruit Pink')].index.to_list() grapefruit_TP_idxs = preds_df[(preds_df['y_true'] == preds_df['y_pred']) & (preds_df['y_pred'] == 'Grapefruit Pink')].index.to_list() Теперь, когда все наши данные предварительно обработаны, модель загружена полностью и содержит список групп отлаживаемых предсказаний. Можно двигаться вперед. И пусть же начнется интерпретация! Визуализирование процесса усвоения с помощью активационных методов Прежде чем мы перейдем к обсуждению активаций, слоев, фильтров, нейронов, градиентов, сверток, ядер и всех фантастических элементов, составляющих CNNсеть, давайте сначала проведем краткую ревизию механизма работы CNN-сети и данной сети в частности. Сверточный слой (или слой свертки) является краеугольным строительным блоком CNN-сети. Он свертывает данные на входе с помощью усваивающих фильтров, которые относительно невелики, но применяются по всей ширине, высоте и глубине на определенных расстояниях или шагах сдвига (рис. 8.10). В случае CNN-сети плодов первый сверточный слой содержит 16 фильтров с ядром 2  2 , сдвиговым шагом по умолчанию 1  1 и без дополнения нулем (valid). Каждый фильтр производит двуразмерную активационную карту (так называемую карту признаков). Она называется активационной картой, потому что обозначает позиции активаций на изображениях — другими словами, места, где расположены конкретные "признаки". В этом контексте признак является абстрактным пространственным представлением, которое в процессе нижестоящей обработки отражается в усвоенных
324 Часть II. Освоение методов интерпретации весах полносвязных (dense) слоев. Фильтры соответствуют маске, потому что они в итоге активируют области активационной карты, когда на входном изображении обнаруживаются определенные шаблоны. Но прежде чем перейти к плотным слоям, необходимо выполнить редукцию размерностей наших фильтров, повторяя ее до тех пор, пока они не достигнут приемлемого размера. Например, если разгладить результат первой свертки ( 99  99  16 ), то у нас будет почти 157 000 признаков. Думаю, мы все согласимся с тем, что это было бы слишком много для подачи в полносвязный слой. Даже если бы мы использовали достаточное число нейронов для обработки этой рабочей нагрузки, мы, вероятно, не смогли бы уловить достаточно пространственных представлений, чтобы нейронная сеть могла понять смысл изображений. По этой причине сверточные слои часто располагаются в паре со слоями сведения, которые выполняют понижающий отбор данных на входе, в данном случае сведение на основе максимума, которое берет максимальное значение в окне или ядре. Размер ядра в этом случае составляет 2  2 , т. е. сведение берет одно значение из каждого четырехзначного кластера, по существу, уменьшая ширину и высоту данных на выходе вдвое. Рис. 8.10. Архитектура CNN-сети плодов Мы также наслаиваем дополнительные слои свертки, чтобы поочередно улавливать более крупные представления. По мере того как фильтры становятся меньше по ширине и высоте, усвоенные представления будут становиться крупнее. Другими словами, первый сверточный слой может быть посвящен таким деталям, как текстура, следующий — краям, а последний — фигурам. Затем необходимо разгладить данные на выходе из сверточных слоев, чтобы передать их многослойному персептрону, который с этого момента берет управление на себя.
Глава 8. Визуализация сверточных нейронных сетей 325 К счастью, разглаженный результат на выходе имеет более приемлемый размер: 3200 признаков. В данной CNN-сети есть только два плотных или полносвязных слоя. В первом из них 150 нейронов, а в последнем — 16, которые, используя активацию softmax, выводят вероятности в интервале от 0 до 1 по каждому классу. В CNN-сети плодов было задействовано несколько слоев отсева, чтобы помогать в регуляризации усвоения. Их можно полностью проигнорировать, потому что при генерировании результата они игнорируются. Не переживайте, если все это было не совсем понятно! В последующих разделах с использованием активаций, градиентов и пертурбаций будет наглядно продемонстрировано то, каким образом сеть, по всей видимости, усвоила или не усвоила представления изображений плодов. Промежуточные активации При вычислении результата изображение поступает на вход сети, а предсказание выводится на выходе, проходя через каждый отдельный слой. Однако одно из преимуществ наличия последовательной и многослойной архитектуры заключается в том, что можно извлекать данные на выходе из любого слоя, а не только из завершающего. Промежуточные активации — это просто данные на выходе из любого слоя свертки или сведения. Они являются активационными картами, потому что после применения функции активации более яркие пятна соотносятся с признаками изображения. В данном случае модель использовала активацию ReLU на всех сверточных слоях — именно она и активирует пятна. Нас интересуют только промежуточные активации сверточных слоев, потому что слои сведения — это просто прореженные (в результате понижающего отбора) версии этих слоев. Почему бы вместо этого не посмотреть версию с более высокой разрешающей способностью? Сейчас нам нужно пройтись по всем сверточным слоям и извлечь активации для каждого из них. С этой целью мы создадим список с именами сверточных слоев (target_layers) и инициализируем объяснитель tf_explain с помощью ExtractActivations(). Это делается следующим образом: target_layers = ['conv2d_1', 'conv2d_2', 'conv2d_3', 'conv2d_4'] explainer = ExtractActivations() Можно прокрутить все целевые слои и все истинноположительные валидационные образцы авокадо в цикле и создать активационные карты по каждому слою и комбинации образцов. Это делается с помощью функции explain. Она берет образец изображения в очень специфическом формате, т. е. с дополнительной размерностью (1, 100, 100, 3) внутри кортежа, во втором элементе которого указано None. Для этого также требуется модель (cnn_fruits_mdl) и имя слоя (target_layer). Она выводит активационные карты для каждого фильтра в слое (viz_img). В целях визуализации можно использовать функцию compare_img_pred_viz, которая помещает изначальный образец изображения (orig_img) рядом с изображением, полученным методом интерпретации (viz_img). Она также принимает фактическую метку образца (y_true) и предсказываемую метку (y_pred).
326 Часть II. Освоение методов интерпретации Можно опционально предоставить ряд pandas с вероятностями для этого предсказания (probs_s) и заголовком title: for target_layer in target_layers: for idx in avocado_TP_idxs: orig_img = X_val_orig[idx] viz_img = explainer.explain((np.array([X_val[idx]]), None),\ cnn_fruits_mdl, target_layer) y_true = y_val[idx,0] y_pred = y_val_pred[idx] probs_s = probs_df.loc[idx] title = '{} Активации для авокадо № {}'.format(target_layer, idx) mldatasets.compare_img_pred_viz(orig_img, viz_img,\ y_true, y_pred, probs_s, title=title) Приведенный выше исходный код генерирует в общей сложности восемь изображений, включая рис. 8.11 и 8.12. Как видно из рис. 8.11, первый сверточный слой, похоже, улавливает колючую текстуру кожуры авокадо, а также его контуры. Рис. 8.11. Промежуточные активации для первого сверточного слоя для авокадо № 7 На рис. 8.12 показано, как на втором сверточном слое сеть понимает контуры авокадо лучше, а яркие области говорят о большей глубине и выпуклости. В целях перемешивания давайте выполним интерпретацию промежуточных активаций последних двух сверточных слоев с грейпфрутами. Для этого мы просто повторяем тот же исходный код, но заменяем avocado на grapefruit. У этого плода есть 4 истинноположительных классификации, поэтому в итоге мы получим 16 объяснений. Приведенный ниже модифицированный фрагмент исходного кода сгенери-
Глава 8. Визуализация сверточных нейронных сетей 327 рует рис. 8.13 и 8.14. Третий сверточный слой на рис. 8.13, похоже, отражает вытянутую форму грейпфрута, а также зеркальные блики. Рис. 8.12. Промежуточные активации для второго сверточного слоя для авокадо № 5 Рис. 8.13. Промежуточные активации для третьего сверточного слоя для грейпфрута № 38
328 Часть II. Освоение методов интерпретации На рис. 8.14 можно наблюдать, как в четвертом сверточном слое на активационных картах нет других деталей, кроме округлости грейпфрута. Рис. 8.14. Промежуточные активации для четвертого сверточного слоя для грейпфрута № 39 Извлечение промежуточных активаций может давать вам некоторое представление о каждом конкретном образце. Другими словами, это метод локальной модельной интерпретации. Но что делать, если мы хотим разобраться в работе активации глобально? Как раз этим и занимается для нас максимизация активаций. Максимизация активации В целях глобальной интерпретации в расчете на фильтр к сверточному слою можно выполнить максимизацию активации. Как следует из названия, мы максимизируем активацию. Мы делаем это с помощью градиентного восхождения. Если вы помните, градиентный спуск задействуется во время обучения, чтобы отыскивать веса, которые обеспечивают наименьшее значение функции потерь, чтобы входные данные максимально совпадали с желаемой меткой. При максимизации активации мы делаем обратное — отсюда и название "градиентное восхождение". Мы держим веса постоянными и модифицируем входное изображение до тех пор, пока не отыщем то, на что каждый фильтр реагирует максимально. Сначала нам нужно будет определить две простые функции — одну для модифицирования модели, а другую для возврата конкретного значения потерь. Нам необходимо модифицировать модель, потому что, как и в случае с методом промежуточных активаций, нам нужно притвориться, что модель заканчивается в выбран-
Глава 8. Визуализация сверточных нейронных сетей 329 ном целевом слое. В отличие от промежуточной активации мы делаем активации целевого слоя линейными. Обратите внимание, мы ожидаем, что сверточные слои будут иметь нелинейные функции активации (например, ReLU). Что касается функции потерь, то она нам нужна, чтобы возвращать потерю для одиночного фильтра: def model_modifier(mdl): global target_layer target = mdl.get_layer(name=target_layer) new_mdl = tf.keras.Model(inputs=mdl.inputs, outputs=target.output) new_mdl.layers[-1].activation = tf.keras.activations.linear return new_mdl def loss(output): global filter_num return output[..., filter_num] Теперь, когда у нас определены необходимые функции, можно прокрутить все 4 сверточных целевых слоя в цикле и сформировать сетку с 16 фильтрами по каждому. Первый слой содержит 16 фильтров, но для всех остальных они выбираются случайно с помощью функции np.random.choice. Затем она прокручивает каждый фильтр в цикле и вычисляет изображение, которое максимизирует каждый фильтр. Это делается сперва созданием экземпляра библиотечного класса ActivationMaximization с моделью (cnn_fruits_mdl), функцией модифицирования модели (model_modifier) и инструкцией клонировать модель вместо того, чтобы модифицировать оригинал (clone=True). Затем функция потерь loss передается в созданный экземпляр ActivationMaximization, который генерирует изображение: # Для скольких фильтров нужно построить график # активационной максимизации в каждом слое num_filters = 16 # Вычислить размер (ширину или высоту) изображения на основе num_filters gridsize = math.ceil(math.sqrt(num_filters)) # Пройтись по каждому целевому слою и... for target_layer in target_layers: # Случайно отбирать индекс фильтров из общей суммы в слое for layer in cnn_fruits_mdl.layers: if layer.name == target_layer: total_filters = layer.filters if total_filters == num_filters or total_filters < num_filters: filter_num_l = [*range(num_filters)] else: filter_num_l = list(np.random.choice([*range(total_filters)],\ num_filters)) # Построить график активационной # максимизации для каждого случайного фильтра fig = plt.figure(figsize=(10,10)) for f, filter_num in zip([*range(len(filter_num_l))], filter_num_l):
330 Часть II. Освоение методов интерпретации plt.subplot(gridsize, gridsize, f+1) plt.title('Filter # {}'.format(filter_num), fontsize=12) activation_maximization = ActivationMaximization(cnn_fruits_mdl,\ model_modifier,\ clone=True) activation = activation_maximization(loss) plt.imshow(activation[0].astype(np.uint8), interpolation='spline16') plt.axis("off") fig.suptitle('Слой {}'.format(target_layer), fontsize=18, weight='bold') plt.subplots_adjust(bottom=0, top=0.92) plt.show() Рис. 8.15. Максимизация активации в отношении первого сверточного слоя модели Приведенный выше фрагмент исходного кода генерирует 4 изображения, включая рис. 8.15 и 8.16. Первый сверточный слой на рис. 8.15, по-видимому, не макси-
Глава 8. Визуализация сверточных нейронных сетей 331 мально реагирует на какой-либо конкретный шаблон, кроме вариантов цвета. Учтите, что этот шаблон имеет общий характер, и, поскольку их всего 16, они не могут быть очень конкретными. Рис. 8.16. Максимизация активации в отношении четвертого сверточного слоя модели К тому времени, когда сеть достигает четвертого сверточного слоя, фильтры, повидимому, максимально реагируют на все виды узоров в горошек. Они могут быть гораздо более конкретными, потому что в этом слое находится 128 фильтров. Для плодов максимизация активации не особо полезна, поскольку многие плоды имеют одинаковые формы и узоры. Однако, если бы это был классификатор кошек и собак, то вы смогли бы четко различать некоторые шаблоны, принадлежащие любому классу. Однако суть здесь заключалась в том, чтобы дать представление о роли фильтров в CNN-сети. Далее мы задействуем градиенты, чтобы понять, почему модель классифицирует ошибочно.
332 Часть II. Освоение методов интерпретации Оценивание ошибочных классификаций с помощью градиентных методов атрибуции Градиентные методы рассчитывают атрибуционные карты для каждой классификации как с прямым, так и с обратным прохождением по CNN-сети. Как следует из названия, в этих методах используются градиенты в обратном прохождении для вычисления атрибуционных карт. Все эти методы являются методами локальной интерпретации, поскольку они производят только одну интерпретацию на образец. Кстати, атрибуции в этом контексте означают, что мы приписываем предсказанные метки областям изображения. В академической литературе их также нередко называют картами чувствительности. Для начала нам нужно создать массив со всеми образцами ошибочной классификации (X_misclass) из валидационного набора данных (X_val). Многие из этих методов могут вычислять атрибуционные карты в пакетном режиме, способствуя данному процессу. Затем можно распечатать форму массива ошибочных классификаций, чтобы убедиться, что все 17 образцов присутствуют: idxs = avocado_FN_idxs + grapefruit_FP_idxs X_misclass = X_val[idxs] print(X_misclass.shape) Эти методы выполняют проход по сети в прямом направлении вплоть до последнего полносвязного слоя. Напомним, что этот слой выводит вероятность каждого класса. Мы знаем, какая из этих вероятностей является для каждого образца наибольшей, соответствуя предсказанному классу. Однако эти методы, прежде чем предсказывать класс, будут поворачивать назад и выполнять обратный проход. Поэтому нам нужно будет им сообщить, с помощью какого из 16 выходов следует рассчитывать потерю. Для этого нам понадобится, чтобы метка образца была закодирована в порядковом формате, что делается с помощью OrdinalEncoder библиотеки scikit-learn. Сначала необходимо обеспечить, чтобы метки имели правильный формат, с помощью np.expand_dims, и после того, как все метки будут закодированы, мы возьмем их подмножество из наших образцов и преобразуем их в список, который можно легко распечатать: enc = preprocessing.OrdinalEncoder() enc.fit(y_train) y_val_pred_exp = np.expand_dims(np.array(y_val_pred),axis=1) y_val_pred_enc = enc.transform(y_val_pred_exp) labels_l = y_val_pred_enc[idxs].squeeze().astype(int).tolist() print(labels_l) Приведенный выше исходный код печатает следующий список: [9, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] Как можно заметить, в указанном списке много шестерок (labels_l), соответствующих седьмому классу, т. е. грейпфруту. И действительно, 15 из 17 примеров являются ложноположительными классификациями грейпфрута.
Глава 8. Визуализация сверточных нейронных сетей 333 Далее мы определяем функцию model_modifier. Нам нужно внести одну-единственную модификацию — превратить активационную функцию последнего полносвязного слоя в линейную. Нас не интересуют создаваемые активацией softmax вероятности, потому что она потенциально сдувает результат, чтобы результат для всех классов в сумме составлял единицу. Вместе с тем нас не интересуют и остальные классы, а только предсказанный класс. Затем эти методы выполняют обратное распространение, чтобы проследить этот линейно активированный результат до частей изображения, которые внесли в него свой вклад: def model_modifier(mdl): mdl.layers[-1].activation = tf.keras.activations.linear return mdl def loss(output): global labels_l pos_l = [*range(len(labels_l))] output_l = [] for p, l in zip(pos_l, labels_l): output_l.append(output[p][l]) return tuple(output_l) Что касается функции потерь в приведенном выше фрагменте исходного кода, то нам нужно, чтобы она возвращала кортеж с потерями для каждой комбинации образца и предсказанного класса. В ее обязанности входит лишь преобразование изначальных данных на входе в модель, чтобы производить только те потери, которые нас интересуют. Далее мы изучим наш первый градиентный метод. Карты значимости "Классические" карты значимости (salience map) опираются на абсолютное значение градиентов. Интуитивно, указанный метод будет брать изображение и отыскивать в нем пикселы, которые можно подвергнуть минимальной перестановке, и при этом на выходе получить результат, который изменяется с этими значениями максимально. Он не производит пертурбаций, поэтому не подтверждает гипотезу, а использование абсолютных значений не дает ему отыскивать другие сведения в подтверждение обратного. Этот первый метод карт значимости был в свое время новаторским и дал начало массе разных методов. Обычно его называют "классическим", чтобы отличить от других методов карт значимости, в частности, от метода сглаженно-градиентных карт значимости (SmoothGrad), который вносит малые случайные перестановки в изображении образца — другими словами, добавляет шум. Он несколько раз создает разные зашумленные версии одного и того же образца изображения, а затем вычисляет градиенты. Затем он усредняет эти градиенты, что и делает карты значимости намного более сглаженными. Но подождите, возможно, вы спросите: разве этот метода не должен быть пертурбационным?! В нашей книге мы уже рассматривали несколько пертурбационных
334 Часть II. Освоение методов интерпретации методов, от SHAP до якорей, и у них есть нечто общее в том, что они перетасовывают данные на входе, чтобы измерить влияние на данные на выходе. Метод SmoothGrad не измеряет влияние на результат, но фокусирует свое внимание на градиентах. Генерировать как классические, так и сглаженно-градиентные карты значимости для всех ошибочно классифицированных образцов относительно просто. Сначала надо инициализировать экземпляр библиотечного класса Saliency, предоставив свою модель (cnn_fruits_mdl), функцию модифицирования модели (model_modifier) и сообщение о создании копии модели (clone=True). Затем можно создавать как классические, так и сглаженно-градиентные карты значимости, имея один и тот же экземпляр. Экземпляру для генерирования карты требуется лишь функция потерь (loss) и образцы (X_misclass), после чего он нормализует карту так, чтобы на выходе данные находились в интервале от 0 до 1. Для метода SmoothGrad делается то же самое, за исключением того, что предоставляются несколько вариаций порождаемых образцов (smooth_samples=20) и величина случайного шума для добавления к каждому из них (smooth_noise=0.20): saliency = Saliency(cnn_fruits_mdl, model_modifier=model_modifier, clone=True) saliency_maps = saliency(loss, X_misclass) saliency_maps = normalize(saliency_maps) smoothgrad_saliency_maps = saliency(loss, X_misclass,\ smooth_samples=20, smooth_noise=0.20) smoothgrad_saliency_maps = normalize(smoothgrad_saliency_maps) Чтобы понять смысл сделанного, можно построить график результирующих карт значимости бок о бок с образцом изображения. Библиотека matplotlib сделает это легко с помощью сетки subplot. Мы создадим сетку 1  3 и разместим образец изображения № 0 в первой позиции, его классическую карту значимости — во второй, а его сглаженно-градиентную карту значимости — в третьей. Порожденные карты значимости представлены в оттенках серого, но мы применим цветовую карту (cmap='jet'), чтобы выделяющиеся области выглядели ярче: plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(X_misclass[0]) plt.grid(b=None) plt.title("Изначальное изображение ") plt.subplot(1, 3, 2) plt.imshow(saliency_maps[0], cmap='jet') plt.grid(b=None) plt.title("Классическая карта значимости ") plt.subplot(1, 3, 3) plt.imshow(smoothgrad_saliency_maps[0], cmap='jet') plt.grid(b=None) plt.title("Карта значимости SmoothGrad") Приведенный выше исходный код генерирует графики на рис. 8.17.
Глава 8. Визуализация сверточных нейронных сетей 335 Рис. 8.17. Карты значимости для авокадо, ошибочно классифицированного как красный лук Образец изображения на рис. 8.17 явно представляет собой авокадо, но если выполнить инструкцию print(y_val_pred[idxs[0]]), то она покажет вам, что предсказанием будет красный лук. Классическая карта значимости приписывает это предсказание в основном более гладким областям, вызванным размытием глубины резкости фотографии, в особенности вблизи края плода. Эти области также имеют красноватый оттенок, совпадающий со светло-голубыми областями на классической карте значимости. Выделено несколько пятен вокруг плодоножки, что говорит о том, что модель считает, что они тоже похожи на лук — возможно, из-за желтого цвета. Что касается метода SmoothGrad, то просто поразительно, насколько эта карта отличается от классической. Это не всегда так; нередко это просто более плавная версия. Скорее всего, произошло то, что 20%-ный шум исказил атрибуции, или что 20 гладких образцов было недостаточно. Однако трудно судить, потому что также возможно, что SmoothGrad точнее отражает реальную историю. Мы не будем делать этого сейчас, но можно визуально "отрегулировать" параметры smooth_noise и smooth_samples. Это можно попробовать сделать с меньшей величиной шума и с большим числом образцов, используя ряд комбинаций, таких как 5% и 80, 10% и 40, пытаясь увидеть между ними общность. Тот, с которым вы согласитесь, будет тем, который наиболее четко и согласованно описывает всю историю. Одним из недостатков метода SmoothGrad является необходимость определять оптимальные параметры. В любом случае, если вы возьмете сглаженно-градиентную карту значимости на рис. 8.17 за чистую монету, то пятна, совпадающие с красноватыми областями авокадо, согласуются с классической картой. Однако вместо того, чтобы сильно выделять нижний край плода, метод выделяет правый край, который тоже имеет значительное размытие. Дополнительно он находит заметные области за пределами плода. Светло-серый фон дезориентирует модель, т. к. тренировочные данные имели только белый фон. Таким образом, если бы вы подвели итог интерпретации, то увидели бы, что отсутствие более зрелых авокадо с красноватой кожурой, размытость глубины резкости
336 Часть II. Освоение методов интерпретации и небелый фон в тренировочных данных являются ключевыми причинами этой ошибочной классификации. Теперь давайте посмотрим, что можно узнать с помощью еще одного метода. Метод градиентных карт активаций классов Grad-CAM Прежде чем обратиться к градиентным картам активаций классов (Grad-CAM), сначала следует рассмотреть карту активации классов (class activation map, CAM). Принцип работы метода CAM заключается в том, что он удаляет все, кроме последних полносвязных слоев, и заменяет последний слой, слой сведения на основе максимума (MaxPooling), слоем сведения на основе глобального среднего (global average pooling, GAP). Например, в данном случае: 1. Последний сверточный слой выдает тензор размерностью 10  10  128. 2. GAP редуцирует размерность, просто усредняя первые две размерности этого тензора, производя тензор 1  1  128. 3. Затем он подает его в полносвязный слой с 16 нейронами, соответствующими каждому классу. 4. После переобучения CAM-модели и пропускания образца изображения через CAM-модель он берет веса из последнего слоя (тензор 128 16) и извлекает значения, соответствующие предсказанному классу (тензор 128 1). 5. Затем выполняется точечное произведение результата на выходе из последнего сверточного слоя (10 10 128) с весовым тензором (128 1). 6. Эта взвешенная сумма в итоге будет тензором 10  10  1. 7. После билинейной интерполяции, растягивающей тензор до размерности 100  100  1, он становится картой активации класса с увеличенной дискретизацией. Интуитивная идея в основе метода CAM состоит в том, что CNN-сети органически поддерживают пространственные детали в сверточных слоях, но, к сожалению, они теряются в полносвязных слоях. Фактически, каждый фильтр в последнем сверточном слое представляет визуальные шаблоны в разных пространственных местоположениях. После взвешивания они представляют наиболее заметные области во всем изображении. Однако в целях применения CAM необходимо вносить в модель радикальную модификацию и переобучать ее, а некоторые модели поддаются этому с трудом. Как следует из названия (градиентные карты активаций классов), метод GradCAM реализует схожую концепцию, но в нем отсутствуют проблемы с модифицированием и переобучением, и вместо этого используются градиенты, в частности, градиенты логитов классов (перед softmax), касающиеся активационных карт сверточного слоя. На этих градиентах выполняется сведение на основе глобального среднего для получения весов важности нейронов. Затем с этими весами вычисляется взвешенная линейная комбинация активационных карт, за которой следует ReLU. ReLU очень важна, потому что она обеспечивает обнаружение признаков, которые влияют на исход только положительно. Как и метод CAM,
Глава 8. Визуализация сверточных нейронных сетей 337 она проводит повышающий отбор с билинейной интерполяцией для соответствия размерам изображения. Метод Grad-CAM все же имеет несколько недостатков, таких как неспособность идентифицировать несколько случаев появления или целиком весь объект, представляемый предсказанным классом. Как и CAM, разрешающая способность активационных карт может лимитироваться размерностями завершающего сверточного слоя, отсюда и потребность в повышающем отборе. По этим причинам вместо этого мы используем Grad-CAM++, который решает эти проблемы путем попиксельного вычисления средневзвешенных значений градиентов. И, следовательно, он продуцирует активационные карты, которые не требуют повышающего отбора. В области интерпретации CNN-сетей дискуссии продолжаются по-прежнему. И исследователи все еще изобретают новые и более качественные методы, но даже методы, которые являются почти идеальными для большинства случаев использования, все же имеют недостатки. Что касается методов, подобных CAM, то появился ряд более новых, таких как Score-CAM и Eigen-CAM, которые обеспечивают похожую функциональность, но не опираются на градиенты, которые бывают нестабильными и, следовательно, временами ненадежными. Здесь мы не будем их обсуждать, потому что, конечно же, они не являются градиентными! Но важно отметить, что не будет никакого вреда, если вы попробуете разные методы, чтобы понять, что подходит для вашего варианта использования. Создание карт Grad-Cam++ Теперь давайте создадим несколько карт Grad-CAM++! Соответствующий исходный код похож на тот, который использовался для карт значимости. Сначала создается экземпляр библиотечного класса GradCamPlusPlus со всеми теми же самыми параметрами. После его создания будут созданы теплокарты Grad-CAM++ с теми же параметрами, что и для карты значимости, за исключением того, что будет указан предпоследний слой (penultimate_layer=-1). Затем карты нормализуются точно так же, как это делалось раньше: gradcam = GradcamPlusPlus(cnn_fruits_mdl, model_modifier, clone=True) gradcam_maps = gradcam(loss, X_misclass, penultimate_layer=-1) gradcam_maps = normalize(gradcam_maps) Сгенерировав теплокарты Grad-CAM++ для всех ошибочно классифицированных валидационных образцов, выведем первый из них рядом с его теплокартами, как отдельно, так и наложив образец изображения. Обратите внимание, что мы используем функцию цветовой карты библиотеки matplotlib (cm.jet). Это делается для конвертирования изначальной полутоновой карты атрибуции в более выразительную теплокарту: plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(X_misclass[0]) plt.grid(b=None) plt.title("Изначальное изображение")
338 Часть II. Освоение методов интерпретации plt.subplot(1, 3, 2) heatmap = np.uint8(cm.jet(gradcam_maps[0])[..., :3] * 255) plt.imshow(heatmap) plt.grid(b=None) plt.title("GRAD-CAM++") plt.subplot(1, 3, 3) plt.imshow(X_misclass[0]) plt.imshow(heatmap, alpha=0.5) plt.grid(b=None) plt.title("Наложенная карта GRAD-CAM++") Приведенный выше исходный код генерирует рис. 8.18, который сильнее выделяет почти весь верхний фон и в меньшей степени — пояс между верхней левой областью авокадо и нижней размытой областью, исключая плодоножку. Единственным участком, который чуть-чуть совпадает с обеими картами значимости, является нижний край авокадо. Однако ранее имелись признаки того, что фон дезориентировал модель в карте значимости SmoothGrad, и данная теплокарта, похоже, это подтверждает. Рис. 8.18. Теплокарты Grad-CAM++ для авокадо, ошибочно классифицированного как красный лук Интегрированные градиенты Интегрированные градиенты (integrated gradients, IG), так называемые градиенты, интегрированные по траектории, — это метод, который не является исключительным для CNN-сетей. Его можно применять к любой нейросетевой архитектуре, потому что он вычисляет градиенты данных на выходе по отношению к данным на входе, усредненные по всему пути между базовым уровнем и фактическими данными на входе. Он не зависит от наличия слоев свертки. Однако для этого требуется определение базового уровня, который, как предполагается, должен доносить отсутствие сигнала, например равномерно окрашенное изображение. На практике, в частности для CNN-сетей, это то, чем является нулевой базовый уровень, который для каждого пиксела будет означать полностью черное изображение. Кроме того, хотя название предполагает использование интегралов по траекториям (path
Глава 8. Визуализация сверточных нейронных сетей 339 integrals), интегралы не вычисляются, а аппроксимируются с суммированием в достаточно малых интервалах на протяжении определенного числа шагов. Для CNN это означает, что вариации входного изображения поступательно становятся все более темными, пока оно не станет черным (базовым уровнем), соответствуя заданному числу шагов. Затем эти вариации передаются в CNN, выполняется вычисление градиентов для каждой и их усреднение. Интегрированные градиенты представляют собой точечное произведение изображения на средние значения градиентов. Как и значения Шепли, метод IG основывается на прочной математической теории. В данном случае это фундаментальная теорема исчисления линейных интегралов. Математическое доказательство обеспечивает сложение атрибуций. Другими словами, произведенные методом IG атрибуции сводятся к сумме разниц между предсказанием между данными на входе и базовым уровнем. В дополнение к этому свойству, которое называется полнотой (completeness), существует свойство сохранения линейности, сохранения симметрии и свойство чувствительности. Мы не будем описывать здесь каждое из них. Однако важно отметить, что некоторые методы интерпретации удовлетворяют заметным математическим свойствам, тогда как другие демонстрируют свою эффективность в практическом плане. Использование объяснителя Для метода IG мы будем использовать иную библиотеку — tf-explain. Как и в случае с другими методами, сначала создается объект, экземпляр объяснителя IntegratedGradients(). Однако он не выполняет пакетную обработку каждого объяснения; вместо этого мы будем прокручивать все наши образцы ошибочной классификации и продуцировать каждую теплокарту IG независимо с помощью метода explain(). Обратите внимание, что модифицировать модель не требуется, но нам нужно определить конкретное число шагов (n_steps=25). На каждой итерации мы добавляем карту в список (ig_maps), на который мы сможем ссылаться позже: explainer = IntegratedGradients() ig_maps = [] for i in range(len(labels_l)): img = ([X_misclass[i]], None) label = labels_l[i] ig_map = explainer.explain(img, cnn_fruits_mdl, label, n_steps=25) ig_maps.append(ig_map) Точно так же, как это делалось с Grad-CAM++, можно построить график первого образца изображения бок о бок с картой IG, а также этой картой, наложенной поверх изображения. Это делается с помощью следующего исходного кода: plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(X_misclass[0]) plt.grid(b=None) plt.title("Изначальное изображение") plt.subplot(1, 3, 2) heatmap = np.uint8(cm.jet(ig_maps[0])[..., :3] * 255)
340 Часть II. Освоение методов интерпретации plt.imshow(heatmap) plt.grid(b=None) plt.title("Теплокарта интегрированных градиентов") plt.subplot(1, 3, 3) plt.imshow(X_misclass[0]) plt.imshow(heatmap, alpha=0.5) plt.grid(b=None) plt.title("Наложенная карта интегрированных градиентов") Приведенный выше исходный код выводит на экран рис. 8.19. Рис. 8.19. Теплокарты на основе интегрированных градиентов для авокадо, ошибочно классифицированного как красный лук Область на рис. 8.19 совпадает со многими участками, обнаруженными классической картой значимости, в частности, участками авокадо, которые имеют красноватый цвет или кажутся более гладкими из-за размытия глубины резкости. Это еще раз подтверждает гипотезу о том, что, поскольку модель была натренирована не на более красных, более зрелых, а на зелено-коричневых незрелых авокадо Хасса, она ошибочно принимает плод за красный лук. Размытые области говорят о гладкости, которая не характерна для авокадо, что еще больше дезориентирует. Эксперты все еще не пришли к заключению, что небелый фон является решающим фактором, потому что так, похоже, считает только метод Grad-CAM. У методов интегрированных градиентов, таких как Grad-CAM, есть свои антагонисты, которые придумали похожие методы, позволяющие избегать использования градиентов, такие как DeepLift и его ответвления послойного распространения релевантности (layer-wise relevance propagation, LRP). Опять же, мы не будем их здесь обсуждать, потому что они не являются градиентными! Тем не менее у них есть много преимуществ, в особенности с учетом нуль-значных градиентов и разрывов градиентов, которые могут приводить к дезориентирующим атрибуциям. Но это указывает на общие недостатки, присущие всем градиентным методам. В частности, используемая нами библиотека tf-explain определяет базовый уровень как ноль, что может стать проблемой при использовании очень темных образцов изображений. Он должен включать параметр, позволяющий настраивать базовый уровень. К счастью, число шагов всегда можно увеличить. Авторы статьи по интег-
Глава 8. Визуализация сверточных нейронных сетей 341 рированным градиентам предполагают, что интеграл аппроксимируется в пределах 5% шагами в количестве где-то от 20 до 300. Окончательная сборка Теперь мы возьмем все, что узнали о градиентных методах атрибуции, и применим все это, чтобы понять причины всех выбранных ошибочных классификаций (ложноотрицательных классификаций авокадо и ложноположительных классификаций грейпфрута). Как и в случае с картами промежуточных активаций, можно задействовать функцию compare_img_pred_viz, чтобы разместить образец изображения с более высокой разрешающей способностью бок о бок с четырьмя атрибуционными картами: классической картой значимости, сглаженно-градиентной картой значимости (SmoothGrad), картой Grad-CAM++ и картой интегрированных градиентов (IG). С этой целью мы сначала должны прокрутить позиции и индексы всех ошибочных классификаций в цикле и извлечь все карты. Обратите внимание, что мы используем функцию heatmap_overlay для порождения нового изображения поверх изначального изображения с теплокартой для Grad-CAM++ (map3) и IG (map4). Наконец, мы конкатенируем четыре результата атрибуции в одно изображение (viz_img). Как и раньше, мы извлекаем фактическую метку (y_true), предсказанную метку (y_pred) и ряд pandas с вероятностями (probs_s), чтобы добавить контекст в график, который мы будем генерировать. Цикл for сгенерирует 17 графиков, но мы обсудим только три из них: for pos, idx in zip([*range(len(idxs))], idxs): orig_img = X_val_orig[idx] map1 = np.uint8(cm.jet(saliency_maps[pos])[..., :3] * 255) map2 = np.uint8(cm.jet(smoothgrad_saliency_maps[pos])[..., :3] * 255) map3 = mldatasets.heatmap_overlay(X_misclass[pos], gradcam_maps[pos]) map4 = mldatasets.heatmap_overlay(X_misclass[pos], ig_maps[pos]) viz_img = cv2.vconcat([cv2.hconcat([map1, map2]), cv2.hconcat([map3, map4])]) y_true = y_val[idx, 0] y_pred = y_val_pred[idx] probs_s = probs_df.loc[idx] title = 'Градиентные атрибуции для ошибочной классификации № {}'.format(pos+1) mldatasets.compare_img_pred_viz(orig_img, viz_img, y_true,\ y_pred, probs_s, title=title) Приведенный выше исходный код генерирует рис. 8.20–8.22. Атрибуционные карты значимости во втором, ошибочно классифицированном, авокадо на рис. 8.20 указывают на то, что причиной ошибочной классификации были блестящие более темные участки авокадо. Изображения бананов в тренировочных данных имели темные пятна с блеском, в особенности ближе к концам, тогда как изображения авокадо были тусклее, темно-зелеными с коричневыми пятнами. Что касается интегрированных градиентов, то темные пятна в основном охвачены, но есть и много зеленых областей. Grad-CAM++ в данном случае не сработал, что, возможно, происходит из-за нестабильности градиентов и сильной зависимости этого метода от них.
342 Часть II. Освоение методов интерпретации Рис. 8.20. Градиентные атрибуции для ошибочной классификации авокадо № 2 Рис. 8.21. Градиентные атрибуции для ошибочной классификации груши № 3 На рис. 8.21 плодоножка явно дезориентирует модель. Это имеет смысл, учитывая, что у груш в тренировочных данных не было такой темной, выступающей плодоножки. Они также отличаются более чистым оттенком зеленого, с меньшим количеством желтого оттенка, и пятна распределены равномернее. По этой причине
Глава 8. Визуализация сверточных нейронных сетей 343 неудивительно, что, согласно модели, существовала вероятность 3,5%, что это был банан, у которого тоже есть расположенные хаотично темные пятна и выделяющаяся темная плодоножка. Метод интегрированных градиентов дает атрибуционную карту, которая взвешивает пятна тяжелее, но она довольно-таки согласуется с картами значимости. С другой стороны, метод Grad-CAM снова дезориентирован фоном, но успешно идентифицирует плодоножку как интересную область. Рис. 8.22. Градиентные атрибуции для ошибочной классификации нектарина № 7 Наконец, рис. 8.22 очень интересен, потому что нектарин влажный. По словам руководителей сети минимаркетов, многие фрукты хранятся в открытых холодильных витринах, что приводит к скоплению на поверхности фруктов капель конденсированной жидкости. Следовательно, влажный фрукт является характерным для реально существующих условий. К сожалению, тренировочные данные этого не учитывали, и поэтому все плоды были сухими. Как и в предыдущих примерах, плодоножка является источником путаницы, но можно с уверенностью сказать, что все три метода атрибуции выявляют участки с несколькими каплями или некоторым блеском, вызванными влажностью. Кроме того, метод интегрированных градиентов также указывает на плодоножку. Однако вы также должны задаться вопросом: почему он так уверен, что это грейпфрут? Другими словами, какие атрибуты, по мнению модели, грейпфрут включает или исключает, которые имеются одновременно на изображениях нектарина (см. рис. 8.22) и груши (см. рис. 8.21), не говоря уже о всех разных образцах. Далее мы попытаемся выяснить, что, собственно, модель узнала об авокадо и грейпфрутах с помощью пертурбационных методов атрибуции.
344 Часть II. Освоение методов интерпретации Объяснение классификаций с помощью пертурбационных методов атрибуции Исходный код исключительно для этого раздела можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter08/FruitClassifier_part2.ipynb. Все шаги подготовки повторяются с самого начала. Однако здесь отключено поведение TensorFlow 2 (tf.compat.v1.disable_v2_behavior()), потому что на момент написания книги библиотека alibi, которую мы будем использовать для метода контрастивных объяснений, все еще опирается на конструкты TensorFlow 1. Пертурбационные методы уже были в значительной степени рассмотрены в данной книге до этого. Поэтому многие рассмотренные нами методы, включая SHAP, LIME, якоря и даже перестановочная важность признаков задействуют пертурбационные стратегии. В их основе лежит интуитивная идея, которая заключается в том, что если удалить, изменить или наложить маску на признаки во входных данных, а затем генерировать с ними предсказания, то можно будет приписывать разницу между новыми предсказаниями и изначальными предсказаниями изменениям, которые были внесены во входные данные. Эти стратегии могут использоваться как в методах глобальной, так и в методах локальной интерпретации. Теперь мы сделаем то же самое, что и с образцами ошибочной классификации, но с выбранными истинноположительными результатами, и соберем их все в один массив (X_tp). Печать размерности этого массива должна подтвердить наличие шести образцов изображений со стандартной шириной, высотой и каналами (100 100  3) : idxs = avocado_TP_idxs + grapefruit_TP_idxs X_tp = X_val[idxs] print(X_tp.shape) Схожим образом можно сделать то же самое, что делалось с ошибочными классификациями, для получения меток из порядково-кодированного массива: labels_l = y_val_pred_enc[idxs].squeeze().astype(int).tolist() print(labels_l) Приведенный выше исходный код печатает следующий список: [3, 3, 6, 6, 6, 6] Как и ожидалось, первые два элемента — это авокадо (класс № 3), а последние четыре — грейпфруты (класс № 6). Окклюзивная чувствительность Окклюзивная чувствительность (occlusion sensitivity) — это относительно простой метод. В его обязанности входит загораживание фрагмента образца изображения на входе серым пятном. Карта чувствительности производится с разницей в вероятности для целевого класса в каждой точке, где это пятно размещается. Карта чувствительности интерполируется так, чтобы она имела ту же размерность, что и образец изображения.
Глава 8. Визуализация сверточных нейронных сетей 345 В целях создания карты сначала инициализируется объяснитель на основе окклюзивной чувствительности (OcclusionSensitivity()), а затем выполняется итеративный обход каждого образца изображения и продуцируется карта каждого из них с объяснением. Для этого требуется лишь изображение, модель (cnn_fruits_mdl), метка и размер пятна. Мы определили здесь участок размером 3  3 , определяющий область, которая загораживается каждый раз. Большие участки могут влиять на вероятность классификации значительно сильнее, но меньшие участки могут точно определять конкретные области с наибольшим влиянием: explainer = OcclusionSensitivity() os_maps = [] for i in range(len(labels_l)): img = ([X_tp[i]], None) label = labels_l[i] os_map = explainer.explain(img, cnn_fruits_mdl, label, 3) os_maps.append(os_map) Имея карты окклюзивной чувствительности в списке (os_maps), теперь можно быстро их визуализировать, как и раньше, с помощью всех предыдущих методов: plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(X_tp[2]) plt.grid(b=None) plt.title("Исходное изображение") plt.subplot(1, 3, 2) plt.imshow(os_maps[2]) plt.grid(b=None) plt.title("Окклюзивная чувствительность") plt.subplot(1, 3, 3) plt.imshow(X_tp[2]) plt.imshow(os_maps[2], alpha=0.5) plt.grid(b=None) plt.title("Наложенная карта окклюзивной чувствительности") Приведенный выше исходный код генерирует рис. 8.23. По-видимому, он показывает, что гладкие и менее освещенные области в левом верхнем углу грейпфрута оказывают наибольшее влияние на его положительную классификацию. В чем значимость этих пятен? Давайте попробуем провести простой эксперимент со следующим ниже исходным кодом, который выбирает случайный грейпфрут из тренировочного набора данных и генерирует карту окклюзивной чувствительности, как это делалось для валидационных данных: idx = np.random.choice(np.where(y_train[:,0] == 'Grapefruit Pink')[0], 1)[0] os_map_train = explainer.explain(([X_train[idx]], None), cnn_fruits_mdl, label, 5) plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(X_train[idx]) plt.grid(b=None)
346 Часть II. Освоение методов интерпретации plt.title("Исходное изображение") plt.subplot(1, 3, 2) plt.imshow(os_map_train) plt.grid(b=None) plt.title("Окклюзивная чувствительность") plt.subplot(1, 3, 3) plt.imshow(X_train[idx]) plt.imshow(os_map_train, alpha=0.5) plt.grid(b=None) plt.title("Наложенная карта окклюзивной чувствительности") Рис. 8.23. Карты окклюзивной чувствительности для валидационного набора истинноположительной классификации грейпфрута Рис. 8.24. Карты окклюзивной чувствительности для тренировочного набора истинноположительной классификации грейпфрута Если бы вы построили график карт, то получили бы рис. 8.24. Можно попробовать несколько случайных тренировочных изображений, и они подтвердят, что болееменее одно и то же левое верхнее пятно имеет решающее значение с точки зрения предсказания грейпфрута. Помогает то, что грейпфрут представляет собой сферический объект, и освещение было стабильным на каждом снимке, поэтому зеркальная подсветка всегда находится в центре, слегка смещенном к левому верхнему углу. Такое освещение создает серую тень на нижней и правой поверхностях плодов.
Глава 8. Визуализация сверточных нейронных сетей 347 Полоса с обеих сторон указывает на то, как модель реагирует на серый блок в этом месте, что дезориентирует, учитывая то, насколько этот цвет близок к цвету тени. Квадратное пятно серого цвета, задействуемое для окклюзивной чувствительности, является произвольным, потому что строительные блоки изображений не обязательно имеют квадратную форму, и серый цвет может быть не лучшим цветом для противопоставления содержимому изображения. Давайте посмотрим на еще один метод, который сегментирует изображение по-другому, но сохраняет стратегию затемнения. Объяснитель изображений методом LIME Локально интерпретируемые модельно-агностические объяснения (local interpretable model-agnostic explanations, LIME) уже были рассмотрены в главе 6, но мы дали разъяснения только по табличному и текстовому объяснителям. Теперь обратимся к объяснителю изображений. Его принцип работы во многом тот же самый. В нем с целью выявления наиболее важных признаков по-прежнему используются пертурбационная стратегия и разреженная линейная модель. Однако в данном случае признаки — это не столбцы, как в табличном объяснителе, и не слова, как в текстовом объяснителе, а суперпикселы! Суперпикселы не являются пикселами в буквальном смысле этого слова, это целые сегменты изображения, которые могут быть или не быть серыми. Указанные сегменты вычисляются алгоритмически. По умолчанию в пакете lime используется алгоритм Quickshift, но можно применять любой метод сегментации изображений библиотеки scikit-learn (skimage.segmentation). Преимущество подхода LIME заключается в том, что он выводит линейные коэффициенты, позволяющие выяснить, какие признаки положительно или отрицательно коррелируют с предсказанным классом. В данном случае у вас могут быть любые сегменты — и с положительной, и с отрицательной корреляцией, а также такие, которые по большей части находятся в нейтральной зоне, потому что коэффициенты не являются статистически значимыми. Шаг 1. Инициализация и создание объяснений Мы легко создаем экземпляр библиотечного класса LimeImageExplainer. Затем перебираем наши образцы в цикле и производим объяснения для каждого из них, используя метод explain_instance(). Его параметры включают образец изображения (X_tp[i]) и модельную функцию предсказания (cnn_fruits_mdl.predict). Есть ряд других необязательных параметров, но применяемые по умолчанию хорошо вписываются в данный конкретный случай использования: explainer = lime_image.LimeImageExplainer() lime_expl = [] for i in range(len(labels_l)): explanation = explainer.explain_instance(X_tp[i].astype('double'),\ cnn_fruits_mdl.predict) lime_expl.append(explanation)
348 Часть II. Освоение методов интерпретации Шаг 2. Извлечение изображения и маски из объяснения Для оценивания одиночного объяснения нам придется извлечь из него изображение и его маску с помощью метода get_image_and_mask(). Мы сделаем это дважды: один раз с hide_rest=True и один раз positive_only=True. Эти аргументы означают, что метод выделит серым цветом порции изображения, которые не объясняют предсказание положительно (img_hide), и еще один раз только с positive_=False, т. е. он вернет все изображение целиком и выделит как положительные, так и отрицательные участки (img_show). Наконец, мы извлечем словарь с коэффициентами для каждого сегмента (dict_heatmap), используя атрибут top_labels в объяснении, и применим эти коэффициенты к сегментам (segments), т. е. к массиву NumPy размером 100  100 с индексами сегментов. Результатом этой операции является теплокарта LIME с коэффициентами: # Объяснение, в котором нерелевантные сегменты скрыты img_hide, mask_hide = lime_expl[2].\ get_image_and_mask(lime_expl[2].top_labels[0], positive_only=True,\ num_features=10, hide_rest=True) img_hide = mark_boundaries(img_hide / 2 + 0.5, mask_hide) # Объяснение, в котором все сегменты помечены # как положительное/отрицательное предсказание img_show, mask_show = lime_expl[2].get_image_and_mask(\ lime_expl[2].top_labels[0], positive_only=False, num_features=10) img_show = mark_boundaries(img_show / 2 + 0.5, mask_show) # Посегментное объяснение на основе теплокарты dict_heatmap = dict(lime_expl[2].local_exp[lime_expl[2].top_labels[0]]) heatmap = np.vectorize(dict_heatmap.get)(lime_expl[2].segments) Шаг 3. Построение графиков объяснений Теперь можно создать сетку подграфиков размером 1  3 , сравнивая все три варианта визуализации: перекрытие нерелевантных фрагментов серым цветом, выявление положительных и отрицательных сегментов и вывод теплокарты: plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(img_hide) plt.grid(b=None) plt.title("Скрытые нерелевантные сегменты") plt.subplot(1, 3, 2) plt.imshow(img_show) plt.grid(b=None) plt.title("Наложение положительных/отрицательных предсказаний") plt.subplot(1, 3, 3) plt.imshow(heatmap, alpha=0.5, cmap='RdBu') plt.grid(b=None) plt.title("Теплокарта LIME") Приведенный выше исходный код строит график на рис. 8.25.
Глава 8. Визуализация сверточных нейронных сетей 349 Рис. 8.25. Разные способы визуализации объяснений LIME для классификации грейпфрута На рис. 8.25 хорошо видно, что правая нижняя и левая верхняя области (включая сегмент плода) не имеют значимости с точки зрения положительной классификации как грейпфрута. Однако, поскольку на втором изображении эти области даже не были выделены красным цветом, мы также знаем, что они не имеют даже такой отрицательной значимости. Однако почти весь плод выделен зеленым цветом, что говорит о том, что эти сегменты коррелируют с классификацией положительно. Наконец, можно сказать, что эти зеленые области не одинаково важны на теплокарте, где отмечаются два зеркальных блика, которые менее важны, чем нижняя половина плода. Левая верхняя область грейпфрута, которая была выделена серым цветом в первой визуализации (для img_hide), действительно отрицательно коррелирует с соседним углом и правым нижним углом, но в меньшей степени. Эта теплокарта, возможно, будет немного дезориентировать относительно величины, потому что темно-красные тона могут предполагать больше отрицательности, чем они на самом деле представляют. Если распечатать коэффициенты в indict_heatmap, можно понять, что наибольший отрицательный коэффициент равен 0,04 , но самые высокие положительные коэффициенты составляют около 0,17. Это различие объясняет причину, почему во второй визуализации (для img_show) область не выделена красным цветом. Метод контрастивных объяснений Метод контрастивных объяснений (contrastive explanation method, CEM) уже рассматривался в главе 7. Однако он не всегда полезен для табличных данных, но особенно хорош для задач классифицирования изображений, где признаки, другими словами — пикселы, всегда непрерывны, а низкие и высокие значения отражают отсутствие и присутствие чего-либо, например света или первичного цвета. Мы не будем здесь снова объяснять принцип работы CEM, но важно помнить одно, что в нем можно генерировать уместный (пертинентный) отрицательный (pertinent negative, PN) результат, который минимально и достаточно отсутствует, чтобы предсказать другой класс. С другой стороны, можно генерировать уместный
350 Часть II. Освоение методов интерпретации (пертинентный) положительный (pertinent positive, PP) результат, который минимально и достаточно присутствует, чтобы предсказать тот же класс. С учетом этого мы сразу же приступим к делу! Первым необязательным, но настоятельно рекомендуемым шагом для CEM является обучение автокодироващика влиять на функцию потерь, как это делалось в главе 7. У нас есть кодировщик (coder), декодировщик (decoder) и бутылочное горлышко (bottleneck) между ними. Однако теперь мы будем использовать исключительно слои свертки, сведения и повышающего отбора. Бутылочное горлышко редуцирует размерность до 1/24 от размерности на входе, и вся идея состоит в том, чтобы натренировать эту модель так, чтобы данные на выходе максимально совпадали с данными на входе: input_layer = tf.keras.layers.Input(shape=(100, 100, 3)) encoder = tf.keras.layers.Conv2D(\ 16, (3, 3), activation='relu', padding='same')(input_layer) encoder = tf.keras.layers.Conv2D(\ 16, (3, 3), activation='relu', padding='same')(encoder) encoder = tf.keras.layers.MaxPooling2D((2, 2), padding='same')(encoder) bottleneck = tf.keras.layers.Conv2D(\ 1, (3, 3), activation=None, padding='same')(encoder) decoder = tf.keras.layers.Conv2D(\ 16, (3, 3), activation='relu', padding='same')(bottleneck) decoder = tf.keras.layers.UpSampling2D((2, 2))(decoder) decoder = tf.keras.layers.Conv2D(\ 16, (3, 3), activation='relu', padding='same')(decoder) output_layer = tf.keras.layers.Conv2D(\ 3, (3, 3), activation=None, padding='same')(decoder) autoencoder_mdl = tf.keras.Model(input_layer, output_layer) autoencoder_mdl.summary() Приведенный выше исходный код строит автокодировочную модель послойно в целях обеспечения ее более легкого понимания. Если посмотреть на сводные результаты (summary()) его работы, можно увидеть, как размерности в процессе кодирования переходят от 100  100  3 к 50  50  16 , а потом к 50  50  1 . Естественно, при декодировании они возвращаются с 50  50  1 к 50  50  16 , а потом к 100  100  3 : Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 100, 100, 3)] 0 _________________________________________________________________ conv2d (Conv2D) (None, 100, 100, 16) 448 _________________________________________________________________ conv2d_1 (Conv2D) (None, 100, 100, 16) 2320 _________________________________________________________________
Глава 8. Визуализация сверточных нейронных сетей 351 max_pooling2d (MaxPooling2D) (None, 50, 50, 16) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 50, 50, 1) 145 _________________________________________________________________ conv2d_3 (Conv2D) (None, 50, 50, 16) 160 _________________________________________________________________ up_sampling2d (UpSampling2D) (None, 100, 100, 16) 0 _________________________________________________________________ conv2d_4 (Conv2D) (None, 100, 100, 16) 2320 _________________________________________________________________ conv2d_5 (Conv2D) (None, 100, 100, 3) 435 ================================================================= Total params: 5,828 Trainable params: 5,828 Non-trainable params: 0 _________________________________________________________________ Затем выполняются компиляция и обучение автокодировщика — тот же процесс, что и в любой нейросетевой модели, за исключением того, что первый (X) и второй (y) аргументы функции fit одинаковы: X_train. Сеть ожидаемо произведет на выходе то, что будет находиться как можно ближе к тому, что в нее вошло: autoencoder_mdl.compile(loss='mse', optimizer='adam') autoencoder_history = autoencoder_mdl.fit(X_train, X_train,\ epochs=5, batch_size=32, verbose=1, validation_data=(X_test, X_test)) Теперь давайте посмотрим, насколько хорошо наша модель воспроизводит наши тренировочные данные, случайно отобрав семь тренировочных изображений и сопоставив их с декодированными версиями тех же самых изображений на графике. Для создания декодированных версий нам нужно лишь передать образцы тренировочных изображений в функцию predict автокодировщика: n = 7 rand_idxs = np.random.choice([*range(len(y_test))], n) decoded_imgs = autoencoder_mdl.predict(X_test[rand_idxs]) plt.figure(figsize=(14, 4)) for i in range(n): ax = plt.subplot(2, n, i + 1) plt.imshow(X_test[rand_idxs[i]]) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax = plt.subplot(2, n, i + n + 1) plt.imshow(decoded_imgs[i]) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) Приведенный выше исходный код генерирует рис. 8.26. Из рисунка следует, что декодированные версии — это просто более размытые и тусклые версии оригиналов. Совокупная верность воспроизведения довольно хорошая.
352 Часть II. Освоение методов интерпретации Рис. 8.26. Изначальные тренировочные изображения в сопоставлении с декодированными изображениями из натренированного автокодировщика Мы поступим так же, как и в главе 7, и создадим экземпляр класса CEMобъяснителя один раз для PN и еще один раз для PP. В данном случае мы будем использовать нашу модель вместо функции предсказания и определим форму входных данных как (1, 100, 100, 3), а диапазон признаков — как (0.0, 1.0), поскольку модель на входе ожидает только числа с плавающей точкой в указанном диапазоне. Помимо этого, все остальное остается без изменений: cem_pn = CEM(cnn_fruits_mdl, 'PN', (1,) + X_train.shape[1:],\ feature_range=(0.0, 1.0), max_iterations=100,\ ae_model=autoencoder_mdl, gamma=100, c_init=1.) cem_pn.fit(X_train, no_info_type='median') cem_pp = CEM(cnn_fruits_mdl, 'PP', (1,) + X_train.shape[1:],\ feature_range=(0.0, 1.0), max_iterations=100,\ ae_model=autoencoder_mdl, gamma=100, c_init=0.5, beta=0.1) cem_pp.fit(X_train, no_info_type='median') Теперь, когда объекты-объяснители созданы, можно обработать каждый образец в цикле и объяснить их поочередно с помощью обоих объяснителей. В обоих случаях объяснитель может генерировать изображение и предсказание. Обратите внимание, что он, возможно, не найдет ни PP, ни PN, поэтому при отсутствии предсказания изображения не будет. В этом случае с помощью функции np.ones мы будем создавать пустое изображение, которое будет представлять отсутствие PP и PN, и пустой строковый литерал, который будет представлять предсказываемый класс. И будем в цикле добавлять предсказания и соответствующие изображения в списки: cem_pn_preds = [] cem_pn_imgs = [] cem_pp_preds = [] cem_pp_imgs = [] for i in range(len(labels_l)): pn_explanation = cem_pn.explain(np.array([X_tp[i]])) if pn_explanation.PN_pred is not None: cem_pn_preds.append(fruits_l[pn_explanation.PN_pred]) cem_pn_imgs.append(pn_explanation.PN[0])
Глава 8. Визуализация сверточных нейронных сетей 353 else: cem_pn_preds.append("") cem_pn_imgs.append(np.ones(X_train.shape[1:4])) pp_explanation = cem_pp.explain(np.array([X_tp[i]])) if pp_explanation.PP_pred is not None: cem_pp_preds.append(fruits_l[pp_explanation.PP_pred]) norm_img = (pp_explanation.PP[0] pp_explanation.PP[0].min()) / (pp_explanation.PP[0].max() pp_explanation.PP[0].min()) cem_pp_imgs.append(norm_img) else: cem_pp_preds.append("") cem_pp_imgs.append(np.ones(X_train.shape[1:4])) Как и ранее, мы возьмем образец истинноположительного валидационного изображения и сравним его с его объяснениями. На этот раз это будет бок о бок с объяснениями PN и PP: plt.subplots(figsize=(15,5)) plt.subplot(1, 3, 1) plt.imshow(X_tp[0]) plt.grid(b=None) plt.title('Оригинал: '+fruits_l[labels_l[0]]) plt.subplot(1, 3, 2) plt.imshow(cem_pn_imgs[0]) plt.grid(b=None) plt.title('PN: '+cem_pn_preds[0]) plt.subplot(1, 3, 3) plt.imshow(cem_pp_imgs[0]) plt.grid(b=None) plt.title('PP: '+cem_pp_preds[0]) Приведенный выше исходный код сгенерировал сетки изображений на рис. 8.27. Рис. 8.27. Пертинентные отрицательный и положительный результаты CEM для истинноположительного валидационного набора данных об авокадо
354 Часть II. Освоение методов интерпретации Метод CEM нашел для них как PN, так и PP, что не всегда бывает так. Что касается PN, то нескольких разбросанных по всему зеленому авокадо красных пикселов было достаточно, чтобы переключить классификацию на красный лук. А согласно объяснению PP, черное, как смоль, изображение с несколькими зелеными точками квалифицируется как авокадо. Эти странные результаты ставят под сомнение предсказательную способность и надежность модели. Если у вас есть какие-то сомнения по поводу решения модели о том, что PN-снимок был красным луком или PPснимок был авокадо, то нам нужно лишь вернуть эти изображения обратно в функцию predict моделей и распечатать вероятность и предсказываемый класс для каждого из них: pp_pred = cnn_fruits_mdl.predict(np.array([cem_pn_imgs[0]]))[0] print('PP-вероятности:%s' % pp_pred) print('PP-предсказания:%s' % fruits_l[pp_pred.argmax()]) pn_pred = cnn_fruits_mdl.predict(np.array([cem_pp_imgs[0]]))[0] print('PN-вероятности:%s' % pn_pred) print('PN-предсказания:%s' % fruits_l[pn_pred.argmax()]) Приведенный выше фрагмент исходного кода печатает следующее: PP-вероятности: PP-предсказания: PN-вероятности: PN-предсказания: [0. 0. 0. 0.155 0. 0. 0. 0. 0. 0.845 0. 0. 0. 0. 0. 0.] Onion Red [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] Avocado И действительно, изображенное на PN авокадо с красными брызгами не только квалифицировалось как красный лук, но и с модельной уверенностью 84,5%. И черное полотно с несколькими зелеными и фиолетовыми пятнами не только квалифицировалось как авокадо, но и определилось со 100%-ной уверенностью. Мы рассмотрим эту проблему подробнее в главе 13, но все это указывает не только на недостаточную надежность модели, но и на тему устойчивости к антагонизму, потому что этот классификатор очень легко обмануть пертурбациями, даже непреднамеренно. Благодаря своим минимальным, достаточным и преднамеренным пертурбациям метод CEM привлек наше внимание к легкости, с которой модель можно одурачивать! Окончательная сборка Теперь мы рассмотрим всё, что мы узнали о пертурбационных методах атрибуции и применим их, чтобы понять причины всех выбранных истинноположительных классификаций (как для авокадо, так и для грейпфрутов). Как и раньше, мы будем задействовать функцию compare_img_pred_viz, чтобы размещать образец изображения с более высокой разрешающей способностью бок о бок с четырьмя атрибуционными картами: окклюзивной чувствительности, LIME, PN и PP. Прежде всего, нам необходимо прокрутить позиции и индексы всех классификаций в цикле и извлечь все карты. Обратите внимание, что мы используем функцию heatmap_overlay, чтобы генерировать новое изображение, наложенное поверх изначального изображения с теплокартой для окклюзии (map1). Наконец, мы соединим четыре результата атри-
Глава 8. Визуализация сверточных нейронных сетей 355 буции в одно изображение (viz_img). Как и раньше, мы извлекаем фактическую метку (y_true), предсказанную метку (y_pred) и ряд pandas с вероятностями (probs_s), чтобы добавить в генерируемый график некоторый контекст. Цикл for произведет шесть графиков, но мы обсудим только два из них: for pos, idx in zip([*range(len(idxs))], idxs): orig_img = X_val_orig[idx] map1 = mldatasets.heatmap_overlay(X_tp[pos], os_maps[pos]/255) img_show, mask_show = lime_expl[pos].get_image_and_mask(\ lime_expl[pos].top_labels[0], positive_only=False, num_features=10) map2 = np.uint8(mark_boundaries(img_show / 2 + 0.5, mask_show)[..., :3] *255) map3 = np.uint8(cem_pn_imgs[pos][..., :3] * 255) map4 = np.uint8(cem_pp_imgs[pos][..., :3] * 255) viz_img = cv2.vconcat([cv2.hconcat([map1, map2]), cv2.hconcat([map3, map4])]) y_true = y_val[idx,0] y_pred = y_val_pred[idx] probs_s = probs_df.loc[idx] title = 'Пертурбационные атрибуции № {} (PN:{}, PP:{})'.format(\ pos+1, cem_pn_preds[pos], cem_pp_preds[pos]) mldatasets.compare_img_pred_viz(orig_img, viz_img, y_true, y_pred, probs_s,\ title=title) Рис. 8.28. Пертурбационные атрибуции для классификации авокадо № 2 Приведенный выше исходный код генерирует несколько объяснений, включая рис. 8.28 и 8.29. В соответствии с окклюзивной чувствительностью, на рис. 8.28 видно, как небольшое пятно вверху плода и слегка затененный нижний край,
356 Часть II. Освоение методов интерпретации по-видимому, отвечают за предсказание в наибольшей степени. Тем не менее метод LIME выделяет бóльшую часть авокадо зеленым цветом, но при этом еще выделяет верхний правый угол красным. Если посмотреть на авокадо в тренировочных данных, то станет яснее, почему в соответствии с моделью хорошо освещенный верхний правый угол нехарактерен для авокадо. На этих изображениях освещение всегда делало ту сторону намного темнее. Также становится очевидным, насколько стабильно освещенными и зелеными на этих же тренировочных изображениях были верхние центральные части. Нижний край все еще был значительно темнее, как и в валидационном образце. Наконец, PP возвращает серый фон с двумя противоположными кольцами разбросанных зеленых и фиолетовых точек. Нет смысла извлекать из этого слишком много смысла, кроме того факта, что модель можно обмануть столь легко. Устойчивая модель вернула бы нечто, гораздо больше напоминающее авокадо. Окклюзивная чувствительность на рис. 8.29 показывает аналогичное пятно, как и у всех других грейпфрутов. Можно сделать то же самое заключение, что и раньше, т. е. модель усвоила, что яркие желтоватые поверхности с несколькими чуть-чуть более темными точками, без теней и без зеркальных бликов характерны для грейпфрутов, и только для грейпфрутов. Если посмотреть на тренировочные изображения других плодов, таких как апельсины, яблоки, персики и нектарины, то они обладают меньшей отражающей способностью, поэтому кажутся намного более тусклыми, чем должны быть. Для продуцирования устойчивой модели более подходящей стратегией в тренировочном наборе данных было бы нестабильное освещение и более разнообразные варианты фруктов. Рис. 8.29. Пертурбационные атрибуции для классификации грейпфрута № 3
Глава 8. Визуализация сверточных нейронных сетей 357 Что касается модели LIME на рис. 8.29, то бóльшая часть изображения выделена зеленым цветом, и значит положительно коррелирует с предсказанным классом. Однако левый верхний фоновый угол выделен красным цветом. Модель LIME создала этот сегмент с использованием алгоритма сегментации Quickshift, но модель не обязательно выявляет целые сегменты как характерные для класса. Можно определить фрагмент, который, скорее всего, является виновником отрицательной корреляции, если только сравнить этот сегмент с пятном окклюзивной чувствительности. Эта порция пятна, которая перекрывается с фоном, является виновником. Можно предположить, что для сферического плода нехарактерно наличие серого блока, прикрепленного к его самой гладкой хорошо освещенной области. Что касается PN, то можно сделать вывод о дезориентированности модели в отношении фона, потому что шум есть почти везде. Если вы в состоянии отметить тренд с PNрезультатами, то он будет заключаться в том, что чем белее и чище фон, тем больше шума сконцентрировано на плоде. Этот тренд говорит о том, что модель не понимает, где начинается и заканчивается изображение плода, потому что модель была натренирована на изображениях только с белым фоном. Бонусный метод: глубокий объяснитель SHAP К CNN-сетям применим еще один важный метод, который мы не обсуждали: SHAP. Мы впервые узнали о SHAP в главе 5 и с тех пор понемногу использовали его в каждой главе. Мы допустили бы оплошность, если бы не включили его в данную главу, потому что каждый метод интерпретации рассказывает вам один сюжет истории, а SHAP — невероятно полезный инструмент. Он не был включен в остальные пертурбационные методы, потому что, хотя значения Шепли и основаны на пертурбациях, глубокий объяснитель SHAP, который мы будем использовать сейчас, базируется на алгоритме определения важности признаков глубокого обучения (deep learning important features, DeepLIFT), и DeepLIFT не является ни градиентным, ни пертурбационным методом. Этот подход основан на обратном распространении! Мы не будем здесь повторно объяснять принцип его работы, но достаточно сказать, что подобно интегрированным градиентам (IG) и значениям Шепли, DeepLIFT был разработан для обеспечения полноты (completeness) и в силу этого соответствует замечательным математическим свойствам. Результат работы алгоритма DeepLIFT просто адаптируется в библиотеке shap таким образом, чтобы он аппроксимировал значения Шепли. Выполнять аппроксимацию помогают именно фоновые образцы, которые согласовывают DeepLIFT с значениями Шепли. С этой целью первым делом мы собираем 100 случайных образцов изображений для тренировочных данных. Затем можно распечатать размер этого массива background, чтобы убедиться, что он равен (100, 100, 100, 3): background = X_train[np.random.choice(X_train.shape[0], 100, replace=False)] print(background.shape) Далее мы инициализируем наш глубокий объяснитель (DeepExplainer) с помощью модели (cnn_fruits_mdl) и фоновых образцов. Затем из объяснителя мы извлекаем
358 Часть II. Освоение методов интерпретации значения Шепли (shap_values) для наших истинноположительных (X_tp) классификаций: explainer = shap.DeepExplainer(cnn_fruits_mdl, background) shap_values = explainer.shap_values(X_tp) Вычислив значения SHAP, можно вывести их на график с помощью инструкции shap.image_plot(shap_values, X_tp). Однако это привело бы к генерированию сетки изображений высотой 6 образцов и шириной 16 классов. С такой большой сеткой трудно определить, какие области изображения являются наиболее показательными в отношении того или иного класса. В данном случае мы знаем, что только первые семь классов имеют какую-либо важность, а значит, вместо этого мы могли бы построить график shap.image_plot(shap_values[0:7], X_tp). Но поскольку мы знаем, что модель не путает авокадо и грейпфруты, будет лучше, если мы просто добавим такие значения, чтобы они появлялись в одном столбце: shap.image_plot(shap_values[3] + shap_values[6], X_tp) Приведенная выше строка исходного кода генерирует рис. 8.30. Результаты должны нас удивить. В целом модель, по-видимому, выявляет области на периферии плода как характерные для него. Однако некоторые более темные или яркие области выделены синим цветом, в особенности тень в авокадо и правая зеркальная подсветка в первом грейпфруте. Рис. 8.30. Значения SHAP для всех истинноположительных валидационных образцов Миссия выполнена Миссия состояла в том, чтобы обеспечить объективное оценивание модели классифицирования фруктов для сети минимаркетов. Предсказательная результативность вневыборочных валидационных изображениях была ужасной! Вы могли бы на этом остановиться, но тогда вы не знали бы, как построить более качественную модель. Однако оценивание предсказательной результативности сыграло решающую роль в выведении конкретных ошибочных классификаций, а также правильных классификаций для диагностики с использованием других методов интерпретации. С этой целью вы использовали исчерпывающий комплект методов интерпретации, включая активационные, градиентные и пертурбационные методы, а также методы на основе обратного распространения. Консенсус между всеми методами заключался в том, что у модели обнаружены следующие проблемы:  выявление разницы между фоном и плодом;  понимание того, что разные сорта плодов имеют некоторые общие цветовые от- тенки;
Глава 8. Визуализация сверточных нейронных сетей 359  смешение условий освещения, таких как зеркальные блики и тени, в качестве специфических характеристик плодов;  дезориентирование, вызванное условиями влажности, такими как капли воды;  неспособность выявлять уникальные признаки каждого плода, такие как плодоножки, текстуры кожуры и пятна. Кроме того, метод контрастивных объяснений (CEM) показал, что модель совсем неустойчива к антагонистическим атакам. Для решения этих проблем модель необходимо было тренировать с нуля с использованием более разнообразного набора данных, в идеале такого, который отражает реально существующие условия работы минимаркетов; например, многочисленные фоны, разные условия освещения, влажные плоды и даже плоды, частично закрытые руками, перчатками, сумками и т. д. Кроме того, сортность и уровень зрелости плодов должны отражать то, что продается в этих магазинах на постоянной основе. После того как этот набор данных был собран, важно задействовать методы насыщения (аугментации) данных, чтобы сделать модель еще более устойчивой ко всем видам изменений: угол, яркость, контрастность, насыщенность и варианты оттенков. И для того чтобы решить выявленные методом CEM проблемы, можно сделать гораздо больше, подвергнув модель стресс-тестированию. Мы рассмотрим это в главе 13. Резюме После прочтения этой главы вы должны понять, как использовать традиционные методы интерпретации для более тщательной диагностики предсказательной результативности CNN-классификатора и визуализации процесса усвоения знаний в CNN-сетях с помощью активационных методов. Вы также должны разобраться в том, как сравнивать и сопоставлять ошибочные классификации и истинноположительные классификации с помощью градиентных и пертурбационных методов атрибуции. В следующей главе мы изучим методы интерпретации, применяемые в отношении многомерных временны́х рядов и анализа чувствительности. Источники данных и изображений  Baron Karl (photographer). Local Daiei got a selfcheckout [digital image]). 2003. March 26. URL: https://www.flickr.com/photos/82365211@N00/9244253015. Лицензия CC 2.0. (Барон Карл. Местная сеть магазинов Дайэй получила возможность самостоятельного оформления заказов.)  Muresan H., Oltean M. Fruit recognition from images using deep learning // Acta Universitatis Sapientiae, Informatica. — 2017. — № 10. — P. 26–42. — URL: https://arxiv.org/abs/1712.00580. (Муресан Х., Олтеан М. Распознавание фруктов по изображениям с использованием глубокого обучения.)  Набор данных Fruits 360 (Oltean M., 2020). URL: https://www.kaggle.com/ moltean/fruits (CC BY-SA 4.0).
360 Часть II. Освоение методов интерпретации Справочные материалы  Smilkov D., Thorat N., Kim B., Viégas F., Wattenberg M. SmoothGrad: Removing noise by adding noise // ArXiv. — 2017. — abs/1706.03825.— URL: https://arxiv.org/abs/1706.03825. (Смилков Д., Торат Н., Ким Б., Виегас Ф., Ваттенберг М. SmoothGrad: удаление шума путем добавления шума.)  Chattopadhyay A., Sarkar A., Howlader P., Balasubramanian V. GradCAM++: Generalized Gradient-Based Visual Explanations for Deep Convolutional Networks // 2018 IEEE Winter Conference on Applications of Computer Vision (WACV). — 2018. — P. 839–847. — URL: https://arxiv.org/abs/1710.11063. (Чаттопадхьяй А., Саркар А., Хауладер П., Баласубраманян В. Grad-CAM++: обобщенные визуальные объяснения на основе градиента для глубоких сверточных сетей.)  Sundararajan M., Taly A., Yan, Q. Axiomatic Attribution for Deep Networks // Proceedings of Machine Learning Research. International Convention Centre, Sydney, Australia. — 2017. — P. 3319–3328. — URL: https://arxiv.org/abs/1703.01365. (Сундарараджан М., Тали А., Ян К. Аксиоматическая атрибуция для глубоких сетей.)  Zeiler M. D., Fergus R. Visualizing and Understanding Convolutional Networks / European conference on computer vision. — 2014. — P. 818–833. — URL: https://arxiv.org/abs/1311.2901. (Зейлер М. Д., Фергус Р. Визуализация и понимание сверточных сетей.)  Shrikumar A., Greenside P., Kundaje A. Learning Important Features Through Propagating Activation Differences. — 2017. — URL: https://arxiv.org/abs/1704.02685. (Шрикумар А., Гринсайд П., Кундадже А. Усвоение важных признаков путем распространения активационных разниц.)
9 Методы интерпретации для многопеременного прогнозирования и анализа чувствительности На протяжении всей этой книги мы учились использовать различные методы интерпретации моделей контролируемого обучения. Указанные методы бывают весьма эффективными в диагностике моделей, они одновременно раскрывают свои наиболее влиятельные предсказатели и их скрытые взаимодействия. Но, как предполагает термин "контролируемое обучение", эти методы задействуют только известные образцы и перестановки, основанные на распределениях этих известных образцов. Однако, когда образцы ассоциированы с будущим, все становится запутаннее! Как точно заметил нобелевский лауреат по физике Нильс Бор, "предсказывать очень трудно, в особенности если речь идет о будущем". И действительно, когда вы видите, как точки данных колеблются во временнóм ряду, может показаться, что они ритмично танцуют по предсказуемой схеме — по меньшей мере, в лучших сценариях. Подобно двигающейся в такт танцовщице, каждое повторяющееся движение (или частоту) можно отнести к сезонным регулярностям, тогда как постепенное изменение громкости (или амплитуды) — к одинаково предсказуемому тренду. Танец неизбежно вводит в заблуждение, потому что всегда есть недостающие части головоломки, которые слегка смещают точки данных, такие как задержка в снабженческой цепочке поставщика, вызывающая неожиданное снижение текущих показателей продаж. Еще хуже, что раз в десятилетие, раз в поколение или просто раз в жизни происходят непредвиденные катастрофические события, которые могут радикально изменять до неузнаваемости до некоторой степени понятное движение временнóго ряда, подобно тому, как у танцовщицы случается болевой приступ. Например, в 2020 году прогнозы продаж повсеместно, к лучшему или к худшему, оказались бесполезными из-за COVID-19! Можно было бы назвать это экстремальным выбросом, но необходимо признать, что модели не были созданы для предсказывания таких переломных событий, потому что они были натренированы на почти полностью вероятных событиях. Отсутствие предсказаний этих маловероятных, но все же наиболее влиятельных событий является причиной, согласно которой вообще не следует доверять моделям прогнозирования, в особенности без обсуждения определенности или границ уверенности.
362 Часть II. Освоение методов интерпретации В этой главе будет рассмотрена задача многопеременного прогнозирования с использованием моделей на основе долгой кратковременной памяти (long shortterm memory, LSTM). Сначала мы выполним диагностику модели с помощью традиционных методов интерпретации, а затем применим метод интегрированных градиентов, о котором мы узнали в главе 8, для генерирования локальных атрибуций нашей модели. Но что еще важнее, мы лучше поймем процесс усвоения знаний и ограничения в LSTM-сети. Затем мы задействуем метод-аппроксиматор предсказания и ядерный объяснитель SHAP как для глобальной, так и для локальной интерпретации. Наконец, прогнозирование и неопределенность неразрывно связаны, и анализ чувствительности — это семейство методов, предназначенных для измерения неопределенности результата на выходе из модели по отношению к ее данным на входе, поэтому он очень полезен в сценариях прогнозирования. Мы также изучим два таких метода: метод анализа чувствительности Морриса для приоритизации факторов и метод анализа чувствительности Соболя для фиксации факторов, который предусматривает стоимостную чувствительность. Вот главные темы, которые будут рассмотрены в этой главе:  диагностика моделей, основанных на временнóм ряде, с использованием тради- ционных методов интерпретации;  генерирование атрибуций в LSTM с помощью интегрированных градиентов;  вычисление глобальных и локальных атрибуций с помощью ядерного объясни- теля SHAP;  выявление влиятельных признаков с помощью факторной приоретизации;  квантифицирование неопределенности и стоимостной чувствительности с по- мощью фиксирования факторов. Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, statsmodels, tensorflow, matplotlib, seaborn, alibi, distython, shap и salib. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/InterpretableMachine-Learning-with-Python/tree/master/Chapter09. Миссия Транспортные пробки на автомагистралях — это проблема, которая затрагивает города по всему миру. По мере того как число транспортных средств на душу населения в развивающихся странах неуклонно растет, а дорожной и парковочной инфраструктуры недостаточно, чтобы идти в ногу с ним, транспортные пробки увеличиваются, достигая тревожных уровней. В США статистика транспортных средств на душу населения одна из самых высоких в мире (838 на 1000 человек в 2019 году). По этой причине 62 города США из 381 города во всем мире по уровню перенасыщенности транспортными пробками составляют не менее 15%.
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 363 Миннеаполис — один из таких городов (рис. 9.1), где этот порог был недавно превышен и продолжает расти. Для того чтобы представить этот метропольный ареал в контексте, взгляните на уровни перенасыщенности — они чрезвычайно высоки, превышая 50%, причем средняя перенасыщенность (15–25%) уже является предупреждающим сигналом грядущей плохой перенасыщенности. Обратить вспять перенасыщенность транспортными пробками при достижении ею 25%-ного порога чрезвычайно сложно, т. к. любое улучшение инфраструктуры будет дорогостоящим без дальнейшего нарушения дорожного движения. Одна из худших точек перегруженности находится между городами-близнецами Миннеаполисом и Сент-Полом на всем протяжении высокоскоростной автомагистрали 94 (I-94), которая закупоривает альтернативные маршруты, поскольку автолюбители пытаются сократить время в пути. Зная это, мэры обоих городов получили федеральные финансовые средства на расширение автомагистрали. Рис. 9.1. Индекс дорожного движения TomTom за 2019 год для Миннеаполиса Мэры хотят разрекламировать введение в строй расширения магистрали как совместное усилие, чтобы их переизбрали на второй срок. Однако они хорошо понимают, что шумное, грязное и мешающее движению расширение может стать большой помехой для автомобилистов, и, таким образом, проект строительства может иметь политические последствия, если его не сделать почти незаметным. Поэтому они предусмотрели, чтобы строительная компания как можно больше изготавливала изделия заводским способом и собирала их на месте только в часы с низким потоком дорожного движения. В такие часы интенсивность составляет менее 1500 автомобилей в час. Строители также могут одновременно работать только в одном направлении автомагистрали и блокировать не более половины ее полос во время работы на ней. В целях обеспечения соблюдения этих требований предусмотрены штрафные санкции к строительной компании, если ее рабочие будут блокировать более четверти автомагистрали в любое время, когда этот объем превышает указанный порог, в размере 15 долларов США за транспортное средство. В дополнение к этому, если дорожное движение по автомагистрали превысит половину своей емкости в то время, пока строительная бригада находится на месте, то это будет стоить им 5000 долларов в день. Если рассматривать это в перспективе, то блокировка во время обычного часа пик может стоить им 67 000 долларов в час
364 Часть II. Освоение методов интерпретации плюс ежедневная плата в размере 5000 долларов! Местные власти будут использовать станции автоматической регистрации дорожного движения (automated traffic recorder, ATR) вдоль маршрута для мониторинга объема движения, а также местную дорожную полицию для регистрации времени, когда полосы движения становятся заблокированными для строительства. Планировалось, что указанный строительный проект продлится 2 года; в первый год будут расширены полосы движения в западном направлении по маршруту I-94, а во второй — полосы движения в восточном направлении. Осуществляемая на месте часть строительства будет проходить только с мая по октябрь, потому что в эти месяцы снег будет задерживать строительство с меньшей вероятностью. В течение остальной части года строители будут сосредоточены на предварительном изготовлении комплектующих в заводских условиях. Они будут работать только в будние дни потому, что профсоюз работников договорился о щедрой оплате сверхурочных за выходные. Именно поэтому строительство в выходные дни будет происходить только в том случае, если возникнут значительные отклонения от графика. Однако профсоюз дал согласие на работы в праздничные дни с мая по октябрь по той же ставке. Строительная компания не хочет рисковать! Поэтому им нужна модель для предсказания дорожного движения по маршруту I-94 и, что еще важнее, для понимания факторов, которые создают неопределенность и, возможно, увеличивают расходы. Для этого они наняли эксперта по машинному обучению: вас! Предоставленные строительной компанией данные ATR содержат почасовые объемы дорожного движения по сентябрь 2018 года, а также данные о погоде в том же масштабе времени. Они состоят только из полос движения в западном направлении, потому что это расширение будет первым. Кроме того, с 2015 года транспортные пробки значительно усилились в часы пик, что стало новой нормой для пассажиров пригородных поездов. Поэтому они заинтересованы в обучении модели только данными за 3 года. Подход Вы натренировали модель двунаправленной LSTM-сети данными почти за 2,5 года (октябрь 2015 — март 2018). Вы зарезервировали последние 13 недель для тестирования (июль — сентябрь 2018) и предшествующие 13 недель до этого для валидации (апрель — июнь 2018). Это имело смысл, поскольку комбинированные тестовый и валидационный наборы данных хорошо согласуются с ожидаемыми условиями проекта расширения автомагистрали (май — октябрь). Вы задумывались об использовании других схем разбивки, которые использовали только те данные, которые отражают эти условия, но вы не хотели столь резко сокращать тренировочные данные, и, возможно, в конце концов, они понадобятся для зимних предсказаний. Ретроспективное окно определяет объем прошлых данных, к которым модель временнóго ряда имеет доступ. В качестве размера ретроспективного окна вы выбрали 672 часа (4 недели), потому что по мере продвижения модели вперед она
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 365 сможет усваивать ежедневную и еженедельную сезонность, а также некие тренды и закономерности, которые можно наблюдать только в течение нескольких недель. Вы также натренировали еще одну модель с ретроспективой 168 часов (1 неделя) в качестве резервной копии. Вы наметили следующие шаги, чтобы оправдать ожидания клиента. 1. С помощью RMSE, графиков регрессии, матриц путаницы и многого другого вы получите доступ к предсказательной результативности моделей и, что важнее, к распределению ошибки. 2. С помощью интегрированных градиентов вы поймете, выбрали ли вы наилучшую стратегию моделирования, поскольку указанный метод поможет вам визуализировать каждый путь модели к принятию решения и на основе этого помочь вам выбрать модель. 3. С помощью ядерного объяснителя SHAP и метода аппроксимации предсказания вы получите как глобальное, так и локальное понимание признаков, которые являются важными для выбранной модели. 4. С помощью метода анализа чувствительности Морриса вы выявите приоритетность факторов, которая ранжирует факторы (другими словами, признаки) по тому, насколько они способны влиять на изменчивость на выходе. 5. С помощью метода анализа чувствительности Соболя вы вычислите фиксацию факторов, который поможет определить факторы, не оказывающие скольконибудь важного влияния. Он делает это путем квантифицирования вклада и взаимодействия факторов на входе в изменчивость на выходе. Благодаря ему вы сможете понять неопределенные факторы, которые могут оказывать наибольшее влияние на потенциальные штрафы и расходы, что позволит провести дисперстный анализ стоимостной чувствительности. Подготовительные работы Исходный код этого примера можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter09/Traffic.ipynb. Загрузка библиотек В целях выполнения этого примера вам потребуется инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования набором данных;  tensorflow для загрузки модели;  statsmodels, sklearn (scikit-learn), matplotlib, seaborn, alibi, distython, shap и SALib для наглядных визуализаций и упрощения интерпретированя.
366 Часть II. Освоение методов интерпретации Сначала необходимо их все загрузить следующим образом: import math import os import mldatasets import pandas as pd import numpy as np import tensorflow as tf from tensorflow import keras from tensorflow.keras.preprocessing.sequence import\ TimeseriesGenerator from keras.utils.data_utils import get_file from sklearn.preprocessing import MinMaxScaler from sklearn import metrics from statsmodels.tsa.seasonal import seasonal_decompose from statsmodels.tsa.stattools import acf import matplotlib.pyplot as plt import seaborn as sns from alibi.explainers import IntegratedGradients from distython import HEOM import shap from SALib.sample import morris as ms from SALib.analyze import morris as ma from SALib.plotting import morris as mp from SALib.sample.saltelli import sample as ss from SALib.analyze.sobol import analyze as sa from SALib.plotting.bar import plot as barplot Давайте проверим, что TensorFlow загрузил правильную версию, используя инструкцию print(tf.__version__). Версия должна быть 2.0 или выше. Изучение проблемы и подготовка данных В следующем ниже фрагменте исходного кода мы загружаем данные в кадр данных Обратите внимание на аргумент prepare=True, который важен тем, что он выполняет необходимые подготовительные шаги, такие как взятие подмножества данных в требуемом временнóм интервале с октября 2015 года, некоторая интерполяция, корректировка на праздники и выполнение кодирования с одним активным состоянием: traffic_df. traffic_df = mldatasets.load("traffic-volume", prepare=True) Должно быть более 25 000 записей и 15 столбцов. Это можно подтвердить с помощью метода traffic_df.info(): <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 25656 entries, 2015-10-28 00:00:00 to 2018-09-30
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 367 23:00:00 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 dow 25656 non-null int64 1 hr 25656 non-null int64 2 temp 25656 non-null float64 3 rain_1h 25656 non-null float64 4 cloud_coverage 25656 non-null float64 5 is_holiday 25656 non-null int64 6 traffic_volume 25656 non-null float64 7 weather_Clear 25656 non-null uint8 8 weather_Clouds 25656 non-null uint8 9 weather_Haze 25656 non-null uint8 10 weather_Mist 25656 non-null uint8 11 weather_Other 25656 non-null uint8 12 weather_Rain 25656 non-null uint8 13 weather_Snow 25656 non-null uint8 14 weather_Unknown 25656 non-null uint8 dtypes: float64(4), int64(3), uint8(8) memory usage: 1.8 MB Приведенный выше результат проверку проходит. Все признаки являются числовыми и не имеют пропущенных значений, а категориальные признаки уже были закодированы с одним активным состоянием за нас. Словарь данных Всего имеется 8 признаков, но из-за категориальной кодировки они становятся 15 столбцами.  dow — порядковый; день недели, начинающийся с понедельника (от 0 до 6).  hr — порядковый; время суток (от 0 до 23).  temp — постоянный; средняя температура в градусах Цельсия (от 30 до 37).  rain_1h — непрерывный; миллиметры осадков в час (между 0 и 31).  cloud_coverage — непрерывный; процент облачности (от 0 до 100).  is_holiday — двоичный; является ли день национальным или государственным праздником, когда он наступает в понедельник — пятницу (1 — да, 0 — нет)?  traffic_volume — непрерывный, целевой, объем дорожного движения.  weather — категориальный; краткое описание погоды в течение этого часа (ясно | облачно | дымка | туман | дождь | снег | неизвестна | другая).
368 Часть II. Освоение методов интерпретации Изучение данных Первым делом в уяснении задачи о временнóм ряде необходимо понять целевую переменную. Это обусловлено тем, что она определяет подход ко всему остальному, от подготовки данных до моделирования. Целевая переменная, вероятно, будет иметь особую связь со временем, такую как сезонное движение или тренд. Недели Прежде всего, из каждого сезона можно взять образец в один 168-часовой период, чтобы немного лучше понять разницу между днями недели, а затем получить представление о том, как они могут варьировать в зависимости от сезона и праздников: fig, (ax0,ax1,ax2,ax3) = plt.subplots(4,1, figsize=(15,8)) plt.subplots_adjust(top = 0.99, bottom=0.01, hspace=0.4) traffic_df[:168].traffic_volume.plot(ax=ax0) traffic_df[(168*13):(168*14)].traffic_volume.plot(ax=ax1) traffic_df[(168*26):(168*27)].traffic_volume.plot(ax=ax2) traffic_df[(168*39):(168*40)].traffic_volume.plot(ax=ax3) Рис. 9.2. Несколько выборочных недельных периодов для объема дорожного движения (traffic_volume), представляющих каждый сезон
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 369 Приведенный выше исходный код генерирует графики, показанные на рис. 9.2. Если читать их слева направо, то можно увидеть, что все они начинаются со среды и заканчиваются вторником следующей недели. Каждый день недели начинается и заканчивается в низкой точке, с высокой точкой между ними. В будние дни, как правило, наблюдаются два пика, соответствующих утреннему и дневному часу пик, в то время как в выходные дни наблюдается только один скачок в середине дня. Есть некоторые серьезные неувязки, такие как суббота 31 октября, — Хэллоуин, который не является официальным праздником. Кроме того, 2 февраля (во вторник) началась сильная метель, и период с 27 августа по 2 сентября гораздо более хаотичен, чем в другие выбранные недели. Оказывается, в том году проходила Государственная ярмарка. Как и Хэллоуин, это не федеральный и не региональный праздник, но важно отметить, что ярмарочные площадки расположены на полпути между Миннеаполисом и Сент-Полом. Вы также заметите, что в пятницу, 29, в полночь будет пробка из-за того, что это большой день для миннеаполисских концертов. Пытаться объяснять эти неувязки при сравнении периодов во временнóм ряду — неплохое упражнение, поскольку оно помогает понимать переменные, которые следует добавить в модель, или, по меньшей мере, узнавать, чего не хватает. В нашем случае мы знаем, что переменная is_holiday не включает такие дни, как Хэллоуин, или всю характерную для штата ярмарочную неделю, и у нас нет переменной для крупных музыкальных или спортивных мероприятий. В изначальном наборе данных есть переменная snow_1h, но она была удалена, поскольку была ненадежной. В целях создания более устойчивой модели было бы целесообразно поискать надежные внешние источники данных и добавить дополнительные признаки, охватывающие все эти возможности, не говоря уже о валидации существующих переменных. А пока мы будем работать с тем, что у нас есть. Дни В проекте расширения автомагистрали крайне важно понять, как выглядит дорожное движение в течение среднего рабочего дня. Строительная бригада будет работать только в будние дни (понедельник, ..., пятница), если только у рабочих не возникнут отклонения от графика, и в этом случае они будут работать и в выходные дни. Мы также должны проводить различие между праздничными днями и другими будними днями, т. к. они, скорее всего, будут отличаться. С этой целью мы создадим кадр данных (weekend_df) и сконструируем новый столбец (type_of_day), который кодирует часы как часть "праздничного дня", "буднего дня" или "выходных дней". Далее можно сгруппировать данные по этому столбцу и столбцу времени суток (hr) и агрегировать среднее значение (mean) и среднее квадратичное отклонение (std). Затем можно реформировать кадр данных (применив метод pivot()), чтобы получить один столбец со средними значениями и стандартными отклонениями объемов дорожного движения для категории everytype_of_day, где строки представляют время суток (hr). Наконец, можно построить график результирующего кадра данных.
370 Часть II. Освоение методов интерпретации При этом можно создать интервалы со стандартными отклонениями: weekend_df = traffic_df[['hr', 'dow', 'is_holiday', 'traffic_volume']].copy() weekend_df['type_of_day'] = np.where(\ weekend_df.is_holiday == 1, 'Holiday',\ np.where(weekend_df.dow >= 5, 'Weekend', 'Weekday')) weekend_df = weekend_df.groupby(['type_of_day','hr'])[\ 'traffic_volume'].agg(['mean','std']).reset_index().pivot(\ index='hr', columns='type_of_day', values=['mean', 'std']) weekend_df.columns = [''.join(col).strip().replace('mean','')\ for col in weekend_df.columns.values] fig, ax = plt.subplots(figsize=(15,8)) weekend_df[['Holiday','Weekday','Weekend']].plot(ax=ax) plt.fill_between(weekend_df.index,\ np.maximum(weekend_df.Weekday - 2 * weekend_df.std_Weekday, 0),\ weekend_df.Weekday + 2 * weekend_df.std_Weekday,\ color='darkorange', alpha=0.2) plt.fill_between(weekend_df.index,\ np.maximum(weekend_df.Weekend - 2 * weekend_df.std_Weekend, 0),\ weekend_df.Weekend + 2 * weekend_df.std_Weekend,\ color='green', alpha=0.1) plt.fill_between(weekend_df.index,\ np.maximum(weekend_df.Holiday - 2 * weekend_df.std_Holiday, 0),\ weekend_df.Holiday + 2 * weekend_df.std_Holiday,\ color='cornflowerblue', alpha=0.1) ax.axhline(y=5300, linewidth=3, color='red', dashes=(2,2)) ax.axhline(y=2650, linewidth=2, color='darkviolet', dashes=(2,2)) ax.axhline(y=1500, linewidth=2, color='teal', dashes=(2,2)) Приведенный фрагмент исходного кода строит график, приведенный на рис. 9.3. Он отражает среднечасовой трафик, но при этом имеется довольно много вариаций, и именно поэтому строительная компания подходит к делу с осторожностью. На график нанесены горизонтальные линии, которые представляют один из трех порогов:  5300 для обозначения полной емкости автомагистрали;  2650 для обозначения половины объема, после которого строительную компа- нию будут штрафовать на указанную ежедневную сумму;  1500 является порогом отсутствия строительных работ, после которого строи- тельную компанию будут штрафовать на указанную почасовую сумму. Строители хотят работать только с понедельника по пятницу в часы, которые обычно ниже порога 1500. При таких условиях предпочтительными вляются пять часов — с 23:00 (днем ранее) до 4:00, а если бы им приходилось работать в выход-
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 371 ные, то этот график обычно задерживался бы и начинался с 1:00 и заканчивался в 6:00. В будние дни дисперсия значительно меньше, поэтому понятно, отчего строительная компания непреклонна в отношении только рабочих дней. В эти часы праздничные дни кажутся похожими на выходные, но праздничные дни тяготеют даже к большей вариативности, чем выходные, что потенциально создает еще больше проблем. Рис. 9.3. Средний почасовой объем дорожного движения в праздничные, будние и выходные дни с интервалами Обычно в подобного рода проекте проводится разведывательный анализ предсказательных переменных в той степени, в какой это делается в отношении целевой переменной. Эта книга посвящена интерпретации моделей, поэтому мы будем узнавать о предсказателях путем интерпретирования моделей. Но прежде чем перейти к моделям, необходимо подготовить для них данные. Подготовка данных Первым шагом подготовки данных является разделение их на тренировочный, валидационный и тестовый наборы. Обратите внимание, что тестовый набор данных содержит последние 13 недель (2184 часа), тогда как валидационный набор данных содержит 13 недель до того, поэтому он начинается с 4368 и заканчивается за 2184 часа до последней строки кадра данных: train = traffic_df[:-4368] valid = traffic_df[-4368:-2184] test = traffic_df[-2184:]
372 Часть II. Освоение методов интерпретации Теперь, когда кадр данных разделен, можно построить его график и убедиться, что его части разделены должным образом. Это делается с помощью следующего исходного кода: plt.plot(train.index.values, train.traffic_volume.values, label='train') plt.plot(valid.index.values, valid.traffic_volume.values, label='validation') plt.plot(test.index.values, test.traffic_volume.values, label='test') plt.ylabel('Объем дорожного движения') plt.legend() Приведенный выше исходный код производит график на рис. 9.4. Он показывает, что для тренировочного набора данных было выделено почти 2,5 года данных и около четверти года на валидацию и тестирование для каждой. С этого места в данном примере мы не будем ссылаться на валидационный набор данных, т. к. он был полезен только во время обучения для диагностики предсказательной результативности модели после каждой эпохи. Рис. 9.4. Временнóй ряд, разделенный на тренировочный (train), валидационный (validation) и тестовый (test) наборы Следующим шагом является нормализация данных в виде минимаксного нормирования. Мы делаем это, потому что большие значения приводят к более медленному обучению во всех нейронных сетях в целом, а LSTM-сети очень склонны к взрывному росту и исчезновению градиентов. С этими проблемами помогут справиться относительно однородные и малые числа. Данный вопрос будет затронут в этой главе позже, но в сущности сеть становится численно нестабильной либо неэффективной при достижении глобального минимума. Минимаксное нормирование применяется с помощью библиотечного класса MinMaxScaler из пакета scikit. На данный момент в наши обязанности входит подгонка (fit) нормировщика, чтобы мы могли его использовать всякий раз, когда он нам понадобится. Для нашей цели (traffic_volume) создается нормировщик y_scaler, и еще один создается для остальных переменных (X_scaler) со всем набором данных, чтобы преобразования были неукоснительными независимо от того, какая часть используется, будь то тренировочная (train), валидационная (valid) или тестовая (test).
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 373 Процесс подгонки лишь обеспечивает, чтобы каждая переменная вписывалась в интервал между нулем и единицей: y_scaler = MinMaxScaler() y_scaler.fit(traffic_df[['traffic_volume']]) X_scaler = MinMaxScaler() X_scaler.fit(traffic_df.drop(['traffic_volume'], axis=1)) Теперь мы преобразовываем (transform) тренировочный и тестовый наборы данных с помощью нормировщика, создавая пары y и X для каждого: y_train = y_scaler.transform(train[['traffic_volume']]) X_train = X_scaler.transform(train.drop(['traffic_volume'], axis=1)) y_test = y_scaler.transform(test[['traffic_volume']]) X_test = X_scaler.transform(test.drop(['traffic_volume'], axis=1)) Однако для модели временнóго ряда созданные нами пары y и X бесполезны, потому что каждое наблюдение является шагом во времени. И каждый временнóй шаг больше, чем переменные для этого временного шага, но предыдущие временные шаги отстают на определенную величину. Поэтому для каждого временного шага необходимо сгенерировать массив, а также массив для его отставаний. К счастью, в keras есть функция TimeseriesGenerator, которая берет ваши X и y и создает генератор, который передает данные в вашу модель. Вы должны указать определенную длину (length), т. е. число отстающих временны́х шагов (так называемое ретроспективное окно). Размер пакета (batch_size) по умолчанию равен единице, но мы используем 24, потому что клиент предпочитает получать прогнозы по 24 часа за раз; к тому же обучение и выведение результата проходят намного быстрее при более крупном размере пакета. Естественно, когда нужно будет спрогнозировать завтрашний день, понадобится погода на завтра, но временные шаги можно заполнять прогнозами погоды: gen_train_672 = TimeseriesGenerator(X_train, y_train, length=672, batch_size=24) gen_test_672 = TimeseriesGenerator(X_test, y_test, length=672, batch_size=24) print("gen_train_672:%s×%s→%s" % (len(gen_train_672), gen_train_672[0][0].shape,\ gen_train_672[0][1].shape)) print("gen_test_672:%s×%s→%s" % (len(gen_test_672), gen_test_672[0][0].shape,\ gen_test_672[0][1].shape)) Приведенный фрагмент исходного кода выводит размерности тренировочного (gen_train_672) и тестового (gen_test_672) генераторов, в которых используется длина 672 и размер пакета 24: gen_train_672: 859 × (24, 672, 14) → (24, 1) gen_test_672: 63 × (24, 672, 14) → (24, 1) Модель, которая была натренирована с одномесячным ретроспективным окном, будет нуждаться в этом генераторе. Каждый генератор представляет собой список кортежей, соответствующих каждом пакету. Индекс 0 этого кортежа является массивом признаков X, а индекс 1 — массивом меток y. Следовательно, первое число на выходе — это длина списка, т. е. число пакетов. Далее следуют размерности
374 Часть II. Освоение методов интерпретации массивов X и y. Например, gen_train_672 содержит 859 пакетов, и каждый пакет содержит 24 временных шага длиной 672 и 14 признаков. Форма предсказанных меток, ожидаемых из этих 24 временных шагов, равна (24, 1). Теперь необходимо сделать то же самое, чтобы подготовить генераторы для модели с ретроспективным окном, равным 1 неделе, которая должна иметь длину (length) 168 часов и тот же размер пакета (batch_size): gen_train_168 = TimeseriesGenerator(X_train, y_train, length=168, batch_size=24) gen_test_168 = TimeseriesGenerator(X_test, y_test, length=168, batch_size=24) Приведенный исходный код создает генераторы для модели с однонедельным ретроспективным окном. Наконец, прежде чем перейти к манипулированию моделями и методам стохастической интерпретации, давайте попытаемся сделать данные более воспроизводимыми, инициализировав случайные начальные числа: rand = 9 os.environ['PYTHONHASHSEED']=str(rand) tf.random.set_seed(rand) np.random.seed(rand) Загрузка LSTM-моделей Быстро загрузить первую модель и вывести сводную информацию о ней можно так: model_path = get_file('LSTM_traffic_672_final.hdf5', '''https://github.com/PacktPublishing/ Interpretable-MachineLearning-with-Python/ blob/master/models/LSTM_traffic_672_final.hdf5?raw=true''') lstm_traffic_672_mdl = keras.models.load_model(model_path) lstm_traffic_672_mdl.summary() Как следует из сводки, сгенерированной приведенным выше фрагментом исходного кода, модель начинается со слоя двунаправленной LSTM-сети с формой на выходе (24, 672). 24 соответствует размеру пакета, а 672 означает, что не один, а две 336-блочные LSTM-сети идут в противоположных направлениях и встречаются посередине. Модель выполняет отсев (dropout) 10%, а затем производит плотный (dense) слой с одним блоком активации ReLU. Активация ReLU обеспечивает, чтобы все предсказания были выше нуля, т. к. отрицательный объем дорожного движения не имеет смысла: Model: "Traffic_Bidirectional_LSTM_672" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= Bidir_LSTM (Bidirectional) (24, 672) 943488 _________________________________________________________________ Dropout (Dropout) (24, 672) 0 _________________________________________________________________ Dense (Dense) (24, 1) 673 =================================================================
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… Total params: 944,161 Trainable params: 944,161 375 Non-trainable params: 0 _________________________________________________________________ Затем можно загрузить вторую модель таким же образом и распечатать ее сводку (summary()): model_path = get_file('LSTM_traffic_168_final.hdf5', '''https://github.com/PacktPublishing/Interpretable-MachineLearning-with-Python/ blob/master/models/LSTM_traffic_168_final.hdf5?raw=true''') lstm_traffic_168_mdl = keras.models.load_model(model_path) lstm_traffic_168_mdl.summary() Произведенная сводка предназначена для однонаправленной LSTM-модели со 168 блоками в слое LSTM, соответствующем ретроспективному окну в 168 часов. Она имеет отсев 15% и плотный слой с одним блоком активации ReLU. Обратите внимание, что эта модель почти в 8 раз меньше двунаправленной, поскольку у нее почти в 8 раз меньше параметров: Model: "Traffic_LSTM_168" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= LSTM (LSTM) (24, 168) 122976 _________________________________________________________________ Dropout (Dropout) (24, 168) 0 _________________________________________________________________ Dense (Dense) (24, 1) 169 ================================================================= Total params: 123,145 Trainable params: 123,145 Non-trainable params: 0 _________________________________________________________________ Ради простоты, начиная с этого момента, мы будем называть двунаправленную LSTM-модель, натренированную на 4-недельном ретроспективном окне, "моделью 672". А однонаправленная LSTM-модель с ретроспективным окном в 1 неделю будет "моделью 168". Теперь давайте проведем диагностику обеих моделей, используя традиционные методы интерпретации. Диагностика моделей временного ряда с использованием традиционных методов интерпретации Модель в форме регрессора временнóго ряда можно оценивать так же, как оценивают любую регрессионную модель, т. е. с использованием метрик, выводимых из средней вадратичной ошибки или балла R-квадрат. Конечно же, бывают случаи,
376 Часть II. Освоение методов интерпретации когда требуется использовать метрику с медианами, логарифмами, девиансами1 или абсолютными значениями. Данные модели ничего из этого не требуют. Использование стандартных регрессионных метрик Функция evaluate_reg_mdl может оценивать модель, выводить некоторые стандартные регрессионные метрики и строить их график. Параметрами этой функции являются подогнанная модель (lstm_traffic_672_mdl), X_train (gen_train_672), X_test (gen_ test_672), y_train и y_test. Можно опционально указать y_scaler, чтобы модель оценивалась с обратным преобразованием меток, что значительно упрощает интерпретацию графика и корня из средней квадратичной ошибки (root mean square error, RMSE). Еще одним опциональным параметром, который в данном случае очень необходим, является y_truncate=True, т. к. наши y_train и y_test имеют более крупные размерности, чем предсказанные метки. Эта неувязка возникает из-за того, что первое предсказание выполняется через несколько временных шагов после первого временного шага в наборе данных из-за ретроспективного окна. Поэтому нам нужно было вычесть эти временные шаги из y_train, чтобы он соответствовал длине gen_train_672. Теперь оценим обе модели с помощью следующего исходного кода. Для наблюдения за ходом предсказания по мере его выполнения, мы будем использовать predopts={"verbose":1}. Обратите внимание, что на выведение результата работы первой модели (lstm_traffic_672_mdl) уходит более продолжительное время: print(lstm_traffic_672_mdl.name) y_tuple_672 = mldatasets.evaluate_reg_mdl(lstm_traffic_672_ mdl,\ gen_train_672, gen_test_672, y_train, y_test, scaler=y_scaler,\ y_truncate=True, predopts={"verbose":1}) y_train_pred_672, y_test_pred_672, y_train_672, y_test_672 = y_tuple_672 print(lstm_traffic_168_mdl.name) y_tuple_168 = mldatasets.evaluate_reg_mdl(lstm_traffic_168_mdl,\ gen_train_168, gen_test_168, y_train, y_test, scaler=y_scaler,\ y_truncate=True, predopts={"verbose":1}) y_train_pred_168, y_test_pred_168, y_train_168, y_test_168 = y_tuple_168 Приведенный фрагмент исходного кода производит графики и метрики, показанные на рис. 9.5. Оба графика имеют схожие показатели результативности, за исключением того, что модель 168 намного переобученнее, т. к. тренировочная RMSE значительно лучше. "Регрессионные графики", по сути, представляют собой графики рассеяния наблюдаемого и предсказываемого объемов дорожного движения, подогнанной линейно-регрессионной модели, чтобы показать, насколько хорошо они совпадают. Эти графики показывают, что модель 672 способна предсказывать 1 Девианс (deviance) — сумма квадратов отклонений выборочных значений от их среднего. — Прим. перев.
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 377 нулевой объем дорожного движения, когда он существенно выше. Кроме того, в модели 168 есть более экстремальные выбросные точки, а модель 672 имеет тенденцию немного отклоняться в сторону наибольших объемов дорожного движения: Рис. 9.5. Оценивание предсказательной результативности обеих моделей Оценивать модели также можно, сравнивая наблюдаемый и предсказанный объемы дорожного движения. Было бы полезно также распределить ошибку по часам и типу дня. С этой целью можно создать кадры данных с этими значениями — по одному для каждой модели. Но сначала необходимо урезать кадр данных (-y_test_pred_672.shape[0]), чтобы он соответствовал длине массива предсказаний, и нам не понадобятся все столбцы, поэтому мы предоставляем индексы только для тех, которые нас интересуют: traffic_volume под № 6, но нам также понадобится dow (№ 0), hr (№ 1) и is_holiday (№ 5). Мы переименуем traffic_volume в actual_traffic и создадим новый столбец predicted_traffic с нашими предсказаниями. Затем мы создадим столбец type_of_day, как это делалось раньше, в котором будет указано, является ли это день праздничным, будним или выходным днем. Наконец, можно отказаться от столбцов dow и is_holiday, т. к. они нам не понадобятся: evaluate_672_df = test.iloc[-y_test_pred_672.shape[0]:, [0,1,5,6]].\ rename(columns={'traffic_volume':'actual_traffic'}) evaluate_672_df['predicted_traffic'] = y_test_pred_672 evaluate_672_df['type_of_day'] = np.where(evaluate_672_df.is_holiday == 1,\ 'Holiday', np.where(evaluate_672_df.dow >= 5, 'Weekend', 'Weekday')) evaluate_672_df.drop(['dow','is_holiday'], axis=1, inplace=True)
378 Часть II. Освоение методов интерпретации Мы заменяем все цифры 672 на 168 и выполняем тот же самый исходный код, но для другой модели. Содержимое кадров данных можно быстро просмотреть, просто выполнив ячейку с evaluate_672_df либо evaluate_168_df. Обе должны иметь по 4 столбца. Агрегации предсказательных ошибок Может оказаться так, что некоторые дни недели и времена суток будут более подвержены ошибкам предсказания. Для того чтобы понять, как эти ошибки распределяются во времени, можно построить график RMSE на почасовой основе, сегментированный по типу дня (type_of_day). Для этого нужно сначала определить функцию rmse, а затем сгруппировать каждый оцененный модельный кадр данных по типу дня (type_of_day) и времени суток (hr) и применить функцию apply для агрегирования с использованием функции rmse. Затем можно реформировать кадр данных (применив метод pivot()) для того, чтобы у каждого типа дня (type_of_day) имелся столбец со значениями RMSE на почасовой основе. Затем эти столбцы можно усреднить и сохранить в виде ряда (Series) pandas: def rmse(g): rmse = np.sqrt(metrics.\ mean_squared_error(g['actual_traffic'], g['predicted_traffic'])) return pd.Series({'rmse': rmse}) evaluate_by_hr_672_df = evaluate_672_df.\ groupby(['type_of_day', 'hr']).apply(rmse).reset_index().\ pivot(index='hr', columns='type_of_day', values='rmse') evaluate_by_hr_168_df = evaluate_168_df.\ groupby(['type_of_day', 'hr']).apply(rmse).reset_index().\ pivot(index='hr', columns='type_of_day', values='rmse') mean_by_daytype_672_s = evaluate_by_hr_672_df.mean(axis=0) mean_by_daytype_168_s = evaluate_by_hr_168_df.mean(axis=0) Теперь, когда у нас есть кадры данных с почасовыми значениями RMSE для праздничных, будничных и выходных дней, а также среднее значение для этих "типов" дней, можно построить их графики. Мы создадим два подграфика (subplots): один для модели 672, и другой для модели 168. Затем выведем кадры данных evaluate_by_hr на этих подграфиках. Мы также создадим штриховые горизонтальные линии со средними значениями для каждого типа дня (type_of_day) из ряда mean_by_daytype библиотеки pandas: fig, (ax0,ax1) = plt.subplots(2, 1, figsize=(15,10)) plt.subplots_adjust(top = 0.99, bottom=0.01, hspace=0.2) evaluate_by_hr_672_df.plot(ax=ax0) ax0.set_title('Модель 672: почасовое распределение значений RMSE', fontsize=16) ax0.set_ylim([0,2500]) ax0.axhline(y=mean_by_daytype_672_s.Holiday, linewidth=2,\ color='cornflowerblue', dashes=(2,2))
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… ax0.axhline(y=mean_by_daytype_672_s.Weekday, linewidth=2,\ color='darkorange', dashes=(2,2)) ax0.axhline(y=mean_by_daytype_672_s.Weekend, linewidth=2,\ color='green', dashes=(2,2)) evaluate_by_hr_168_df.plot(ax=ax1) ax1.set_title('Модель 168: почасовое распределение значений RMSE', fontsize=16) ax1.set_ylim([0,2500]) ax1.axhline(y=mean_by_daytype_168_s.Holiday, linewidth=2,\ color='cornflowerblue', dashes=(2,2)) ax1.axhline(y=mean_by_daytype_168_s.Weekday, linewidth=2,\ color='darkorange', dashes=(2,2)) ax1.axhline(y=mean_by_daytype_168_s.Weekend, linewidth=2,\ color='green', dashes=(2,2)) Рис. 9.6. Почасовая RMSE, сегментированная по типу дня для обеих моделей 379
380 Часть II. Освоение методов интерпретации Приведенный выше исходный код сгенерировал графики, показанные на рис. 9.6. Хорошо видно, что модель 168 имеет стабильно более низкую RMSE для всех типов дня и часов дня — по меньшей мере, для времени года, представленного тестовым набором данных. Однако это может означать, что одна модель переоценивает объем дорожного движения, а переоценка не так плоха, как недооценка. Оценивание как классификационная задача Действительно, аналогично классификационным задачам с ложноположительными и ложноотрицательными классификациями, и осознавая, что одна классификация имеет более высокую стоимость, нежели другая, с помощью таких понятий любую регрессионную задачу можно сформулировать как недооценку и переоценку. Такая формулировка особенно полезна, когда одно стоит дороже другого. Если есть четко определенные пороги, как у нас для этого проекта, то любую регрессионную задачу можно оценить так же, как и классификационную. Мы выполним диагностику обеих моделей с помощью матрицы путаницы с порогами половинного объема и присутствия/отсутствия строительных работ. Для этого мы прокрутим оба кадра данных модельного оценивания в цикле и применим функцию np.where для получения двоичных массивов, когда фактические данные и предсказания превышают каждый порог. Затем можно применить функцию compare_confusion_matrices для сравнения матриц путаницы по каждой модели: evaluate_dfs = [evaluate_672_df, evaluate_168_df] lookbacks = [672, 168] for e in range(2): evaluate_df = evaluate_dfs[e] lb = lookbacks[e] actual_over_half_cap = np.where(evaluate_df['actual_traffic'] > 2650, 1, 0) pred_over_half_cap = np.where(evaluate_df['predicted_traffic'] > 2650, 1, 0) actual_over_nc_thresh = np.where(evaluate_df['actual_traffic'] > 1500, 1, 0) pred_over_nc_thresh = np.where(evaluate_df['predicted_traffic'] > 1500, 1, 0) mldatasets.compare_confusion_matrices(\ actual_over_half_cap, pred_over_half_cap,\ actual_over_nc_thresh, pred_over_nc_thresh,\ 'Модель '+str(lb)+': порог половины емкости',\ 'Модель '+str(lb)+': порог отсутствия строительных работ') Приведенный фрагмент исходного кода сгенерировал матрицы путаницы, показанные на рис. 9.7. Нас больше всего интересует процент ложноотрицательных классификаций, потому что предсказывание отсутствия трафика за переделами порога, когда он на самом деле его превысил, будет приводить к крутому штрафу. С другой стороны, стоимость ложноположительных классификаций заключается в упреждающем оставлении строительной площадки, когда трафик все-таки не превысил порога. Лучше перестраховаться, чем потом сожалеть! Если сравнить ложноотрицательные классификации для порога "отсутствие строительных работ", то у модели 672 (1,32%) они в два раза выше, чем у модели 168 (0,64%). Для порога поло-
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 381 винной емкости процент ложноотрицательных классификаций у модели 672 ниже, чем у модели 162. В конечном счете самым важным является порог отсутствия строительных работ. Рис. 9.7. Матрицы путаницы для порогов превышения половины емкости и отсутствия строительных работ для обеих моделей Теперь, когда мы задействовали традиционные методы, чтобы понять решения модели, давайте перейдем к нескольким более продвинутым модельно-агностическим методам. Генерирование LSTM-атрибуций с помощью интегрированных градиентов Впервые мы узнали об интегрированных градиентах (integrated gradients, IG) в главе 8. В отличие от других градиентных методов атрибуции, изученных в той главе, интегрированные по траектории градиенты не зависят от слоев свертки и не ограничиваются классификационными задачами. Поскольку метод вычисляет градиенты данных на входе относительно усредняемых по пути данных на выходе, вход и выход могут быть любыми! Интегрированные градиенты общепринято использовать с CNN-сетями и рекуррентными нейронными сетями (recurrent neural
382 Часть II. Освоение методов интерпретации networks, RNN), подобными той, которую мы интерпретируем в этой главе. Честно говоря, когда вы видите пример интегрированно-градиентной LSTM-сети онлайн, он имеет слой вложения и является естественноязыковым (NLP) классификатором, но метод интегрированных градиентов можно очень эффективно использовать в LSTM-сетях, которые обрабатывают даже звуки или генетические данные! Интегрированно-градиентный объяснитель и объяснители, которые мы будем использовать в дальнейшем, могут обращаться к любой части набора данных о дорожном движении. Прежде всего, давайте создадим генератор для всех них и для обеих моделей: y_all = y_scaler.transform(traffic_df[['traffic_volume']]) X_all = X_scaler.transform(traffic_df.drop(['traffic_volume'], axis=1)) gen_all_672 = TimeseriesGenerator(X_all, y_all, length=672, batch_size=24) gen_all_168 = TimeseriesGenerator(X_all, y_all, length=168, batch_size=24) Интегрированные градиенты являются методом локальной интерпретации. Давайте возьмем несколько образцов "интересующих нас примеров", которые можно проинтерпретировать. Мы знаем, что праздничные дни вызывают беспокойство, поэтому посмотрим, заметят ли наши модели важность is_holiday в первом примере (holiday_afternoon_s). Кроме того, нас интересуют и утренние часы, в особенности утренние часы с более крупным, чем в среднем, часом пик из-за погодных условий, поэтому у нас есть пример и для этого (peak_morning_s). Наконец, бóльшая интенсивность дорожного движения бывает и в жаркий день, в особенности в выходные дни (hot_saturday_s): X_df = traffic_df.drop(['traffic_volume'], axis=1).reset_index(drop=True) holiday_afternoon_s = X_df[(X_df.index >= 23471) & (X_df.dow==0) &\ (X_df.hr==16) & (X_df.is_holiday==1)] peak_morning_s = X_df[(X_df.index >= 23471) & (X_df.dow==2) & (X_df.hr==8) &\ (X_df.weather_Clouds==1) & (X_df.temp<20)] hot_saturday_s = X_df[(X_df.index >= 23471) & (X_df.dow==5) &\ (X_df.hr==12) & (X_df.temp>29)] Теперь, когда мы создали несколько экземпляров, давайте создадим экземпляры наших объяснителей. Для интегрированных градиентов из пакета alibi требуется только модель глубокого обучения, но рекомендуется задавать число шагов (n_steps) для интегральной аппроксимации и размер внутреннего пакета (internal_batch_size). Мы создадим один экземпляр объяснителя для каждой модели: ig_672 = IntegratedGradients(lstm_traffic_672_mdl,\ n_steps=25, internal_batch_size=24) ig_168 = IntegratedGradients(lstm_traffic_168_mdl,\ n_steps=25, internal_batch_size=24) Прежде чем прокрутить наши образцы и объяснители в цикле, важно понять, как вводить образец в объяснитель, потому что для этого потребуется пакет из 24 элементов. С этой целью нам нужно будет взять индекс образца после вычета ретроспективного окна (nidx). Затем для этого образца можно взять пакет из генератора
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 383 (gen_all_672). Каждый пакет включает 24 временны́х шага, поэтому nidx уменьшается на 24 (nidx//24), чтобы мы могли получить позицию пакета в этом образце. Взяв пакет в образце (batch_x) и распечатав форму (24, 672, 14), мы не должны удивиться, что первое число равно 24. Разумеется, в целях получения данных образца необходимо будет взять индекс образца в пакете (nidx%24): nidx = holiday_afternoon_s.index.tolist()[0] – 672 batch_X = gen_all_672[nidx//24][0] print(batch_X.shape) Процесс интегрированных градиентов довольно медленный, поэтому мы выполним итеративный обход только первых двух экземпляров. Цикл for будет использовать ранее описанный метод локализации пакета образца (batch_X). Указанный batch_X вводится в функцию explain. Это обусловлено тем, что мы имеем дело с регрессионной задачей, и нет целевого класса, т. е. target=None. После того как будет сгенерировано объяснение, свойство attributions будет иметь атрибуции для всего пакета. Его можно получить для образца и транспонировать (transpose), чтобы произвести изображение вот такой формы: (14, 1b). Остальной исходный код в цикле for просто получает метки для использования в делениях шкалы, а затем строит график изображения, растянутого в соответствии с размерами нашего рисунка (figure), вместе с его метками: samples = [holiday_afternoon_s, peak_morning_s] sample_names = ['второй половины праздничного дня', 'утра в час пик'] igs = [ig_672, ig_168] lbs = [672, 168] for s in range(len(samples)): for i in range(len(igs)): nidx = samples[s].index.tolist()[0] – lb lb = lbs[i] if lb == 672: batch_X = gen_all_672[nidx//24][0] p = 5 # Создать 5 меток делений, f = '7D' # отделенные однонедельными периодами else: batch_X = gen_all_168[nidx//24][0] p = 8 # Создать 8 меток 8 делений, f = '1D' # отделенные однодневными периодами explanation = igs[i].explain(batch_X, target=None) attributions = explanation.attributions attribution_img = np.transpose(attributions[nidx%24,:,:]) end_date = traffic_df.iloc[samples[s].index].index.to_pydatetime()[0] date_range = pd.date_range(end=end_date, periods=p,\ freq=f).to_pydatetime().tolist()
384 Часть II. Освоение методов интерпретации columns = samples[s].columns.tolist() plt.title('Интегрированно-градиентная карта атрибуций для {} в модели {}'.\ format(sample_names[s], lb), fontsize=16) plt.imshow(attribution_img, interpolation='nearest',\ aspect='auto', cmap='plasma') plt.xticks(np.linspace(0,672,p).astype(int), labels=date_range) plt.yticks([*range(14)], labels=columns) plt.colorbar(pad=0.01,fraction=0.02,anchor=(1.0,0.0)) plt.show() Рис. 9.8. Аннотированная интегрированно-градиентная карта атрибуций для праздничной второй половины дня в обеих моделях
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 385 Приведенный выше исходный код сгенерирует графики, показанные на рис. 9.8 и 9.9. На оси y показаны имена переменных, а на оси x — даты, соответствующие ретроспективному окну для анализируемого образца. Крайняя правая часть оси x — это дата образца, и, двигаясь влево, вы возвращаетесь во времени назад. Например, образец с праздничной второй половиной дня был 3 сентября в 16:00, и для модели 672 есть 4-недельный ретроспективный период, поэтому каждое деление шкалы — это неделя до этой даты. Модель 168 имеет только однонедельный ретроспективный период, поэтому каждое деление шкалы представляет один день. Рис. 9.9. Аннотированная интегрированно-градиентная карта атрибуции для утра в час пик в обеих моделях По интенсивности на картах атрибуции на рис. 9.8 можно судить о том, какой час/переменные играли ключевую роль для предсказания. Цветовая панель справа от каждой карты атрибуции может служить ключом. Отрицательные числа соответ-
386 Часть II. Освоение методов интерпретации ствуют более темному цвету и отрицательной корреляции, тогда как положительные числа соответствуют более светлому цвету и положительной корреляции. Однако довольно очевидной является тенденция к ослаблению интенсивности по мере движения назад во времени. Хорошо видно, что в двунаправленной модели 672 это происходит с обоих концов, что имеет смысл, потому что она — двунаправленная. Удивительно то, так быстро это происходит. Что касается утра в час пик на рис. 9.9, то атрибуции имеют смысл, поскольку обе модели понимают, что это ясная погода после того, как ранее было дождливо и облачно, и это привело к тому, что час пик достигал максимума очень быстро, а не увеличивался медленно. В определенной степени LSTM-сеть усвоила, что важность имеет только недавняя погода — не более чем за 2–3 дня. Однако это не единственная причина, по которой интегрированные градиенты затухают. Они также затухают из-за проблемы исчезающего градиента. Эта проблема возникает во время обратного распространения, поскольку на каждом шаге значения градиентов умножаются на весовые матрицы, поэтому градиенты могут уменьшаться экспоненциально до нуля. Наши LSTM организованы в очень длинную последовательность, что делает сеть всё более неэффективной для долгосрочного улавливания зависимостей. К счастью, эти LSTM поддерживают внутреннее состояние, т. е. они выстраивают пакеты в последовательность, задействуя состояния из предыдущего пакета. Поддержка внутреннего состояния обеспечивает усвоение знаний из длинной последовательности, несмотря на исчезающие градиенты. Однако, если посмотреть на карты атрибуции для "второй половины праздничного дня", то, похоже, что для is_holiday нет атрибуции ни для одной из моделей. Оказывается, 3 сентября (День труда) на рис. 9.9 отстоит почти на 2 месяца после предыдущего праздника (Дня независимости), который является более интенсивным в плане движения праздничным днем. Возможно ли, что модель не улавливает эти закономерности? Мы могли бы попробовать разбить праздники на категории по закономерностям их дорожного движения, чтобы посмотреть, поможет ли это модели их идентифицировать. Мы также могли бы сделать скользящие агрегации предыдущих погодных условий, чтобы облегчить модели улавливание недавних погодных регулярностей. Погодные регулярности охватывают несколько часов, поэтому совершенно естественно их агрегировать, не говоря уже о том, что их легче интерпретировать. Методы интерпретации могут указать нам правильное направление в том, как усовершенствовать модели, и, безусловно, для усовершенствования есть много возможностей. Учитывая то, что мы узнали о почасовом распределении значений RMSE, матрицах путаницы и интегрированно-градиентных картах атрибуции, нет сомнений в том, что модель 168 является более качественной. Она имеет более низкие значения RMSE в рабочие часы и более низкую частоту ложноотрицательных классификаций для превышения порога отсутствия строительных работ. Интегрированноградиентные карты атрибуции показывают, что недельная ретроспектива не слишком коротка, потому что модель 672 не имеет ничего, кроме нулевых атрибуций в
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 387 течение более 3 недель. Как бы то ни было, мы всё равно перейдем к модели 672, потому что, поскольку она имеет больше недостатков, она создает более интересный вариант исследования! Следующий исходный код обеспечивает использование модели 672, но весь исходный код всегда можно выполнить заново, продолжив с моделью 168, и провести сравнение: lookback = 672 gen_all = gen_all_672 lstm_traffic_mdl = lstm_traffic_672_mdl Далее попробуем применить перестановочный метод! Вычисление глобальных и локальных атрибуций с помощью ядерного объяснителя SHAP Перестановочные методы вносят изменения в модельные данные на входе с целью проведения диагностики величины их отличия от данных на выходе. Мы впервые обсудили этот вопрос в главе 4, но, если вы помните, для выполнения этих перестановок существует коалиционный каркас, который будет производить средний маргинальный вклад для каждого признака по разным коалициям признаков. Результатом этого процесса являются значения Шепли, которые обладают существенными математическими свойствами, такими как аддитивность и симметрия. К сожалению, вычисление значений Шепли обходится дорого для наборов данных, которые имеют немалый размер, поэтому в библиотеке SHAP есть методы аппроксимации. Одним из таких методов является ядерный объяснитель, который мы использовали в главе 5. Он аппроксимирует значения Шепли с помощью взвешенной локальной линейной регрессии, точно так же, как это делается в методе LIME. Зачем использовать ядерный объяснитель? Мы имеем дело с моделью глубокого обучения, так почему же мы не используем глубокий объяснитель SHAP, как это делалось с CNN-сетью в главе 8? Глубокий объяснитель адаптировал алгоритм DeepLIFT, чтобы аппроксимировать значения Шепли. Он очень хорошо работает с любой сетью прямого распространения, которая используется для табличных данных, CNN- и RNN-сетями со слоем вложения, например для естественноязыкового (NLP) классификатора или даже для обнаружения геномных последовательностей. Его применение становится неоднозначным для многоперемерного временнóго ряда, потому что глубокий объяснитель не знает, что делать с трехмерным массивом входных данных. И даже если бы он знал, указанный массив содержит данные, относящиеся к предыдущим временны́м шагам, поэтому вы не сможете переставить один временнóй шаг без учета предыдущих. Например, если перестановка предписывает температуру на 5 градусов ниже, не повлияет ли она на все температуры предыдущего временнóго шага в течение определенного числа часов? А что делать, если она ниже на 20 градусов? Разве это не означает, что это, скорее всего, происходит в другое время года с со-
388 Часть II. Освоение методов интерпретации вершенно другой погодой — возможно, также с большей облачностью и бóльшим объемом снега? Ядерный объяснитель SHAP может получать любую произвольную функцию предсказания типа черного ящика. Он также делает допущения о размерностях входных данных. К счастью, можно изменить входные данные до того, как они будут переставлены, чтобы ядерному объяснителю казалось, будто он имеет дело с табличным набором данных. Произвольная функция предсказания не должна просто вызывать модельную функцию предсказания — она может изменять данные как на входе, так и на выходе! Определение стратегии, позволяющей работать с моделью многопеременного временного ряда В целях имитирования возможных прошлых погодных регулярностей на основе переставленных входных данных мы могли бы создать генеративную модель или что-то в этом роде. Эта стратегия поможет нам генерировать разновидности прошлых временны́х шагов, которые вписываются в переставленный временнóй шаг, а также генерировать изображения для конкретного класса. Хотя это, вероятно, и приведет к более точным предсказаниям, мы не будем использовать эту стратегию, т. к. она невероятно затратная по времени. Вместо этого мы найдем временной ряд, который наилучшим образом соответствует переставленным входным данным, с существующими примерами из нашего генератора gen_all. Для отыскания именно того, который расположен к переставленному входу ближе всего, можно использовать метрики расстояния. Однако нам следует установить некие ограждения, потому что, если перестановка относится к субботе в 5:00 с температурой 27 C и 90%-ной облачностью, то самое близкое наблюдение к этому времени может быть пятницей в 7:00, но независимо от погодного трафика, он будет совершенно другим. Поэтому можно имплементировать фильтрационную функцию, которая обеспечивает отыскание только самых близких наблюдений для одного и того же дня недели (dow), признака праздничного дня (is_holiday) и времени суток (hr). Фильтрационная функция также может очищать переставленный образец, удаляя или модифицируя что-либо бессмысленное для модели, например непрерывное значение категориального признака (рис. 9.10). На приведенной схеме показана остальная часть процесса, в которой используется функция расстояния для отыскания ближайшего наблюдения к модифицированному переставленному образцу. Эта функция возвращает индекс ближайшего наблюдения, но модель не может предсказывать на одиночных наблюдениях (или временны́х шагах), поэтому ей требуется прошлая почасовая история вплоть до ретроспективного окна. По этой причине она извлекает из генератора нужный пакет и делает предсказание на нем, но предсказания будут в другой шкале, поэтому их необходимо преобразовать обратно с помощью нормировщика y_scaler. После того как функция предсказания прокрутит все образцы в цикле, сделает для них прогнозы и заново нормирует их, она отправляет их обратно в ядерный объяснитель, который выдает их значения SHAP.
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 389 Рис. 9.10. Стратегия аппроксимации перестановок Заложение основы для стратегии аппроксимации перестановок Можно определить прикладную фильтрационную функцию (filt_fn). Для этого требуется кадр данных pandas со всем набором данных (X_df), который нужно отфильтровывать, а также переставленный образец (x) для фильтрации и длина ретроспективного окна (lookback). Указанная функция также может модифицировать переставленный образец. В данном случае это необходимо сделать, т. к. очень многие признаки модели являются дискретными, а процесс перестановки делает их непрерывными. Как мы упоминали ранее, фильтрация лишь защищает функцию расстояния от отыскания бессмысленного образца, ближайшего к переставленному образцу, путем лимитирования вариантов: def filt_fn(X_df, x, lookback): x_ = x.copy() x_[0] = round(x_[0]) x_[1] = round(x_[1])
390 Часть II. Освоение методов интерпретации x_[4] = round(x_[4]) x_[5] = round(x_[5]) if x_[1] < 0: x_[1] = 24 + x_[1] x_[0] = x_[0] – 1 if x_[0] < 0: x_[0] = 7 + x_[0] X_filt_df = X_df[(X_df.index >= lookback) &\ (X_df.dow==x_[0]) & (X_df.hr==x_[1]) &\ (X_df.is_holiday==x_[5]) & (X_df.temp-5<=x_[2]) &\ (X_df.temp+5>=x_[2])] return X_filt_df, x Если обратиться к рис. 9.10, можно заметить, что после фильтрационной функции необходимо определить функцию расстояния. Можно было бы использовать любую стандартную функцию расстояния, принятую в scipy.spatial.distance.cdist, например для евклидова, косинусного расстояния или расстояния Хэмминга. Проблема с этими стандартными функциями расстояния состоит в том, что они хорошо работают либо с непрерывными, либо с дискретными переменными, но не с обоими типами одновременно. А в этом наборе данных у нас есть и то и другое! К счастью, существует несколько альтернатив, которые способны справляться с обоими, например гетерогенная метрика евклидова наложения (heterogeneous Euclidean-overlap metric, HEOM) и гетерогенная метрика разности значений (heterogeneous value difference metric, HVDM). Оба метода применяют разные метрики расстояния в зависимости от природы переменной. В HEOM для непрерывной переменной используется нормализованное евклидово расстояние ( a  b 2 ), а для дискретной переменной расстояние "наложения". С HVDM все сложнее, потому что для непрерывных переменных это абсолютное расстояние между обоими значениями, деленное на среднее квадратичное отклонение рассматриваемого признака, умноженное на четыре; оно является отличной метрикой расстояния для обработки выбросов. Для дискретных переменных в нем используется нормализованная метрика разности значений, которая основана на разнице между условной вероятностью обоих значений. Несмотря на то что HVDM лучше, чем HEOM, для наборов данных с многочисленными непрерывными значениями, в данном случае это излишне. После фильтрования набора данных по дням недели (dow) и времени суток (hr) все остальные дискретные признаки являются двоичными, поэтому расстояние "наложения" подходит идеально, а для трех оставшихся непрерывных признаков (temp, rain_1h и cloud_coverage) должно быть достаточно евклидова расстояния. В distython есть метод расстояния HEOM, и для него требуется только фоновый набор данных (X_df.values) и индексы категориальных признаков (cat_idxs). Эти признаки можно выявить программно с помощью команды np.where. Если вы хотите подтвердить,
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 391 что они как раз те самые, выполните инструкцию print(cat_idxs) в ячейке. Следует опустить только индексы 2, 3 и 4: cat_idxs = np.where(\ traffic_df.drop(['traffic_volume'], axis=1).dtypes != np.float64)[0] heom_dist = HEOM(X_df.values, cat_idxs) print(cat_idxs) Теперь можно создать лямбда-функцию, которая принимает всё, что изображено на рис. 9.10. В ней используется функция approx_predict_ts, которая отвечает за весь конвейер. Он принимает нашу фильтрационную функцию (filt_fn), функцию расстояния (heom_dist. heom), генератор (gen_all) и подогнанную модель (lstm_traffic_mdl) и выстраивает их в цепочку, как описано на рис. 9.10. Она также нормирует данные с помощью наших нормировщиков (x_scaler и y_scaler). Расстояние вычисляется на преобразованных признаках для более высокой точности, и на выходе предсказания преобразуются в обратную сторону: predict_fn = lambda X: mldatasets.\ approx_predict_ts(X, X_df, gen_all, lstm_traffic_mdl,\ dist_metric=heom_dist.heom, lookback=lookback,\ filt_fn=filt_fn, X_scaler=X_scaler, y_scaler=y_scaler) Теперь можно использовать функцию предсказания с ядерным объяснителем, но это следует делать на образцах, наиболее репрезентативных для ожидаемых условий работы строительной бригады, т. е. строители планируют работать только с мая по октябрь, предпочтительно в будние дни и часы с низкой интенсивностью дорожного движения. С этой целью давайте создадим кадр данных (working_season_df), который включает только эти месяцы и инициализирует ядерный объяснитель (KernelExplainer) функцией predict_fn и k средними кадра данных в качестве фоновых данных: working_season_df = traffic_df[lookback:].drop(['traffic_volume'], axis=1).copy() working_season_df = working_season_df[(working_season_df.index.month >= 5) &\ (working_season_df.index.month <= 10)] explainer = shap.KernelExplainer(predict_fn,\ shap.kmeans(working_season_df.values, 10)) Теперь можно сгенерировать значения SHAP для случайного набора наблюдений кадра данных working_season_df. Вычисление значений SHAP Мы отберем (sample) из него 48 наблюдений. Ядерный объяснитель (KernelExplainer) работает довольно медленно, в особенности, когда он применяет наш метод аппроксимации. В целях получения оптимальной глобальной интерпретации лучше всего использовать большое число наблюдений, но также и большое число (nsamples) образцов, т. е. число повторных оцениваний модели при объяснении каждого предсказания. К сожалению, наличие 50 наблюдений каждого приведет к то-
392 Часть II. Освоение методов интерпретации му, что на выполнение своей работы объяснителю потребуется много часов в зависимости от имеющихся у вас вычислительных возможностей, поэтому мы зададим nsamples=5. Можно посмотреть на индикатор прогрессии SHAP и соответствующим образом настроить это число. После выполнения работы он создаст сводный график (summary_plot) важности признаков, содержащий значения SHAP: X_samp_df = working_season_df.sample(48, random_state=rand) shap_values = explainer.shap_values(X_samp_df, nsamples=5) shap.summary_plot(shap_values, X_samp_df) Приведенный выше исходный код строит сводный график, показанный на рис. 9.11. Неудивительно, что признаки hr и dow являются наиболее важными, за которыми следуют несколько погодных признаков. Как ни странно, температура и дождь, похоже, на предсказания не влияют, но поздняя весна и осень могут и не быть значимым фактором. Либо, вполне возможно, что более качественную глобальную интерпретацию дадут больше наблюдений и более высокое число nsample. Рис. 9.11. Сводный график SHAP, основанный на значениях SHAP, произведенных 48 отобранными наблюдениями То же самое можно сделать и с интересующими нас экземплярами, которые мы выбрали в предыдущем разделе для локальных интерпретаций. Давайте прокрутим все эти точки данных в цикле. Затем можно создать переменную shap_values_single, но на этот раз с 60 образцами (nsamples=60), а после этого сгенерировать силовой график (force_plot) для каждого из них: datapoints = [holiday_afternoon_s, peak_morning_s, hot_saturday_s] datapoint_labels = ['Вторая половина праздничного дня',\ 'Утро в час пик', 'Жаркая суббота']
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 393 for i in range(len(datapoints)): print(datapoint_labels[i]) shap_values_single = explainer.shap_values(datapoints[i], nsamples=60) shap.force_plot(explainer.expected_value, shap_values_single[0],\ datapoints[i], matplotlib=True) plt.show() Приведенный исходный код генерирует графики, показанные на рис. 9.12. "Вторая половина праздничного дня" имеет час (hr=16), который подталкивает к более высокому предсказанию, тогда как понедельник (dow=0) и праздник (is_holiday=1) являются движущей силой в противоположном направлении. С другой стороны, вариант "утро в час пик" в основном является пиковым из-за часа (hr=8.0), но оно имеет высокую облачность (cloud_coverage), подтверждающую облачную погоду (weather_Clouds) и, тем не менее, отсутствие дождя (rain_1h=0.0). Наконец, вариант "жаркая суббота" имеет день недели (dow=5), требующий более низкого значения, но аномально высокое значение в основном связано с тем, что время относится к полудню, и у данного варианта есть набор погодных признаков, наиболее важным из которых является средняя температура temp=29.42... (85 F). Рис. 9.12. Силовые графики, сгенерированные со значениями SHAP и nsamples=60 для второй половины праздничного дня, утра в час пик и жаркой субботы
394 Часть II. Освоение методов интерпретации С помощью подхода SHAP, основанного на теории игр, можно замерить число перестановок для существующих наблюдений, которые маргинально варьируют предсказанный исход по многочисленным возможным коалициям признаков. Однако этот подход бывает очень ограничительным, потому что существующая дисперсия фоновых данных формирует наше понимание дисперсии исхода. В реальном мире изменчивость нередко определяется тем, что НЕ представлено в ваших данных, но является бесконечно правдоподобным. Например, достижение температуры воздуха 25 °C до 5:00 летом в Миннеаполисе не является обычным явлением, но с глобальным потеплением оно может стать частым событием, поэтому мы хотели бы просимулировать то, как это может повлиять на регулярности дорожного движения. Прогнозные модели особенно подвержены риску, поэтому симулирование является важнейшим компонентом интерпретации для диагностики этой неопределенности. Более глубокое понимание неопределенности может привести к созданию более устойчивых моделей либо непосредственно информировать принимаемые решения. Далее мы обсудим вопрос о том, как производить симуляции с помощью методов анализа чувствительности. Выявление влияющих признаков с помощью факторной приоритизации Метод Морриса — один из нескольких методов анализа глобальной чувствительности, которые варьируют от более простого дробного факториала до сложной фильтрации Монте-Карло. Метод Морриса находится где-то посередине этого спектра, и его можно разделить на две категории. В нем используется взятие по одному образцу за раз, а значит, между поочередными симуляциями изменяется только одно значение. Это также элементарные эффекты (elementary effects, EE), т. е. он не квантифицирует точный эффект фактора в модели, а замеряет его важность и взаимосвязь с другими факторами. Кстати, фактор — это просто еще одно слово для признака или переменной, которое обычно используется в прикладной статистике. В целях обеспечения согласованности со связанной с ним теорией указанный термин будет использоваться в этом и последующем разделах. Еще одним свойством метода Морриса является то, что он обходится дешевле в вычислительном плане, чем дисперсные методы, которые мы изучим далее. Он может обеспечивать больше сущностных сведений, чем более простые и менее дорогостоящие методы, такие как регрессионные, производные или факториальные методы. Он не способен квантифицировать эффекты точно, но зато может выявлять эффекты с незначительными или интеракционными последствиями, что делает его идеальным методом для обследования факторов, когда число факторов невелико. Обследование также носит название приоритизации факторов, поскольку она может расставлять ваши факторы по порядку в соответствии с тем, как они классифицированы.
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 395 Вычисление индексов чувствительности Морриса Метод Морриса выводит распределение элементарных эффектов, которые он ассоциирует с отдельным фактором. Каждое распределение элементарных эффектов имеет среднее значение ( ) и среднее квадратичное отклонение ( ). Эти две статистические величины помогают относить факторы в разные классификации. Среднее значение может быть отрицательным, когда модель немонотонна, поэтому вариант метода Морриса подстраивается под это с помощью абсолютных значений ( (* ), обеспечивая бóльшую интерпретируемость. Здесь мы будем использовать этот вариант. Теперь давайте ограничим масштабы этой проблемы, чтобы сделать ее более управляемой. Неопределенность дорожного движения, с которой столкнется строительная бригада, будет продолжаться с мая по октябрь, с понедельника по пятницу, с 23:00 до 4:00 часов, поэтому можно взять кадр данных working_season_df, а затем его подмножество, произведя кадр рабочих часов (working_hrs_df), который можно описать (describe). Мы включим процентили 2,5-й, 50-й и 97,5-й, чтобы понять, где находятся медиана и выбросы: working_hrs_df = working_season_df[(working_season_df.dow < 5) &\ ((working_season_df.hr < 5) | (working_season_df.hr > 22))] working_hrs_df.describe(percentiles=[.025,.5,.975]).transpose() Рис. 9.13. Сводная статистика за период, который строительная бригада планирует отработать
396 Часть II. Освоение методов интерпретации Приведенный выше исходный код сгенерировал таблицу на рис. 9.13. Эту таблицу можно использовать для извлечения диапазонов, которые мы будем использовать для наших признаков в симуляции. В типичной ситуации мы используем правдоподобные значения, которые превысили существующие максимумы или минимумы. В большинстве моделей любое признаковое значение можно увеличить или уменьшить сверх известных пределов, и поскольку модель усвоила монотонную связь, она может выводить реалистичный исход. Например, она может усвоить, что дождь после определенного момента будет всё значительнее уменьшать интенсивность дорожного движения. Затем, допустим, вы хотите просимулировать сильное подтопление, скажем, 30 мм осадков в час; в такой ситуации она сможет точно предсказывать отсутствие дорожного движения. Однако, поскольку мы используем метод аппроксимации предсказания, который отбирает образцы из исторических значений, мы ограничены тем, насколько далеко можно раздвинуть границы за пределы известного. По этой причине в качестве наших пределов мы будем использовать значения 2,5-го и 97,5-го процентилей. Необходимо отметить, что это важная оговорка для любых находок, в особенности для признаков, которые могут правдоподобно выходить за эти пределы, таких как средняя температура (temp), миллиметры осадков в час (rain_1h) и облачность (cloud_coverage). Следует отметить еще одну вещь в резюме рис. 9.13, а именно, что многие двоичные признаки, связанные с погодой, очень разрежены. Об этом можно судить по их чрезвычайно низкому среднему значению. Каждый фактор, добавляемый в симуляцию на основе анализа чувствительности, ее замедляет, поэтому мы возьмем только три верхних, т. е. ясную погоду (weather_Clear), облачную погоду (weather_Clouds) и дождливую погоду (weather_Rain). Эти факторы указываются вместе с другими шестью факторами в словаре задачи (morris_problem), в котором задаются их соответствующие имена (names), границы (bounds) и группы (groups). Границы (bounds) имеют критическую важность, потому что они обозначают диапазоны значений, для которых каждый фактор будет симулироваться. Мы будем использовать [0; 4] (понедельник — пятница) для дня недели (dow) и [ 1 ; 4] (23:00–4:00 часов) для времени суток (hr). Фильтрационная функция автоматически транслирует отрицательные часы в часы предыдущего дня, т. е. 1 во вторник экивалентно 23 в понедельник. Остальные границы будут информироваться процентилями. Обратите внимание, что все группы (groups) имеют факторы в одной и той же группе, за исключением трех погодных факторов: morris_problem = { # Имеется 9 переменных 'num_vars': 9, # Вот их имена 'names': ['dow', 'hr', 'temp', 'rain_1h', 'cloud_coverage', 'is_holiday',\ 'weather_Clear', 'weather_Clouds', 'weather_Rain'], # Правдоподобные диапазоны, в которых # мы будем перемещать переменные
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 'bounds': [[0, 4], [-1, 4], 397 # dow # hr [3., 25.], # temp (C) [0., 1.5], # rain_1h [0., 90.], # cloud_coverage [0, 1], # is_holiday [0, 1], # weather_Clear [0, 1], # weather_Clouds [0, 1] # weather_Rain ], # Только погода собирается в группу 'groups': ['dow', 'hr', 'temp', 'rain_1h', 'cloud_coverage', 'is_holiday',\ 'weather', 'weather', 'weather'] } Определив словарь, можно сгенерировать образцы метода Морриса с помощью метода sample() библиотеки SALib. В дополнение к словарю требуется число траекторий (300) и уровней (num_levels=4). В указанном методе используется сетка с факторами и уровнями для построения траекторий, для которых данные на входе случайно перемещаются по одному элементу за раз (one-at-a-time, OAT). Здесь важно учитывать, что большее число уровней увеличивает разрешающую способность сетки, потенциально способствуя более качественному анализу. Однако это может занимать очень много времени. Лучше начать с соотношения между числом траекторий и уровней 25:1 или выше. Затем это соотношение можно поступательно уменьшать. Другими словами, если у вас достаточно вычислительных возможностей, то можно сделать num_levels соответствующим числу траекторий, но если ваши вычислительные возможности ограничены, то можно попробовать optimal_trajectories=True. Однако, учитывая, что у нас есть группы, аргумент local_optimization должен быть задан как False. Результат работы метода sample() представляет собой массив, состоящий из одного столбца для каждого фактора и  G  1  T строк (где G — это число групп, T — число траекторий). У нас 7 групп и 300 траекторий, поэтому инструкция print должна распечатать форму в 2400 строк на 9 столбцов: morris_sample = ms.sample(morris_problem, 300, num_levels=4, seed=rand) print(morris_sample.shape) Учитывая, что функция предсказания будет работать только с 14 факторами, необходимо модифицировать образцы, заполнив оставшиеся пять факторов нулями. Мы используем нули, потому что это среднее значение указанных признаков. Медианы имеют меньше всего шансов предсказывать увеличение дорожного движения, но вам следует приспособить значения, которые используются в каждом конкретном случае по умолчанию. Если вы вспомните наш пример сердечно-сосудистых заболеваний (ССЗ) из главы 2, значение признака, которое увеличило бы риск ССЗ, иногда было минимальным либо максимальным.
398 Часть II. Освоение методов интерпретации Функция np.hstack может конкатенировать массив горизонтально так, чтобы три нулевых фактора следовали за образцами для первых восьми факторов. Затем есть одинокий девятый фактор образца, соответствующий дождливой погоде (weather_Rain), за которым следуют два нулевых фактора. Результирующий массив должен содержать то же число строк, что и раньше, но 14 столбцов: morris_sample_mod = np.hstack((\ morris_sample[:,0:8], np.zeros((morris_sample.shape[0],3)),\ morris_sample[:,8:9], np.zeros((morris_sample.shape[0],2)))) print(morris_sample_mod.shape) Массив NumPy под названием morris_sample_mod теперь содержит образцы по методу Морриса в форме, понятной нашей функции предсказания. Если бы эта модель была натренирована на табличном наборе данных, то можно было бы просто использовать модельную функцию предсказания. Однако, как и в случае с SHAP, необходимо применять метод аппроксимации. На этот раз мы не будем задействовать predict_fn, потому что в approx_predict_ts мы хотим задать одну дополнительную опцию, progress_bar=TRUE. Все остальное останется прежним. Индикатор прогрессии пригодится, потому что это займет некоторое время. Выполните ячейку и сделайте перерыв на кофе: morris_preds = mldatasets.approx_predict_ts(morris_sample_mod,\ X_df, gen_all, lstm_traffic_mdl, filt_fn=filt_fn, dist_metric=heom_dist.heom,\ lookback=lookback, X_scaler=X_scaler, y_scaler=y_scaler, progress_bar=True) Для проведения анализа чувствительности с помощью функции analyze библиотеки SALib нужен лишь словарь задачи (morris_problem), исходные образцы по методу Морриса (morris_sample) и предсказания, которые мы только что сделали с этими образцами (morris_preds). Существует необязательный аргумент уровня интервала уверенности (conf_level), но подойдет принятое по умолчанию значение 95. Для вычисления этого уровня уверенности используется многократный отбор образцов, число которых по умолчанию равно 1000. Этот параметр также можно изменить с помощью необязательного аргумента num_resamples: morris_sensitivities = ma.analyze(morris_problem, morris_sample, morris_preds,\ print_to_console=False) Анализирование элементарных эффектов Функция analyze библиотеки SALib будет возвращать словарь с индексами чувствительности по методу Морриса, включая среднее значение ( ) элементарного эффекта и его среднее квадратичное отклонение ( ), а также абсолютное значение среднего значения (* ). Легче оценивать эти значения в табличном формате, поместив их в кадр данных и отсортировав и закодировав их по цвету в соответствии с величиной * , которую можно интерпретировать как совокупную важность факто-
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 399 ра. С другой стороны,  — это мера, определяющая, насколько фактор взаимодействует с другими: morris_df = pd.DataFrame({'Признаки':morris_sensitivities['names'],\ 'μ':morris_sensitivities['mu'],\ 'μ*':morris_sensitivities['mu_star'],\ 'σ':morris_sensitivities['sigma']}) morris_df.sort_values('μ*', ascending=False).style.\ background_gradient(cmap='plasma', subset=['μ*']) Приведенный выше исходный код выводит на экран кадр данных, изображенный на рис. 9.14. Хорошо видно, что is_holiday неожиданно становится вторым по важности фактором, хотя и не с огромным отрывом, по меньшей мере, в пределах, указанных в определении задачи (morris_problem). Следует отметить еще одну вещь: погода (weather) действительно имеет абсолютный элементарный эффект, но неубедительные интеракционные эффекты. Группы сложно продиагностировать, в особенности, когда они представляют собой разреженные двоичные факторы. Рис. 9.14. Разложение факторов на элементарные эффекты Кадр данных на рис. 9.14 не лучший способ визуализации элементарных эффектов. Когда факторов не слишком много, легче построить их график. Библиотека SALib поставляется с двумя графопостроительными методами. Горизонтальная гистограмма (horizontal_bar_plot) и график ковариаций (covariance_plot) могут быть размещены рядом друг с другом. График ковариаций превосходен, но он не аннотирует области, которые очерчивает. Об этом мы узнаем позже. И поэтому, исключительно в учебных целях для размещения аннотаций, мы будем использовать метод text(): fig, (ax0, ax1) = plt.subplots(1,2, figsize=(12,8)) mp.horizontal_bar_plot(ax0, morris_sensitivities, {}) mp.covariance_plot(ax1, morris_sensitivities, {}) ax1.text(ax1.get_xlim()[1] * 0.45, ax1.get_ylim()[1] * 0.75,\ 'Нелинейно и/или немонотонно',\ horizontalalignment='center', color='gray')
400 Часть II. Освоение методов интерпретации ax1.text(ax1.get_xlim()[1] * 0.75, ax1.get_ylim()[1] * 0.5,\ 'Почти', horizontalalignment='center', color='gray') ax1.text(ax1.get_xlim()[1] * 0.83, ax1.get_ylim()[1] * 0.2,\ 'Монотоно', horizontalalignment='center', color='gray') ax1.text(ax1.get_xlim()[1] * 0.9, ax1.get_ylim()[1] * 0.025,\ 'Линейно', horizontalalignment='center', color='gray') Приведенный исходный код создает графики, показанные на рис. 9.15. Гистограмма слева ранжирует факторы по * , тогда как линии, выступающие из каждого столбика, обозначают соответствующие им полосы уверенности. График ковариаций справа представляет собой график рассеяния с * по оси x и  по оси y. Поэтому, чем правее точка, тем она важнее, а чем дальше она находится на графике, тем больше она взаимодействует с другими факторами и становится всё менее монотонной. Естественно, это означает, что факторы, которые взаимодействуют мало и в основном являются монотонными, соответствуют линейно-регрессионным допущениям, таким как линейность и мультиколлинеарность. Однако спектр между линейным и нелинейным или немонотонным определяется по диагонали соотношением между  и * . Рис. 9.15. Столбиковый и ковариационный графики, изображающие элементарные эффекты Из приведенного графика ковариаций следует, что все факторы являются нелинейными либо немонотонными. Фактор hr, безусловно, наиболее важный, при этом
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 401 следующие три (is_holiday, dow и temp) сгруппированы относительно близко. Группа weather на графике отсутствует, потому что интерактивность была неубедительной, но миллиметры осадков в час (rain_1h) и облачность (cloud_coverage) сами по себе были более интерактивными, чем важными. Элементарные эффекты помогают нам понимать, как классифицировать наши факторы в соответствии с их влиянием на исходы модели. Однако этот метод является неустойчивым для надлежащего квантифицирования своих эффектов либо эффектов, выводимых из взаимодействий факторов. Для этого нам пришлось бы обратиться к дисперсному глобальному методу, в котором для разложения дисперсии данных на выходе и отслеживания ее до данных на входе используется вероятностный каркас. Эти методы включают тест амплитудной чувствительности на рядах Фурье (Fourier amplitude sensitivity test, FAST) и метод Соболя. Далее мы рассмотрим последний подход из двух упомянутых. Квантифицирование неопределенности и стоимостной чувствительности с помощью фиксирования факторов Благодаря индексам Морриса стало очевидно, что все факторы являются нелинейными либо немонотонными. Между ними имеется высокая степень интерактивности, как и ожидалось! Не должно быть ничего удивительного в том, что климатические факторы (temp, rain_1h и cloud_coverage), вероятно, мультиколлинеарны с временем суток (hr). Между временем суток (hr), праздничным днем (is_holiday) и днем недели (dow), с одной стороны, и целью, с другой, можно также найти закономерности. Многие из этих факторов, безусловно, не имеют монотонной связи с целью. Мы это уже знаем. Например, дорожное движение не увеличивается стабильно по мере увеличения времени в течение дня. А между днями недели это не так! Однако мы не знали, в какой степени праздничный день (is_holiday) и средняя температура (temp) влияли на модель, в особенности в рабочие часы строительной бригады, что было важным пониманием. При этом факторную приоритизацию с помощью индексов Морриса обычно следует трактовать как отправную точку или "первую прикидку", потому что, убедившись в том, что существуют эффекты взаимодействия, лучше всего их распутать. С этой целью существует "вторая прикидка", именуемая фиксированием факторов. Можно квантифицировать дисперсию и, тем самым, неопределенность, привносимую всеми факторами. Только дисперсные методы способны квантифицировать эти эффекты в статистически строгом ключе. Метод анализа чувствительности по методу Соболя является одним из таких методов, а именно, он разлагает выходную дисперсию модели на проценты и связывает ее с входными данными и взаимодействиями модели. Как и в методе Морриса, в нем есть шаг отбора образцов, а также шаг оценивания индекса чувствительности.
402 Часть II. Освоение методов интерпретации В отличие от метода Морриса, отбор образцов подчинен не серии уровней, а распределению входных данных. В нем используется метод квази-Монте-Карло, при котором он отбирает точки в гиперпространстве, подчиняющиеся распределениям значений вероятности данных на входе. Методы Монте-Карло — это семейство алгоритмов, которые выполняют случайный отбор образцов, часто для оптимизации или симуляции. Они стремятся срезать углы в решении задач, которые невозможно было бы решить с помощью грубой силы или полностью детерминированных подходов. Именно по этой причине методы Монте-Карло широко распространены в анализе чувствительности. Методы квази-Монте-Карло преследуют ту же цель. Однако они сходятся быстрее, потому что в них вместо применения псевдослучайной последовательности используется детерминированная последовательность с низким уровнем расхождений. В методе Соболя применяется последовательность Соболя, разработанная тем же математиком. Мы будем использовать еще одну схему отбора образов, выведенную из метода Соболя, именуемую схемой Сальтелли. Произведя образцы, оценщики Монте-Карло вычисляют дисперсные индексы чувствительности. Эти индексы способны квантифицировать нелинейные неаддитивные эффекты и индексы второго порядка, которые связывают с взаимодействием между двумя факторами. Метод Морриса способен выявлять интерактивность в модели, но не точно так, как она проявляется. Метод Соболя может сообщать о том, какие факторы взаимодействуют и в какой степени. Генерирование и предсказывание на образцах Сальтелли Для того чтобы приступить к анализу чувствительности по методу Соболя с помощью библиотеки SALib, сперва необходимо сформулировать задачу. Мы делаем то же самое, что и с методом Морриса. На этот раз мы сократим факторы, потому что поняли, что группировка погоды привела к неубедительным результатам. Необходимо включить наименее редкий из всех погодных факторов, т. е. ясную погоду (weather_Clear). И поскольку в методе Соболя используется вероятностный каркас, нет никакого вреда в расширении границ до их минимальных и максимальных значений для средней температуры (temp), миллиметров осадков в час (rain_1h) и облачности (cloud_coverage), как показано на рис. 9.13: sobol_problem = { 'num_vars': 7, 'names': ['dow', 'hr', 'temp', 'rain_1h', 'cloud_coverage', 'is_holiday',\ 'weather_Clear'], 'bounds': [[0, 4], [-1, 4], # dow # hr [-3., 31.], # temp (C) [0., 11.], # rain_1h [0., 100.], # cloud_coverage [0, 1], # is_holiday
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… [0, 1] 403 # weather_Clear ], 'groups': None } Генерирование образцов тоже должно быть вам знакомым. Функция sample Солтели требует следующего:  постановки задачи (sobol_problem);  числа производимых образцов на фактор (300);  вычисляемых индексов второго порядка (calc_second_order=True). Учитывая, что нам нужны взаимодействия, на выходе из функции sample будет массив, содержащий по одному столбцу для каждого фактора и N   2 F  2  строк (где N — число образцов, F — число факторов). У нас 7 факторов и 300 образцов на фактор, поэтому инструкция print должна напечатать форму из 4800 строк и 7 столбцов. Сперва мы ее модифицируем, как это делали раньше, с помощью функции hstack, чтобы добавить 7 пустых факторов, необходимых для генерирования предсказаний, в результате чего получится 14 столбцов: saltelli_sample = ss.sample(sobol_problem, 300, calc_second_order=True, seed=rand) saltelli_sample_mod = np.hstack(\ (saltelli_sample, np.zeros((saltelli_sample.shape[0],7)))) print(saltelli_sample_mod.shape) Теперь давайте выполним предсказание на этих образцах. Это должно занять некоторое время, так что снова настал момент для чашечки кофе: saltelli_preds = mldatasets.approx_predict_ts(\ saltelli_sample_mod, X_df, gen_all, lstm_traffic_mdl,\ filt_fn=filt_fn, dist_metric=heom_dist.heom, lookback=lookback,\ X_scaler=X_scaler, y_scaler=y_scaler, progress_bar=True) Выполнение анализа чувствительности по методу Соболя Для анализа чувствительности по методу Соболя (analyze) требуются лишь постановка задачи (sobol_problem) и данные на выходе из модели (saltelli_preds). Но предсказания не рассказывают историю неопределенности. Разумеется, в предсказанном дорожном движении есть дисперсия, но это дорожное движение становится проблемой только тогда, когда оно превышает 1500. Неопределенность хочется связать с риском или вознаграждением, расходами или доходами, убытками или прибылью — чем-то осязаемым, что можно связать со своей задачей. Прежде всего, необходимо выполнить диагностику существования вообще какоголибо риска. Для того чтобы понять, имелось ли превышение предсказанным дорожным движением в образцах порога отсутствия строительных работ в рабочие часы, можно применить инструкцию print(max(saltelli_preds[:,0])). Максимальный уровень дорожного движения должен быть где-то в районе 1800–1900, и это означает, что существует, по меньшей мере, некоторый риск того, что строительная компа-
404 Часть II. Освоение методов интерпретации ния заплатит штраф. Вместо того чтобы использовать предсказания (saltelli_preds) в качестве данных на выходе из модели, можно создать простой двоичный массив с единицами, когда оно превысило 1500, и нулем в противном случае. Мы назовем его расходами (costs), а затем выполним с ним функцию analyze. Обратите внимание, что здесь также задан аргумент calc_second_order=True. Благодаря ему будет выдана ошибка, если sample и analyze не имеют сопоставимых параметров. Как и в случае с методом Морриса, имеется необязательный аргумент уровня интервала уверенности (conf_level), но подойдет принятое по умолчанию значение 95: costs = np.where(saltelli_preds > 1500, 1,0)[:,0] factor_fixing_sa = sa.analyze(sobol_problem, costs,\ calc_second_order=True, print_to_console=False) Функция analyse вернет словарь с индексами чувствительности по методу Соболя, включая индексы первого порядка (S1), второго порядка (S2) и общего порядка (ST), а также общие границы уверенности (ST_conf). Индексы соответствуют процентам, но итоговые значения не обязательно будут суммироваться, если только модель не является аддитивной. Легче оценивать эти значения в табличном формате, чтобы иметь возможность поместить их в кадр данных, отсортировать и закодировать их цветом в соответствии с общим значением, которое можно интерпретировать как совокупную важность фактора. Однако мы пропустим индексы второго порядка, потому что они двуразмерные и похожи на график корреляций: sobol_df = pd.DataFrame({'Признаки':sobol_problem['names'],\ '1-го порядка':factor_fixing_sa['S1'],\ 'Общего порядка':factor_fixing_sa['ST'],\ 'Суммарная уверенность':factor_fixing_sa['ST_conf'],\ 'Среднее входных данных':saltelli_sample.mean(axis=0) [0:7]}) sobol_df.sort_values('Итого', ascending=False).\ style.background_gradient(cmap='plasma', subset=['Total']) Приведенный исходный код выводит на экран кадр данных, изображенный на рис. 9.16. Видно, что средняя температура (temp) и праздничный день (is_holiday) входят в верхнюю четверку, по меньшей мере, в пределах, указанных в определении задачи (sobol_problem). Следует отметить еще одну вещь — ясная погода (weather_Clear) имеет более высокий эффект сама по себе, но миллиметры осадков в час (rain_1h) и облачность (cloud_coverage), похоже, не влияют на потенциальные расходы. Нечто интересное в значениях первого порядка заключается в том, насколько они низки, что позволяет предположить, что взаимодействия объясняют бóльшую часть дисперсии результата на выходе из модели. С индексами второго порядка можно легко сгенерировать теплокарту, чтобы это подтвердить. Именно комбинация этих индексов и индексов первого порядка складывается в итоговые значения: sns.heatmap(factor_fixing_sa['S2'], cmap='Blues',\ xticklabels=sobol_problem['names'],\ yticklabels=sobol_problem['names'])
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 405 Рис. 9.16. Индексы глобальной чувствительности по методу Соболя для семи факторов Приведенная строка исходного кода выводит на экран теплокарту (рис. 9.17). Рис. 9.17. Индексы второго порядка по методу Соболя для семи факторов Из приведенного выше следует, что праздничный день (is_holiday) и время суток (hr) — это два фактора, которые вносят наибольший вклад в дисперсию результата на выходе. Фактор времени суток (hr) имеет значительное взаимодействие со всеми факторами, кроме средней температуры (temp), но фактор дня недели (dow) имеет взаимодействие с факторами времени суток (hr) и средней температуры (temp). Встраивание реалистичной функции стоимости Теперь можно создать функцию стоимости, которая берет наши входные (saltelli_sample) и выходные (saltelli_preds) данные и вычисляет величину, на которую города-близнецы будут штрафовать строительную компанию, плюс любые дополнительные расходы, которые могли бы приводить к дополнительному дорож-
406 Часть II. Освоение методов интерпретации ному движению. Лучше сделать это, если входные и выходные данные находятся в одном массиве, потому что для расчета расходов нам понадобятся данные обоих. Можно применить функцию hstack, чтобы соединить образцы и соответствующие им предсказания, создав массив с восемью столбцами (saltelli_sample_preds). Тогда можно определить функцию стоимости, которая будет вычислять расходы (cost_fn) при получении массива с этими восемью столбцами: # Соединить входные и выходные данные в массив образцов + предсказаний saltelli_sample_preds = np.hstack((saltelli_sample, saltelli_preds)) Мы знаем, что порог половинной емкости не был превышен ни для каких выборочных предсказаний, поэтому мы даже не будем утруждать себя включением в функцию ежедневного штрафа. Кроме того, штрафы составляют 15 долларов США за транспортное средство, которое превышает почасовой порог отсутствия строительных работ. В дополнение к этим штрафам, чтобы иметь возможность покинуть площадку вовремя, строительная компания оценивает дополнительные расходы: 1500 долларов дополнительной заработной платы, если порог будет превышен в 4:00, и еще 4500 долларов по пятницам, чтобы ускорить перемещение своего оборудования, потому что оно не может оставаться на обочине шоссе в выходные дни. После того как у нас появится функция стоимости, мы сможем выполнить итеративный обход объединенного массива (saltelli_sample_preds), рассчитав расходы по каждому образцу. Операция включения в список делает это эффективно: # Определение функции стоимости def cost_fn(x): cost = 0 if x[7] > 1500: cost = (x[7] - 1500) * 15 if round(x[1]) == 4: cost = cost + 1500 if round(x[0]) == 4: cost = cost + 4500 return cost # Использовать операцию вклчения в список для расчета # расходов для массива образцов + предсказаний costs2 = np.array([cost_fn(xi) for xi in saltelli_sample_preds]) # Напечатать суммарные штрафы для предсказаний всех образцов print(sum(costs2)) Инструкция print должна напечатать стоимость где-то между 110–130 тыс. долларов. Но не волнуйтесь! Строительная бригада планирует работать на объекте всего около 180 дней в год и по 5 часов в день, в общей сложности 900 часов. Тем не менее существует 4800 образцов, а значит, предсказываемые расходы приходятся более чем на 5 лет из-за избыточного дорожного движения. В любом случае, смысл
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 407 расчета этих расходов состоит в том, чтобы выяснить, как они соотносятся с данными на входе в модель. Чем больше многолетний период, который охватывается отобранными образцами, тем у́же интервалы уверенности. Теперь можно выполнить анализ снова, но с costs2, и сохранить анализ в словаре factor_fixing2_sa. Наконец, можно создать новый отсортированный и кодированный цветом кадр данных со значениями этого словаря, как это делалось раньше для рис. 9.16, который генерирует результат, показанный на рис. 9.18: factor_fixing2_sa = sa.analyze(sobol_problem, costs2,\ calc_second_order=True, print_to_console=False) Хорошо видно, что после того, как были учтены фактические расходы, фактор праздничного дня (is_holiday) становится самым рискованным, а фактор дня недели (dow) также становится более важным, тогда как последние три фактора сохраняют свои позиции рис. 9.16. Рис. 9.18. Индексы глобальной чувствительности по методу Соболя для семи факторов с использованием реалистичной функции стоимости По этой таблице трудно судить об одной вещи, а именно, об интервалах уверенности в отношении индексов чувствительности. Для этого можно использовать гистограмму, но сначала необходимо преобразовать весь словарь в кадр данных, чтобы графопостроительная функция библиотеки SALib смогла ее построить: factor_fixing2_df = factor_fixing2_sa.to_df() fig, (ax) = plt.subplots(1,1, figsize=(15, 7)) sp.plot(factor_fixing2_df[0], ax=ax) Приведенный исходный код генерирует гистограмму на рис. 9.19. 95%-ный интервал уверенности для фактора is_holiday намного больше, чем для других важных факторов, что неудивительно, учитывая, что модель была натренирована с меньшим числом случаев праздников (только 3% дней являются праздничными). Еще один интересный момент заключается в том, как фактор ясной погоды (weather_Clear) оказывает отрицательные первопорядковые эффекты, поэтому положительные индексы общего порядка полностью приписываются индексам второго порядка, которые расширяют интервал уверенности.
408 Часть II. Освоение методов интерпретации Рис. 9.19. Гистограмма с индексами общего порядка чувствительности по методу Соболя и их интервалами уверенности с использованием реалистичной функции стоимости Чтобы понять, как это происходит, давайте снова построим теплокарту, показанную на рис. 9.17, но на этот раз с использованием factor_fixing2_sa вместо factor_fixing_sa. На новой теплокарте должно быть показано, как реалистичные расходы отражают взаимодействия в модели (рис. 9.20). Рис. 9.20. Индексы второго порядка по методу Соболя для семи факторов при учете более реалистичной функции стоимости
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 409 Приведенная теплокарта демонстрирует заметные взаимодействия, аналогичные тем, что отражены на рис. 9.17, но они имеют гораздо больше нюансов, поскольку в них больше оттенков. Становится очевидным, что между временем суток (hr) и средней температурой в градусах Цельсия (temp) существует незначительное взаимодействие, а между днем недели (dow) и временем суток (hr) наблюдаются меньшие эффекты второго порядка, чем между днем недели (dow) и средней температурой (temp). Между тем, время суток (hr) по-прежнему взаимодействует со всеми другими факторами, кроме средней температуры (temp), но эффекты менее заметны между фактором празничного дня (is_holiday) и другими. Миссия выполнена Миссия состояла в том, чтобы натренировать модель предсказывать дорожное движение и понять, собственно какие факторы создают неопределенность и, возможно, увеличивают расходы строительной компании. Можно сделать вывод, что крупная часть потенциальных штрафов в размере 20 тыс. долларов США в год может быть приписана фактору праздничного дня (is_holiday). Поэтому строительной компании следует переосмыслить работу в праздничные дни. Между маем и октябрем всего пять или шесть праздничных дней, и они могут обойтись дороже из-за штрафов, чем работа в несколько воскресений. Конечно, эти выводы относятся к выбранной модели, которую можно сравнить с другими, более качественными моделями. С учетом этой оговорки миссия была успешной, но все еще есть много возможностей для усовершенствований. Например, можно было бы рассмотреть одну вещь подробнее, а именно, истинное влияние средней температуры в градусах Цельсия (temp) и миллиметры осадков в час (rain_1h), а также признаки снега. Наш метод аппроксимации предсказания не позволил методу Соболя проверить влияние экстремальных погодных событий. Если бы мы модифицировали модель для обучения на агрегированных погодных признаках в одиночных временны́х шагах и встроили некие ограждения, то смогли бы сымитировать экстремальные погодные условия с помощью метода Соболя. И "третья прикидка" анализа чувствительности, именуемая соотнесением факторов, поможет точно засечь некоторые значения факторов, которые влияют на предсказанный исход, что приведет к более строгому анализу затрат и выгод, но мы не будем рассматривать эту тему в данной главе. На протяжении части II этой книги мы исследовали экосистему методов интерпретации: глобальной и локальной; модельно-специфических и модельно-агностических; на основе перестановок и на основе чувствительности. В методах интерпретации, которые можно подобрать для любого варианта использования машинного обучения, нет недостатка. Однако следует всегда четко осознавать, что НИ один метод не является совершенным. Тем не менее они могут друг друга дополнять, чтобы вы могли приблизиться к более глубокому пониманию вашего технического решения в области машинного обучения и задачи, которую оно призвано решить.
410 Часть II. Освоение методов интерпретации В этой главе акцент на определенности в прогнозировании был сделан для того, чтобы пролить свет на конкретную задачу в сообществе машинного обучения: чрезмерную уверенность. В разд. "Деловое обоснование интерпретируемости" главы 1 описаны многие предубеждения, которые мешают принятию решений человеком. Эти предубеждения часто подпитываются чрезмерной уверенностью в знаниях предметной области или впечатляющими результатами наших моделей. И эти впечатляющие результаты мешают нам осознавать пределы наших моделей в атмосфере роста общественного недоверия к ИИ. Как мы обсуждали в главе 1, машинное обучение предназначено только для решения неполных задач. В противном случае мы могли бы также использовать детерминированное и процедурное программирование, подобное тому, которое встречается в системах с замкнутым циклом. Неполная задача требует неполного решения, которое должно быть оптимизировано для решения как можно большей ее части. Будь то градиентный спуск, оценивание на основе наименьших квадратов или расщепление и обрезка деревьев решений, машинное обучение не продуцирует модель, которая обобщает идеально. Именно из-за этого дефицита полноты в машинном обучении нам и нужны методы интерпретации. В двух словах: модели усваивают знания из наших данных, и можно многое усвоить из наших моделей, но только если мы их интерпретируем! Однако интерпретируемость на этом не заканчивается. Модельные интерпретации могут руководить решениями и помогать нам понимать сильные и слабые стороны моделей. Однако нередко в самих данных или моделях возникают проблемы, которые могут делать их менее интерпретируемыми. В части III этой книги мы научимся настраивать модели и тренировочные данные на интерпретируемость за счет уменьшения сложности, ослабления систематического смещения, установления ограждений и повышения надежности. Статистик Джордж Э. П. Бокс однажды лихо пошутил, что "все модели неверны, но некоторые полезны". Возможно, они не всегда неверны, но от практиков машинного обучения требуется смирение, чтобы признавать, что даже высокорезультативные модели должны подвергаться тщательному анализу и иметь допущения в них. Неопределенность в моделях машинного обучения является ожидаемой и не должна быть источником стыда или смущения. А это подводит нас к еще одному выводу из этой главы: неопределенность влечет за собой последствия, будь то расходы или повышение прибыли, и можно замерить их с помощью анализа чувствительности. Резюме После прочтения этой главы вы должны понять, как диагностировать предсказательную результативность модели, основанной на временнóм ряде, научиться выполнять для нее локальные интерпретации с интегрированными градиентами, и научиться создавать как локальные, так и глобальные атрибуции с помощью SHAP. Вы также должны научиться задействовать факторную приоритизацию и фиксацию факторов для любой модели в анализе чувствительности.
Глава 9. Методы интерпретации для многопеременного прогнозирования и анализа… 411 В следующей далее главе мы научимся уменьшать модельную сложность и делать модель более интерпретируемой с помощью отбора и конструирования признаков. Источники данных и изображений  TomTom (2019). Индекс дорожного движения: https://www.tomtom.com/en_gb/ traffic-index/ranking/?congestion=WORST,BAD,MODERATE.  Репозиторий машинного обучения UCI (2019). Набор данных об объеме метро- польного дорожного движения между штатами: https://archive.ics.uci.edu/ml/ datasets/Metro+Interstate+Traffic+Volume. Справочные материалы  Wilson D. R., Martinez T. Improved heterogeneous distance functions // J. Artif. Int. Res. — 1997. — Vol. 6 (1). — P. 1–34. — URL: https://arxiv.org/abs/cs/9701101. (Уилсон Д. Р., Мартинес Т. Улучшенные функции гетерогенного расстояния.)  Morris M. Factorial sampling plans for preliminary computational experiments // Quality Engineering. — 1991. — № 37. — P. 307–310. — URL: https://doi.орг/ 10.2307%2F1269043. (Моррис М. Планы факториального отбора образцов для предварительных вычислительных экспериментов.)  Saltelli A., Tarantola S., Campolongo F., Ratto M. Sensitivity analysis in practice: a guide to assessing scientific models. — Chichester: John Wiley & Sons, 2007. (Сальтелли А., Тарантола С., Камполонго Ф., Ратто М. Анализ чувствительности на практике: руководство по диагностике научных моделей.)  Sobol I. M. Global sensitivity indices for nonlinear mathematical models and their Monte Carlo estimates // MATH COMPUT SIMULAT. — 2001. — Vol. 55 (1–3). — P. 271–280. — URL: https://doi.org/10.1016/S0378-4754(00)00270-6. (Соболь И. М. Индексы глобальной чувствительности для нелинейных математических моделей и их оценки методом Монте-Карло.)  Saltelli A., Annoni P., Azzini I., Campolongo F., Ratto M., Tarantola S. Variance based sensitivity analysis of model output. Design and estimator for the total sensitivity index // Computer Physics Communications. — 2010. — Vol. 181 (2). — P. 259– 270. — URL: https://doi.org/10.1016/j.cpc.2009.09.018. (Сальтелли А., Аннони П., Аззини И., Камполонго Ф., Ратто М., Тарантола С. Дисперсный анализ чувствительности результата работы модели. Дизайн и оценщик суммарного индекса чувствительности.)
Часть III Настройка на интерпретируемость Из этой части вы поймете, как уменьшать влияние систематического смещения в наборах данных, и научитесь настраивать модели на интерпретируемость. Эта часть включает следующие главы.  Глава 10. Отбор и конструирование признаков для обеспечения интерпретируе- мости.  Глава 11. Ослабление систематического смещения и причинно-следственный вывод.  Глава 12. Монотонные ограничения и настройка моделей на интерпретируе- мость.  Глава 13. Устойчивость к антагонизму.  Глава 14. Интерпретируемость машинного обучения: что дальше?
10 Отбор и конструирование признаков для обеспечения интерпретируемости В первых трех главах мы обсуждали тему сложности, а также постарались выяснить, как она мешает интерпретируемости машинного обучения. Следует стремиться к компромиссу, т. к. некоторая сложность нужна для максимизации предсказательной результативности, но не до такой степени, чтобы полагаться на модель в достижении абсолютной интерпретируемости: объективности, подотчетности и прозрачности. Эта глава — первая из четырех, посвященных проблеме настройки модели на достижение интерпретируемости. Для улучшения интерпретируемости существует один из самых простых способов — отбор признаков. У него много преимуществ, таких как более быстрое обучение и упрощение модельной интерпретации. Но если эти две причины вас не убедили, возможно, ваше мнение изменит еще одна. Бытует распространенное заблуждение о том, что сложные модели могут отбирать признаки самостоятельно и при этом еще работать хорошо. Тогда зачем вообще беспокоиться об отборе признаков? Да, во многих модельных классах есть механизмы, которые "присматривают" за бесполезными признаками, но они не идеальны. И риск для переобучения возрастает с каждым оставшимся признаком. Переобученные модели ненадежны, даже если они более точны. Таким образом, хотя задействование модельных механизмов, таких как регуляризация, по-прежнему настоятельно рекомендуется во избежание переобучения, первым шагом является отбор признаков. В этой главе вы поймете, каким образом нерелевантные признаки отрицательно влияют на исход модели и, следовательно, уясните важность отбора признаков для обеспечения модельной интерпретируемости. Далее мы рассмотрим фильтрационные методы отбора признаков, такие как корреляция Спирмена, и узнаем о встроенных методах, таких как LASSO и гребневая регрессия. Затем вы откроете для себя оберточные методы, такие как последовательный отбор признаков, и гибридные методы, такие как рекурсивное устранение признаков (recursive feature elimination, RFE), а также более продвинутые, такие как генетические алгоритмы (genetic algorithms, GA). Наконец, несмотря на то что конструирование признаков (или выработку признаков) обычно проводят до их отбора, по многим причинам имеет смысл изучить конструирование признаков после того, как они будут отобраны.
416 Часть III. Настройка на интерпретируемость Вот главные темы, которые будут рассмотрены в этой главе:  влияние нерелевантных признаков;  обзор фильтрационных методов отбора признаков;  анализ встроенных методов отбора признаков;  раскрытие потенциала оберточных, гибридных и расширенных методов отбора признаков;  обзор конструирования признаков. Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, scipy, и seaborn. Исходный код GitHub этой главы находится по адресу: https://github.com/PacktPublishing/InterpretableMachine-Learning-with-Python/tree/master/Chapter10/. mlxtend, genetic_selection, xgboost, sklearn, matplotlib Миссия По оценкам, в мире насчитывается более 10 млн некоммерческих организаций и, хотя значительная их часть финансируется государством, жизнедеятельность большинства из них в основном зависит от частных инвесторов, как корпоративных, так и индивидуальных. В силу этого сбор денежных средств является критически важным и осуществляется в течение всего года. Из года в год доходы от пожертвований растут, но некоммерческие организации сталкиваются c несколькими проблемами: интересы спонсоров эволюционируют, поэтому благотворительность, популярная в один год, может быть забыта в следующем; конкуренция между некоммерческими организациями ужесточается, и демография меняется. В США средний спонсор делает только два благотворительных подарка в год и ему больше 64 лет. Задача выявления потенциальных благотворителей крайне сложна, и маркетинговые кампании по их поиску бывают дорогостоящими. Национальная некоммерческая организация ветеранов имеет большой список рассылки, насчитывающий около 190 000 состоявшихся спонсоров, и хотела бы отправлять специальную почтовую рассылку с просьбой о пожертвованиях. Тем не менее даже с учетом специальной оптовой скидки, это обходится им в 0,68 доллара за адрес. В сумме это составляет более 130 тыс. долларов. Организация располагает маркетинговым бюджетом только в размере 35 тыс. долларов. Учитывая, что рассылка в приоритете, руководство организации готово увеличить бюджет, но только в том случае, если возвратность инвестиций (return on investment, ROI) будет достаточно высокой, чтобы оправдать дополнительные расходы. Для того чтобы минимизировать и без того ограниченный бюджет, вместо массовой рассылки организация хотела бы попробовать прямую рассылку, цель кото-
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 417 рой — выявление потенциальных спонсоров с использованием уже известных данных, таких как прошлые пожертвования, географическое местоположение и демографические данные. Активисты организации также будут связываться с другими пожертвователями по электронной почте, что намного дешевле и стоит не более 1000 долларов в месяц за весь их список. Они надеются, что этот гибридный маркетинговый план даст более высокие результаты. Они также признают, что ценные спонсоры лучше реагируют на персонализированные бумажные почтовые рассылки, тогда как мелкие благотворители в любом случае лучше реагируют на электронную почту. В любой подобной маркетинговой кампании пожертвования не превышают 6% списка рассылки. Использование машинного обучения для предсказания поведения человека отнюдь не простая задача, в особенности, когда оно настолько не сбалансировано. Тем не менее успех измеряется не высочайшей предсказательной точностью, а приростом прибыли. Другими словами, модель прямой рассылки, оцененная на тестовом наборе данных, должна приносить больше прибыли, нежели при массовой рассылке всего набора данных. Активисты организации обратились к вам за помощью, чтобы использовать машинное обучение для построения модели, которая выявляет наиболее вероятных спонсоров, но в таком ключе, который гарантирует возвратность инвестиций (ROI). Обратите внимание, что модель должна быть надежной в генерировании ROI. Из указанной некоммерческой организации вы получили набор данных, который более-менее распределен поровну между обучением и валидацией. Если вы отправите почтовую рассылку абсолютно всем, кто находится в тестовом наборе данных, то получите прибыль в размере 11 173 долларов, но если вам каким-то образом удастся выявить только тех, кто будет вносить пожертвования, то будет достигнута максимальная отдача в размере 73 136 долларов. Ваша цель состоит в том чтобы добиться высокого прироста прибыли и разумной возвратности инвестиций. Когда маркетинговая кампания запустится, она, скорее всего, будет выявлять благотворителей для всего списка рассылки, и в некоммерческой организации надеются потратить в общей сложности немного больше 35 тыс. долларов. Однако набор данных содержит 435 столбцов, и некоторые простые статистические тесты и модельные упражнения показывают, что данные из-за переобучения слишком зашумлены, чтобы можно было выявить надежность потенциальных спонсоров. Подход Вы решили сначала выполнить обучение базовой модели со всеми признаками и продиагностировать ее на разных уровнях сложности, чтобы понять, как наличие большего числа признаков увеличивает склонность к переобучению. Затем вы задействуете ряд методов отбора признаков, начиная от простых фильтрационных методов и заканчивая самыми передовыми, чтобы определить, какой из них достигает целей прибыльности и надежности, к которым стремится клиент. Наконец, после того как будет отобран список окончательных признаков, на этом шаге можно
418 Часть III. Настройка на интерпретируемость рассмотреть возможность конструирования признаков с целью повышения модельной интерпретируемости. Учитывая чувствительную к стоимости природу задачи, пороги важны для оптимизации прироста прибыли. Мы обратимся к роли порогов чуть позже, но один существенный эффект заключается в том, что, хотя эта задача является классификационной, лучше всего использовать регрессионные модели, а затем классифицировать предсказания, отрегулировав только один порог. То есть для классификационных моделей вам потребуется порог для метки, к примеру, для тех, кто пожертвовал более 1 доллара, а затем еще один для предсказанных вероятностей. С другой стороны, регрессия предсказывает пожертвование, и на основе этого можно оптимизировать порог. Подготовительные работы Исходный код этого примера можно найти по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ tree/master/Chapter10/Mailer.ipynb. Загрузка библиотек В целях выполнения этого примера необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas, numpy и scipy для манипулирования данными;  mlxtend, genetic_selection, xgboost и sklearn (scikit-learn) для обучения моделей;  matplotlib и seaborn для наглядных визуализаций и упрощения интерпретирования. Для загрузки библиотек используйте следующий фрагмент исходного кода: import math import os import mldatasets import pandas as pd import numpy as np import timeit from tqdm.notebook import tqdm from sklearn.feature_selection import VarianceThreshold, mutual_info_classif, SelectKBest from sklearn.feature_selection import SelectFromModel from sklearn.linear_model import LogisticRegression, LassoCV, LassoLarsCV, LassoLarsIC from mlxtend.feature_selection import SequentialFeatureSelector from sklearn.feature_selection import RFECV from sklearn.decomposition import PCA import shap from genetic_selection import GeneticSelectionCV
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 419 from scipy.stats import rankdata from sklearn.discriminant_analysis import LinearDiscriminantAnalysis from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor import xgboost as xgb import matplotlib.pyplot as plt import seaborn as sns Далее мы загрузим и подготовим набор данных. Изучение проблемы и подготовка данных Мы загружаем данные таким образом в два кадра данных (X_train, X_test) с признаками и двумя массивами NumPy с соответствующими метками (y_train, y_test). Обратите внимание, что эти кадры данных уже были предварительно подготовлены за нас, в том числе были удалены разреженные или ненужные признаки, обработаны пропущенные значения и закодированы категориальные признаки: X_train, X_test, y_train, y_test =\ mldatasets.load("nonprofit-mailer", prepare=True) y_train = y_train.squeeze() y_test = y_test.squeeze() Все признаки являются числовыми без пропущенных значений, а категориальные признаки уже были закодированы за нас с одним активным состоянием. Между тренировочным и тестовым списками рассылки должно быть более 191 500 записей и 435 признаков. Это можно подтвердить следующим образом: print(X_train.shape) print(y_train.shape) print(X_test.shape) print(y_test.shape) Приведенный выше исходный код должен напечатать следующее: (95485, 435) (95485,) (96017, 435) (96017,) Затем можно проверить, что тестовые метки имеют нужное число жертвователей (test_donators), пожертвований (test_donations) и диапазонов прибыли (test_min_profit, test_max_profit). Их можно распечатать, а затем сделать то же самое для тренировочного набора данных: var_cost = 0.68 y_test_donators = y_test[y_test > 0] test_donators = len(y_test_donators) test_donations = sum(y_test_donators) test_min_profit = test_donations - (len(y_test)*var_cost) test_max_profit = test_donations - (test_donators*var_cost)
420 Часть III. Настройка на интерпретируемость print('Тестовых жертвователей: %s на общую сумму $%.0f' %\ (test_donators, test_donations)) print('(мин.прибыль: $%.0f, макс.прибыль: $%.0f)' %\ (test_min_profit, test_max_profit)) y_train_donators = y_train[y_train > 0] train_donators = len(y_train_donators) train_donations = sum(y_train_donators) train_min_profit = train_donations – (len(y_train)*var_cost) train_max_profit = train_donations – (train_donators*var_cost) print('Тренировочных жертвователей: %s на общую сумму $%.0f' %\ (train_donators, train_donations)) print('(мин. прибыль: $%.0f, макс. прибыль: $%.0f)' %\ (train_min_profit, train_max_profit)) Приведенный выше исходный код должен напечатать следующее: Тренировочных жертвователей: 4894 на общую сумму $76464 (мин. прибыль: $11173, макс. прибыль: $73136) Тестовых жертвователей: 4812 на общую сумму $75113 (мин. прибыль: $10183, макс. прибыль: $71841) Действительно, если бы некоммерческая организация выполнила массовую рассылку всем участникам тестового списка рассылки, то получила бы прибыль в размере около 11 000 долларов, но для этого ей пришлось бы значительно превысить бюджет. Некоммерческая организация признает, что получение максимальной прибыли за счет выявления и ориентации только на жертвователей — почти невозможное достижение сродни подвигу. Поэтому ее активисты были бы довольны в случае создания модели, способной надежно прогнозировать больше минимальной прибыли, но с меньшими затратами, предпочтительно в рамках бюджета. Изучение эффекта нерелевантных признаков Отбор признаков, также именуемый отбором переменных или атрибутов, — это метод, с помощью которого можно автоматически либо вручную выбирать подмножество конкретных признаков, полезных для построения моделей машинного обучения. Большее число признаков не обязательно приводит к более качественным моделям. Нерелевантные признаки могут влиять на процесс усвоения, приводя к переобучению. Поэтому требуются некие стратегии для устранения любых признаков, которые могут негативно повлиять на усвоение. Некоторые преимущества отбора меньшего подмножества признаков позволяют:  легче понимать более простые модели, например, важность признаков для мо- дели, использующей 15 переменных, гораздо легче понять, чем для модели, использующей 150 переменных;
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 421  сократить время обучения — сокращение числа переменных снижает стои- мость вычислений, ускоряет обучение модели, и, возможно, некоторые более простые модели будут быстрее выводить результаты предсказаний;  лучше выполнять обобщение за счет сокращения переобучения — иногда при малом значении предсказания многие переменные являются просто шумом. Однако модель машинного обучения принимает этот шум за полезную информацию и начинает переобучаться, при этом одновременно минимизируя обобщение. Путем устранения этих нерелевантных шумных признаков можно значительно улучшить способность моделей машинного обучения обобщать;  избежать избыточности переменных — обычно наборы данных имеют колли- неарные признаки, а это может означать, что они избыточны. В подобных случаях, при условии, что не будет потеряна существенная информация, можно рассмотреть только одну переменную и удалять другие. Теперь мы выполним обучение нескольких моделей, чтобы продемонстрировать эффект слишком большого числа признаков. Построение базовой модели Давайте создадим базовую модель для нашего набора данных — списка рассылки, чтобы посмотреть, как это происходит. Но сначала зададим случайные числа, чтобы обеспечить воспроизводимость: rand = 9 os.environ['PYTHONHASHSEED']=str(rand) np.random.seed(rand) На протяжении всей этой главы мы будем пользоваться регрессором на основе случайного леса (random forest, RF) пакета XGBoost (XGBRFRegressor). Он похож на соответствующий регрессор библиотеки scikit-learn, но быстрее, т. к. в нем используется аппроксимация второго порядка целевой функции. У него также больше настроечных опций, таких как скорость усвоения и монотонные ограничения, которые мы рассмотрим в главе 12. Мы инициализируем XGBRFRegressor значением максимальной глубины (max_depth), равным 4, и для согласованности всегда используем 200 оценщиков. Затем мы выполняем его подгонку под тренировочные данные. Мы будем использовать функцию timeit для замера занимаемого времени, которое будем хранить в переменной (baseline_time) для последующего использования: stime = timeit.default_timer() reg_mdl = xgb.XGBRFRegressor(max_depth=4, n_estimators=200, seed=rand) fitted_mdl = reg_mdl.fit(X_train, y_train) etime = timeit.default_timer() baseline_time = etime-stime Теперь, когда у нас есть базовая модель, давайте ее оценим.
422 Часть III. Настройка на интерпретируемость Оценивание модели Давайте создадим словарь (reg_mdls) для размещения всех моделей, обучение которых мы выполним в этой главе, чтобы тестировать подмножества признаков, которые порождают наилучшие модели. Здесь можно оценить модель случайного леса со всеми признаками и значением максимальной глубины (max_depth), равным 4 (rf_4_all), используя функцию evaluate_reg_mdl. Она сгенерирует сводку и диаграмму рассеяния с линией регрессии: reg_mdls = {} reg_mdls['rf_4_all'] = mldatasets.evaluate_reg_mdl(fitted_mdl, X_train, X_test,\ y_train, y_test, plot_regplot=True, ret_eval_dict=True) Приведенный исходный код производит метрики и график, показанные на рис. 10.1. Рис. 10.1. Предсказательная результативность базовой модели В графике, подобном тому, который изображен на рис. 10.1, обычно ожидается диагональная линия, и по одному взгляду на этот график можно понять, что модель бесполезна. Кроме того, значения RMSE могут показаться неплохими, но в контек-
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 423 сте такой однобокой задачи они выглядят мрачно. Только подумайте: лишь 5% благотворителей из списка делают пожертвования, и лишь 20% этих пожертвований превышают 20 долларов, поэтому средняя ошибка величиной 4,3−4,6 доллара просто огромна. Так значит, эта модель бесполезна? Ответ заключается в том, какие пороги мы используем для классифицирования с их помощью. Начнем с определения массива пороговых значений (threshs) в диапазоне от 0,40 до 25 долларов. Запускаем процесс и будем продолжать с интервалом в один цент до тех пор, пока не достигнем 1 доллара, далее продолжим с интервалом в 10 центов до тех пор, пока не достигнем 3 долларов, а затем продолжим с интервалом в 1 доллар: threshs = np.hstack([np.linspace(0.40,1,61), np.linspace(1.1,3,20),\ np.linspace(4,25,22)]) В библиотеке mldatasets есть функция, которая может вычислять прибыль при каждом пороге (profits_by_thresh). Ей нужны лишь фактические (y_test) и предсказанные метки, за которыми следуют пороги (threshs), переменные расходы (var_costs) и требуемая минимальная прибыль (min_profit). Она производит кадр данных pandas с доходами, расходами, прибылью и возвратностью инвестиций для каждого порога, если прибыль превышает минимальное значение (min_profit). Помните, что мы установили этот минимум в начале главы в размере 11 173 долларов, т. к. нет смысла ориентироваться на жертвователей меньше этой суммы. После того как мы сгенерируем эти кадры данных о прибыли для тестового и тренировочного наборов данных, мы сможем поместить максимальные и минимальные суммы в словарь модели для последующего использования. А затем задействуем функцию compare_df_plots для построения графика расходов, прибыли и коэффициента возвратности инвестиций для валидации и обучения по каждому порогу, когда он превышал минимум прибыли: y_formatter = plt.FuncFormatter(lambda x, loc: "${:,}K".format(x/1000)) profits_test = mldatasets.profits_by_thresh(y_test,\ reg_mdls['rf_4_all']['preds_test'], threshs, var_costs=var_cost,\ min_profit=test_min_profit) profits_train = mldatasets.profits_by_thresh(y_train,\ reg_mdls['rf_4_all']['preds_train'], threshs,\ var_costs=var_cost, min_profit=train_min_profit) reg_mdls['rf_4_all']['max_profit_train'] = profits_train.profit.max() reg_mdls['rf_4_all']['max_profit_test'] = profits_test.profit.max() reg_mdls['rf_4_all']['max_roi'] = profits_test.roi.max() reg_mdls['rf_4_all']['min_costs'] = profits_test.costs.min() reg_mdls['rf_4_all']['profits_train'] = profits_train reg_mdls['rf_4_all']['profits_test'] = profits_test mldatasets.compare_df_plots(\ profits_test[['costs', 'profit', 'roi']],\ profits_train[['costs', 'profit', 'roi']],\
424 Часть III. Настройка на интерпретируемость 'Тест', 'Тренировка', y_formatter=y_formatter, x_label='Порог',\ plot_args={'secondary_y':'roi'}) Приведенный фрагмент исходного кода генерирует графики на рис. 10.2. По ним хорошо видно, что графики метрик при Валидации и Обучении почти идентичны. Расходы неуклонно снижаются высокими темпами, а прибыль — более низкими темпами, в то время как возвратность инвестиций неуклонно растет. Однако существуют некоторые различия, такие как возвратность инвестиций, которая со временем становится немного выше, и хотя жизнеспособные пороговые значения начинаются в одной и той же точке, обучение все-таки заканчивается на другом значении порога. Оказывается, модель может приносить прибыль, поэтому, несмотря на внешний вид графика на рис. 10.1, модель далеко не бесполезна. Рис. 10.2. Сравнение прибыли (profit), расходов (cost) и возвратности инвестиций (roi (right)) для валидационного и обучающего наборов данных в базовой модели по всем пороговым значениям Разница в значениях RMSE для тренировочного и тестового наборов подтверждает, что модель не переобучилась. Главная причина том, что мы использовали относительно мелкие деревья, установив значение максимальной глубины (max_depth) равным 4. Можно легко заметить этот эффект от использования мелких деревьев, вычислив число признаков, которые имели значение важности (feature_importances_) больше 0: reg_mdls['rf_4_all']['num_feat'] = \ sum(reg_mdls['rf_4_all']['fitted'].feature_importances_> 0) print(reg_mdls['rf_4_all']['num_feat']) Приведенный выше исходный код печатает 160. Другими словами, из 435 использовалось только 160 — только такое число признаков можно разместить в таком мелком дереве! Естественно, это приводит к снижению переобучения, но в то же время вариант выбора признаков с мерами загрязненности (impurity) в случайной подборке признаков не обязательно является наиболее оптимальным.
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 425 Обучение базовой модели на разных максимальных глубинах Что же произойдет, если сделать деревья глубже? Давайте повторим все шаги, которые мы предприняли для мелкого дерева, но теперь с максимальной глубиной от 5 до 12: for depth in tqdm(range(5, 13)): mdlname = 'rf_'+str(depth)+'_all' stime = timeit.default_timer() reg_mdl = xgb.XGBRFRegressor(max_depth=depth, n_estimators=200, seed=rand) fitted_mdl = reg_mdl.fit(X_train, y_train) etime = timeit.default_timer() reg_mdls[mdlname] = mldatasets.evaluate_reg_mdl(fitted_mdl,\ X_train, X_test, y_train, y_test,\ plot_regplot=False, show_summary=False, ret_eval_dict=True) reg_mdls[mdlname]['speed'] = (etime-stime)/baseline_time reg_mdls[mdlname]['depth'] = depth reg_mdls[mdlname]['fs'] = 'all' profits_test = mldatasets.profits_by_thresh(y_test,\ reg_mdls[mdlname]['preds_test'], threshs, var_costs=var_cost,\ min_profit=test_min_profit) profits_train = mldatasets.profits_by_thresh(y_train,\ reg_mdls[mdlname]['preds_train'], threshs, var_costs=var_cost,\ min_profit=train_min_profit) reg_mdls[mdlname]['max_profit_train'] = profits_train.profit.max() reg_mdls[mdlname]['max_profit_test'] = profits_test.profit.max() reg_mdls[mdlname]['max_roi'] = profits_test.roi.max() reg_mdls[mdlname]['min_costs'] = profits_test.costs.min() reg_mdls[mdlname]['profits_train'] = profits_train reg_mdls[mdlname]['profits_test'] = profits_test reg_mdls[mdlname]['total_feat'] = \ reg_mdls[mdlname]['fitted'].feature_importances_.shape[0] reg_mdls[mdlname]['num_feat'] = \ sum(reg_mdls[mdlname]['fitted'].feature_importances_ > 0) Теперь изобразим детали в кадрах данных о прибыли для "самой глубокой" модели (с максимальной глубиной 12), как делали раньше с помощью функции compare_df_plots, и получим рис. 10.3. Посмотрите, насколько на этот раз валидация и обучение отличаются на рис. 10.3. Валидация достигает максимума при 15 тыс. долларов, а обучение превышает 20 тыс. долларов. Расходы на графике обучения резко падают, что делает возвратность инвестиций на порядки выше, чем на графике валидации. Кроме того, диапа-
426 Часть III. Настройка на интерпретируемость зоны порогов сильно различаются. "Почему это является проблемой?" — спросите вы. Если бы нам пришлось угадывать, какой именно порог задать, чтобы выбрать тех, на кого ориентироваться в следующей почтовой рассылке, то оптимум в обучении был бы выше, чем в валидации, а это означает, что при использовании переобученной модели мы могли бы промахнуться и показать более низкую результативность на ранее не встречавшихся данных. Рис. 10.3. Сравнение прибыли (profit), расходов (cost) и возвратности инвестиций (roi (right)) для валидационного и обучающего наборов данных в "глубокой" базовой модели по всем пороговым значениям Давайте конвертируем словарь моделей (reg_mdls) в кадр данных и извлечем из него некоторые детали. Затем можно отсортировать его по глубине, закодировать цветом и вывести на экран: reg_metrics_df = pd.DataFrame.from_dict(reg_mdls, 'index')\ [['depth', 'fs', 'rmse_train', 'rmse_test', 'max_profit_train',\ 'max_profit_test', 'max_roi', 'min_costs', 'speed', 'num_feat']] with pd.option_context('display.precision', 2): html = reg_metrics_df.sort_values(by='depth', ascending=False).style.\ background_gradient(cmap='plasma', low=0.3, high=1,\ subset=['rmse_train', 'rmse_test']).\ background_gradient(cmap='viridis', low=1, high=0.3,\ subset=['max_profit_train', 'max_profit_test'])\ html Приведенный фрагмент исходного кода выводит кадр данных, показанный на рис. 10.4. Сразу видно то, что метрики RMSE на обучающей и валидационныой выборках данных являются взаимно обратными друг другу. Одна из них резко уменьшается, а другая немного увеличивается по мере увеличения глубины. То же самое можно сказать и о прибыли. Возвратность инвестиций, как правило, растет с увеличением глубины и скорости обучения, а также числа используемых признаков.
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 427 Рис. 10.4. Сравнение метрик всех базовых моделей на основе случайного леса с разной глубиной (depth) У вас может возникнуть соблазн использовать модель rf_11_all с максимальной прибылью, но это рискованно! Распространенное заблуждение заключается в том, что модели типа черного ящика могут эффективно устранять любое число нерелевантных признаков. Хотя они нередко и отыскивают нечто ценное и извлекают из этого максимальную пользу, слишком много признаков будет препятствовать ее надежности из-за более легкого переобучения. К счастью, есть золотая середина, позволяющая достичь высокой прибыльности с минимальным переобучением, но для этого вам сначала нужно сократить число признаков! Обзор фильтрационных методов отбора признаков Фильтрационные методы подбирают признаки из набора данных независимо без использования какого-либо машинного обучения. Эти методы зависят только от характеристик переменных и являются относительно эффективными, вычислительно недорогими и быстродействующими. Поскольку они являются легкодоступными методами отбора признаков, их обычно рассматривают в качестве первого шага в любом конвейере отбора признаков. Существует два вида фильтрационных методов.  Однопеременные (univariate): индивидуально и независимо от признакового пространства они оценивают и ранжируют по одному признаку за раз. Проблема с однопеременными методами заключается в том, что они могут отфильтровывать слишком много, поскольку не учитывают взаимосвязь между признаками.  Многопеременные (multivariate): учитывают все признаковое пространство и то, как признаки внутри него взаимодействуют друг с другом. В целом фильтрационные методы очень эффективны в устранении неактуальных, избыточных, постоянных, дублированных и некоррелированных признаков. Однако, не учитывая сложные, нелинейные, немонотонные корреляции и взаимодействия, которые могут отыскиваться только моделями машинного обучения, они не эффективны, когда эти взаимосвязи в данных ярко выражены.
428 Часть III. Настройка на интерпретируемость Мы рассмотрим три категории фильтрационных методов:  базовые;  корреляционные;  ранжирующие. Их более подробное объяснение мы приведем в соответствующих разделах. Базовые фильтрационные методы Мы задействуем базовые фильтрационные методы на шаге подготовки данных, в частности, на шаге очистки данных, перед любым моделированием по той причине что существует низкий риск принятия решений, связанных с отбором признаков, которые повлияют на модели негативно. Они предусматривают общепринятые операции, такие как устранение признаков, которые не несут никакой информации либо ее дублируют. Постоянные признаки с дисперсным порогом Постоянные признаки не изменяются в тренировочном наборе данных и, следовательно, не несут никакой информации, и модель не может усваивать из них знания. Можно использовать однопеременный метод, именуемый дисперсным порогом (VarianceThreshold), который отфильтровывает признаки с низкой дисперсией. Мы зададим нулевой порог, т. к. хотим отфильтровывать признаки только с нулевой дисперсией, другими словами — постоянные. Он работает только с числовыми признаками, поэтому сначала необходимо определить, какие признаки являются числовыми, а какие — категориальными. Применив указанный метод к числовым столбцам, метод get_support() возвращает список признаков, которые не являются постоянными, и затем можно использовать алгебру множеств для возвращения только постоянных признаков (num_const_cols): num_cols_l = X_train.select_dtypes([np.number]).columns cat_cols_l = X_train.select_dtypes([np.bool, np.object]).columns num_const = VarianceThreshold(threshold=0) num_const.fit(X_train[num_cols_l]) num_const_cols = list(set(X_train[num_cols_l].columns) -\ set(num_cols_l[num_const.get_support()])) Приведенный фрагмент исходного кода произвел список постоянных числовых признаков. А что делать с категориальными признаками? Категориальные признаки будут иметь только одну категорию или уникальное значение. Это можно легко проверить, применив функцию nunique() к категориальным признакам. Она вернет ряд (Series) pandas, и затем лямбда-функция сможет отфильтровать только те признаки, у которых есть одно уникальное значение. После этого метод .index.tolist() возвращает имена признаков в виде списка. Теперь вы просто соединяете оба спи-
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 429 ска постоянных признаков, и вуаля! У вас есть все константы (all_const_cols). Их можно распечатать; должно быть всего три: cat_const_cols = X_train[cat_cols_l].nunique()[lambda x: x<2].index.tolist() all_const_cols = num_const_cols + cat_const_cols print(all_const_cols) В большинстве случаев устранения постоянных признаков недостаточно. Избыточный признак может быть почти постоянным или квази-постоянным. Квазипостоянные признаки с количеством значений Квазипостоянные признаки имеют почти полностью одинаковые значения. В отличие от фильтрации постоянных признаков, дисперсный порог не будет работать, т. к. высокая дисперсия и квазипостоянство не являются взаимоисключающими. Вместо этого мы будем прокручивать все признаки в цикле и получать численность, применяя функцию value_counts(), которая возвращает число строк по каждому значению. Затем делит эти числа на суммарное число строк, чтобы получить процент, и сортирует в возрастающем порядке процента. Если верхнее значение превышает заданный порог (thresh), мы добавляем его в список квазипостоянных (quasi_const_cols). Обратите внимание, что выбирать этот порог следует с особой осторожностью и пониманием задачи. Например, в данном случае мы знаем, что это однобоко, т. к. пожертвования оказывают только 5% клиентов, большинство из которых жертвуют небольшую сумму, а значит, влияние может оказывать даже крошечный процент признака, и поэтому наш порог является столь высоким — 99,9%: thresh = 0.999 quasi_const_cols = [] num_rows = X_train.shape[0] for col in tqdm(X_train.columns): top_val = (X_train[col].value_counts() / num_rows).\ sort_values(ascending=False).values[0] if top_val >= thresh: quasi_const_cols.append(col) print(quasi_const_cols) Приведенный исходный код должен напечатать пять признаков, включая три, которые были получены ранее. Далее мы рассмотрим еще одну форму нерелевантных признаков: дубликаты! Дублирующие признаки Обычно при обсуждении темы дубликатов в данных первое, что приходит в голову, — это повторяющиеся строки, но повторяющиеся столбцы тоже создают проблемы. Их можно найти так же, как отыскиваются повторяющиеся строки, — с по-
430 Часть III. Настройка на интерпретируемость мощью функции duplicated() библиотеки pandas, за исключением того, что кадр данных сначала транспонируют, т. е. конвертируют его столбцы в строки: X_train_transposed = X_train.T dup_cols = X_train_transposed[X_train_transposed.duplicated()].index.tolist() print(dup_cols) Приведенный фрагмент исходного кода выводит на экран список с двумя дублированными строками. Устранение ненужных признаков В отличие от других селекционных методов, которые необходимо провалидировать на моделях, можно сразу применить базовые фильтрационные методы отбора признаков, устранив признаки, которые вы сочли бесполезными. Но на всякий случай рекомендуется сделать копию исходных данных. Обратите внимание, что мы не включаем постоянные столбцы (all_constant_cols) в столбцы, которые необходимо удалить (drop_cols), т. к. они уже содержаться в квазипостоянных столбцах: X_train_orig = X_train.copy() X_test_orig = X_test.copy() drop_cols = quasi_const_cols + dup_cols X_train.drop(labels=drop_cols, axis=1, inplace=True) X_test.drop(labels=drop_ Далее мы рассмотрим многопеременные фильтрационные методы на оставшихся признаках. Корреляционные фильтрационные методы Корреляционные фильтрационные методы квантифицируют силу взаимосвязи между двумя признаками. Это дает определенную выгоду для отбора признаков, т. к. может возникать потребность в отфильтровке чрезвычайно коррелированных признаков или тех, которые вообще не коррелируют с другими. В любом случае этот метод отбора признаков является многопеременным — двупеременным, если быть точным. Но сначала необходимо выбрать корреляционный метод.  Коэффициент корреляции Пирсона измеряет величину, с которой два призна- ка линейно коррелируют между 1 (отрицательно) и 1 (положительно), при этом 0 означает отсутствие линейной корреляции. Как и линейная регрессия, он исходит из допущений о линейности, нормальности и гомоскедастичности.  Коэффициент ранговой корреляции Спирмена измеряет силу монотонности двух признаков независимо от наличия либо отсутствия линейной связи между ними. Он тоже измеряется в интервале между 1 и 1, при этом 0 означает отсутствие монотонной корреляции. Он не исходит из допущений о распределении и может работать как с непрерывными, так и с дискретными признаками. Однако его слабость относится к немонотонным связям.
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 431  Коэффициент корреляции тау Кендалла измеряет порядковую ассоциацию между признаками. Он также варьирует в интервале между 1 и 1, но эти пределы означают, соответственно, низкую и высокую ассоциацию. Коэффициент полезен при работе с дискретными признаками. Набор данных представляет собой смесь непрерывных и дискретных величин, и мы не можем использовать там никаких линейных допущений, поэтому аргумент method='spearman' является правильным вариантом выбора. Вместе с тем все три корреляционных метода можно использовать с функцией corr библиотеки pandas: corrs = X_train.corr(method='spearman') print(corrs.shape) Приведенный исходный код должен напечатать размер матрицы корреляций, т. е. (428, 428). Этот вывод имеет смысл, т. к. осталось 428 признаков, и каждый признак имеет связь с 428 признаками, включая самого себя. Теперь можно выполнить поиск удаляемых признаков в матрице корреляций (corr). Обратите внимание, что для этого необходимо задать пороги. Например, можно сказать, что чрезвычайно коррелированный признак имеет абсолютный коэффициент более 0,99 и некоррелированный признак — менее 0,15. С учетом этих порогов можно найти признаки, которые коррелируют только с одним признаком и чрезвычайно коррелируют более чем с одним признаком. Почему именно один признак? Потому что диагонали в матрице корреляций всегда равны 1, т. к. признак всегда идеально коррелирует сам с собой. Лямбда-функции в следующем исходном коде обеспечивают учет этого условия: extcorr_cols = (abs(corrs) > 0.99).sum(axis=1)[lambda x: x>1].index.tolist() print(extcorr_cols) uncorr_cols = (abs(corrs) > 0.15).sum(axis=1)[lambda x: x==1].index.tolist() print(uncorr_cols) Приведенный исходный код напечатает два списка: ['MAJOR', 'HHAGE1', 'HHAGE3', 'HHN3', 'HHP1', 'HV1', 'HV2', 'MDMAUD_R', 'MDMAUD_F', 'MDMAUD_A'] ['TCODE', 'MAILCODE', 'NOEXCH', 'CHILD03', 'CHILD07', 'CHILD12', 'CHILD18', 'HC15', 'MAXADATE'] Первый список — это один из признаков, который чрезвычайно коррелирует с другими, отличными от него самого. Вместе с тем полезно знать, что не следует удалять признаки из этого списка, не понимая, с какими признаками они связаны и как именно, а также с целевой переменной. Затем, только если обнаружена избыточность, следует удалить лишь один из них. Второй список — это некоррелированные признаки с любыми другими, кроме них самих, что в данном случае подозрительно, учитывая огромное число признаков. При этом также следует рассмотреть их один за другим, в особенности для того, чтобы измерить их относительно целевой переменной, чтобы увидеть их избыточность. Однако мы рискнем и создадим подмножество признаков (corr_cols), исключающее некоррелированные: corr_cols = X_train.columns[~X_train.columns.isin(uncorr_cols)].tolist() print(len(corr_cols))
432 Часть III. Настройка на интерпретируемость Приведенный исходный код должен напечатать 419. Теперь выполним обучение модели случайного леса только с этими признаками. Учитывая, что все еще существует более 400 признаков, мы будем использовать значение максимальной глубины (max_depth), равное 11. За исключением ее и другого имени модели (mdlname), исходный код тот же, что и раньше: mdlname = 'rf_11_f-corr' stime = timeit.default_timer() reg_mdl = xgb.XGBRFRegressor(max_depth=11, n_estimators=200, seed=rand) fitted_mdl = reg_mdl.fit(X_train[corr_cols], y_train) : reg_mdls[mdlname]['num_feat'] = \ sum(reg_mdls[mdlname]['fitted'].feature_importances_> 0) Прежде чем сравнивать результаты приведенной выше модели, давайте познакомимся с ранжирующими фильтрационными методами. Ранжирующие фильтрационные методы Ранжирующие фильтрационные методы основаны на статистических тестах с использованием рангов однопеременных признаков. В этих тестах оценивают силу признаков по отношению к цели. Вот несколько самых популярных методов.  Тест ANOVA (ANalysis OF VAriance, анализ дисперсии) на основе F-стати- стики измеряет линейную зависимость между признаками и целью. Как следует из названия, он делает это путем разложения дисперсии. Он принимает допущения, схожие с линейной регрессией, такие как нормальность, независимость и гомоскедастичность. В библиотеке scikit-learn можно использовать f_regression и f_classification, соответственно, в регрессии и классификации для ранжирования признаков по F-баллу, полученному с помощью F-теста.  Тест независимости на основе статистики хи-квадрат измеряет взаимосвязь между неотрицательными категориальными переменными и двоичными целями, поэтому он подходит только для классификационных задач. В библиотеке scikitlearn можно использовать chi2.  Взаимная информация (mutual information, MI). В отличие от двух предыду- щих методов, этот метод основан не на классической статистической проверке гипотез, а на теории информации. Он имеет другое название, но его концепцию мы уже обсуждали в этой книге, как дивергенцию Кульбака — Лейблера (Kullback — Leibler, KL), потому что он представляет собой дивергенцию Кульбака — Лейблера для признака X и цели Y. В имплементации на Python в библиотеке scikit-learn применяют численно стабильное и симметричное ответвление дивергенции Кульбака — Лейблера, именуемое дивергенцией Дженсена — Шеннона (Jensen — Shannon, JS), и для вычисления расстояний используют k ближайших соседей. Признаки ранжируют по взаимной информации с помощью mutual_info_regression и mutual_info_classif, соответственно, в регрессии и классификации.
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 433 Из трех упомянутых вариантов наиболее подходящий для нашего набора данных — взаимная информация, т. к. мы не можем исходить из допущения о линейности признаков, и большинство из них также не являются категориальными. Можно попробовать классификацию с порогом 0,68 доллара, что, по меньшей мере, покрывает стоимость выполнения почтовой рассылки. С этой целью сначала необходимо создать цель двоичной классификации (y_train_class) с указанным порогом: y_train_class = np.where(y_train > 0.68, 1, 0) Далее можно применить SelectKBest, чтобы получить верхние 160 признаков в соответствии с классификацией на основе взаимной информации (MI classification, MIC). Затем мы используем метод get_support(), чтобы получить булев вектор (или маску), который сообщает о том, какие признаки входят в список верхних 160, и с помощью этой маски берем подмножество указанного списка признаков: mic_selection = SelectKBest(mutual_info_classif, k=160).\ fit(X_train, y_train_class) mic_cols = X_train.columns[mic_selection.get_support()].tolist() print(len(mic_cols)) Приведенный исходный код должен подтвердить, что в списке mic_cols имеется 160 признаков. Кстати, это произвольное число. В идеале, если бы было время, мы могли бы поэкспериментировать с разныеми порогами для целевой переменной классификации и значения k для взаимной информации в поисках модели, которая достигла наибольшего прироста прибыли при наименьшем переобучении. Далее можно выполнить обучение модели случайного леса, как это делалось раньше, с признаками MIC. На этот раз мы будем использовать максимальную глубину 5, потому что признаков значительно меньше: mdlname = 'rf_5_f-mic' stime = timeit.default_timer() reg_mdl = xgb.XGBRFRegressor(max_depth=5, n_estimators=200, seed=rand) fitted_mdl = reg_mdl.fit(X_train[mic_cols], y_train) : reg_mdls[mdlname]['num_feat'] = \ sum(reg_mdls[mdlname]['fitted'].feature_importances_> 0) Теперь построим график прибыли для валидации и обучения, как это делалось на рис. 10.3, но для модели MIC. Будет сгенерирован рис. 10.5. На рис. 10.5 хорошо видно, что существует довольно большая разница между валидацией и обучением, но сходство указывает на минимальное переобучение. Например, самая высокая прибыльность находится в интервале между 0,65 и 0,7 для обучения, и хотя валидация в основном находится в интервале между 0,65 и 0,7, впоследствии величина метрик постепенно снижается. Хотя мы визуально проинспектировали модель MIC, приятно стать более уверенным, посмотрев на сырые метрики. Далее мы сравним все модели, которые натренировали до этого, используя сопоставимые метрики.
434 Часть III. Настройка на интерпретируемость Рис. 10.5. Сравнение прибыли (profit), расходов (coast) и возвратности инвестиций (roi (rigth)) для валидационного и обучающего наборов данных в модели с признаками MIC по всем пороговым значениям Сравнение фильтрационных методов Мы сохраняем показатели в словаре (reg_mdls), который легко конвертируем в кадр данных и выводим на экран, как это делалось раньше, но на этот раз мы сортируем по max_profit_test: reg_metrics_df = pd.DataFrame.from_dict(reg_mdls, 'index')[['depth', 'fs',\ 'rmse_train', 'rmse_test', 'max_profit_train', 'max_profit_test',\ 'max_roi', 'min_costs', 'speed', 'num_feat']] with pd.option_context('display.precision', 2): html = reg_metrics_df.sort_values(by='max_profit_test', ascending=False).\ style.background_gradient(cmap='plasma', low=0.3, high=1,\ subset=['rmse_train', 'rmse_test']). background_gradient(cmap='viridis', low=1, high=0.3,\ subset=['max_profit_train', 'max_profit_test']) html Приведенный фрагмент исходного кода сгенерировал то, что показано на рис. 10.6. Очевидно, что фильтрационная модель MIC наименее переобучена из всех. Она присвоила признакам ранги более высокие, нежели более сложные модели с бóльшим числом признаков, и на ее обучение ушло меньше времени, чем у любой другой модели. Ее быстродействие благоприятно сказывается на настройке гиперпараметров. Что произойдет, если мы захотим отыскать наилучшие пороги целевой переменной классификации или значения k? Не будем делать этого сейчас. Мы, вероятно, могли бы получить более совершенную модель, если бы выполнили каждую комбинацию, правда, для этого потребуется время и тем больше, чем больше будет число признаков.
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 435 Рис. 10.6. Сравнение метрик всех базовых моделей и фильтрационных моделей, построенных с применением отбора признаков На рис. 10.6 хорошо видно, что корреляционная фильтрационная модель (f-corr) работает хуже, чем модель с бóльшим числом признаков и равной величиной максимальной глубины (max_depth), говоря о том, что мы, должно быть, удалили важный признак. Как мы предупреждали в этом разделе, проблема со слепой установкой порогов и удалением всего, что выше, заключается в том, что существует возможность непреднамеренно удалить что-то полезное. Не все чрезвычайно коррелированные и некоррелированные признаки бесполезны, поэтому требуется дальнейшее исследование. Далее мы рассмотрим несколько встроенных методов, которые в сочетании с кросс-валидацией уменьшают недочеты. Анализ встроенных методов отбора признаков Встроенные методы существуют внутри самих моделей, отбирая признаки естественным образом во время обучения. Для улавливания отбираемых признаков можно задействовать внутренние свойства любой модели, в которой они есть.  Древовидные модели. Мы много раз применяли следующий ниже исходный код для подсчета числа признаков, используемых в моделях на основе случайного леса, что свидетельствует об отборе признаков, естественно происходящем в процессе усвоения: sum(reg_mdls[mdlname]['fitted'].feature_importances_ > 0) В случайном лесе XGBoost по умолчанию используется вклад (gain), т. е. среднее уменьшение в ошибке во всех разбивках (расщеплениях), где он брал признак для вычисления его важности. Можно увеличивать порог выше 0, чтобы отбирать еще меньше признаков в соответствии с этим относительным вкладом. Однако, ограничивая глубину деревьев, мы уже вынудили модель выбирать еще меньше признаков.  Регуляризованные модели с коэффициентами. Мы изучим их подробнее в главе 12, но многие модельные классы могут встраивать штрафную регуляризацию, такую как L1, L2 и эластичная сеть. Однако не все из них имеют внутренние параметры, такие как коэффициенты, которые можно извлекать, чтобы выявлять оштрафованные признаки.
436 Часть III. Настройка на интерпретируемость В этом разделе будут рассмотрены только регуляризованные модели, учитывая, что мы уже используем древовидную модель. Лучше всего применять разные модельные классы, чтобы с разных точек зрения оценить признаки, которые имеют наибольшую важность. Мы охватили несколько таких моделей в главе 3, однако ниже перечислены некоторые модельные классы, которые встраивают штрафную регуляризацию и выдают признаково-специфичные коэффициенты.  Оператор наименьшего абсолютного сжатия и отбора (least absolute shrinkage and selection operator, LASSO): поскольку метод LASSO в функции потерь использует штраф L1, он может устаналивать коэффициенты равными 0.  Регрессия с наименьшим углом (least-angle regression, LARS) аналогична LASSO, но основана на векторах и больше подходит для высокоразмерных данных. Она также относится объективнее к одинаково коррелированным признакам.  Гребневая регрессия в функции потерь использует штраф L2 и из-за этого может сжимать близко к 0, но не до 0, только нерелевантные коэффициенты.  Регрессия на основе эластичной сети в качестве штрафов использует сочетание норм L1 и L2.  Логистическая регрессия в зависимости от решателя может манипулировать L1-, L2- или эластично-сетевыми штрафами. Кроме того, существует несколько вариаций упомянутых выше моделей, такие как LASSO LARS, т. е. LASSO-подгонка с использованием алгоритма LARS, или даже LASSO LARS IC, т. е. то же самое, но с использованием критериев AIC или BIC для отбора модели:  информационный критерий Акаике (Akaike’s information criteria, AIC) — мера относительного качества подгонки, основанная на теории информации;  байесов информационный критерий (Bayesian information criteria, BIC) — критерий, имеющий формулу, аналогичную AIC, но другой штрафной член. Хорошо, теперь давайте воспользуемся SelectFromModel для извлечения верхних признаков из модели LASSO. Мы будем использовать объект LassoCV, потому что он способен автоматически выполнять кросс-валидацию, чтобы отыскивать оптимальную силу штрафа. После выполнения его подгонки мы получаем признаковую маску, применяя для этого метод get_support(). Затем можно распечатать число и список признаков: lasso_selection = SelectFromModel(LassoCV(n_jobs=-1, random_state=rand)) lasso_selection.fit(X_train, y_train) lasso_cols = X_train.columns[lasso_selection.get_support()].tolist() print(len(lasso_cols)) print(lasso_cols) Приведенный выше исходный код напечатает следующее: 7 ['ODATEDW', 'TCODE', 'POP901', 'POP902', 'HV2', 'RAMNTALL', 'MAXRDATE']
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 437 Теперь попробуем то же самое, но с LassoLarsCV: llars_selection = SelectFromModel(LassoLarsCV(n_jobs=-1)) llars_selection.fit(X_train, y_train) llars_cols = X_train.columns[llars_selection.get_support()].tolist() print(len(llars_cols)) print(llars_cols) Приведенный фрагмент исходного кода печатает следующий результат: 8 ['RECPGVG', 'MDMAUD', 'HVP3', 'RAMNTALL', 'LASTGIFT', 'AVGGIFT', 'MDMAUD_A', 'DOMAIN_SOCIALCLS'] LASSO сжал коэффициенты всех признаков до 0, кроме семи, а LASSO LARS сделал то же самое, но для восьми. Однако обратите внимание, что между обоими списками нет совпадений! Хорошо, теперь, давайте попробуем встроить отбор модели AIC в LASSO LARS с помощью LassoLarsIC: llarsic_selection = SelectFromModel(LassoLarsIC(criterion='aic')) llarsic_selection.fit(X_train, y_train) llarsic_cols = X_train.columns[llarsic_selection.get_support()].tolist() print(len(llarsic_cols)) print(llarsic_cols) Приведенный фрагмент исходного кода печатает следующий результат: 111 ['TCODE', 'STATE', 'MAILCODE', 'RECINHSE', 'RECP3', 'RECPGVG', 'RECSWEEP', ..., 'DOMAIN_URBANICITY', 'DOMAIN_SOCIALCLS', 'ZIP_LON'] Это тот же алгоритм, но с другим методом отбора значения регуляризационного параметра. Обратите внимание, как этот менее консервативный подход расширяет число признаков до 111. Сейчас все использованные до этого методы имеют норму L1. Давайте попробуем один из них с L2 — точнее, с логистической регрессией, штрафуемой нормой L2. Мы делаем в точности так же, как и раньше, но на этот раз выполняем обучение с двоично-классификационными целями (y_train_class): log_selection = SelectFromModel(LogisticRegression(\ C=0.0001, solver='sag', penalty='l2', n_jobs=-1, random_state=rand)) log_selection.fit(X_train, y_train_class) log_cols = X_train.columns[log_selection.get_support()].tolist() print(len(log_cols)) print(log_cols) Приведенный выше исходный код печатает следующий результат: 87 ['ODATEDW', 'TCODE', 'STATE', 'POP901', 'POP902', 'POP903', 'ETH1', 'ETH2', 'ETH5', 'CHIL1', 'HHN2',... , 'AMT_7', 'ZIP_LON']
438 Часть III. Настройка на интерпретируемость Теперь, когда для валидации у нас есть несколько подмножеств признаков, можно поместить их имена в список (fsnames), а списки подмножеств признаков в еще один список (fscols): fsnames = ['e-lasso', 'e-llars', 'e-llarsic', 'e-logl2'] fscols = [lasso_cols, llars_cols, llarsic_cols, log_cols] Затем можно прокрутить все имена списков в цикле, выполнить обучение и оценить модель XGBRFRegressor, как это делалось раньше, но на каждой итерации увеличивая максимальную глубину (max_depth): for i, fsname in tqdm(enumerate(fsnames), total=len(fsnames)): depth = i + 3 cols = fscols[i] mdlname = 'rf_'+str(depth)+'_'+fsname stime = timeit.default_timer() reg_mdl = xgb.XGBRFRegressor(max_depth=depth, n_estimators=200, seed=rand) fitted_mdl = reg_mdl.fit(X_train[cols], y_train) : reg_mdls[mdlname]['num_feat'] = \ sum(reg_mdls[mdlname]['fitted'].feature_importances_ > 0) Теперь давайте посмотрим, как встроенные модели с применением отбора признаков смотрятся по сравнению с отфильтрованными. Повторно выполним исходный код, который мы выполняли, чтобы вывести на экран то, что было показано на рис. 10.6. На этот раз мы получим сводку, представленную на рис. 10.7. Рис. 10.7. Сравнение метрик всех базовых моделей, фильтрационных и встроенных моделей, построенных с применением отбора признаков Согласно рис. 10.7, три из четырех опробованных нами встроенных методов породили модели с самым низкой тестовой RMSE. Кроме того, все они тренируются намного быстрее, чем любая другая модель, и являются более прибыльными, чем
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 439 любая другая модель равной сложности. Одна из них (rf_5_e-llarsic) — даже очень прибыльная. Сравните ее с rf_9_all с аналогичной тестовой прибыльностью и увидите, как ее результативность расходится на тренировочных данных. Раскрытие потенциала оберточных, гибридных и продвинутых методов отбора признаков В этом разделе мы узнаем о других, более исчерпывающих, методах с многочисленными возможными вариантов настройки. Вот они:  оберточный — исчерпывающий поиск наилучшего подмножества признаков путем подгонки модели машинного обучения с использованием поисковой стратегии, которая измеряет улучшение на метрике;  гибридный — метод, который сочетает встроенные методы и фильтрационные методы с оберточными методами;  продвинутый — метод, который не подпадает ни под одну из ранее обсуждав- шихся категорий. Примеры включают редукцию размерности, модельно-агностическое определение важности признаков и генетические алгоритмы. А теперь давайте начнем с оберточных методов! Оберточные методы Концепция, лежащая в основе оберточных методов, достаточно проста: оценить разные подмножества признаков в модели машинного обучения и выбрать то, которое достигает наилучшего балла в заданной целевой функции. Здесь меняется только стратегия поиска.  Последовательный прямой отбор (sequential forward selection, SFS). Этот под- ход начинается без признака и добавляет по одному признаку за раз.  Последовательный прямой плавающий отбор (sequential forward floating selection, SFFS). Такой же, как и предыдущий, за исключением каждого добавляемого признака — метод может удалять признак при условии, что целевая функция увеличивается.  Последовательный обратный отбор (sequential backward selection, SBS). Этот процесс начинается со всех имеющихся признаков и устраняет по одному признаку за раз.  Последовательный плавающий обратный отбор (sequential floating backward selection, SFBS). Такой же, как и предыдущий, за исключением каждого удаляемого признака — он может добавлять признак при условии, что целевая функция увеличивается.  Исчерпывающий отбор признаков (exhaustive feature selection, EFS). Этот подход проводит поиск по всем возможным комбинациям признаков.
440 Часть III. Настройка на интерпретируемость  Двунаправленный поиск (bidirectional search, BDS). Этот последний подход позволяет отбирать признаки одновременно как в прямом, так и в обратном направлении, получая одно уникальное решение. Указанные методы являются жадными алгоритмами, т. к. они решают задачу по частям, выбирая части на основе их непосредственной выгоды. Несмотря на то что они могут достигать глобального максимума, в них используется подход, более пригодный для отыскания максимумов локальных. В зависимости от числа признаков они могут обходиться вычислительно слишком дорого, чтобы обеспечивать практическую пользу; особенно это касается метода исчерпывающего отбора признаков (EFS), который растет комбинаторно. В целях сокращения времени поиска мы сделаем две вещи: 1. Начнем поиск с признаков, коллективно отбираемых другими методами, чтобы выбирать из меньшего признакового пространства. С этой целью мы объединим списки признаков из нескольких методов в один список top_cols: top_cols = list(set(mic_cols).union(set(llarsic_cols)).union(set(log_cols))) len(top_cols) 2. Будем брать выборки из наборов данных в целях ускорения работы моделей машинного обучения. Можно использовать функцию np.random.choice для случайного отбора индексов строк без возврата: sample_size = 0.1 sample_train_idx = np.random.choice(\ X_train.shape[0], math.ceil(X_train.shape[0]*sample_size), replace=False) sample_test_idx = np.random.choice(\ X_test.shape[0], math.ceil(X_test.shape[0]*sample_size), replace=False) Из представленных оберточных методов будем выполнять только последовательный прямой отбор (SFS) и последовательный обратный отбор (SBS), учитывая их большую времязатратность. Тем не менее с еще меньшим набором данных можно попробовать другие варианты, которые также поддерживаются библиотекой mlextend. Последовательный прямой отбор (SFS) Первым аргументом оберточного метода является неподогнанный оценщик (модель). В SequentialFeatureSelector мы размещаем модель линейного дискриминантного анализа (LDA), LinearDiscriminantAnalysis. Другие аргументы: направление (forward=true); указание, является ли признак плавающим (floating=False); число признаков, которые мы хотим отобрать (k_features=27); число кросс-валидаций (cv=3); используемая функция потерь (scoring=f1). Можно ввести несколько рекомендуемых необязательных аргументов — детализацию (verbose=2) и число заданий, выполняемых в параллельном режиме (n_jobs=-1). Поскольку его выполнение может занимать немало времени, вы определенно захотите, чтобы он что-то выводил на экран и использовал как можно больше процессоров: sfs_lda = SequentialFeatureSelector(LinearDiscriminantAnalysis(n_components=1),\ forward=True, floating=False, k_features=27, cv=3,\
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 441 scoring='f1', verbose=2, n_jobs=-1) sfs_lda = sfs_lda.fit(X_train.iloc[sample_train_idx][top_cols],\ y_train_class[sample_train_idx]) sfs_lda_cols = X_train.columns[list(sfs_lda.k_feature_idx_)].tolist() После подгонки SFS, модель вернет индекс признаков, которые были отобраны с помощью k_feature_idx_, и затем их можно использовать для взятия подмножества столбцов и получения списка имен признаков. Последовательный обратный отбор (SBS) В методе последовательного обратного отбора мы будем использовать ExtraTreesRegressor, который контролирует переобучение, тренируя чрезвычайно рандомизированные деревья решений на подвыборках набора данных. Из-за своей дикой природы он может отыскивать подмножества признаков, которых не будет в такой модели, как линейный дискриминантный анализ (LDA): sbs_et = SequentialFeatureSelector(\ ExtraTreesRegressor(max_depth=3, random_state=rand),\ floating=False, k_features=135, forward=False, cv=2,\ scoring='neg_root_mean_squared_error', verbose=2, n_jobs=-1) sbs_et = sbs_et.fit(X_train.iloc[sample_train_idx][top_cols],\ y_train[sample_train_idx]) sbs_et_cols = X_train.columns[list(sbs_et.k_feature_idx_)].tolist() После подгонки SBS мы сделаем то же самое, что и раньше, сохранив отобранные признаки. В типичной ситуации оберточные методы демонстрируют высокую эффективность в отыскании подмножеств признаков, которые уменьшают переобучение и повышают предсказательную результативность, поскольку они обнаруживают взаимодействия важных признаков, обнаруживать которые не способны фильтрационные методы. Их главное ограничение заключалось в том, что нам приходилось брать выборки из тренировочных данных, чтобы делать их пригодными для данного варианта использования. Гибридные методы Если начинать с 435 признаков, то будет более 1042 комбинаций только 27 подмножеств признаков! Так что, вы видите, насколько модель на основе исчерпывающего отбора признаков (EFS) была бы непрактичной на таком большом признаковом пространстве. Поэтому, за исключением EFS на всем наборе данных, оберточные методы для отбора признаков неизменно будут использовать некоторые сокращения. Независимо от направления, прямого, обратного либо того и другого, пока вы не проведете диагностику каждой отдельной комбинации признаков, легко можно упустить лучшую из них.
442 Часть III. Настройка на интерпретируемость Однако можно применить более строгий, исчерпывающий подход с использованием оберточных методов с фильтрационными и встроенными методами. Результатом такой комбинации являются гибридные методы. Например, можно задействовать фильтрационный или встроенный методы для выведения только 10 верхних признаков и выполнения исчерпывающего отбора признаков (EFS) или последовательного обратного отбора (SBS) только на них. Рекурсивное устранение признаков (RFE) Еще один, более распространенный подход в чем-то похож на последовательный обратный отбор (SBS), но вместо устранения признаков с целью лишь улучшения метрики задействуются внутренние модельные параметры, используемые для ранжирования признаков и устранения только тех, которые ранжированы ниже всего. Этот подход называется рекурсивным устранением признаков (recursive feature elimination, RFE) и является гибридом встроенного и оберточного методов. Использовать модели можно только с важностями признаков (feature_importances_) или коэффициентами (coef_), потому что именно так метод узнает о том, какие функции следует устранять. Модельные классы в библиотеке scikit-learn с этими атрибутами делятся на линейные (linear_model), древовидные (tree) и ансамблевые (ensemple) модели. Кроме того, совместимые с scikit-learn версии XGBoost: LightGBM и CatBoost, — также имеют переменную важности признаков (feature_importances_). Мы будем использовать кросс-валидированную версию рекурсивного устранения признаков (RFE), т. к. она надежнее. RFECV сначала берет оценщика (LinearDiscriminantAnalysis). Затем можно задать шаг (step), устанавливающий число признаков, которое он должен удалять на каждой итерации, число кросс-валидаций (cv) и метрику, используемую для оценивания (scoring). Наконец, рекомендуется задать детализацию (verbose=2) и использовать как можно больше процессоров (n_jobs=-1). В целях ускорения процесса мы снова используем выборку для обучения и начнем с 267 для верхних столбцов (top_cols): rfe_lda = RFECV(LinearDiscriminantAnalysis(n_components=1),\ step=2, cv=3, scoring='f1', verbose=2, n_jobs=-1) rfe_lda.fit(X_train.iloc[sample_train_idx][top_cols],\ y_train_class[sample_train_idx]) rfe_lda_cols = np.array(top_cols)[rfe_lda.support_].tolist() Можно попробовать RandomForestRegressor, на этот раз с более крупным размером шага 0,05, т. е. на каждой итерации должно устраняться 5% всех признаков: rfe_rf = RFECV(RandomForestRegressor(random_state=rand, max_depth=4),\ step=0.05, cv=3, verbose=2, n_jobs=-1, scoring='neg_root_mean_squared_error') rfe_rf.fit(X_train.iloc[sample_train_idx][top_cols], y_train[sample_train_idx]) rfe_rf_cols = np.array(top_cols)[rfe_rf.support_].tolist() Далее мы испытаем разные методы, которые не относятся к трем главным категориям отбора признаков: фильтрационным, встроенным и оберточным.
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 443 Продвинутые методы Многие методы можно отнести к категории продвинутых методов отбора признаков, в том числе к следующим подкатегориям.  Редукция размерности. Некоторые методы редукции размерности, такие как анализ главных компонент (principal component analysis, PCA), могут возвращать объясненную дисперсию на основе признаков. Для других, таких как анализ факторов, он может выводиться из иных результатов. Объясненную дисперсию можно использовать для ранжирования признаков.  Модельно-агностическая важность признаков. Любой метод определения важности признаков, описанный в главах 4 и 5, можно применять для получения признаков модели, находящихся в верхней части списка, для их отбора.  Генетический алгоритм. Это оберточный метод в том смысле, что он "оберты- вает" модель, диагностируя предсказательную результативность по многочисленным подмножествам признаков. Однако, в отличие от рассмотренных нами оберточных методов, он не жадный и более оптимизирован для работы с крупными признаковыми пространствами. Он называется генетическим, потому что его функциональность навеяна биологией, в частности естественным отбором.  Автокодировщики. Мы не будем чересчур вникать в этот вопрос, но глубокое обучение может использоваться для отбора признаков с помощью автокодировщиков. В этом разделе мы кратко коснемся первых трех, чтобы понять, как они могут быть имплементированы. Давайте приступим к делу! Редукция размерности Мы рассмотрели анализ главных компонент (PCA) в главе 3, но не извлекали из него объясненную дисперсию, и мы фактически использовали его для редукции размерности. Как видно из следующего ниже исходного кода, на этот раз мы сохраним число компонент (n_components) в качестве числа признаков и задействуем способность метода раскладывать дисперсию с помощью сингулярного разложения (singular value decomposition, SVD): pca = PCA(n_components=X_train.shape[1]) fitted_pca = pca.fit(X_train) pca_evrs = pd.DataFrame({'col':X_train.columns,\ 'evr':fitted_pca.explained_variance_ratio_}).\ sort_values(by='evr', ascending=False) pca_cols = pca_evrs.head(150).col.tolist() Как хорошо видно в приведенном выше фрагменте исходного кода, обучение PCA выполняется так же, как и любой другой модели, за исключением того, что для нее не требуются метки, потому что это неконтролируемый метод. Затем мы извлекаем коэффициент объясненной дисперсии explained_variance_ratio_ (explained variance
444 Часть III. Настройка на интерпретируемость ratio, EVR) и помещаем его в кадр данных, который сортируем по EVR. Наконец, мы берем верхние 150 признаков и сохраняем их в списке (pca_cols). Модельно-агностическая важность признаков Популярным модельно-агностическим методом, который мы использовали на протяжении всей этой книги, является SHAP, и он обладает многими свойствами, которые делают его более надежным, чем другие методы. В следующем исходном коде можно взять наилучшую модель и извлечь для нее значения SHAP (shap_values) с помощью древовидного объяснителя (TreeExplainer): fitted_rf_mdl = reg_mdls['rf_11_all']['fitted'] shap_rf_explainer = shap.TreeExplainer(fitted_rf_mdl) shap_rf_values = shap_rf_explainer.shap_values(X_test_orig.iloc[sample_test_idx]) shap_imps = pd.DataFrame({'col':X_train_orig.columns,\ 'imp':np.abs(shap_rf_values).mean(0)}). sort_values(by='imp',ascending=False) shap_cols = shap_imps.head(150).col.tolist() Затем мы усредняем абсолютное значение SHAP по первой размерности, что дает нам ранг по каждому признаку. Мы помещаем это значение в кадр данных и сортируем его, как это делалось в случае с PCA. Наконец, тоже берем верхние 150 и помещаем их в список (shap_cols). Генетические алгоритмы Генетические алгоритмы — это навеянный естественным отбором технический прием стохастической глобальной оптимизации, который обертывает модель так же, как это делают оберточные методы. Однако они не подчиняются последовательности на пошаговой основе. У генетических алгоритмов нет итераций, но есть поколения, которые включают популяции хромосом. Каждая хромосома — это двоичное представление вашего признакового пространства, где 1 означает отбор признака, а 0 — нет. Каждое поколение производится следующими операциями.  Селекция. Как и в случае естественного отбора, это симбиоз случайности (раз- ведка) и произошедшего факта (эксплуатация). То, что сработало ранее (факт), является приспособленностью. Приспособленность диагностируется с помощью "оценщика", очень похожего на оберточные методы. Хромосомы с плохой приспособленностью удаляются, а хорошие получают возможность размножаться с помощью "скрещивания".  Скрещивание. Несколько хороших бит (или признаков) каждого родителя слу- чайно переходят к потомку.  Мутация. Даже когда хромосома доказала свою эффективность, учитывая низ- кую частоту мутации, она иногда мутирует или меняет один из своих битов, или, выражаясь по-нашему, признаков. Используемая нами имплементация на Python имеет ряд опций. Мы не будем объяснять их все здесь, но они хорошо задокументированы в исходном коде, если вам
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 445 будет интересно. Первый атрибут — это оценщик. Мы также можем задать итерации кросс-валидации (cv=3), метрику оценивания (scoring), чтобы определить приспособленность или неприспособленность хромосомы, и максимальное число признаков, которые он должен отбирать в каждой хромосоме (max_features). Есть несколько важных вероятностных свойств, таких как вероятность мутации бита (mutation_independent_proba) и вероятность обмена бит (crossover_independent_proba). С точки зрения поколения, n_gen_no_change предоставляет средство для ранней остановки, если поколения не улучшились, а n_generations, по умолчанию равное 40, является точкой жесткой остановки. Обучение модели GeneticSelectionCV выполняется так же, как и любой другой модели. Обучение может занять некоторое время, поэтому лучше всего определить детализацию и позволить ей использовать все обрабатывающие возможности. После завершения можно применить булеву маску (support_), чтобы взять подмножество признаков: ga_rf = GeneticSelectionCV(RandomForestRegressor(random_state=rand, max_depth=3),\ cv=3, scoring='neg_root_mean_squared_error',\ max_features=90, crossover_independent_proba=0.5,\ n_gen_no_change=5, mutation_independent_proba=0.05, n_jobs=-1, verbose=2) ga_rf = ga_rf.fit(X_train[top_cols], y_train) ga_rf_cols = np.array(top_cols)[ga_rf.support_].tolist() Хорошо. Теперь, когда в этом разделе мы рассмотрели широкий спектр оберточных, гибридных и продвинутых методов отбора признаков, давайте оценим их все сразу и сравним результаты. Оценивание всех моделей, построенных с применением отбора признаков Как и в случае со встроенными методами, можно разместить имена подмножеств признаков (fsnames), списки (fscol) и соответствующие глубины (depths) в списках: fsnames = ['w-sfs-lda', 'w-sbs-et', 'h-rfe-lda', 'h-rfe-rf', 'a-pca', 'a-shap', 'a-ga-rf'] fscols = [sfs_lda_cols, sbs_et_cols, rfe_lda_cols, rfe_rf_cols, pca_cols, shap_cols, ga_rf_cols] depths = [5, 6, 6, 6, 6, 6, 5] Затем можно прокрутить все подмножества признаков в цикле, тренируя с ними XGBRFRegessor и помещая результаты оценивания в словарь моделей (reg_mdls): for i, fsname in tqdm(enumerate(fsnames), total=len(fsnames)): depth = depths[i] cols = fscols[i] mdlname = 'rf_'+str(depth)+'_'+fsname stime = timeit.default_timer() reg_mdl = xgb.XGBRFRegressor(max_depth=depth, n_estimators=200, seed=rand) fitted_mdl = reg_mdl.fit(X_train[cols], y_train)
446 Часть III. Настройка на интерпретируемость etime = timeit.default_timer() reg_mdls[mdlname] = mldatasets.evaluate_reg_mdl(fitted_mdl,\ X_train[cols], X_test[cols], y_train, y_test, plot_regplot=False, show_summary=False, ret_eval_dict=True) : reg_mdls[mdlname]['num_feat'] = \ sum(reg_mdls[mdlname]['fitted'].feature_importances_ > 0) Как и ранее в этой главе, можно конвертировать reg_mlds в кадр данных (reg_metrics_df), но на этот раз отфильтровать его, чтобы включить только модели с максимальной глубиной менее 7 (reg_metrics_df = reg_metrics_df[reg_metrics_df.depth < 7]). Затем можно вывести кадр данных. Результат показан на рис. 10.8. Рис. 10.8. Сравнение метрик всех моделей, построенных с применением отбора признаков На рис. 10.8 показано, что модели, построенные с применением отбора признаков, более прибыльны, нежели модели, включающие все признаки, при одинаковой глубине. Кроме того, встроенные LASSO LARS с информационным критерием AIC (e-llarsic) и фильтрационным методом MIC (f-mic) превосходят все оберточные, гибридные и продвинутые методы. Тем не менее мы также создавали этим методам препятствия, используя выборку тренировочного набора данных, что было необходимо для ускорения процесса. Возможно, в противном случае они превзошли бы лучших. Однако четыре перечисленных далее метода отбора признаков являются довольно конкурентоспособными:  рекурсивное устранение признаков (RFE) с линейным дискриминантным анали- зом (LDA) — гибридный метод (h-rfe-lda);  SHAP — продвинутый метод (a-shap);
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 447  генетические алгоритмы (GA) со случайным лесом — продвинутый метод (a-ga-rf);  встроенная логистическая регрессия с регуляризацией L2 — встроенный метод (e-logl2). Было бы разумно потратить несколько дней на выполнение многочисленных вариаций рассмотренных в этой книге методов. Например, возможно, рекурсивное устранение признаков с регуляризованной логистической регрессией L1 или генетический алгоритм (GA) с опорно-векторными машинами с дополнительной мутацией даст наилучшую модель. Возможностей очень много! Тем не менее если бы вы были вынуждены дать рекомендацию, основываясь на рис. 10.8, по одной только прибыли, то 111-признаковая e-llarsic является лучшим вариантом, но она также имеет более высокие минимальные расходы и более низкую максимальную возвратность инвестиций, чем любая из топовых моделей. Правда, имеется компромисс. И хотя у нее один из самых низких тестовых RMSE, модель на основе случайного леса и GA с 63 признаками (a-ga-rf) имеет ту же RMSE и превосходит ее по максимальной возвратности инвестиций и минимальным расходам. Следовательно, они являются двумя разумными вариантами. Но прежде чем выдать окончательное решение, необходимо сопоставить прибыльность по всем пороговым значениям, чтобы диагностировать условия, при которых каждая модель может давать наиболее надежные предсказания и при определенных расходах и возвратности инвестиций. Обзор конструирования признаков Допустим, что некоммерческая организация решила использовать модель, признаки которой были отобраны с помощью LASSO LARS с AIC (e-llarsic), но хотела бы оценить, сможете ли вы усовершенствовать модель. Теперь, когда вы устранили более 300 признаков, которые, возможно, лишь незначительно улучшали предсказательную результативность, но в основном добавляли шум, у вас остались более важные признаки. Однако вы также знаете, что 63 признака, отобранные генетическими алгоритмами (a-ga-rf), дали такую же величину RMSE, что и 111 признаков. Это означает, что, хотя в этих дополнительных признаках есть нечто, повышающее прибыльность, оно не улучшает RMSE. С точки зрения отбора признаков для решения этой задачи можно сделать многое. Например, рассмотреть наложение и разницу признаков между e-llarsic и a-ga-rf и выполнить вариации отбора строго по этим признакам, чтобы увидеть, падает ли RMSE в какой-либо комбинации при сохранении или улучшении текущей прибыльности. Однако существует и другая возможность, которая заключается в конструировании признаков. Есть несколько важных причин, по которым может возникнуть потребность в конструировании признаков на этом шаге.  Облегчить понимание модельной интерпретации. Иногда признаки имеют шкалу, которая не является интуитивно понятной, либо шкала интуитивно понятна, но распределение затрудняет ее восприятие. До тех пор пока преобразо-
448 Часть III. Настройка на интерпретируемость вания этих признаков не ухудшают модельную результативность, есть ценность в преобразовании признаков для более глубокого понимания результатов методов интерпретации. По мере обучения модели на большем числе признаков приходит понимание того, что конкретно работает и почему, собственно, это работает. А это поможет оценить применимость модели для исследуемых данных.  Разместить ограждения на отдельных признаках. Иногда признаки имеют неровное распределение, и модели склонны к переобучению в более разреженных областях гистограммы признака или там, где существуют влиятельные выбросы.  Устранить нелогичные взаимодействия. Некоторые отыскиваемые моделью взаимодействия не имеют смысла и существуют только потому, что признаки коррелируют, но не по естественным причинам. Они могут быть спутывающими переменными или, возможно, даже избыточными (такими, как та, которую мы нашли в главе 5). В этом случае можно сконструировать интеракционный признак либо устранить избыточный. В связи с последними двумя причинами мы коснемся стратегии конструирования признаков подробнее в главе 12. В этом разделе основное внимание будет уделено первой причине, в частности потому, что она является неплохой стартовой точкой, поскольку она позволит вам лучше понять данные, пока вы не узнаете их достаточно хорошо, чтобы внести больше преобразовательных изменений. Таким образом, у нас осталось 111 признаков, но мы понятия не имеем, как они соотносятся с целевой переменной или друг с другом. Первым делом необходимо определить важность признаков. Для этого можно использовать древовидный объяснитель (TreeExplainer) SHAP на модели e-llarsic. Его преимущество заключается в том, что он может вычислять интеракционные значения SHAP (shap_interaction_values) вместо выведения массива размером (N, 111), где N — это число наблюдений, как это делает shap_values; он будет выводить (N, 111, 111). С его помощью можно сгенерировать сводный график (summary_plot), который ранжирует как отдельные признаки, так и взаимодействия. Единственная разница для интеракционных значений (так называемых значений взаимодействия) заключается в том, что используется компактный точечный график plot_type="compact_dot": fitted_rf_mdl = reg_mdls['rf_5_e-llarsic']['fitted'] shap_rf_explainer = shap.TreeExplainer(fitted_rf_mdl) shap_rf_interact_values = shap_rf_explainer.shap_interaction_values(\ X_test.iloc[sample_test_idx][llarsic_cols]) shap.summary_plot(shap_rf_interact_values,\ X_test.iloc[sample_test_idx][llarsic_cols],\ plot_type="compact_dot", sort=True) Приведенный выше фрагмент исходного кода производит сводный график взаимодействия SHAP, показанный на рис. 10.9. Рис. 10.9 можно истолковывать, как и любой сводный график, за исключением того, что он включает двупеременные взаимодействия дважды — сначала с одним
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 449 признаком, а затем с другим. Например, MDMAUD_A* - CLUSTER — это интеракционные значения SHAP для этого взаимодействия с точки зрения MDMAUD_A*, поэтому значения признаков соответствуют только этому признаку, но значения SHAP предназначены для взаимодействия. Еще один интересный вывод состоит в том, что высокие значения большинства признаков соответствуют более высоким значениям SHAP. Именно MDMAUD_A и CLUSTER являются исключениями. Рис. 10.9. Сводный график взаимодействия SHAP На протяжении всей этой книги главы с табличными данными начинались со словаря данных. Эта глава была исключением, учитывая, что мы имели вообще-то 435 признаков. Теперь имеет смысл, по меньшей мере, понять, каковы главные признаки. Полный словарь данных [1] находится по адресу: https://kdd.ics.uci.edu/ databases/kddcup98/epsilon_mirror/cup98dic.txt, но некоторые признаки уже были изменены из-за категориальной кодировки, поэтому ниже мы объясним их подробнее:  MAXRAMNT — непрерывный, денежная стоимость в долларах самого большого по- дарка на сегодняшний день;  HVP2 — дискретный, процент домов со стоимостью 150 тыс. долларов США и бо- лее в районах благотворителей (значения от 0 до 100);  LASTGIFT — непрерывный, денежная сумма в долларах за самый последний подарок;  RAMNTALL — непрерывный, денежная стоимость в долларах пожизненных подар- ков на сегодняшний день;
450 Часть III. Настройка на интерпретируемость  AVGGIFT — непрерывный, средняя стоимость подарков в долларах на сегодняш- ний день;  MDMAUD_A — порядковый, код денежной суммы пожертвования от благотворите- лей, которые сделали подарок на сумму более 100 долларов США в любое время в своей истории пожертвований (значения от 0 до 3, 1 для тех, кто никогда не превышал 100 долларов США). Код суммы — это третий байт кода RFA (recency/frequency/amount — недавность/частота/сумма) матрицы главных клиентов. Указанный код представляет переданную денежную сумму. Эти категории таковы:  0 — менее 100 долларов (низкий доллар);  1 — 100–499 долларов (основной);  2 — 500–999 долларов (основной);  3 — 1000 долларов и более (нуль-порядковый предсказатель);  NGIFTALL — дискретный, число пожизненных подарков на сегодняшний день;  AMT_14 — порядковый, код денежной суммы пожертвования RFA для 14-й пре- дыдущей акции (за 2 года до этого), которая соответствует последней денежной сумме в долларах, переданной тогда:  0 — 0,01–1,99 долларов;  1 — 2,00–2,99 долларов;  2 — 3,00–4,99 долларов;  3 — 5,00–9,99 долларов;  4 — 10,00–14,99 долларов;  5 — 15,00–24,99 долларов;  6 — 25,00 долларов и более;  DOMAIN_SOCIALCLS — номинальный, социально-экономический статус (socio- economic status, SES) района, который сочетается с DOMAIN_URBANICITY (0 — городская агломерация, 1 — город, 2 — пригород, 3 — поселок городского типа, 4 — сельский район), что означает следующее:  1 — самый высокий SES;  2 — среднее значение SES, за исключением выше среднего для сообществ городской агломерации;  3 — самый низкий SES, за исключением ниже среднего для сообществ городской агломерации;  4 — самый низкий SES только для сообществ городской агломерации;  CLUSTER — номинальный, код, указывающий группу кластеров, в которую входит донор;  MINRAMNT — непрерывный, денежная сумма в долларах самого маленького подар- ка на сегодняшний день;
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 451  LSC2 — дискретный, процентный возраст испаноязычных семей в районе донора (значения от 0 до 100);  IC15 — дискретный, процент семей с доходом менее 15 тыс. долларов в районе благотворителя (значения от 0 до 100). Из этого словаря и рис. 10.9 можно сделать следующие выводы.  Преобладают денежные суммы подарков. Семь верхних признаков относятся к денежным суммам подарков, будь то суммарная величина, минимум, максимум, средняя или последняя. Если включить количество подарков (NGIFTALL), то будет восемь признаков, которые связаны с историей пожертвований, что имеет полное отношение к делу. И почему это имеет отношение к делу? Потому что они, вероятно, сильно коррелированы и позволяют нам понять, как именно хранить ключи к тому, что может улучшать модель. Пожалуй, можно создать другие признаки, которые значительно улучшат эти связи.  Высокие значения признаков непрерывной денежной суммы подарков имеют высокие значения SHAP. Постройте диаграмму типа "ящик с усами" любого из этих признаков, наподобие plt.boxplot(x_test.MAXRAMNT), и вы увидите, насколько эти признаки скошены вправо. Возможно, преобразование, такое как разбивка их на корзины (так называемая дискретизация), или использование другой шкалы, такой как логарифмическая (попробуйте plt.boxplot(np.log(x_test.MAXRAMNT))), помогут интерпретировать эти признаки и найти карманы, где вероятность пожертвования резко возрастает.  Связь с 14-й предыдущей акцией. Что произошло за 2 года до того, как благо- творительная организация присоединила эту акцию к той, которая указана в метках набора данных? Были ли рекламные материалы похожими? Есть ли фактор сезонности, который возникает в одно и то же время каждые пару лет? Возможно, вам удастся сконструировать признак, который выявляет указанные закономерности лучше.  Несогласующиеся классификации. Признак DOMAIN_SOCIALCLS имеет разные ка- тегории в зависимости от значения признака DOMAIN_URBANICITY. Всё можно состыковать, используя все пять категорий шкалы (самая высокая, выше среднего, средняя, ниже среднего и самая низкая), даже если это означает, что благотворители из других городов будут использовать только три. Преимущество такого подхода заключалось бы в упрощении интерпретации, и весьма маловероятно, что это негативно скажется на результативности модели. Сводный график взаимодействия SHAP полезен для выявления рангов признаков и взаимодействий и некоторых общностей между ними. Но в целях более глубокого анализа взаимодействий сначала нужно квантифицировать их влияние. С этой целью давайте создадим теплокарту только с верхними взаимодействиями, измеряемыми по их среднему абсолютному значению SHAP (shap_rf_interact_ avgs). Затем необходимо установить все диагональные значения равными 0 (shap_rf_interact_avgs_nodiag), потому что это не взаимодействия, а значения SHAP,
452 Часть III. Настройка на интерпретируемость и без них легче наблюдать взаимодействия. Эту матрицу можно поместить в кадр данных, но этот указанный кадр состоит из 111 столбцов и 111 строк, поэтому в целях его фильтрования по этим признакам с наибольшим числом взаимодействий мы их суммируем и ранжируем с помощью функции rankdata библиотеки scipy. Затем мы используем ранг для определения 12 наиболее взаимовлияющих признаков (most_interact_cols) и разбивки кадра данных по ним. Наконец, мы строим график кадра данных в виде теплокарты: shap_rf_interact_avgs = np.abs(shap_rf_interact_values).mean(0) shap_rf_interact_avgs_nodiag = shap_rf_interact_avgs.copy() np.fill_diagonal(shap_rf_interact_avgs_nodiag, 0) shap_rf_interact_df = pd.DataFrame(shap_rf_interact_avgs_nodiag) shap_rf_interact_df.columns = X_test[llarsic_cols].columns shap_rf_interact_df.index = X_test[llarsic_cols].columns shap_rf_interact_ranks = 112 - rankdata(np.sum(shap_rf_interact_avgs_nodiag, axis=0)) most_interact_cols = shap_rf_interact_df.columns[shap_rf_interact_ranks < 13] shap_rf_interact_df = shap_rf_interact_df.\ loc[most_interact_cols, most_interact_cols] sns.heatmap(shap_rf_interact_df, cmap='Blues', annot=True,\ annot_kws={'size':10}, fmt='.3f', linewidths=.5) Приведенный фрагмент исходного кода выводит на экран теплокарту, представленную на рис. 10.10. На нем изображены наиболее заметные взаимодействия признаков в соответствии с абсолютными средними значениями взаимодействия SHAP. Обратите внимание, что это средние значения, поэтому, учитывая величину скошенности большинства этих признаков вправо, они, вероятно, намного выше для многих наблюдений. Тем не менее это все еще хороший индикатор относительного воздействия. Разобраться во взаимодействии признаков один за другим можно с помощью графика независимости SHAP (dependence_plot). Например, можно взять верхний признак MAXRAMNT и вывести его на график с кодированными цветом взаимодействиями с такими признаками, как RAMNTALL, LSC4, HVP2 и AVGGIFT. Но сначала нам нужно будет вычислить значения SHAP (shap_values). Однако необходимо разобраться с парой затруднений, о которых мы упоминали ранее. Они имеют отношение к следующему.  Преобладание выбросов. Можно вырезать их из графика, ограничив оси x и y, используя процентили для признака и значений SHAP, соответственно, с помощью plt.xlim и plt.ylim. Это существенно увеличивает масштаб случаев, которые лежат между 1-м и 99-м процентилями.  Однобокое распределение признаков денежных сумм в долларах. В любом признаке, связанном с деньгами, обычно наблюдается скошенность вправо. И целый ряд способов помогает ее упростить, например применение процентилей для разбивки признака на корзины, но самый быстрый способ упростить ее оценку состоит в использовании логарифмической шкалы. В библиотеке matplotlib
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 453 это делается с помощью plt.xscale('log') без необходимости преобразования признака. Рис. 10.10. Теплокарта взаимодействий SHAP Следующий исходный код объясняет эти два затруднения. Можно закомментировать xlim, ylim или xscale, и увидеть большу́ю разницу, которую они индивидуально вносят в понимание графика зависимости (dependence_plot): shap_rf_values = shap_rf_explainer.shap_values(\ X_test.iloc[sample_test_idx][llarsic_cols]) maxramt_shap = shap_rf_values[:,llarsic_cols.index("MAXRAMNT")] shap.dependence_plot("MAXRAMNT", shap_rf_values,\ X_test.iloc[sample_test_idx][llarsic_cols],\ interaction_index="AVGGIFT", show=False, alpha=0.1) plt.xlim(xmin=np.percentile(X_test.MAXRAMNT, 1),\ xmax=np.percentile(X_test.MAXRAMNT, 99)) plt.ylim(ymin=np.percentile(maxramt_shap, 1),\ ymax=np.percentile(maxramt_shap, 99)) plt.xscale('log')
454 Часть III. Настройка на интерпретируемость Приведенный выше исходный код генерирует график на рис. 10.11. Он показывает переломный момент, который существует где-то между 10 и 100 для MAXRAMNT, когда среднее влияние на результат модели начинает увеличиваться, и это коррелирует с более высоким значением AVGGIFT. Рис. 10.11. График взаимодействия SHAP между признаками MAXRAMNT и AVGGIFT Из рис. 10.11 можно сделать вывод о том, что кластер формируется определенными значениями этих признаков и, возможно, несколькими другими, которые повышают вероятность пожертвования. С точки зрения конструирования признаков можно использовать неконтролируемые методы для создания специальных кластерных признаков, исключительно основываясь на нескольких признаках, которые вы определили как родственные. Либо можно было бы пойти более ручным маршрутом, сравнивая разные графики, чтобы понять, как выявлять кластеры лучше всего. Из этого процесса можно извлечь двоичные признаки или даже соотношение между признаками, которые четче изображают взаимодействия или принадлежность к кластеру. Идея здесь не в том, чтобы изобретать велосипед, пытаясь сделать то, что модель уже делает и так хорошо, а в первую очередь стремиться к более простой интерпретации модели. Будем надеяться, это даже окажет положительное влияние на предсказательную результативность за счет приведения признаков в порядок, потому что, если вы поймете их лучше, то, возможно, модель сделает то же самое! Это, подобно сглаживанию зернистого изображения, будет меньше запутывать и вас, и модель (подробнее об этом см. в главе 13)! Но более глубокое понимание данных с помощью модели имеет и другие положительные побочные эффекты. На самом деле, уроки не заканчиваются конструированием признаков или построением модели, но могут применяться непосредственно к маркетинговым акциям. Здорово, если бы выявленные переломные моменты можно было использовать для поощрения пожертвований. Может быть, стоит предлагать бесплатную кружку, ес-
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 455 ли жертвуется свыше X долларов? Или организовать эксклюзивный список важных персон за регулярное пожертвование денежных сумм в размере X долларов? Мы закончим данную тему на вот такой высокой ноте, но, надеюсь, она вас подстегнет, и вы оцените то, как можно применять уроки модельной интерпретации для отбора и конструирования признаков и многого другого. Миссия выполнена Для того чтобы приблизиться к решению этой миссии, вы снизили переобучение, используя прежде всего инструментарий отбора признаков. Некоммерческая организация удовлетворена увеличением прибыли примерно на 30%, которое обошлось в общей сложности в 35 601 доллар, что на 30 тыс. долларов меньше, чем стоила бы отправка всем клиентам из тестового набора данных почтовой рассылки. И всё же, ее активисты по-прежнему хотят быть уверенными в том, что могут использовать эту модель безопасно для себя, не беспокоясь о возможных убытках. В этой главе мы рассмотрели, как переобучение может приводить к нестабильности кривых прибыльности. Невыровненность имеет решающее значение, поскольку она может означать, что выбор порога, основанного на тренировочных данных, не будет надежным для вневыборочных данных. И поэтому вы используете функцию compare_df_plots для сравнения прибыльности между тестовым и тренировочным наборами, как это делалось раньше, но на этот раз для выбранной модели (rf_5_ellarsic): profits_test = reg_mdls['rf_5_e-llarsic']['profits_test'] profits_train = reg_mdls['rf_5_e-llarsic']['profits_train'] mldatasets.compare_df_plots(\ profits_test[['costs', 'profit', 'roi']],\ profits_train[['costs', 'profit', 'roi']],\ 'Валидация', 'Обучение', x_label='Порог', y_formatter=y_formatter,\ plot_args={'secondary_y':'roi'}) Приведенный исходный код генерирует графики на рис. 10.12. Этот рисунок можно показать некоммерческой организации, чтобы доказать, что существует преимущество в 0,68 доллара, являющееся второй по величине прибылью, достижимой в валидации. Она тоже находится в пределах досягаемости бюджета организации и обеспечивает возвратность инвестиций в размере 41%. Что еще важнее, указанные цифры недалеко ушли от тех, которые имеются для обучения. Приятно видеть еще одну вещь, а именно, кривая прибыли медленно скользит вниз как для обучения, так и для валидации вместо того, чтобы резко идти на спад. Некоммерческая организация может быть уверена в том, что кампания все равно будет прибыльной, если ее активисты решат увеличить порог. В конце концов, они хотят ориентироваться на благотворителей из всего списка рассылки, и для того, чтобы это было финансово осуществимо, указанный список должен быть более эксклюзивным. Допустим, они используют порог в 0,77 доллара для всего списка рассылки; марке-
456 Часть III. Настройка на интерпретируемость тинговая кампания обойдется примерно в 46 тыс. долларов, но принесет прибыль более 24 тыс. долларов. Рис. 10.12. Сравнение прибыли (profit), расходов (coast) и прибыльности инвестиций (roi (rigth)) для валидационного и обучающего наборов данных в модели с LASSO LARS с использованием признаков AIC по всем пороговым значениям Примите поздравления! Эта миссия была вами выполнена! Но мы бы упустили нечто важное, если бы не упомянули одну важную деталь. Хотя мы и тренировали эту модель с учетом следующей маркетинговой кампании, модель, скорее всего, будет использоваться в будущих кампаниях прямого маркетинга без переобучения. Повторное использование этой модели представляет проблему. Существует понятие дрейфа данных, так называемого дрейфа признаков, который заключается в том, что со временем усвоенное моделью о признаках, относящихся к целевой переменной, больше не соответствует действительности. Еще одно понятие, дрейф концепции, касается того, как определение целевого признака меняется с течением времени. Например, мера, представляющая выгодного благотворителя, может измениться. Оба дрейфа могут происходить одновременно, и в случае задач, связанных с человеческим поведением, этого следует ожидать. Поведение формируется культурой, привычками, отношениями, технологиями и модой, которые постоянно эволюционируют. Можно предупредить некоммерческую организацию о том, что вы гарантируете только то, что модель будет надежной для следующей маркетинговой кампании, но организация не сможет позволить себе нанимать вас каждый раз для переобучения моделей! Можно предложить клиенту создать скрипт, который отслеживает дрейф непосредственно в базе данных списка рассылки. Если он обнаружит существенные изменения в используемых моделью признаках, то он будет предупреждать и их, и вас. В этом месте вы могли бы запускать автоматическое переобучение модели. Однако, если дрейф связан с повреждением данных, у вас не будет возможности решить проблему. И даже если автоматическое переобучение будет выполнено, модель нельзя будет развернуть, если метрики результативности не соответствуют заранее установленным стандартам. В любом случае, для обеспечения гарантии надежно-
Глава 10. Отбор и конструирование признаков для обеспечения интерпретируемости 457 сти необходимо внимательно следить за предсказательной результативностью. Надежность является важной темой в рамках модельной интерпретируемости, поскольку она в значительной степени связана с подотчетностью. В этой книге тема обнаружения дрейфа не рассматривается, но в будущих главах обсуждается тема насыщения, или аугментации, данных (см. главу 11) и устойчивости к антагонизму (см. главу 13), которые относятся к надежности. Резюме В этой главе мы узнали о том, как нерелевантные признаки влияют на исходы модели и как отбор признаков обеспечивает инструментарий для решения этой проблемы. Затем мы изучили целый ряд разных методов из этого инструментария, от самых простых фильтрационных методов до самых продвинутых. Наконец, мы затронули тему конструирования, или выработки, признаков для обеспечения интерпретируемости. Конструирование признаков может способствовать большей интерпретируемости модели, которая будет способна показывать более высокую результативность. Эта тема будет рассмотрена подробнее в главе 12. В следующей далее главе мы обсудим методы ослабления систематического смещения и методы причинно-следственного вывода. Источники наборов данных  Ling C., Li C. Data Mining for Direct Marketing: Problems and Solutions. In Proceed- ings of the Fourth International Conference on Knowledge Discovery and Data Mining (KDD’98). — AAAI Press, 1998. — P. 73–79. — URL: https://dl.acm.org/ doi/10.5555/3000292.3000304 (Линг С., Ли С. Добыча знаний из данных для прямого маркетинга: проблемы и решения.)  Репозиторий машинного обучения UCI. Набор данных "Кубок KDD" за 1998 год по адресу: https://archive.ics.uci.edu/ml/datasets/KDD+Cup+1998+Data. Справочные материалы  Ross B. C. Mutual Information between Discrete and Continuous Data Sets // PLoS ONE. — 2014. — № 9. — URL: https://journals.plos.org/plosone/article?id=10.1371/ journal.pone.0087357. (Росс Б. С. Взаимная информация между дискретными и непрерывными наборами данных.)  Geurts P., Ernst D., Wehenkel L. Extremely randomized trees // Machine Learning. — 2006. — Vol. 63 (1). — P. 3–42. — URL: https://link.springer.com/article/10.1007/ с10994-006-6226-1. (Гертс П., Эрнст Д., Венкель Л. Чрезвычайно рандомизированные деревья.)
458 Часть III. Настройка на интерпретируемость  Abid A., Balin M. F., Zou J. Concrete Autoencoders for Differentiable Feature Selec- tion and Reconstruction // ICML. — 2019. — URL: https://arxiv.org/abs/1901.09346. (Абид А., Балин М. Ф., Зоу, Дж. Конкретные автокодировщики для дифференцируемого отбора и реконструкции признаков.)  Tan F., Fu X., Zhang Y., Bourgeois A. G. A genetic algorithm-based method for fea- ture subset selection // Soft Computing. — 2008. — № 12. — P. 111–120. — URL: https://link.springer.com/article/10.1007/s00500-007-0193-8. (Тан Ф., Фу Х., Чжан Ю., Буржуа А. Г. Метод на основе генетического алгоритма для селекции подмножества признаков.)  manuel-calzolari/sklearn-genetic: sklearn-genetic 0.3.0 // Zenodo : [site]. — URL: http://doi.org/10.5281/zenodo.4081754.
11 Ослабление систематического смещения и причинно-следственный вывод В главе 7 мы рассмотрели объективность и ее связь с принятием решений, но ограничились методами постфактумной модельной интерпретации. В главе 10 мы затронули тему чувствительности к стоимости, которая часто связана с балансом или объективностью. В этой главе мы рассмотрим методы, которые позволят уравновешивать данные и настраивать модели на объективность. Используя стандартный набор данных кредитных карт, мы научимся задействовать целевые визуализаторы, такие как классовый баланс, с целью обнаружения нежелательного систематического смещения, а затем его ослаблять посредством методов предобработки, таких как перевесовка, методов промежуточной переработки, таких как устранитель неблагоприятного воздействия, и методов постобработки, таких как уравненные шансы. Продолжая темы глав 7 и 10, мы также займемся решениями в сфере корпоративной политики, которые могут иметь неожиданные, противоречащие здравому смыслу либо даже пагубные последствия. В контексте статистической проверки гипотез решением называется экспериментальная процедура. Для многих сценариев принятия решений крайне важно оценивать их влияние и обеспечивать надежность этой оценки. В связи с этим мы выдвинем гипотезу об экспериментальных процедурах, предназначенных для уменьшения дефолта по кредитным картам в отношении наиболее уязвимых групп населения и задействуем причинно-следственное моделирование с целью определения средних эффектов экспериментальных процедур (average treatment effects, ATE) и условных средних эффектов экспериментальных процедур (conditional average treatment effects, CATE). Наконец, мы проведем валидацию причинно-следственных допущений и устойчивости оценок, используя разнообразные методы. Вот главные темы, которые будут рассмотрены в этой главе:  обнаружение систематического смещения;  ослабление систематического смещения;  построение причинно-следственной модели;  понимание гетерогенных эффектов эспериментальных процедур;  проверка устойчивости оценки.
460 Часть III. Настройка на интерпретируемость Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, lightgbm, xgboost, matplotlib, seaborn, xai, aif360, econml и dowhy. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/InterpretableMachine-Learning-with-Python/tree/master/Chapter11. Миссия По всему миру люди используют более 2,8 млрд кредитных карт, и посредством них в совокупности тратится более 25 трлн долларов США ежегодно (https://www.ft.com/content/ad826e32-2ee8-11e9-ba00-0251022932c8). Это, несомненно, астрономические денежные суммы, но размер индустрии кредитных карт лучше всего измеряется не тем, сколько потрачено, а тем, какова кридитная ставка. Карточные эмитенты, такие как банки, зарабатывают основную часть своих денег на процентах. И, следовательно, задолженность потребителей в размере более 60 трлн долларов, значительная часть которой приходится на задолженность по крединым картам, обеспечивает кредиторам стабильный доход в виде процентов. Можно аргументированно доказать, что это хорошо для бизнеса, но это также создает значительный риск, потому что, если заемщик объявит о своей финансовой несостоятельности до погашения основной суммы с добавленными операционными расходами, то кредитор может понести убытки, в особенности после того, как он исчерпал юридические возможности для взыскания долга. Когда возникает "кредитный пузырь", эта проблема усугубляется, потому что нездоровый уровень долга может поставить под угрозу финансы кредиторов и потопить их совладельцев вместе с ними, когда "пузырь" лопнет. Так было в случае с "жилищным пузырем" 2008 года, так называемым кризисом субстандартного ипотечного кредитования. Такие "пузыри" часто начинаются со спекуляций на росте и поиска неквалифицированного спроса для подпитки этого роста. В случае ипотечного кризиса финансовый рынок предлагал ипотечные кредиты людям, у которых не было доказанной способности их погасить. Он также, к сожалению, был нацелен на меньшинство, у которых весь их собственный капитал был уничтожен, как только "пузырь" лопнул. Финансовые кризисы и депрессии, а также все промежуточные бедствия, как правило, затрагивают наиболее уязвимых с гораздо более высокими темпами нарастания проблемы. Кредитные карты тоже участвовали в катастрофических "пузырях", в особенности в Южной Корее в 2003 году (https://www.bis.org/repofficepubl/arpresearch_fs_ 200806.10.pdf) и на Тайване в 2006 году. В этой главе будут рассмотрены данные за 2005 год, приведшие к кризису тайваньских кредитных карт. К 2006 году просроченная задолженность по кредитным картам там достигла 268 млрд долларов, которые задолжали более 700 тыс. человек. Чуть более 3% населения Тайваня не могли оплачивать даже минимальный баланс кредитной карты и в просторечии стали известны как рабы кредитных карт. Это привело к значительным социальным последствиям: резкому увеличению числа бездомных, наркотрафику/злоупотреблению наркотиками и даже самоубийствам. После азиатского финансового кризиса
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 461 1997 года число самоубийств в регионе неуклонно росло. Скачок на 23% в период с 2005 по 2006 год привел к тому, что уровень самоубийств в Тайване занял второе место в мире (https://www.taiwannews.com.tw/en/news/358044). Если проследить кризис до его первопричин, то речь шла о том, что новые банкиэмитенты карт исчерпали насыщенный рынок недвижимости, снизив требования к получению кредитных карт, которые в то время плохо регулировались властями. Это больше всего ударило по молодым людям, потому что у них, как правило, меньше доходов и опыта в управлении деньгами. В 2005 году Тайваньская комиссия по финансовому надзору издала новые правила, повышающие требования к заявителям на получение кредитных карт, предотвращая появление новых "рабов кредитных карт". Однако для решения проблемы задолженности и уже находящихся в системе должников потребуются дополнительные политические решения. Власти начали обсуждать создание корпораций по управлению активами (asset management corporations, AMC) для списания безнадежных долгов с баланса банков. Они также хотели принять регуляторное положение о погашении задолженности, которое обеспечило бы основу для переговоров о разумном плане погашения. Обе эти политики не были кодифицированы в законе вплоть до 2006 года. Гипотетически, допустим, что сейчас август 2005 года, и вы пришли из будущего, вооруженные новыми методами машинного обучения и причинно-следственного вывода! Тайваньский банк хочет построить классификационную модель для предсказания клиентов, которые объявят дефолт по своим кредитным обязательствам. Они предоставили вам набор данных с 30 000 клиентами своих кредитных карт. Регулирующие органы всё еще разрабатывают законы, поэтому есть возможность предложить корпоративные политические решения, которые принесут пользу как банку, так и должникам. Когда закон будет принят, с помощью классификационной модели они смогут предвидеть, какие долги им следует продать AMC, а с помощью причинно-следственной модели — оценить политические решения, которые принесут пользу другим клиентам и банку. Но они хотят сделать это объективно и устойчиво — в этом и состоит ваша миссия! Подход Банк подчеркнул важность наличия в ваших методах объективности, потому что регулирующие органы и общественность в целом хотят быть уверенными в том, что банки больше не причинят вреда. Их репутация тоже зависит от этого, потому что в последние месяцы средства массовой информации неустанно обвиняли их в нечестной и хищнической практике кредитования, вызывая недоверие у потребителей. По этой причине они хотят использовать современную проверку своих действий, чтобы продемонстрировать, что выбранная политика снизит остроту проблемы. Предлагаемый вами подход включает следующие пункты.  По сообщениям, более молодые кредиторы больше предрасположены к дефолту по кредитной задолженности, поэтому вы ожидаете обнаружить возрастное систематическое смещение, но вы также будете искать систематическое смещение в отношении других защищенных групп, таких как пол.
462 Часть III. Настройка на интерпретируемость  Обнаружив систематическое смещение, вы сможете его ослабить с помощью алгоритмов предварительной обработки, промежуточной обработки и последующей обработки с использованием библиотеки AI Fairness 360 (AIF360). В этом процессе вы будете тренировать разные модели с каждым алгоритмом, диагностировать их объективность и выбирать наиболее объективную модель.  В целях обеспечения возможности понять влияние корпоративных политиче- ских решений банк провел эксперимент на небольшой части клиентов. Используя экспериментальные результаты, вы сможете выполнить обучение причинноследственной модели с помощью библиотеки dowhy, которая выявит причинноследственные эффекты. Причинно-следственная модель подразделила эти эффекты дальше, чтобы выявить гетерогенные эффекты экспериментальных процедур.  Затем можно провести диагностику гетерогенных эффектов эксперименталь- ных процедур, чтобы понять эти эффекты и определиться с наиболее эффективными процедурами.  Наконец, в целях обеспечения прочности наших выводов вы попробуете опро- вергнуть эту оценку несколькими методами с целью проведения проверки сохранения эффекта. Давайте приступим к работе! Подготовительные работы Исходный код этого примера находится по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ blob/master/Chapter11/CreditCardDefaults.ipynb. Загрузка библиотек В целях выполнения этого примера необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования данными;  sklearn (scikit-learn), xgboost, aif360 и lightgbm для разбивки данных и обучения моделей;  matplotlib, seaborn и xai для наглядных визуализаций и упрощения интерпрети- рования;  econml и dowhy для причинно-следственных выводов. Сначала необходимо их все загрузить следующим образом: import math import os import mldatasets
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 463 import pandas as pd import numpy as np from tqdm.notebook import tqdm from sklearn import model_selection, tree import lightgbm as lgb import xgboost as xgb from aif360.datasets import BinaryLabelDataset from aif360.metrics import BinaryLabelDatasetMetric, ClassificationMetric from aif360.algorithms.preprocessing import Reweighing, DisparateImpactRemover from aif360.algorithms.inprocessing import PrejudiceRemover, GerryFairClassifier from aif360.algorithms.postprocessing.calibrated_eq_odds_postprocessing \ import CalibratedEqOddsPostprocessing from aif360.algorithms.postprocessing.eq_odds_postprocessing \ import EqOddsPostprocessing from econml.dr import LinearDRLearner import dowhy from dowhy import CausalModel import xai from networkx.drawing.nx_pydot import to_pydot from IPython.display import Image, display import matplotlib.pyplot as plt import seaborn as sns Изучение проблемы и подготовка данных Мы загружаем данные в кадр данных, который назовем ccdefault_all_df, вот таким образом: ccdefault_all_df = mldatasets.load("cc-default", prepare=True) Должно быть 30 000 записей и 31 столбец. Это можно подтвердить с помощью библиотечного метода info(): ccdefault_all_df.info() Приведенная выше строка исходного кода выводит на экран следующее: <class 'pandas.core.frame.DataFrame'> Int64Index: 30000 entries, 1 to 30000 Data columns (total 31 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CC_LIMIT_CAT 30000 non-null int8 1 EDUCATION 30000 non-null int8 2 MARITAL_STATUS 30000 non-null int8 3 GENDER 30000 non-null int8
464 Часть III. Настройка на интерпретируемость 4 AGE_GROUP 30000 non-null int8 5 pay_status_1 30000 non-null int8 6 pay_status_2 30000 non-null int8 7 pay_status_3 30000 non-null int8 8 pay_status_4 30000 non-null int8 9 pay_status_5 30000 non-null int8 10 pay_status_6 30000 non-null int8 11 paid_pct_1 30000 non-null float64 12 paid_pct_2 30000 non-null float64 13 paid_pct_3 30000 non-null float64 14 paid_pct_4 30000 non-null float64 15 paid_pct_5 30000 non-null float64 16 paid_pct_6 30000 non-null float64 17 bill1_over_limit 30000 non-null float64 18 IS_DEFAULT 30000 non-null int8 19 _AGE 30000 non-null int16 20 _spend 30000 non-null int32 21 _tpm 30000 non-null int16 22 _ppm 30000 non-null int16 23 _RETAIL 30000 non-null int8 24 _URBAN 30000 non-null int8 25 _RURAL 30000 non-null int8 26 _PREMIUM 30000 non-null int8 27 _TREATMENT 30000 non-null int8 28 _LTV 30000 non-null float64 29 _CC_LIMIT 30000 non-null int32 30 _risk_score 30000 non-null float64 dtypes: float64(9), int16(3), int32(2), int8(17) memory usage: 3.2 MB Полученный результат проверку прошел. Все признаки являются числовыми без пропущенных значений, потому что мы использовали аргумент prepare=True. Все категориальные признаки имеют тип int8, т. к. они уже были закодированы. Словарь данных В наборе данных имеется 30 признаков, но мы не будем использовать их вместе, потому что 18 из них предназначены для упражнения по ослаблению систематического смещения, а остальные 12, начинающиеся с подчеркивания (_), — для упражнения по причинно-следственному выводу. Вскоре мы разделим данные на соответствующие наборы для каждого упражнения. Важно отметить, что признаки в нижнем регистре имеют отношение к истории транзакций каждого клиента, тогда
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 465 как признаки учетной записи клиента или целевые признаки указаны в верхнем регистре. В упражнении по ослаблению систематического смещения будут использоваться следующие признаки.  CC_LIMIT_CAT — порядковый; лимит кредитной карты (_CC_LIMIT), разделенный на восемь более или менее равномерно распределенных квартилей.  EDUCATION — номинальный; уровень образования клиента (0 — другой, 1 — сред- нее, 2 — бакалавриат, 3 — выпускник).  MARITAL_STATUS — номинальный; семейное положение клиента (0 — другое, 1 — холост, 2 — женат).  GENDER — номинальный; пол клиента (1 — мужчина, 2 — женщина).  AGE GROUP — двоичный; обозначает принадлежность клиента к привилегирован- ной возрастной группе (1 — привилегированная (26–47 лет), 0 — непривилегированная (любой другой возраст)).  pay_status_1, ..., pay_status_6 — порядковый; статус погашения за предыдущие 6 периодов с апреля, pay_status_6, по август 2005 года, pay_status_1 ( 1 — оплата в надлежащем порядке, 1 — оплата задерживается на 1 месяц, 2 — оплата задерживается на 2 месяца, ..., 8 — 8 месяцев задержка, 9 — 9 месяцев и более).  paid_pct_1, ..., paid_pct_6 — непрерывный; процент счета, причитающийся каж- дый месяц с апреля, paid_pct_6, по август 2005 года, paid_pct_1, который был оплачен.  bill1_over_limit — непрерывный; отношение последнего баланса в августе 2005 года к соответствующему кредитному лимиту.  IS_DEFAULT — двоичный; цель; допустил ли клиент дефолт или нет. Следующие признаки будут использоваться только в упражнении по причинноследственному выводу.  _AGE — непрерывный; возраст клиента в годах.  _spend — непрерывный; сколько было потрачено каждым клиентом в новых тайваньских долларах (New Taiwan Dollar, NT$).  _tpm — непрерывный; средние транзакции в месяц, совершенные клиентом с по- мощью кредитной карты за предыдущие 6 месяцев.  _ppm — непрерывный; средние покупки в месяц, совершенные клиентом с помо- щью кредитной карты за предыдущие 6 месяцев.  _RETAIL — двоичный; клиент обратился в банк самостоятельно, а не был направ- лен своим работодателем.  _URBAN — двоичный; это городской клиент.  _RURAL — двоичный; это сельский клиент.
466 Часть III. Настройка на интерпретируемость  _PREMIUM — двоичный; клиент относится к "премиум-классу". Клиенты премиум- класса получают предложения по возврату денежных средств и другие стимулы для расходов.  _TREATMENT — номинальный; вмешательство, или политика, предписанная каждо- му клиенту ( 1 — не является частью эксперимента, 0 — контрольная группа, 1 — сниженный кредитный лимит, 2 — план платежей, 3 — план платежей и сниженный кредитный лимит).  _LTV — непрерывный; результат вмешательства, который представляет собой пожизненную ценность1, рассчитанную в новых тайваньских долларах (NT$), с учетом кредитно-платежного поведения за предыдущие 6 месяцев.  _CC_LIMIT — постоянный; первоначальный лимит кредитной карты в новых тай- ваньских долларах (NT$), который был у клиента до экспериментальной процедуры. Банкиры ожидают, что этот признак окажет значительное влияние на результат экспериментальной процедуры.  _risk_score — непрерывный; балл риска, который банк рассчитал за 6 месяцев до этого для каждого клиента на основе соотношения счета кредитной карты к лимиту кредитной карты2. Он похож на признак bill1_over_limit, за исключением того, что это средневзвешенное значение за 6 месяцев истории платежей и рассчитано за 5 месяцев до выбора экспериментальной процедуры. Мы объясним признаки причинно-следственного вывода и их назначение чуть-чуть подробнее в соответствующем разделе. Тем временем давайте разложим признак _TREATMENT на его значения с помощью метода value_counts(), чтобы понять, как мы будем разбивать этот набор данных. Это делается следующим образом: ccdefault_all_df._TREATMENT.value_counts() Приведенная выше строка исходного кода выводит на экран следующее: -1 28904 3 274 2 274 1 274 0 274 Name: _TREATMENT, dtype: int64 Большинство наблюдений относятся к экспериментальной процедуре 1 , поэтому они не являются частью причинно-следственного вывода. Оставшиеся наблюдения были поделены поровну между тремя экспериментальными процедурами (1–3) и контрольной группой (0). Естественно, мы будем использовать эти четыре группы для упражнения по причинно-следственному выводу. Однако, поскольку кон- 1 Пожизненная ценность (lifetime value, LTV) — это оценка среднего дохода, который клиент будет генерировать в течение всего срока свого пребывания в качестве клиента. — Прим. перев. 2 То есть соотношение баланса к лимиту. Иными словами, сумма денег, которую клиент должен по кредитной карте, по сравнению с его кредитным лимитом. — Прим. перев.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 467 трольной группе экспериментальная процедура не была назначена, ее можно использовать в упражнении по ослаблению систематического смещения наряду с экспериментальной процедурой 1 . Не следует забывать о необходимости исключить клиентов, чье поведение подверглось манипуляциям в процессе ослабления систематического смещения. Весь смысл в том, чтобы предсказать клиентов, которые объявят дефолт с наибольшей вероятностью в "обычных деловых" условиях, пытаясь при этом уменьшить систематическое смещение. Подготовка данных Наш единственный шаг подготовки данных на текущий момент состоит в разбивке наборов данных, что можно легко сделать, взяв подмножества кадров данных pandas, используя столбец _TREATMENT. С помощью этого подмножества мы создадим один кадр данных для каждого упражнения: ослабления систематического смещения (ccdefault_bias_df) и причинно-следственного вывода (ccdefault_causal_df). Их можно увидеть в следующем фрагменте исходного кода: ccdefault_bias_df = ccdefault_all_df[ccdefault_all_df._TREATMENT < 1] ccdefault_causal_df = ccdefault_all_df[ccdefault_all_df._TREATMENT >= 0] Еще несколько шагов по подготовке данных будет сделано в рамках детального изучения, но сейчас мы готовы приступить к работе! Обнаружение систематического смещения В машинном обучении есть целый ряд источников систематического смещения. Как указано в главе 1, существуют обширные источники систематического смещения. Это, например, смещения, возникающие из уверенности, что представленные данные (всеохватывающие и структурные) истинны. Кроме того, существуют систематические смещения, коренящиеся в самих данных, например смещение вследствие отбора, вследствие изъятия, вследствие ассоциации и вследствие измерения. Наконец, существуют систематические смещения в сведениях, которые мы выводим из данных или моделей, с которыми следует проявлять осторожность, например систематическое смещение вследствие консерватизма, смещение вследствие значимости и фундаментальная ошибка атрибуции. В целях правильного распутывания столь большого числа уровней систематического смещения в этом примере, необходимо связать наши данные с данными переписи населения Тайваня в 2005 году и историческими данными о кредитовании, разбитыми по демографии. Затем, используя эти внешние наборы данных, проконтролировать условия контрактов по кредитным картам, а также пол, доход и другие демографические данные, чтобы выяснить, были ли молодые люди, в частности, ориентированы на кредитные карты с высокими процентами, на которые они не должны были претендовать. Нам также потребуется отследить набор данных до авторов и проконсультироваться с ними и экспертами в предметной области, чтобы изучить набор данных на предмет проблем с качеством данных, связанных со систематическим смещением. В идеале эти шаги необходимы для подтверждения ги-
468 Часть III. Настройка на интерпретируемость потезы смещения данных, но это уже монументальная задача, требующая рассуждений в нескольких главах. Поэтому, руководствуясь соображениями целесообразности, мы принимаем посылку этой главы за чистую монету. То есть из-за хищнической практики кредитования определенные возрастные группы были уязвимее к дефолту по кредитным картам не по своей вине. В том же духе мы также примем за истину качество набора данных. С учетом перечисленных предостережений это означает, что если мы обнаружим диспаритеты между возрастными группами в данных либо любой модели, выведенной из этих данных, то это можно приписать исключительно хищнической практике. Есть также два вида объективности, и они таковы:  объективность внутренней процедуры — речь идет о нейтральном или равном обращении. Этот термин трудно определить юридически, потому что он очень сильно зависит от контекста;  объективность исхода — речь идет исключительно об измерении объективных результатов. Эти два понятия не являются взаимоисключающими, т. к. обращение с клиентом может быть объективным, а исход необъективным, или наоборот. В этом примере предлагать кредитные карты с высокими процентами неквалифицированным клиентам было необъективным обращением. Тем не менее в данной главе мы сосредоточимся на объективности исхода. Когда мы обсуждаем тему систематического смещения в машинном обучении, нам следует учесть, что оно будет влиять на защищенные признаки, и в рамках этих признаков будут привилегированная и непривилегированная группы. Последняя представляет собой группу, на которую систематическое смещение влияет отрицательно. Кроме того, систематическое смещение проявляется самыми разными путями и, следовательно, обрабатывается следующим образом.  Представленность. Может иметься недостаточная представленность либо чрезмерная представленность непривилегированной группы. Модель будет усваивать об этой группе слишком мало либо слишком много по сравнению с другими.  Распределение. Разницы в распределении признаков между группами могут привести к тому, что модель создаст систематически смещенные ассоциации, которые могут повлиять на исходы модели прямо или косвенно.  Вероятность. Для классификационных задач расхождения в балансе классов между группами, такие как те, которые обсуждаются в главе 7, могут привести к тому, что модель начнет усваивать, будто одна группа имеет более высокую вероятность быть частью того или иного класса. Их можно легко наблюдать с помощью матриц путаницы или сравнения их классификационных метрик, таких как частоты ложноположительных или ложноотрицательных классификаций.  Гибрид — комбинация любого из предыдущих проявлений.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 469 Стратегии для любого проявления систематического смещения обсуждаются в разд. "Ослабление систематического смещения" далее, но тот вид, который мы рассматриваем в этой главе, относится к диспаритетам с вероятностью для нашего главного защищенного атрибута (_AGE). Мы будем наблюдать за ним с помощью следующих средств:  визуализация систематического смещения набора данных — наблюдение за диспаритетом в данных в отношении защищенного признака с помощью визуализаций;  квантифицирование систематического смещения набора данных — их из- мерение с помощью метрик объективности;  квантифицирование систематического смещения модели — мы натренируем классификационную модель и будем использовать другие метрики объективности, разработанные для моделей. Систематическое смещение модели можно визуализировать так, как мы уже делали в главе 7, или как мы будем делать в главе 12. Мы проведем анализ нескольких других визуализаций в разд. "Окончательная сборка!" далее в этой главе. Без дальнейших церемоний давайте перейдем к практической части этого раздела. Визуализирование систематического смещения набора данных Сами данные рассказывают о степени вероятности того, что одна группа принадлежит к положительному классу относительно другой. Если это категориальный признак, то указанные вероятности можно получить, разделив функцию value_counts() для положительного класса на всю численность. Например, для пола можно было бы сделать следующее: ccdefault_bias_df[ccdefault_bias_df.IS_DEFAULT==1].GENDER.value_counts() /\ ccdefault_bias_df.GENDER.value_counts() Приведенный фрагмент исходного кода производит следующий результат, который показывает, что у мужчин в среднем вероятность дефолта по их кредитной карте выше: 2 0.206529 1 0.241633 Name: GENDER, dtype: float64 Исходный код того, как это делается для непрерывного признака, немного сложнее. Рекомендуется использовать метод qcut() библиотеки pandas, чтобы сначала разделить признак на квартили, а затем использовать тот же подход, что и для категориальных признаков. К счастью, функция plot_prob_progression делает это за вас и выводит на график прогрессию вероятностей для каждого квартиля. Первый атрибут — это ряд, массив или список pandas с защищенным признаком (_AGE), а второй — то же самое, но для целевого признака (IS_DEFAULT). Затем мы выбираем число интервалов (x_intervals), которые устанавливаем в качестве квартилей (use_quartiles=True). Остальные атрибуты являются эстетическими: это метка, заго-
470 Часть III. Настройка на интерпретируемость ловок и добавление средней линии (mean_line). Соответствующий исходный код показан ниже: mldatasets.plot_prob_progression(\ ccdefault_bias_df._AGE, ccdefault_bias_df.IS_DEFAULT,\ x_intervals=8,\ use_quartiles=True, xlabel='Возраст', mean_line=True,\ title='Вероятность дефолта по кредитной карте по возрасту') Приведенный исходный код сгенерировал следующий результат на рис. 11.1, который показывает, как самые молодые (21–25) и самые пожилые (47–79), скорее всего, будут объявлять дефолт по кредитной карте. Все остальные группы представляют чуть более одного стандартного отклонения от среднего значения. Рис. 11.1. Вероятность дефолта по кредитной карте по признаку _AGE Самый молодой и самый пожилой квартили можно назвать непривилегированной группой, а все остальные — привилегированной группой. В целях обнаружения и ослабления необъективности лучше всего кодировать их как двоичный признак — как раз это мы и сделали с AGE_GROUP. Можно снова воспользоваться plot_prob_progression, но на этот раз с признаком AGE_GROUP вместо признака AGE, и мы заменим числа метками, которые нам будет легче интерпретировать. Соответствующий исходный код можно увидеть ниже: mldatasets.plot_prob_progression(\ ccdefault_bias_df.AGE_GROUP.replace({0:'21-25,48+',1:'26-47'}),\ ccdefault_bias_df.IS_DEFAULT,\ xlabel='Возрастная группа',\ title='Вероятность дефолта по кредитной карте по возрастной группе',\ mean_line=True)
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 471 Приведенный фрагмент сгенерировал следующий результат на рис. 11.2, в котором диспаритеты между обеими группами довольно очевидны. Рис. 11.2. Вероятность дефолта по кредитной карте по признаку AGE_GROUP Далее давайте внесем в рисунок пол (GENDER). Можно задействовать функцию plot_prob_contour_map, которая похожа на функцию plot_prob_progression, но в двух размерностях, с цветовым кодированием вероятностей вместо рисования линии. Итак, первые два атрибута — это признаки, которые мы хотим видеть на оси x (GENDER) и оси y (AGE_GROUP), а третьим является цель (IS_DEFAULT). Поскольку оба признака двоичные, лучше всего использовать аргумент plot_type='grid', а не contour. Соответствующий исходный код можно увидеть ниже: mldatasets.plot_prob_contour_map(\ ccdefault_bias_df.GENDER.replace({1:'Male',2:'Female'}),\ ccdefault_bias_df.AGE_GROUP.replace({0:'21-25,48+',1:'26-47'}),\ ccdefault_bias_df.IS_DEFAULT,\ xlabel='Пол',\ ylabel='Возрастная группа',\ title='Вероятность дефолта по полу/возрастной группе',\ annotate=True,\ plot_type='grid') Приведенный фрагмент генерирует следующий результат на рис. 11.3. Сразу видно, что наиболее привилегированной группой являются женщины в возрасте 26–47 лет, за которыми следуют их одногодки-мужчины с разницей примерно в 3–4%. То же самое происходит и с непривилегированной возрастной группой. Половая разница — интересное наблюдение, и мы могли бы выдвинуть ряд гипотез относительно того, почему женщины реже объявляют о своей финансовой несо-
472 Часть III. Настройка на интерпретируемость стоятельности. Они просто лучше справляются с долгами, или у мужчин есть другие проблемы, такие как семейные расходы или расходы на любимую девушку? Может быть, это связано с их семейным положением или образованием? Мы не будем углубляться в эти вопросы. Учитывая, что нам известно только о дискриминации по возрасту, будем использовать признак AGE_GROUP только в привилегированных группах, но оставляем признак GENDER защищенным атрибутом, который будет включаться в некоторые подлежащие отслеживанию метрики объективности. Говоря о метриках, далее мы займемся квантифицированием систематического смещение набора данных. Рис. 11.3. Вероятностная решетка дефолта по кредитной карте по признакам GENDER и AGE_GROUP Квантифицирование систематического смещения набора данных Ниже очерчены три вида метрик объективности.  Индивидуальная объективность — степень близости индивидуальных наблю- дений к их визави в данных. Этой цели могут служить метрики расстояния, такие как евклидово и манхэттенское расстояние.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 473  Групповая объективность — степень отдаленности меток или исходов друг от друга между группами в среднем. Они могут измеряться либо в данных, либо для модели.  И то и другое — несколько метрик измеряют энтропию или дисперсию путем учета неравенства как внутри группы, так и между группами, такие как индекс Тейла и коэффициент вариации. В этой главе мы сосредоточимся исключительно на метриках групповой объективности. Прежде чем мы рассчитаем метрики объективности, необходимо закончить несколько незавершенных шагов подготовки данных. Давайте убедимся, что в наборе данных, который мы будем использовать для ослабления систематического смещения (ccdefault_bias_df), есть только соответствующие столбцы, которые не начинаются с подчеркивания ("_"). С другой стороны, упражнение по причинно-следственному выводу будет включать только столбцы с подчеркиванием в именах плюс AGE_GROUP и IS_DEFAULT. Соответствующий фрагмент исходного кода показан ниже: cols_bias_l = ccdefault_all_df.columns[\ ~ccdefault_all_df.columns.str.startswith('_')].tolist() cols_causal_l = ['AGE_GROUP', 'IS_DEFAULT'] + \ ccdefault_all_df.columns[ccdefault_all_df.columns.str.startswith('_')].\ tolist() ccdefault_bias_df = ccdefault_bias_df[cols_bias_l] ccdefault_causal_df = ccdefault_causal_df[cols_causal_l] Кроме того, важнее квантифицировать систематическое смещение в тренировочных данных, т. к. именно на этих данных модель будет учиться, поэтому давайте продолжим и разделим данные на тренировочную и тестовую пары X и y. Разумеется, это делается после того, как было инициализировано случайное начальное значение, чтобы добиться некоторой воспроизводимости. Соответствующий фрагмент исходного кода можно увидеть ниже: rand = 9 os.environ['PYTHONHASHSEED']=str(rand) np.random.seed(rand) y = ccdefault_bias_df['IS_DEFAULT'] X = ccdefault_bias_df.drop(['IS_DEFAULT'], axis=1).copy() X_train, X_test, y_train, y_test = model_selection.train_test_split(\ X, y, test_size=0.25, random_state=rand) Несмотря на то что мы будем использовать данные pandas, которые мы только что разбили для обучения и верификации предсказательной способности модели, библиотека AIF360, которую мы будем использовать для этого упражнения, абстрагирует наборы данных в базовые классы. Эти классы включают данные, конвертированные в массив NumPy, и хранят атрибуты, связанные с объективностью. Для регрессии библиотека AIF360 имеет набор данных RegressionDataset, но в нашем примере двоичной классификации мы будем использовать набор данных
474 Часть III. Настройка на интерпретируемость BinaryLabelDataset. Его можно инициализировать с помощью кадра данных pandas признаками и метками (x_train.join(y_ train)). Затем нужно указать имена меток (label_names) и защищенные атрибуты (protected_attribute_names), и рекомендуется ввести значение благоприятной метки (favorable_label) и неблагоприятной метки (unfavorable_label), которое указывает библиотеке AIF360 на то, какие значения меток предпочтительнее учитывать при диагностике объективности. Каким бы запутанным это ни выглядело, положительность и, напротив, отрицательность в двоичной классификации относятся только к тому, что мы пытаемся предсказать, — к положительному классу, — а не к тому, является ли этот исход благоприятным. Соответствующий фрагмент исходного кода приводится ниже: train_ds = BinaryLabelDataset(df=X_train.join(y_train),\ label_names=['IS_DEFAULT'],\ protected_attribute_names=['AGE_GROUP', 'GENDER'],\ favorable_label=0, unfavorable_label=1) test_ds = BinaryLabelDataset(df=X_test.join(y_test),\ label_names=['IS_DEFAULT'],\ protected_attribute_names=['AGE_GROUP', 'GENDER'],\ favorable_label=0, unfavorable_label=1) Далее мы создаем массивы для непривилегированных групп (unprivileged_groups) и привилегированных групп (privileged_groups). Те, кто находится в AGE_GROUP=1, имеют меньшую вероятность дефолта, поэтому они привилегированные, и наоборот. Затем, используя их и абстрагированный набор данных для train(train_ds), можно инициализировать класс метрик. Это делается посредством библиотечного класса BinaryLabelDatasetMetric. Указанный класс имеет функции для вычисления нескольких метрик групповой объективности, оценивая только данные. Мы покажем три из них, а затем объясним их смысл. Соответствующий исходный код можно увидеть ниже: unprivileged_groups=[{'AGE_GROUP': 0}] privileged_groups=[{'AGE_GROUP': 1}] metrics_train_ds = BinaryLabelDatasetMetric(train_ds,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) print('Статистически паритетная разница (SPD): %.4f' %\ metrics_train_ds.statistical_parity_difference()) print('Неблагоприятное воздействие (DI): %.4f' %\ metrics_train_ds.disparate_impact()) print('Сглаженная эмпирическая дифференцированная объективность (SEDF): %.4f' %\ metrics_train_ds.smoothed_empirical_differential_fairness()) Приведенный выше фрагмент генерирует следующий результат: Статистически паритетная разница (SPD): Неблагоприятное воздействие (DI): -0.0437 0.9447 Сглаженная эмпирическая дифференцированная объективность (SEDF): 0.3514
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 475 Теперь давайте объясним смысл каждой метрики.  Статистически паритетная разница (statistical parity difference, SPD)3 — так называемая разница в средних. Это разница в средней вероятности благоприятных исходов между непривилегированной и привилегированной группами. Отрицательное число — плохо, положительное число — лучше, но число, близкое к нулю, всегда предпочтительнее. Разницу в средних вычисляют по следующей ниже формуле, где f — значение благоприятного класса: Pr Y  f | D  непривилегированный   Pr Y  f | D  привилегированный  .  Неблагоприятное воздействие (disparate impact, DI)4. Метрика неблагоприятно- го воздействия в точности похожа на метрику статистически паритетной разницы, за исключением того, что это соотношение, а не разность. И, как это водится с соотношениями, чем ближе к единице, тем лучше; всё, что меньше единицы, будет означать неблагоприятность, а всё, что больше единицы, — благоприятность. Формула показана ниже: Pr Y  f | D  непривилегированный  Pr Y  f | D  привилегированный  .  Сглаженная эмпирическая дифференцированная объективность (smoothed empirical differential fairness, SEDF). Эта метрика объективности является одной из многих новых метрик из статьи под названием "Межсекторальное определение объективности" ("An Intersectional Definition of Fairness"). В отличие от предыдущих двух метрик, она не ограничивается заранее определенными привилегированной и непривилегированной группами, но расширена за счет включения всех категорий в защищенных атрибутах — в данном случае четырех на рис. 11.3. Авторы статьи утверждают, что объективность усложняется, особенно когда имеется перекрестная таблица защищенных атрибутов. Это происходит из-за парадокса Симпсона, т. е. когда одна группа может быть благоприятной либо неблагоприятной в целом, но не при ее делении на перекрестные таблицы. Мы не будем вдаваться в математику, но такой метод учитывает эту возможность при измерении разумного уровня объективности в межсекторальных сценариях. При интерпретировании ноль представляет абсолютную объективность, и чем дальше от нуля, тем она менее объективна. Далее мы выполним квантификацию групповых метрик объективности для модели. 3 Статистически паритетная разница (statistical parity difference) измеряет разницу в том или ином результате, получаемом большинством и защищенными классами. Когда эта разница невелика, говорят, что классификатор имеет "статистический паритет", т. е. соответствует понятию объективности. — Прим. перев. 4 Неблагоприятное воздействие (disparate impact), или неравномерное воздействие — ненужное дискриминационное воздействие на защищенный класс, вызванное практикой или политикой (например, в сфере занятости или жилья). — Прим. перев.
476 Часть III. Настройка на интерпретируемость Квантифицирование систематического смещения модели Прежде чем рассчитывать метрики, нам нужно натренировать модель. С этой целью мы инициализируем классификатор LightGBM (LGBMClassifier) оптимальными гиперпараметрами (lgb_params). Они уже были настроены за нас гиперпараметрически (подробнее об этом см. в главе 12). Обратите внимание, что эти параметры включают scale_pos_weight, который предназначен для взвешивания классов. Поскольку мы имеем дело с несбалансированной классификационной задачей, этот параметр является важным и необходим для обучения классификатора с учетом чувствительности к стоимости, штрафуя одну форму ошибочной классификации по сравнению с другой. После инициализации классификатора выполняется его подгонка (fit), а затем оценивание с помощью evaluate_class_mdl, возвращающего словарь с метриками предсказательной результативности, которые можно сохранить в словаре моделей (cls_mdls). Соответствующий фрагмент исходного кода приведен ниже: cls_mdls = {} lgb_params = {'learning_rate': 0.4, 'reg_alpha': 21,\ 'reg_lambda': 1, 'scale_pos_weight': 1.8} lgb_base_mdl = lgb.LGBMClassifier(random_seed=rand, max_depth=6,\ num_leaves=33, **lgb_params) lgb_base_mdl.fit(X_train, y_train) cls_mdls['lgb_0_base'] = mldatasets.evaluate_class_mdl(lgb_base_mdl,\ X_train, X_test, y_train, y_test,\ plot_roc=False, plot_conf_matrix=True,\ show_summary=True, ret_eval_dict=True) Рис. 11.4. Оценивание базовой модели LightGBM
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 477 Приведенный выше фрагмент генерирует рис. 11.4. Параметр scale_pos_weight обеспечивает более здоровый баланс между ложноположительными классификациями в правом верхнем углу и ложноотрицательными классификациями в левом нижнем углу. В результате точность и полнота не слишком далеки друг от друга. Для подобного рода задачи мы предпочитаем высокую точность, потому что хотим максимизировать истинноположительные классификации, однако, не за счет больших затрат на полноту, поэтому баланс между ними еще более важен. В гиперпараметрической настройке полезно использовать балл F1 и коэффициент корреляции Мэтьюса (Matthews correlation coefficient, MCC). Оценивание базовой модели LightGBM показано на рис. 11.4. Далее давайте вычислим метрики объективности для модели. Для этого необходимо сделать "глубокую" копию (deepcopy=True) набора данных AIF360, но мы изменим метки и оценки, чтобы они соответствовали предсказаниям нашей модели. Функция compute_aif_metrics задействует класс ClassificationMetric библиотеки AIF360, чтобы сделать для модели то, что библиотечный класс BinaryLabelDatasetMetric делал для набора данных. Однако он не взаимодействует с моделью напрямую. Он вычисляет объективность, используя изначальный набор данных (test_ds) и модифицированный модельными предсказаниями (test_pred_ds). Функция compute_aif_metrics создает словарь с несколькими предварительно рассчитанными метриками (metrics_ test_dict) и классом метрик (metrics_test_cls), которые можно использовать для получения метрик по одной за раз. Ниже приведен соответствующий фрагмент исходного кода: test_pred_ds = test_ds.copy(deepcopy=True) test_pred_ds.labels = cls_mdls['lgb_0_base']['preds_test'].reshape(-1,1) test_pred_ds.scores = cls_mdls['lgb_0_base']['probs_test'].reshape(-1,1) metrics_test_dict, metrics_test_cls = mldatasets.compute_aif_metrics(\ test_ds, test_pred_ds,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) cls_mdls['lgb_0_base'].update(metrics_test_dict) print('Статистически паритетная разница (SPD): %.4f' %\ metrics_test_cls.statistical_parity_difference()) print('Неблагоприятное воздействие (DI): %.4f' %\ metrics_test_cls.disparate_impact()) print('Средняя разница шансов (AOD): %.4f' %\ metrics_test_cls.average_odds_difference()) print('Разница в равных возможностях (EOD): %.4f' %\ metrics_test_cls.equal_opportunity_difference()) print('Усиление смещения дифференцированной объективности (DFBA): %.4f' %\ metrics_test_cls.differential_fairness_bias_amplification())
478 Часть III. Настройка на интерпретируемость Приведенный выше фрагмент генерирует следующий результат: Статистически паритетная разница (SPD): Неблагоприятное воздействие (DI): -0.0679 0.9193 Средняя разница шансов (AOD): -0.0550 Разница в равных возможностях (EOD): -0.0265 Усиление смещения дифференцированной объективности (DFBA): 0.2328 Теперь, отложив в сторону метрики, смысл которых мы уже объяснили, давайте объясним смысл других.  Средняя разница шансов (average odds difference, AOD) — разница между частотой ложноположительных классификаций (FPR), усредненная разницей между частотой ложноотрицательных классификаций (FNR) как для привилегированной ( D  п ), так и для непривилегированной ( D  нп ) групп. Отрицательность означает, что для непривилегированной группы существует неблагоприятная ситуация, при этом чем ближе значение к нулю, тем лучше. Соответствующая формула показана ниже: 1  FPRD  нп  FPRD  п   TPRD  нп  TPRD  п   . 2  Разница в равных возможностях (equal opportunity difference, EOD) — это только истинноположительные частотные (TPR) разницы AOD, поэтому данная метрика полезна для измерения возможности лишь для частот истинноположительных классификаций (TPR). Как и в случае с AOD, отрицательный результат подтверждает неблагоприятность для непривилегированной ( D  нп ) группы, и чем ближе значение к нулю, тем лучше. Соответствующая формула показана ниже: TPRD  нп  TPRD п .  Усиление смещения дифференцированной объективности (differential fairness bias amplification, DFBA). Эта метрика взята из той же статьи, что и сглаженная эмпирическая дифференцированная объективность (SEDF), и она тоже имеет ноль в качестве базового уровня объективности и является межсекторальной. Однако она служит мерой разницы в необъективности лишь пропорционально между моделью и данными в явлении, именуемом усилением (амплификацией) систематического смещения. Другими словами, ее значение показывает степень, с которой модель увеличивает необъективность по сравнению с изначальными данными. Если сравнить метрики SPD и DI модели с метриками данных, то они действительно хуже. В этом нет ничего удивительного, потому что этого и следовало ожидать, поскольку усвоенные моделью представления, как правило, усиливают систематическое смещение. Это можно подтвердить с помощью метрик DFBA. Что касается AOD и EOD, то они, как правило, находятся в той же окрестности, что и метрики SPD, но в идеале метрика EOD существенно ближе к нулю, чем метрика AOD, по-
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 479 тому что в данном примере нас больше интересуют частоты истинноположительных классификаций (TPR). Далее мы рассмотрим методы ослабления систематического смещения модели. Ослабление систематического смещения Систематическое смещение можно ослаблять на трех разных стадиях с помощью методов, которые оперируют на этих отдельных стадиях.  Предварительная обработка. Это процедура по выявлению и устранению сис- тематического смещения в тренировочных данных перед обучением модели. Использующие предобработку методы имеют то преимущество, что они устраняют систематическое смещение в источнике. С другой стороны, любое необнаруженное систематическое смещение будет моделью.  Промежуточная обработка. Эти методы уменьшают систематическое смеще- ние во время обучения модели и, следовательно, сильно зависят от ее вида, в отличие от методов предобработки и постобработки. Они также требуют регулировки гиперпараметров с целью калибровки метрик объективности.  Последующая обработка. Эти методы ослабляют систематическое смещение во время процесса выведения модельного результата. В главе 7 мы затронули тему использования инструмента WIT для выбора правильных порогов (см. рис. 7.14), и мы скорректировали их вручную с целью достижения паритета с ложноположительными классификациями. Как и тогда, методы постобработки направлены на выявление и исправление объективности непосредственно в результатах, но характер корректировок, которые необходимо внести, будет зависеть от того, какие метрики наиболее важны для вашей задачи. У них есть преимущество в том, что они могут бороться с необъективностью результата там, где она может оказать наибольшее влияние, но, поскольку она не связана с остальной частью разработки модели, она может исказить ситуацию. Обратите внимание, что методы ослабления систематического смещения могут снижать предсказательную результативность, поэтому нередко приходится идти на компромисс. Они могут быть противоположными целями, особенно в тех случаях, когда данные отражают систематически смещенную истину. Вместо этого можно решить перенацелиться на более подходящую истину: подлинную — ту, которую мы хотим, а не ту, которая у нас есть. В этом разделе будет дано объяснение нескольким методам для каждой стадии, но будут имплементированы и оценены только два для каждой. Кроме того, хотя мы не будем делать этого в данной главе, но для максимального ослабления можно комбинировать разные методы: например, можно использовать метод предобработки с целью устранения систематического смещения из данных, затем натренировать модель с их помощью и, наконец, применить метод постобработки с целью устранения систематического смещения, добавленного моделью.
480 Часть III. Настройка на интерпретируемость Методы ослабления систематического смещения стадии предварительной обработки Вот несколько наиболее важных специфичных для конкретных данных методов стадии предобработки для устранения систематического смещения:  Неосведомленность (unawareness) — так называемое подавление. Самый про- стой способ устранить систематическое смещение — исключить смещенные признаки из набора данных, но этот подход наивен, т. к. вы исходите из того, что смещение содержится строго в этих признаках.  Конструирование признаков (feature engineering:) — так называемая выра- ботка признаков. Иногда непрерывные признаки улавливают систематическое смещение, потому что существует очень много разреженных областей, где модель может заполнять пробелы допущениями или усваивать знания у выбросов. Они могут делать то же самое со взаимодействиями. Конструирование признаков позволяет устанавливать ограждения. Мы обсудим эту тему в главе 12.  Балансировка (balancing) — так называемые перевыборка, или ресэмплинг. Сами по себе проблемы с представлением относительно легко решаются путем балансировки набора данных. Библиотека XAI (https://github.com/EthicalML/xai) имеет функцию balance, которая делает это путем случайной понижающей и повышающей выборки представлений групп. Понижающую выборку, или недостаточную выборку, принято называть взятием выборки, т. е. извлечением определенного процента наблюдений, тогда как повышающая, или избыточная, выборка создает определенное число случайных дубликатов. Некоторые стратегии не дублируют, а выполняют повышающую синтетическую выборку, например техника избыточной выборки синтетического меньшинства (synthetic minority oversampling technique, SMOTE)5. Однако необходимо предупредить, что, если у вас достаточно данных, то всегда предпочтительнее использовать понижающую, нежели повышающую выборку. Лучше всего не использовать лишь стратегию балансировки, если есть другие возможные проблемы со систематическим смещением.  Переразметка (relabeling) — так называемое переформатирование или масси- рование меток. Это алгоритм изменения меток, касающихся наблюдений, которые выглядят наиболее смещенными, в результате которого данные переформатируются путем их ранжирования. Обычно это выполняется с помощью классификатора на основе наивного Байеса, и для поддержания распределения классов он не только повышает ранг некоторых наблюдений, но и понижает равное число других.  Перевесовка (reweighing). Этот метод ранжирует наблюдения аналогично пере- разметке, но вместо того, чтобы переворачивать их метки, он выводит вес каж5 Метод избыточного взятия выборок, который позволяет генерировать синтетические выборки для миноритарных категорий. — Прим. перев.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 481 дой метки, который затем можно имплементировать в процессе усвоения. Подобно применению весов классов к каждому классу, веса образцов применяются к каждому наблюдению или образцу. Многие регрессоры и классификаторы, включая LGBMClassifier, поддерживают веса образцов. Несмотря на то что в техническом плане перевесовка не затрагивает данные и применяемое к модели решение, этот метод относится к предобработке, потому что мы обнаружили систематическое смещение в данных.  Устранитель неблагоприятного воздействия (disparate impact remover). Авторы этого метода очень тщательно соблюдали юридические определения термина систематического смещения и поддерживали целостность данных без изменения меток или защищенных атрибутов. В нем имплементирован процесс восстановления, который пытается устранить систематическое смещение в оставшихся признаках. Этот процесс отлично подходит к ситуациям, когда мы подозреваем, что именно там находится бóльшая часть систематического смещения, т. е. признаки сильно коррелируют с защищенными атрибутами, но он не устраняет систематическое смещение в других местах. В любом случае, он является неплохим базовым уровнем, который можно использовать для понимания того, какая часть систематического смещения связана с незащищенными признаками.  Усвоение объективных представлений (learning fair representations). Этот ме- тод задействует каркас антагонистического усвоения знаний. Есть генератор (автокодировщик), который создает представления данных, исключая защищенный атрибут, и критик, цель которого состоит в том, чтобы усвоенные представления в привилегированной и непривилегированной группах были как можно ближе.  Оптимизированная предобработка для предотвращения дискриминации (optimized preprocessing for discrimination prevention). Этот метод производит преобразования путем математической оптимизации данных в таком ключе, чтобы сохранялись совокупные распределения значений вероятности. В то же самое время корреляция между защищенными атрибутами и целью аннулируется. Результатом этого процесса являются данные, которые слегка искажены, чтобы устранить их систематическое смещение. Учитывая, что существует столь много методов предобработки, в этой главе мы будем использовать только два из них. Тем не менее если вы заинтересованы в использовании тех, которые мы не будем рассматривать, то они доступны в библиотеке AIF360, и о них можно прочитать в документации (http://aif360.mybluemix.net/). Метод перевесовки Метод перевесовки (Reweighing) довольно прост в имплементировании. Он инициализируется путем указания групп, затем выполняется его подгонка (fit) под данные и преобразование (transform) данных, как при использовании любого кодировщика
482 Часть III. Настройка на интерпретируемость или нормировщика библиотеки scikit-learn. Для тех, кто не знаком с методом fit(), сообщим, что алгоритм узнает, как преобразовывать переданные ему данные, а в методе transform() используется то, что было усвоено для их преобразования. Соответствующий фрагмент исходного кода приведен ниже: reweighter = Reweighing(unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) reweighter.fit(train_ds) train_rw_ds = reweighter.transform(train_ds) Преобразование, выведенное в результате этого процесса, не изменяет данные, а создает веса для каждого наблюдения. Библиотека AIF360 оборудована возможностями вносить эти веса в расчеты объективности, поэтому можно использовать библиотечный класс BinaryLabelDatasetMetric, как и раньше, для вычисления разных метрик. Соответствующий фрагмент исходного кода показан ниже: metrics_train_rw_ds = BinaryLabelDatasetMetric(train_rw_ds,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) print('Статистически паритетная разница (SPD): %.4f' %\ metrics_train_rw_ds.statistical_parity_difference()) print('Неблагоприятное воздействие (DI): %.4f' %\ metrics_train_rw_ds.disparate_impact()) print('Сглаженная эмпирическая дифференцированная объективность (SEDF): %.4f' %\ metrics_train_rw_ds.smoothed_empirical_differential_fairness()) Приведенный выше исходный код печатает следующее: Статистически паритетная разница (SPD): Неблагоприятное воздействие (DI): -0.0000 1.0000 Сглаженная эмпирическая дифференцированная объективность (SEDF): 0.1942 Веса оказывают идеальный эффект на SPD и DI, делая их абсолютно объективными с точки зрения этих метрик. Однако обратите внимание, что метрика SEDF лучше, чем раньше, но не равна нулю. Это обусловлено тем, что привилегированная и непривилегированная группы относятся только к защищенному атрибуту AGE_GROUP, а не к GENDER. SEDF — это мера межсекторальной объективности, которая при перевесовке не учитывается. Можно было бы подумать, что добавление весов в наблюдения будет сказываться на предсказательной результативности отрицательно. Однако этот метод был разработан для поддержания баланса. В невзвешенном наборе данных все наблюдения имеют вес, равный единице, и, следовательно, среднее значение всех весов равно единице. В то время как перевесовка изменяет веса наблюдений, среднее значение по-прежнему приближенно равно единице. Это можно проверить, взяв абсолютную разницу в среднем значении атрибута instance_weights между изначальным набором
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 483 данных и подвергнутом перевесовке. Она должна быть бесконечно малой. Соответствующий фрагмент исходного кода приведен ниже: np.abs(train_ds.instance_weights.mean() -\ train_rw_ds.instance_weights.mean()) < 1e-6 И как же применять instance_weights, вы спросите? В методе fit() многих модельных классов содержится менее известный атрибут под названием sample_weight. Нужно просто подключить эту переменную в указанный атрибут, и во время обучения он будет учиться на наблюдениях, руководствуясь соответствующими весами. Этот подход показан в следующем фрагменте исходного кода: lgb_rw_mdl = lgb.LGBMClassifier(\ random_seed=rand, max_depth=6, num_leaves=33, **lgb_params) lgb_rw_mdl.fit(X_train, y_train, sample_weight=train_rw_ds.instance_weights) Эту модель можно оценить, как и в случае с базовой моделью, с помощью функции evaluate_class_mdl. Однако, когда мы рассчитаем метрики объективности с помощью функции compute_aif_metrics, мы сохраним их в словаре моделей. Вместо того чтобы рассматривать результаты каждого метода по отдельности, будем сравнивать их в конце раздела. Взгляните на следующий фрагмент исходного кода: cls_mdls['lgb_1_rw'] = mldatasets.evaluate_class_mdl(\ lgb_rw_mdl, train_rw_ds.features,\ X_test, train_rw_ds.labels, y_test,\ plot_roc=False, plot_conf_matrix=True,\ show_summary=True, ret_eval_dict=True) test_pred_rw_ds = test_ds.copy(deepcopy=True) test_pred_rw_ds.labels = cls_mdls['lgb_1_rw']['preds_test'].reshape(-1,1) test_pred_rw_ds.scores = cls_mdls['lgb_1_rw']['probs_test']. reshape(-1,1) metrics_test_rw_dict, _ = mldatasets.compute_aif_metrics(\ test_ds, test_pred_rw_ds,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups)\ cls_mdls['lgb_1_rw'].update(metrics_test_rw_dict) Приведенный фрагмент выводит на экран матрицу путаницы и метрики результативности, как показано на рис. 11.5. Если сравнить рис. 11.5 и 11.4, то можно сделать вывод, что между перевзвешенной и базовой моделью нет большой разницы в предсказательной результативности. Этот результат был ожидаемым, но все равно его неплохо подтвердить. Некоторые методы ослабления систематического смещения влияют на предсказательную результативность отрицательно, но перевесовка в их число не входит. Если на то пошло, в их число не входит и метрика неблагоприятного воздействия (DI), о которой мы поговорим далее!
484 Часть III. Настройка на интерпретируемость Рис. 11.5. Оценивание перевзвешенной модели LightGBM Устранитель неблагоприятного воздействия В центре внимания этого метода находится систематическое смещение, не расположенное в защищенном атрибуте (AGE_GROUP), поэтому указанный признак придется по ходу удалить. Для этого понадобится его индекс — другими словами, позиция, которую он занимает в списке столбцов. Эту позицию (protected_index) можно сохранить в качестве переменной вот так: protected_index = train_ds.feature_names.index('AGE_GROUP') Устранитель неблагоприятного воздействия не является непараметрическим. Он требует коррекционного уровня от нуля до единицы, поэтому нужно найти оптимальный. С этой целью можно прокрутить массив с разными значениями коррекционного уровня (levels) в цикле, инициализировать библиотечный класс DisparateImpactRemover каждым уровнем и выполнить подгонку и преобразование (fit_transform) данных, которое устранит систематическое смещение из данных. Однако затем мы тренируем модель без защищенного атрибута и используем библиотечный класс BinaryLabelDatasetMetric для диагностирования disparate_impact. Помните, что метрика неблагоприятного воздействия (DI) — это соотношение, поэтому она может быть больше или меньше единицы, а оптимальное неблагоприятное воздействие находится максимально близко к единице. Следовательно, перебирая разные коррекционные уровни, мы будем постоянно сохранять ту модель, метрика неблагоприятного воздействия которой находится максимально близко к единице. Мы также будем добавлять метрики неблагоприятного воздействия в массив для последующего использования. Взгляните на следующий фрагмент исходного кода: di = np.array([]) train_dir_ds = None
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 485 test_dir_ds = None lgb_dir_mdl = None X_train_dir = None X_test_dir = None levels = np.hstack([np.linspace(0., 0.1, 41), np.linspace(0.2, 1, 9)]) for level in tqdm(levels): di_remover = DisparateImpactRemover(repair_level=level) train_dir_ds_i = di_remover.fit_transform(train_ds) test_dir_ds_i = di_remover.fit_transform(test_ds) X_train_dir_i = np.delete(train_dir_ds_i.features, protected_index, axis=1) X_test_dir_i = np.delete(test_dir_ds_i.features, protected_index, axis=1) lgb_dir_mdl_i = lgb.LGBMClassifier(random_seed=rand, max_depth=5,\ num_leaves=33, **lgb_params) lgb_dir_mdl_i.fit(X_train_dir_i, train_dir_ds_i.labels) test_dir_ds_pred_i = test_dir_ds_i.copy() test_dir_ds_pred_i.labels = lgb_dir_mdl_i.predict(X_test_dir_i) metrics_test_dir_ds = BinaryLabelDatasetMetric(\ test_dir_ds_pred_i,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) di_i = metrics_test_dir_ds.disparate_impact() if (di.shape[0]==0) or (np.min(np.abs(di-1)) >= abs(di_i-1)): print(abs(di_i-1)) train_dir_ds = train_dir_ds_i test_dir_ds = test_dir_ds_i X_train_dir = X_train_dir_i X_test_dir = X_test_dir_i lgb_dir_mdl = lgb_dir_mdl_i di = np.append(np.array(di), di_i) Для того чтобы наблюдать метрику неблагоприятного воздействия на разных коррекционных уровнях, можно применить приведенный далее исходный код, и если вы хотите увеличить область, внутри которой находится наилучшее значение указанной метрики, следует просто раскомментировать строку с методом xlim(): plt.plot(levels, di, marker='o') plt.ylabel('Неблагоприятное воздействие (DI)', fontsize=14) plt.xlabel('Коррекционный уровень', fontsize=14) #plt.xlim(0,0.1) Приведенный выше исходный код генерирует следующий результат на рис. 11.6. Как можно судить по этому результату, оптимальный коррекционный уровень на-
486 Часть III. Настройка на интерпретируемость ходится где-то между 0 и 0,1, потому что именно там он максимально приближается к единице. Рис. 11.6. Метрика неблагоприятного воздействия (DI) на разных коррекционных уровнях устранителя неблагоприятного воздействия Теперь давайте оценим наилучшую модель DI с помощью функции evaluate_class_mdl и вычислим метрики объективности (compute_aif_metrics). На этот раз мы даже не будем строить матрицу путаницы, но сохраним все результаты в словаре cls_mdls для последующего рассмотрения. Соответствующий фрагмент исходного кода приведен ниже: cls_mdls['lgb_1_dir'] = mldatasets.evaluate_class_mdl(\ lgb_dir_mdl, X_train_dir,\ X_test_dir, train_dir_ds.labels,\ test_dir_ds.labels, plot_roc=False,\ plot_conf_matrix=False, show_summary=False, ret_eval_dict=True) test_pred_dir_ds = test_ds.copy(deepcopy=True) test_pred_dir_ds.labels = cls_mdls['lgb_1_dir']['preds_test'].reshape(-1,1) metrics_test_dir_dict, _ = mldatasets.compute_aif_metrics(\ test_ds, test_pred_dir_ds, unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) cls_mdls['lgb_1_dir'].update(metrics_test_dir_dict) Следующим звеном в цепочке после данных является модель, поэтому, даже если вы устраняете систематическое смещение данных, модель вносит собственное систематическое смещение. Поэтому имеет смысл тренировать модели, которые оснащены для работы с ним, и это то, что мы будем учиться делать дальше!
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 487 Методы ослабления систематического смещения стадии промежуточной обработки Вот несколько наиболее важных модельно-специфических методов стадии промежуточной обработки для ослабления систематического смещения.  Чувствительность к стоимости обучения (cost-sensitive training). Этот метод уже встраивается в каждую тренируемую в этой главе модель LightGBM с помощью параметра scale_pos_weight. Обычно он используется в несбалансированных классификационных задачах и просто рассматривается как средство повышения точности для миноритарных классов. Однако, учитывая, что дисбалансы с классами, как правило, благоприятствуют одним группам в ущерб другим, этот метод также ослабляет систематическое смещение. Он может встраиваться в качестве весов классов или путем создания прикладной функции потерь. Его имплементация будет варьировать в зависимости от модельного класса и от стоимостей, которые связаны со систематическим смещением. Если они растут линейно вместе с ошибочной классификацией, то будет достаточно взвешивания классов, но в противном случае рекомендуется использовать прикладную функцию потерь.  Ограничения (constraints). Многие модельные классы поддерживают монотон- ные и интеракционные ограничения, и библиотека TensorFlow Lattice (TFL) предлагает более продвинутые ограничения прикладной формы. Они обеспечивают ограничение взаимосвязей между признаками и целями определенным шаблоном, размещая ограждения на модельном уровне. Потребность в их задействовании обусловлена рядом причин, но главная из них — это ослабление систематического смещения. Мы обсудим эту тему в главе 12.  Регуляризатор устранителя предубеждений (prejudice remover regularizer). Этот метод определяет предубеждение как статистическую зависимость между чувствительной и целевой переменными. Однако целевая задача этого метода состоит в том, чтобы минимизировать косвенное предубеждение, исключающее предубеждение, которого можно избежать, просто удалив чувствительную переменную. Поэтому указанный метод начинается с ее квантифицирования с помощью индекса предубеждения (prejudice index, PI), т. е. взаимной информации между целевой и чувствительной переменными. Кстати, мы рассмотрели взаимную информацию в главе 10. Затем, наряду с L2, индекс предубеждения встраивается в прикладной член регуляризации. Теоретически, с помощью PI-ориентированного регуляризатора выполнять регуляризацию может любой модельный классификатор, но пока что в единственной имплементации используется логистическая регрессия.  Классификатор с использованием махинаций с объективностью (gerry fair classifier). Этот метод навеян идеей предвыборных махинаций с объективно-
488 Часть III. Настройка на интерпретируемость стью в двухпартийной системе6, которая имеет видимость объективности в одной группе, но начисто лишена объективности при делении на подгруппы. В алгоритме задействуется подход, основанный на теории игр с использованием фиктивной игры, при которой между учеником и аудитором происходит игра с нулевой суммой. Ученик минимизирует ошибку предсказания и штрафной член, основанный на агрегатной объективности. Аудитор делает еще один шаг вперед, штрафуя ученика, основываясь на худших исходах для подгруппы, с которой обращаются наиболее необъективно. Цель игры состоит в достижении равновесия Нэша, которое достигается, когда два некооперативных игрока с возможно противоречащими целями достигают решения, которое частично удовлетворяет обоих. В этом случае ученик получает минимальную ошибку предсказания и агрегатную необъективность, а аудитор — минимальную необъективность подгруппы. Имплементация этого метода является модельно-агностической.  Антагонистическое устранение систематического смещения (adversarial debiasing). Подобно махинациям с объективностью, в антагонистическом устранении систематического смещения задействуются два противоборствующих субъекта, но на этот раз это две нейронные сети: предсказатель и антагонист. Мы максимизируем способность предсказателя предсказывать цель, одновременно минимизируя способность антагониста предсказывать защищенный признак, тем самым увеличивая равенство шансов между привилегированной и непривилегированной группами.  Экспоненцированная редукция градиента (exponentiated gradient reduction). Этот метод автоматизирует чувствительную к стоимости оптимизацию, сводя ее к последовательности таких задач и используя объективностные ограничения в отношении защищенных атрибутов, таких как демографический паритет или уравненные шансы. Он является модельно-агностическим, но ограничен только двоичными классификаторами, совместимыми с библиотекой scikit-learn. С учетом такого большого числа методов промежуточной обработки, в этой главе мы будем использовать только два из них. Тем не менее если вы заинтересованы в использовании тех, которые мы не будем рассматривать, они доступны в библиотеке и документации AIF360. 6 Термин "махинация с объективностью" (gerry fair или fairness gerrymandering) — это практика проведения границ избирательных округов таким образом, чтобы дать одной политической партии необъективное (несправедливое) преимущество перед ее соперниками (партийное мошенничество) или ослабить право голоса представителей этнических или языковых меньшинств (расовое мошенничество). Этот термин происходит от названия правительства Элбриджа Джерри из Массачусетса, администрация которого в 1812 г. приняла закон, определяющий новые сенаторские округа штата. Закон объединил голоса федералистской партии в нескольких округах и, таким образом, дал непропорционально большое представительство демократам-республиканцам. Указанный термин обозначает случаи, когда классификатор выглядит справедливым для каждой отдельной группы, но сильно нарушает ограничение объективности для одной или нескольких структурированных подгрупп, определенных над защищенными атрибутами. — Прим. перев.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 489 Устранитель предубеждения Устранитель предубеждения, имплементированный в библиотечном классе PrejudiceRemover, — это специальная имплементация логистической регрессии. Указанный метод инициализируется скоростью усвоения (eta), конкретным чувствительным атрибутом и классовым атрибутом. Затем выполняется его подгонка (fit()). Этот метод можно увидеть в следующем фрагменте исходного кода: log_pr_mdl = PrejudiceRemover(eta=1.0, sensitive_attr='AGE_GROUP',\ class_attr='IS_DEFAULT') log_pr_mdl.fit(train_ds) Можно применить функцию predict, чтобы получить тренировочное и тестовое предсказания, а затем задействовать функции evaluate_class_metrics_mdl и compute_aif_metrics, чтобы получить, соответственно, метрику предсказательной результативности и метрику объективности. Мы помещаем обе метрики в словарь cls_mdls, как показано в следующем фрагменте исходного кода: train_pred_pr_ds = log_pr_mdl.predict(train_ds) test_pred_pr_ds = log_pr_mdl.predict(test_ds) cls_mdls['log_2_pr'] = mldatasets.evaluate_class_metrics_mdl( log_pr_mdl, train_pred_pr_ds.labels,\ test_pred_pr_ds.scores, test_pred_pr_ds.labels, y_train, y_test) metrics_test_pr_dict, _ = mldatasets.compute_aif_metrics( test_ds, test_pred_pr_ds, unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) cls_mdls['log_2_pr'].update(metrics_test_pr_dict) Далее мы узнаем о более модельно-агностическом методе промежуточной обработки, в котором учитывается межсекторальность. Классификатор с использованием махинаций с объективностью Классификатор с использованием махинаций с объективностью является частично модельно-агностическим. Он поддерживает только линейные модели, опорновекторные машины (support vector machines, SVM), ядерную регрессию и деревья решений. Библиотечный класс GerryFairClassifier инициализируется путем определения силы регуляризации (C), аппроксимации объективности для ранней остановки (gamma), детализации (printflag), максимального числа итераций (max_iters), модели (predictor) и применяемого понятия объективности (fairness_def). Используемым нами понятием объективности будет "FN", т. е. ложноотрицательные классификации, для вычисления взвешенного диспаритета нарушений объективности. После инициализации классификатора нужно лишь выполнить его подгонку (fit) и задать раннюю терминацию (early_termination), чтобы останавливать его, если он не улучшился в пяти итерациях. Соответствующий фрагмент исходного кода показан ниже: dt_gf_mdl = GerryFairClassifier(C=100, gamma=.005, max_iters=50,\ fairness_def='FN', printflag=True,\
490 Часть III. Настройка на интерпретируемость predictor=tree.DecisionTreeRegressor(max_depth=3)) dt_gf_mdl.fit(train_ds, early_termination=True) Можно применить функцию predict, чтобы получить тренировочное и тестовое предсказания, а затем задействовать функции evaluate_class_metrics_mdl и compute_aif_metrics, чтобы получить, соответственно, метрику предсказательной результативности и метрику объективности. Мы помещаем обе метрики в словарь cls_mdls, как показано в следующем фрагменте исходного кода: train_pred_gf_ds = dt_gf_mdl.predict(train_ds, threshold=False) test_pred_gf_ds = dt_gf_mdl.predict(test_ds, threshold=False) cls_mdls['dt_2_gf'] = mldatasets.evaluate_class_metrics_mdl(\ dt_gf_mdl, train_pred_gf_ds.labels, None,\ test_pred_gf_ds.labels, y_train, y_test) metrics_test_gf_dict, _ = mldatasets.compute_aif_metrics( test_ds, test_pred_gf_ds,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) cls_mdls['dt_2_gf'].update(metrics_test_gf_dict) Следующим и последним звеном в цепочке после модели является выведение результата. Даже если устранить систематическое смещение данных и модели, всё равно может остаться некоторое систематическое смещение, отсюда имеет смысл уладить этот вопрос и на этом этапе, что мы и научимся делать дальше! Методы ослабления систематического смещения стадии последующей обработки Вот несколько наиболее важных специфичных для выведения результата методов стадии постобработки для ослабления систематического смещения.  Воздержание от предсказания (prediction abstention). Этот метод имеет много потенциальных преимуществ, таких как объективность, безвредность или контроль затрат, но всё будет зависеть от вашей задачи. В типичной ситуации модель возвращает все предсказания, даже с низкой степенью уверенности, т. е. предсказания, близкие к классификационному порогу, или когда модель возвращает интервалы уверенности, выходящие за пределы заданного порога. Когда дело касается объективности, то после изменения предсказания на "я не знаю" (I don’t know, IDK) в участках с низкой степенью уверенности, скорее всего, в качестве побочного эффекта модель станет более объективной, если мы будем оценивать метрики объективности, основываясь только на сделанных предсказаниях. Также есть возможность воздержаться от предсказания, используя метод промежуточной обработки. В статье "Предсказывай ответственно: повышение объективности, учась откладывать на потом" ("Predict Responsibly: Increasing Fairness by Learning to Defer") обсуждаются два подхода, которые позволяют это делать путем обучения модели понтировать (т. е. учиться предска-
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 491 зывать IDK)7 либо откладывать (т. е. предсказывать IDK, когда шансы на правильность ниже, чем мнение эксперта). В еще одной статье под названием "Полезность воздержания в двоичной классификации" ("The Utility of Abstaining in Binary Classification") задействуется каркас обучения с подкреплением под названием "знает то, что знает" (knows what it knows, KWIK), который осознает свои ошибки, но допускает воздержанность.  Постобработка с уравниванием шансов (equalized odds postprocessing) — так называемое выравнивание неодинакового обращения8. Этот метод обеспечивает, чтобы привилегированная и непривилегированная группы обращались с ошибочными классификациями одинаково, будь то ложноположительные или ложноотрицательные. Он находит оптимальные вероятностные пороги, с помощью которых изменение меток выравнивает шансы между группами.  Постобработка с калиброванным уравниванием шансов (calibrated equalized odds postprocessing). Вместо изменения меток этот метод изменяет оценки вероятности в таком ключе, чтобы они в среднем были равными. Это называется калибровкой. Однако указанное ограничение нельзя выполнять конкурентно для ложноположительных и ложноотрицательных классификаций, поэтому приходится предпочитать одно другому. Следовательно, он выгоден в тех случаях, когда полнота намного важнее точности либо наоборот, и есть выгоды от калибровки расчетных вероятностей.  Классифицирование с выбраковкой вариантов (reject option classification). В этом методе используется интуитивная идея о том, что предсказания, лежащие вблизи границы решения, как правило, являются наименее объективными. Затем он отыскивает оптимальную полосу вокруг границы решения, для которой перевертывание меток непривилегированной и привилегированной групп дает наиболее объективные исходы. В этой главе мы задействуем только два приведенных выше метода постобработки. Метод классифицирования с выбраковкой вариантов доступен в библиотеке и документации AIF360. Постобработка с уравниванием шансов Метод постобработки с уравниванием шансов (EqOddsPostprocessing) инициализируется группами, для которых мы хотим уравнять шансы, и случайным начальным значением (seed). Затем мы выполняем его подгонку (fit). Обратите внимание, что для этого требуются два набора данных: изначальный (test_ds), а затем набор данных с предсказаниями для нашей базовой модели (test_pred_ds). Метод fit() вы- 7 "Понтировать" (punt) означает выводить "я не знаю" (IDK), когда модель предпочитает не делать ни положительного, ни отрицательного предсказания. — Прим. перев. 8 Процесс принятия решений страдает от неодинакового обращения в отношении конкретного чувствительного признака (например, расы), если частоты ошибочной классификации различаются для групп людей, имеющих разные значения этого чувствительного признака (например, чернокожих и белых). — Прим. перев.
492 Часть III. Настройка на интерпретируемость числяет оптимальные вероятностные пороги. Затем метод predict() создает новый набор данных, в котором эти пороги изменили метки. Соответствующий фрагмент исходного кода приведен ниже: epp = EqOddsPostprocessing(privileged_groups=privileged_groups,\ unprivileged_groups=unprivileged_groups, seed=rand) epp = epp.fit(test_ds, test_pred_ds) test_pred_epp_ds = epp.predict(test_pred_ds) Можно задействовать функции evaluate_class_metrics_mdl и compute_aif_metrics, чтобы получить, соответственно, метрику предсказательной результативности и метрику объективности для вероятности равного соотношения (equal-proportion probability, EPP). Мы помещаем обе метрики в словарь cls_mdls. Соответствующий фрагмент исходного кода показан ниже: cls_mdls['lgb_3_epp'] = mldatasets.evaluate_class_metrics_mdl(\ lgb_base_mdl, cls_mdls['lgb_0_base']['preds_train'],\ test_pred_epp_ds.scores, test_pred_epp_ds.labels, y_train, y_test) metrics_test_epp_dict, _ = mldatasets.compute_aif_metrics(\ test_ds, test_pred_epp_ds, unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) cls_mdls['lgb_3_epp'].update(metrics_test_epp_dict) Далее мы изучим еще один метод постобработки. Его главное отличие заключается в том, что он не только изменяет предсказанные метки, но и калибрует баллы вероятности. Постобработка с калиброванным уравниванием шансов Метод постобработки с калиброванным уравниванием шансов (CalibratedEqOddsPostprocessing) имплементирован точно так же, как и простой метод уравнивания шансов, за исключением того, что в нем есть еще один важный атрибут (cost_constraint). Этот атрибут определяет ограничение, которое следует соблюдать, поскольку он не может сделать баллы объективными одновременно для частот ложноположительных и ложноотрицательных классификаций. Мы выбираем частоту ложноположительных классификаций, а затем выполняем подгонку (fit), предсказываем (predict) и оцениваем (evaluate), как это делалось для уравненных шансов. Соответствующий фрагмент исходного кода приведен ниже: cpp = CalibratedEqOddsPostprocessing(privileged_groups=privileged_groups,\ unprivileged_groups=unprivileged_groups,\ cost_constraint="fpr", seed=rand) cpp = cpp.fit(test_ds, test_pred_ds) test_pred_cpp_ds = cpp.predict(test_pred_ds) cls_mdls['lgb_3_cpp'] = mldatasets.evaluate_class_metrics_mdl(\ lgb_base_mdl, cls_mdls['lgb_0_base']['preds_train'],\ test_pred_cpp_ds.scores, test_pred_cpp_ds.labels,\ y_train, y_test)
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 493 metrics_test_cpp_dict, _ = mldatasets.compute_aif_metrics(\ test_ds, test_pred_cpp_ds,\ unprivileged_groups=unprivileged_groups,\ privileged_groups=privileged_groups) cls_mdls['lgb_3_cpp'].update(metrics_test_cpp_dict) Теперь, когда мы испробовали шесть методов ослабления систематического смещения, по два на каждой стадии, можно сравнить их друг с другом и базовой моделью! Окончательная сборка В целях сравнения метрик всех методов можно взять словарь (cls_mdls) и поместить его в кадр данных (cls_metrics_df). Нас интересуют только несколько метрик результативности и большинство зарегистрированных метрик объективности. Затем мы выводим на экран кадр данных, отсортированный по тестовой точности и со всеми метриками объективности, кодированными цветом. Соответствующий фрагмент исходного кода показан ниже: cls_metrics_df = pd.DataFrame.from_dict(cls_mdls, 'index')\ [['accuracy_train', 'accuracy_test', 'f1_test',\ 'mcc_test', 'SPD', 'DI', 'AOD', 'EOD', 'DFBA']] with pd.option_context('display.precision', 4): html = cls_metrics_df.sort_values(by='accuracy_test',\ ascending=False).style.background_gradient(\ cmap='plasma_r', low=0.3, high=1,\ subset=['SPD', 'AOD', 'EOD']).background_gradient(\ cmap='viridis_r', low=1, high=0.3,\ subset=['DI', 'DFBA']) html Приведенный фрагмент выводит на экран кадр данных на рис. 11.7. Рис. 11.7. Сравнение всех методов ослабления систематического смещения с разными метриками объективности На рис. 11.7 показано, что большинство методов сгенерировали модели, более объективные, чем базовая модель, для метрик статистически паритетной разницы
494 Часть III. Настройка на интерпретируемость (SPD), средней разницы шансов (AOD) и разницы в равных возможностях (EOD). Метод постобработки с калиброванным уравниванием шансов (lgb_3_cpp) был исключением, но у него была одна из лучших метрик усиления смещения дифференцированной объективности (DFBA). Обратите внимание, что указанный метод особенно хорош для достижения паритета для частоты ложноположительных (FP) или частоты ложноотрицательных (FN) классификаций при калибровке баллов, но ни одна из этих метрик объективности не полезна в его улавливании. Вместо этого можно создать метрику, представляющую собой соотношение между частотами ложноположительных классификаций, как это делалось в главе 7. Кстати, это был бы идеальный вариант использования для калиброванных уравненных шансов (CPP). Что касается метрики неблагоприятного воздействия (DI), то два метода дали неоптимальную метрику DI; одна — слишком низкая, а другая — слишком высокая. В случае CPP это связано с однобоким характером калибровки, но в случае устранителя предубеждений (log_2_pr) логистическая регрессия не смогла получить хорошую точность при ограничивании объективности регуляризацией. Метод, который получил лучшие метрики SPD, DI, AOD и DFBA, а также вторую среди лучших метрику EOD, был постобработкой с выравниванием шансов (lgb_3_epp), поэтому давайте визуализируем объективность для него, используя графики библиотеки XAI. С этой целью мы сначала создаем кадр данных с тестовыми примерами (test_df), а затем используем метод replace(), чтобы изготовить категориальную группу AGE_GROUP и получить список категориальных столбцов (cat_cols_l). Затем можно сравнить разные метрики (metrics_plot), используя истинные метки (y_test), предсказанные баллы вероятности для модели EPP (т. е. модели, построенной на стадии постобработки методом уравненных шансов), кадр данных (test_df), защищенный атрибут (cross_cols) и категориальные столбцы. То же самое можно сделать для графика рабочей характеристики приемника roc_plot (receiver operating characteristic, ROC) и графика точности — полноты pr_plot (precisionrecall, PR). Вот соответствующий фрагмент исходного кода: test_df = ccdefault_bias_df.loc[X_test.index] test_df['AGE_GROUP'] = test_df.AGE_GROUP.\ replace({0:'unprivileged', 1:'privileged'}) cat_cols_l = ccdefault_bias_df.dtypes[lambda x: x==np.int8].index.tolist() _ = xai.metrics_plot(y_test, cls_mdls['lgb_3_epp']['probs_test'],\ df=test_df, cross_cols=['AGE_GROUP'], categorical_cols=cat_cols_l) _ = xai.roc_plot(y_test, cls_mdls['lgb_3_epp']['probs_test'],\ df=test_df, cross_cols=['AGE_GROUP'], categorical_cols=cat_cols_l) _ = xai.pr_plot(y_test, cls_mdls['lgb_3_epp']['probs_test'],\ df=test_df, cross_cols=['AGE_GROUP'], categorical_cols=cat_cols_l) Приведенный фрагмент выводит на рис. 11.8 три графика. Первый показывает, что даже самая объективная модель все еще имеет некоторые диспаритеты между обеими группами, в особенности между точностью и полнотой и, как следствие, баллом F1, т. е. их средним показателем. Однако кривая ROC показывает степень близости обеих групп с точки зрения частоты ложноположительных классифика-
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 495 ций (FPR) по сравнению с частотой истинноположительных классификаций (TPR). Третий график демонстрирует, что разница в точности и полноте становится еще более очевидной. Все это доказывает, как трудно сохранять объективный баланс на всех фронтах! Некоторые методы лучше всего подходят для того, чтобы сделать совершенным один аспект, и ничего больше, тогда как другие довольно хороши для их горстки, и больше ничего. Несмотря на недостатки методов, большинство из них добились значительного улучшения. В конечном счете выбор методов будет зависеть от того, что вас больше всего волнует, а их сочетание также рекомендуется для достижения максимального эффекта! Рис. 11.8. Графики, демонстрирующие объективность для наиболее объективной модели Мы завершаем рассмотрение примера по ослаблению систематического смещения и переходим к изучению причинно-следственного вывода, в рамках которого обсудим вопрос обеспечения объективных и прочных политических мер. Построение причинно-следственной модели Принятие решений часто предполагает понимание причин и следствий. Если результат является желательным, можно решить воспроизвести его причину либо
496 Часть III. Настройка на интерпретируемость в иной ситуации ее избежать. Можно изменить что-то специально, чтобы понаблюдать, как это изменение меняет исходы, или отследить случайное следствие до его причины, или просимулировать разные изменения, чтобы выяснить, какое из них будет иметь наиболее благоприятное последствие. Сделать всё это нам поможет причинно-следственный вывод через создание причинно-следственных графиков и моделей. Они связывают все переменные воедино и оценивают риски для принятия более принципиальных решений. Однако в целях надлежащей диагностики влияния причины, будь то преднамеренного или случайного, необходимо отделить ее следствие от спутывающих переменных. Причина, по которой причинно-следственный вывод имеет отношение к этой главе, заключается в том, что решения банка в области корпоративной политики могут существенно влиять на средства к существованию владельцев карт и, учитывая рост числа самоубийств, даже на уровень жизни и смерти. Поэтому существует моральный императив проводить диагностику политических решений с предельной тщательностью. Тайваньский банк начал эксперимент с кредитной политикой 6 месяцев назад. В банке поняли, куда дует ветер, и были осведомлены о том, что у клиентов с наибольшим риском дефолта каким-то образом будут списаны средства с их балансовых счетов, чтобы уменьшить финансовые обязательства этих клиентов. Поэтому основное внимание в эксперименте уделялось только тем, кого, по мнению банка, можно было спасти, т. е. клиентам с низким или средним риском дефолта, и теперь, когда эксперимент завершился, представители банка хотят понять, как перечисленные далее политические меры повлияли на поведение клиентов.  Сниженный кредитный лимит. У некоторых клиентов кредитный лимит был снижен на 25%.  План платежей. Клиентам было предоставлено 6 месяцев для погашения их текущей задолженности по кредитной карте. Другими словами, долг был разделен на шесть частей, и каждый месяц они должны были выплачивать одну часть.  Обе меры: уменьшение кредитного лимита и план платежей. Кроме того, преобладающие процентные ставки по кредитным картам на Тайване в 2005 году составляли около 16–20%, но банк прознал, что в скором времени Комиссия по финансовому надзору Тайваня их ограничит, снизив примерно до 4%. Поэтому представители банка обеспечили, чтобы всем участвующим в эксперименте клиентам автоматически предоставлялись процентные ставки на этом уровне. Некоторые руководители банка полагали, что это только усугубит задолженность и по ходу создаст больше "рабов кредитных карт". Эти опасения побудили к предложению провести эксперимент с более низким лимитом кредитной карты в качестве контрмеры. С другой стороны, план платежей был разработан для того, чтобы убедиться, что облегчение долгового бремени действительно дает клиентам возможность пользоваться картой без опасений. С деловой точки зрения обоснование заключалось в том, что необходимо поощрять здоровый уровень расходов, поскольку при более низких процентных ставках основная часть прибыли будет поступать от обработки платежей, партнерских программ
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 497 по возврату денежных средств наличными и других источников, связанных с расходами, и, в свою очередь, увеличится срок службы клиентов. Тем не менее это также было бы выгодно и для клиентов, потому что, если бы они были более прибыльными как производители расходов, чем дебиторы, то это означало бы наличие стимулов, чтобы не дать им стать последними. Все это оправдывало использование расчетной пожизненной ценности (_LTV) в качестве косвенной метрики в отношении того, как исход эксперимента принес пользу как банку, так и его клиентам. В течение многих лет банк использовал достаточно точные расчеты, чтобы диагностировать ценность, которую владелец кредитной карты предоставит банку, учитывая его историю расходов и платежей, а также такие параметры, как лимиты и процентные ставки. На языке экспериментального дизайна выбранная политика называется экспериментальной процедурой обращения, и наряду с тремя подвергнутыми процедуре группами, еще есть контрольная группа, которая не была подвергнута процедуре, т. е. никаких изменений в политике не было вообще, даже более низких процентных ставок. Прежде чем двигаться дальше, давайте сначала инициализируем список с именами экспериментальных процедур (treatment_names) и список, включающий еще и контрольную группу (all_treatment_names), следующим образом: treatment_names = ['Пониженный кредитный лимит', 'План платежей', 'План платежей и пониженный кредитный лимит'] all_treatment_names = np.array(["Ничего"] + treatment_names) Теперь давайте рассмотрим результаты эксперимента, чтобы помочь себе в построении оптимальной причинно-следственной модели. Изучение результатов эксперимента Довольно интуитивным способом диагностирования эффективности экспериментальной процедуры является сравнение их исходов. Мы хотим знать ответы на два следующих простых вопроса:  Снизила ли экспериментальная процедура частоту дефолта по сравнению с контрольной группой?  Способствовали ли линии поведения в отношении расходов увеличению оценок пожизненной ценности клиентов? И то, и другое можно визуализировать на одном графике. С этой целью мы получаем ряд pandas с процентом для каждой группы, которая допустила дефолт (pct_s), затем еще один ряд с суммой пожизненных ценностей по каждой группе (ltv_s) в тысячах новых тайваньских долларов (K$). Мы помещаем оба ряда в кадр данных pandas и строим его график, как показано в следующем ниже фрагменте исходного кода: pct_s = ccdefault_causal_df[ccdefault_causal_df.IS_DEFAULT==1].\ groupby(['_TREATMENT']).size() / ccdefault_causal_df.groupby(['_TREATMENT']).\ size() ltv_s = ccdefault_causal_df.groupby(['_TREATMENT'])['_LTV'].sum()/1000 plot_df = pd.DataFrame({'% объявивших дефолт':pct_s,\ 'Суммарная пожизненная ценность, K$':ltv_s})
498 Часть III. Настройка на интерпретируемость plot_df.index = all_treatment_names ax = plot_df.plot(secondary_y=['Total LTV, K$'], figsize=(8,5)) ax.get_legend().set_bbox_to_anchor((0.7, 0.99)) plt.grid(False) plt.title("Исходы эксперимента с кредитной политикой", fontsize=16) Приведенный фрагмент генерирует график, показанный на рис. 11.9. Из него можно сделать вывод, что все виды экспериментальных процедур проходят лучше, чем в контрольной группе. Уменьшение кредитного лимита само по себе снижает уровень дефолта более чем на 12% и более чем вдвое превышает расчетную пожизненную ценность (LTV), тогда как план платежей уменьшает дефолты только на 3% и увеличивает LTV примерно на 85%. Тем не менее обе политики в совокупности увеличили LTV контрольной группы в четыре раза и снизили уровень дефолта почти на 15%! Рис. 11.9. Исходы эксперимента для процедур с разными кредитными политиками Прежде чем руководители банка порадуются тому, что они нашли выигрышную политику, мы должны рассмотреть то, как они распределили ее среди держателей кредитных карт в ходе эксперимента. Мы узнали, что они выбрали процедуру в соответствии со своим фактором риска, который измеряется переменной _risk_score. Однако на пожизненную ценность в значительной степени влияет доступный кредитный лимит (_CC_LIMIT), и это необходимо учитывать. Понять распределение можно, сопоставив обе переменные друг с другом на диаграмме рассеяния, кодированной цветом по экспериментальной процедуре (_TREATMENT). Соответствующий фрагмент исходного кода показан ниже: sns.scatterplot(x=ccdefault_causal_df['_CC_LIMIT'].values,\ y=ccdefault_causal_df['_risk_score'].values,\
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 499 hue=all_treatment_names[ccdefault_causal_df['_TREATMENT'].values],\ hue_order=all_treatment_names) plt.title("Избранная кредитная политика ('Процедура')") plt.xlabel("Изначальный кредитный лимит") plt.ylabel("Фактор риска") Приведенный фрагмент сгенерировал график на рис. 11.10. Он показывает, что три экспериментальных процедуры соответствуют разным уровням риска, тогда как контрольная группа ("Ничего") распределена более вертикально. Выбор в пользу назначения процедуры на основе уровня риска также означал, что они неровно распределяли процедуры на основе признака _CC_LIMIT. Резонно задаться вопросом: позволяют ли систематически смещенные условия этого эксперимента вообще интерпретировать его исходы? Взгляните на рис. 11.10. Рис. 11.10. Факторы риска в сравнении с изначальным кредитным лимитом Диаграмма рассеяния демонстрирует стратификацию экспериментальных процедур по факторам риска. Однако диаграммы рассеяния бывает трудно интерпретировать для понимания распределений. Для этого лучше всего использовать график оценки плотности ядра (kernel density estimate, KDE)9. Итак, давайте посмотрим, как _CC_LIMIT распределяется по всем процедурам с помощью displot библиотеки 9 Оценка плотности ядра (kernel density estimate) — это непараметрический способ оценивания плотности распределения вероятностей случайной величины, где ядро — это неотрицательная функция. — Прим. перев.
500 Часть III. Настройка на интерпретируемость seaborn, а заодно и на аналогичный график для пожизненной ценности (_LTV)! Взгляните на следующий ниже фрагмент исходного кода: sns.displot(ccdefault_causal_df, x="_CC_LIMIT", hue="_TREATMENT", kind="kde", fill=True) sns.displot(ccdefault_causal_df, x="_LTV", hue="_TREATMENT", kind="kde", fill=True) Приведенный фрагмент строит графики, отражающие разницы всех четырех распределений, в основном в отношении процедуры № 3 ("План платежей и сниженный кредитный лимит"), которая тяготеет к тому, чтобы центрироваться значительно правее и имеет более длинный и толстый правый "хвост". Рис. 11.11. Распределения KDE для признаков _CC_LIMIT и _LTV по признаку _TREATMENT В идеале, когда проводится подобного рода эксперимент, необходимо стремиться к равному распределению между всеми группами на основе любых соответствующих факторов, которые могут изменять исходы. Однако это не всегда бывает осуществимо из-за логистических либо из-за стратегических ограничений. В данном случае исход (_LTV) варьирует в зависимости от лимитов кредитной карты клиента (_CC_LIMIT), признака гетерогенности — другими словами, меняющегося признака, который непосредственно влияет на эффект экспериментальной процедуры, также именуемого модификатором гетерогенного эффекта экспериментальной процедуры. Можно создать причинно-следственную модель, которая включает и признак _TREATMENT, и модификатор эффекта (_CC_LIMIT). Изучение причинно-следственных моделей Причинно-следственную модель, которую мы построим, можно разделить на четыре компонента следующим образом:  исход модели (Y) — результирующая переменная (переменные) причинно- следственной модели;  процедуры обращения (T) — процедурная переменная (переменные), которая влияет на исход;
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 501  модификаторы эффекта (X) — переменная (переменные), которая влияет на гетерогенность эффекта, обусловливая его. Она находится между процедурой и исходом;  контрольные признаки (W) — так называемые общие причины или спуты- вающие факторы. Это признаки, которые влияют как на исход, так и на экспериментальную процедуру. Начнем с определения каждого из этих компонентов в данных как отдельных кадров данных pandas следующим образом: W = ccdefault_causal_df[['_spend', '_tpm', '_ppm', '_RETAIL', '_URBAN', '_RURAL', '_PREMIUM']] X = ccdefault_causal_df[['_CC_LIMIT']] T = ccdefault_causal_df[['_TREATMENT']] Y = ccdefault_causal_df[['_LTV']] Для оценивания эффектов экспериментальной процедуры мы будем использовать метод дважды устойчивого обучения (doubly robust learning, DRL). Слово "дважды" в названии связано с тем, что в нем используются две модели, а именно:  он предсказывает исход с помощью регрессионной модели, как показано ниже: Y ~W  X;  он предсказывает экспериментальную процедуру с помощью модели склонно- сти, как показано ниже: T ~W  X. Он также является устойчивым благодаря заключительной стадии, которая объединяет обе модели, сохраняя при этом многие желательные статистические свойства, такие как интервалы уверенности и асимптотическая нормальность. Выражаясь формальнее, в оценивании задействуется регрессионная модель g и модель склонности p, обусловленная экспериментальной процедурой t, следующим образом: Yt  gt W , X   t . Он также делает следующее: Pr T  t | X , W   pt W , X  . Цель состоит в том, чтобы вывести условный средний эффект экспериментальной процедуры (conditional average treatment effect, CATE), обозначаемый как t  X  , ассоциированный с каждой экспериментальной процедурой t при наличии гетерогенного эффекта X. Сначала метод DRL устраняет систематическое смещение регрессионной модели. Это делается путем применения обратной склонности следующим образом: Yi ,DRL  gt Wi , X i   t Yi  gt Wi , X i   1Ti  t. pt  X i , Wi 
502 Часть III. Настройка на интерпретируемость Как именно оценивать t  X  по Yi ,DRL , будет зависеть от задействуемого варианта t DRL. Мы будем использовать линейный вариант (LinearDRLearner), чтобы он возвращал коэффициенты и пересечения, которые можно легко интерпретировать. Он выводит t  X  путем выполнения обычной линейной регрессии (ordinary linear regression, OLS) для разниц в исходе между экспериментальной процедурой и контрольным признаком ( Yi ,DRL  Yi ,0DRL ). В интуитивном плане это имеет смысл, потому t что расчетный эффект экспериментальной процедуры за вычетом расчетного эффекта отсутствия экспериментальной процедуры ( t  0 ) является чистым эффектом указанной экспериментальной процедуры. А теперь, когда мы закончили разбираться с теорией, давайте копнем поглубже! Инициализация линейного дважды устойчивого ученика Библиотечный класс LinearDRLearner инициализируется из библиотеки econml. Мы даем ему имя drlearner, указав любой совместимый с библиотекой scikit-learn регрессор (model_regression) и классификатор (model_propensity). Мы будем использовать XGBoost для обоих, но учтите, что классификатор имеет атрибут objective="multi:softmax". Помните, что у нас есть несколько экспериментальных процедур, и, следовательно, эта задача является многоклассовой классификацией. Соответствующий фрагмент исходного кода приведен ниже: drlearner = LinearDRLearner( model_regression=xgb.XGBRegressor(learning_rate=0.1), model_propensity=xgb.XGBClassifier(learning_rate=0.1, max_depth=2,\ objective="multi:softmax"), random_state=rand) Если вы хотите понять, что делает модель регрессии и склонности, можно легко выполнить обучение моделей xgb.XGBRegressor().fit(W.join(X), Y) и xgb.XGBClassifier(objective="multi:softmax").fit(W.join(X), T). Сейчас мы делать этого не будем, но если вам любопытно, то вы могли бы оценить их результативность и даже выполнить методы определения важности признаков, чтобы понять факторы, которые влияют на их предсказания в индивидуальном плане. Причинноследственная модель сводит их вместе с помощью каркаса DRL, приводя к разным выводам. Обучение причинно-следственной модели Можно использовать метод fit() в ученике drlearner, чтобы выполнить подгонку причинно-следственной модели, используя обертку dowhy библиотеки econml. Первыми атрибутами являются компоненты Y, T, X и Y — кадры данных pandas. Для каждого из этих компонентов можно опционально указать имена переменных — имена столбцов каждого кадра данных pandas. Наконец, мы хотели бы оценить эффекты экспериментальной процедуры. Для этого можно опционально предоставить
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 503 модификаторы эффекта (X), и для выполнения указанной работы мы будем использовать половину этих данных, как показано в следующем фрагменте исходного кода: causal_mdl = drlearner.dowhy.fit(Y, T, X, W,\ outcome_names=Y.columns.to_list(),\ treatment_names=T.columns.to_list(),\ feature_names=X.columns.to_list(),\ confounder_names=W.columns.to_list(),\ target_units=X.iloc[:550].values) Инициализировав причинно-следственную модель, можно выполнить ее визуализацию. Библиотека pydot с pygraphviz может сделать это за нас. Обратите внимание, что эта библиотека печально известна своим непостоянством, поэтому она может выйти из строя и вместо view_model показать вам по умолчанию гораздо менее привлекательную графику. Не переживайте, если это случится. Взгляните на следующий фрагмент исходного кода: try: display(Image(to_pydot(causal_mdl._graph._graph).create_png())) except: causal_mdl.view_model() Приведенный выше фрагмент выводит на экран диаграмму модели, показанную на рис. 11.12. С ее помощью можно судить о межсоединениях всех переменных. Рис. 11.12. Диаграмма причинно-следственной модели Причинно-следственная модель уже обучена, поэтому давайте рассмотрим и проинтерпретируем ее результаты. Гетерогенные эффекты экспериментальной процедуры Во-первых, важно отметить, что обертка DoWhy библиотеки econml сократила несколько шагов с помощью метода dowhy.fit(). Обычно во время построения подобного рода причинно-следственной модели непосредственно с помощью обертки DoWhy пользуются методом identify_effect(), который выводит вероятностное вы-
504 Часть III. Настройка на интерпретируемость ражение для оцениваемого эффекта (выявленную расчетную величину). В данном случае оно называется средним эффектом экспериментальной процедуры (average treatment effect, ATE). Затем еще один метод, estimate_effect, использует это выражение и модели, которые оно должно связать вместе (регрессия и склонность). С ними он вычисляет как средний эффект экспериментальной процедуры (ATE), оценку дважды устойчивого ученика (DRL) Yi ,DRL , так и условный средний t эффект экспериментальной процедуры (CATE) i ,t  X  для каждого исхода i и экспериментальной процедуры t. Однако, поскольку эту обертку мы использовали для подгонки (fit) причинно-следственной модели, она выполняет шаги выявления и оценивания автоматически. К выявленному ATE можно обратиться через свойство identified_estimand_, а к результатам оценивания — через свойство estimate_ причинно-следственной модели. Соответствующий фрагмент исходного кода показан ниже: identified_ate = causal_mdl.identified_estimand_ print(identified_ate) drlearner_estimate = causal_mdl.estimate_ print(drlearner_estimate) Приведенный выше фрагмент печатает выражение расчетной величины для identified_estimand_, т. е. деривацию ожидаемого значения для Y  W  X с некоторыми допущениями. Затем причинно-следственная "реализованная" величина estimate_ возвращает средний эффект экспериментальной процедуры (ATE) для экспериментальной процедуры № 1, как показано в следующем фрагменте исходного кода: Estimand type: nonparametric-ate ### Estimand : 1 Estimand name: backdoor1 (Default) Estimand expression: d ─────────────(Expectation(_LTV|_URBAN,_ppm,_CC_LIMIT,_tpm,_spend,_RETAIL,_PREM d[_TREATMENT] IUM,_RURAL)) Estimand assumption 1, Unconfoundedness: If U→{_TREATMENT} and U→_LTV then P(_LTV|_TREATMENT,_URBAN,_ppm,_CC_LIMIT,_tpm,_spend,_RETAIL, _PREMIUM,_RURAL,U) = \ P(_LTV|_TREATMENT,_URBAN,_ppm,_CC_LIMIT,_tpm,_spend,_RETAIL,_PREMIUM,_RURAL) *** Causal Estimate *** ## Identified estimand Estimand type: nonparametric-ate
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 505 ## Realized estimand b: _LTV~_TREATMENT+_URBAN+_ppm+_CC_LIMIT+_tpm+_spend+_RETAIL+_PREMIUM+_RURAL | _CC_LIMIT Target units: ## Estimate Mean value: 7221.414390341943 Effect estimates: [6762.97178458 7330.10299182 7355.87769131 ... 7217.74562572 7492.35375285 7214.96052799] Затем в цикле можно прокрутить все экспериментальные процедуры в причинноследственной модели и вернуть сводку по каждой экспериментальной процедуре следующим образом: for i in range(causal_mdl._d_t[0]): print("Экспериментальная процедура: %s" % treatment_names[i]) display(econml_mdl.summary(T=i+1)) Приведенный выше исходный код печатает три линейно-регрессионные сводки. Первая из них выглядит так, как показано на рис. 11.13. Рис. 11.13. Сводная информация об одной из экспериментальных процедур В целях более глубокого понимания коэффициентов и пересечений можно вывести их на график с соответствующими интервалами уверенности. Для этого сначала создается индекс экспериментальных процедур (idxs). Мы имеем три экспериментальные процедуры, и значит, это просто массив чисел от 0 до 2. Затем все коэффициенты (coef_) и пересечения (intercept_) помещаются в этот массив с помощью операции включения в список. Однако это немного сложнее для 90%-ных интервалов уверенности как для коэффициентов, так и для пересечений, потому что coef_ и intercept_ возвращают нижнюю и верхнюю границы этих интервалов. Нам нужна длина погрешности ошибки в обоих направлениях, а не границы. Мы вычитаем коэффициент и пересечение из этих границ и получаем соответствующую им погрешность ошибки, как показано в следующем фрагменте исходного кода: idxs = np.arange(0, causal_mdl._d_t[0]) coefs = np.hstack([causal_mdl.coef_(T=i+1) for i in idxs]) intercepts = np.hstack([causal_mdl.intercept_(T=i+1) for i in idxs]) coefs_err = np.hstack([causal_mdl.coef__interval(T=i+1) for i in idxs])
506 Часть III. Настройка на интерпретируемость coefs_err[0, :] = coefs - coefs_err[0, :] coefs_err[1, :] = coefs_err[1, :] - coefs intercepts_err = np.vstack([causal_mdl.intercept__interval(T=i+1) \ for i in idxs]).T intercepts_err[0, :] = intercepts - intercepts_err[0, :] intercepts_err[1, :] = intercepts_err[1, :] - intercepts Затем мы строим график коэффициентов по каждой экспериментальной процедуре и соответствующей ошибке, используя функцию errorbar. То же самое можно сделать и с пересечениями в качестве еще одного подграфика, как показано в следующем фрагменте исходного кода: ax1 = plt.subplot(2, 1, 1) plt.errorbar(idxs, coefs, coefs_err, fmt="o") plt.xticks(idxs, treatment_names) plt.setp(ax1.get_xticklabels(), visible=False) plt.title("Коэффициенты") plt.subplot(2, 1, 2) plt.errorbar(idxs, intercepts, intercepts_err, fmt="o") plt.xticks(idxs, treatment_names) plt.title("Пересечения") Приведенный выше фрагмент выводит на экран графики на рис. 11.14. Рис. 11.14. Коэффициенты и пересечения для всех экспериментальных процедур
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 507 По рис. 11.14 можно судить об относительной величине погрешности ошибки всех пересечений и коэффициентов. Тем не менее совершенно очевидно, что при чтении слева направо экспериментальные процедуры становятся предельно лучше только на коэффициентах. Но прежде чем приходить к выводу о том, что "План платежей и сниженный кредитный лимит" является наилучшей политикой, необходимо рассмотреть пересечение, которое для этой экспериментальной процедуры ниже, чем для первой. По сути это означает, что клиент с минимальным лимитом кредитной карты, скорее всего, будет больше увеличивать пожизненную ценность за счет первой политики, потому что коэффициенты умножаются на предел, тогда как пересечение является отправной точкой. Учитывая, что не существует единой наилучшей политики для всех клиентов, давайте рассмотрим принцип выбора политики для каждого из них, используя причинно-следственную модель. Выбор политики Решение о кредитной политике можно принимать на поклиентской основе, используя метод const_marginal_effect(), который берет модификатор эффекта X (_CC_LIMIT) и вычисляет контрфактический условный средний эффект процедуры   X  (CATE). Другими словами, он возвращает расчетную пожизненную ценность (_LTV) для всех экспериментальных процедур по всем наблюдениям в X. Однако не все они стоят одинаково. Создание плана платежей требует административных и юридических расходов в размере около 1000 новых тайваньских долларов за контракт, и, по данным актуарного отдела банка, снижение кредитного лимита 25 имеет альтернативную стоимость, оцениваемую в 72 новых тайваньских доллара в расчете на средние платежи в месяц (_ppm) в течение всего срока пребывания должника в качестве клиента банка. Для того чтобы учесть эти стоимости, можно сконструировать простую лямбда-функцию, которая берет стоимость плана платежей для всех процедур и добавляет их в переменные расходы кредитного лимита, что, естественно, умножается на _ppm. Имея массив длиной n с лимитами кредитной карты, функция стоимости возвращает массив размером (n, 3) со стоимостью для каждой экспериментальной процедуры. Далее мы получаем контрфактический условный средний эффект процедуры (CRATE) и вычитаем стоимости (treatment_effect_minus_costs). Затем мы расширяем массив, чтобы включить столбец нулей, представляющий экспериментальную процедуру "Ничего", и используем argmax, чтобы возвращать индекс рекомендуемой для клиента экспериментальной процедуры (recommended_t), как показано в следующем фрагменте исходного кода: cost_fn = lambda X: np.repeat(np.array([[0, 1000, 1000]]),\ X.shape[0], axis=0) + (np.repeat(np.array([[72, 0, 72]]),\ X.shape[0], axis=0) * X._ppm.values.reshape(-1,1)) treatment_effect_minus_costs = causal_mdl.const_marginal_effect(\ X=X.values) - cost_fn(ccdefault_causal_df) treatment_effect_minus_costs = np.hstack([np.zeros(X.shape),\ treatment_marginal_effect]) recommended_T = np.argmax(treatment_effect_minus_costs, axis=1)
508 Часть III. Настройка на интерпретируемость Можно построить диаграмму рассеяния и вывести на ней _CC_LIMIT и _ppm, кодированные цветом в соответствии с рекомендуемой экспериментальной процедурой, чтобы соблюсти оптимальную для клиента кредитную политику, следующим образом: sns.scatterplot(\ x=ccdefault_causal_df['_CC_LIMIT'].values,\ y=ccdefault_causal_df['_ppm'].values,\ hue=all_treatment_names[recommended_T],\ hue_order=all_treatment_names) plt.title("Оптимальная кредитная политика в зависимости от клиента") plt.xlabel("Изначальный кредитный лимит") plt.ylabel("Платежи в месяц") Приведенный выше фрагмент выводит на экран диаграмму рассеяния на рис. 11.15. Рис. 11.15. Оптимальная для клиента кредитная политика в зависимости от изначального кредитного лимита и пользования картой На рис. 11.15 хорошо видно, что никакая экспериментальная процедура не рекомендуется вообще, и этот факт сохраняется даже тогда, когда стоимости не вычитаются — можно удалить cost_fn и еще раз выполнить исходный код, который покажет график в подтверждение. Можно заключить, что для клиентов полезны все экспериментальные процедуры, некоторые больше, чем другие. И, разумеется, некоторые экспериментальные процедуры приносят банку пользы больше, чем другие, в зависимости от клиента. Здесь приходится идти по тонкому льду.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 509 Одной из самых больших проблем является объективность по отношению к клиентам, в особенности к тем, с кем банк поступил необъективно в большей степени: непривилегированной возрастной группе. Просто потому, что одна политика обходится банку дороже, чем другая, она должна исключать возможность доступа к другим политикам. Провести ее диагностику можно путем использования составной процентной гистограммы для всех рекомендуемых политик. Благодаря этому можно наблюдать, как рекомендуемая политика распределяется между привилегированной и непривилегированной группами. Взгляните на следующий фрагмент исходного кода: cdefault_causal_df['recommended_T'] = recommended_T plot_df = ccdefault_causal_df.groupby(['recommended_T', 'AGE_GROUP']).\ size().reset_index() plot_df['AGE_GROUP'] = plot_df.AGE_GROUP.\ replace({0:'unprivileged', 1:'privileged'}) plot_df = plot_df.pivot(columns='AGE_GROUP', index='recommended_T', values=0) plot_df.index = treatment_names plot_df = plot_df.apply(lambda r: r/r.sum()*100, axis=1) plot_df.plot.bar(stacked=True, rot=0) plt.xlabel('Оптимальная политика') plt.ylabel('%') Приведенный выше фрагмент выводит на экран гистограмму на рис. 11.16. Рис. 11.16. Объективность распределений оптимальных политик На рис. 11.16 показано, как привилегированным группам с большей долей назначается одна из политик — "План платежей и сниженный кредитный лимит". Этот диспаритет в первую очередь связан с тем, что одним из факторов являются расходы банка, поэтому, если бы банк взял на себя часть этих расходов, то это могло бы
510 Часть III. Настройка на интерпретируемость сделать ее более объективной. Но каким было бы объективное решение? Выбор кредитных политик является примером внутрибанковской процедурной объективности, и существует масса возможных определений политики. Означает ли равное обращение с клиентами буквально равное обращение или же пропорциональное обращение? Включает ли оно также понятия свободы выбора? Что делать, если клиент предпочитает одну политику другой? Следует ли разрешить ему переключаться с одной на другую? Каким бы ни было определение, его можно урегулировать с помощью причинно-следственной модели. Можно назначить всем клиентам одинаковую политику, или же распределение рекомендуемых политик можно откалибровать таким образом, чтобы доли были равными, либо каждый клиент мог выбирать между первой и второй наиболее оптимальной политикой. Существует масса вариантов действий! Проверка устойчивости оценки Библиотека DoWhy в своем составе имеет четыре метода проверки устойчивости расчетного причинно-следственного эффекта. Их описание приведено ниже.  Случайная общая причина (random common cause) — добавление случайно сгенерированной спутывающей переменной. Если оценка является устойчивой, то средний эффект экспериментальной процедуры (ATE) не должен изменяться слишком сильно.  Опровергатель плацебо-процедур (placebo treatment refuter) — замена экспе- риментальных процедур случайными переменными (плацебо). Если оценка является устойчивой, то средний эффект экспериментальной процедуры (ATE) должен быть близким к нулю.  Опровергатель подмножества данных (data subset refuter) — удаление случай- ного подмножества данных. Если оценщик обобщает хорошо, то средний эффект экспериментальной процедуры (ATE) не должен изменяться слишком сильно.  Добавление ненаблюдаемой общей причины (add unobserved common cause) — добавление ненаблюдаемой спутывающей переменной, ассоциированной как с экспериментальной процедурой, так и с исходом. Оценщик допускает некоторый уровень неспутанности, но добавление большей спутанности должно приводить к систематическому смещению оценок. В зависимости от силы эффекта спутанной переменной он должен оказывать равное влияние на средний эффект экспериментальной процедуры (ATE). Далее мы проверим устойчивость оценки с помощью первых двух методов. Добавление случайной общей причины Этот метод проще всего имплементировать, вызвав функцию refute_estimate с именем метода method_name="random_common_cause". Она вернет сводку, которую можно распечатать.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 511 Взгляните на следующий фрагмент исходного кода: ref_random = causal_mdl.refute_estimate(method_name="random_common_cause") print(ref_random) Приведенный выше фрагмент печатает следующее: Refute: Add a Random Common Cause Estimated effect:7221.414390341943 New effect:7546.695920181393 Показанный результат говорит о том, что новая общая причина, или переменная W, не оказывает существенного влияния на средний эффект экспериментальной процедуры (ATE). Замена экспериментальной процедуры случайной переменной С помощью этого метода мы заменим переменную экспериментальной процедуры шумом. Если экспериментальная процедура устойчиво коррелирует с исходом, это должно свести среднее влияние к нулю. Для его имплементации мы также вызываем функцию refute_estimate, но с использованием placebo_treatment_refuter в качестве метода. Мы также должны указать тип плацебо (placebo_type) и число симуляций (num_simulations). Будем использовать тип плацебо permute, и чем больше симуляций, тем лучше, правда, это также займет больше времени. Соответствующий фрагмент исходного кода показан ниже: ref_placebo = causal_mdl.refute_estimate(method_name="placebo_treatment_refuter",\ placebo_type="permute", num_simulations=20) print(ref_placebo) Приведенный фрагмент печатает следующее: Refute: Use a Placebo Treatment Estimated effect:7221.414390341943 New effect:132.77295305233164 p value:0.43187234564256083 Насколько можно судить по полученному результату, новый эффект находится вблизи нуля. Однако, учитывая, что p-значение выше 0,05, мы не можем отклонить нулевую гипотезу, которая утверждает, что средний эффект экспериментальной процедуры (ATE) больше нуля. Это говорит о том, что расчетный причинноследственный эффект не очень прочен. Вероятно, можно его улучшить, добавив релевантные спутывающие переменные либо использовав другую причинноследственную модель. Но это также означает, что в экспериментальном дизайне были недостатки, которые невозможно исправить, такие как систематическая смещенность банка в том, как он предписывал процедуры в соответствии с фактором риска.
512 Часть III. Настройка на интерпретируемость Миссия выполнена Миссия этой главы была двоякой:  построить объективную предсказательную модель, чтобы предсказывать клиен- тов, которые могут объявить о своей финансовой несостоятельности с наибольшей вероятностью;  построить устойчивую причинно-следственную модель, чтобы оценивать поли- тики, выявляя наиболее выгодные из них для клиентов и банка. Что касается первой цели, то мы сгенерировали четыре модели с методами ослабления систематического смещения, которые были беспристрастно объективнее, чем базовая модель, в соответствии с четырьмя метриками объективности (SPD, DI, AOD, EOD) — при сравнении привилегированной и непривилегированной возрастных групп. Однако, согласно DFBA, только две из этих моделей являются более объективными с точки зрения как возрастной группы, так и пола (см. рис. 11.7). Мы все еще можем значительно повысить объективность путем комбинирования методов, но любая из четырех моделей улучшает базовую модель. В отношении второй цели каркас причинно-следственных выводов определил, что любая из опробованных стратегий лучше, чем отсутствие политики для обеих сторон. Ура! Тем не менее он произвел оценки, которые не выявили ни одного победителя. Вместе с тем, как и ожидалось, рекомендуемая политика варьирует в зависимости от кредитного лимита клиента; с другой стороны, если мы стремимся максимизировать прибыльность банка, то должны учитывать среднее пользование кредитными картами. Вопрос прибыльности ставит перед нами две цели, которые необходимо согласовать: предписывать рекомендуемую политику, которая принесет наибольшую пользу либо клиенту, либо банку. По этой причине вопрос о том, как быть объективным с процедурной точки зрения, является сложным и имеет целый ряд возможных ответов, и любое из решений потребует, чтобы банк брал на себя часть расходов, связанных с имплементированием политики. Что касается устойчивости, то несмотря на небезупречный эксперимент, можно сделать вывод: наши оценки имеют посредственный уровень устойчивости, проходя один тест на устойчивость, но не проходя другой. С учетом вышесказанного всё зависит от того, что считать достаточно устойчивым для подтверждения наших выводов. В идеале мы бы попросили банк начать новый несмещенный эксперимент, но ожидание в течение еще 6 месяцев, возможно, окажется невыполнимым. В науке о данных мы часто сталкиваемся с небезупречными экспериментами и систематически смещенными данными и должны использовать их максимально. Причинно-следственный вывод обеспечивает способ достижения этой цели, распутывая причину и следствие в комплекте с оценками и соответствующими интервалами уверенности. Затем можно предлагать находки со всеми оговорками, чтобы руководители могли принимать обоснованные решения. Систематически смещенные решения приводят к систематически смещенным результатам, поэтому моральный императив борьбы со смещенностью может начинаться с формирования процесса принятия решений.
Глава 11. Ослабление систематического смещения и причинно-следственный вывод 513 Резюме После прочтения этой главы вы должны узнать о том, как обнаруживать систематическое смещение визуально и с помощью метрик как в данных, так и в моделях, а затем ослаблять с помощью методов предварительной обработки, промежуточной обработки и последующей обработки. Мы также познакомились со причинноследственными выводами, оценив гетерогенные эффекты экспериментальных процедур, приняв с их помощью объективные политические решения и проверив их устойчивость. В центре внимания следующей главы тоже будет систематическое смещение, но мы научимся настраивать модели для достижения нескольких целевых критериев, включая и объективность. Источник набора данных Yeh I. C., Lien C. H. The comparisons of data mining techniques for the predictive accuracy of probability of default of credit card clients // Expert Systems with Applications. — 2009. — Vol. 36 (2). — P. 2473–2480. — URL: https://dl.acm.org/doi/ abs/10.1016/j.eswa.2007.12.020. (Да И. C., Льен Г. Г. Сравнение технических приемов добычи знаний из данных для предсказательной точности вероятности дефолта клиентов по кредитным картам.) Справочные материалы  Chang C., Chang H. H., Tien J. A study on the coping strategy of financial supervisory organization under information asymmetry: case study of Taiwan’s credit card market // Universal Journal of Management. — 2017. — № 5. — P. 429–436. — URL: http://doi.org/10.13189/ujm.2017.050903. (Чанг С., Чанг Х. Х., Тянь Дж. Исследование стратегии преодоления трудностей в организации финансового надзора в условиях информационной асимметрии: тематическое исследование рынка кредитных карт Тайваня.)  Foulds J., Pan S. An intersectional definition of fairness // 2020 IEEE 36th International Conference on Data Engineering, ICDE. — 2020. — P. 1918–1921. — URL: https://arxiv.org/abs/1807.08362. (Фоулдс Дж., Пан С. Межсекторальное определение термина объективности.)  Kamiran F., Calders T. Data preprocessing techniques for classification without discrimination // Knowledge and Information Systems. — 2011. — № 33. — P. 1– 33. — URL: https://link.springer.com/article/10.1007/s10115-011-0463-8. (Камиран Ф., Колдерс Т. Методы предобработки данных для классифицирования без дискриминации.)  Feldman M., Friedler S., Moeller J., Scheidegger C., Venkatasubramanian S. Certifying and Removing DI // Proceedings of the 21st ACM SIGKDD International Conference on Knowledge Discovery and Data Minin. — 2015. — URL: https://arxiv.org/abs/1412.3756. (Фельдман М., Фридлер С., Меллер Дж., Шейдеггер С., Венкатасубраманян С. Сертификация и удаление DI.)
514 Часть III. Настройка на интерпретируемость  Kamishima T., Akaho S., Asoh H., Sakuma J. Fairness-Aware Classifier with Prejudice Remover Regularizer // ECML/PKDD. — 2012. — URL: https://dl.acm.org/doi/10.5555/3120007.3120011. (Камисима Т., Акахо С., Асох Х., Сакума Дж. Классификатор, учитывающий объективность, с регуляторомустранителем предубеждений.)  Kearns M., Neel S., Roth A., Wu Z. Preventing Fairness Gerrymandering: Auditing and Learning for Subgroup Fairness // ICML. — 2018. — URL: https://arxiv.org/pdf/1711.05144.pdf. (Кернс М., Нил С., Рот А., Ву З. Предотвращение махинаций с объективностью: аудит и усвоение информации для обеспечения объективности в подгруппах.)  Pleiss G., Raghavan M., Wu F., Kleinberg J., Weinberger K. Q., 2017. On Fairness and Calibration // NIPS. — 2017. — URL: https://arxiv.org/abs/1709.02012. (Плейс Г., Рагхаван М., Ву Ф. Кляйнберг Дж., Вайнбергер К. К. Об объективности и калибровке.)  Foster D., Syrgkanis V. Orthogonal Statistical Learning // ICML. — 2019. — URL: http://arxiv.org/abs/1901.09036. (Фостер Д., Сыргканис В. Ортогональное статистическое обучение.)
12 Монотонные ограничения и настройка моделей на интерпретируемость Большинство модельных классов имеют гиперпараметры, которые можно настраивать для повышения быстродействия, увеличения предсказательной результативности и уменьшения переобучения. Этого можно добиться путем введения в обучение модели регуляризации. В главе 3 мы назвали регуляризацию коррекционным свойством интерпретируемости, которое уменьшает сложность с помощью штрафа или ограничения, заставляющего модель усваивать более разреженные представления входных данных. Регуляризованные модели обобщают лучше, поэтому настоятельно рекомендуется настраивать модели в соответствии с этой стратегией. В качестве побочного эффекта регуляризованная модель требует меньше признаков и их взаимодействий, что упрощает интерпретацию модели — меньше шума означает более четкий сигнал! И хотя существует большое число гиперпараметров, мы сосредоточимся только на тех, которые улучшают интерпретируемость посредством контроля за переобучением. Кроме того, мы в определенной степени вернемся к ослаблению систематического смещения посредством связанных с классовым дисбалансом гиперпараметров, рассмотренных в предыдущих главах. В главе 2 объясняются три модельных свойства, влияющие на интерпретируемость: нелинейность, интерактивность и немонотонность. Предоставленная сама себе, модель может усваивать некоторые мнимые и нелогичные нелинейности и интерактивности. Как обсуждалось в главе 10, во избежание этого могут устанавливаться ограждения посредством тщательного конструирования признаков. Однако что именно от нас требуется, чтобы разместить ограждения в случае монотонности? В этой главе мы научимся это делать с помощью монотонных ограничений. И так же как монотонные ограничения, которые являются модельным аналогом для конструирования признаков, регуляризация выступает модельным аналогом для методов отбора признаков, которые мы обсудили в главе 10! Вот главные темы, которые будут рассмотрены в этой главе:  установка ограничений с помощью конструирования признаков;  настройка моделей на интерпретируемость;  имплементирование модельных ограничений.
516 Часть III. Настройка на интерпретируемость Технические требования В примере этой главы используются библиотеки mldatasets, pandas, numpy, sklearn, xgboost, lightgbm, catboost, tensorflow, bayes_opt, tensorflow_lattice, matplotlib, seaborn, scipy, xai и shap. Исходный код этой главы находится по адресу: https://github.com/ PacktPublishing/Interpretable-Machine-Learning-with-Python/tree/master/Chapter12. Миссия Вопрос алгоритмической объективности имеет серьезные социальные последствия, начиная с распределения ресурсов социального обеспечения и заканчивая приоритизацией операций экстренной хирургии и отсевом заявлений на работу. Эти алгоритмы машинного обучения могут определять средства к существованию или жизнь человека, и часто именно наиболее маргинализованные и уязвимые группы населения подвергаются худшему обращению со стороны этих алгоритмов, потому что они увековечивают всеохватывающие систематические смещения, усвоенные из данных. Следовательно, именно наибеднейшие семьи ошибочно классифицируются, как проявляющие жестокое обращение с детьми; это люди из расовых меньшинств, которые получают недостаточное медицинское обслуживание; и это женщины, которых отсеивают с высокооплачиваемых технологических вакансий. Даже в случаях, связанных с менее непосредственными и индивидуализированными рисками, такими как онлайновый поиск, боты Twitter и социально-медийные профили, получают усиление такие социальные предрассудки, как дискриминация по признаку принадлежности к элите (элитарность), по расовому признаку (расизм), по половому признаку (сексизм) и по признаку возраста (агизм). В этой главе будет продолжено описание миссии из главы 7. Если вы с ней не знакомы, вернитесь назад и пробегитесь по первым нескольким страницам указанной главы, чтобы сначала получить прочное представление о задаче. Пример с рецидивизмом из главы 7 представляет собой случай алгоритмического систематического смещения. Соучредитель компании, разработавшей алгоритм COMPAS (correctional offender management profiling alternative sanctions — профилирование исправительного управления правонарушителями для альтернативных санкций), признал, что получить балл без вопросов, коррелированных с расой, чрезвычайно сложно. Эта корреляция является одной из главных причин того, что баллы систематически смещены по отношению к афроамериканцам. Другая причина — это чрезмерная представленность чернокожих ответчиков в тренировочных данных. Мы не знаем наверняка, потому что у нас нет изначальных тренировочных данных, но мы знаем, что большинство заключенных являются представителями небелых меньшинств. Мы также знаем, что чернокожие люди, как правило, чрезмерно представлены при арестах из-за кодифицированной дискриминации в отношении мелких правонарушений, связанных с наркотиками, и чрезмерной численности полицейских в местах компактного проживания чернокожих. Итак, что можно сделать, чтобы это исправить?
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 517 В главе 7 с помощью косвенной модели нам удалось продемонстрировать, что алгоритм COMPAS был систематически смещенным. В этой главе мы допустим, что журналист опубликовал ваши выводы, а группа адвокатов алгоритмического правосудия прочитала статью и обратилась к вам. Компании, создающие инструменты криминальной диагностики, не берут на себя ответственность за систематическую смещенность и говорят, что их инструменты просто отражают реальность. Группа адвокатов наняла вас, чтобы продемонстрировать, что модель машинного обучения можно натренировать так, чтобы она стала значительно менее систематически смещенной по отношению к чернокожим ответчикам и отражала только проверенные реалии уголовного правосудия. Эти доказанные реалии включают монотонное снижение риска рецидивизма с возрастом и сильную корреляцию с числом предшествующих судимостей, которая сильно увеличивается с возрастом. Еще один факт, подтверждаемый академической литературой, заключается в том, что женщины значительно менее склонны к рецидивизму и преступности в целом. Прежде чем мы двинемся дальше, следует признать, что модели контролируемого обучения сталкиваются с рядом препятствий в улавливании знаний предметной области из данных. Рассмотрим следующие случаи.  Систематическое смещение вследствие отбора данных, вследствие исклю- чения или вследствие предубеждения. Что, если ваши данные на самом деле не отражают среду, которую ваша модель намеревается обобщать? Если это так, то знания предметной области не будут совпадать с тем, что вы наблюдаете в данных. Что делать, если среда, в которой были сгенерированы данные, имеет встроенное общесистемное или институциональное смещение? Тогда данные будут отражать эти смещения.  Классовый дисбаланс. Как видно из главы 11, классовый дисбаланс может бла- гоприятствовать одним группам по сравнению с другими. Двигаясь наиболее эффективным путем к высокой точности, модель будет усваивать знания из этого дисбаланса, противоречащие знаниям предметной области.  Немонотонность. Разреженные области на гистограмме признаков или выбросы с высоким размахом могут приводить к тому, что модель будет усваивать немонотонность, когда знания предметной области требуют иного, и любые ранее упомянутые проблемы также могут способствовать этому.  Невлиятельные признаки. Нерегуляризованная модель по умолчанию будет пытаться усваивать знания из всех признаков, если они содержат некоторую информацию, но это мешает усваивать из релевантных признаков. Более экономная модель с большей вероятностью будет подкреплять признаки, поддерживаемые знаниями предметной области.  Нелогичные взаимодействия. Как упоминалось в главе 10, могут существовать нелогичные взаимодействия, которые модель предпочитает взаимодействиям, поддерживаемым знаниями предметной области. В качестве побочного эффекта
518 Часть III. Настройка на интерпретируемость они могут в итоге благоприятствовать некоторым коррелирующим с ними группам. И в главе 7 мы увидели доказательство этого через понимание двойных стандартов.  Исключения. Наши факты предметной области основаны на совокупном пони- мании, но при поиске шаблонов в более гранулярном масштабе модели будут отыскивать исключения, такие как изолированные участки, в которых рецидивизм среди женщин имеет более высокий риск, чем у мужчин. Эти модели, возможно, не будут поддерживаться известными явлениями, но они, тем не менее, могут оказаться верными, поэтому следует проявлять осторожность, чтобы не стереть их в результате наших усилий по настройке. Группа адвокатов проверила данные на предмет их адекватного представления только одного округа во Флориде, и они передали вам сбалансированный набор данных. Констатировать и контролировать первое препятствие чрезвычайно сложно. О втором уже позаботились. Теперь вам предстоит разобраться с оставшимися четырьмя! Подход Вы решили использовать тройной подход.  Установить ограждения с помощью конструирования признаков. Задейст- вуя уроки, извлеченные из главы 7, а также знания предметной области, которые у нас уже есть, в частности, о предшествующих данных и возрасте, мы сконструируем несколько признаков.  Настроить модели на интерпретируемость. После того как данные будут го- товы, мы настроим многочисленные модели с разными методами взвешивания классов и предотвращения переобучения. Эти методы не только обеспечат моделям более качественное обобщение, но и облегчат их интерпретацию.  Имплементировать модельные ограничения. И последнее, но не менее важ- ное: мы выполним имплементацию монотонных и интеракционных ограничений на наилучших моделях, чтобы они гарантированно не отклонялись от проверенных и объективных взаимодействий. В последних двух разделах мы убедимся, что модели работают точно и объективно. Мы также сравним распределения риска рецидивизма между данными и моделью, чтобы убедиться, что они согласуются. Подготовительные работы Исходный код для этого примера находится по адресу: https://github.com/PacktPublishing/Interpretable-Machine-Learning-with-Python/ tree/master/Chapter12/Recidivism_part2. ipynb.
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 519 Загрузка библиотек В целях выполнения этого примера необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  pandas и numpy для манипулирования данными;  sklearn (scikit-learn), tensorflow_lattice для xgboost, lightgbm, catboost, tensorflow, разбивки данных и обучения моделей; bayes_opt и  matplotlib, seaborn, scipy, xai и shap для наглядных визуализаций и их интерпре- тирования. Сначала необходимо их все загрузить следующим образом: import math import os import copy import mldatasets import pandas as pd import numpy as np from sklearn import preprocessing, model_selection, metrics, linear_model, svm, neural_network, ensemble import xgboost as xgb import lightgbm as lgb import catboost as cb import tensorflow as tf from bayes_opt import BayesianOptimization import tensorflow_lattice as tfl from tensorflow.keras.wrappers.scikit_learn import KerasClassifier import matplotlib.pyplot as plt import seaborn as sns import scipy import xai import shap Давайте проверим, что TensorFlow загрузил нужную версию. Для этого мы воспользуемся инструкцией print(tf.__version__). Версия должна быть 2.0 и выше. Изучение проблемы и подготовка данных Мы загружаем данные в кадр данных, который назовем recidivism_df, следующим образом: recidivism_df = mldatasets.load("recidivism-risk-balanced") Должно быть более 11 000 записей и 11 столбцов. Это можно подтвердить с помощью библиотечного метода info() следующим образом: recidivism_df.info()
520 Часть III. Настройка на интерпретируемость Приведенная выше строка исходного кода выводит на экран следующее: <class 'pandas.core.frame.DataFrame'> RangeIndex: 11142 entries, 0 to 11141 Data columns (total 12 columns): # Column Non-Null Count --- ------------------0 sex 11142 non-null 1 age 11142 non-null 2 race 11142 non-null 3 juv_fel_count 11142 non-null 4 juv_misd_count 11142 non-null 5 juv_other_count 11142 non-null 6 priors_count 11142 non-null 7 c_charge_degree 11142 non-null 8 days_b_screening_arrest 11142 non-null 9 length_of_stay 11142 non-null 10 compas_score 11142 non-null 11 is_recid 11142 non-null dtypes: float64(2), int64(7), object(3) memory usage: 1.0+ MB Dtype ----object int64 object int64 int64 int64 int64 object float64 float64 int64 int64 Результат проверку проходит. Отсутствующих значений нет, и все признаки, кроме трех: пол (sex), раса (race) и степень преступления, в котором ответчику предъявлено обвинение в настоящее время (charge_degree), — являются числовыми. Это те же данные, которые мы использовали в главе 7, поэтому словарь данных точно такой же. Однако набор данных был сбалансирован с помощью методов перевыборки, и на этот раз он не был подготовлен за нас, поэтому нам нужно будет это сделать самим, но перед этим давайте разберемся в результатах балансировки. Подтверждение баланса перевыборки Распределение признаков расы (race) и наличия рецидивизма у ответчика в течение 2 лет (is_recid) можно проверить с помощью графика дисбаланса (imbalance_plot) библиотеки XAI. Другими словами, он подсчитывает число записей для каждой комбинации "раса — наличие рецидивизма". Этот график позволит нам проследить наличие дисбалансов в числе ответчиков, проявлявших рецидивизм, по каждой расе. Соответствующий фрагмент исходного кода можно увидеть ниже: categorical_cols_l = ['sex', 'race', 'c_charge_degree', 'is_recid',\ 'compas_score'] xai.imbalance_plot(recidivism_df, 'race', 'is_recid',\ categorical_cols=categorical_cols_l) Приведенный выше фрагмент выводит на экран рис. 12.1, который рассказывает о том, как все расы имеют одинаковое количество is_recid=0 и is_recid=1. Однако Other (прочие) не имеют паритета в численности с другими расами. Кстати, в этой версии набора данных все другие расы были обозначены как Other, и выбор в пользу того, чтобы для достижения полного равенства не выполнять повышаю-
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 521 щий отбор прочих рас (Other) или не осуществлять понижающий отбор двух других рас, сделан по той простой причине, что они менее представлены в популяции ответчиков. Этот сбалансированный выбор является одним из многих, которые можно сделать в подобной ситуации. Демографически все зависит от того, что конкретно должны представлять ваши данные: ответчиков, заключенных, гражданских лиц среди населения в целом? И на каком уровне, из какого округа, штата, страны? Рис. 12.1. Распределение двухлетнего рецидивизма (is_recid) по расе (race) Затем вычислим величину монотонного коррелирования каждого признака с целевой переменной. Мы узнали о ранговой корреляции Спирмена в главе 5, но она будет полезна и в этой главе, поскольку измеряет монотонность между двумя признаками. В конце концов, одной из технических тем этой главы являются монотонные ограничения, и первостепенная задача состоит в том, чтобы построить значительно менее смещенную модель. Сначала мы создаем новый кадр данных без балла compass_score системы COMPAS (recidivism_corr_df). Используя этот кадр данных, мы выводим кодированный цветом кадр данных с признаковым столбцом (feature) с именами первых 10 признаков и еще одним с коэффициентом Спирмена (correlation_to_target) для всех 10 признаков по отношению к 11-му признаку — целевой переменной. Соответствующий фрагмент исходного кода можно увидеть ниже: recidivism_corr_df = recidivism_df.drop(['compas_score'], axis=1) pd.DataFrame({'feature': recidivism_corr_df.columns[:-1],\ 'correlation_to_target': scipy.stats.spearmanr(recidivism_corr_df).\ correlation[10,:-1]}).style.background_gradient(cmap='coolwarm') Приведенный выше фрагмент выводит на экран кадр данных, показанный на рис. 12.2. Наиболее коррелированными признаками являются число предшествую-
522 Часть III. Настройка на интерпретируемость щих судимостей (priors_count), за которым следуют возраст (age), три численности, связанных с несовершеннолетними (jul_), и пол (sex). Коэффициенты степени преступления, в котором ответчику предъявлено обвинение в настоящее время (c_charge_degree), числа дней предварительного ареста (days_b_screening_arrest), продолжительность пребывания в заключении (length_of_stay) и расы (race) незначительны. Рис. 12.2. Коэффициенты Спирмена для всех признаков по отношению к целевой переменной перед конструированием признаков Далее мы научимся использовать методику конструирования признаков, чтобы "внедрить" в признаки некоторые знания предметной области. Установка ограничений с помощью конструирования признаков В главе 7 мы узнали, что, помимо расы (race), наиболее заметными признаками в наших объяснениях были возраст (age), число предшествующих судимостей (priors_count) и степень преступления, в котором ответчику предъявлено обвинение в настоящее время (c_charge_degree). К счастью, теперь данные сбалансированы, так что расовая систематическая смещенность, приписываемая этому дисбалансу, исчезла. Однако, используя якорные и контрфактические объяснения, мы обнаружили некоторые тревожные неувязки. В случае с возрастом (age) и числом предшествующих судимостей (priors_count) эти неувязки были вызваны тем, как эти признаки были распределены. Исправить проблемы с распределением можно с помощью методики конструирования признаков и благодаря ему обеспечить, чтобы модель не усваивала из неровного распределения. В случае со степенью преступления (c_charge_degree), будучи категориальным признаком, ему не хватало заметного порядка, и это отсутствие порядка создавало нелогичные объяснения. В этом разделе мы изучим три способа установки ограждений с помощью методики конструирования признаков: упорядочение, дискретизацию и члены взаимодействия (интеракционные члены).
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 523 Упорядочение Сначала обратимся к следующему ниже фрагменту исходного кода и посмотрим, сколько наблюдений у нас есть для каждой категории степени преступления (c_charge_degree): recidivism_df.c_charge_degree.value_counts() Приведенная выше строка исходного кода выводит на экран следующий результат: (F3) 6555 (M1) 2632 (F2) 857 (M2) 768 (F1) 131 (F7) 104 (MO3) 76 (F5) 7 (F6) 5 (NI0) 4 (CO3) 2 (TCX) 1 Name: c_charge_degree, dtype: int64 Каждая степень преступления соответствует тяжести обвинения. В этих величинах тяжести есть порядок, который теряется при использовании категориального признака. Это можно легко исправить, заменив каждую категорию соответствующим порядком. Можно по-разному рассуждать о том, каким этот порядок должен быть. Например, мы могли бы обратиться к законам или руководящим принципам в основе вынесения приговоров — разная тяжесть обвинения влечет за собой минимальные либо максимальные сроки тюремного заключения. Мы также могли бы посмотреть статистику о том, насколько жестокими являются эти люди в среднем, и назначить эту информацию степени преступления. В каждом подобном решении есть потенциал для систематического смещения, и если у нас нет существенных подтверждающих данных в его поддержку, лучше всего использовать последовательность целых чисел. Итак, вот что мы сейчас собираемся сделать. Мы создадим словарь (charge_degree_code_rank), который соотносит степени с числом, соответствующим рангу тяжести, от низкой до высокой. Затем мы применим функцию replace библиотеки pandas, чтобы выполнить замены, используя словарь. Соответствующий фрагмент исходного кода приведен ниже: charge_degree_code_rank = {'(F10)':15, '(F9)':14, '(F8)':13,\ '(F7)':12, '(TCX)':11, '(F6)':10,\ '(F5)':9, '(F4)':8, '(F3)':7,\ '(F2)':6, '(F1)':5, '(M1)':4,\ '(NI0)':4, '(M2)':3, '(CO3)':2,\ '(MO3)':1, '(X)':0} recidivism_df.c_charge_degree.replace(charge_degree_code_rank, inplace=True)
524 Часть III. Настройка на интерпретируемость Провести диагностику степени соответствия этот порядка вероятности рецидивизма можно посредством линейного графика, который показывает, как она меняется по мере увеличения степени обвинения. Для этого можно использовать функцию прогрессии вероятности (plot_prob_progression), которая в первом аргументе (c_charge_degree) принимает непрерывный признак, измеряемый относительно вероятности двоичного признака во втором (is_recid). Она может разбить непрерывный признак на интервалы (x_intervals) и даже использовать квартили (use_quartiles). Наконец, можно задать метки и заголовки осей. Соответствующий фрагмент исходного кода показан ниже: mldatasets.plot_prob_progression(recidivism_df.c_charge_degree,\ recidivism_df.is_recid, x_intervals=12, use_quartiles=False,\ xlabel='Относительная степень обвинения',\ title='Верояность рецидивизма по относительной степени обвинения') Приведенный выше фрагмент генерирует график на рис. 12.3. По мере увеличения ранжируемой степени обвинения наблюдается тенденция к снижению вероятности двухлетнего рецидивизма, за исключением 1-го ранга. Ниже вероятности располагается гистограмма, которая показывает распределение наблюдений по каждому рангу. Ввиду того что степень распределена так неровно, к этой тенденции следует относиться с долей скептицизма. Вы заметите, что некоторые ранги, такие как 0, 8 и 13–15, на графике отсутствуют, потому что категории степени обвинения существовали в системе уголовного правосудия, но отсутствовали в данных. Рис. 12.3. График прогрессии вероятности по степени обвинения С технической точки зрения для улучшения признака степени преступления (c_charge_degree) можно сделать совсем немного, потому что он уже представляет отдельные категории, которые теперь дополнены порядком. Любые дальнейшие преобразования могут привести к значительной потере информации. С другой сто-
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 525 роны, непрерывные признаки заведомо имеют порядок, но проблема может возникнуть из-за уровня точности, который они несут, потому что малые различия могут быть несущественными. Неровное распределение и нелогичные взаимодействия только усугубляют эту проблему. Дискретизация Для того чтобы понять, каким образом выполнить дискретизацию непрерывного признака возраста (age) лучше всего, давайте попробуем два разных подхода. Можно применить равновеликую дискретизацию, так называемые корзины, или интервалы, фиксированной ширины, т. е. размер корзины определяется по формуле: max  x   min  x  N , где N — число корзин. Это можно сделать и по-другому — посредством равночастотной дискретизации, именуемой квартилями, которая обеспечивает, чтобы в каждой корзине было более-менее одинаковое число наблюдений. Правда, иногда, учитывая скошенную природу гистограммы, может оказаться так, что разбить их N способами будет невозможно, и в результате можно получить N  1 или N  2 квартилей. Оба подхода легко сравнить с помощью функции plot_prob_progression, но на этот раз мы строим два графика: один с корзинами фиксированной ширины (use_quartiles=False), а другой с квартилями (use_quartiles=True). Соответствующий фрагмент исходного кода можно увидеть ниже: mldatasets.plot_prob_progression(recidivism_df.age, recidivism_df.is_recid,\ x_intervals=7, use_quartiles=False,\ title='Вероятность рецидивизма по возрасту, дискретизированному в фиксир. корзины', xlabel='Возраст') mldatasets.plot_prob_progression(recidivism_df.age, recidivism_df.is_recid,\ x_intervals=7, use_quartiles=True,\ title='Вероятность рецидивизма по возрасту, дискретизированному в квартили',\ xlabel='Возраст') Приведенный фрагмент выводит на экран рис. 12.4. Если судить по части "Наблюдения" графика корзин фиксированной ширины, то гистограмма для возраста (age) скошена вправо, что приводит к выстреливанию вероятности вверх в последней корзине. Причина в том, что в этой корзине есть несколько выбросов. С другой стороны, гистограмма фиксированно-частотного (квартильного) графика является более ровной, и вероятность стабильно уменьшается. Другими словами, она является монотонной — как и должно быть, в соответствии с нашими знаниями предметной области. Легко заметить причину, по которой подход с использованием квантилей для группирования признака в корзины является более качественным. Можно взять возраст и сконструировать новый признак под названием age_group. Функция qcut библиоте-
526 Часть III. Настройка на интерпретируемость ки pandas может выполнять квантильную дискретизацию. Соответствующий фрагмент исходного кода показан ниже: recidivism_df['age_group'] = pd.qcut(recidivism_df.age, 7,\ precision=0).astype(str) Рис. 12.4. Сравнение двух подходов к дискретизации возраста Таким образом, теперь возраст (age) дискретизирован в возрастную группу (age_group). Однако следует отметить, что многие модельные классы дискретизируются автоматически, тогда зачем вообще беспокоиться? Потому что это позволяет вам контролировать модельные эффекты. В противном случае модель будет выбирать корзины, которые не обеспечивают монотонность. Например, модель может всегда, когда это возможно, использовать 10 квартилей. Тем не менее если вы попытаетесь использовать этот уровень гранулярности по возрасту (x_intervals=10), то получите всплески в прогрессии вероятности. Наша цель — обеспечить усвоение моделью факта о том, что возраст (age) и повторяемость рецидивов у ответчика в течение 2 лет (is_recid) имеют монотонную взаимосвязь, и мы не сможем это обеспечить, если позволим модели выбирать корзины, которые могут достигать или не достигать одинаковой цели. Мы удалим возраст, потому что в возрастной группе (age_group) есть всё, что нам нужно. Но вы наверняка спросите: не потеряем ли мы некоторую важную информацию, если удалим эту переменную? Да, утеряем, но только из-за его взаимодействия с числом предшествующих судимостей (priors_count). Так что, прежде чем отбрасывать какие-либо признаки, давайте проанализируем эту взаимосвязь и поймем, как, создав интеракционный член, можно удержать часть информации, теряемой в результате удаления возраста, сохранив при этом взаимодействие. Члены взаимодействия и нелинейные преобразования Из главы 7 мы уже знаем, что возраст (age) и число предшествующих судимостей (priors_count) являются двумя наиболее важными предсказателями, и благодаря функции plot_prob_contour_map легко можно увидеть, как они вместе влияют на повторяемость рецидивов (is_recid). Эта функция создает контурные линии с кодированными цветом участками контура, обозначающими разные магнитуды. Их широ-
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 527 ко применяют в топографии, где они показывают высоты реперных точек. В машинном обучении они могут отражать характер изменения метрики в участках в двух размерностях. В данном случае нашими размерностями являются возраст (age) и число предшествующих судимостей (priors_count), а метрикой — повторяемость рецидивов (is_recid). Получаемые этой функцией аргументы совпадают с аргументами функции plot_prob_progression, за исключением того, что она принимает два признака, относящиеся к осям x и y. Соответствующий фрагмент исходного кода можно увидеть ниже: mldatasets.plot_prob_contour_map(recidivism_df.age, recidivism_df.priors_count,\ recidivism_df.is_recid, use_quartiles=True,\ xlabel='Возраст', ylabel='Число предшествующих судимостей',\ title='Вероятность рецидива по возрасту/преступлениям, дискретизированная в квартилях') Рис. 12.5. Контурная карта вероятности рецидивизма для возраста (age) и числа предшествующих судимостей (priors_count) Приведенный выше фрагмент сгенерировал рис. 12.5, на котором показано, как при дискретизировании по квартилям вероятность двухгодичного рецидива увеличивается, чем меньше возраст (age) и чем выше число предшествующих судимостей
528 Часть III. Настройка на интерпретируемость (priors_count). Он также показывает гистограммы обоих признаков. Число предшествующих судимостей (priors_count) сильно скошено вправо, поэтому выполнить дискретизацию чрезвычайно сложно, а контурная карта не обеспечивает идеально диагональной прогрессии между правым нижним и левым верхним участками. И если этот график выглядит знакомым, то это потому, что он похож на интеракционные графики частичной зависимости, которые мы генерировали в главе 4, за исключением того, что он измеряется не относительно предсказаний, а относительно эмпирической истины (is_recid). Следует проводить различие между тем, что, собственно, данные могут отражать непосредственно, и тем, что модель из них усвоила. Теперь можно сконструировать интеракционный член, который включает в себя оба признака. Несмотря на то что контурная карта дискретизировала признаки, чтобы получить более плавную прогрессию, эту взаимосвязь дискретизировать не нужно. А вот сделать ее соотношением числа предшествующих судимостей (priors_count) в расчете на год имеет смысл. Но сколько лет прошло с тех пор? Конечно же, с тех пор, как ответчики достигли взрослого возраста, прошли годы. Однако для получения лет нельзя использовать возраст минус 18, потому что это приведет к делению на ноль, поэтому мы будем использовать 17. Разумеется, это можно сделать по-разному. Лучше всего было бы, если значения возрастов гипотетически были десятичными дробями, и, вычтя 18, мы могли бы вычислять очень точное соотношение числа предшествующих судимостей в год. Но, к сожалению, у нас этого нет. Соответствующий фрагмент исходного кода показан ниже: recidivism_df['priors_per_year'] =\ recidivism_df['priors_count']/(recidivism_df['age'] - 17) В типичной ситуации модели типа черного ящика находят членов взаимодействия автоматически. Например, скрытые слои в нейронной сети имеют все взаимодействия первого порядка, но из-за нелинейных активаций они не ограничиваются линейными комбинациями. Однако "ручное" определение членов взаимодействия и даже нелинейное преобразование позволяют нам интерпретировать их лучше после того, как модель подогнана. Кроме того, мы можем использовать на них монотонные ограничения, именно то, что мы сделаем позже с числом предшествующих судимостей в год (priors_per_year). А пока давайте проверим соблюдение монотонности с помощью графика прогрессии вероятности (plot_prob_progression). Взгляните на следующий ниже фрагмент исходного кода: mldatasets.plot_prob_progression(\ recidivism_df.priors_per_year, recidivism_df.is_recid, x_intervals=8,\ xlabel='Числа предшествующих судимостей в год',\ title='Вероятность рецидивизма по числам предшествующих преступлений в год (по данным)') Приведенный выше фрагмент выводит прогрессию на рис. 12.6, который показывает, что новый признак является почти монотонным. Причина, по которой числа предшествующих судимостей в год (priors_per_year) не имеют более высокую монотонность, заключается в степени разреженности интервала равной более 3,0 в год. Следовательно, было бы очень необъективно по отношению к этим немногим ответчикам применять к данному признаку монотонность, потому что они представляют падение риска на 75%. Эта проблема решается путем
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 529 сдвига указанных наблюдений влево с помощью установки для них значения priors_per_year=-1, как показано в следующем фрагменте исходного кода: recidivism_df.loc[recidivism_df.priors_per_year > 3, 'priors_per_year'] = -1 Рис. 12.6. Прогрессия вероятности для чисел предшествующих судимостей в год (priors_per_year) Разумеется, этот сдвиг совсем немного меняет интерпретацию признака, учитывая, что несколько значений 1 на самом деле означают более 3. Теперь давайте сгенерируем еще одну контурную карту, но на этот раз между возрастной группой (age_group) и количеством предшествующих судимостей в год (priors_per_year). Последние будут дискретизированы в квартилях (y_intervals=6, use_quartiles=True), чтобы легче наблюдать вероятность рецидивизма. Соотвествующий фрагмент исходного кода показан ниже: mldatasets.plot_prob_contour_map(recidivism_df.age_group,\ recidivism_df.priors_per_year, recidivism_df.is_recid,\ y_intervals=6, use_quartiles=True,\ xlabel='Возрастная группа',\ title='Вероятность рецидива по возрасту/числам судимостей в год дискретно в квартилях', ylabel='Число предшествующих судимостей в год') Приведенный фрагмент генерирует контуры на рис. 12.7. Он показывает, что график движется по большей части в одном направлении. Мы надеялись достичь этого исхода, потому что он позволяет нам с помощью одного признака взаимодействия контролировать монотонность того, что раньше включало два признака. Почти все готово, но признак возрастной группы (age_group) по-прежнему остается категориальным, поэтому необходимо его закодировать, чтобы он принял числовую форму.
530 Часть III. Настройка на интерпретируемость Рис. 12.7. Контурная карта вероятности рецидива для возрастной группы (age_group) и чисел предшествующих судимостей в год (priors_per_year) Категориальное кодирование Лучшим методом категориального кодирования возрастной группы (age_group) является порядковое кодирование, или так называемое кодирование меток, поскольку оно будет удерживать ее порядок. Кроме того, необходимо закодировать два других категориальных признака в наборе данных: пол и расу. Для пола порядковое кодирование преобразовывает его в двоичную форму, эквивалентную фиктивному кодированию. С другой стороны, кодировать расу сложнее, потому что она имеет три категории, и использование порядковой кодировки может привести к систематическому смещению. Вместе с тем ответ на вопрос: "Следует ли вместо этого использовать кодирование с одним активным состоянием?", — зависит от того, какие модельные классы применяются. Древовидные модели не имеют проблем с систематическим смещением порядковых признаков, но другие модели, которые работают с весами на попризнаковой основе, такие как нейронные сети и логистическая регрессия, в силу этого порядка могут усваивать неправильную информацию. С учетом того, что набор данных был сбалансирован по расе, риск, что всё будет происходить именно так, меньше, и мы всё равно удалим этот признак позже, поэтому продолжим в том же духе и выполним его порядковое кодирование.
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 531 Для порядкового кодирования трех признаков будем применять порядковый кодировщик (OrdinalEncoder) библиотеки scikit-learn. Он позволяет использовать его комбинированную функцию fit_transform для подгонки и преобразования признаков одним махом. Заодно необходимо также удалить ненужные признаки. Взгляните на следующий ниже фрагмент исходного кода: cat_feat_l = ['sex', 'race', 'age_group'] ordenc = preprocessing.OrdinalEncoder(dtype=np.int8) recidivism_df[cat_feat_l] = ordenc.fit_transform(recidivism_df[cat_feat_l]) recidivism_df.drop(['age', 'priors_count', 'compas_score'], axis=1, inplace=True) Но мы еще не совсем закончили. Еще нужно инициализировать случайные начальные числа и разбить данные на тренировочный/тестовый наборы. Другие подготовительные работы Следующие далее подготовительные работы довольно просты. В целях обеспечения воспроизводимости давайте зададим случайное начальное значение везде, где это необходимо, затем зададим переменную y признаком is_recid и переменную X всеми остальными признаками. На этих двух переменных мы выполняем функцию разбивки train_test_split. Наконец, мы реконструируем кадр данных recidivism_df с переменной X и последующей переменной y. Единственная причина такой последовательности состоит в том, чтобы признак is_recid был последним столбцом, что поможет нам на следующем шаге. Соответствующий исходный код приведен ниже: rand = 9 os.environ['PYTHONHASHSEED'] = str(rand) tf.random.set_seed(rand) np.random.seed(rand) y = recidivism_df['is_recid'] X = recidivism_df.drop(['is_recid'], axis=1).copy() X_train, X_test, y_train, y_test = model_selection.train_test_split(\ X, y, test_size=0.2, random_state=rand) recidivism_df = X.join(y) Теперь мы подтвердим, что корреляции Спирмена улучшились там, где это необходимо, и остаются прежними в противном случае. Взгляните на следующий фрагмент исходного кода: pd.DataFrame(\ {'feature': X.columns,\ 'correlation_to_target': scipy.stats.\ spearmanr(recidivism_df).correlation[10,:-1]}).\ style.background_gradient(cmap='coolwarm') Приведенный фрагмент выводит на экран кадр данных, показанный на рис. 12.8. Сравните его с рис. 12.2. Обратите внимание, что дискретизированный в квартилях возраст еще более монотонно коррелирует с целевой переменной. После порядкового кодирования признак c_charge_degree тоже намного более коррелирован, да и признак priors_per_year улучшился по сравнению с признаком priors_count. Никакие
532 Часть III. Настройка на интерпретируемость другие признаки не были затронуты, в том числе те, которые имеют самые низкие коэффициенты. Рис. 12.8. Коэффициенты корреляции Спирмена всех признаков по отношению к целевой переменной (после конструирования признаков) Признаки с наименьшими коэффициентами, вероятно, в модели тоже не нужны, но мы позволим модели принять решение об их полезности посредством регуляризации. Вот что мы будем делать дальше. Настройка моделей на интерпретируемость Традиционно регуляризация достигалась только путем накладывания штрафных членов, таких как L1, L2 или эластичная сеть, на коэффициенты или веса, которые сжимают влияние наименее релевантных признаков. Как видно из разд. "Анализ встроенных методов отбора признаков" главы 10, эта форма регуляризации приводит к отбору признаков при одновременном уменьшении переобучения. И это подводит нас к еще одному более широкому определению регуляризации, которое не требует штрафного члена. Часто она происходит как наложение предела или критерия остановки, который вынуждает модель сдерживать свою сложность. В дополнение к регуляризации, как в узком смысле (на основе штрафов), так и в широком смысле (с использованием методов обучения), существуют и другие методы, которые настраивают модель на интерпретируемость, т. е. повышают объективность, подотчетность и прозрачность модели путем внесения поправок в тренировочный процесс. Например, объективность повышают гиперпараметры классового дисбаланса, которые мы обсуждали в главе 10, а также антагонистическое устранение систематического смещения, которое мы рассматривали в главе 11. Кроме того, ограничения, которые мы будем изучать далее в этой главе, имеют потенциальные преимущества для объективности, подотчетности и прозрачности. Существует очень много разных возможностей настройки и в многочисленных модельных классах. Как указано в начале главы, мы сосредоточимся на опциях, связанных с интерпретируемостью, но также ограничим модельные классы популярной библиотекой глубокого обучения (Keras), несколькими популярными древо-
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 533 видными ансамблями (XGBoost, RandomForest и т. д.), опорно-векторными машинами (support vector machines, SVM) и логистической регрессией. За исключением последней, все они считаются моделями типа черного ящика. Настройка нейронной сети Keras Для модели Keras мы выберем наилучшие регуляризационные параметры с помощью гиперпараметрической настройки и многократной стратифицированной K-блочной кросс-валидации. Мы сделаем это, используя следующие шаги: 1. В начале нам необходимо определить модель и настроечные параметры. 2. Затем мы выполним настройку. 3. Далее мы проанализируем ее результаты. 4. Наконец, мы извлечем наилучшую модель и оценим ее предсказательную результативность. Давайте рассмотрим каждый из этих шагов подробно. Определение модели и настроечных параметров В начале необходимо создать функцию (build_nn_mdl), чтобы построить и скомпилировать регуляризуемую модель Keras. Указанная функция принимает аргументы, которые помогут настроить модель. Функция принимает кортеж с количеством нейронов в скрытых слоях (hidden_layer_sizes) и величину регуляризации L1 (l1_reg) и L2 (l1_reg), которую нужно применить на ядре слоя. Наконец, она принимает величину отсева (dropout), который, в отличие от штрафов L1 и L2, является методом стохастической регуляризации, поскольку в нем используется случайный отбор. Взгляните на следующий фрагмент исходного кода: def build_nn_mdl(hidden_layer_sizes, l1_reg=0, l2_reg=0, dropout=0): nn_model = tf.keras.Sequential([\ tf.keras.Input(shape=[len(X_train.keys())]),\ tf.keras.layers.experimental.preprocessing.Normalization()]) reg_args = {} if (l1_reg > 0) or (l2_reg > 0): reg_args = {'kernel_regularizer': tf.keras.regularizers.l1_l2(l1=l1_reg, l2=l2_reg)} for hidden_layer_size in hidden_layer_sizes: nn_model.add(tf.keras.layers.Dense(hidden_layer_size,\ activation='relu', **reg_args)) if dropout > 0: nn_model.add(tf.keras.layers.Dropout(dropout)) nn_model.add(tf.keras.layers.Dense(1, activation='sigmoid')) nn_model.compile(loss='binary_crossentropy',\ optimizer=tf.keras.optimizers.Adam(lr=0.0004),\ metrics=['accuracy',tf.keras.metrics.AUC(name='auc')]) return nn_model
534 Часть III. Настройка на интерпретируемость Приведенная выше функция инициализирует модель (nn_model) как последовательную (Sequential), в которой входной слой соответствует числу признаков в тренировочных данных, а слой нормализации (Normalization) стандартизирует данные на входе. Затем, если любой из штрафных членов превышает ноль, то она будет задавать словарь (reg_args) с ядерным регуляризатором (kernel_regularizer) в атрибуте tf.keras.regularizers.l1_l2, инициализированном этими штрафами. После добавления скрытых плотных (Dense) слоев с соответствующим размером скрытого слоя (hidden_layer_size) она будет передавать каждому слою словарь reg_args в качестве дополнительных аргументов. После того как все скрытые слои будут добавлены, она опционально будет добавлять слой отсева (Dropout) и последний плотный (Dense) слой с сигмоидной (sigmoid) активацией для выхода. Затем модель компилируется с помощью двоичной кросс-энтропии (binary_crossentropy) в качестве потери и оптимизатора Adam с низкой скоростью усвоения и настраивается для мониторинга метрик точности (accuracy) и площади под кривой (auc). Выполнение гиперпараметрической настройки Теперь, когда мы определили модель и настроечные параметры, мы инициализируем кросс-валидатор RepeatedStratifiedKFold, который разбивает (n_splits) тренировочные данные на пять блоков в общей сложности трижды (n_repeats), используя в каждом повторе разную рандомизацию. Затем мы создаем сетку (nn_grid) для гиперпараметрической настройки методом решеточного поиска. Он рассматривает только два возможных варианта трех параметров (l1_reg, l2_reg и dropout), что дает 23  8 комбинаций. Мы будем использовать обертку библиотеки scikit-learn (KerasClassifier), чтобы иметь модель, совместимую с решеточным поиском scikitlearn. Затем мы инициализируем объект GridSearchCV, который, используя модель Keras (estimator), выполняет кросс-валидированный (cv) решеточный поиск (param_grid) параметров. Мы хотим, чтобы он выбирал наилучшие параметры на основе точности (scoring), а также по ходу не вызывал ошибок (error_score=0). Наконец, мы выполняем обучение объекта GridSearchCV, как и с любой моделью Keras, передав X_train, y_train, эпохи (epochs) и размер пакета (batch_size). Соответствующий фрагмент исходного кода показан ниже: cv = model_selection.RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=rand) nn_grid = {'hidden_layer_sizes':[(80,)], 'l1_reg':[0,0.005], 'l2_reg':[0,0.01], 'dropout':[0,0.05]} nn_model = KerasClassifier(build_fn=build_nn_mdl) nn_grid_search = model_selection.GridSearchCV(\ estimator=nn_model, cv=cv, n_jobs=-1,\ param_grid=nn_grid, scoring='precision', error_score=0, verbose=0) nn_grid_result = nn_grid_search.fit(X_train.astype(float), y_train.astype(float),\ epochs=400, batch_size=128, verbose=0)
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 535 Анализ результатов После завершения решеточного поиска можно вывести наилучшие параметры словаря на экран с помощью инструкции: print(nn_grid_result.best_params_). Либо можно поместить все результаты в кадр данных, отсортировать их по убыванию точности (sort_values) и вывести на экран следующим образом: pd.DataFrame(nn_grid_result.cv_results_) [['param_hidden_layer_sizes','param_l1_reg',\ 'param_l2_reg', 'param_dropout',\ 'mean_test_score', 'std_test_score',\ 'rank_test_score']].sort_values(by='rank_test_score') Приведенный фрагмент выводит на экран кадр данных, показанный на рис. 12.9. Нерегуляризованная модель является безнадежным аутсайдером, демонстрируя, что все комбинации регуляризованных моделей показали более высокую результативность. Следует отметить, что в данном случае с учетом примерно 1,5–2% стандартных отклонений (std_ test_score) и того, что высший показатель отстоит от низшего всего на 2,2%, выгоды с точки зрения точности являются маргинальными; но несмотря на это, учитывая другие выгоды, следует использовать регуляризованную модель. Рис. 12.9. Результаты кросс-валидированного решеточного поиска в нейросетевой модели Оценивание наилучшей модели Решеточный поиск произвел еще один важный элемент, а именно, наиболее результативную модель (nn_grid_result.best_estimator_). Для хранения всех моделей, обучение которых мы выполним в этой главе (fitted_class_mdls), можно создать словарь, а затем, используя функцию evaluate_ class_mdl, оценить эту регуляризованную модель Keras, при этом держа оценивание в словаре. Взгляните на следующий фрагмент исходного кода: fitted_class_mdls = {} fitted_class_mdls['keras_reg'] = mldatasets.evaluate_class_mdl(\ nn_grid_result.best_estimator_,\ X_train.astype(float), X_test.astype(float),\ y_train.astype(float), y_test.astype(float),\ plot_roc=False, plot_conf_matrix=True, ret_eval_dict=True)
536 Часть III. Настройка на интерпретируемость Приведенный фрагмент сгенерировал матрицу путаницы и метрики, показанные на рис. 12.10. Точность стала чуть-чуть лучше, чем у изначальной модели COMPAS из главы 7, но стратегия оптимизирования для получения боле высокой точности при регуляризации породила модель с почти вдвое меньшим числом ложноположительных классификаций, но с 50%-ным увеличением числа ложноотрицательных классификаций. Рис. 12.10. Оценивание регуляризованной модели Keras Калибровку классового баланса можно улучшить еще больше, используя прикладную функцию потерь или веса классов, как мы сделаем позже. Далее мы рассмотрим вопрос настройки других модельных классов. Настройка других популярных модельных классов В этом разделе мы выполним обучение разных многочисленных моделей, как регуляризованных, так и нерегуляризованных. С этой целью будем выбирать параметры из широкого спектра, которые выполняют штрафуемую регуляризацию, контролируют переобучение другими средствами и учитывают классовый дисбаланс. Краткое введение в релевантные модельные параметры Для справки приведем две таблицы параметров, используемых для настройки многочисленных популярных моделей. Они были разделены на две части. Часть А (рис. 12.11) содержит пять моделей библиотеки scikit-learn со штрафуемой регуляризацией. Часть В (рис. 12.12) содержит все древовидные ансамбли, включая модели scikit-learn на основе случайного леса и модели из самых популярных библиотек бустированных деревьев (XGBoost, LightGBM и CatBoost). На рис. 12.11 модели расположены в столбцах, а соответствующие имена параметров — в строках, при этом справа указаны их значения по умолчанию. Между именем параметра и значением по умолчанию есть знак " + /–", указывающий, должно ли изменение значений в ту или иную сторону делать модель консервативнее.
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость ПЕРЕОБУЧЕНИЕ LogisticRegression алгоритм solver регуляризация penalty C l1_ratio max_iter "l2" +/– 1 None +/– 100 tol – 1e–4 итерации скорость усвоения ранняя остановка RidgeClassifier SVC NuSVC Ridge SVR NuSVR "lbfgs" solver alpha "auto" kernel +/– 1 "rbf" kernel MLPClassifier MLPRegressor "rbf" solver "adam" +/– 0,0001 max_iter C gamma + None max_iter +/– 1 nu "scale" gamma +/– 1 max_iter +/– 0,5 alpha "scale" +/– 1 max_iter learning_rate_init learning_rate tol – 1e–3 tol – 1e–3 tol – 1e–3 tol – 1e–4 n_iter_no_change – 10 early_stopping False validation_fraction 0,1 классовый дисбаланс class_weight None class_weight None class_weight None class_weight None вес образца None sample_weight* None sample_weight* None sample_weight* None sample_weight* 537 +/– 200 0,001 "adaptive" Рис. 12.11. Параметры настройки для моделей библиотеки scikit-learn, регуляризуемых на основе штрафа Эти параметры также сгруппированы по следующим категориям:  алгоритм: некоторые алгоритмы обучения менее склонны к переобучению, хотя часто это зависит от данных;  регуляризация: только в более строгом смысле; другими словами, параметры, которые управляют штрафной регуляризацией;  итерации: эта категория определяет число выполняемых тренировочных раун- дов, итераций или эпох. Корректировка итерации в том или ином направлении может повлиять на переобучение. В древовидных моделях число оценщиков аналогично числу деревьев;  скорость усвоения: управляет скоростью, с которой происходит усвоение. Она работает в тандеме с итерациями. Чем ниже скорость усвоения, тем больше требуется итераций для оптимизации целевой функции. Вместе с тем, если вы захотите прервать обучение до того, как модель начнет переобучаться, вам требуется ранняя остановка;  ранняя остановка: эти параметры определяют момент, когда следует прекра- тить обучение;  классовый дисбаланс: в большинстве моделей он штрафует ошибочные клас- сификации на меньших классах в функции потерь, а в древовидных моделях, в частности, он используется для перевесовки критерия расщепления. В любом случае, он работает только с классификаторами;  вес образца: использовался в главе 11, чтобы мы могли назначать веса на основе образцов с целью ослабления систематического смещения. В заголовках представлены как классификационные, так и регрессионные модели, и они имеют одинаковые параметры. Обратите внимание, что линейная регрессия библиотеки scikit-learn не включена в раздел LogisticRegression, поскольку регуляризация в нее не встроена. В любом случае, в данном разделе мы будем использовать только классификационные модели.
Рис. 12.12. Настроечные параметры в древовидных ансамблевых моделях 538 Часть III. Настройка на интерпретируемость
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 539 Рис. 12.12 очень похож на рис. 12.11, за исключением того, что он содержит еще несколько параметрических категорий, доступных лишь в древовидных ансамблях:  выборка признаков: работает путем учета меньшего числа признаков в расще- плениях узлов, узлах или при обучении дерева. Это метод стохастической регуляризации, поскольку признаки отбираются случайно;  размер дерева: ограничение дерева максимальной глубиной, либо максималь- ным числом листьев, либо каким-либо другим параметром, ограничивающим его рост, что, в свою очередь, сдерживает его переобучение;  расщепление: любой параметр, который контролирует способ расщепления уз- лов в дереве, может косвенно влиять на переобучение;  бэггинг: так называемое агрегирование бутстраповских выборок. Он начина- ется с бутстрапирования, которое предусматривает взятие случайных выборок из тренировочных данных, чтобы выполнять обучение слабых учеников. Этот метод уменьшает дисперсию и помогает справляться с переобучением, и, как следствие, в гиперпараметрической настройке обычно преобладают соответствующие выборочные параметры;  ограничения: мы дадим их более подробное объяснение в следующем разделе, но по своей сути они показывают, как признаки должны сковываться в уменьшении или увеличении относительно результата на выходе. Ограничение может уменьшать переобучение в областях, в которых данные очень разрежены. Однако уменьшение переобучения обычно не является главной целью, тогда как ограничения взаимодействия могут лимитировать перечень признаков, способных взаимодействовать. Обратите внимание, что на рис. 12.12 параметры со звездочкой (*) обозначают параметры, устанавливаемые в методе fit(), в отличие от параметров, инициализируемых моделью. Кроме того, за исключением моделей RandomForest библиотеки scikit-learn, все остальные параметры в типичной ситуации имеют много псевдонимов. Для этого мы используем оберточные функции scikit-learn, но все параметры также существуют в нативных версиях. Здесь мы просто не в состоянии дать пояснение по каждому модельному параметру, но, чтобы получить более полное представление о том, что делает каждый из них, рекомендуется обратиться непосредственно к документации. Смысл этого раздела — служить руководством или справочником. Далее мы предпримем шаги, аналогичные тем, которые мы делали с моделью Keras, но для многочисленных разных моделей сразу, и, наконец, выполним диагностику наилучшей модели с точки зрения объективности. Пакетная гиперпараметрическая настройка моделей Итак, теперь, когда мы прошли краткий ускоренный курс по рычагам, используемым для настройки моделей, давайте определим словарь со всеми моделями, как это делалось в других главах. На этот раз мы включили решетку (grid) с несколь-
540 Часть III. Настройка на интерпретируемость кими значениями параметров для поиска в ней. Взгляните на следующий ниже фрагмент исходного кода: class_mdls = { 'logistic':{ 'model':linear_model.LogisticRegression(random_state=rand, max_iter=1000), 'grid':{'C':np.linspace(0.01, 0.49, 25), 'class_weight':[{0:6,1:5}], 'solver':['lbfgs', 'liblinear', 'newton-cg']}}, 'svc':{'model':svm.SVC(probability=True, random_state=rand), 'grid':{'C':[15,25,40], 'class_weight':[{0:6,1:5}]}}, 'nu-svc':{'model':svm.NuSVC(probability=True, random_state=rand), 'grid':{'nu':[0.2,0.3], 'gamma':[0.6,0.7], 'class_weight':[{0:6,1:5}]}}, 'mlp':{'model':neural_network.MLPClassifier(random_state=rand, hidden_layer_sizes=(80,), early_stopping=True), 'grid':{'alpha':np.linspace(0.05, 0.15, 11), 'activation':['relu','tanh','logistic']}}, 'rf':{'model':ensemble.RandomForestClassifier(random_state=rand, max_depth=7, oob_score=True, bootstrap=True), 'grid':{'max_features':[6,7,8], 'max_samples':[0.75,0.9,1], 'class_weight':[{0:6,1:5}]}}, 'xgb-rf':{'model':xgb.XGBRFClassifier(seed=rand, eta=1, max_depth=7, n_estimators=200), 'grid':{'scale_pos_weight':[0.85], 'reg_lambda':[1,1.5,2], 'reg_alpha':[0,0.5,0.75,1]}}, 'xgb':{'model':xgb.XGBClassifier(seed=rand, eta=1, max_depth=7), 'grid':{'scale_pos_weight':[0.7], 'reg_lambda':[1,1.5,2], 'reg_alpha':[0.5,0.75,1]}}, 'lgbm':{'model':lgb.LGBMClassifier(random_seed=rand, learning_rate=0.7, max_depth=5), 'grid':{'lambda_l2':[0,0.5,1], 'lambda_l1':[0,0.5,1], 'scale_pos_weight':[0.8]}}, 'catboost':{'model':cb.CatBoostClassifier(random_seed=rand, depth=5, learning_rate=0.5, verbose=0), 'grid':{'l2_leaf_reg':[2,2.5,3], 'scale_pos_weight':[0.65]}} } Следующий шаг — добавление в каждую модель словаря в цикле for, затем глубокая копия (deepcopy) словаря и подгонка (fit) его элементов для создания "базовой" нерегуляризованной модели. Далее мы выполним ее оценивание с помощью функции evaluate_class_mdl и сохраним ее в словаре fitted_class_mdls, который ранее создали для модели Keras. Теперь необходимо сгенерировать регуляризованную версию модели. И поэтому мы делаем еще одну глубокую копию (deepcopy) и продолжаем выполнять те же шаги, которые предприняли с Keras, чтобы выполнить кросс-валидированный решеточный поиск на основе экземпляра класса RepeatedStratifiedKFold с помощью объекта GridSearchCV, и мы также выполняем оценивание в таком же по-
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 541 рядке, сохраняя результаты в словаре подогнанных моделей. Соответствующий фрагмент исходного кода показан ниже: for mdl_name in class_mdls: print(mdl_name) base_mdl = copy.deepcopy(class_mdls[mdl_name]['model']) base_mdl = base_mdl.fit(X_train, y_train) fitted_class_mdls[mdl_name+'_base'] = mldatasets.evaluate_class_mdl(\ base_mdl, X_train, X_test, y_train, y_test, plot_roc=False,\ plot_conf_matrix=False, show_summary=False, ret_eval_dict=True) reg_mdl = copy.deepcopy(class_mdls[mdl_name]['model']) grid = class_mdls[mdl_name]['grid'] cv = model_selection.RepeatedStratifiedKFold(n_splits=5, n_repeats=3,\ random_state=rand) grid_search = model_selection.GridSearchCV(estimator=reg_mdl, cv=cv,\ param_grid=grid,\ scoring='precision', n_jobs=-1,\ error_score=0, verbose=0) grid_result = grid_search.fit(X_train, y_train) fitted_class_mdls[mdl_name+'_reg'] = mldatasets.evaluate_class_mdl( grid_result.best_estimator_, X_train, X_test, y_train, y_test,\ plot_roc=False, plot_conf_matrix=False, show_summary=False,\ ret_eval_dict=True) fitted_class_mdls[mdl_name+'_reg']['cv_best_params'] = grid_result.best_params_ По завершении работы приведенного выше фрагмента можно выполнить ранжирование моделей по точности. Оценивание моделей по точности Метрики словаря подогнанных моделей можно извлечь и поместить в кадр данных с помощью библиотечного метода from_dict(). Затем можно отсортировать модели по их точности на валидационной выборке и закодировать цветом два наиболее важных столбца, т. е. столбцы precision_test и recall_test. Соответствующий фрагмент исходного кода показан ниже: class_metrics = pd.DataFrame.from_dict( fitted_class_mdls,'index')[['accuracy_train', 'accuracy_test', 'precision_train', 'precision_test', 'recall_train', 'recall_test', 'roc-auc_test', 'f1_test', 'mcc_test']] with pd.option_context('display.precision', 3): html = class_metrics.sort_values(by='precision_test', ascending=False).\ style.background_gradient(cmap='plasma', subset=['precision_test']).\ background_gradient(cmap='viridis', subset=['recall_test']) html Приведенный выше фрагмент выводит на экран кадр данных, показанный на рис. 12.13. Из рисунка следует, что регуляризованные модели на основе древовидных ансамблей управляют рангами. Единственное исключение — регуляризован-
542 Часть III. Настройка на интерпретируемость ная модель Nu-SVC, которая стоит вторым номером, и ее нерегуляризованная версия, которая является безнадежным аутсайдером! Рис. 12.13. Лучшие модели в соответствии с кросс-валидированным решеточным поиском Регуляризованная нейросетевая модель Keras имеет меньшую точность, чем логистическая регрессия, но более высокую полноту. Следует признать, что мы хотим выполнить оптимизацию под высокую точность, потому что она влияет на ложноположительные классификации, которые мы хотим минимизировать, но точность может составлять 100%, а полнота — 0%, и если это так, то ваша модель никуда не годится. В то же время еще есть объективность, которая заключается в наличии низкой частоты ложноположительных классификаций, будучи при этом равно распределенным по расам. Таким образом, мы имеем некий уравновешивающий акт, и погоня за одной метрикой к нему не приведет. Диагностика объективности наиболее результативной модели Для того чтобы определить, как действовать дальше, сначала необходимо диагностировать качество наиболее результативной модели с точки зрения объективности. Это делается с помощью функции сравнения матриц путаницы (compare_confusion_matrices). Как и в случае с функцией confusion_matrix библиотеки scikit-learn, ее первым аргументом являются значения эмпирической истины, или целевой переменной (часто именуемые y_true), а вторым — модельные предсказания (часто именуемые y_pred). Разница здесь в том, что указанная функция принимает два набора — y_true и y_pred, первый из которых соответствует одному сегменту наблюдений, а второй — другому. После этих первых четырех аргументов каждому сегменту задается имя, и им посвящены следующие два аргумента. Наконец, аргумент compare_fpr=True обеспечивает сравнение частот ложноположительных (FPR) классификаций между обеими матрицами путаницы. Взгляните на следующий фрагмент исходного кода: y_test_pred = fitted_class_mdls['catboost_reg']['preds_test'] _ = mldatasets.compare_confusion_matrices(\ y_test[X_test.race==1], y_test_pred[X_test.race==1],\ y_test[X_test.race==0], y_test_pred[X_test.race==0],\
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 543 'Caucasian', 'AfricanAmerican', compare_fpr=True) y_test_pred = fitted_class_mdls['catboost_base']['preds_test'] _ = mldatasets.compare_confusion_matrices(\ y_test[X_test.race==1], y_test_pred[X_test.race==1],\ y_test[X_test.race==0], y_test_pred[X_test.race==0],\ 'Caucasian', 'AfricanAmerican', compare_fpr=True) Приведенный выше фрагмент выводит на экран рис. 12.14 и 12.15, соответствующие регуляризованной и базовой моделям в указанном порядке. Рис. 12.14. Матрицы путаницы между расами в регуляризованной модели CatBoost На рис. 12.15 показано, что в регуляризованной модели частоты ложноположительных (FPR) классификаций ниже. Рис. 12.15. Матрицы путаницы между расами в базовой модели CatBoost
544 Часть III. Настройка на интерпретируемость Однако базовая модель на рис. 12.15 имеет соотношение частот ложноположительных классификаций 1,08 по сравнению с 1,09 в регуляризованной модели, а также слегка меньшую объективность, несмотря на более качественные совокупные метрики. Этого различия недостаточно, чтобы оправдать использование базовой модели. Но при попытке достичь нескольких целей одновременно трудно оценивать и сравнивать модели объективно, и именно этим мы займемся в следующем разделе. Оптимизация под объективность с помощью байесовой гиперпараметрической настройки и прикладных метрик Наша миссия состоит в том, чтобы построить модель с высокой точностью и хорошей полнотой, сохраняя при этом непредвзятость с точки зрения рас. И поэтому для того чтобы оставаться верными всей миссии, нам потребуется разработать прикладную метрику. Разработка прикладной метрики Мы могли бы использовать балл F1, но он трактует точность и полноту одинаково, поэтому придется создать взвешенную метрику. Заодно мы также можем учесть характер распределения точности и полноты по каждой расе. Это можно сделать путем применения стандартного отклонения, которое дает количественную оценку дисперсии в этом распределении. С этой целью мы будем штрафовать точность половиной межгруппового стандартного отклонения точности, и, за неимением более подходящего термина, эту величину можно назвать оштрафованной точностью. Соответствующая формула показана ниже: 1 Pоштрафованная  P   P . 2 То же самое можно сделать и для полноты, как показано ниже: 1 Rоштрафованная  R   R . 2 Затем мы берем средневзвешенные значения оштрафованной точности и полноты, где точность стóит вдвое больше, чем полнота, как показано ниже: прикладная метрика  2 Pоштрафованная  Rоштрафованная . 3 В целях вычисления этой новой метрики необходимо создать функцию, которую можно назвать weighted_penalized_pr_average. Она принимает y_true и y_pred в качестве метрик предсказательной результативности. Однако она также включает X_group с рядом или массивом pandas, содержащим значения группы, и group_vals со списком значений, по которым она будет брать подмножество предсказаний. В этом случае группой является раса, в которой могут быть значения от 0 до 2. Указанная функция содержит цикл for, который перебирает эти возможные значения, беря подмножества предсказаний по каждой группе. Благодаря этому она может вычислять точность и полноту по каждой группе. Остальная часть функции затем
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 545 просто выполняет описанные выше три математические операции. Соответствующий фрагмент исходного кода показан ниже: def weighted_penalized_pr_average(y_true, y_pred, X_group, group_vals,\ penalty_mult=0.5, precision_mult=2, recall_mult=1): precision_all = metrics.precision_score(y_true, y_pred, zero_division=0) recall_all = metrics.recall_score(y_true, y_pred, zero_division=0) p_by_group = [] r_by_group = [] for group_val in group_vals: in_group = X_group==group_val p_by_group.append(metrics.\ precision_score(y_true[in_group], y_pred[in_group], zero_division=0)) r_by_group.append(metrics.\ recall_score(y_true[in_group], y_pred[in_group], zero_division=0)) precision_all = precision_all - (np.array(p_by_group).std()*penalty_mult) recall_all = recall_all - (np.array(r_by_group).std()*penalty_mult) return ((precision_allprecision_mult)+ \ (recall_allrecall_mult))/(precision_mult+recall_mult) Теперь для приведения этой функции в действие необходимо выполнить настройку. Выполнение байесовой гиперпараметрической настройки Байесова оптимизация — — это метод глобальной оптимизации, в котором используется апостериорное распределение целевых функций типа черного ящика и их параметры с непрерывным диапазоном знечний. Другими словами, указанный метод проводит итеративный поиск наилучших параметров, перебирая различные значения на каждом шаге, каждый раз руководствуясь значениями, полученным на прошлых шагах. В отличие от решеточного поиска, в нем не участвуют фиксированные комбинации находящихся в решетке параметров, а используется то, что он уже знает из предыдущего и текущего шагов иттераций. Библиотека байесовой оптимизации bayesian-optimization является модельноагностической. Ей требуются лишь функция и параметры с их границами. Она проводит разведку значений этих параметров в указанных пределах. Функция принимает эти параметры и возвращает число. Оно представляет собой число, или цель, которое максимизируется алгоритмом байесовой оптимизации. Следующий ниже исходный код предназначен для целевой (objective) функции, которая инициализирует объект многократной стратифицированной K-блочной кросс-валидации (RepeatedStratifiedKFold) с четырьмя разбивками и тремя повторами. Затем он прокручивает все разбивки в цикле и выполняет на них обучение классификатора CatBoost (CatBoostClassifier). Наконец, он вычисляет прикладную метрику weighted_penalized_pr_average для каждого обучения модели и добавляет ее в список. В результате указанная функция возвращает медиану прикладной метрики для всех 12 тренировок. Соответствующий фрагмент исходного кода показан ниже: def hyp_catboost(l2_leaf_reg, scale_pos_weight): cv = model_selection.RepeatedStratifiedKFold(n_splits=4, n_repeats=3,\ random_state=rand)
546 Часть III. Настройка на интерпретируемость metric_l = [] for train_index, val_index in cv.split(X_train, y_train): X_train_cv, X_val_cv = X_train.iloc[train_index], X_train.iloc[val_index] y_train_cv, y_val_cv = y_train.iloc[train_index], y_train.iloc[val_index] mdl = cb.CatBoostClassifier(random_seed=rand, learning_rate=0.5,\ verbose=0, depth=5, l2_leaf_reg=l2_leaf_reg,\ scale_pos_weight=scale_pos_weight) mdl = mdl.fit(X_train_cv, y_train_cv) y_val_pred = mdl.predict(X_val_cv) metric = weighted_penalized_pr_average(y_val_cv, y_val_pred,\ X_val_cv['race'], range(3)) metric_l.append(metric) return np.median(np.array(metric_l)) Теперь, когда функция определена, выполнить процесс байесовой оптимизации несложно. Сначала надо задать словарь границ параметров (pbounds), инициализировать байесову оптимизацию с помощью функции hyp_catboost, а затем ее выполнить с помощью функции maximize. Функция maximize принимает значения точек инициализации (init_points), задающих число итераций, которые она должна выполнить изначально, используя случайный поиск. Затем идет n_iter — число итераций оптимизации, которые она должна выполнить, чтобы отыскать максимальное значение. Мы установим init_points и n_iter равными, соответственно, 3 и 7, потому что выполнение функции может занять много времени, но чем больше эти числа, тем лучше. Вот фрагмент исходного кода: pbounds = {'l2_leaf_reg': (2,4), 'scale_pos_weight': (0.55,0.85)} optimizer = BayesianOptimization(hyp_catboost, pbounds, random_state=rand) optimizer.maximize(init_points=3, n_iter=7) После этого к лучшим параметрам можно обращаться следующим образом: print(optimizer.max['params']) Указанная инструкция вернет словарь со следующими параметрами: {'l2_leaf_reg': 2.0207483077713997, 'scale_pos_weight': 0.7005623776446217} Теперь давайте выполним обучение модели с этими параметрами и оценим его качество. Обучение и оценивание модели с наилучшими параметрами Инициализировать объект CatBoostClassifier с этими параметрами очень просто — надо лишь передать словарь best_params в качестве аргумента. Затем остается лишь выполнить подгонку (fit) и оценивание модели (evaluate_class_mdl). Соответствующий фрагмент исходного кода показан ниже: cb_opt = cb.CatBoostClassifier(random_seed=rand, depth=5,\ learning_rate=0.5, verbose=0, **optimizer.max['params']) cb_opt = cb_opt.fit(X_train, y_train) fitted_class_mdls['catboost_opt'] = mldatasets.evaluate_class_mdl(\ cb_opt, X_train, X_test, y_train, y_test,\ plot_roc=False, plot_conf_matrix=True, ret_eval_dict=True)
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 547 Приведенный фрагмент выводит на экран метрики предсказательной результативности: Accuracy_train: 0.9721 Precision_test: 0.8354 ROC-AUC_test: 0.8815 Accuracy_test: 0.8282 Recall_test: 0.8244 F1_test: 0.8299 MCC_test: 0.6564 Это наивысшие метрики Accuracy_test, Precision_test и Recall_test, которых мы достигли к настоящему моменту. Давайте теперь посмотрим, насколько модель справляется с объективностью, применив функцию compare_confusion_matrices. Взгляните на следующий фрагмент исходного кода: y_test_pred = fitted_class_mdls['catboost_opt']['preds_test'] _ = mldatasets.compare_confusion_matrices(\ y_test[X_test.race==1], y_test_pred[X_test.race==1],\ y_test[X_test.race==0], y_test_pred[X_test.race==0],\ 'Caucasian', 'AfricanAmerican', compare_fpr=True) Приведенный фрагмент выводит рис. 12.16, на котором показаны наилучшие метрики объективности, которые мы получили до сих пор. Рис. 12.16. Сравнение матриц путаницы между расами в оптимизированной модели CatBoost Эти результаты хороши, но мы не можем быть полностью уверены в том, что модель не является расово систематически смещенной, потому что этот признак все еще присутствует. Измерить его влияние можно путем применения методов определения важности признаков. Исследования расового систематического смещения посредством метода определения важности признаков Несмотря на то что CatBoost является наиболее результативной моделью, мы продолжим работать с XGBoost, потому что CatBoost не поддерживает ограничения взаимодействия — имплементацией этих ограничений мы займемся в следующем разделе. Но сначала сравним их обе с точки зрения того, что они сочли важным.
548 Часть III. Настройка на интерпретируемость К тому же, значения аддитивных объяснений Шепли (SHAP) обеспечивают надежное средство для измерения и визуализации важности признаков, поэтому давайте вычислим их для нашей оптимизированной модели CatBoost и регуляризованной модели XGBoost. Для этого с каждой моделью необходимо инициализировать древовидный объяснитель (TreeExplainer), а затем использовать shap_values для получения соответствующих значений аддитивных объяснений Шепли по каждой, как показано в следующем фрагменте исходного кода: fitted_cb_mdl = fitted_class_mdls['catboost_opt']['fitted'] shap_cb_explainer = shap.TreeExplainer(fitted_cb_mdl) shap_cb_values = shap_cb_explainer.shap_values(X_test) fitted_xgb_mdl = fitted_class_mdls['xgb_reg']['fitted'] shap_xgb_explainer = shap.TreeExplainer(fitted_xgb_mdl) shap_xgb_values = shap_xgb_explainer.shap_values(X_test) Теперь можно сгенерировать два графика summary_plot, используя функцию subplot библиотеки matplotlib, следующим образом: ax0 = plt.subplot(1, 2, 1) shap.summary_plot(shap_xgb_values, X_test, plot_type="dot", plot_size=None, show=False) ax0.set_title("Сводка XGBoost SHAP", fontsize=15) ax1 = plt.subplot(1, 2, 2) shap.summary_plot(shap_cb_values, X_test, plot_type="dot", plot_size=None, show=False) ax1.set_title("Сводка Catboost SHAP", fontsize=15) Приведенный фрагмент генерирует рис. 12.17, который показывает схожесть моделей CatBoost и XGBoost. Это сходство не должно удивлять, потому что, в конце концов, они обе являются градиентно-бустированными деревьями решений. Плохая же новость состоит в том, что раса в обеих является четвертой. Однако преобладание оттенка, соответствующего более низким значениям признаков справа, говорит о том, что афроамериканцы (race=0) коррелируют с рецидивизмом отрицательно. В любом случае имеет смысл удалить расу из тренировочных данных, но сначала необходимо выяснить, почему модель считает, что этот признак является критически важным. Взгляните на следующий фрагмент исходного кода: shap_xgb_interact_values = shap_xgb_explainer.shap_interaction_values(X_test) В главе 5 мы обсудили тему диагностирования эффектов взаимодействия (или интеракционных эффектов). Самое время вернуться к этой теме, но на этот раз мы будем извлекать указанные интеракционные значения аддитивных объяснений Шепли (SHAP) (shap_interaction_values) вместо использования графиков зависимости SHAP. Взаимодействия SHAP можно легко ранжировать с помощью графика summary_plot. Сводный график SHAP очень информативен, но он далеко не так интуитивно понятен, как теплокарта взаимодействия. В целях генерирования теплокарты с метками необходимо поместить суммированные по первой оси значения shap_xgb_interact_values в кадр данных, затем назвать столбцы и строки (index) именами признаков. Остальное просто использует функцию heatmap библиотеки seaborn для выведения на экран кадра данных в виде теплокарты.
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 549 Соответствующий фрагмент исходного кода показан ниже: shap_xgb_interact_avgs = np.abs(shap_xgb_interact_values).mean(0) np.fill_diagonal(shap_xgb_interact_avgs, 0) shap_xgb_interact_df = pd.DataFrame(shap_xgb_interact_avgs) shap_xgb_interact_df.columns = X_test.columns shap_xgb_interact_df.index = X_test.columns sns.heatmap(shap_xgb_interact_df, cmap='Blues', annot=True,\ annot_kws={'size':13}, fmt='.2f', linewidths=.5) Рис. 12.17. Сводный график значений SHAP в регуляризованной модели XGBoost и оптимизированной модели CatBoost Приведенный фрагмент сгенерировал теплокарту, показанную на рис. 12.18. Она демонстрирует самое сильное взаимодействие расы с продолжительностью пребывания в заключении (length_of_stay), возрастной группой (age_group) и предшествующими судимостями в год (priors_per_year) Эти взаимодействия, разумеется, исчезнут, как только мы удалим расу. Однако, учитывая эту находку, следует тщательно рассмотреть вопрос о том, не встроены ли в эти признаки расовые систематические смещения. Исследования подтверждают необходимость возрастной группы (age_group) и предшествующих судимостей в год (priors_per_year), что оставляет продолжительность пребывания в заключении (length_of_stay) в качестве кандидата для проверки. Мы не будем делать этого в данной главе, но указанное соображение, безусловно, дает пищу для размышлений. Еще один интересный вывод из рис. 12.18 состоит в том, как признаки могут кластеризоваться. Можно вполне себе нарисовать прямоугольник вокруг нижнего правого квадранта между степенью преступления (c_charge_degree) и предшествующими судимостями в год (priors_per_year), потому что, как только мы удалим расу,
550 Часть III. Настройка на интерпретируемость бóльшая часть взаимодействия будет расположена здесь. В ограничивании тревожащих взаимодействий есть много выгод. Например, почему все признаки ювенальной преступности, такие как juv_fel_count, должны взаимодействовать с возрастной группой (age_group)? Почему пол (sex) должен взаимодействовать с продолжительностью пребывания в заключении (length_of_stay)? Далее мы узнаем, как разместить ограждение вокруг нижнего правого квадранта, ограничив взаимодействие между этими признаками с помощью ограничений взаимодействий. Мы также обеспечим монотонность для предшествующих судимостей в год (priors_per_year) с помощью монотонных ограничений. Рис. 12.18. Теплокарта со значениями взаимодействий SHAP в регуляризованной модели XGBoost Имплементирование модельных ограничений Далее мы обсудим тему имплементирования ограничений сначала с моделью XGBoost и всеми популярными древовидными ансамблями, если на то пошло, потому что параметры имеют одинаковые имена (см. рис. 12.12). Затем мы сделаем это с использованием библиотеки TensorFlow Lattice. Но прежде давайте удалим расу из данных следующим образом: X_train_con = X_train.drop(['race'], axis=1).copy() X_test_con = X_test.drop(['race'], axis=1).copy()
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 551 Теперь, когда раса вне игры, предоставленная самой себе модель все еще может иметь некоторую систематическую смещенность. Однако выполненное нами конструирование признаков и ограничения, которые мы установим, помогут привести модель в соответствие с ними, если учесть двойные стандарты, которые мы обнаружили в главе 7. При этом результирующая модель может работать хуже относительно тестовых данных. Для этого есть две причины.  Потеря информации. Раса влияла на модельный исход, в особенности посред- ством взаимодействия с другими признаками, поэтому, к сожалению, она несла некоторую информацию.  Несоответствие между реальностью и идеалами, определяемыми политикой возникает, когда главной причиной применения этих ограничений является обеспечение соответствия модели не только знаниям предметной области, но и идеалам, а это бывает неочевидно в данных. Необходимо помнить, что эмпирическая истина могла быть испорчена целым рядом институциональных проявлений расизма. Модель отражает данные, но данные отражают реальность эмпирически, по месту возникновения, что само по себе необъективно. С учетом всего этого давайте начнем с имплементации ограничений! Ограничения в XGBoost В этом разделе мы сделаем три простых шага. Сначала мы зададим параметры обучения, затем обучим и оценим ограниченную модель и, наконец, проанализируем влияние ограничений. Установка параметров регуляризации и параметров ограничений Возьмем наилучшие параметры для регуляризованной модели XGBoost с помощью инструкции print(fitted_class_mdls['xgb_reg']['cv_best_params']). Они находятся в словаре best_xgb_params, наряду с eta и max_depth. Затем, чтобы наложить монотонные ограничения на признак предшествующих судимостей в год (priors_per_year), мы должны сначала узнать его положение и направление, в котором находится монотонная корреляция. Из рис. 12.8 мы знаем ответы на оба вопроса. Этот признак последний, и корреляция является положительной, поэтому кортеж mono_con должен содержать девять элементов, причем последний из них равен единице, а остальные — нулям. Что касается ограничений взаимодействий, то мы позволим взаимодействовать друг с другом лишь последним пяти признакам, и то же самое касается первых четырех. Кортеж interact_con представляет собой список списков, который отражает эти ограничения. Соответствующий фрагмент исходного кода показан ниже: best_xgb_params = {'eta':1.3, 'max_depth':8, 'reg_alpha':0.4451,\ 'reg_lambda':0.7168, 'scale_pos_weight':0.9914} mono_con = (0,0,0,0,0,0,0,0,1) interact_con = [[4, 5, 6, 7, 8],[0, 1, 2, 3]] Далее мы натренируем и оценим модель XGBoost с учетом этих ограничений.
552 Часть III. Настройка на интерпретируемость Обучение и оценка ограниченной модели Теперь мы проведем обучение и оценивание одним махом. Сначала мы инициализируем модель XGBClassifier с параметрами ограничения и регуляризации, а затем выполним ее обучение, используя тренировочные данные, в которых отсутствует признак расы (x_train_con). Затем мы оцениваем предсказательную результативность с помощью функции evaluate_class_mdl и сравниваем объективность с помощью функции compare_confusion_matrices, как это делалось раньше. Соответствующий фрагмент исходного кода можно увидеть ниже: xgb_con = xgb.XGBClassifier(seed=rand, monotone_constraints=mono_con,\ interaction_constraints=interact_con, **best_xgb_params) xgb_con = xgb_con.fit(X_train_con, y_train) fitted_class_mdls['xgb_con'] = mldatasets.evaluate_class_mdl(\ xgb_con, X_train_con, X_test_con, y_train, y_test,\ plot_roc=False, ret_eval_dict=True) y_test_pred = fitted_class_mdls['xgb_con']['preds_test'] _ = mldatasets.compare_confusion_matrices(\ y_test[X_test.race==1], y_test_pred[X_test.race==1],\ y_test[X_test.race==0], y_test_pred[X_test.race==0],\ 'Caucasian', 'African-American', compare_fpr=True) Приведенный выше фрагмент создает две матрицы путаницы на рис. 12.19 и несколько метрик предсказательной результативности. Если эти матрицы сравнить с матрицами рис. 12.16, то расовые различия, измеряемые нашим соотношением частот ложноположительных (FPR) классификаций, сильно пострадали. Кроме того, предсказательная результативность находится на 2–4% ниже, чем у оптимизированной модели CatBoost по всем направлениям. Вероятно, можно было бы эти метрики немного увеличить, выполнив на этой модели ту же байесову гиперпараметрическую настройку. Рис. 12.19. Сравнение матриц путаницы между расами в ограниченной модели XGBoost
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 553 Следует учитывать одну вещь — хотя расовое неравенство является главной проблемой этой главы, мы также хотим обеспечить оптимальность модели и в других отношениях. Как уже говорилось ранее, это уравновешивающий акт. Например, вполне уместно, что ответчики с наибольшим числом предшествующих судимостей в год (priors_per_year) представляют бóльший риск, чем те, у кого их меньше всего, и мы обеспечили это монотонными ограничениями. Давайте подтвердим результаты! Анализ ограничений Понаблюдать за ограничениями в действии очень просто. Надо лишь построить сводный график аддитивных объяснений Шепли (SHAP) (summary_plot), как это делалось на рис. 12.17, но на этот раз мы построим только один график. Взгляните на следующий фрагмент исходного кода: fitted_xgb_con_mdl = fitted_class_mdls['xgb_con']['fitted'] shap_xgb_con_explainer = shap.TreeExplainer(fitted_xgb_con_mdl) shap_xgb_con_values = shap_xgb_con_explainer.shap_values(X_test_con) shap.summary_plot(shap_xgb_con_values, X_test_con, plot_type="dot") Приведенный фрагмент генерирует рис. 12.20. Он демонстрирует, как признак предшествующих судимостей в год (priors_per_year) слева направо показывает более чистый градиент, т. е. более низкие значения стабильно оказывают отрицательное влияние, а более высокие — положительное, как и должно быть! Рис. 12.20. Сводный график значений аддитивных объяснений Шепли (SHAP) в ограниченной модели XGBoost
554 Часть III. Настройка на интерпретируемость Далее рассмотрим взаимодействие признаков возрастной группы (age_group) и предшествующих судимостей в год (priors_per_year), которое мы видели через призму данных на рис. 12.7. Кроме того, к моделям можно применить функцию plot_prob_contour_map, добавив дополнительные аргументы:  подогнанную модель (fitted_xgb_con_mdl);  кадр данных, используемый для выведения результата с помощью (x_test_con);  имена двух столбцов в кадре данных для сравнения по каждой оси (x_col, y_col). Результатом является интеракционный график частичной зависимости, как показано в главе 4, за исключением того, что в нем используется набор данных (recidivism_df), чтобы генерировать гистограммы для каждой оси. Для проведения сравнения мы прямо сейчас создадим два таких графика: один для регуляризованной модели XGBoost, а другой — для ограниченной. Соответствующий фрагмент исходного кода показан ниже: mldatasets.plot_prob_contour_map(recidivism_df.age_group,\ recidivism_df.priors_per_year, recidivism_df.is_recid,\ x_intervals=ordenc.categories_[2],\ y_intervals=6, use_quartiles=True,\ xlabel='Возрастная группа',\ ylabel='Предшествующие судимости в год',\ X_df=X_test, x_col='age_group', y_col='priors_per_year',\ model=fitted_xgb_mdl,\ title='Вероятность рецидивизма по возрасту/судимостям в год (регуляризованная XGBoost)') mldatasets.plot_prob_contour_map(recidivism_df.age_group,\ recidivism_df.priors_per_year, recidivism_df.is_recid,\ x_intervals=ordenc.categories_[2],\ y_intervals=6, use_quartiles=True xlabel='Возрастная группа',\ ylabel='Предшествующие судимости в год',\ X_df=X_test_con, x_col='age_group', y_col='priors_per_year',\ model=fitted_xgb_con_mdl,\ title='Вероятность рецидивизма по возрасту/судимостям в год (ограниченная XGBoost)') Приведенный фрагмент генерирует графики, показанные на рис. 12.21. Он демонстрирует, что регуляризованная модель XGBoost отражает эти данные (см. рис. 12.7). С другой стороны, ограниченная модель XGBoost сгладила и упростила контуры. Далее можно сгенерировать теплокарту интеракционных значений аддитивных объяснений Шепли (SHAP), которые были приведены на рис. 12.18, но для ограниченной модели. Исходный код — тот же самый, только в нем используются объяснитель shap_xgb_con_explainer и данные X_test_con. Соответствующий фрагмент исходного кода показан ниже: shap_xgb_interact_values = shap_xgb_con_explainer.shap_interaction_values(\ X_test_con) shap_xgb_interact_df = pd.DataFrame(np.sum(shap_xgb_interact_values, axis=0))
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость shap_xgb_interact_df.columns = X_test_con.columns shap_xgb_interact_df.index = X_test_con.columns sns.heatmap(shap_xgb_interact_df, cmap='RdBu', annot=True,\ annot_kws={'size':13}, fmt='.0f', linewidths=.5) Рис. 12.21. Контурные карты вероятности рецидивизма для возрастной группы и предшествующих судимостей в соответствии с регуляризованной и ограниченной моделями XGBoost Рис. 12.22. Теплокарта с интеракционными значениями аддитивных объяснений Шепли (SHAP) в ограниченной модели XGBoost 555
556 Часть III. Настройка на интерпретируемость Приведенный фрагмент выводит на экран теплокарту, показанную на рис. 12.22. Она демонстрирует эффективность ограничений взаимодействий из-за нулей в левом нижнем и правом нижнем квадрантах, соответствующих взаимодействиям между двумя группами признаков, которые мы выделили. Если сравнить с рис. 12.18, то также можно заметить, как ограничения сдвинули наиболее заметные взаимодействия, сделав признак возрастной группы (age_group) и продолжительность пребывания в заключении (length_of_stay), безусловно, самыми важными. Теперь давайте посмотрим на имплементацию монотонности в TensorFlow и на другие "ограничения контура" с помощью библиотеки TensorFlow Lattice. Ограничения в TensorFlow Lattice Нейронные сети бывают чрезвычайно эффективны в поиске оптимума функции потерь (loss). Потеря связана с последствиями, которые мы хотим предсказать. В данном случае это будет двухлетний рецидивизм. В этике утилитарный (или консеквенциалистский) взгляд на объективность не имеет с этим проблем, если тренировочные модельные данные не являются систематически смещенными. Тем не менее с точки зрения деонтолога, этические принципы или политика определяют этические вопросы и вытесняют последствия. Руководствуясь всем этим, библиотека TensorFlow Lattice способна воплощать этические принципы в модели как ограничения контура (shape constraints) модели. Решетка (lattice) — это интерполированная подстановочная таблица, т. е. массив, который аппроксимирует данные на входе с данными на выходе с помощью интерполяции. В высокоразмерном пространстве эти массивы становятся гиперкубами. Соотнесения каждого элемента данных на входе с элементом данных на выходе ограничиваются посредством калибровочных слоев, и они поддерживают самые разнообразные ограничения, а не только монотонность. Рис. 12.23 это показывает. На рис. 12.23 показано несколько ограничений контура. Первые три применяются к одиночному признаку (x), ограничивающему кривую f(x), представляющую данные на выходе. Последние два применяются к паре признаков  x1 , x2  , ограничивающих кодированную цветом контурную карту (f(x)). Ниже приводится краткое объяснение каждого из них.  Монотонность заставляет функцию (f(x)) всегда увеличиваться (1) или уменьшаться ( 1 ) относительно данных на входе (x).  Выпуклость заставляет функцию (f(x)) быть выпуклой (1) или вогнутой ( 1 ) относительно данных на входе (x). Выпуклость может быть смешана с монотонностью, давая эффект, подобный показанному на рис. 12.23.  Унимодальность похожа на монотонность, за исключением того, что она идет в обоих направлениях, позволяя функции (f(x)) иметь одну долину (1) или пик ( 1 ).  Доверие заставляет одну монотонную функцию ( x1 ) опираться на другую ( x2 ). Пример на рис. 12.23 показывает доверие Эджворта, но существует также его вариация, именуемая трапезоидным доверием с другим ограничением контура.
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 557  Доминирование. Монотонное доминирование ограничивает один монотонный ( x1 ) признак для определения направления наклона или эффектов при сравнении с другим ( x2 ). Его альтернатива — диапазонное доминирование — аналогично, за исключением того, что оба признака являются монотонными. Рис. 12.23. Несколько ограничений, поддерживаемых библиотекой TensorFlow Lattice Нейронные сети особенно подвержены переобучению, и манипулировать рычагами контроля за ними сравнительно трудно. Например, сложно сказать, какая именно комбинация скрытых узлов, отсева, весовой регуляризации и эпох приведет к приемлемо низкому уровню переобучения. С другой стороны, перемещение одного параметра в древовидной модели, например глубины дерева, скорее всего, снизит переобучение до приемлемого уровня, но может и уменьшить ее предсказательную способность, и для того чтобы сделать модель оптимальной, возможно, потребуется варьирование нескольких разных параметров. Применение ограничений контура не только повышает интерпретируемость, но и регуляризует модель, поскольку упрощает функцию. Библиотека TensorFlow Lattice также поддерживает разные виды штрафной регуляризации на попризнаковой основе или для ядра калибровочного слоя, используя штрафы L1 и L2 посредством лапласова, гессианова и торсионного регуляризаторов, а также регуляризатора складок (wrinkle). Эти регуляризаторы делают функции более плоскими, линейными или гладкими. Мы не будем их объяснять, но достаточно сказать, что регуляризация имеется для любого варианта использования. Существует также несколько способов имплементирования каркаса — их слишком много, чтобы описать их здесь подробно! Тем не менее важно отметить, что этот пример является лишь одним из нескольких способов его имплементации. Библиотека TFL поставляется в комплекте со встроенными стандартными оценщиками, которые абстрагируют некоторые конфигурации. Кроме того, есть возможность
558 Часть III. Настройка на интерпретируемость создавать прикладного оценщика, используя слои TFL. Для Keras можно использовать либо готовые модели, либо построить модель Keras со слоями TensorFlow Lattice. Этим последним мы и займемся дальше! Инициализация модели и входных данных TensorFlow Lattice Сейчас мы создадим серию входных слоев, каждый из которых включает в себя один-единственный признак. Они соединены с калибровочными слоями, которые позволяют каждому входу вписываться в кусочно-линейную функцию (piece-wise linear, PWL), соответствующую требованиям индивидуальных ограничений и регуляризаций, за исключением пола (sex), в котором будет использоваться категориальная калибровка. Все калибровочные слои подаются в многоразмерный слой Lattice, продуцируя выходные данные через плотный слой с сигмоидной активацией. Это описание трудно сразу понять, поэтому можно спокойно перейти к рис. 12.24, чтобы получить хоть какую-то наглядную подсказку. Кстати, для генерирования глубокой решетчатой сети (deep lattice network, DLN) можно подсоединять много типов слоев, включая следующие:  линейный для линейных функций между более чем одним входом, в том числе с ограничениями контура доминирования;  агрегационный для выполнения агрегационной функции более чем на одном входе;  параллельно-комбинационный для размещения многочисленных калибровоч- ных слоев в рамках одной функции, что делает его совместимым с последовательными (Sequential) слоями Keras. Ни один из этих слоев в данном примере использоваться не будет, но, возможно, эти знания еще больше вдохновят вас на дальнейшее изучение библиотеки TensorFlow Lattice. Как бы там ни было, давайте вернемся к нашему примеру! Первым делом нужно определить переменную lattice_sizes, которая представляет собой кортеж, соответствующий числу вершин в расчете на размерность. Мы выбрали архитектуру с одной размерностью на каждый признак, поэтому необходимо выбрать девять чисел, бóльших или равных 2. Признаки с меньшей кардинальностью для категориальных признаков или поворотных точек для непрерывных признаков гарантированно обеспечивают меньшее число вершин. Однако мы также, возможно, захотим ограничить выразительность признака, намеренно выбрав еще меньшее число вершин. Например, признак juv_fel_count имеет 10 уникальных значений, но мы назначаем ему только две вершины. Переменная lattice_sizes показана ниже: lattice_sizes = [2, 2, 2, 2, 3, 5, 7, 7, 6] Затем мы инициализируем два списка: один для размещения всех входных слоев (model_inputs), а другой — для калибровочных слоев (lattice_inputs). Затем для каждого признака поочередно мы определяем входной слой с помощью tf.keras.layers.Input и калибровочный слой с помощью категориальной калибровки (tfl.layers.CategoricalCalibration) либо калибровки PWL (tfl.layers.PWLCalibration).
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 559 Как входные, так и калибровочные слои будут добавлены в соответствующие списки по каждому признаку. То, что происходит внутри калибровочного слоя, зависит от признака. Во всех калибровках PWL используются input_keypoints, которые спрашивают, в каких местах должна сегментироваться функция PWL. Иногда на это лучше всего отвечать фиксированной шириной (np.linspace) либо в других случаях фиксированной частотой (np.quantile). В категориальной калибровке вместо этого используются корзины (num_buckets), соответствующие числу категорий. Все калибраторы имеют следующие аргументы:  output_min — минимальный выход для калибратора;  output_max — максимальный выход для калибратора — всегда должен соответст- вовать входному минимуму плюс размер решетки минус 1;  monotonicity — следует ли ограничивать функцию PWL монотонно, и если да, то как;  kernel_regularizer — как регуляризовывать функцию. В дополнение к этим аргументам аргументы convexity и is_cyclic (для монотонной унимодальной функции) могут модифицировать ограничение контура. Взгляните на следующий фрагмент исходного кода: model_inputs = [] lattice_inputs = [] sex_input = tf.keras.layers.Input(shape=[1], name='sex') lattice_inputs.append(tfl.layers.CategoricalCalibration(\ name='sex_calib', num_buckets=2, output_min=0.0,\ output_max=lattice_sizes[0] - 1.0,\ kernel_regularizer=tf.keras.regularizers.l1_l2(l1=0.001),\ kernel_initializer='constant')(sex_input)) model_inputs.append(sex_input) juvf_input = tf.keras.layers.Input(shape=[1], name='juv_fel_count') lattice_inputs.append(tfl.layers.PWLCalibration(\ name='juvf_calib', monotonicity='none',\ input_keypoints=np.linspace(0, 20, num=5, dtype=np.float32),\ output_min=0.0, output_max=lattice_sizes[1] - 1.0,\ kernel_regularizer=tf.keras.regularizers.l1_l2(l1=0.001),\ kernel_initializer='equal_slopes')(juvf_input)) model_inputs.append(juvf_input) : age_input = tf.keras.layers.Input(shape=[1], name='age_group') lattice_inputs.append(tfl.layers.PWLCalibration(\ name='age_calib', monotonicity='none',\ input_keypoints=np.linspace(0, 6, num=7, dtype=np.float32),\ output_min=0.0, output_max=lattice_sizes[7] - 1.0,\ kernel_regularizer=('hessian', 0.0, 1e-4))(age_input)) model_inputs.append(age_input) priors_input = tf.keras.layers.Input( shape=[1], lattice_inputs.append(tfl.layers.PWLCalibration(
560 Часть III. Настройка на интерпретируемость name='priors_calib', monotonicity='increasing',\ input_keypoints=np.quantile(X_train_con['priors_per_year'],\ np.linspace(0, 1, num=7)),\ output_min=0.0, output_max=lattice_sizes[8] - 1.0)(priors_input)) model_inputs.append(priors_input) Таким образом, теперь у нас есть список с model_inputs и еще один с калибровочными слоями, которые будут подаваться на вход решетки (lattice_inputs). Сейчас нам осталось лишь привязать их вместе к решетке. Построение модели Keras со слоями TensorFlow Lattice У нас уже подсоединены первые два строительных блока этой модели. Теперь давайте создадим последние два строительных блока, начиная с решетки (tfl.layers.Lattice). Этот слой в качестве аргументов принимает lattice_sizes, выходные минимумы и максимумы, а также монотонности (monotonicities), которые он должен обеспечивать. Обратите внимание, что для последнего элемента, признака priors_per_year, задана возрастающая монотонность. Затем решеточный слой проходит заключительную стадию обработки, т. е. попадает в плотный слой с сигмоидной (sigmoid) активацией. Соответствующий фрагмент исходного кода показан ниже: lattice = tfl.layers.Lattice(name='lattice', lattice_sizes=lattice_sizes,\ monotonicities=['none', 'none', 'none', 'none', 'none',\ 'none', 'none', 'none', 'increasing'],\ output_min=0.0, output_max=1.0)(lattice_inputs) model_output = tf.keras.layers.Dense(1, name='output',\ activation='sigmoid')(lattice) Два первых строительных блока, выступающих в качестве данных на входе (input), теперь можно соединить с помощью tf.keras.models.Model с двумя последними, выступающими в качестве данных на выходе (outputs). И вуаля! Теперь мы имеем полностью сформированную модель, а ее исходный код показан ниже: tfl_mdl = tf.keras.models.Model(inputs=model_inputs, outputs=model_output) В целях получения представления о схеме соединения всех слоев можно всегда выполнить библиотечный метод tfl_mdl.summary(), но его результаты не столь интуитивно понятны, как результаты применения функции tf.keras.utils.plot_model, которые показаны в следующем ниже фрагменте исходного кода: tf.keras.utils.plot_model(tfl_mdl, rankdir='LR') Приведенный фрагмент генерирует диаграмму модели, показанную на рис. 12.24. Далее необходимо скомпилировать модель. Мы будем использовать функцию потерь бинарной кросс-энтропии binary_crossentropy, оптимизатор Adam и задействуем точность и площадь под кривой (area under the curve, AUC) в качестве метрик, как показано в следующем фрагменте исходного кода: tfl_mdl.compile(loss='binary_crossentropy',\ optimizer=tf.keras.optimizers.Adam(lr=0.004),\ metrics=['accuracy',tf.keras.metrics.AUC(name='auc')])
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 561 Рис. 12.24. Схема модели Keras со слоями TFL Все уже почти готово! Далее следует самый последний шаг. Обучение и оценка модели Если внимательно приглядеться к рис. 12.24, то можно заметить, что модель имеет не один входной слой, а девять, а это означает, что необходимо разбить тренировочные и тестовые данные на девять частей. Для этого можно использовать функцию np.split, которая сгенерирует список из девяти массивов NumPy. Что касается меток, то TFL не принимает массивы с одной размерностью. С помощью функции expand_dim мы конвертируем их формы из (N,) в (N,1), как показано в следующем фрагменте исходного кода: X_train_expand = np.split(X_train_con.values.astype(\ np.float32), indices_or_sections=9, axis=1) y_train_expand = np.expand_dims(y_train.values.astype(np.float32), axis=1) X_test_expand = np.split(X_test_con.values.astype(\ np.float32), indices_or_sections=9, axis=1) y_test_expand = np.expand_dims(y_test.values.astype(np.float32), axis=1) Теперь начинается обучение! Во избежание перобучения можно использовать раннюю остановку, отслеживая валидационную AUC (val_auc). А для учета классового дисбаланса мы используем class_weight в функции fit, как показано в следующем фрагменте исходного кода: es = tf.keras.callbacks.EarlyStopping(monitor='val_auc', mode='max', verbose=1,\ patience=20, restore_best_weights=True)
562 Часть III. Настройка на интерпретируемость tfl_history = tfl_mdl.fit(X_train_expand, y_train_expand,\ class_weight={0:18, 1:16}, batch_size=128,\ epochs=60, validation_split=0.2, shuffle=False, callbacks=[es], verbose=1) Натренировав модель, можно применить функцию evaluate_class_mdl, чтобы вывести на экран краткую сводку о предсказательной результативности, как это делалось раньше, а затем функцию compare_confusion_matrices, чтобы оценить объективность. Соответствующий фрагмент исходного кода показан ниже: fitted_class_mdls['tfl_con'] = mldatasets.evaluate_class_mdl(\ tfl_mdl, X_train_expand, X_test_expand,\ y_train.values.astype(np.float32),\ y_test.values.astype(np.float32),\ plot_roc=False, ret_eval_dict=True) y_test_pred = fitted_class_mdls['tfl_con']['preds_test'] _ = mldatasets.compare_confusion_matrices(\ y_test[X_test.race==1], y_test_pred[X_test.race==1],\ y_test[X_test.race==0], y_test_pred[X_test.race==0],\ 'Caucasian', 'African-American', compare_fpr=True) Приведенный фрагмент сгенерировал матрицы путаницы, показанные на рис. 12.25. Модель TensorFlow Lattice в целом работает намного лучше, чем регуляризованная модель Keras, но соотношение частот ложноположительных (FPR) классификаций хуже, чем у ограниченной модели XGBoost. Следует отметить, что параметры XGBoost были настроены заранее. С помощью библиотеки TensorFlow Lattice можно было бы сделать еще ряд вещей, чтобы улучшить частоту ложноположительных (FPR) классификаций, в том числе с помощью прикладной функции потерь или улучшенных метрик ранней остановки, которые каким-то образом учитывают расовые различия. Рис. 12.25. Сравнение матриц путаницы между расами в ограниченной модели TensorFlow Lattice
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 563 Далее мы сделаем несколько выводов на основе того, что было усвоено в этой главе, и определим, была ли выполнена наша миссия. Миссия выполнена Нередко именно данные берут на себя вину за низкорезультативную, не поддающуюся интерпретации или систематически смещенную модель, и это похоже на правду, но на стадиях подготовки и построения модели можно делать много разных вещей, чтобы ее улучшить. Если проводить аналогию, то это похоже на выпечку пирога. Требуются качественные ингредиенты, это да. Но качество пирога может измениться из-за, казалось бы, крошечных различий в соединении этих ингредиентов и в самой выпечке — таких как температура выпечки, используемая емкость и время. Самое ужасное, на выпечку может повлиять даже то, что находится вне вашего контроля, к примеру, атмосферное давление или влажность! Даже после того, как все будет готово, качество пирога можно продегустировать массой разных способов! Эта глава посвящена многочисленным деталям, и, как и в случае с выпечкой, они являются частично точной наукой и частично — искусством. Обсуждаемые в этой главе концепции также имеют далеко идущие последствия, в особенности относительно того, как оптимизировать задачу, которая не обладает единой целью и имеет глубокие социальные последствия. Один из возможных подходов заключается в комбинировании метрик и учете дисбалансов. С этой целью мы создали метрику — средневзвешенное значение точности-полноты, — которая штрафует расовое неравенство, можно эффективно вычислить ее для всех наших моделей и поместить ее в словарь моделей (fitted_class_mdls). Затем, как это делалось раньше, мы помещаем этот словарь в кадр данных и выводим его на экран, но на этот раз сортируем по прикладной метрике (wppra_test). Соответствующий фрагмент исходного кода показан ниже: for mdl_name in fitted_class_mdls: fitted_class_mdls[mdl_name]['wppra_test'] = weighted_penalized_pr_average(\ y_test, fitted_class_mdls[mdl_name]['preds_test'],\ X_test['race'], range(3)) class_metrics = pd.DataFrame.from_dict(\ fitted_class_mdls, 'index')[['precision_test',\ 'recall_test', 'wppra_test']] with pd.option_context('display.precision', 3): html = class_metrics.sort_values(by='wppra_test', ascending=False).\ style.background_gradient(cmap='plasma', subset=['precision_test']).\ background_gradient(cmap='viridis', subset=['recall_test']) html Приведенный фрагмент сгенерировал кадр данных, показанный на рис. 12.26. Глядя на рис. 12.26, заманчиво предложить одну из моделей, стоящих в самом верху списка. Однако они были натренированы с признаком расы (race) и не учитывали проверенные реалии уголовного правосудия. В самой высокорезультативной ограни-
564 Часть III. Настройка на интерпретируемость ченной модели — модели XGBoost (xgb_con) — раса отсутствовала, была обеспечена монотонность признака предшествующих судимостей в год (priors_per_year), а признаку возрастной группы (age_group) не разрешалось взаимодействовать с признаками ювенальной преступности, и она делала всё это, одновременно значительно улучшив предсказательную результативность по сравнению с изначальной моделью. Она также является более объективной, потому что сократила соотношение частот ложноположительных (FPR) классификаций между привилегированной и непривилегированной группами с 1,84х (см. рис. 7.2) до 1,17х (см. рис. 12.19). Она не идеальна, однако эти показатели являются огромным улучшением! Рис. 12.26. Лучшие модели в этой главе после сортировки по прикладной метрике средневзвешенной оштрафованной точности — полноты Миссия состояла в том, чтобы доказать, что точность и знания предметной области могут сосуществовать с повышением объективности, и мы успешно ее выполнили. Тем не менее по-прежнему есть возможности для усовершенствования. Следовательно, план действий будет состоять в демонстрации ограниченной модели XGBoost вашему клиенту и продолжении работ по усовершенствованию и построению более ограниченных моделей. Неограниченные должны служить лишь эталоном. Объективность можно существенно улучшить, если объединить методы этой главы с методами, усвоенными в главе 11. Мы не включили их в эту главу, чтобы сосредоточиться исключительно на методах моделирования (или промежуточной обработки), которые в типичной ситуации не рассматриваются как часть инструментария по ослаблению систематического смещения, но они очень помогают в этом, не говоря уже о методах настройки моделей, которые служат для повышения их надежности. Резюме После прочтения этой главы вы должны понять, как задействовать методику конструирования данных для повышения интерпретируемости, методику регуляризации для уменьшения переобучения и методику ограничений для соблюдения требова-
Глава 12. Монотонные ограничения и настройка моделей на интерпретируемость 565 ний политик. Первостепенными конечными целями являются установка ограждений и сдерживание сложности, препятствующей интерпретируемости. В следующей главе мы рассмотрим способы повышения надежности моделей посредством обеспечения устойчивости к антагонизму. Источник набора данных  Хранилище данных ProPublica. COMPAS Recidivism Risk Score Data and Analysis, 2019. Первоначально получено по адресу: https://www.propublica.org/datastore/ dataset/compas-recidivism-risk-score-data-and-analysis. (Данные и анализ баллов риска рецидивизма системы COMPAS.) Справочные материалы  Hastie T. J., Tibshirani R. J., Friedman J. H., 2001. The elements of statistical learn- ing. Springer-Verlag. — New York, 2001. (Хасти Т. Дж., Тибширани Р. Дж., Фридман Дж. Х. Элементы статистического обучения.)  Wang S., Gupta M. Deontological ethics by monotonicity shape constraints // AISTATS. — 2020. — URL: https://arxiv.org/abs/2001.11990. (Ван С., Гупта М. Деонтологическая этика посредством монотонностных ограничений контура.)  Cotter A., Gupta M., Jiang H., Ilan E. L., Muller J., Narayan T., Wang S., Zhu T. Shape Constraints for Set Functions // ICML. — 2019. — URL: http://proceedings.mlr.press/v97/cotter19a.html. (Коттер А., Гупта М., Цзян Х., Илан Э. Л., Мюллер Дж., Нараян Т., Ван С., Чжу Т. Ограничения контура для функций на множествах.)  Gupta M. R., Cotter A., Pfeifer J., Voevodski K., Canini K., Mangylov A., Moc- zydlowski W., van Esbroeck A. Monotonic calibrated interpolated look-up tables // Journal of Machine Learning Research. — 2016. — Vol. 17 (109). — P. 1−47. — URL: https://arxiv.org/abs/1505.06378. (Гупта М. Р., Коттер А., Пфейфер Дж., Воеводский К., Канини К., Мангылов А., Мочидловский В., Ван Эсбрук А. Монотонные калиброванные интерполированные просмотровые таблицы.)  Noble S. Algorithms of oppression: data discrimination in the age of Google. — NYU Press, 2018. (Нобл С. Алгоритмы подавления: дискриминация со стороны данных в эпоху Google.)
13 Устойчивость к антагонизму Интерпретация машинного обучения охватывает целый ряд задач, начиная от обнаружения знаний и заканчивая серьезными задачами с ощутимыми этическими последствиями, такими как вопросы объективности и справедливости, рассмотренные в последних двух главах. В этой главе мы сосредоточим наше внимание на задачах, связанных с надежностью, безвредностью и безопасностью. Как мы поняли, используя метод контрастивного объяснения (contrastive explanation method, CEM) в главе 8, классификатор изображений можно легко обмануть, заставив его делать смущающе ложные предсказания. Эта способность может иметь серьезные последствия. Например, преступник может наклеить черную наклейку на знак "Уступи дорогу", и хотя большинство водителей все равно узнали бы в нем этот знак, самоуправляемый автомобиль перестал бы его распознавать и, как следствие, потерпел бы аварию. Грабитель банка мог бы надеть охлаждающий костюм, предназначенный для обмана тепловизионной системы банковского хранилища, и, хотя любой человек это бы заметил, тепловизионная система не сработала бы. Классификатор изображений не обязательно должен быть даже сложным. Обмануть можно любую модель! Контрфактические примеры, приведенные в главе 7, похожи на антагонистические примеры, за исключением случаев, когда цель состоит в том, чтобы обмануть. Атакующий может задействовать любой пример ошибочного классифицирования, преодолевая границу принятия решения антагонистически — например, спамер может понять, что корректировка нескольких атрибутов электронной почты увеличивает вероятность обхода спам-фильтров. Сложные модели более уязвимы для антагонистических атак, тогда почему мы должны им доверять?! Мы, безусловно, можем сделать их более защищенными от неосторожного обращения, и это умозаключение подводит нас к теме устойчивости к антагонизму. Антагонист может мешать модели намеренно многими способами, но мы сосредоточимся на эвазивных атаках (или атаках уклонения) и кратко объясним другие формы атак. Затем мы объясним два метода защиты: предобработку посредством пространственного сглаживания и антагонистическое обучение. Наконец, мы продемонстрируем один метод оценивания устойчивости и один метод сертификации. Вот главные темы, которые будут рассмотрены в этой главе:  знакомство с эвазивными атаками;  защита от целенаправленных атак с помощью предобработки;  защита от любой эвазивной атаки с помощью антагонистического обучения ус- тойчивого классификатора;  оценивание и сертифицирование устойчивости к антагонизму.
Глава 13. Устойчивость к антагонизму 567 Технические требования В примере этой главы используются библиотеки mldatasets, numpy, sklearn, tensorflow, keras, adversarial-robustness-toolbox, matplotlib и seaborn. Исходный код этой главы находится по адресу: https://github.com/PacktPublishing/InterpretableMachine-Learning-with-Python/tree/master/Chapter13. Миссия Мировой рынок частных охранных услуг оценивается в более чем 250 млрд долларов США и растет примерно на 5% ежегодно. Однако он сталкивается со многими проблемами, такими как нехватка надлежаще подготовленных охранников и специализированных экспертов по безопасности во многих юрисдикциях, а также целым рядом неожиданных угроз безопасности. Эти угрозы включают широкомасштабные скоординированные атаки на кибербезопасность, массовые беспорядки, социальные потрясения и — последнее, но не менее важное — риски для здоровья, вызываемые пандемиями. И действительно, 2020 год стал испытанием отрасли, которую накрыла волна атак вымогателей и дезинформаторов, протестов и COVID-19 в придачу. По следам всех этих событий одна из крупнейших больничных сетей в США попросила свою законтрактованную охранную компанию заняться мониторингом правильного использования масок как посетителями, так и медперсоналом по всей больнице. Охранная компания отнекивалась от этой просьбы как могла, потому что это отвлекало бы сотрудников службы безопасности от борьбы с другими угрозами, такими как злоумышленники, агрессивные пациенты и агрессивные посетители. Видеонаблюдение установлено в каждом коридоре, операционных, зале ожидания и на входе в больницу. Невозможно все время следить за каждой камерой, поэтому решили, что смогут помочь охранникам с помощью моделей глубокого обучения. Эти модели уже предупреждают персонал о необычной активности, такой как беготня по коридорам и размахивание оружием в любом месте помещения. Охранная компания предложила больничной сети добавить новую модель, которая определяет правильное использование масок. До COVID-19 существовала политика обязательного использования масок в определенных отделениях каждой больницы, а во время COVID-19 это уже требовалось повсеместно. Администраторы больниц хотели бы включать и отключать эту функцию мониторинга в зависимости от уровня риска пандемии в будущем. Они понимают, что медперсонал устает и забывает снова надевать маски; вдобавок маски иногда частично соскальзывают. Многие посетители к тому же относятся к использованию масок враждебно и могут надевать их при входе в больницу, но снимать, когда поблизости нет охраны. Это не всегда делается намеренно, поэтому они не хотят посылать охранников по каждому сигналу, в отличие от других угроз. Вместо этого они предпочли бы использовать осведомленность и чувство стыда, чтобы модифицировать поведение и вмешиваться только в отношении повторных нарушителей.
568 Часть III. Настройка на интерпретируемость Осведомленность — очень эффективный метод, например, с использованием радарных знаков скорости (рис. 13.1), которые делают дороги безопаснее, предупреждая водителей о том, что они едут слишком быстро. Не менее эффективным могло бы быть наличие экрана в конце коридоров с интенсивным движением, на котором были бы показаны снимки тех, кто недавно по ошибке или намеренно не выполнил обязательное предписание носить маски, что потенциально может создать некоторое неудобство для нарушителей. Система будет регистрировать повторных нарушителей, чтобы охранники могли их искать и либо заставлять их подчиниться, либо просить их освободить помещение. Рис. 13.1. Подобного рода радарные знаки скорости помогают ограничивать превышение скорости Есть некоторые опасения, что посетители будут пытаться обманывать модель, чтобы уклоняться от соблюдения требований, поэтому охранная компания наняла вас, чтобы вы обеспечили устойчивость модели к такого рода антагонистическим атакам. Сотрудники службы безопасности и раньше замечали некоторые низкотехнологичные уловки, когда люди на мгновение закрывали лицо руками или частью свитера, когда они понимали, что за ними следят камеры. В одном тревожном инциденте, посетитель приглушил свет и распылил немного геля на камеру, а в другом случае человек разрисовал свой рот. Однако существуют опасения по поводу высокотехнологичных атак, таких как глушение беспроводного сигнала камеры или излучение мощных лазеров, направленное непосредственно на камеры. Устройства, которые выполняют эти атаки, всё проще приобретать, и они могут влиять на другие функции наблюдения в более широком масштабе, такие как функции предотвращения краж. Охранная компания надеется, что этот пример обеспечения устойчивости поможет им в совершенствовании каждой системы и модели видеонаблюдения. В итоге охранная компания хотела бы создать собственный набор данных с изображениями лиц пациентов, посетителей и медперсонала больниц, которые они отслеживают. Между тем, фото закрытых одноразовой маской лиц из внешних источников — это лучшее, что можно найти для создания модели в краткосрочной
Глава 13. Устойчивость к антагонизму 569 перспективе. С этой целью вам был предоставлен большой набор правильно и неправильно закрытых одноразовой маской лиц и их не закрытых маской аналогов. Эти два набора данных были объединены, а исходные размеры 1024  1024 были уменьшены до размера миниатюры 124  124 . Кроме того, в целях повышения эффективности из почти 210 000 изображений этих наборов данных было отобрано 21 000 изображений. Подход Вы решили использовать четырехсторонний подход, а именно:  применение разведывательного анализа нескольких возможных эвазивных атак, чтобы понять степень уязвимости модели к ним и степень вероятности этих атак в качестве угроз;  использование метода предобработки для защиты модели от этих атак;  задействование анатагонистического переобучения для генерирования устойчи- вого классификатора, который имманентно менее подвержен многим из этих атак;  оценивание устойчивости с использованием самых современных методов, позволяющих убедить администраторов больниц в том, что модель устойчива к антагонизму. Давайте начнем! Подготовительные работы Бóльшая часть исходного кода этого примера находится по адресу: https://github.com/ PacktPublishing/Interpretable-Machine-Learning-with-Python/tree/master/Chapter13/ Masks_part1.ipynb, вплоть до исходного кода, используемого в разд. "Сертифицирование устойчивости с помощью рандомизированного сглаживания". Исходный код только для этого раздела находится по адресу: https://github.com/PacktPublishing/ InterpretableMachine-Learning-with-Python/tree/master/Chapter13/Masks_part2. ipynb. Загрузка библиотек В целях выполнения этого примера необходимо инсталлировать следующие библиотеки:  mldatasets для загрузки набора данных;  numpy и sklearn (scikit-learn) для манипулирования данными;  tensorflow для обучения моделей;  matplotlib и seaborn для наглядных визуализаций и упрощения интерпретирования.
570 Часть III. Настройка на интерпретируемость Сначала необходимо их все загрузить следующим образом: import math import os import warnings warnings.filterwarnings("ignore") import mldatasets import numpy as np from sklearn import preprocessing import tensorflow as tf from tensorflow.keras.utils import get_file import matplotlib.pyplot as plt import seaborn as sns # Только ЧАСТЬ 1 from sklearn import metrics from art.estimators.classification import KerasClassifier from art.attacks.evasion import FastGradientMethod, ProjectedGradientDescent,\ BasicIterativeMethod from art.attacks.evasion import CarliniLInfMethod from art.attacks.evasion import AdversarialPatchNumpy from art.defences.preprocessor import SpatialSmoothing from art.defences.trainer import AdversarialTrainer from tqdm.notebook import tqdm # Только ЧАСТЬ 2 from art.estimators.classification import TensorFlowV2Classifier from art.estimators.certification.randomized_smoothing \ import TensorFlowV2RandomizedSmoothing from art.utils import compute_accuracy Давайте проверим, что TensorFlow загрузил нужную версию, воспользовавшись для этого инструкцией print(tf.__version__). Версия должна быть 2.0 или выше. Мы также должны отключить немедленное исполнение и убедиться, что отключение сработало, с помощью следующих ниже инструкций. На выходе должно быть получено False: tf.compat.v1.disable_eager_execution() print('Включено немедленное исполнение:', tf.executing_eagerly()) Изучение проблемы и подготовка данных Мы загружаем данные в четыре массива NumPy, соответствующие тренировочному/тестовому наборам данных. Заодно мы делим изображения X лиц на 255, потому что благодаря этому они будут иметь значения от нуля до единицы, что лучше для
Глава 13. Устойчивость к антагонизму 571 моделей глубокого обучения. Нам нужно будет записать значения min_ и max_ для тренировочных данных, т. к. они понадобятся нам позже. Соответствующий фрагмент исходного кода показан ниже: X_train, X_test, y_train, y_test = mldatasets.load(\ "maskedface-net_thumbs_sampled", prepare=True) X_train, X_test = X_train / 255.0, X_test / 255.0 min_ = X_train.min() max_ = X_train.max() Всегда важно проверять свои данные при загрузке, чтобы убедиться, что они не были повреждены. Это можно сделать с помощью следующего исходного кода: print('X_train разм:\t%s' % (X_train.shape,)) print('X_test разм:\t%s' % (X_test.shape,)) print('y_train разм:\t%s' % (y_train.shape,)) print('y_test разм:\t%s' % (y_test.shape,)) print('X_train мин:\t%s' % (min_)) print('X_train макс:\t%s' % (max_)) print('y_train метки:\t%s' % (np.unique(y_train))) Приведенный фрагмент исходного кода сгенерирует результат, показанный далее, в котором говорится, что изображения имеют размер 128  128 пикселов и три канала (цвета). Получено 16 800 тренировочных и 4200 тестовых изображений. Метки имеют единицу только во втором значении, что указывает на то, что они не кодированы с одним активным состоянием. И действительно, распечатав уникальные значения (np. unique (y_train)), можно убедиться, что метки представлены в виде текста "Correct" для лица, правильно закрытого маской, "Incorrect" для лица, неправильно закрытого маской и "None" для обозначения отсутствия маски на лице. Соответствующий результат показан ниже: X_train разм: (16800, 128, 128, 3) X_test разм: (4200, 128, 128, 3) y_train разм: (16800, 1) y_test разм: (4200, 1) X_train мин: 0.0 X_train макс: 1.0 y_train метки: ['Correct' 'Incorrect' 'None'] Следовательно, шаг предобработки, который нам нужно будет выполнить, заключается в преобразовании меток y в кодировку с одним активным состоянием (one-hot encode, OHE), потому что форма OHE нам понадобится для оценивания предсказательной результативности модели. После инициализации кодировщика OneHotEncoder необходимо выполнить его подгонку (fit) под тренировочные данные (y_train). Мы также можем извлечь категории из кодировщика в список (labels_l), чтобы подтвердить, что в нем есть все три метки.
572 Часть III. Настройка на интерпретируемость Взгляните на следующий фрагмент исходного кода: ohe = preprocessing.OneHotEncoder(sparse=False) ohe.fit(y_train) labels_l = ohe.categories_[0].tolist() print(labels_l) Для обеспечения воспроизводимости следует всегда инициализировать свои случайные начальные числа. Это делается так: rand = 9 os.environ['PYTHONHASHSEED'] = str(rand) tf.random.set_seed(rand) np.random.seed(rand) Безусловно, обеспечивать детерминизм в глубоком обучении очень трудно, и он нередко зависит от сеанса, платформы и архитектуры. Если вы используете графический процессор (graphics processing unit, GPU) NVIDIA, то можно инсталлировать библиотеку tensorflow-determinism. Многие методы антагонистической атаки, защиты и оценивания, которые мы изучим в этой главе, очень ресурсоемки, поэтому, если бы мы использовали с ними весь тестовый набор данных, то выполнение только одного метода, вероятно, заняло бы много часов! Для повышения эффективности настоятельно рекомендуется использовать выборки из тестового набора данных. Поэтому мы создадим среднюю выборку из 200 изображений (x_test_mdsample, y_test_mdsample) и малую выборку из 20 изображений (x_test_smsample, y_test_smsample), используя для этого функцию np.random.choice. Соответствующий фрагмент исходного кода показан ниже: sampl_md_idxs = np.random.choice(X_test.shape[0], 200, replace=False) X_test_mdsample = X_test[sampl_md_idxs] y_test_mdsample = y_test[sampl_md_idxs] sampl_sm_idxs = np.random.choice(X_test.shape[0], 20, replace=False) X_test_smsample = X_test[sampl_sm_idxs] y_test_smsample = y_test[sampl_sm_idxs] Теперь давайте взглянем на изображения в наших наборах данных. В приведенном выше фрагменте мы взяли среднюю и малую выборки из тестового набора данных. Мы помещаем каждое изображение малой выборки в сетку 4  5 с меткой класса над ним, используя следующий фрагмент исходного кода: plt.subplots(figsize=(15,12)) for s in range(20): plt.subplot(4, 5, s+1) plt.title(y_test_smsample[s][0], fontsize=12) plt.imshow(X_test_smsample[s], interpolation='spline16') plt.axis('off') Приведенный фрагмент выводит сетку изображений, показанную на рис. 13.2.
Глава 13. Устойчивость к антагонизму 573 Рис. 13.2. Малая выборка из тестового набора лиц в масках и без масок На рис. 13.2 приведены разнообразные фотоснимки лиц, правильно и неправильно закрытых маской и без маски, людей всех возрастов, полов и национальностей. Несмотря на это разнообразие фотоснимков, следует отметить, что в указанном наборе данных представлены только светло-голубые хирургические маски, а изображения в основном представлены в ракурсе спереди. В идеале мы должны были бы создать еще более крупный набор данных со всеми цветами и типами масок и наполнить его случайными поворотами, сдвигами и настройками яркости до обучения либо во время нее. Это насыщение данных позволило бы построить гораздо более устойчивую модель. Тем не менее мы должны проводить различие между этим общим типом устойчивости и устойчивостью к антагонизму, и хотя оба они необходимы, тратя время на первое, мы будем отвлекаться от последнего. Таким образом, давайте допустим, что набор данных уже был насыщен за счет аугментации. Загрузка базовой модели CNN Вам не нужно тренировать базовую модель в форме сверточной нейронной сети (convolutional neural network, CNN), но исходный код для этого, тем не менее, имеется в репозитории книги на GitHub. Предварительно натренированная модель тоже была сохранена там.
574 Часть III. Настройка на интерпретируемость Эту модель можно быстро загрузить и вывести ее сводку следующим образом: model_path = get_file('CNN_Base_MaskedFace_Net.hdf5',\ '''https://github.com/PacktPublishing/InterpretableMachine-Learning-withPython/blob/master/models/CNN_Base_MaskedFace_Net.hdf5?raw=true''') base_model = tf.keras.models.load_model(model_path) base_model.summary() Приведенный выше фрагмент исходного кода выводит на экран такую сводку: Model: "CNN_Base_MaskedFaceNet_Model" _______________________________________________________________ Layer (type) Output Shape Param # =============================================================== conv2d_1 (Conv2D) (None, 126, 126, 16) 448 _______________________________________________________________ maxpool2d_1 (MaxPooling2D) (None, 63, 63, 16) 0 _______________________________________________________________ conv2d_2 (Conv2D) (None, 61, 61, 32) 4640 _______________________________________________________________ maxpool2d_2 (MaxPooling2D) (None, 30, 30, 32) 0 _______________________________________________________________ conv2d_3 (Conv2D) (None, 28, 28, 64) 18496 _______________________________________________________________ maxpool2d_3 (MaxPooling2D) (None, 14, 14, 64) 0 _______________________________________________________________ conv2d_4 (Conv2D) (None, 12, 12, 128) 73856 _______________________________________________________________ maxpool2d_4 (MaxPooling2D) (None, 6, 6, 128) 0 _______________________________________________________________ flatten_6 (Flatten) (None, 4608) 0 _______________________________________________________________ dense_1 (Dense) (None, 768) 3539712 _______________________________________________________________ dropout_6 (Dropout) (None, 768) 0 _______________________________________________________________ dense_2 (Dense) (None, 3) 2307 =============================================================== Total params: 3,639,459 Trainable params: 3,639,459 Non-trainable params: 0 _______________________________________________________________ Сводка содержит практически всё, что нам нужно знать о модели. Она имеет четыре сверточных слоя (Conv2D), за каждым из которых следует слой сведения на основе максимума (MaxPooling2D). Затем она имеет уплощающий (Flatten) слой и полносвязный (Dense) слой. Далее идет слой отсева (Dropout) перед вторым плотным
Глава 13. Устойчивость к антагонизму 575 (Dense) слоем. Естественно, в этом последнем слое находятся три нейрона, которые соответствуют каждому классу. Диагностика базового классификатора CNN Оценить модель можно, используя тестовый набор данных, с помощью функции evaluate_multiclass_ mdl. Ее аргументы включают модель (base_model), тестовые данные (x_test) и соответствующие метки (y_test), а также имена классов (labels_l) и кодировщик (ohe). Наконец, нам он не нужен для построения графика кривых рабочих характеристик приемника (receiver operating characteristic, ROC), т. к. они будут идеальными (plot_roc=False). Эта функция возвращает предсказанные метки и вероятности, которые можно сохранить в переменных для последующего использования. Рис. 13.3. Матрица путаницы и метрики предсказательной результативности в базовом классификаторе, оцененные на тестовом наборе данных
576 Часть III. Настройка на интерпретируемость Соответствующий фрагмент исходного кода можно увидеть ниже: y_test_pred, y_test_prob = mldatasets.evaluate_multiclass_mdl(\ base_model, X_test, y_test, labels_l, ohe,\ plot_conf_matrix=True, predopts={"verbose":1}) Приведенный фрагмент генерирует рис. 13.3 с матрицей путаницы и метриками результативности для каждого класса. Несмотря на то что матрица путаницы на рис. 13.3, по-видимому, предлагает совершенную классификацию, можно с определенной уверенностью сказать, что модель имела проблемы с ошибочным классифицированием неправильно закрытых маской лиц, как только вы заметите отсутствие корреляции между точностью и полнотой. Теперь можно приступить к атаке на эту модель, чтобы провести диагностику ее реального совершенства! Эвазивные атаки Существует шесть широких категорий антагонистических атак.  Эвазия (evasion — подмена, уклонение) означает разработку входных данных, которые становятся причиной неправильного модельного предсказания, в особенности, если оно не обманывает наблюдателя-человека. Эвазия может быть как целенаправленной, так и нецеленаправленной, в зависимости от намерения атакующего обманно заставлять модель неправильно классифицировать один класс по отношению к другому, или не классифицировать. Методы атаки могут быть типа белого ящика, если атакующий имеет полный доступ к модели и ее тренировочному набору данных, или типа черного ящика с доступом только к выведению результата. Серый ящик находится посередине; черный ящик всегда является модельно-агностическим, тогда как модели типа белого и серого ящиков лишь могут быть таковыми.  Отравление (poisoning). Введение ошибочных тренировочных данных или параметров в модель может осуществляться в многочисленных формах в зависимости от возможностей и доступа атакующего. Например, в системах с генерируемыми пользователями данными атакующий может добавлять ошибочные данные или метки. Если бы у него было больше доступа, то он, вероятно, мог бы модифицировать крупные объемы данных. Он также может подправлять алгоритм усвоения или только гиперпараметры или схемы насыщения данных. Как и в случае с эвазией, отравление тоже может быть целенаправленным либо нецеленаправленным.  Интерфейс (inference) означает извлечение тренировочного набора данных посредством генерирования модельных результатов. Атаки с выведением результатов тоже имеют многочисленные формы и могут использоваться для шпионажа (атаки на конфиденциальность) посредством выведения информации о членстве, подтверждающей сведения о принадлежности некого примера (например, конкретного человека) тренировочному набору данных. Выведение атрибута
Глава 13. Устойчивость к антагонизму 577 определяет, была ли категория примера (например, этническая принадлежность) представлена в тренировочных данных. В выведении входных данных (так называемой инверсии модели) используются методы атаки не для угадывания и подтверждения, а для извлечения тренировочного набора данных из модели. Они имеют широкие последствия для конфиденциальности и регулирования, в особенности в медицинских и юридических приложениях, а в юрисдикциях с более строгой конфиденциальностью, таких как Регламент о защите персональных данных (General Data Protection Regulation, GDPR) в Европейском союзе (ЕС), могут затрагивать многие другие отрасли промышленности.  Троянирование (trojaning) — взлом существующих моделей, переделанных другими для трансферного усвоения или как часть ансамбля моделей, чтобы изменить поведение модели.  Обход через лазейку (backdooring) похож на троянирование, но лазейка остает- ся, даже если перетренировать модель с нуля.  Перепрограммирование (reprogramming) — дистанционное саботирование мо- дели во время обучения путем тайного внесения примеров, специально разработанных для получения конкретных результатов. Например, если предоставить достаточное число примеров, помеченных как тигровая акула, в которых четыре малых черных квадрата всегда находятся в одном и том же месте, то модель поймет, что это тигровая акула, независимо от того, что это такое. Первые три метода являются наиболее распространенными формами антагонистических атак. Атаки могут дополнительно подразделяться на подкатегории после их деления на стадии и цели (рис. 13.4). Стадия относится к тому моменту, когда совершается атака, потому что она может повлиять на обучение модели или выводимый ею результат, а цель — это то, что атакующий надеется получить от нее. В данной главе будут рассмотрены атаки только с эвазивным саботажем, потому что мы ожидаем, что посетители больницы, пациенты и медперсонал время от времени будут саботировать производственную модель. В таблице на рис. 13.4 представлен краткий обзор методов из категории антагонистических атак по стадии и цели. Цель Шпионаж Стадия Обучение Саботаж Выведение Троянирование (путем отправления) Мошенничество Отравление Обход через лазейку Производство Выведение результата Перепрограммирование Эвазия Рис. 13.4. Таблица методов категории антагонистических атак по стадиям и целям
578 Часть III. Настройка на интерпретируемость Несмотря на то что для атаки, защиты и оценивания устойчивости модели мы используем модели типа белого ящика, мы не ожидаем, что атакующие будут иметь такой уровень доступа. Мы будем использовать только методы типа белого ящика, потому что у нас есть полный доступ к модели, и нет необходимости беспокоиться о том, чтобы пробовать методы типа черного или серого ящика. В других же обстоятельствах, таких как система банковского наблюдения с тепловизионной подсистемой и соответствующей моделью для обнаружения злоумышленников, можно ожидать, что профессиональные злоумышленники будут использовать методы типа черного ящика поиска уязвимостей! Поэтому нам, как защитникам такой системы, будет разумнее пробовать те же самые методы атаки. Библиотека, которую мы будем использовать для обеспечения устойчивости к антагонизму, называется инструментарием обеспечения устойчивости к антагонизму (adversarial robustness toolbox, ART), и она поддерживается фондом LF AI & Data Foundation, теми же людьми, которые поддерживают другие проекты с открытым исходным кодом, такие как AI Explainability 360 (AIX360) и проект AI Fairness 360 (AIF360), который был рассмотрен в главе 11. Библиотека ART требует, чтобы атакуемые модели абстрагировались в оценщике или классификаторе, даже если это черный ящик. В большей части этой главы мы будем использовать классификатор KerasClassifier, за исключением последнего раздела, в котором задействуем классификатор TensorFlowV2Classifier. Инициализация классификатора ART довольно проста. Необходимо указать атрибут model, а иногда есть и другие обязательные атрибуты. Для KerasClassifier все остальные атрибуты являются опциональными, но рекомендуется использовать clip_values для указания диапазона признаков. Многие атаки представляют собой перестановки входных значений, поэтому важно знать, какие входные значения допустимы или правдоподобны. Взгляните на следующий фрагмент исходного кода: base_classifier = KerasClassifier(model=base_model, clip_values=(min_, max_)) y_test_mdsample_prob = np.max(y_test_prob[sampl_md_idxs], axis=1) y_test_smsample_prob = np.max(y_test_prob[sampl_sm_idxs], axis=1) В приведенном выше фрагменте мы заодно подготовим два массива с вероятностями предсказанного класса для средней и малой выборок. Это совершенно необязательно, но они помогают размещать предсказанную вероятность рядом с предсказанной меткой при построении графиков некоторых примеров. Атака быстрым методом на основе знака градиента Одним из наиболее популярных методов атаки является быстрый метод на основе знака градиента (fast gradient sign method, FGSM или FGM)1. Как следует из названия, в нем используется градиент модели глубокого обучения, предназначенный для 1 Быстрый метод атаки на основе знака градиента (FGSM) в сущности заключается в добавлении (неслучайного) шума, направление которого совпадает с градиентом функции потери по отношению к данным. — Прим. перев.
Глава 13. Устойчивость к антагонизму 579 отыскания антагонистических примеров. Он выполняет малые пертурбации в пикселах входного изображения, добавления либо вычитания, и конкретная используемая операция зависит от знака градиента, который указывает направление, в котором потеря будет увеличиваться или уменьшаться согласно интенсивности пиксела. Как и со всеми методами атаки в библиотеке ART, сначала мы инициализируем метод, предоставляя оценщик или классификатор из библиотеки ART. Библиотечный класс FastGradientMethod также требует размера шага атаки eps, который будет определять силу атаки. Кстати, eps означает эпсилон (  ), который в математике обычно представляет пределы погрешности ошибки или бесконечно малые ошибки аппроксимации. Низкий размер шага будет приводить к тому, что изменения интенсивности пикселов будут менее заметны, но это также будет приводить к ошибочной классификации меньшего числа примеров. Больший размер шага будет приводить к ошибочной классификации большего числа примеров, с более заметными изменениями. Соответствующий исходный код приведен ниже: attack_fgsm = FastGradientMethod(base_classifier, eps=0.1) Следующий шаг после инициализации — генерирование (generate) анатагонистических примеров. Единственным обязательным атрибутом являются изначальные примеры (x_test_mdsample). Обратите внимание, что быстрый метод на основе знака градиента (FGSM) может быть целенаправленным, поэтому в инициализации есть опциональный атрибут targeted, но вам также потребуется указывать необходимые при генерировании соответствующие метки. Данная атака не является целенаправленной, поскольку намерение атакующего состоит в саботировании модели. Соответствующий исходный код приведен ниже: X_test_fgsm = attack_fgsm.generate(X_test_mdsample) В отличие от других методов генерирование антагонистических примеров методом FGSM происходит быстро, отсюда и "быстрый" в названии указанного метода! Теперь мы собираемся сделать две вещи одним махом. Сначала с помощью функции evaluate_multiclass_mdl мы оценим анагонистические примеры (x_test_fgsm) относительно базовой классификаторной модели (base_classifier.model). Затем можно задействовать функцию compare_image_predictions, чтобы вывести на экран сетку изображений в противопоставлении со случайно отобранными антагонистическими примерами (x_test_fgsm) относительно изначальных примеров (x_test_mdsample) и соответствующих им предсказанных меток (y_test_fgsm_pred, y_test_mdsample) и вероятностей (y_test_fgsm_prob, y_test_mdsample_prob). Мы приспосабливаем заголовки и ограничиваем сетку 4 примерами (num_samples). По умолчанию функция compare_ image_predictions сравнивает только ошибочные классификации, но если задать опциональный атрибут use_misclass как False, появляется возможность сравнивать правильные классификации. Соответствующий фрагмент исходного кода показан ниже: y_test_fgsm_pred, y_test_fgsm_prob = mldatasets.evaluate_multiclass_mdl(\ base_classifier.model, X_test_fgsm,\
580 Часть III. Настройка на интерпретируемость y_test_mdsample, labels_l, ohe,\ plot_conf_matrix=False, plot_roc=False) y_test_fgsm_prob = np.max(y_test_fgsm_prob, axis=1) mldatasets.compare_image_predictions(\ X_test_fgsm, X_test_mdsample,\ y_test_fgsm_pred, y_test_mdsample.flatten(),\ y_test_fgsm_prob, y_test_mdsample_prob,\ title_mod_prefix="Атакованое:",\ title_difference_prefix="Средняя пертурбация атаки методом FSGM:",\ num_samples=4) Приведенный фрагмент сначала выводит таблицу, которая показывает, что модель имеет точность всего 44% в примерах, атакованных методом FGSM! И хотя эта атака не была целенаправленной, она была наиболее эффективной в отношении правильно закрытых маской лиц. Таким образом, гипотетически, если злоумышленникам удастся вызвать такой уровень искажения или интерференции сигнала, то они серьезно подорвут способность охранной компании контролировать соблюдение требований масочного режима. Приведенный исходный код также выводит рис. 13.5, на котором показано несколько ошибочных классификаций, вызванных атакой методом FGSM. Указанная атака почти ровно распределила шум по всем изображениям. Видно, что изображение модифицировалось только со средней абсолютной ошибкой 0,092, и, поскольку значения пикселов варьируют от 0 до 1, ее величина означает 9,2%. Если нужно откалибровать атаки так, чтобы они были менее заметными, но все же эффективными, вы должны учитывать, что значение eps, равное 0,1, является причиной средней абсолютной пертурбации в размере 9,2%, что снижает точность до 44%. Рис. 13.5. Сравнение изображений, атакованных методом FGSM, с исходными изображениями в базовом классификаторе
Глава 13. Устойчивость к антагонизму 581 Говоря о менее обнаруживаемых атаках, мы теперь познакомимся с атаками методом Карлини и Вагнера (C&W). Атака методом инфинитной нормы Карлини и Вагнера В 2017 году Карлини и Вагнер задействовали три нормоориентированные метрики расстояния: L0 , L2 и L , измеряя разницы между исходным и антагонистическим изображениями. В других статьях эти метрики уже обсуждались, в том числе посвященных и FGSM-методу. Представленная указанными авторами инновация заключалась в том, как эти метрики были задействованы с использованием алгоритма оптимизации на основе градиентного спуска, разработанного для аппроксимирования минимумов функции потерь. В частности, во избежание застреваний в градиентном спуске авторы используют несколько отправных точек, а для того чтобы процесс "порождал валидное изображение", он оценивает три метода с целью ограничивать задачу оптимизации в рамках верхней и нижней границ. В этом случае мы хотим найти антагонистический вариант, в котором расстояния между этим вариантом и исходным изображением являются минимальными, но при этом остаются реалистичными. Во всех трех атаках методом Карлини и Вагнера ( L0 , L2 и L ) используется оптимизатор Adam для быстрого схождения. Их главное отличие заключается в метрике расстояния, из которых L , возможно, является наилучшей. Она определяется следующим образом: L  x  x   max  x1  x1 , ..., xn  xn  . И, поскольку она показывает максимальное расстояние до любой координаты, как вы убедитесь, антагонистический пример не просто отличается "в среднем" минимально, но и не слишком отличается где-либо еще в признаковом пространстве. Как раз это и сделало бы атаку менее обнаруживаемой! Инициализация атак методом инфинитной нормы Карлини и Вагнера (C&W) и генерирование с ее помощью антагонистических примеров аналогичны FGSMметоду. В целях инициализации CarliniLInfMethod мы определяем eps и, опционально, размер пакета batch_size (по умолчанию равный 128). Затем для генерирования (generate) нецеленаправленной антагонистической атаки применяем то же самое, что и в случае FGSM — требуется только X, когда она нецеленаправленная, но y необходим, когда атака целенаправленная. Соответствующий фрагмент исходного кода показан ниже: attack_cw = CarliniLInfMethod(base_classifier, eps=0.3, batch_size=40) X_test_cw = attack_cw.generate(X_test_mdsample) Далее мы выполним оценивание антагонистических примеров C&W (x_test_cw), как и в случае с FGSM. Исходный код точно такой же, но с заменой fsgm на cw и другими заголовками в функции compare_image_predictions. Как и в случае с FGSM, сле-
582 Часть III. Настройка на интерпретируемость дующий ниже исходный код произведет отчет о классификации и сетку изображений (рис. 13.6): y_test_cw_pred, y_test_cw_prob = mldatasets.evaluate_multiclass_mdl(\ base_classifier.model, X_test_cw,\ y_test_mdsample, labels_l, ohe,\ plot_conf_matrix=False, plot_roc=False) y_test_cw_prob = np.max(y_test_cw_prob, axis=1) mldatasets.compare_image_predictions(\ X_test_cw, X_test_mdsample, y_test_cw_pred,\ y_test_mdsample.flatten(), y_test_cw_prob,\ y_test_mdsample_prob, title_mod_prefix="Атакованое:",\ title_difference_prefix="Средняя пертурбация атаки методом инф. нормы C&W:",\ num_samples=4) В результате выполнения приведенного выше исходного кода антагонистические примеры C&W имеют 92%-ю точность по сравнению с базовой моделью. Этого достаточно, чтобы сделать модель бесполезной для использования по своему назначению. Если бы атакующий достаточно сильно нарушил сигнал камеры, то он смог бы добиться тех же результатов. И, как видно из рис. 13.6, здесь пертурбация в размере 0,3% является крошечной по сравнению с FGSM, но этого было достаточно, чтобы ошибочно классифицировать 8%, включая четыре снимка в сетке, классифицирвать которые не составляет труда. Рис. 13.6. Сравнение снимков, атакованных методом инфинитной нормы Карлини и Вагнера (C&W) по сравнению с исходными изображениями в базовом классификаторе Иногда неважно, проводится ли атака незаметно или нет. Смысл в том, чтобы создать прецедент, и это могут делать антагонистические заплаты (adversarial patch, AP).
Глава 13. Устойчивость к антагонизму 583 Целенаправленная атака методом антагонистических заплат Антагонистическая заплата (AP) — это устойчивый, универсальный и целенаправленный метод. Вы генерируете заплату, которую можно либо наложить на изображение, либо распечатать и физически разместить в сцене, чтобы обмануть классификатор, игнорируя все остальное в сцене. Заплата предназначена для работы в самых разнообразных условиях и преобразованиях. В отличие от других подходов к генерированию антагонистических примеров, намерение камуфлировать атаку отсутствует, потому что, по сути, вы заменяете обнаруживаемую часть сцены заплатой. Указанный метод работает за счет задействования варианта алгоритма предпочтения ожидания над преобразованием (expectation over transformation, EOT), который тренирует изображения вместо преобразований данной заплаты в разных местах изображения. При получении тренировочных примеров он усваивает именно заплату, которая обманывает классификатор больше всего. Этот метод требует большего числа параметров и шагов, чем методы FGSM и C&W. Для начала мы будем использовать AdversarialPatchNumpy, являющийся вариантом, который работает с любым нейросетевым классификатором изображений или видео. Есть еще один для TensorFlow v2, но нашим базовым классификатором является KerasClassifier. Первым аргументом выступает классификатор (base_classifier), а остальные, которые мы определим, не обязательны, но настоятельно рекомендуемые. Диапазоны нормирования scale_min и scale_max имеют особую важность, поскольку они определяют возможную величину заплат по отношению к изображениям — в данном случае мы хотим рассмотреть не менее 40% и не более 70%. Кроме того, имеет смысл определить целевой класс (target). В данном случае мы хотим, чтобы заплата была нацелена на "правильный" класс (Correct). Для коэффициента скорости обучения (learning_rate) и максимального числа итераций (max_iter) мы используем значения по умолчанию, но обратите внимание, что их можно изменить для повышения эффективности антагонистических заплат. Соответствующий фрагмент исходного кода можно увидеть ниже: attack_ap = AdversarialPatchNumpy(\ base_classifier, scale_min=0.4, scale_max=0.7,\ learning_rate=5., max_iter=500, batch_size=40, target=0) Мы не хотим, чтобы алгоритм генерации заплат тратил время на их просмотр по всем изображениям, поэтому можно использовать булеву маску. Указанная маска сообщает алгоритму, где можно центрировать заплату. Генерируя маску, мы начнем с создания массива размером 128  128 , состоящего из нулей. Затем мы поместим единицы в прямоугольную область между пикселами 80–93 и 45–84, что на большинстве изображений примерно соответствует покрытию центра ротовой области. Наконец, мы расширяем размеры массива так, чтобы он имел форму (1, W, H), и конвертируем его в булев тип. Затем можно приступить к генерированию (generate) заплат, используя малоразмерные выборки из тестового набора данных и маску. Соответствующий фрагмент исходного кода можно увидеть ниже: placement_mask = np.zeros((128,128)) placement_mask[80:93,45:83] = 1
584 Часть III. Настройка на интерпретируемость placement_mask = np.expand_dims(placement_mask, axis=0).astype(bool) patch, patch_mask = attack_ap.generate(\ x=X_test_smsample, y=ohe.transform(y_test_smsample), mask=placement_mask) Теперь можно вывести заплату на график с помощью следующего исходного кода: plt.imshow(patch * patch_mask) Приведенный выше исходный код сгенерировал изображение, показанное ниже на рис. 13.7. Как и ожидалось, оно имеет множество встречающихся в масках оттенков синего. В нем также имеются ярко-красные и желтые оттенки, в основном отсутствующие в тренировочных примерах, что дезориентирует классификатор. Рис. 13.7. Антагонистическая заплата, сгенерированная для ошибочного классифицирования лиц как правильно закрытых маской В отличие от других методов, процедура generate создавала не антагонистические примеры, а одну заплату, представляющую собой изображение, которое затем можно размещать поверх фотоснимков для создания антагонистических примеров. Эта задача выполняется с помощью apply_patch, которая использует исходные изображения из X_test_smsample, а применяемый нами масштаб составляет 55%. Также рекомендуется использовать аргумент mask, который будет обеспечивать нанесение заплаты там, где это имеет больше смысла — в данном случае в области вокруг рта. Соответствующий фрагмент исходного кода можно увидеть ниже: X_test_ap = attack_ap.apply_patch(X_test_smsample, scale=0.55,\ mask=placement_mask) Теперь самое время оценить нашу атаку и рассмотреть несколько ошибочных классификаций. Мы поступим точно так же, как и раньше, используя повторно исходный код, который сгенерировал рис. 13.5 и 13.7, за исключением того, что мы заменим переменные, подставив ap, и изменим заголовок. Соответствующий фрагмент исходного кода можно увидеть ниже: y_test_ap_pred, y_test_ap_prob = mldatasets.evaluate_multiclass_mdl(\ base_classifier.model, X_test_ap,\ y_test_smsample, labels_l, ohe,\ plot_conf_matrix=False, plot_roc=False)
Глава 13. Устойчивость к антагонизму 585 y_test_ap_prob = np.max(y_test_ap_prob, axis=1) mldatasets.compare_image_predictions(\ X_test_ap, X_test_smsample,\ y_test_ap_pred, y_test_smsample.flatten(),\ y_test_ap_prob, y_test_smsample_prob,\ title_mod_prefix="Атакованое:",\ title_difference_prefix="Средняя пертурбация атаки методом AP:",\ num_samples=4) Приведенный исходный код выдает точность атаки на уровне 65%, что довольно хорошо, учитывая малое число примеров, на которых он был натренирован. Метод AP нуждается в большем их числе, чем другой метод. Целенаправленные атаки, как правило, нуждаются в большем числе примеров, чтобы можно было понять, как лучше всего нацеливаться на один класс. Приведенный выше исходный код также сгенерировал сетку изображений, показанную на рис. 13.8, которая демонстрирует, каким образом, гипотетически, люди могли бы легко обманывать модель, если бы они ходили, держа картонку перед лицом. Рис. 13.8. Сравнение изображений, атакованных методом AP, с исходными изображениями в базовом классификаторе Пока что мы изучили три метода атаки, но еще не решили, как от этих атак защищаться. Далее мы рассмотрим несколько решений. Защита от целенаправленных атак с помощью предобработки Существует пять широких категорий защиты от антагонизма.  Предобработка — изменение модельных входных данных таким образом, что- бы их было труднее атаковать.
586 Часть III. Настройка на интерпретируемость  Антагонистическое обучение — обучение новой устойчивой модели, предна- значенной для преодоления атак.  Детекция — обнаружение атак, например, можно натренировать модель обна- руживать антагонистические примеры.  Преобразователь — модифицирование модельной архитектуры и обучение та- ким образом, чтобы сделать ее устойчивее; сюда могут входить такие методы, как дистилляция, входные фильтры, обрезка нейронов и утрачивание усвоенных знаний.  Постобработка — изменение модельных выходных данных для преодоления атак на выведение модельного результата или атак на извлечение модели. С эвазивными атаками работают только первые четыре защиты, и в этой главе мы рассмотрим лишь первые две: предобработку и антагонистическое обучение. Атаки методами FGSM и C&W можно легко отражать с помощью любого из них, но атаки методом AP отражать сложнее, поэтому может потребоваться более сильный детекционный или преобразовательный метод. Прежде чем начать отражать атаки, мы должны создать целенаправленную атаку. Мы будем использовать проецированный градиентный спуск (projected gradient descent, PGD), т. е. сильную атаку, по результату очень похожую на выходе на FGSM, т. е. алгоритм создает зашумленные изображения. Мы не будем приводить подробное объяснение проецированного градиентного спуска, но важно отметить, что, как и в случае с FGSM, метод рассматривается как первопорядковый антагонист, поскольку в нем используется информация первого порядка о сети (вследствие градиентного спуска). Кроме того, проецированный градиентный спуск экспериментально доказывает, что устойчивость к нему обеспечивает устойчивость к любому антагонисту первого порядка. Будучи именно сильной атакой, проецированный градиентный спуск является залогом убедительных эталонов. В целях создания целенаправленной атаки против класса правильно закрытых маской примеров, лучше всего отбирать примеры только с неправильно закрытыми масками (y_test_notmasked) с соответствующими им метками (y_test_notmasked) и предсказанными вероятностями (y_test_notmasked_ prob). Затем следует создать массив с классом (Correct), для которого мы хотим сгенерировать антагонистические примеры (y_test_masked). Соответствующий фрагмент исходного кода можно увидеть ниже: not_masked_idxs = np.where(y_test_smsample != 'Correct')[0] X_test_notmasked = X_test_smsample[not_masked_idxs] y_test_notmasked = y_test_smsample[not_masked_idxs] y_test_notmasked_prob = y_test_smsample_prob[not_masked_idxs] y_test_masked = np.array(['Correct'] * X_test_notmasked.shape[0]).reshape(-1,1) Проецированный градиентный спуск (ProjectedGradientDescent) инициализируется так же, как и FGSM, за исключением того, что мы собираемся установить максимальную пертурбацию (eps), размер шага атаки (eps_step), максимальное число ите-
Глава 13. Устойчивость к антагонизму 587 раций (max_iter) и целенаправленность (targeted=True). Именно по той причине, что он является целенаправленным, мы собираемся задать как X, так и y. Соответствующий фрагмент исходного кода можно увидеть ниже: attack_pgd = ProjectedGradientDescent(base_classifier, eps=0.3, eps_step=0.01,\ max_iter=40, targeted=True) X_test_pgd = attack_pgd.generate(X_test_notmasked, y=ohe.transform(y_test_masked)) Теперь давайте оценим атаку методом PGD, как это делалось раньше, но на этот раз сгенерируем матрицу путаницы (plot_conf_matrix=True) следующим образом: y_test_pgd_pred, y_test_pgd_prob = mldatasets.evaluate_multiclass_mdl(\ base_classifier.model, X_test_pgd,\ y_test_notmasked, labels_l, ohe,\ plot_conf_matrix=True, plot_roc=False) y_test_pgd_prob = np.max(y_test_pgd_prob, axis=1) Приведенный фрагмент исходного кода производит матрицу путаницы, показанную на рис. 13.9. Атака методом PGD была настолько эффективной, что обеспечивала точность 0%, в результате чего все незакрытые маской и неправильно закрытые маской образцы воспринимались как закрытые маской. Рис. 13.9. Матрица путаницы для изображений, атакованных методом PGD, оцененных относительно базового классификатора
588 Часть III. Настройка на интерпретируемость Далее давайте выполним функцию compare_image_prediction, чтобы увидеть несколько случайных ошибочных классификаций: mldatasets.compare_image_predictions(\ X_test_pgd, X_test_notmasked, y_test_pgd_pred,\ y_test_notmasked.flatten(), y_test_pgd_prob,\ y_test_smsample_prob,\ title_mod_prefix="Атакованое:",\ title_difference_prefix="Средняя пертурбация атаки методом PGD:",\ num_samples=4) Приведенный исходный код выводит на экран сетку изображений, показанную на рис. 13.10. Средняя абсолютная пертурбация является самой высокой, которую мы видели до сих пор, — 14,7%, и все незакрытые маской лица в сетке классифицируются как правильно закрытые маской. Рис. 13.10. График сравнения изображений, атакованных методом PGD, с исходными изображениями в базовом классификаторе Точность не может стать еще хуже, а изображения имеют зернистый вид, не подлежа восстановлению. И как тогда бороться с шумом? Если вы помните, мы уже сталкивались с этой проблемой раньше. В главе 8 метод SmoothGrad улучшал карты значимости, усредняя градиенты. У нас совсем другое приложение, но принцип тот же — как и в случае с человеком, которому сложнее интерпретировать карту значимости с шумом, чем гладкую, модель испытывает гораздо больше трудностей в интерпретировании зернистого изображения, нежели более размытого. Пространственное сглаживание — это просто причудливый термин для операции размытия! Однако новизна его введения в качестве метода антагонистической защиты заключается в том, что предлагаемая имплементация (SpatialSmoothing) требует использования медианы, а не среднего значения в скользящем окне. Значение размера окна (window_size) поддается конфигурированию, и рекомендуется его корректировать там, где это полезнее всего в качестве защиты. После инициализации защиты вы подключаете антагонистические примеры (x_test_pgd). В результате
Глава 13. Устойчивость к антагонизму 589 будут выведены пространственно сглаженные антагонистические изображения (X_ test_pgd_ss). Соответствующий фрагмент исходного кода можно увидеть ниже: defence_ss = SpatialSmoothing(window_size=11) X_test_pgd_ss, _ = defence_ss(X_test_pgd) Теперь можно взять произведенные размытые антагонистические изображения и выполнить их оценивание, как это делалось раньше, сначала с помощью функции evaluate_multiclass_mdl, чтобы получить предсказанные метки (y_test_pgd_ ss_pred) и вероятности (y_test_pgd_ss_prob), а также результат нескольких метрик предсказательной результативности. В целях построения сетки изображений с помощью функции compare_image_predictions давайте применим аргумент use_misclass=False, чтобы сравнить надлежаще классифицированные изображения — другими словами, антагонистические примеры, которые были успешно защищены. Соответствующий фрагмент исходного кода можно увидеть ниже: y_test_pgd_ss_pred, y_test_pgd_ss_prob = mldatasets.evaluate_multiclass_mdl(\ base_classifier.model, X_test_pgd_ss,\ y_test_notmasked, labels_l, ohe,\ plot_conf_matrix=False, plot_roc=False) y_test_pgd_ss_prob = np.max(y_test_pgd_ss_prob, axis=1) mldatasets.compare_image_predictions(\ X_test_pgd_ss, X_test_notmasked,\ y_test_pgd_ss_pred, y_test_notmasked.flatten(),\ y_test_pgd_ss_prob, y_test_notmasked_prob,\ use_misclass=False,\ title_mod_prefix="Атак. + защищ.:", num_samples=4,\ title_difference_prefix="Средняя пертурбация атаки методом PGD и защиты:") Рис. 13.11. Сравнение пространственно-сглаженных изображений, атакованных методом PGD, с исходными изображениями в базовом классификаторе
590 Часть III. Настройка на интерпретируемость Приведенный фрагмент порождает уровень точности 54%, что намного лучше, чем 0% до пространственно-сглаженной защиты. Он также выводит рис. 13.11, который демонстрирует, как размытие эффективно предотвратило атаку методом PGD. Оно даже вдвое уменьшило средную абсолютную пертурбацию! Далее мы испытаем еще один метод защиты из нашего арсенала: антагонистическое обучение! Защита от любой эвазивной атаки с помощью антагонистического обучения устойчивого классификатора В главе 8 мы познакомились с классификатором изображений фруктов, который, вероятно, будет работать плохо в предполагаемой среде самообслуживания минимаркета. Невероятно ужасная результативность на вневыборочных данных была обусловлена тем, что классификатор тренировался на многочисленных изображениях одного или двух фруктов на класс, снятых под совершенно разными углами при стабильном освещении. Оказывается, разнообразие ракурсов было не так важно, как разнообразие фруктов и освещения! В заключении главы содержалась рекомендация тренировать сеть изображениями, представляющими их предполагаемую среду, чтобы содействовать построению более устойчивой модели. Для обеспечения устойчивости модели решающее значение имеет разнообразие тренировочных данных, но только в том случае, если оно отражает предполагаемую среду. В статистических терминах речь идет об использовании для обучения выборок, которые репрезентативно отображают популяцию, помогая модели учиться правильно их классифицировать. Для обеспечения устойчивости к антагонизму применяются те же принципы. Если насытить данные, включив в них правдоподобные примеры антагонистических атак, то модель научится их классифицировать. Одним словом, в этом и состоит суть антагонистического обучения. Исследователи машинного обучения предполагают, что эта форма защиты очень эффективна для любого вида эвазивной атаки, по сути, защищая ее. Тем не менее она не является непроницаемой. Ее эффективность зависит от использования правильных видов антагонистических примеров в обучении, оптимальных гиперпараметров и т. д. Исследователи рекомендуют увеличивать количество нейронов в скрытых слоях и использовать метод PGD или базовый итеративный метод (basic iterative method, BIM) с целью генерирования антагонистических примеров для обучения. Базовый итеративный метод (BIM) похож на FGSM-метод, но он не так быстр, потому что выполняется в цикле, чтобы аппроксимировать наилучший антагонистический пример в пределах -окрестности изначального изображения. Атрибут eps ограничивает эту окрестность. Обучение устойчивой модели бывает очень ресурсоемким. Вообще-то устойчивость не требуется, т. к. есть возможность скачать уже натренированную для вас модель, но важно понимать, как выполнять ее с помощью библиотеки ART. Соответствующие шаги мы поясним, и если вы хотите выполнить обучение модели
Глава 13. Устойчивость к антагонизму 591 с помощью ART, то сможете это сделать. В противном случае просто пропустите шаги и скачайте натренированную модель. Устойчивая модель (robust_model) очень похожа на базовую модель (base_model), за исключением того, что мы используем фильтры одинакового размера в четырех сверточных (Conv2D) слоях. Мы делаем это, чтобы уменьшить сложность либо противостоять сложности, которую мы добавляем, увеличивая число нейронов в первом скрытом (Dense) слое в четыре раза, как предлагается исследователями машинного обучения. Соответствующий фрагмент исходного кода можно увидеть ниже: robust_model = tf.keras.models.Sequential([\ tf.keras.layers.InputLayer(input_shape=X_train.shape[1:]),\ tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'),\ tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),\ tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'),\ tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),\ tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'),\ tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),\ tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'),\ tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),\ tf.keras.layers.Flatten(),\ tf.keras.layers.Dense(3072, activation='relu'),\ tf.keras.layers.Dropout(0.2),\ tf.keras.layers.Dense(3, activation='softmax')],\ name='CNN_Robust_MaskedFaceNet_Model') robust_model.compile(optimizer=tf.keras.optimizers.Adam(\ lr=0.001), loss='categorical_crossentropy', metrics=['accuracy']) robust_model.summary() Библиотечный метод summary() в приведенном фрагменте выводит на экран следующий результат. Хорошо видно, что тренируемые параметры составляют около 3,6 млн — аналогично базовой модели: Model: "CNN_Robust_MaskedFaceNet_Model" _______________________________________________________________ Layer (type) Output Shape Param # =============================================================== conv2d_1 (Conv2D) (None, 126, 126, 32) 896 _______________________________________________________________ maxpool2d_1 (MaxPooling2D) (None, 63, 63, 32) 0 _______________________________________________________________ conv2d_2 (Conv2D) (None, 61, 61, 32) 9248 _______________________________________________________________ maxpool2d_2 (MaxPooling2D) (None, 30, 30, 32) 0 _______________________________________________________________ conv2d_3 (Conv2D) (None, 28, 28, 32) 9248 _______________________________________________________________
592 Часть III. Настройка на интерпретируемость maxpool2d_3 (MaxPooling2D) (None, 14, 14, 32) 0 _______________________________________________________________ conv2d_4 (Conv2D) (None, 12, 12, 32) 9248 _______________________________________________________________ maxpool2d_4 (MaxPooling2D) (None, 6, 6, 32) 0 _______________________________________________________________ flatten (Flatten) (None, 1152) 0 _______________________________________________________________ dense_1 (Dense) (None, 3072) 3542016 _______________________________________________________________ dropout (Dropout) (None, 3072) 0 _______________________________________________________________ dense_2 (Dense) (None, 3) 9219 =============================================================== Total params: 3,579,875 Trainable params: 3,579,875 Non-trainable params: 0 _______________________________________________________________ Далее можно натренировать модель антагонистически, сначала инициализировав новый классификатор KerasClassifier устойчивой моделью (robust_model). Затем на этом классификаторе мы инициализируем атаку методом BasicIterativeMethod. Наконец, мы инициализируем антагонистического тренера (AdversarialTrainer) устойчивым классификатором (robust_classifier) и атаки методом BIM и выполняем его подгонку (fit). Обратите внимание, что мы сохранили атаку методом BIM в переменной attacks, потому что вместо одной атаки можно иметь список атак из библиотеки АРТ. Также обратите внимание, что в тренере AdversarialTrainer есть атрибут ratio. Он определяет процент тренировочных примеров, которые являются антагонистическими. Указанный процент существенно влияет на эффективность антагонистических атак. Если он слишком низкий, то тренер, возможно, будет работать с антагонистическими примерами плохо, а если он слишком высокий, то тренер, вероятно, будет работать с примерами, не связанными с антагонизмом, менее эффективно. Если выполнить trainer, то на его выполнение, скорее всего, уйдет много часов, так что не пугайтесь. Соответствующий фрагмент исходного кода показан ниже: robust_classifier = KerasClassifier(model=robust_model, clip_values=(min_, max_)) attacks = BasicIterativeMethod(robust_classifier, eps=0.3, eps_step=0.01,\ max_iter=20) trainer = AdversarialTrainer(robust_classifier, attacks, ratio=0.5) trainer.fit(X_train, ohe.transform(y_train), nb_epochs=30, batch_size=128) Если вы не натренировали устойчивый классификатор (robust_classifier), то можно скачать предварительно натренированную устойчивую модель (robust_model) и инициализировать с ее использованием устойчивого классификатора (robust_classifier) следующим образом: model_path = get_file('CNN_Robust_MaskedFace_Net.hdf5',\ '''https://github.com/PacktPublishing/InterpretableMachine-Learning-withPython/blob/master/models/CNN_Robust_MaskedFace_Net. hdf5?raw=true''')
Глава 13. Устойчивость к антагонизму 593 robust_model = tf.keras.models.load_model(model_path) robust_classifier = KerasClassifier(model=robust_model, clip_values=(min_, max_)) Теперь оценим устойчивый классификатор (robust_classifier) относительно исходного тестового набора данных, используя функцию evaluate_multiclass_mdl. Мы устанавливаем атрибут plot_conf_matrix=True, чтобы увидеть матрицу путаницы, следующим образом: (y_test_robust_pred, y_test_robust_prob) = mldatasets.evaluate_multiclass_mdl(\ robust_classifier.model, X_test, y_test, labels_l, ohe,\ plot_conf_matrix=True, predopts={"verbose":1}) Рис. 13.12. Метрики матрицы путаницы и результативности устойчивого классификатора Приведенный исходный код выводит матрицу путаницы и метрики результативности, показанные на рис. 13.12. Устойчивый классификатор на 1,8% менее точен,
594 Часть III. Настройка на интерпретируемость чем базовый. Большинство ошибочных классификаций связаны с тем, что правильно закрытые масками лица классифицируются как неправильно закрытые масками. Безусловно, есть компромисс при выборе 50%-го соотношения антагонистических примеров, или, может быть, стоит немного отрегулировать гиперпараметры либо модельную архитектуру, чтобы его улучшить. Посмотрим, как устойчивая модель противостоит антагонистическим атакам. Давайте снова применим метод FastGradientMethod, но на этот раз заменим базовый классификатор (base_classifier) устойчивым классификатором (robust_classifier) следующим образом: attack_fgsm_robust = FastGradientMethod(robust_classifier, eps=0.1) X_test_fgsm_robust = attack_fgsm_robust.generate(X_test_mdsample) Далее можно применить функции evaluate_multiclass_mdl и compare_image_predictions, чтобы измерить и наблюдать эффективности нашей атаки, но на этот раз относительно устойчивого классификатора (robust_classifier), следующим образом: (y_test_fgsm_robust_pred,\ y_test_fgsm_robust_prob) = mldatasets.evaluate_multiclass_mdl(\ robust_classifier.model, X_test_fgsm_robust,\ y_test_mdsample, labels_l, ohe, plot_conf_matrix=False, plot_roc=False) y_test_fgsm_robust_prob = np.max(y_test_fgsm_robust_prob, axis=1) mldatasets.compare_image_predictions(\ X_test_fgsm_robust, X_test_mdsample,\ y_test_fgsm_robust_pred, num_samples=4,\ y_test_mdsample.flatten(), y_test_fgsm_robust_prob,\ y_test_mdsample_prob, title_mod_prefix="Атакованое:",\ title_difference_prefix="Средняя пертурбация атаки методом FSGM:") Приведенный фрагмент исходного кода выводит несколько метрик результативности, которые засвидетельствовали точность в размере 95,5%. Если сравнить одинаково усиленную атаку методом FGSM относительно базового классификатора (base_classifier), то можно заметить, что тот дал точность 44%. Это было настоящее улучшение! Приведенный выше исходный код также создает сетку изображений, показанную на рис. 13.13. По рисунку можно судить, как атака методом FGSM на устойчивую модель делает изображения менее зернистыми и более пятнистыми. В среднем они менее пертурбированы, чем были относительно базовой модели, потому что очень немногие из них были успешными, но те, которые были, значительно деградировали. Похоже, что метод сократил глубину их цвета с миллионов возможных цветов (24+ бита) до 256 (8-битовых) или 16 (4-битовых) цветов. Конечно же, эвазивная атака на самом деле не способна этого сделать, но алгоритм FGSM сошелся в тех же оттенках синего, коричневого, красного и оранжевого, что и те, которые могли обманывать классификатор! Другие оттенки остаются неизменными. До сих пор мы оценивали устойчивость моделей, но только по одной-единственной силе атаки, не принимая в расчет возможные защиты строгим кросс-валидированным способом, и тем самым сертифицируя ее устойчивость. В следующем разделе мы изучим два метода, которые это делают.
Глава 13. Устойчивость к антагонизму 595 Рис. 13.13. Сравнение изображений, атакованных методом FGSM, с исходными изображениями в устойчивом классификаторе Оценивание и сертифицирование устойчивости к антагонизму В любой инженерной деятельности следует обязательно проводить валидацию своих систем. Это делают для того, чтобы понимать степень их уязвимости к атакам или случайным отказам. Однако безопасность — это область, в которой необходимо подвергать свою систему стресс-тестированию, чтобы определять уровень атаки, достаточный для того, чтобы вывести вашу систему из строя за пределами допустимого порога. Кроме того, процесс выяснения уровня защиты, необходимого для отражения атаки, является полезной информацией. Сравнение устойчивости модели с силой атаки Теперь у нас есть два классификатора, которые можно сравнить относительно одинаково мощной атаки, и можно попробовать разные силы атаки, чтобы увидеть, как они ведут себя. Мы будем использовать FGSM-метод, т. к. он быстр, но можно использовать любой! Первая сила атаки, которую можно продиагностировать, — это отсутствие силы атаки. Другими словами, какова точность классификации относительно тестового набора данных без атаки? Мы уже сохранили предсказанные метки как для базовой (y_test_pred), так и для устойчивой (y_test_robust_pred) модели, поэтому их легко получить с помощью метрики точности (accuracy_score) библиотеки scikit-learn, как показано в следующем фрагменте исходного кода: accuracy_base_0 = metrics.accuracy_score(y_test, y_test_pred) accuracy_robust_0 = metrics.accuracy_score(y_test, y_test_robust_pred)
596 Часть III. Настройка на интерпретируемость Теперь можно прокрутить силы атак (eps_range) от 0,01 до 0,9 в цикле. Используя функцию linspace, можно сгенерировать 9 значений от 0,01 до 0,09 и 9 значений от 0,1 до 0,9 и конкатенировать (concatenate) их в один массив. Мы будем валидировать атаки с этими 18 значениями eps, пройдя по всем ним в цикле for, а затем атакуя каждую модель и получая точности после атаки с помощью библиотечного метода evaluate(). Соответствующие точности добавляются в два списка (accuracy_base и accuracy_robust), а после цикла for мы добавляем в начало eps_range ноль, чтобы учесть точности до любых атак, как показано в следующем фрагменте исходного кода: eps_range = np.concatenate((np.linspace(0.01, 0.09, 9),\ np.linspace(0.1, 0.9, 9)), axis=0).tolist() accuracy_base = [accuracy_base_0] accuracy_robust = [accuracy_robust_0] for eps in tqdm(eps_range, desc='EPS'): attack_fgsm.set_params(**{'eps': eps}) X_test_fgsm_base_i = attack_fgsm.generate(X_test_mdsample) _, accuracy_base_i = base_classifier.model.evaluate(\ X_test_fgsm_base_i, ohe.transform(y_test_mdsample)) attack_fgsm_robust.set_params(**{'eps': eps}) X_test_fgsm_robust_i =attack_fgsm_robust.generate(X_test_mdsample) _, accuracy_robust_i = robust_classifier.model.evaluate(\ X_test_fgsm_robust_i, ohe.transform(y_test_mdsample)) accuracy_base.append(accuracy_base_i) accuracy_robust.append(accuracy_robust_i) eps_range = [0] + eps_range Теперь можно построить график точностей обоих классификаторов по всем силам атак с помощью следующего исходного кода: fig, ax = plt.subplots(figsize=(14,7)) ax.plot(np.array(eps_range), np.array(accuracy_base),\ 'b–', label='Базовый классификатор') ax.plot(np.array(eps_range), np.array(accuracy_robust),\ 'r–', label='Устойчивый классификатор') legend = ax.legend(loc='upper center', shadow=True, fontsize=15) plt.xlabel('Сила атаки (eps)', fontsize=17) plt.ylabel('Точность', fontsize=17) Приведенный исходный код генерирует рис. 13.14, который демонстрирует, что устойчивая модель работает лучше в диапазоне от 0,02 до 0,3, но затем работает стабильно хуже, примерно на 10%. Единственное, что рис. 13.14 не учитывает, — это защита. Если бы, к примеру, больничные камеры постоянно глушились или подделывались, то было бы крайне
Глава 13. Устойчивость к антагонизму 597 неосторожным со стороны охранной компании не защитить свои модели. Сделать это для такого рода атак довольно просто — посредством своего рода сглаживания. Рис. 13.14. Точность, измеренная для устойчивого и базового классификаторов в разных значениях сил атаки методом FGSM Антагонистическое обучение также создает эмпирически устойчивый классификатор, работу которого в рамках заранее определенных обстоятельств вы не сможете гарантировать, поэтому существует необходимость в сертифицированных защитах. А как насчет того, чтобы встроить эти защиты в саму модель и заодно сертифицировать устойчивость? Пожалуй, как раз это мы и рассмотрим далее — сертифицирование устойчивости с помощью рандомизированного сглаживания! Сертифицирование устойчивости с помощью рандомизированного сглаживания Исходный код только для этого раздела можно найти по адресу: https://github.com/ PacktPublishing/Interpretable-Machine-Learning-with-Python/tree/master/Chapter12/ Masks_part2.ipynb. Все шаги подготовки повторяются с самого начала. Однако, в отличие от остальных, в нем используется оценщик TensorFlow v2 библиотеки ART, а не оценщик Keras, потому что на момент написания этой книги рандомизированное сглаживание в ART недоступно для Keras, и многие из ранее описанных методов недоступны для Tensorflow v2. По этой причине в данном блокноте мы не отключаем немедленное исполнение (tf.compat.v1.disable_eager_execution()), как это делалось раньше. Кроме того, принятый по умолчанию вещественный тип задается равным 32 (tf.keras.backend.set_floatx('float32')), поскольку имплементация этого метода бывает нестабильной с 64-битовыми вещественными типами.
598 Часть III. Настройка на интерпретируемость Сейчас мы изучим метод, который больше, чем метод оценивания; это метод сертификации устойчивости. Но он делает даже больше, чем просто сертифицирует, потому что также тренирует устойчивую модель. Ранее мы видели, как сглаживание может нейтрализовывать антагонистический шум, но вам приходится применить его на шаге предобработки, не говоря уже о том, что вам нужно выяснять, в каком объеме его применять, чтобы оно было эффективным. В рандомизированном сглаживании этот принцип сглаживания задействуется путем построения "сглаженного" классификатора из базового классификатора. Как и в любом классификаторе, предсказанный класс — это класс с наибольшей вероятностью P. Соответствующая формула приведена ниже: g  x   arg max   f  x     c  . cY Разница в том, что она применяет случайный гауссов шум  к копиям входных данных. Шум является гауссовым, потому что он подчиняется нормальному распределению N и ограничен дисперсией 2 . Соответствующая формула представлена ниже:  ~ N  0,  2 I  . Процесс сертификации гарантирует этот результат, доказывая, что сглаженный классификатор g является устойчивым для x в L2 -радиусе R, как показано ниже: R  1   pa   1  pb   .  2 Здесь 1 — это кумулятивная функция распределения (cumulative distribution function, CDF) для гауссовой функции, а pa и pb — вероятности, соответственно, для наиболее вероятного и второго по вероятности класса. Из этой информации важно вынести то, что предсказания классов оперируют в границах решения, и роль радиуса — служить в качестве порога для воздержания от сертифицирования устойчивости сглаженного классификатора g для x. И действительно, привлекательным свойством сглаженного классификатора является то, что он может воздерживаться как от предсказывания, так и от сертифицирования. Сделать предсказание может оказаться невозможным из-за "близости результатов", если оно не проходит статистическую проверку биномиальной гипотезы, в которой параметр  является порогом, что делает его доказуемо уязвимым к антагонистическим атакам. Имплементации рандомизированного сглаживания могут предпочесть не настаивать на воздержании от предсказания, но у них не получится сертифицировать предсказание. В целях обучения сглаженного классификатора сначала необходимо определить базовый классификатор и все его тренировочные параметры. Для этого необходимо инициализировать несколько стандартных параметров, таких как число эпох (nb_epochs), размер пакета (batch_size), алгоритм оптимизации на основе градиент-
Глава 13. Устойчивость к антагонизму 599 ного спуска (optimizer) и функция потерь (loss_object). Рандомизированное сглаживание также должно знать число классов (nb_classes) и размер выборки (sample_size), т. е. число пертурбированных экземпляров, которые классификатор должен создавать в расчете на каждый пример. Далее необходимо изменить наши тренировочный и тестовый наборы данных, чтобы они имели тип float32 вместо float64. Будет полезно создать версию меток, кодированную с одним активным состоянием, чтобы было проще подключать их к функциям обучения (y_train_ohe) и оценивания (y_test_mdsample_ohe) без необходимости каждый раз их преобразовывать. Соответствующий фрагмент исходного кода можно увидеть ниже: nb_epochs = 10 batch_size = 128 optimizer = tf.keras.optimizers.Adam(lr=0.001) loss_object = tf.keras.losses.CategoricalCrossentropy() nb_classes = len(np.unique(y_train)) sample_size = 100 X_train, X_test_mdsample =\ (X_train.astype(np.float32), X_test_mdsample.astype(np.float32)) y_train_ohe = ohe.transform(y_train).astype(np.float32) y_test_mdsample_ohe = ohe.transform(y_test_mdsample).astype(np.float32) Теперь напишем простую функцию get_model, которая возвращает ненатренированную базовую модель. Она имеет ту же архитектуру, что и ранее использовавшаяся базовая модель. Соответствующий исходный код приведен ниже: def get_model(input_shape, min_, max_): test_model = Sequential([\ Conv2D(16, (3, 3), activation='relu', input_shape=input_shape),\ MaxPooling2D(pool_size=(2, 2)),\ Conv2D(32, (3, 3), activation='relu'),\ MaxPooling2D(pool_size=(2, 2)),\ Conv2D(64, (3, 3), activation='relu'),\ MaxPooling2D(pool_size=(2, 2)),\ Conv2D(128, (3, 3), activation='relu'),\ MaxPooling2D(pool_size=(2, 2)),\ Flatten(),\ Dense(768, activation='relu'),\ Dropout(0.35),\ Dense(3, activation='softmax')])\ return test_model
600 Часть III. Настройка на интерпретируемость Далее мы определяем функцию train_step, которая применяет обновления градиента к тренируемым переменным (trainable_variables) с учетом модели (model), изображений (images) и меток (labels). В ней задействуются ранее определенная функция loss_object, которая используется для вычисления потерь, и оптимизатор (optimizer) для применения градиентов, как показано в следующем фрагменте исходного кода: def train_step(model, images, labels): with tf.GradientTape() as tape: predictions = model(images, training=True) loss = loss_object(labels, predictions) gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) Следующая функция, train_rs_classifier, инициализирует и тренирует гладкий классификатор с использованием оценщика TensorFlowV2RandomizedSmoothing из библиотеки ART. Если sigma (  ) равна нулю, что означает отсутствие дисперсии в предполагаемом гауссовом шуме, то она также может построить несглаженный классификатор с помощью TensorFlowV2Classifier и поместить его в TensorFlowV2RandomizedSmoothing, чтобы он мог быть сертифицирован с помощью атрибута sigma_cert. Указанная функция принимает тренировочные данные (x_train, y_train) и все параметры, которые мы инициализировали ранее. Она также по умолчанию устанавливает порог  воздержания от предсказаний на уровне 0,001. Соответствующий исходный код приведен ниже: def train_rs_classifier(X_train, y_train, nb_epochs, batch_size, min_, max_,\ nb_classes, sample_size, loss_object, train_step,\ sigma=0, sigma_cert=0.5, alpha=0.001): input_shape = X_train.shape[1:] if sigma > 0: rs_classifier = TensorFlowV2RandomizedSmoothing(\ model=get_model(input_shape, min_, max_),\ input_shape=input_shape, clip_values=(min_, max_),\ nb_classes=nb_classes, sample_size=sample_size,\ loss_object=loss_object, train_step=train_step,\ scale=sigma, alpha=alpha, channels_first=False) rs_classifier.fit(X_train, y_train, nb_epochs=nb_epochs,\ batch_size=batch_size) return rs_classifier else: classifier = TensorFlowV2Classifier(\ model=get_model(input_shape, min_, max_),\ input_shape=input_shape, clip_values=(min_, max_),\
Глава 13. Устойчивость к антагонизму 601 nb_classes=nb_classes, loss_object=loss_object,\ train_step=train_step, channels_first=False) classifier.fit(X_train, y_train, nb_epochs=nb_epochs,\ batch_size=batch_size) rs_classifier = TensorFlowV2RandomizedSmoothing(\ model=classifier.model, input_shape=input_shape,\ clip_values=(min_, max_), nb_classes=nb_classes,\ sample_size=sample_size, loss_object=loss_object,\ train_step=train_step,\ scale=sigma_cert, alpha=alpha, channels_first=False) return classifier, rs_classifier Теперь давайте натренируем три классификатора следующим образом:  classifier_0 — несглаженный классификатор. Обратите внимание, что при   0 функция train_rs_classifier также возвращает rs_classifier_0, который является не сглаженным классификатором, а натренированным несглаженным сертифицируемым классификатором;  rs_classifier_1 — сертифицированно сглаженный классификатор с   0,25.  rs_classifier_2 — сертифицированно сглаженный классификатор с   0,5. Следующий фрагмент исходного кода тренирует три перечисленных выше классификатора с помощью функции train_rs_classifier: sigma_0 = 0 classifier_0, rs_classifier_0 = train_rs_classifier(\ X_train, y_train_ohe, nb_epochs, batch_size,\ min_, max_, nb_classes, sample_size, loss_object, train_step, sigma_0) sigma_1 = 0.25 rs_classifier_1 = train_rs_classifier(X_train, y_train_ohe, nb_epochs,\ batch_size, min_, max_, nb_classes, sample_size, loss_object,\ train_step, sigma_1) sigma_2 = 0.5 rs_classifier_2 = train_rs_classifier(X_train, y_train_ohe, nb_epochs,\ batch_size, min_, max_, nb_classes, sample_size, loss_object,\ train_step, sigma_2) После обучения трех классификаторов мы сможем делать предсказания (predict) на тестовых выборках (x_test_mdsample) для всех из них. На это уйдет больше времени, чем обычно, т. к. ему необходимо убедиться в устойчивости предсказаний. Соответствующий исходный код для этого приведен ниже: y_preds_0 = classifier_0.predict(X_test_mdsample) y_preds_rs_1 = rs_classifier_1.predict(X_test_mdsample) y_preds_rs_2 = rs_classifier_2.predict(X_test_mdsample)
602 Часть III. Настройка на интерпретируемость С помощью предсказаний теперь можно замерить предсказательную результативность всех трех классификаторов посредством функции compute_accuracy. Полезной особенностью этой функции является то, что она возвращает точность и охват. Охват — это процент сделанных предсказаний, другими словами, процент предсказаний, от которых он не воздержался; а точность рассчитывается только на предсказаниях, которые он сделал. Взгляните на следующий фрагмент исходного кода: acc_0, cov_0 = compute_accuracy(y_preds_0, y_test_mdsample_ohe) acc_rs_1, cov_rs_1 = compute_accuracy(y_preds_rs_1, y_test_mdsample_ohe) acc_rs_2, cov_rs_2 = compute_accuracy(y_preds_rs_2, y_test_mdsample_ohe) print("Изначальный классификатор") print(": %.2f%%: %.2f%%" % (acc_0, cov_0)) print("Сглаженный классификатор (σ=%.2f)" % (sigma_1)) print(": %.2f%%: %.2f%%" % (acc_rs_1, cov_rs_1)) print("Сглаженный классификатор (σ=%.2f)" % (sigma_2)) print(": %.2f%%: %.2f%%" % (acc_rs_2, cov_rs_2)) Приведенный фрагмент выводит на экран следующее: Изначальный классификатор Точность: 99.50% Охват: 100.00% Сглаженный классификатор (σ=0.25) Точность: 100.00% Охват: 99.50% Сглаженный классификатор (σ=0.50) Точность: 98.99% Охват: 99.50% Имея всего 100 образцов, оцененных относительно 200 изображений, все три классификатора не слишком далеки друг от друга. С   0, 25 сглаженный классификатор сообщает о 100%-й точности, но с 99,5%-ным охватом, что говорит о том, что классифицировать одно изображение было особенно трудно. Скорее всего, это то же самое изображение, которое несглаженный классификатор классифицировал ошибочно. С   0,5 сглаженный классификатор уменьшает точность, предполагая, что увеличение  в шуме привело к ошибочной классификации еще одного изображения. Эти результаты кажутся очень многообещающими, но на самом деле мы еще не проводили стресс-тестирование классификаторов. Мы делаем это с помощью библиотечного метода certify(), за исключением того, что на этот раз мы увеличиваем число образцов до 500 (n). Указанный метод возвращает предсказания и соответствующие радиусы по каждому предсказанию, как показано в следующем ниже фрагменте исходного кода: predictions_0, radiuses_0 = rs_classifier_0.certify(X_test_mdsample, n=500) predictions_1, radiuses_1 = rs_classifier_1.certify(X_test_mdsample, n=500) predictions_2, radiuses_2 = rs_classifier_2.certify(X_test_mdsample, n=500)
Глава 13. Устойчивость к антагонизму 603 Как задействовать радиусы для сертифицирования точности? Радиусы необходимо использовать в качестве порогов, измеряя процент предсказаний выше порога радиуса, которые остаются правильными. Для этого эффекта можно создать функцию (calc_cert_accuracy). Она принимает список валидируемых порогов радиусов (radius_list), результаты процесса сертификации модели (predictions, radiuses) и метки (y_test), относительно которых рассматривать предсказание. Соответствующий фрагмент исходного кода показан ниже: def calc_cert_accuracy(radius_list, predictions, radiuses, y_test): cert_accuracy = [] nb_certs = len(radiuses) for r in radius_list: r_idx = np.where(radiuses >= r)[0] y_test_subset = y_test[r_idx] cert_accuracy_r = np.sum(predictions[r_idx] ==\ np.argmax(y_test_subset, axis=1)) / nb_certs cert_accuracy.append(cert_accuracy_r) return cert_accuracy Теперь мы построим линейный график с валидируемыми порогами радиусов по оси x и соответствующей сертифицированной точностью по оси y для всех трех классификаторов. Мы будем варьировать 151 радиус (radius_list) в диапазоне от 0 до 1,5 (с равноотстоящим интервалом 0,01), а затем применим функцию calc_cert_accuracy для расчета сертифицированной точности для трех классификаторов. Остальная часть исходного кода просто выводит их на график относительно функции radius_list, как показано в следующем фрагменте исходного кода: radius_list = np.linspace(0, 1.5, 151) cert_accuracy_0 = calc_cert_accuracy(radius_list, predictions_0,\ radiuses_0, y_test_mdsample_ohe) cert_accuracy_1 = calc_cert_accuracy(radius_list, predictions_1,\ radiuses_1, y_test_mdsample_ohe) cert_accuracy_2 = calc_cert_accuracy(radius_list, predictions_2,\ radiuses_2, y_test_mdsample_ohe) plt.figure(figsize=(14,9)) plt.plot(radius_list, cert_accuracy_0, 'r-', label='оригинал') plt.plot(radius_list, cert_accuracy_1, '-', color='green',\ label='сглаженный, σ=' + str(sigma_1)) plt.plot(radius_list, cert_accuracy_2, '-', color='blue',\ label='сглаженный, σ=' + str(sigma_2)) plt.xlabel('Радиус', fontsize=14) plt.ylabel('Сертифицированная точность', fontsize=14) plt.legend() plt.show()
604 Часть III. Настройка на интерпретируемость Приведенный фрагмент создает график, показанный на рис. 13.15. Рис. 13.15. Сертифицированная точность для изначального несглаженного классификатора и обоих сглаженных классификаторов На рис. 13.15 показано, что обе сглаженные модели являются изначально более устойчивыми, чем несглаженная, но сглаженная с   0,5 всегда сертифицированно более надежна, тогда как   0, 25 не превышает радиуса около 0,5. Разумеется, мы получили бы еще более убедительные результаты, если бы оценивали относительно более 200 тестовых изображений, но превосходство сглаженных классификаторов, вероятно, сохранилось бы. Миссия выполнена Миссия состояла в том, чтобы провести несколько тестов на устойчивость к антагонизму на модели ношения маски для лица, чтобы определить, смогут ли посетители и медперсонал больницы уклоняться от обязательного соблюдения масочного режима. Базовая модель справлялась со многими эвазивными атаками очень плохо, от самых агрессивных до самых трудно уловимых. Вы также рассмотрели возможные защиты от этих атак, такие как пространственное сглаживание и антагонистическое переобучение, а затем нашли способы оценивания и сертифицирования устойчивости предлагаемых вами защит. Теперь можно предоставить сквозной каркас для защиты от такого рода атак. Тем не ме-
Глава 13. Устойчивость к антагонизму 605 нее сделанное вами представляло лишь доказательство концепции (proof of concept, POC). Далее можно предложить тренировать сертифицированно устойчивую модель относительно атак, с которыми больница ожидает сталкиваться чаще всего, но сначала вам понадобятся ингредиенты для устойчивой в целом модели. Для этого вам нужно будет взять все 210 000 изображений из исходного набора данных, внести ряд изменений в цвета и типы масок и еще больше расширить его с помощью разумных преобразований яркости, сдвига и поворота образцов. Наконец, устойчивая модель должна быть натренирована на несколько видов атак, включая несколько видов антагонистических заплат (AP). Они важны, потому что имитируют наиболее распространенное поведение уклонения от соблюдения требований, заключающееся в сокрытии лиц частями тела или предметами одежды. Резюме Прочитав эту главу, вы должны понять, каким образом могут совершаться атаки на модели машинного обучения и, в частности, с помощью эвазивных атак (атак уклонения). Вы должны научиться имитировать атаки быстрым методом на основе знака градиента (FGSM), базового итеративного метода (BIM), методом проецированного градиентного спуска (PGD), методом Карлини и Вагнера (C&W) и методом антагонистических заплат (AP) и противостоять им с помощью пространственного сглаживания, антагонистического обучения и рандомизированного сглаживания. И последнее, но не менее важное: вы должны научиться оценивать и сертифицировать устойчивость к антагонизму. Следующая глава — последняя, и в ней излагается несколько идей о том, что будет дальше с интерпретацией машинного обучения. Источники наборов данных  Cabani A., Hammoudi K., Benhabiles H., Melkemi M. MaskedFace-Net — a dataset of correctly/incorrectly masked face images in the context of COVID-19 / Smart Health. — Elsevier, 2020. — ISSN 2352-6483. — URL: https://doi.org/10.1016/ j.smhl.2020.100144 (лицензия Creative Commons BY-NC-SA 4.0 от корпорации NVIDIA). (Кабани А., Хаммуди К., Бенхабилес Х., Мелкеми М. MaskedFaceNet — набор фотоснимков с правильно/неправильно закрытыми маской лицами в контексте COVID-19.)  Karras T., Laine S., Aila T. A Style-based generator architecture for generative adver- sarial networks // 2019 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR). — 2019. — P. 4396–4405. — URL: https://arxiv.org/abs/1812.04948 (лицензия Creative Commons BY-NC-SA 4.0 от корпорации NVIDIA). (Каррас Т., Лейн С., Айла Т. Архитектура стилевого генератора для генеративных антагонистических сетей.)
606 Часть III. Настройка на интерпретируемость Справочные материалы  Polyakov A. How to attack Machine Learning: Evasion, Poisoning, Inference, Tro- jans, Backdoors. — URL: https://towardsdatascience.com/how-to-attack-machinelearning-evasion-poisoninginference-trojans-backdoors-a7cb5832595c. (Поляков А. Как атаковать машинное обучение: уклонение, отравление, выведение, трояны, обходы через лазейку.)  Carlini N., Wagner, D. Towards Evaluating the Robustness of Neural Networks // 2017 IEEE Symposium on Security and Privacy (SP). — 2017. — P. 39–57. — URL: https://arxiv.org/abs/1608.04644. (Карлини Н., Вагнер Д. К оцениванию устойчивости нейронных сетей.)  Brown T., Mané D., Roy A., Abadi M., Gilmer J. Adversarial Patch // ArXiv. — 2017. — URL: https://arxiv.org/abs/1712.09665. (Браун Т., Мане Д., Рой А., Абади М., Гилмер Дж. Антагонистическая заплата.)  Cohen J. M., Rosenfeld E., Kolter J. Z. Certified Adversarial Robustness via Randomized Smoothing // ICML. — 2019. — URL: https://arxiv.org/abs/1902.02918. (Коэн Дж. М., Розенфельд Э, Колтер Дж. З. Сертифицированная устойчивость к антагонизму с помощью рандомизированного сглаживания.)
14 Интерпретируемость машинного обучения: что дальше? В предыдущих тринадцати главах мы проводили поиск области интерпретируемости машинного обучения. Как указано в предисловии, она представляет собой обширную область исследований, бóльшая часть которых еще даже не покинула лабораторию и не стала широко использоваться, и в этой книге мы не намерены охватывать абсолютно все подобласти и методы. Вместо этого цель состоит в том, чтобы подробно представить различные инструменты обеспечения интерпретируемости так, чтобы они были полезны в качестве отправной точки для начинающих и даже дополняли знания более продвинутых читателей. В этой главе мы обобщим то, что мы узнали в контексте экосистемы методов обеспечения интерпретируемости машинного обучения, а затем поговорим о том, что будет дальше! Вот главные темы, которые будут рассмотрены в этой главе:  современное состояние интерпретируемости машинного бучения;  размышления о будущем интерпретируемости машинного бучения. Современное состояние интерпретируемости машинного обучения Прежде всего, мы предоставим некоторый контекст в отношении того, как эта книга соотносится с главными целями интерпретируемости машинного обучения и как практики могли бы начать применять методы для достижения этих широких целей. Затем мы обсудим текущие точки роста исследований. Связываем всё воедино! Как обсуждалось в главе 1, когда речь идет об интерпретируемости машинного обучения, имеются в виду три главные темы: объективность, подотчетность и прозрачность (fairness, accountability, and transparency, FAT), и каждая из них представляет собой ряд подтем (рис. 14.1). Думаю, каждый согласится, что все они являются для модели желательными свойствами! И действительно, это касается всех существующих возможностей для совершенствования систем искусственного интеллекта (ИИ). Эти совершенствования начинаются с использования методов модельной интерпретации для оценивания моделей, подтверждения или оспаривания допущений и отыскания проблем. Характер вашей цели будет зависеть от того, на какой стадии вы находитесь в рабочем потоке машинного обучения. Если модель уже внедрена в производство, то
608 Часть III. Настройка на интерпретируемость цель может состоять в том, чтобы оценить ее с помощью целого комплекта метрик, но если модель всё еще находится на ранней стадии разработки, то целью может быть отыскание более глубоких проблем, которые метрика не обнаружит. Возможно, вы также просто используете модели типа черного ящика для обнаружения знаний, как это делалось в главах 4–6; другими словами, задействуете модели для усвоения знаний из данных, не планируя использовать их в производстве. Если это так, то можно подтвердить или оспорить допущения, которые у вас были относительно данных и, соответственно, модели. В любом случае ни одна из этих целей не является взаимоисключающей, и вам, вероятно, всегда следует искать проблемы и оспаривать допущения, даже если модель, по всей видимости, показывает хорошую результативность! И независимо от цели и первостепенной задачи, использовать многочисленные методы интерпретации рекомендуется не только потому, что ни один технический прием не совершенен, но и потому, что все проблемы и цели взаимосвязаны. Другими словами, нет справедливости без выверенности и надежности — без прозрачности. На самом деле, можно читать рис. 14.1 снизу вверх, как если бы это была пирамида, потому что прозрачность лежит в основе всего, за ней на втором ярусе следует подотчетность и в конечном счете — объективность в качестве вишенки на торте. Поэтому, даже если цель состоит в том, чтобы оценить объективность модели, модель должна быть подвергнута стресс-тестированию на устойчивость. Все важности признаков и взаимодействия должны быть хорошо поняты. В противном случае будет уже неважно, если предсказания не окажутся надежными и прозрачными. На рис. 14.1 перечислен целый ряд методов интерпретации, и это далеко не все. На нем указаны наиболее популярные методы, за которыми стоят хорошо поддерживаемые библиотеки с открытым исходным кодом. В этой книге мы затронули большинство из них, хотя некоторые лишь вкратце. Обойденные вниманием выделены курсивом, а те, которые обсуждались, сопровождаются соответствующими номерами глав рядом с ними. Особо рассмотрены модельно-агностические методы для моделей типа черного ящика контролируемого обучения. И тем не менее за пределами этой области существует масса других методов интерпретации, таких как методы обучения с подкреплением, генеративные модели или многочисленные статистические методы, используемые строго для линейной регрессии. И даже в области моделей типа черного ящика контролируемого обучения существуют сотни прикладных методов модельной интерпретации, используемых в приложениях, начиная от CNN-сетей химических графов и заканчивая преобразовательными сетями. При этом многие из обсуждаемых в этой книге методов можно адаптировать для широкого спектра применений. Интегрированные градиенты можно применять для интерпретации аудиоклассификаторов, моделей гидрологического прогнозирования и естественно-языковых классификаторов настроений. Анализ чувствительности можно использовать в финансовом моделировании и моделях риска инфекционных заболеваний. Методы на основе причинно-следственного вывода — для улучшения пользовательского опыта и испытаний лекарственных препаратов. Здесь слово "улучшить" является ключевым, потому что методы интерпретации имеют обратную сторону!
Глава 14. Интерпретируемость машинного обучения: что дальше? ОБЪЕКТИВНОСТЬ Признаки Методы интерпретации Беспристрастность Справедливость Разнородность Инклюзивность  Сравнение метрик 609  Классовый баланс (FPR, FNR)  Сравнение графиков (матрица путаницы, кривая ROC, кривая PR)  Групповые метрики объективности / Индивидуальные метрики объективности (SPD, DI, AOD, EOD, DFBA, CDD)  Контурная карта/теплокарта вероятностей  Оценивания систематического смещения вследствие отбора данных ПОДОТЧЕТНОСТЬ Надежность Определенность Безопасность Безвредность Устойчивость Конфиденциальность  Вневыборочные оценивания  Анализ чувствительности (метод Соболя, метод Морриса, FAST)  Методы причинно-следственного вывода (DRL, DML, на основе леса, метаученики)  Оценивания эвазивной устойчивости к антагонизму (FSGM, PGD, C&W, антагонистические заплаты, граничный, PDG, B&B, DeepFool, ...)  Оценивания устойчивости к антагонизму на основе выведения результата, извлечения и отравления  Обнаружение/метрики аномалий  Метрики конфиденциальности ПРОЗРАЧНОСТЬ Интерпретируемость Объяснимость Выверенность Правдоподобность Ясность  Методы определения важности признаков (SHAP, перестановочные, модельно-агностические)  Методы редукции размерности (PCA, t-SNE, NAE, DIP-VAE)  Аквариумные методы (EMB, Skoped-Rules)  Графики частичной зависимости и подобные им (зависимости ICE, ALE, SHAP)  Суррогаты типа белого ящика (логистическая регрессия, линейная регрессия, модели на основе правил, CART, KNN, ProfWeight)  Подтверждение с помощью статистических тестов и корреляций (Спирмана, точечно-двурядовое, Крамера, Z-тест)  Локальная интерпретация (участки решения, ICE, якоря, контрфакты, WIT, CEM, SHAP)  Методы, специфичные для глубокого обучения (IG, карты значимости, Grad-CAM, SmoothGrad, семантическая сегментация)  Метрики объяснимости Рис. 14.1. Методы интерпретации машинного обучения
610 Часть III. Настройка на интерпретируемость ПОДХОД ДАННЫЕ МОДЕЛЬ ПРЕДСКАЗАНИЕ Перевесовка/DIR LFR / DIR / неосведомленность Чувствительное к стоимости усвоения Регуляризация предрассудков/махинации с объективностью Калибровка/ уравнивание шансов Установление ограждений Повышение надежности Конструирование признаков Насыщение данных Монотонные ограничения Антагонистическое устранение систематического смещения Воздержание от предсказаний Сокращение сложности Отбор признаков Регуляризация Повышение надежности Обнаружение дрифта Насыщение данных Антагонистическое обучение Сокращение сложности Регуляризация Отбор признаков Защиты от антагонизма на стадии предобработки Ослабление систематического смещения Конструирование признаков Монотонные ограничения (+ интеракционные/ двупеременные ограничения) Калибровка/уравнивание шансов Обеспечение конфиденциальности Анонимизация данных Дифференцированная конфиденциальность Объединенное (федеративное) усвоение Антагонистические защиты от атак на всеобъемлющее выведение информации Выведение результатов с сохранением конфиденциальности Сокращение сложности Отбор признаков Регуляризация Повышение надежности Конструирование признаков Монотонные ограничения ОБЪЕКТИВНОСТЬ Ослабление систематического смещения ПОДОТЧЕТНОСТЬ ПРОЗРАЧНОСТЬ Классификация с выбраковкой вариантов Сертификация модели на объективность Антагонистические защиты на стадии Преобразовательные защиты постобработки Защиты с испольот антагонизма зованием обнаружеСертифицированная по устойния антагонизма чивости к антагонизму обучение Предсказательные интервалы уверени выведение результатов ности Рис. 14.2. Набор инструментов для решения задач с FAT Локальная интерпретация
Глава 14. Интерпретируемость машинного обучения: что дальше? 611 В данной книге эта обратная сторона называлась настройкой на интерпретируемость, что означает создание решений задач на основе принципа FAT. Эти решения можно оценить на рис. 14.2. Я наблюдал пять подходов к техническим решениям по обеспечению интерпретируемости.  Ослабление систематического смещения — любая коррекционная мера, при- нятая для учета систематического смещения. Обратите внимание, что это смещение относится к систематическим смещениям вследствие отбора, исключения, предвзятости и измерений данных, наряду с любыми другими смещениями, вносимыми в рабочий поток машинного обучения.  Размещение ограждений — любое техническое решение, которое гарантирует, что модель не противоречит знаниям предметной области и не предсказывает без уверенности.  Повышение надежности — любая поправка, повышающая достоверность и вы- веренность предсказаний, исключая те, которые делают это за счет уменьшения сложности.  Уменьшение сложности — любые средства, с помощью которых вводится раз- реженность. В качестве побочного эффекта оно, как правило, повышает надежность за счет улучшенного обобщения.  Обеспечение конфиденциальности — любые усилия по защите личных дан- ных и модельной архитектуры от третьих сторон. В этой книге данный подход не рассматривался. Кроме того, эти подходы можно применять в отношении:  данных ("предобработка") — путем модифицирования тренировочных дан- ных;  модели ("промежуточная обработка") — путем модифицирования модели, ее параметров или тренировочной процедуры;  предсказания ("постобработка") — путем вмешательства в процесс выведения модельного результата. Есть и четвертая область, которая может влиять на перечисленные три, а именно, регулирование данными и алгоритмическое регулирование. Сюда входят правила и стандарты, которые диктуют определенную методологию или рамки. Этот столбец отсутствует, потому что очень немногие отрасли и юрисдикции имеют законы, диктующие методы и подходы, которые следует применять для соблюдения требований принципа FAT. Например, регулирование может возлагать стандарт для объяснения алгоритмических решений, происхождения данных или порога сертификации устойчивости. Мы обсудим это подробнее в следующем разделе. На рис. 14.2 видно, что для FAT многие методы повторяются. Отбор и конструирование признаков, монотонные ограничения и регуляризация приносят пользу всем трем подходам, но не всегда используются в одном и том же подходе. Насыщение данных также может повышать надежность для объективности и подотчетности. Как
612 Часть III. Настройка на интерпретируемость и на рис. 14.1, пункты, выделенные курсивом, не были освещены в этой книге, и из них выделяются две темы: устойчивость к антагонизму и сохранение конфиденциальности — увлекательные темы, заслуживающие отдельной книги. Текущие тренды Одним из наиболее существенных сдерживающих факторов внедрения ИИ является отсутствие интерпретируемости, что частично является причиной того, что 50–90% проектов ИИ никогда не запускаются, а другая причина — этические нарушения, которые происходят в результате несоблюдения принципа FAT. В этом аспекте интерпретируемое машинное обучение (interpretable machine learning, iML) может возглавить машинное обучение в целом, поскольку оно способно помочь в достижении обеих целей с помощью соответствующих методов, приведенных на рис. 14.1 и 14.2. К счастью, мы наблюдаем рост интереса и производства в iML, в основном в рамках объяснимого искусственного интеллекта (explainable artificial intelligence, XAI), рис. 14.3. В научном сообществе интерпретируемое машинное обучение попрежнему остается самым популярным термином, но объяснимый искусственный интеллект доминирует в публичной среде. О БЪЯСНИМЫЙ ИСКУССТВЕННЫЙ ИНТЕЛЛЕКТ ПРОТИВ ИНТЕРПРЕТИРУЕМОГО МАШИННОГО ОБУЧЕНИЯ : КАКУЮ ИЗ ЭТИХ ТЕХНОЛОГИЙ ИСПОЛЬЗОВАТЬ ? Мое мнение: хотя эти термины понимаются как синонимы, а интерпретируемое машинное обучение, скорее, рассматривается как академический, практикам машинного обучения, даже тем, кто работает в отрасли, следует относиться к использованию термина "объяснимый искусственный интеллект" с осторожностью. Слова могут обладать огромной силой внушения. Слово "объяснимый" предполагает полное понимание, но слово "интерпретируемый" оставляет место для ошибок, как и должно быть, когда речь идет о моделях, и к тому же чрезвычайно сложных моделях типа черного ящика. Кроме того, ИИ захватил общественное воображение как панацея либо был объявлен опасным. В любом случае, наряду с термином "объяснимый", он служит для того, чтобы сделать его еще более наполненным высокомерием для тех, кто думает, что он — панацея, и, возможно, успокоить тех, кто считает, что он опасен. Объяснимый искусственный интеллект как маркетинговый термин, возможно, и служит определенной цели. Однако для тех, кто строит модели, внушающая сила слова "объяснимый" может сделать нас слишком уверенными в наших интерпретациях. Но это всего лишь мнение. Это означает, что так же, как машинное обучение, которое начинает стандартизироваться, регулироваться, консолидироваться и интегрироваться в целый ряд других дисциплин, интерпретация тоже скоро получит свое место за столом. Машинное обучение заменяет программно-информационное обеспечение во всех отраслях промышленности. И по мере того, как всё больше областей автоматизируется, всё больше моделей развертывается в облаке. И всё станет еще хуже с искусственным интеллектом вещей (artificial intelligence of things, AIoT). Развертывание традиционно не находится в рулевой рубке практика машинного обучения.
Глава 14. Интерпретируемость машинного обучения: что дальше? 613 Рис. 14.3. Публикации и поисковые тренды в отношении интерпретируемого машинного обучения и объяснимого искусственного интеллекта Именно поэтому машинное обучение всё больше зависит от операций машинного обучения (machine learning operations, MLOps)1. А темпы автоматизации означают, 1 Операции машинного обучения — практика применения наработок DevOps с целью автоматизации, управления и аудита рабочих потоков машинного обучения. — Прим. перев.
614 Часть III. Настройка на интерпретируемость что для построения, валидирования, развертывания и мониторинга этих моделей требуется больше инструментов. В то же время существует необходимость в стандартизации инструментов, методов и метрик. Медленно, но верно это происходит. С 2017 года у нас действует Открытый нейросетевой обмен (Open Neural Network Exchange, ONNX) — открытый стандарт интеропреабельности. И на момент написания этой книги в Международной организации по стандартизации (International Organization for Standardization, ISO) было написано более двух десятков стандартов ИИ (и один опубликован), некоторые из них предусматривают интерпретируемость. Естественно, что-то будет стандартизировано из-за широко распространенного использования, из-за консолидации классов моделей машинного обучения, методов, библиотек, практик и поставщиков услуг. Со временем один или несколько человек в каждой области станут победителями. Наконец, учитывая огромную роль машинного обучения в алгоритмическом принятии решений, это только вопрос времени, когда они будут отрегулированы. Только некоторые финансовые рынки регулируют торговые алгоритмы, такие как Комиссия по ценным бумагам и биржам (Securities and Exchange Commission, SEC) в Соединенных Штатах и Управление по финансовому поведению (Financial Conduct Authority, FCA) в Великобритании. Кроме того, широко обеспечивается соблюдение только правил конфиденциальности и происхождения данных, таких как HIPAA в США и LGPD в Бразилии. GDPR в Европейском союзе заходит немного дальше со своим "правом на объяснение" алгоритмических решений, но предполагаемый масштаб и методология пока что не ясны. Интерпретируемость машинного обучения быстро растет, но отстает от собственно машинного обучения. Некоторые инструменты интерпретации были интегрированы в облачную экосистему, от SageMaker до DataRobot. Им еще предстоит полностью автоматизироваться, стандартизироваться, консолидироваться и отрегулироваться, но нет никаких сомнений в том, что это произойдет. Размышления о будущем интерпретируемости машинного обучения Нередко приходится слышать высказывания, которые метафорически называют этот период "Диким Западом искусственного интеллекта" или, что еще хуже, "Золотой лихорадкой ИИ"! Это вызывает в воображении образы неразведанной и необузданной территории, которую охотно завоевывают или, что еще хуже, цивилизуют. Тем не менее в XIX веке западные районы США не слишком отличались от других регионов планеты и уже давно были населены коренными американцами, так что метафора не совсем работает. Предсказание с точностью и уверенностью, которых можно достичь с помощью машинного обучения, напугало бы наших предков и не является "естественной" позицией для нас, людей. Это больше похоже на полет, чем на разведывание неизвестной земли. В статье "Навстречу реактивной эре машинного обучения" (ссылка приведена в разделе "Справочные материалы" в конце этой главы) представлена гораздо более
Глава 14. Интерпретируемость машинного обучения: что дальше? 615 подходящая метафора, состоящая в том, что ИИ подобен зарождению авиации. Она нова и захватывающа, и люди внизу все еще восхищаются тем, что можно сделать (рис. 14.4)! Однако ей еще предстояло реализовать свой потенциал. Спустя десятилетия после бурной эры авиация превратилась в безопасную, надежную и эффективную реактивную эру коммерческой авиации. В случае с авиацией обещание состояло в том, что она сможет надежно доставлять товары и людей через полмира менее чем за сутки. В случае ИИ обещание состоит в том, что он сможет принимать объективные, подотчетные и прозрачные решения — возможно, не для любой задачи, но, по меньшей мере, для тех, для которых он был разработан, если только это не пример общего искусственного интеллекта (artificial general intelligence, AGI). Рис. 14.4. Развлекательные полеты в 1920-е годы (источник: отдел печати и фотографий Библиотеки Конгресса США) И как же нам туда добраться? Далее приведены несколько идей, которые, как я ожидаю, возникнут в стремлении достичь реактивной эры машинного обучения. Новое видение машинного обучения Поскольку мы намерены продвинуться с ИИ дальше, чем когда-либо прежде, практики машинного обучения завтрашнего дня должны быть более осведомлены об опасностях неба. А под небом я подразумеваю новые рубежи предсказательной и предписывающей аналитики. Риски многочисленны и предусматривают всевозможные систематические смещения и допущения, проблемы с известными и потенциальными данными, а также математические свойства и ограничения наших моделей. Легко быть обманутым моделями машинного бучения, думая, что они являются программно-информационным обеспечением. Тем не менее в этой аналогии программно-информационное обеспечение полностью детерминировано по своей природе — оно прочно закреплено на земле, а не парит в небе!
616 Часть III. Настройка на интерпретируемость Для того чтобы гражданская авиация стала безопасной, ей требовалось новое мышление — новая культура. Летчики истребительной авиации Второй мировой войны, какими бы способными они ни были, должны были пройти переподготовку для работы в гражданской авиации. Это совсем другая миссия, потому что, когда вы знаете, что перевозите пассажиров на борту, и ставки высоки, все меняется. Этический ИИ и, как следствие, интерпретируемое машинное обучение, в конечном счете требуют осознания того, что модели прямо или косвенно перевозят пассажиров "на борту" и что модели не так устойчивы, как кажутся. Устойчивая модель должна быть способна надежно выдерживать практически любые условия снова и снова, как это делают современные самолеты. Для этого нам необходимо использовать больше инструментов, и эти инструменты представлены в форме методов интерпретации. Междисциплинарный подход Для моделей, соответствующих принципам FAT, необходима более тесная интеграция со многими дисциплинами. Это означает более активное участие специалистов по этике искусственного интеллекта, юристов, социологов, психологов, человеко-ориентированных дизайнеров и бесчисленного множества других профессионалов. Вместе с технологами ИИ и инженерами программно-информационного обеспечения они помогут воплотить лучшие практики в стандарты и правила. Соответствующая требованиям стандартизация Новые стандарты потребуются не только для исходного кода, метрик и методологий, но и для языка. Язык, лежащий в основе данных, в основном был заимствован из статистики, математики, информатики и эконометрики, что приводит к большой путанице. Исполнение регуляторных предписаний Вероятно, потребуется, чтобы все серийные модели соответствовали следующим техническим требованиям.  Являются сертифицированно устойчивыми и объективными.  Способны объяснять свои рассуждения, лежащие в основе одного предсказания, с помощью команды трассировки (TRACE), и в некоторых случаях должны предоставлять рассуждения вместе с предсказанием.  Могут воздерживаться от предсказания, в котором они не уверены.  Способны производить уровни уверенности для всех предсказаний.  Могут иметь метаданные о происхождении тренировочных данных (даже если они являются анонимными) и авторстве, а также, при необходимости, сертификаты соответствия нормативным требованиям и метаданные, привязанные к публичному реестру — возможно, блокчейну.
Глава 14. Интерпретируемость машинного обучения: что дальше? 617  Имеют сертификаты безопасности, во многом как это делается в отношении веб- сайтов, для обеспечения определенного уровня доверия.  Прекращают работу по истечении срока действия, пока не пройдут переобуче- ние с новыми данными.  Автоматически отключаются от сети, когда не проходят модельную диагности- ку, и снова подключаются к сети только после прохождения.  Имеют конвейеры непрерывного обучения/непрерывной интеграции (Conti- nuous Training/Continuous Integration, CT/CI), которые помогают перетренировывать модель и регулярно выполнять модельную диагностику, чтобы избегать простоев модели.  Диагностируются сертифицированным аудитором ИИ, когда они катастрофиче- ски отказывают и наносят общественный ущерб. Новые правила, скорее всего, способствуют созданию новых профессий, таких как аудиторы ИИ и инженеры по модельной диагностике. Но они также будут поддерживать инженеров MLOps и инструменты автоматизации машинного обучения. Бесшовная автоматизация машинного обучения со встроенной интерпретацией В будущем мы не станем программировать конвейер машинного обучения; в основном это будет процесс перетаскивания с помощью настроечной панели, предлагающей всевозможные метрики. Он будет эволюционировать главным образом в сторону автоматизации. Автоматизация не должна вызывать удивления, потому что некоторые существующие библиотеки выполняют обучение модели с использованием автоматизированного отбора признаков. Некоторые повышающие интерпретируемость процедуры могут выполняться автоматически, но большинство из них должны требовать человеческого наблюдения. Тем не менее интерпретация должна вводиться на протяжении всего процесса, подобно тому, как самолеты, которые в основном летают сами, имеют датчики, предупреждающие пилотов о проблемах; ценность заключается в информировании практика машинного обучения о потенциальных проблемах и улучшениях на каждом шаге. Найден ли признак, который можно рекомендовать для монотонных ограничений? Обнаружен ли какой-то дисбаланс, который, возможно, нуждается в корректировке? Обнаружении аномалии в данных, которые, возможно, нуждаются в некоторой поправке? Надо показать практику всё, что нужно знать, чтобы принять обоснованное решение, и дать ему принять это решение. Более тесная интеграция с инженерами MLOps Сертифицированно устойчивые модели, натренированные, валидированные и развернутые одним нажатием кнопки, требуют большего, чем просто облачная инфраструктура — оркестровку инструментов, конфигураций и людей, обученных в MLOps, для их мониторинга и регулярного технического обслуживания.
618 Часть III. Настройка на интерпретируемость Так же как авиации, которой потребовалось несколько десятилетий, чтобы стать самым безопасным видом транспорта, ИИ потребуется несколько десятилетий, чтобы стать самым безопасным способом принятия решений. Мы должны будем пройти длинный путь, чтобы добраться туда, но это будет захватывающее путешествие! И помните, лучший способ предсказать будущее — это его создать. Справочные материалы  O’Neil C. Weapons of Math Destruction. — Penguin Books, 2017. (О’Нил С. Ору- жие математического уничтожения.)  Talwalkar A. Toward the Jet Age of machine learning. — O’Reilly, 2018. — URL: https://www.oreilly.com/content/toward-the-jet-age-of-machine-learning/ (Талвалкар А. Навстречу реактивной эре машинного обучения.)
Предметный указатель A Accumulated local effects (ALE) 182 AdditiveExplainer 201 AI Fairness 360 (AIF360), библиотека 462, 578 AI Объяснимость 360 (AIX360), проект 578 alibi 279 AMT_14 450 Area under the curve (AUC) 94 Association-based distance metric (ABDM) 286 Automated traffic recorder (ATR) 364 Average treatment effects (ATE) 459 AVGGIFT 450 B Bayesian rule list (BRL) 123 C CatBoost, классификатор 273 ceteris paribus 63 Classification and regression tree (CART) 91, 118 CLUSTER 450 Conditional average treatment effects (CATE) 459 Contrastive explanations method (CEM) 267 Convolutional neural network (CNN) 306  классификатор 315  классификация ошибочная 320  модель 314 Correctional offender management profiling alternative sanctions (COMPAS) 516 C-SVC-модель, тренировка 237, 239 D DeepExplainer 199 DeepLift 198 DOMAIN_SOCIALCLS 450 E Eigen-CAM 337 Explainable boosting machine (EBM) 134 F False positive rate (FPR) 272 G Generalized additive model (GAM) 104, 201 Generalized linear model (GLM) 69 Generalized linear models (GLM) 103
620 Grad-CAM 336 Grad-CAM++ 337 Gradient-based one-side sampling (GOSS) 274 GradientExplainer 199 H HVP2 449 I IC15 451 K k ближайших соседей (kNN) 86, 248  важность признаков 127  интерпретация 125 k средних, кластеризация 240 k-d-дерево 299 KernelExplainer 200  вычисление значений SHAP 239 Kullback — Leibler divergence lower and upper confidence bounds (KL-LUCB) 278 L LASSO LARS 436 LASSO LARS IC 436 LASSO-регрессия 111, 123 LASTGIFT 449 LF AI & Data Foundation, фонд 578 LightGBM-модель, тренировка 253 LIME 200 LimeTabularExplainer, локальная интерпретация одиночного предсказания 249 LimeTextExplainer, локальная интерпретация одиночного предсказания 254 Предметный указатель LinearExplainer 200 Long short-term memory (LSTM) 362  атрибуции, генерирование 381 LSC2 451 M Matthews correlation coefficient (MCC) 95, 477 MAXRAMNT 449 MDMAUD_A 450 Miles per gallon (MPG) 183 Minimal variance sampling (MVS) 274 MINRAMNT 450 Modified value difference metric (MVDM) 286 Multi-armed bandit (MAB) 278 N NGIFTALL 450 O On-time performance (OTP) 76 Ordinary least squares (OLS) 85 out-of-bag 138 P Partial dependence plot (PDP) 73, 182 PartitionExplainer 200 PermutationExplainer 200 Precision-Recall (PR) 297 Principal component analysis (PCA) 97 p-значение 34, 60, 110 Q Q-Q-график 105 Quickshift 347
Предметный указатель R RAMNTALL 449 Root mean square error (RMSE) 88 RuleFit, модель 86, 123  интерпретация и важность признаков 124 R-квадрат (R2) 89 621 tf-explain 339 TreeExplainer 199 TreeSHAP 198 t-статистика 108, 110 V Variance inflation factor (VIF) 105 Variational autoencoder (VAE) 98, 286 S SamplingExplainer 200 Score-CAM 337 SHapley Additive exPlanations (SHAP) 198 Skoped Rules:  глобальная интерпретация 138  локальная интерпретация 139  модель 138  результативность 140 SmoothGrad 200, 588 T T-distributed stochastic neighbor embedding (t-SNE) 98 TensorFlow Lattice (TFL) 487 W What-If Tool (WIT) 289  конфигурирование 289  редактор точек данных 291  результативность и объективность 296 X XGBoost:  имплементирование ограничения модельного 551  оценивание результативности 192  строительство модели 192 XGBoost, модель 253
622 А Автокодировщик 299  вариационный (VAE) 98, 286 Агрегирование бутстраповских выборок 539 Активация промежуточная 325 Алгоритм:  генетический (GA) 444  мутация 444  отбор 444  скрещивание 444  глубокого обучения для определение важных признаков (DeepLIFT) 199, 340  многорукого бандита (MAB) 278 Анализ:  главных компонент (PCA) 97, 150  дисперсии (ANOVA), тест на основе F-статистики 432  квадратичных дискриминант (QDA) 161  линейных дискриминант (LDA) 150  важность признаков 159  чувствительности методом Соболя 401, 403 Атака:  антагонистическая 45  выведение результата 576  обход через лазейку 577  отравление 576  перепрограммирование 577  троянирование 577  эвазия 576  вазивная 576  методом инфинитной нормы (C&W) 581  целенаправленная методом AP 583  эвазивная:  методом инфинитной нормы (C&W) 581  быстрый метод на основе знака градиента (FGSM) 578  целенаправленная методом AP 583 Предметный указатель Б Байес наивный 127  бернуллиев 128  важность признаков 129  гауссов 91, 128  интерпретация 129  мультиномиальный 128  сигма и тета 129 Баланс перевыборки, подтверждение 520 Балл F1 (грамоническое среднее) 95, 152 Бандит многорукий (MAB) 278 Бережливость 41 Болезнь сердца ишемическая 53 Бройден — Флетчер — Гольдфарб — Шанно лимитированный по памяти (L-BFGS) 151 Бустинг 86, 91 Быстродействие 133 Бэггинг 86 В Важность признаков 35, 147  в LDA 159  в древесных моделях 154  в логистической регрессии 156  в многослойном персептроне 161  концепция 162  модельно-агностическая 444  недостатки 165  перестановочная (PFI) 145 239 Валидация перекрестная стратифицированная K-блочная 533 Величина расчетная выявленная 504 Вероятность равного соотношения (EPP) 492 Веса важности нейронов 336 Взаимодействие признаков 204 Взятие по одному за раз (OAT) 397  отбор образцов 394
Предметный указатель Вклад маргинальный 196 Влияние признака на исход, измерение 150 Вложение соседей t-распределенное стохастическое (t-SNE) 98 Возвратность инвестиций (ROI) 416 Воздействие неблагоприятное 475 Возможность равная 298 Воспроизводимость 531 Время вычисления 133 Выборка:  внепакетная 138  градиентная односторонняя (GOSS) 253, 274  минимально-дисперсная (MVS) 274  Монте-Карло 197, 218 Выброс 44 Выведение модельного результата 479 Выделение основ слов 234 Выпуклость 556 Выработка признаков 480 Выравнивание неодинакового обращения 491 Г Гамма, гиперпараметр 237 Гомоскедастичность 33, 105 Градиент:  ожидаемый 198  интегрированный (IG) 338  генерирование LSTM-атрибуций 381  использование объяснителя 339  по траектории 338 Граница:  решения 63  уверенности нижняя и верхняя дивергенции Кульбака — Лейблера (KL-LUCB) 278 График:  зависимости:  SHAP 207  интерпретирование 198 623  индивидуальных условных ожиданий (ICE) 174  недостатки 179  квантильно-квантильный 105  накопленного локального эффекта (ALE) 174, 182, 217  решения для локальной интерпретации групповых предсказаний 241  силовой SHAP 215  для локальной интерпретации одиночного предсказания 244  частичной зависимости (PDP) 73, 145, 182  интеракционный 171  интерпретирование 166  недостатки 174 Д Дерево:  градиентно-бустированное 91  классификационно-регрессионное (CART) 91, 118  решений 85, 118  важность признаков 121  интерпретация 119 Диагностирование риска рецидивизма, необъективное систематическое смещение 266 Дивергенция:  Дженсена — Шеннона 432  Кульбака — Лейблера 98, 278, 432 Дизайн:  ценностно-чувствительный 48  человеко-центрированный 48 Дискретизация 451, 525 Дисперсия:  внутриклассовая 150  межклассовая 150 Доверие 556  трапезоидное 556  Эджворта 556
624 Доказательство концепции (POC) 605 Доминирование 557 Дрейф:  данных 456  концепции 456  признаков 456 З Заболевание сердечно-сосудистое (ССЗ) 52, 397 Загрязненность узла 154 Задача многопеременного прогнозирования, инспектирование с помощью LSTM 362, 364 Защита антагонистическая:  детекция 586  постобработка 586  предобработка 585  преобразователь 586  тренировка антагонистическая 586 Знает то, что знает (KWIK) 491 Значение:  SHAP:  вычисление 201, 391  с помощью ядерного объяснителя 239  среднее 125  Шепли 196, 387  аддитивность 197  замещаемость 197  фиктивность 197  эффективность 197 И Игра фиктивная 488 Измерение влияния признака на исход 150 Иллюзия контроля за систематическим смещением 43 Индекс:  Джини 119  массы тела (ИМТ) 68 Предметный указатель  предубеждения 487  чувствительности, вычмсление методом Морриса 394 Инструментарий обеспечения устойчивости к антагонизму (ART), библиотека 578 Интеллект искусственный (ИИ) 36  объяснимый (XAI), в сопоставлении с iML 612 Интерактивность 72, 105 Интерпретация:  глобальная холистическая 116  локальная:  группы предсказаний с использованием графиков решений 241  для якорного объяснения 281  одиночного предсказания с использованием:  с использованием LimeTextExplainer 249, 254  силового графика 244  машинного обучения 30 Интерпретируемость 37  атрибуты 38  в сопоставлении с объяснимостью 37  имманентная 39  машинного обучения:  автоматизация бесшовная 617  будущее 614  видение 615  выгоды для бизнеса 42  интеграция с инженерами MLOps 617  исполнение регуляторных предписаний 616  ландшафт 607  подход междисциплинарный 616  прогнозы на будущее 614  стандартизация, соответствующая требованием 616  тренды 612  модели:  типа белого ящика 39  типа черного ящика 39
Предметный указатель  глобальная модулярная интерпретация 61  глобальная холистическая интерпретация 61  диапазоны 61  локальная интерпретация группы предсказаний 61  локальная интерпретация отдельного предсказания 61  постфактумная 38  потребность 37  ретроспективная 130  управление сложностью 37 Интерсекционность 303 Информация взаимная (MI) 432 К Карта:  активаций 306  классов (CAM) 336  заметности 333  чувствительности 332 Квадраты наименьшие обычные (OLS) 40, 108 Квази-константа, признак 429 Квартиль 525 Класс:  контрфактический 284  модельный, настройка 536  оценивание объективности наиболее результативной модели 542  пакетная гиперпараметрическая 539  релевантных моделей 536 Классификатор:  CNN базовый, оценивание 575  на основе списка байесовых правил (BRLC) 221  теорема Байеса об условных вероятностях 91  опорно-векторный (SVC) 236 625  с использованием махинаций с объективностью 489 Классификация:  гребневая 90  мультиномиальная 90  на основе взаимной информации (MIC) 432  несбалансированная 92  ошибочная, оценивание с помощью градиентных методов 332 Коалиция 196 Ковариация 34 Кодирование:  категориальное 235, 530  меток 530  порядковое 530  с одним активным состоянием 107, 279, 530, 571  фиктивное 530 Кодировка:  категориальная 149  с одним активным состоянием 149, 268 Количество значений с квазипостоянными признаками 429 Комплектование эксклюзивными признаками (EFB) 253 Компромисс:  между результативностью и интерпретируемостью 130  между систематическим смещением и дисперсией 134 Константа 64 Конструирование признаков 56, 447, 480  установка ограничений 522 Контрфакт 284 Корень из среднеквадратической ошибки (RMSE) 88, 376 Корпорация по управлению активами (AMC) 461 Корреляция:  линейная 34  мнимая 72
626 Коэффициент:  детерминации 89  инфляции дисперсии 105  корреляции:  Мэтьюса (MCC) 95, 152, 238, 477  Пирсона 34, 89, 430  Спирмена 205, 430  тау Кендалла 431  объясненной дисперсии (EVR) 443  сдвиговый 64 Кризис субстандартного ипотечного кредитования 460 Критерий:  информационный Акаике (AIC) 436  информационный байесов (BIC) 436  байесова оптимизация 545 Л Лемматизация 234 Лес случайный 86 Линейность 33 М Максимизация активации 328 Массирование меток 480 Матрица:  корреляций 105  путаницы, оценка смещения систематического предсказательного 270 Махинация с объективностью 487 Машина:  градиентно-бустинговая легкая (LightGBM) 229  объяснимая бустинговая 134  объяснимая бустинговая (EBM) 253  глобальная интерпретация 135  локальная интерпретация 136  результативность 137  опорно-векторная (SVM) 229, 489, 533 Предметный указатель Мера допуска 105 Метод:  активационный, визуализация процесса усвоения знаний 323  анализа чувствительности:  Морриса 362  Соболя 362  ансамблевый 86  бустинг 118  бэггинг 118  стэкинг 118  атрибуции:  градиентный 341  оценивание ошибочных классификаций 332  перестановочный 357  метод контрастивных объяснений (CEM) 349  объяснитель изображений LIME 347  чувствительность окклюзивная 344  пертурбационный 354  классифицирование 344  базовый итеративный (BIM) 590  быстрый на основе знака градиента (FGSM) 578  встроенный 435  дисперсный 401  интерпретации 61  диапазоны 61  глобальной модельной 147  традиционный:  оценивание CNNклассификатора 315  оценивание временного ряда 375  квази-Монте-Карло 401  классификационный:  гауссов наивный Байес 91  дерево:  классификационнорегрессионное (CART) 91  градиентно-бустированное 91  классификация гребневая 90
Предметный указатель            классифицирование статуса задержки рейса 89  метрики:  ROC-AUC 94  полнота 94  прецизионность 94  точность 93  оценивание 92, 95  регрессия логистическая 90  тренировка 92 контрастивных объяснений (CEM) 267, 299, 349  сравнение с объяснением:  контрафактическим 299  якорным 299 корреляционный фильтрационный 430  коэффициент корреляции:  Пирсона 430  тау Кендалла 431  ранговой Спирмена 430 модельно-агностический 608 модельного интерпретирования:  модельно-агностический 61  модельно-специфический 61  типы 61 модельной интерпретации:  глобальный 61  традиционный 84  классифицирование статуса задержки рейса с помощью классификационных методов 89  пределы 102  предсказывание задержки с помощью регрессионных методов 84 Монте-Карло 402 Морриса 394 наименьших квадратов (МНК) 85 определения важности признаков для инспектирования расового систематического смещения 547 отбора признаков:  встроенный, разведывательный анализ 435 627  гибридный 439  раскрытие потенциала 439  рекурсивное устранение признаков 442  оберточный 439  отбор последовательный обратный (SBS) 439  отбор последовательный плавающий обратный (SFBS) 439  отбор последовательный прямой (SFS) 439  отбор исчерпывающий (EFS) 439  поиск двунаправленный (BDS) 440  раскрытие потенциала 439  продвинутый 439  автокодировщики 443  алгоритмы генетические (GA) 444  метод модельно-агностический определения важности признаков 444  раскрытие потенциала 439  редукция размерности 443  фильтрационный 427  многопеременный 427  однопеременный 427  сравнение 434  перевесовки 481  ранжирующий фильтрационный 432  анализ дисперсии (ANOVA)  тест на основе F-статистики 432  информация взаимная (MI) 432  тест независимости на основе статистики хи-квадрат 432  регрессионный:  k ближайших соседей 86  Lasso-регрессия 111  RuleFit 86, 123  R-квадрат (R2) 89  деревья решений 85  корень из среднеквадратической ошибки (RMSE) 88  лес случайный 86  персептрон многослойный 87
628  регрессия:  гребневая 85  полиномиальная 85, 115  регуляризации стохастической 533  редукции размерности:  t-распределенное стохастическое вложение соседей (t-SNE) 98  автокодировщики вариационные 98  анализ главных компонент (PCA) 97  визуализирование задержек рейсов 96  смягчения систематического смещения:  постобработка 479, 490  воздержание от предсказания 490  классифицирование с выбраковкой вариантов 491  с калиброванным уравниванием шансов 491  с уравниванием шансов 491, 492  предобработка 479, 480  балансировка 480  конструирование признаков 480  неосведомленность 480  оптимизированная для предотвращения дискриминации 481  перевесовка 480, 481  переразметка 480  усвоение объективных представлений 481  устранитель неблагоприятного воздействия 481, 484  обработка промежуточная 479, 487  классификатор с использованием махинаций с объективностью 487, 489  ограничения 487  регуляризатор устранителя предубеждений 487  редукция градиента экспоненцированная 488  тренировка, чувствительная к стоимости 487 Предметный указатель устранение антагонистическое систематического смещения 488  устранитель предубеждения 489  тестирования расчетной устойчивости 510  добавление:  ненаблюдаемой общей причины 510  случайной общей причины 510  замена экспериментальной процедуры случайной переменной 511  опровергатель:  плацебо-процедур 510  подмножества данных 510  причина случайная общая 510  фильтрационный базовый 428  признаки:  дублирующие 429  квази-константные с количествами значений 429  постоянные с дисперсным порогом 428  устранение ненужных 430 Метрика:  конкретно-прикладная, разработка 544  модифицированной разности значений (MVDM) 286  объективности:  групповой 473  индивидуальной 472  расстояния на основе ассоциаций (ABDM) 286  регрессионная стандартная:  агрегации предсказательной ошибки 378  использование 376  оценивание как классификационной задачи 380  сравнение с методами смягчения систематического смещения 493 Миля:  в эквиваленте бензина на галлон израсходован-ного топлива (MPGe) 187  на галон израсходованного топлива 183 
Предметный указатель Мода 125, 127 Модель:  CNN базовая, загрузка 573  Keras:  оценивание 561  строительство с помощью TensorFlow Lattice 560  тренировка 561  аквариумная 134  Skoped Rules 138  машина объяснимая бустинговая 134  обнаружение 134  базовая:  оценивание для демонстрации эффекта нерелевантности признаков 422  строительство для демонстрации эффекта нерелевантности признаков 421  тренировка на разных максимальных глубинах для демонстрации эффекта нерелевантности признаков 425  глобально-суррогатная 221  готовая 558  древовидная 435  важность признаков 154  интрепретируемая имманентно 123  косвенная 221, 273  тренировочная 267  линейная обобщенная (GLM) 69, 103  машинного обучения на основе экземпляров 86  модельно-агностическая 162  настройка на интерпретируемость 532  модельных классов 536  нейронной сети Keras 533  непроницаемая, признаки 41  обобщенная:  аддитивная (GAM) 104, 134, 201  линейная (GLM), регрессия  гребневая 111  логистическая 116  полиномиальная 115 629  оценивание по прецизионности 541  предсказания веса 31  причинно-следственная:  инициализация линейного дважды устойчивого ученика 502  инспектирование причин 500  инспектирование результатов 497  компоненты 500  подгонка 502  строительство 495, 500  подгонка и оценивание с помощью параметров 546   прозрачная 39  разреженная линейная 111  регуляризованная с коэффициентами 435  построенная с применением отбора признаков, оценивание 445  типа:  белый ящик 39  RuleFit 123  ближайшие соседи 125  дерево решений 118  наивный Байес 127  черный ящик 39  тренировка для предсказания задержек внутренних рейсов 77 Модификатор гетерогенного эффекта экспериментальной процедуры 500 Монотонность 72, 205, 556 Мультиколлинеарность 34, 165 Н Набор данных отложенный 88 Набросок взвешенный квантильный 253 Направление главное 98 Настройка:  байесова гиперпараметрическая:  выполнение 545  оптимизирование объективности 544  порога 297
630 Недавность/частота/сумма (RFA) 450 Независимость 33, 105 Нелинейность 69, 105 Немонотонность 72, 105 Непроницаемость 41 Норма:  L1 532  L2 85, 532 Нормальность 33, 105  многопеременная 160 О Обработка естественного языка (NLP)  LIME 251  для аддитивных объяснений Шепли (SHAP) 257 Образец Сальтелли:  генерирование 402  предсказывание 402 Обучение:  дважды устойчивое (DRL) 501  машинное интерпретируемое (iML), в сопоставлении с XAI 612 Обход через лазейку 577 Объективность:  исхода 468  подотчетность и прозрачность (FAT) 36, 103, 607  процедуры внутренней 468  сглаженная эмпирическая дифференцированная 475 Объяснение:  аддитивное Шепли (SHAP) 182, 198, 548  в сопоставлении с LIME 260  применение в NLP 257  пропущенность 198  стабильность 198  точность локальная 198  контрфактическое 267, 284  по прототипам 285  сравнение с методом контрастивных объяснений (CEM) 299 Предметный указатель  локально-интерпретируемое модельно-агностическое (LIME) 247, 347  в сопоставлении с SHAP 260  применение 247  применение для NLP 251  слова ключевые 247  якорное 267  интерпретации локальные 281  подготовка с помощью alibi 279  сравнение с методом контрастивных объяснений (CEM) 299 Объяснимость 40  в сопоставлении с интерпретируемостью 37  используемость 41  потребность 41 Объяснитель:  SHAP:  выборочный 200  глубокий 199, 357  градиентный 199  древовидный 199  линейный 200  ядерный 200  вычисление глобальных и локальных атрибуций 387  заложение основы для стратегии аппроксимации перестановок 389  имплементирование для локальной интерпретации со значениями SHAP 236  определение стратегии для работы с моделью на основе многопеременного временного ряда 388  потребность 387  изображений LIME 347  вывод объяснений на график 348  извлечение изображения и маски из объяснения 348  инициализация объяснений 347  объяснение 347  создание объяснений 347
Предметный указатель  Шепли:  аддитивный 201  инициализация 201  перестановочный 200  пораздельный 200 Ограничение:  взаимодействия 550  контура 556  модельное:  для TensorFlow Lattice 556  имплементирование 556  инициализация модели и входов в Lattice 558  для XGBoost 551  инспектирование 553  оценивание 552  тренировка 552  установка параметров регуляризации и ограничения 551  имплементирование 550  для TensorFlow Lattice 556  для XGBoost 551  монотонное 550 Один-против-остальных (OvR) 156 Оператор наименьшего абсолютного сжатия и отбора 436 Оптимизированная предобработка для предотвращения дискриминации 481 Остатки в сопоставлении с ошибкой 33 Отбор:  атрибутов 420  обратный последовательный (SBS) 439, 441  переменных 420  плавающий:  обратный последовательный (SFBS) 439  прямой последовательный (SFFS) 439  признаков 420  исчерпывающий (EFS) 439  прямой последовательный (SFS) 439, 440 631 Отклик мультиномиальный 103 Отравление 576 Отсев 131 Отсутствие мультиколлинеарности 105 Отыскание расщеплений с учетом разреженности 253 Охват 278 Оценка плотности ядра 499 Оценщик 88  конкретно-прикладной 558  стандартный 557 Ошибка:  в сопоставлении с остатками 33  среднеквадратическая (MSE) 189  фундаментальная атрибуции 44 П Парадокс Симпсона 475 Паритет демографический 298 Перевесовка 480 Перевыборка 480 Переменная:  отклик 30  предсказательная 30  признаковая 30  целевая 30  Переменная опосредующая 67 Переподгонка 85 Перепрограммирование 577 Переразметка 480 Пересечение 64 Переформатирование меток 480 Персептрон многослойный 87  важность признаков 161 Пиксел 149 Площадь под кривой (AUC) 94, 238, 560 Подавление 480 Поддержка внутреннего состояния 386 Подрезание 131 Подход тройной 518
632  к техническим решениям по обеспечению интерпретируемости 611 Поиск двунаправленный (BDS) 440 Политика внутрикорпоративная процедурная 496 Полнота 152 Понтирование 490 Популяция 48 Порог:  дисперсный с постоянными признаками 428  классификационный 297 Последовательность Соболя 402 Постобработка:  с калиброванным уравниванием шансов 492  с уравниванием шансов 491 Потеря шарнирная 237 Правдоподобие отрицательное логарифмическое 151 Правило:  c ограниченным диапазоном 278  принятия решения 123 Предобработка для защиты от целенаправленных атак 585 Предпочтение ожидания над преобразованием (EOT) 583 Предсказание отдельное, интерпретирование с помощью логистической регрессии 62 Преобразование нелинейное 526 Препятствия на пути интерпретируемости машинного обучения 67  интерактивность 72  нелинейность 69  немонотонность 72 Прецизионность 278 Прецизионность — полнота (PR) 297, 494 При прочих равных условиях 63 Признак:  гетерогенности 500 Предметный указатель  интеракционный 72  квази-постоянный 429  меньшее подмножество, преимущества 420  постоянный 428  с более высокой кардинальностью 156 Принцип простоты 41 Приоретизация факторная 394  выявления влиятельных признаков 394 Причина общая 501 Проблема исчезающего градиента 386 Прозрачность 130  аквариумная в сопоставлении с обоснованной прозрачностью 42  алгоритмическая 40  конструктивная 40  модельная 40  обоснованная в сопоставлении с аквариумной прозрачностью 42  типы 40 Производительность своевременной работы 76 Просвечиваемость 130 Простота 41 Пространство:  векторное:  вещественное 127  целочисленное 127  пертурбационное 278 Протокол передачи гипертекста (HTTP) 149 Профилирование исправительного управления правонарушителями для альтернативных санкций (COMPAS) 266, 516 Процедура обращения экспериментальная 497 Процесс усвоения знаний, визуализация с помощью активационных методов:  максимизация активации 328  промежуточные активации 325
Предметный указатель Проявление систематического смещения:  вероятность 468  гибрид 468  представление 468  распределение 468 Пул предметный международный личностный (IPIP) 146 Р Раб кредитной карты 460 Равновесие Нэша 488 Разбивка на корзины. См. Дискретизация Разложение:  матрицы ковариаций на собственные значения 97  сингулярное (SVD) 443 Разница:  в равных возможностях 478  в средних 475  статистически паритетная 475 Разреженность 111 Распределение гауссово 91 Распространение релевентности послойное (LRP) 340 Расстояние:  L1 285  L2 285  евклидово 285  манхэттенское 285 Регламент о защите персональных данных (GDPR) 577 Регресия:  гребневая 85, 104, 111, 436  важность признаков 115  интерпретация 112  линейная 103, 104  важность признаков 108  интерпретация 106  логистическая 103, 116, 436  важность признаков 117, 156 633  интерпретация 117  отдельных предсказаний 62  на основе эластичной сети 436  обычная линейная (OLS) 502  опорного-векторная (SVR) 237  полиномиальная 104, 115  пуассонова 103  с наименьшим углом 436 Регулирование алгоритмическое 42 Регуляризатор:  гессианов 557  лапласов 557  складок 557  торсионный 557 Регуляризация 85 Редукция размерности 443 Результат:  истинноотрицательный 270  истинноположительный 270  ложноотрицательный 270  ложноположительный 270  пертинентный:  отрицательный (PN) 299, 349  положительный (PP) 299, 350 Результативность:  оценивание 131  предсказательная 133 Рецидивизм 266 Решатель 151 Ряд временной, оценивание традиционными методами интерпретации 375 С Сведение на основе:  глобального среднего (GAP) 336  максимума (MaxPooling) 336 Сводка SHAP:  генерирование графиков 202  интерпретирование 198
634 Свойство:  модельное:  объяснимость 130  регуляризуемость 131  фиктивности 239 Связь:  латентная 153  мнимая 72 Сглаживание:  пространственное 588  рандомизированное для сертифицирования устойчивости 597 Сеть:  глубокая решетчатая (DLN) 558  нейронная:  Keras 533  выполнение гиперпараметрической настройки 534  инспектирование результатов 535  определение модели и параметров 533  оценивание наилучшей модели 535  глубокая:  оценивание результативности 188  строительство модели 188  рекуррентная (RNN) 381  сверточная 306  эластичная 532  наивная 286 Сжатие (штраф) 111, 112 Сила выразительная 108 Система оповещения о воздушном движении и предупреждения столкновений в воздухе (TCAS) 38 Скорость исполнения 133 Слой калибровки 556 Случай положительный 59 Смещение систематическое:  визуализирование в наборе данных 469  из-за заметности 44 Предметный указатель      из-за иллюзии контроля 43 из-за консерватизма 44 из-за опущенных переменных 35 из-за отбора данных 35 квантифицирование в наборе данных 472  модельное:  квантифицирование 476  обнаружение 467  предсказательное, оценка с помощью матрицы путаницы 270  смягчение 479 Соседи ближайшие:  k ближайших (kNN) 125  радиусные 248 Список байесовых правил 123 Спуск градиентный проецированный (PGD) 586 Средняя разница шансов (AOD) 478 Ставка на принцип разреженности 112 Стандарт двойной 303 Станция автоматической регистрации дорожного движения 364 Статус социально экономический 450 Стэкинг 86 Суждение альтернативное 284 Суррогат:  глобальный 182  интерпретирование 223  оценивание 222 Т Таблица просмотровая интерполированная 556 Теорема Байеса об условной вероятности 127 Теория игр:  коалиционных 196  кооперативных 196 Тест:  амплитудной чувствительности на рядах Фурье (FAST) 401  Гольдфельда — Квандта 105
Предметный указатель  Колмогорова — Смирнова 105  независимости на основе статистики хи-квадрат 432 Техника синтетической избыточной выборки меньшинства (SMOTE) 480 Точность 152 Тренировка:  антагонистическая устойчивого классификатора, противостояние эвазивной атаке 590  модели 479 Троянирование 577 Трюк ядерный 237 635 Ф Факт противоречащий 284 Фактор спутывающий 501 Фиксирование факторов 401 Формула линейно-регрессионная 32 Функция:  кусочно-линейная (PWL) 558  логистическая 58, 116  радиально-базисная (RBF) 237  распределения кумулятивная (CDF) 598  стоимости реалистичная, встраивание 405 У Унимодальность 556 Упорядочение 523 Усвоение объективных представлений 481 Усиление смещения дифференцированной объективности 478 Устойчивость:  к антагонизму:  оценивание 595  сертифицирование 595  модели, сравнение с силой атаки 595  расчетная, тестирование 510  сертифицирование с помощью рандомизированного сглаживания 597 Устранение признаков рекурсивное 442 Устранитель:  неблагоприятного воздействия 481, 484  предубеждения 489 Участок решения 63, 278 Ученик:  ленивый 86, 125  сильный 86  слабый 86  усердный 86, 125 Х Характеристика приемника 94, 494, 575 Ц Цепочка принятых решений 42 Ч Частота:  классификаций:  истинноположительных (TPR) 478  ложноотрицательных (FNR) 478  ложноположительных (FPR) 478, 542  результатов:  истинноположительных 94  ложноположительных 272  отсутствия информации (NIR) 153  появления ошибки null 153  термина — обратная частота документа (TF-IDF) 251 Член взаимодействия 526 Чувствительность 94
636 Ш Шанс логарифмический 59 Шкалирование минимаксное 285 Штраф 112 Э Экземпляр:  интересующий 276  контрфактический 288 Этика техно-моральных добродетелей 48 Эффект:  гетерогенный экспериментальной процедуры 503  выбор политических мер 507 Предметный указатель  накопленный локальный (ALE), графики 174  нерелевантного признака 420  Расёмона 285  процедуры экспериментальной:  средний (ATE) 504  условный средний (CATE) 501  элементарный (EE) 394  анализирование 398 Я Я не знаю (IDK) 490 Язык гипертекстовой разметки (HTML) 32 Якорь 264, 278
Уатт Д., Борхани Р., Катсаггелос А. www.bhv.ru Машинное обучение: основы, алгоритмы и практика применения Отдел оптовых поставок: e-mail: opt@bhv.ru  Интуитивно понятные объяснения  Доступный подход к современным методам численной оптимизации  Комплексное введение в логистическую регрессию и машины опорных векторов  Представление сложных тем через призму аппроксимации функций  Уточненное описание глубоких нейронных сетей и методов ядра Благодаря интуитивно понятному, но строгому подходу к машинному обучению эта книга предоставляет фундаментальные знания и практические инструменты, необходимые для проведения исследований и разработки систем машинного обучения. В книге приведено более 100 углубленных упражнений на языке Python. Дано введение в машинное обучение и математическую оптимизацию, включая методы первого и второго порядков, градиентного спуска и Ньютона. Отдельно рассмотрены продвинутые методы оптимизации. Приведено полное описание обучения с учителем, включая линейную регрессию, двухклассовую и многоклассовую классификацию, а также обучение без учителя и фундаментальные методы генерации признаков. Дано введение в нелинейное обучение с учителем и без. Обсуждается тема автоматизированного отбора подходящих нелинейных моделей, включая перекрестную валидацию, бустирование, регуляризацию и ансамблирование. Рассмотрены фиксированно-контурные ядра, нейронные сети, деревья и другие универсальные аппроксиматоры.
Элбон К. www.bhv.ru Машинное обучение с использованием Python. Сборник рецептов Отдел оптовых поставок: e-mail: opt@bhv.ru В книге Вы найдете рецепты для:  обработки числовых и категориальных данных, текста, изображений, дат и времени;  уменьшения размерности с использованием методов выделения или отбора признаков;  оценивания и отбора моделей;  сохранения и загрузки натренированных моделей. Научитесь решать задачи с использованием:  векторов, матриц и массивов;  линейной и логистической регрессии, деревьев, лесов и k ближайших соседей;  опорно-векторных машин (SVM), наивных байесовых классификаторов, кластеризации и нейронных сетей. Книга содержит около 200 рецептов, которые помогут решить задачи машинного обучения, возникающие в повседневной работе практикующего специалиста, такие как загрузка и обработка текстовых или числовых данных, отбор модели, уменьшение размерности и многие другие. Рассмотрена работа с языком Python и его библиотеками, в том числе pandas и scikit-learn. Решения всех задач сопровождаются подробными объяснениями. Каждый рецепт содержит программный код, который можно скопировать и опробовать на игрушечном наборе данных (toy dataset). Затем этот код можно вставлять, объединять и адаптировать, создавая собственные приложения. Крис Элбон — аналитик данных и политолог с десятилетним опытом применения статистического обучения, искусственного интеллекта и разработки программного обеспечения для политических, социальных и гуманитарных проектов — от мониторинга выборов до оказания помощи в случае стихийных бедствий. В настоящее время является ведущим аналитиком данных в компании BRCK, продвигающей интернет-технологии на африканский рынок.
Лакшманан В., Робинсон С., Мунн М. www.bhv.ru Машинное обучение. Паттерны проектирования Отдел оптовых поставок: e-mail: opt@bhv.ru Подготовка данных, создание моделей, внедрение в производство Вы научитесь:  Выявлять и преодолевать трудности, встречающиеся во время тренировки, оценивания и развертывания моделей машинного обучения  Представлять данные для разных типов моделей машинного обучения, включая векторные вложения, гибриды признаков и многое другое  Выбирать правильный тип модели для той или иной задачи  Строить надежный цикл тренировки с использованием контрольных точек, распределительной стратегии и гиперпараметрической настройки Приводимые в книге паттерны проектирования отражают лучшие практические подходы к решению типичных задач машинного обучения. Указанные паттерны, реализованные в программном коде, сконцентрировали опыт сотен экспертов в простые и легкодоступные советы. Книга содержит подробный разбор 30 паттернов, служащих для представления данных и задач, тренировки моделей, отказоустойчивого обслуживания, обеспечения воспроизводимости и искусственного интеллекта. Каждый паттерн включает в себя постановку задачи, ряд потенциальных решений и рекомендации по выбору технического приема, наилучшим образом подходящего к данной ситуации. Электронный архив на сайте издательства содержит код описанного в книге веб-приложения и другие полезные файлы. Валлиаппа Лакшманан, руководитель отдела аналитики данных и ИИ в коллективе разработчиков Google Cloud. Сара Робинсон, занимает должность developer advocate в коллективе разработчиков Google Cloud со специализацией в машинном обучении. Майкл Мунн, инженер по техническим решениям в области машинного обучения в компании Google, где он помогает клиентам разрабатывать и внедрять модели машинного обучения.
Уиндер Ф. www.bhv.ru Обучение с подкреплением для реальных задач Отдел оптовых поставок: e-mail: opt@bhv.ru  Освойте обучение с подкреплением и узнайте, как его алгоритмы применяются при решении задач  Основательно проработайте фундаментальные темы RL, рассмотренные в книге: марковские процессы принятия решений, динамическое программирование и обучение с применением временной разницы  Разберитесь в различных методах формулирования ценностных функций и градиентов политик  Применяйте продвинутые RL-решения, в частности, метаобучение, иерархическое обучение, мультиагентное и имитационное обучение Книга посвящена промышленно-ориентированному применению обучения с подкреплением (Reinforcement Learning, RL). Объяснено, как обучать промышленные и научные системы решению любых пошаговых задач методом проб и ошибок — без подготовки узкоспециализированных учебных множеств данных и без риска переобучить или переусложнить алгоритм. Рассмотрены марковские процессы принятия решений, глубокие Q-сети, градиенты политик и их вычисление, методы устранения энтропии и многое другое. Данная книга — первая на русском языке, где теоретический базис RL и алгоритмы даны в прикладном, отраслевом ключе. Фил Уиндер, междисциплинарный инженер, эксперт и автор онлайн-курсов на платформе O’Reilly. Возглавляет компанию Winder Research, оказывающую консультации в области науки о данных (data science) для облачно-ориентированных приложений. Компания помогает оптимизировать процессы, связанные с обработкой данных, а также обслуживает платформы и продукты, работающие в этой нише. Автор имеет степени PhD и MEng в электротехнике, полученные в Университете Халла.