Все модели ошибочны, но некоторые из них полезны. — Джордж Бокс

Этот пост является первым в серии из трех, которые соответствуют лекциям качество моделей нашего курса Машинное обучение в производстве, за которыми следуют Измерение точности моделей и Тестирование моделей (за пределами Точность). Более короткая версия этих трех глав ранее была опубликована в виде записи в блоге. Другие темы смотрите в таблице содержания.

Для специалистов по данным оценка обычно начинается с измерения точности прогноза на наборе данных. Хотя точная модель не гарантирует, что система в целом будет хорошо работать в производственной среде при взаимодействии с пользователями и окружающей средой, обеспечение качества модели является важным элементом любой стратегии обеспечения качества для производственных систем с компонентами машинного обучения. Даже без учета качества данных, качества системы и качества инфраструктуры (рассматриваемых в других главах) анализ и измерение качества модели представляет собой сложную задачу, отчасти потому, что она значительно отличается от традиционного тестирования программного обеспечения.

Мы продолжим в трех частях: во-первых, в этой главе мы обсудим более фундаментально, что означает правильность или ошибка модели, даже подчеркнем трудности и различия по сравнению с традиционным тестированием программного обеспечения. . В следующей главе мы рассмотрим традиционные меры точности прогнозов, используемые в науке о данных, и обсудим типичные ловушки, которых следует избегать. В третьей главе мы обсудим, как можно применить более традиционные стратегии тестирования программного обеспечения для обеспечения качества модели.

Пример работы: обнаружение рака

Давайте рассмотрим систему, которая использует радиологические изображения и информацию о пациенте (например, возраст, пол, историю болезни, другие проведенные тесты) в качестве входных данных, чтобы предсказать, есть ли у пациента рак. В двух словах, мы изучаем классификатор, представленный в виде одной изученной функции:

function hasCancer(image: byte[][], age: int, …): boolean

На практике такая модель будет интегрирована в более крупную систему, где она будет иметь доступ к картам пациентов и взаимодействовать с врачами, которые подтвердят окончательный диагноз. Модель, скорее всего, поддержит врачей, чтобы помочь им принимать более правильные решения, а не заменить их. Разработка такой системы, чтобы врачи доверяли ей, чрезвычайно сложна, как хорошо показано в недавнем исследовании Google Hello AI, и системе, вероятно, потребуется объяснить, как она приняла решение и где именно она подозревает рак, но для Теперь давайте просто сосредоточимся на модели бинарной классификации, не рассматривая остальную часть системы.

Что такое правильность?

Удивительно сложно определить, что значит для модели с машинным обучением быть «правильной» или «глючной». Прежде чем мы перейдем к моделям с машинным обучением, давайте кратко рассмотрим, как мы традиционно оцениваем правильность программных реализаций.

В традиционном программном обеспечении мы оцениваем функциональную правильность, сравнивая поведение программного обеспечения с предполагаемым поведением. В идеале у нас есть четкая спецификация предполагаемого поведения, которую можно использовать для определения правильности вычисленных выходных данных для заданных входных данных.

Давайте возьмем простой, хорошо понятный пример, не связанный с ML, с вычислением даты дня, следующего за данным днем. Это не особо сложная задача, но и не совсем тривиальная, учитывая разную длину месяцев и правила високосных лет в григорианском календаре.

/**
 * Given a year, a month (range 1–12), and a day (1–31),
 * the function returns the date of the following calendar day
 * in the Gregorian calendar as a triple of year, month, and day.
 * Throws InvalidInputException for inputs that are not valid dates.
 */
def nextDate(year: Int, month: Int, day: Int) = …

Процесс проверки реализации на соответствие спецификации называется проверкой, в отличие от проверки, которая проверяет, соответствует ли спецификация или реализация проблеме и потребностям клиента. Эти два термина являются стандартными в литературе по разработке программного обеспечения, но также сильно перегружены различными значениями в других контекстах.

Тестирование — это наиболее распространенный подход к проверке того, что реализация правильно реализует спецификацию. Чтобы протестировать программное обеспечение, мы запускаем функцию с предоставленными входными данными в контролируемой среде и проверяем, соответствуют ли выходные данные ожидаемым результатам, согласно к спецификации. Если какой-либо тестовый пример не пройден, мы обнаружили ошибку, предполагая, что тест соответствует спецификации.

