Эксперименты со встроенными возможностями машинного обучения Google BigQuery уже некоторое время были в моем личном списке дел, поэтому я наконец решил сделать решительный шаг. При этом я решил взять с собой всех остальных на случай, если вы заинтересуетесь — но еще не начали — похожее путешествие.
Постановка проблемы
Чтобы найти себе работу, я решил заняться созданием модели, которая может помочь предсказать риск сердечно-сосудистых заболеваний с учетом ряда характеристик, таких как возраст, рост, вес, артериальное давление, курение да/нет и так далее. . Это полезный тестовый прогон, потому что у меня уже есть разумное интуитивное представление о факторах риска здесь, поэтому, когда я запущу свою модель и запущу ее в качестве конечной точки прогнозирования, я смогу увеличить эти «известные» факторы риска и, надеюсь, увидеть в результате правильный прогноз. .
Набор данных
Чтобы найти подходящий набор данных для работы, я обратился к любимому всеми репозиторию данных (и отличным учебникам по машинному обучению!) Kaggle, где нашел этот набор данных:
Этот набор данных великолепен, потому что все уже числовое (обучение машинному обучению любит числа, а не строки), но я все же хотел поместить его в блокнот, чтобы рассмотреть данные более подробно. Именно здесь Google Colab (https://colab.research.google.com/) является фанатским ресурсом. В считанные минуты вы можете запустить блокнот Jupyter с предустановленными различными полезными пакетами машинного обучения.
Поэтому я загрузил свои данные в новый блокнот Colab и начал исследовать.
Обработка данных
Первое, что я сделал, это изменил столбец возраста так, чтобы он указывался в годах, а не в днях (мне так было проще работать), а затем я выполнил описание, чтобы получить сводку данных.
Сразу же я увидел некоторые проблемы с данными. Некоторые функции просто казались немного неправильными. Например, высота 55 см, безусловно, является ошибкой данных, как и артериальное давление 11500 (хотя я потратил некоторое время, пытаясь отладить развертывание Cloud Run, и я почти уверен, что мое кровяное давление где-то там поднялось). …).
Я решил графически взглянуть на распределение значений некоторых из этих функций, создав несколько гистограмм.
Возраст — это не так уж плохо, но остальные, как вы можете видеть, имеют выбросы — в некоторых случаях на обоих концах. Выбросы не подходят для большинства моделей машинного обучения, поскольку они могут негативно повлиять на статистический анализ и, следовательно, на точность вашей модели. Поэтому я решил удалить их из определенных функций в наборе данных. Для этого я использую «метод Z-оценки», популярный способ определения вероятности появления значения в пределах нормального распределения значений:
После того, как я применил это к своим функциям, я вижу по гистограмме(ам), что распределения выглядят намного лучше (честно говоря, распределение высокого/низкого артериального давления по-прежнему выглядит не очень хорошо, но я оставлю его). для продолжения демонстрации)
Быстрая проверка сводной статистики показывает, что большинство функций теперь выглядят намного лучше (просто пока не обращайте внимания на кровяное давление, я понял, что мне, вероятно, нужно больше работать над этим :D)
Я думаю, теперь мы готовы перенести наш «перепутанный» набор данных в BigQuery, чтобы превратить его в модель машинного обучения!
Построение модели в BigQuery
Первое, что нужно сделать, это перенести данные из нашего окончательного CSV-файла записной книжки в таблицу BigQuery. Для этого вы можете просто использовать библиотеку colab python для загрузки CSV, а затем создать таблицу BigQuery на основе данных. Сначала я создал новый набор данных с именем сердечно-сосудистое заболевание, а затем создал новую таблицу с именем raw_data_wrangled на основе загруженного CSV-файла:
Прежде чем я создам модель, мне нужно разделить свои данные на 3 набора, которые я буду использовать на разных этапах своего пути к машинному обучению. Мне понадобятся сами обучающие данные, затем мне понадобятся некоторые данные, отложенные для оценки модели, и, наконец, набор данных для проверки прогнозов. В Интернете есть разные идеи относительно правильного соотношения этого разделения, но я собираюсь работать с 80% тренировочными данными, 10% оценкой и 10% тестом. Я вставлю SQL BigQuery ниже, а затем расскажу, что я делаю:
# get 10% of the table data for validation CREATE OR REPLACE TABLE cardiovascular_disease.validation_data AS SELECT * FROM cardiovascular_disease.raw_data_wrangled WHERE rand() < 0.1; # get 10% of the table data for test while avoiding rows already in validation data CREATE OR REPLACE TABLE cardiovascular_disease.test_data AS SELECT * FROM cardiovascular_disease.raw_data_wrangled WHERE rand() < 0.1 AND id NOT IN (SELECT id FROM `gcp-xxx-494d.cardiovascular_disease.validation_data`); # remove other data sets from the main table DELETE FROM `gcp-xxx-494d.cardiovascular_disease.raw_data_wrangled` WHERE id IN (SELECT id from `gcp-xxx-494d.cardiovascular_disease.test_data`); DELETE FROM `gcp-xxx-494d.cardiovascular_disease.raw_data_wrangled` WHERE id IN (SELECT id from `gcp-xxx-494d.cardiovascular_disease.validation_data`);
Итак, я создал 2 новые таблицы, validation_data и test_data. Каждый создается путем случайного выбора 10% существующего набора данных (хотя с тех пор я понял, что 10% тестовых данных — это не совсем тот процент, поскольку я выбрал его из «оставшихся» 90% строк в основной таблице. Хотя для моих целей сойдет).
Наконец, я удалил строки из основной таблицы, которые теперь существуют в моих таблицах проверки и тестирования. Хорошо, время создать мою модель ML!
Теперь выбор модели в этом случае представляет собой модель бинарной классификации. Эта модель позволяет нам прогнозировать бинарное значение, используя наши доступные функции (имеется в виду значение типа «да/нет», «вкл/выкл». Это соответствует нашему вопросу о том, есть ли у пациента риск сердечно-сосудистых заболеваний — да/нет). Ниже приведен мой оператор создания модели с использованием функции CREATE MODEL:
# Train model CREATE OR REPLACE MODEL `cardiovascular_disease.binary_model_01` OPTIONS (model_type='logistic_reg', labels=['risk']) AS SELECT age,--weight,height,gender,ap_hi,ap_lo,cholesterol,gluc,smoke,alco,active, cardio AS risk FROM `cardiovascular_disease.raw_data_wrangled`;
Я назвал модель xxx_’01’, потому что предполагаю пройти через несколько итераций до конечного продукта. Тип модели — logistic_reg, так как это позволит нам прогнозировать бинарный результат, а мое прогнозируемое значение — «риск» (который в наших наборах данных помечен как кардио 0/1 (0 означает, что у пациента не было сердечно-сосудистых заболеваний, 1 означает, что они сделали). Хотя у меня есть несколько функций для работы, я включаю только возраст на данный момент, потому что я хочу постепенно вводить функции, чтобы увидеть, какое влияние они оказывают.
Итак, я прогнал свою модель через обучающую итерацию, давайте посмотрим на результат!
Хорошо, там кое-что происходит, но меня больше всего интересует кривая ROC (рабочая характеристика приемника). Это 0,6 из диапазона от 0 до 1. Более высокие числа здесь хороши, потому что они указывают на то, что мы получаем большее количество правильных совпадений с данными при обучении. ROC 0,6 не так уж и хорош. Легче добавить больше контекста, если вы также посмотрите на матрицу путаницы:
Таким образом, мы предсказали 100% наших результатов для пациентов с сердечно-сосудистыми заболеваниями, но мы также ошибочно идентифицировали всех пациентов, у которых не было этого заболевания, как имеющих его. Хм. Это может иметь какое-то отношение к порогу, выше которого мы считаем значение положительным (1). Если вы посмотрите на предыдущий снимок экрана, вы увидите ползунок, который показывает порог положительного класса как 0,4602. Вы также можете видеть, что отзыв равен 1 (100%). Таким образом, наш отзыв является фантастическим, но поскольку все, что имеет достоверность 0,46 или выше, считается положительным, мы в основном отмечаем все положительное.
BigQuery позволяет настроить порог положительного класса, чтобы увидеть его влияние на матрицу путаницы. Я поднял порог до 0,5019, и теперь я получаю правильные предсказания 0 и 1 (ура!), но моя память немного снизилась, так как я также получаю ложные предсказания 0 и 1.
Мои следующие шаги после этого заключались в том, чтобы продолжать создавать новые версии и добавлять новые функции в мою модель, обучать и проверять. В итоге я остановился на ревизии 5 со всеми своими функциями (некоторые из них придали моему ROC больший «удар», чем другие, и это часть понимания того, какие функции более важны для вашей модели, чем другие).
Так где же я оказался? Давайте посмотрим на окончательный вывод модели:
Чуть меньше 0,8! Совсем неплохо. Ни в коем случае не идеально, но приличная первая трещина. Если мы установим положительный порог равным 0,5, давайте проверим нашу матрицу путаницы:
Хорошо, теперь мы выглядим лучше. Более 70% правильного предсказания значений 0 и 1. Если бы я собирался запустить эту модель в производство, я бы потратил гораздо больше времени на настройку, проверку, обработку данных и т. д., но сейчас я перехожу к оценке.
# evaluate SELECT roc_auc, CASE WHEN roc_auc > .9 THEN 'excellent' WHEN roc_auc > .8 THEN 'very_good' WHEN roc_auc > .7 THEN 'good' WHEN roc_auc > .6 THEN 'fair' WHEN roc_auc > .5 THEN 'improve_model' ELSE 'poor' END AS model_quality FROM ML.EVALUATE(MODEL `cardiovascular_disease.binary_model_05`, (SELECT age,weight,height,gender,ap_hi,ap_lo,cholesterol,gluc,smoke,alco,active, cardio AS risk FROM `cardiovascular_disease.validation_data`));
Здесь я использую функцию ML.EVALUATE в BigQuery для вычисления значения кривой ROC, но на этот раз для нашей таблицы validation_data. Я также добавил оператор case, чтобы преобразовать числовое значение в (слегка субъективное) текстовое указание того, насколько хорошо работает моя модель.
Хорошо! Наша модель хороша. Не отлично, но хорошо. Я умею жить с добром. Добрый воскресный день - это прекрасно :).
В качестве последнего шага я запущу функцию ML.PREDICT для своих 10% тестовых данных, которые я выделил в начале процесса. Я заключаю прогноз в SELECT COUNT(*), чтобы получить количество записей, в которых прогнозируемое значение совпадает с фактическим значением (здесь важно рассмотреть, как использовать функцию ML.PREDICT, поскольку важны имена столбцов). , например, чтобы отобразить прогнозируемое значение, вы должны использовать predicted_‹label›, где ‹label› — это значение, которое вы хотите предсказать, в данном случае «риск»).
# test out of 4944 rows, predicted 72.9975728% SELECT COUNT(*) FROM ( SELECT predicted_risk, risk as actual_risk FROM ML.PREDICT(MODEL `cardiovascular_disease.binary_model_05`, ( SELECT age, weight, height, gender, ap_hi, ap_lo, cholesterol,gluc,smoke,alco,active, cardio AS risk FROM `cardiovascular_disease.test_data` ))) WHERE predicted_risk = actual_risk;
Итак, из 4944 записей в моей тестовой таблице 3609 были предсказаны правильно, соотношение 72,9%. Опять же, я бы не стал использовать эту модель в производстве, но для демонстрации я думаю, что это не так уж и плохо.
Надеюсь, вы нашли это не менее интересным, чем я. Я думаю, что здесь есть много доказательств того, что BigQuery — полезная рабочая среда ML, и я обязательно буду изучать ее дальше наряду с конкурирующими инструментальными средствами, такими как AWS Sagemaker, Azure ML Studio и так далее.
Я буду писать следующую статью о том, как взять мою лучшую модель, докеризировать ее и запустить в качестве конечной точки прогнозирования через API, так что, пожалуйста, следите за этим и подпишитесь, чтобы не не пропустите! :)
До тех пор!