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

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

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

Затем мы определяем равномерное распределение на этой сетке:

который выглядит следующим образом:

Теперь мы можем извлечь выборку случайных чисел из равномерного распределения:

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

что дает нам следующее распределение:

Теперь определим гауссово распределение. Обратите внимание, что мы делаем sigma параметра ширины tf.Variable, а не тензором или заполнителем, потому что мы хотим определить его позже, используя подгонку максимального правдоподобия, и поэтому берем производные по нему:

Необходимо подумать, чтобы правильно определить область вывода свертки. В этом примере мы используем padding='SAME' для операции свертки, а второй тензор имеет нечетное количество интервалов. Если мы затем выберем второй тензор, равный единице в его среднем интервале и нулю в другом месте («центрированный импульс Дирака»), выходной сигнал свертки будет равен первому тензору. Поэтому мы помещаем наше гауссовское ядро ​​так, чтобы оно находилось в центре среднего бина второго тензора. Область продукта свертки тогда такая же, как область определения функции, спроецированной в первый тензор.

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

В качестве быстрой проверки операции свертки мы сворачиваем наше равномерное распределение с самим собой:

чтобы получить:

что (без учета нормализации) соответствует тому, что получается в результате полностью аналитического расчета.

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

который (для исходного значения параметра сигма) выглядит так:

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

Тогда определение отрицательной логарифмической функции правдоподобия:

На следующем этапе мы строим отрицательную логарифмическую функцию правдоподобия, для которой мы ожидаем увидеть минимум при «истинном» значении сигмы, которое использовалось для построения выборки ранее. Tensorflow может вычислить производную отрицательной логарифмической функции правдоподобия по сигме (показано стрелками на следующем графике):

Теперь, когда у нас есть отрицательная логарифмическая функция правдоподобия и ее производная, мы можем использовать ее с минимизатором. Поскольку у нас есть только один параметр для оптимизации, мы используем метод BFGS, а не стохастический градиентный спуск. У Tensorflow есть интерфейс к scipy's Minimization Toolbox:

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

И запускаем минимизацию:

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

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

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