@Test
void testNextDate() {
  assert nextDate(2010, 8, 20) == (2010, 8, 21);
  assert nextDate(2024, 7, 15) == (2024, 7, 16);
  assert nextDate(2011, 10, 27) == (2011, 10, 28);
  assert nextDate(2024, 5, 4) == (2024, 5, 5);
  assert nextDate(2013, 8, 27) == (2013, 8, 28);
  assert nextDate(2010, 2, 30) throws InvalidInputException;
}

Обычно мы не можем проверить все возможные входные данные, потому что их слишком много. Таким образом, хотя хороший режим тестирования может дать нам некоторую уверенность в правильности, он не дает гарантий, и мы можем пропустить определенные ошибки при тестировании. Существуют формальные методы, которые могут доказать, что программа соответствует спецификации для всех входных данных, но применение формальных методов встречается редко и в основном ограничивается несколькими и небольшими очень важными программными компонентами из-за значительных затрат. Функциональная корректность большинства программ оценивается посредством тестирования.

Важно отметить, что у нас есть сильные ожидания относительно правильности. Спецификации определяют, какие выходные данные являются правильными для заданных входных данных. Мы не оцениваем выходные данные как «довольно хорошие» или «точные на 95%» и не довольны алгоритмом, который вычисляет на следующий день «правильно для 95% всех входных данных на практике». У нас нет терпимости к случайным неправильным вычислениям, приближениям или недетерминированным выводам, если это явно не разрешено спецификацией. Одно неверное вычисление даты будет считаться ошибкой. На практике разработчики могут принять решение не исправлять определенные ошибки, потому что это неэкономично в некоторых настройках, а вместо этого согласиться с тем, что пользователи справляются с ошибочной реализацией — но мы все равно рассматриваем неправильное вычисление как ошибку.

Корректность моделей без спецификаций

То, как мы оцениваем модели с машинным обучением, сильно отличается от традиционного тестирования программного обеспечения. Вообще говоря, модель — это просто алгоритм, который использует входные данные для вычисления выходных данных; просто этот алгоритм был изучен на основе данных, а не закодирован вручную.

/**
 * ????
 */
function hasCancer(scan: byte[][]): boolean;

Ключевая проблема заключается в том, что у нас нет спецификации, которую мы могли бы использовать, чтобы определить, какие выходные данные являются правильными для любого заданного ввода. Мы используем машинное обучение именно потому, что у нас нет таких спецификаций, как правило, потому что проблема слишком сложна для определения или правила просто неизвестны. У нас есть идеи, цели и интуитивные догадки, скажем, «обнаружить, есть ли рак на изображении», которые можно рассматривать как неявные спецификации, но мы ничего не можем записать в виде конкретных правил относительно того, когда результат является правильным для любого случая. данный ввод.

Важно отметить, что мы обычно признаем, что мы не можем избежать некоторых неверных прогнозов. При использовании машинного обучения мы не можем и не ожидаем, что все результаты будут правильными. Один неверный прогноз обычно не является причиной для того, чтобы не использовать модель, тогда как при традиционном тестировании мы отвергаем модель из-за одного неверного прогноза. Ошибка модели из-за одного неверного прогноза кажется неправильной:

@Test
def testPatient1() {
  assertEquals(true, hasCancer(loadImage("patient1.jpg")));
}
@Test
def testPatient2() {
  assertEquals(false, hasCancer(loadImage("patient2.jpg")));
}

В целом, у нас есть слабые предположения о правильности. Мы оцениваем модели на примерах, а не по спецификациям того, что модель должна делать. Мы признаем, что ошибки случаются, и пытаемся оценить частоту ошибок и то, как они могут распространяться. Модель, которая верна в 90% всех прогнозов, может быть довольно хорошей.

Оценка соответствия модели, а не правильности («Все модели неверны»)

Лучший способ подумать о том, как оценивать модели, — думать о моделях не как об правильных или неправильных или глючных, а о том, >соответствует проблеме. Как уже говорилось, мы обычно не ожидаем, что модель всегда будет делать правильные прогнозы, но мы допускаем определенный уровень неправильных результатов. Ключевой вопрос заключается в том, являются ли прогнозы модели достаточно хорошими для практического использования при наличии случайных ошибок.

