Модель имеет следующие параметры:

От ввода к скрытому

  • W: матрица весов 2x2 (элементы: w_00, w_01, w_10, w_11)
  • B: вектор смещения 2x1 (элементы: b_0, b_1)

Скрыто для вывода

  • W2: матрица весов 2x2 (элементы: w2_00, w2_01, w2_10, w2_11)
  • B2: вектор смещения 2x1 (элементы: b2_0, b2_1)

Данные обучения хранятся в таблице BigQuery, где столбцы x1 и x2 имеют входные данные, а y - выходные данные, как показано ниже (имя таблицы: example_project.example_dataset.example_table).

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

Вперед пас

Сначала мы назначим случайные нормальные значения для весовых параметров W и W2 и нулевые значения параметрам смещения B и B2. Случайные значения для W и W2 могут быть сгенерированы в самом SQL. Для простоты мы сгенерируем эти значения извне и будем использовать их в запросе SQL. Внутренний подзапрос для инициализации параметров:

Обратите внимание, что таблица example_project.example_dataset.example_table уже содержит столбцы _19 _, _ 20_ и y. Параметры модели будут добавлены в качестве дополнительных столбцов в результате вышеуказанного запроса.

Затем мы вычислим скрытый слой. Обозначим скрытый слой вектором D с элементами d0 и d1. Нам потребуется выполнить матричную операцию: D = np.maximum(0, np.dot(X, W) + B), где X обозначает входной вектор (элементы x1 и x2). Эта матричная операция включает сначала умножение X на веса в W, а затем добавление вектора смещения B. Затем результат передается через нелинейную функцию активации ReLu, которая просто устанавливает отрицательные значения в 0. Эквивалентный запрос в SQL:

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

На этом преобразование слоя ввода в скрытый завершается. Теперь мы выполним преобразование скрытого слоя в выходной слой.

Сначала мы вычислим оценки для выходного слоя. Формула: scores = np.dot(D, W2) + B2. Затем мы применим функцию softmax к оценкам, чтобы получить прогнозируемую вероятность каждого класса. Эквивалентный внутренний подзапрос в SQL:

Это завершает прямой проход нейронной сети. Затем мы выполним обратное распространение, чтобы настроить параметры модели на основе сравнения прогнозируемого вывода (probs) с ожидаемым результатом (Y).

Сначала мы вычислим совокупный убыток в результате текущего прогноза. Мы будем использовать функцию потерь кросс-энтропии, чтобы вычислить потери. Сначала мы вычислим отрицательный логарифм предсказанных вероятностей правильного класса в каждом примере. Потери перекрестной энтропии - это не что иное, как среднее значение этих значений по всем экземплярам в X и Y. Натуральный логарифм - это возрастающая функция. Следовательно, интуитивно понятно определить убыток как отрицательное значение логарифма предсказанной вероятности правильного класса. Если предсказанная вероятность правильного класса высока, потери будут низкими. И наоборот, если предсказанная вероятность правильного класса низкая, потери будут высокими.

Чтобы уменьшить вероятность переобучения, мы также добавим регуляризацию L2. В общую потерю мы включим 0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W2*W2), где reg - гиперпараметр. Включение этой функции в потерю приведет к штрафу за высокие значения величин в весовых векторах.

В запросе мы также подсчитаем количество обучающих примеров (num_examples). Это будет полезно позже при вычислении средних значений. Запрос в SQL для вычисления общих потерь:

Обратное распространение

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

Напомним, что мы рассчитали баллы с помощью scores = np.dot(D, W2) + B2. Следовательно, на основе производных оценок (обозначенных dscores) мы можем вычислить градиенты скрытого слоя D и параметры модели W2 и B2. Соответствующий запрос:

Действуя аналогичным образом, мы знаем, что D = np.maximum(0, np.dot(X, W) + B). Таким образом, используя производную от D, мы можем вычислить производные от W и B. Нет смысла вычислять производную от X, поскольку она не является параметром модели и не вычисляется с использованием какого-либо параметра модели. Запрос на вычисление производных от W и B:

Наконец, мы обновим параметры модели W, B, W2 и B2, используя их соответствующие градиенты. Это можно вычислить с помощью param -= learning_rate * d_param, где learning_rate - параметр. Дополнительный коэффициент reg*weight также будет добавлен в dW и dW2, чтобы включить L2 регуляризацию в вычисление градиента. Мы также удалим временные столбцы, такие как dw_00, correct_logprobs и т. Д., Которые мы создали во внутренних подзапросах, и сохраним только обучающие данные (столбцы x1, x2 и y) и параметры модели (веса и смещения). Соответствующий запрос:

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

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

Из-за большой вложенности и сложности запроса я столкнулся с несколькими ограничениями ресурсов при попытке выполнить его в BigQuery. Стандартный диалект SQL Bigquery масштабируется лучше, чем его устаревший диалект SQL. Даже со стандартным SQL для набора данных из 100 тыс. Экземпляров сложно выполнить более 10 итераций. Из-за ограничений ресурсов мы оценим эту модель на простой границе решения, чтобы получить приличную точность за небольшое количество итераций.

Мы будем использовать простой набор данных с входными данными x1 и x2, которые выбираются из нормального распределения со средним значением 0 и дисперсией 1. Двоичный выход y просто проверяет, больше ли x1 + x2 нуля или нет. Чтобы обучаться быстрее в течение 10 итераций, мы будем использовать высокую скорость обучения 2,0 (Примечание: такое высокое значение не рекомендуется на практике, так как обучение может отличаться). Применение вышеуказанного запроса с 10 итерациями дает параметры изученной модели, как показано ниже.

Мы сохраним результат в новой таблице, используя функцию Bigquery сохранить в таблицу. Теперь мы можем проверить точность обучающих данных, выполнив только прямой проход, а затем сравнив прогнозируемые и ожидаемые результаты. Фрагмент запроса для этого находится в ссылке. Мы можем получить точность 93% за 10 итераций (точность аналогична для отдельного набора тестовых данных).

Если мы дойдем до ~ 100 итераций, мы получим точность более 99%.

Оптимизация

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

  • Мы можем создавать промежуточные таблицы и несколько SQL-запросов для выполнения большего количества итераций. Например, результат первых 10 итераций можно сохранить в промежуточной таблице. Тот же обучающий запрос теперь можно применить к этой промежуточной таблице для выполнения следующих 10 итераций. Таким образом, мы выполнили 20 итераций обучения. Это можно повторять несколько раз, чтобы выполнить большое количество итераций.
  • Вместо добавления внешних запросов на каждом шаге мы могли бы использовать функции функций, когда это возможно. Например, мы можем вычислить и scores, и probs в одном подзапросе вместо двух вложенных подзапросов.
  • В приведенном выше примере я сохранил все промежуточные столбцы до последнего внешнего запроса. Некоторые из них, например correct_logprobs, можно удалить раньше (хотя механизм SQL может выполнять эту оптимизацию автоматически).
  • Можно изучить применение пользовательских функций (UDF). Если интересно, вы можете проверить проект, в котором BigQuery UDF используется для обслуживания модели (однако обучение не выполняется с использованием SQL или UDF).

Подразумеваемое

Теперь давайте посмотрим на более глубокие последствия использования распределенного механизма SQL в контексте глубокого обучения. Одним из ограничений таких механизмов SQL хранилища, как BigQuery и Presto, является то, что обработка запросов выполняется с использованием ЦП, а не ГП. Было бы интересно проверить результаты с помощью баз данных SQL с ускорением на GPU, таких как blazingdb и mapd. Одним из простых подходов к извлечению может быть выполнение запросов и распределение данных с использованием распределенного механизма SQL и выполнение локальных вычислений с использованием базы данных с ускорением на графическом процессоре.

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

Надеюсь, вам было весело, как и мне! Пожалуйста, поделитесь своими комментариями и мыслями ниже. Буду рад ответить.