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

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

Существует много хороших работ о том, как измерить предвзятость модели машинного обучения, см. Здесь для примера.

Тем не менее, в этом посте мы хотим выделить еще один аспект устранения предвзятости — как спроектировать готовую к производству систему, которая может надежно реализовать эти методы устранения предвзятости. Как упомянуло исследовательское подразделение FICO по искусственному интеллекту:

«Обеспечение справедливости для готовых к производству систем машинного обучения в финтехе требует определенных инженерных обязательств на разных этапах жизненного цикла системы машинного обучения»

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

Готовые к производству этические системы машинного обучения с Delta, Databricks Feature Store и MLFlow.

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

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

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

На диаграмме ниже показано, как может выглядеть такая система на Databricks:

Обзор варианта использования

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

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

Полный код этого примера можно найти в этом репозитории Github.

Магазин функций

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

Для столбцов PAY_* (-2=нет кредита для оплаты, -1=оплата должным образом, 0=выполнение минимального платежа, 1=задержка платежа на один месяц, 2=задержка платежа на два месяца, … 8=задержка платежа на восемь месяцев, 9 = задержка платежа на девять месяцев и более), мы создаем функцию под названием num_paym_unmet, которая суммирует общее количество раз за 6 месяцев, когда у клиента была задержка платежа.

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

def num_paym_unmet_fn(df):
  """
  sums up the total number of times, over 6 months, that a customer had a delayed payment
  """
  def count_unmet(col):
    return F.when(F.col(col) > 0, 1).otherwise(0)
  feature_df = df[["USER_ID", "PAY_1", "PAY_2", "PAY_3", "PAY_4", "PAY_5", "PAY_6"]]
  feature_df = feature_df.withColumn("NUM_PAYM_UNMET", reduce(add, [count_unmet(x) for x in silver.columns if x in pay_cols])) \
.select("USER_ID", "NUM_PAYM_UNMET")
  return feature_df

def num_paym_met_fn(df):
  """
  sums up the total number of times, over 6 months, that a customer met their payment obligations
  """
  def count_met(col):
    return F.when(F.col(col) <= 0, 1).otherwise(0)
  feature_df = df[["USER_ID", "PAY_1", "PAY_2", "PAY_3", "PAY_4", "PAY_5", "PAY_6"]]
  feature_df = feature_df.withColumn("NUM_PAYM_MET", reduce(add, [count_met(x) for x in silver.columns if x in pay_cols])) \
.select("USER_ID", "NUM_PAYM_MET")
  return feature_df

num_paym_unmet_feature = num_paym_unmet_fn(silver)
num_paym_met_feature = num_paym_met_fn(silver)

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

Отслеживание показателей справедливости

Существует несколько пакетов с открытым исходным кодом для измерения показателей справедливости, например Microsoft Fairlearn и Google Model Cards. В этом примере мы будем использовать пакет Veritas с открытым исходным кодом, выпущенный Валютным управлением Сингапура, поскольку он является пример, показывающий, как включить соображения справедливости в производственную систему на основе рекомендаций и инструментов, разработанных действующим финансовым регулятором.

Чтобы использовать этот пакет, мы сначала определяем то, что мы называем защищенными переменными, которые являются входными функциями, которые не должны чрезмерно смещать нашу модель. Здесь мы устанавливаем пол человека и семейное положение в качестве защищенных переменных. Внутри этих защищенных переменных мы также выбираем привилегированные группы. В случае переменной SEX это «мужской» сегмент, а в случае переменной MARRIAGE — «женатый».

p_var = ['SEX', 'MARRIAGE']
p_grp = {'SEX': [1], 'MARRIAGE':[1]}

После инициализации эксперимента mlflow с помощью mlflow.start_run() мы передаем обучающие данные, защищенные переменные и группы, а также нашу модель в объект Model Container. Затем мы указываем, что хотим использовать этот объект для оценки кредитного скоринга, и передаем метрики, на которых мы хотим сосредоточиться, в этот объект.

cre_sco_obj= CreditScoring(model_params = [container], fair_threshold = 0.43, fair_concern = “eligible”, fair_priority = “benefit”, fair_impact = “significant”, perf_metric_name = “balanced_acc”, fair_metric_name = “equal_opportunity”)```

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

cre_sco_obj.evaluate()
cre_sco_obj.tradeoff(output=False)
mlflow.log_artifact("/dbfs/FileStore/fairness_artifacts/cre_sco_obj.pkl")
mlflow.log_dict(cre_sco_obj.fair_conclusion, "fair_conclusion.json")
mlflow.log_dict(cre_sco_obj.get_fair_metrics_results(), "fair_metrics.json")
mlflow.log_dict(cre_sco_obj.get_perf_metrics_results(), "perf_metrics.json")
mlflow.log_dict(get_tradeoff_metrics(cre_sco_obj), "tradeoff.json")

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

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

Чтобы автоматизировать этот процесс, мы используем веб-перехватчики Databricks Model Registry.

Настройка задания и HTTP Webhook

Веб-перехватчики заданий позволяют запускать задание блокнота, когда из реестра моделей MLFlow поступает запрос на переход модели.

В нашем случае мы настраиваем веб-перехватчик HTTP, чтобы получать уведомления Slack при выполнении запроса на переход. Мы также настроили Job Webhook, который запускает задание блокнота для создания отчета о справедливости.

Переход (на основе пользовательского интерфейса)

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

Затем это должно привести к тому, что наша записная книжка проверки достоверности запустится как автоматизированное задание.

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

Блокнот проверки честности

Итак, давайте немного подробнее рассмотрим, какие оценки справедливости мы можем провести для нашей модели.

Паритет

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

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

for p in pvars:
  if all(df[df['protected_var']==p]['parity'] > 0.1) == False:
    client.set_model_version_tag(name=dict["model_name"], version=dict["version"], key=f"parity_check_{p}", value=1)
    print("Parity checks passed")
  else:
    client.set_model_version_tag(name=dict["model_name"], version=dict["version"], key=f"parity_check_{p}", value=0)

Компромисс производительности и справедливости

Какого компромисса с производительностью мы можем ожидать, если откалибруем нашу модель в соответствии с ограничениями справедливости? Это отчеты, которые мы можем предоставить нашим МСП.

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

Заключение

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