Хороший способ подумать об этом — афоризм Все модели ошибочны, но некоторые из них полезны, обычно приписываемый статистику Джорджу Боксу (см. Википедию для расширенного обсуждения цитаты):

Все модели являются приблизительными. Предположения, подразумеваемые или четко сформулированные, никогда не бывают в точности верными. Все модели ошибочны, но некоторые модели полезны. Таким образом, вопрос, который вам нужно задать, заключается не в том, «верна ли модель?» (это никогда не бывает), но «Достаточно ли хороша модель для этого конкретного приложения?» — Джордж Бокс

Хотя эта цитата прекрасно подходит для моделей с машинным обучением, изначально она относилась к моделированию в науке в более широком смысле, например, при создании моделей явлений реального мира, таких как гравитационные силы. Наглядным примером являются законы движения Ньютона, например, «скорость изменения импульса тела во времени прямо пропорциональна приложенной силе и происходит в том же направлении, что и приложенная сила: F = dp/dt. ” Эти модели дают общее объяснение наблюдений и могут использоваться для прогнозирования поведения объектов. На самом деле эти законы неоднократно проверялись экспериментально в течение 200 лет и использовались для всевозможных практических расчетов и научных инноваций. Однако известно, что эти законы являются приближениями, которые нельзя обобщить для очень малых масштабов, очень высоких скоростей или очень сильных гравитационных полей. Они не могут объяснить полупроводники, ошибки GPS, сверхпроводимость и многие другие явления, требующие общей теории относительности и квантовой теории поля. Таким образом, технически мы можем считать законы Ньютона неверными, поскольку мы можем найти множество входных данных, для которых они предсказывают неправильные результаты. В то же время законы невероятно полезны для прогнозирования масштабов и скоростей повседневной жизни.

Точно так же в случае с моделями с машинным обучением мы не рассчитываем изучить модели, которые являются «правильными» в том смысле, что они всегда дают правильный прогноз, но мы ищем модели, полезные для данной проблемы, которые в основном дают правильные результаты. прогнозы для входных данных, которые имеют значение.

Это все еще поднимает проблему того, как оценить полезность или пригодность. Ясно, что модель подходит лучше, когда она делает более правильные прогнозы; такая модель, вероятно, более полезна. Но какой уровень подгонки является достаточным? Сколько и каких неверных предсказаний мы можем принять? Это явно зависит от проблемы.

Еще одна цитата Джорджа Бокса также указывает нам, как думать об оценке соответствия:

«Поскольку все модели неверны, ученый должен быть готов к тому, что неправильно. Неуместно беспокоиться о мышах, когда за границей водятся тигры». - Джордж Бокс, 1976 г.

То есть модель всегда должна оцениваться в контексте проблемы, которую мы пытаемся решить. Мы также должны понимать, что не все ошибки одинаковы, и некоторые из них гораздо более проблематичны, чем другие. Если бы мы могли понять, в каких ситуациях мы можем полагаться на модель (например, не использовать законы Ньютона вблизи скорости света, не использовать детектор рака на детях), мы могли бы больше доверять модели.

Дедуктивное и индуктивное мышление

Еще одна концепция, которая может помочь нам прояснить подход к оценке качества модели, — это различие между дедуктивным и индуктивным рассуждениями.

Классическое программирование и контроль качества глубоко укоренены в дедуктивных рассуждениях. Дедуктивное рассуждение — это форма рассуждения, которая объединяет логические утверждения в соответствии с согласованными правилами для формирования новых утверждений. Это позволяет нам доказывать теоремы из аксиом. Это позволяет нам рассуждать от общего к частному. Это вид математического рассуждения, знакомый по формальным методам и классическим экспертным системам ИИ, основанным на правилах; вид рассуждений, которые мы используем для анализа поведения программы; разновидность формальности, которую мы используем для написания спецификаций и определения того, нарушает ли их программа.

