В машинном обучении существует несколько способов реализовать комплексное решение для одного и того же типа проблемы. Тип проблемы означает контролируемую или неконтролируемую обучающую задачу. В рамках задачи обучения с учителем это может быть линейная или логистическая регрессия для определения непрерывных данных для нашей целевой метки или категории для нашей целевой метки. Существуют и другие типы обучения с учителем, но об этом мы поговорим в другой раз. В этой статье мы исследуем один из способов решения проблемы регрессии от начала до конца. Предположим, что у нас есть данные, загруженные в корзину облачного хранилища Google, и из этих данных впоследствии созданы таблицы, которые можно запрашивать через BigQuery. Теперь давайте продолжим шаг за шагом после того, как запустим нашу записную книжку Jupyter (локально или в облаке). Обратите внимание, что приведенные ниже шаги являются объяснением только одного подхода. Он написан таким образом, что его можно применять к нескольким наборам данных. Запросы и методы написаны в обобщенном виде и, возможно, служат шаблоном регрессии для наборов данных на основе больших запросов. Обобщенный шаблон блокнота можно найти здесь.
Шаг 1:
Импорт библиотек
из облака google импортировать bigquery
импортировать панд как pd
(Вернитесь к этому шагу, чтобы добавить дополнительные импорты)
Шаг 2:
export PROJECT = $(проект списка конфигураций gcloud — формат «value(core.project)»)
Приведенная выше команда извлекает идентификатор проекта из корзины облачного хранилища Google.
Теперь создайте клиент bigquery, используя приведенную ниже команду.
bq = bigquery.Client (проект = ПРОЕКТ)
Шаг 3:
Определите разделение ваших данных. При последующем хешировании формулы разделения гарантируют, что данные будут равномерно хешированы в обучающие, оценочные и тестовые наборы, если они определены и настроены правильно. Это разделение можно определить следующим образом:
Есть и другие способы сделать это, и выбор числа может быть изменен в соответствии с желаемым разделением данных. Мы вернемся к вышеупомянутым ведрам позже в этой статье. Значения в том виде, в каком они определены прямо сейчас, теоретически разделят наши данные на 80 % для обучения, 10 % для оценки и 10 % для нашего тестового набора.
Шаг 4:
Для краткости мы не будем использовать pyplot для визуализации данных. Существует несколько инструментов визуализации и руководств, которые уже объясняют, как визуализировать набор данных. Когда запрос выполняется в bigquery, возвращаемый результат может быть преобразован в фрейм данных для дальнейшей визуализации. Давайте определим вспомогательный метод для этого:
get_df_head(…) по умолчанию отображает первые 10 строк фрейма данных.
Теперь идет некоторая очистка данных и подготовка данных. Следующие несколько шагов являются одними из самых строгих шагов перед построением модели. Мы определяем последовательность операторов SQL (большие запросы). Мы делаем это таким образом, что эти утверждения возвращаются и объединяются в один консолидированный запрос, который в результате будет информировать нас о точности, с которой мы разделили наши данные.
Шаг 5:
Определите столбцы признаков и какие строки мы будем игнорировать или включать в наш набор данных:
Шаг 6:
Выберите объекты для объединения и хеширования с помощью FARM_FINGERPRINT(…)
CONCAT сочетает в себе функции, которые мы выбираем для CAST, а затем хешируем. Мы определили cleanup_query на предыдущих шагах. Приведенный выше запрос создает хеш из результата выполнения cleanup_query. Таким образом, мы можем видеть каскадное наращивание нашего запроса настройки данных монстра. Обратите внимание, что в приведенном выше примере это действительно «состояние», а не дата, как можно было бы интуитивно заметить. Это должно продемонстрировать, что значения времени, отличные от даты, также могут быть включены в вычисление хэша.
Шаг 7:
Затем мы получаем подсчет количества записей для каждого значения хеш-функции. Это соответствует определению хеширования. Здесь мы создаем строку запроса и назначаем ее first_bucket. Мы будем использовать первую корзину в соответствии с нашей логикой каскадного запроса для создания дополнительных запросов на последующих этапах.
Печать хэшей и связанных с ними счетчиков записей через get_df_head(…) выглядит следующим образом:
Шаг 8:
Мы выполняем второй уровень группировки, чтобы подготовить наши данные для равномерного разделения и применить соответствующую фильтрацию на основе значений, определенных на шаге 3.
Результат (получение заголовка фрейма данных) приведенного выше (каскадного) запроса дает:
Из-за значений формулы, которые мы выбрали на шаге 3, мы получаем около 100 сегментов для распределения данных.
Шаг 9:
Трудно понять, насколько хорошо распределены данные, исходя из того, что мы видим в предыдущем выводе. Чтобы получить более четкое представление, мы запрашиваем процент от общего количества записей в каждом сегменте, как показано ниже.
Печать заголовка результирующего фрейма данных показывает нам, как данные разделены в процентах:
Примерно 1% на индекс ведра. Неплохо.
Шаг 10:
Мы объединяем ведра пропорционально и разделяем на обучающие, оценочные и тестовые наборы. Эти запросы приведены ниже:
Вывод вышеизложенного будет выглядеть следующим образом. Если мы заметим номера корзин (индексы) для десяти напечатанных строк, мы увидим, что индексы корзин, кажется, находятся между 0 и 79, как определено предложением where и значениями, выбранными на шаге 3.
Оценочный запрос ниже. Результат будет выглядеть аналогично с индексами корзины в диапазоне от 80 до 89, как определено нашими значениями на шаге 3 и предложением where запроса ниже. Это опущено для краткости
Операции ≥ и ‹ в предложении WHERE определяют разбиение. Тестовый запрос ниже с выходными значениями, соответствующими оставшимся индексам корзины:
Шаг 11:
Union all объединяет результаты трех запросов на предыдущем шаге. Мы делаем это, чтобы иметь возможность сортировать dataset_id в запросе, после чего получаем процент данных, соответствующих обучающему, оценочному и тестовому наборам соответственно. Здесь наш раскол наиболее четко выражен.
Вывод приведенного выше вложенного запроса должен выглядеть примерно так:
Мы должны получить более или менее желаемое разделение, используя значения на шаге 3. Если это не так, нам придется настроить его, чтобы получить желаемое разделение 80–10–10 (более или менее). Шаги с 6 по 11 должны гарантировать, что выбранные нами параметры на шаге 3 приводят к точному разбиению.
Шаг 12:
Следующим шагом является выборка данных для использования в обучении. Причина, по которой это следует сделать, состоит в том, чтобы проверить наши модели на меньшем подмножестве данных. Отладка и настройка по мере необходимости. Ниже приведен один из способов сделать это:
Множитель подвыборки будет меняться в зависимости от общего количества строк в наборе данных. Обратите внимание, как вызывается метод create_data_split_sample(…) и какие параметры передаются для обучения, оценки (валидации) и тестирования. Для примера, который был доступен в облаке Google, оператор печати дал:
Если мы подумаем об этом пропорционально, нам, очевидно, потребуется наибольшее количество образцов во время обучения. Математическое объяснение того, почему приведенный выше запрос работает:
Основываясь на нашем выборе значения на шаге 3, оператор ABS(MOD…) будет выглядеть следующим образом, когда он разрешится: ABS(MOD(hash_values, 1000 * 100)) поэтому хэш-значения, которые мы сгенерировали с помощью FARM_FINGERPRINT, будут по модулю 100 КБ согласно приведенному выше заявление. Итак, если, например, у нас всего 10 миллионов записей, 100k X 100 равняется 10M. 100k / 100 — это 1000. Нам нужна операция модуля, чтобы разделить на 100 сегментов. Вот почему 100 так важно здесь. Итак, если мы выберем 1000 как кратное и сделаем это -> ABS(MOD(hash_values, 1000 * 100)) ›= 0 и ABS(MOD(hash_values, 1000 * 100)) ‹ 80, мы выберем примерно 1 из каждых 1000 записи. Это также будет продолжать масштабироваться за пределы 10 миллионов записей. Если мы приблизимся к 100 млн, мы просто увеличим множитель, чтобы, возможно, выбрать одну из каждых 10000 записей для обучающего набора.
ABS(MOD(hash_values, 1000 * 100)) ›= 80 и ABS(MOD(hash_values, 1000 * 100)) ‹ 90 даст нам наш оценочный набор.
Наконец, ABS(MOD(hash_values, 1000 * 100)) ›= 90 и ABS(MOD(hash_values, 1000 * 100)) ‹ 100 для тестового набора.
Общее количество записей в образце набора данных, используемом для этой статьи, составляет 33 011 359. Математика примерно работает. Запрос, построенный в совокупности после выборки, должен выглядеть примерно так:
Для обучения:
Для оценки:
Запрос будет таким же, за исключением предложения WHERE в конце, которое будет выглядеть так:
ГДЕ ABS(MOD(значения_хэша, 1000*100)) ›= 80 и ABS(MOD(значения_хэша, 1000*100)) ‹ 90
И для тестового набора
ГДЕ ABS(MOD(значения_хэша, 1000*100)) ›= 90 и ABS(MOD(значения_хэша, 1000*100)) ‹ 100
Шаг 13:
Любой другой шаг предварительной обработки, такой как уменьшение размерности, встраивание и т. д., может быть выполнен на уровне кадра данных. Чистый способ сделать это — определить метод, выполнить все операции фильтрации в этом методе и вернуть фрейм данных.
Затем мы вызываем вышеуказанный метод для предварительной обработки наших обучающих, оценочных и тестовых наборов.
train_df = предварительная обработка (train_df)
eval_df = предварительная обработка (eval_df)
test_df = предварительная обработка (test_df)
train_df, eval_df и test_df были определены на шаге 12.
Шаг 14:
Вызовите train_df.head(), train_df.tail() и train_df.describe(), чтобы сделать базовую проверку правильности предварительной обработки. На этом этапе также было бы хорошо повторно визуализировать обработанные данные с помощью pyplot или любого другого инструмента визуализации.
Шаг 15:
В окончательных версиях мы хотим читать из файлов, а не из фреймов данных Pandas. Итак, мы записываем кадры данных Pandas в виде файлов csv. Использование CSV-файлов дает нам преимущество перетасовки во время чтения. Это важно для распределенного обучения, поскольку некоторые рабочие процессы могут быть медленнее других, а перетасовка данных помогает предотвратить назначение одних и тех же данных медленным рабочим процессам.
Шаг 16:
Мы запускаем несколько команд, чтобы убедиться, что файлы .csv были сгенерированы правильно:
туалет -l *.csv
Пример вывода для приведенной выше команды:
2000 eval.csv
1000 тестов.csv
10000 train.csv
Кроме того, мы печатаем несколько строк в этих файлах с помощью команд head и tail:
голова *.csv
хвост *.csv
Это напечатает первые 10 или около того строк всех файлов csv. Это дает нам всю необходимую уверенность в правильности данных в указанных файлах.
Шаг 17:
Далее мы пишем несколько вспомогательных методов. Первый из них — загрузка данных в память.
Шаг 18:
Затем мы определяем метод create_input_layers() для глубокой нейронной сети (DNN), которую мы впоследствии построим:
Обратите внимание, как входные данные определяются на основе типа данных (dtype)
Шаг 19:
Определите вспомогательный метод, который создает столбцы категориальных признаков с учетом имени столбца и возможных значений для столбца.
Шаг 20:
Определите вспомогательный метод, который создает словарь столбцов функций для входных данных. Столбцы функций могут быть числовыми или категориальными.
Шаг 21:
Определите помощника, который использует tf.keras.layers.Dense(…) для создания скрытых слоев, а также выходного слоя нашей глубокой нейронной сети с соответствующим количеством единиц на каждом слое. Это меняется от проблемы к проблеме
Шаг 22:
Для регрессии метрика, используемая для измерения ошибки отклонения от предоставленных значений меток, представляет собой измерение среднеквадратичной ошибки. Вспомогательный метод будет выглядеть так:
Шаг 23:
Мы вызываем все помощники, которые мы тщательно определили в предыдущих шагах, в упорядоченном порядке, используя метод, который мы определили как build_dnn_model().
Чтобы визуализировать глубокую нейронную сеть:
DenseFeatures и Dense отличаются в библиотеке keras.layers. Мы используем DenseFeatures для определения нашего входного векторного слоя, передавая подготовленные входные объекты.
Шаг 24:
Теперь, когда мы построили модель. Нам нужно обучить и оценить только что созданную модель. Мы вызываем созданный ранее помощник load_dataset(…) и передаем TRAIN_BATCH_SIZE в режиме обучения и EVAL_BATCH_SIZE для размера пакета при оценке. Значения этих констант приведены для иллюстративных целей и будут меняться в зависимости от данных.
Обратные вызовы Tensorboard могут быть определены для визуализации обучения на инструменте Tensorboard.
Шаг 25:
Далее следует визуализировать кривую потерь с помощью pyplot или любого другого инструмента визуализации.
Вывод выглядит примерно так:
Шаг 26:
Наконец, мы сохраняем модель, когда видим, что кривые потерь согласуются для обучающих и оценочных наборов следующим образом:
Когда мы используем сохраненную модель и запускаем тестовый набор, мы должны наблюдать за производительностью, аналогичной производительности во время обучения, чтобы подтвердить, что мы не переобучаем модель или наблюдаем какие-либо другие неточности.
На этом завершается сквозная иллюстрация создания регрессионной модели с использованием глубокой нейронной сети. Ссылку на этот обобщенный блокнот можно найти здесь, как упоминалось в начале статьи. Имейте в виду, что записную книжку нельзя запустить как есть, поскольку она является шаблоном. Это обобщение было сформулировано на основе сквозной демонстрации набора данных о рождении в рамках курса сквозного машинного обучения Google на Coursera.