Напротив, машинное обучение использует индуктивные рассуждения, когда мы пытаемся обобщать наблюдения. В целом, мы рассуждаем от частных наблюдений к общим правилам. Вместо того, чтобы заранее определять правила и аксиомы, мы пытаемся вывести их из данных. Веские доказательства в данных предполагают правило, но не гарантируют, что правило будет соблюдаться в целом. Это тип научных рассуждений, знакомых из естественных наук, где мы пытаемся открыть законы природы, часто с помощью наших статистических инструментов.

Индуктивное рассуждение в машинном обучении — вот почему так трудно получить четкое представление о правильности, и именно поэтому мы должны рассуждать о пригодности и полезности.

Проверка против проверки

Как обсуждалось выше, тестирование программного обеспечения — это подход к проверке, при котором реализация сравнивается со спецификацией. В традиционной разработке программного обеспечения проверка — это только часть истории: проверка — это другая часть понимания того, соответствует ли спецификация (или программное обеспечение, соответствие которому проверено на соответствие спецификации) соответствует проблеме.

Можно написать программу, которая идеально соответствует спецификации (т. е. все тесты проходят, может быть, даже формально проверены), но это совершенно бесполезно, когда спецификация не соответствует задаче. Например, реализация функции nextDate для Юлианского календаря может быть полностью правильной в соответствии со спецификацией этого календаря, но если мы хотим использовать ее для вычисления даты для дат в григорианском календаре (отличающемся, прежде всего, обработкой високосных лет ), это не особенно полезно. Проблемы валидации часто более тонкие, когда указанная функция не полностью соответствует потребностям пользователя или игнорирует важные предположения о том, как используется система. Например, полетное программное обеспечение ракеты Ариан-5 было реализовано правильно, как указано, но не учитывала более высокую скорость ракеты, из-за которой входные данные программного обеспечения выходили за ожидаемые и безопасные границы, что привело к аварии при запуске 1996 года, которая привела к при разрушении ракеты.



Взрыв при запуске ракеты «Ариан-5
Первый запуск ракеты-носителя Ариан-5 эффектно закончился неисправностью через несколько секунд после старта…www.youtube.com»



Проблемы валидации обычно относятся к области разработки требований: нам нужно выяснить, каковы потребности пользователей, а также взаимодействие с окружающей средой и потенциальные юридические ограничения и ограничения безопасности, чтобы затем указать решение, которое наилучшим образом соответствует потребностям. У пользователей часто возникают противоречивые потребности, и инженер по требованиям должен разрешить их и разработать спецификацию, которая наилучшим образом соответствует потребностям большинства пользователей (или важных пользователей), а также удовлетворяет другим ограничениям. Валидация, по сути, заключается в оценке пригодности системы для решения проблемы.

Интересно, что машинное обучение играет очень похожую роль по сравнению с разработкой требований в традиционной разработке программного обеспечения. Оба выполняют индуктивные рассуждения: инженеры по требованиям определяют спецификацию, которая лучше всего соответствует потребностям пользователя, на основе интервью и других входных данных, а машинное обучение определяет правила в данных, которые лучше всего соответствуют данным. Мы можем проверить изученную модель на соответствие другим ограничениям, которые у нас могут быть, например ограничениям справедливости и различным инвариантам (см. ниже), но основная часть оценки модели — это проверка того, насколько хорошо она соответствует задаче, а не соответствует ли модель спецификации. . В этом контексте действительно важными становятся вопросы о том, были ли данные обучения репрезентативными, достаточными и качественными.

Возможная интерпретация здесь состоит в том, чтобы рассматривать модель машинного обучения как спецификацию, полученную из данных. Переход от модели к реализации, как правило, прост, поскольку генерируется соответствующий код, и нам не нужно беспокоиться о проблемах реализации, которые могут обнаружить наши традиционные инструменты проверки. Вместо этого мы должны сосредоточиться на проблеме проверки, чтобы убедиться, что модель соответствует задаче, и, возможно, на проблеме совместимости или согласованности модели и других спецификаций. Одна из существенных новых проблем по сравнению с традиционной разработкой требований заключается в том, что большинство моделей с машинным обучением нелегко интерпретировать и они слишком сложны, чтобы люди могли легко определить, подходят ли они для решения задачи.

Обратите внимание, что нас может беспокоить правильность реализации алгоритма машинного обучения, изучающего модель. Действительно, в библиотеках машинного обучения было обнаружено много ошибок (точно так же, как много ошибок было обнаружено в компиляторах). Алгоритмы машинного обучения обычно четко определены и реализуются вручную; хотя это явно далеко не тривиально, особенно при недетерминированности многих алгоритмов обучения, для проверки их правильности можно использовать стандартные методы тестирования программного обеспечения. В этой книге мы сосредоточимся не на тестировании алгоритма машинного обучения, а на оценке моделей, которые они создают.

О терминологии: ошибки, правильность и производительность

Мы рекомендуем избегать термина ошибка модели и избегать разговоров о правильности модели, потому что это несет в себе слишком много багажа от традиционного тестирования программного обеспечения и дедуктивных рассуждений, которые нелегко сопоставить с оценки моделей, учитывая, что единичный неверный прогноз обычно не является проблемой сам по себе. Вместо этого лучше подходят термины, которые относятся к соответствию или точности модели.

Специалисты по машинному обучению обычно говорят о «производительности модели», когда речь идет о точности прогнозирования. Например, «одна модель работает лучше, чем другая» указывает на то, что первая модель в среднем дает более точные прогнозы по некоторым оценкам.

Мы избегаем термина «производительность» в этой книге, потому что он имеет очень разные значения в разных сообществах и может вызвать путаницу, если его небрежно использовать в междисциплинарном сотрудничестве. Большинство разработчиков программного обеспечения думают о времени выполнения алгоритма, говоря о производительности. Тем не менее, другие сообщества имеют совсем другие значения, такие как производительность в искусстве, производительность труда и производительность компании в бизнесе или тест производительности (экзамен на адвоката) в юриспруденции.

Мы намеренно используем термин точность прогнозадля обозначения того, насколько хорошо модель работает при оценке тестовых данных и времени или задержки, когда речь идет о длительности, такой как время обучения или задержка вывода.

Кроме того: тестирование производительности (время выполнения). Может быть интересно сравнить оценку модели с проверкой времени выполнения алгоритма в классической разработке программного обеспечения. Тесты времени выполнения обычно не имеют жестких спецификаций, а в основном имеют частичные спецификации верхних границ. Чаще всего мы добавляем только регрессионные тесты, гарантируя, что время выполнения не ухудшится по мере развития системы, или мы просто тестируем альтернативные реализации, не имея конкретной цели. Временное поведение обычно не является детерминированным, поэтому мы часто усредняем повторяющиеся выполнения (например, «90% всех выполнений завершаются через 1 с»).

@Test(timeout=100)
def testCompute() {
   expensiveComputation(…);
}

Несмотря на то, что в отсутствии жестких спецификаций есть некоторое сходство, аналогия с оценкой времени выполнения не подходит, поскольку источники неопределенности различаются между временем выполнения программного обеспечения и точностью модели с машинным обучением. При тестировании времени выполнения нам необходимо управлять недетерминизмом в программе или среде, тогда как большинство моделей с машинным обучением являются детерминированными в своих прогнозах (т. е. алгоритм обучения часто недетерминирован, но большинство моделей детерминированы). Следовательно, при оценке времени мы многократно выполняем одну и ту же программу с одними и теми же входными данными, тогда как в машинном обучении мы усредняем прогнозы для разных входных данных, а не повторные прогнозы для одного и того же входного сигнала.

Резюме

При отсутствии спецификаций качество модели всегда относится к подгонке, а не к правильности. Мы не отвергаем всю модель, потому что не согласны с одним предсказанием; вместо этого мы принимаем, что некоторые прогнозы будут неверными, и пытаемся количественно определить, как часто модель неверна для данных в нашей задаче. То есть оценка определит, соответствует ли модель проблеме и полезна ли она для решения проблемы. Это существенное отличие от того, как традиционно тестируется программное обеспечение, и оно гораздо больше похоже на действия по валидации программного обеспечения, такие как приемка и пользовательское тестирование.

В следующей главе мы рассмотрим традиционные стратегии оценки точности, направленные на измерение соответствия, и их подводные камни, прежде чем мы рассмотрим, как традиционные методы тестирования программного обеспечения могут быть адаптированы для контекста машинного обучения.

Чтения и ссылки

Как и все главы, этот текст выпущен под лицензией Creative Commons 4.0 BY-SA.