Ответ 1
TL; DR: Реализация tf.nn.conv2d()
написана на С++, которая вызывает оптимизированный код, используя либо Eigen (on CPU) или библиотеки cuDNN (на GPU). Вы можете найти реализацию здесь.
Цепочки функций, которые вы упомянули в вопросе (от tf.nn.conv2d()
вниз), являются функциями Python для построения графика TensorFlow, но они не вызывают реализацию. Напомним, что в TensorFlow вы сначала построили символический граф, а затем выполните его.
Реализация tf.nn.conv2d()
выполняется только тогда, когда вы вызываете Session.run()
, передавая Tensor
, значение которого зависит от результата некоторой свертки. Например:
input = tf.placeholder(tf.float32)
filter = tf.Variable(tf.truncated_normal([5, 5, 3, 32], stddev=0.1)
conv = tf.nn.conv2d(input, filter, strides=[1, 1, 1, 1], padding='SAME')
result = sess.run(conv, feed_dict={input: ...}) # <== Execution happens here.
Вызов sess.run(...)
сообщает TensorFlow о запуске всех ops, которые были выбраны для вычисления значения conv
, включая саму свертку. Путь отсюда к реализации несколько сложный, но проходит следующие шаги:
-
sess.run()
вызывает внутренний ресурс TensorFlow для получения значенияconv
. - Бэкэнд выровняет график вычислений, чтобы определить, какие узлы должны быть выполнены, и размещает узлы на соответствующих устройствах (CPU или GPU).
- Каждому устройству предлагается выполнить его подграф, используя executor.
- Исполнитель в конечном итоге вызывает
tensorflow::OpKernel
, который соответствует оператору свертки, вызывая его методCompute()
.
"Conv2D"
OpKernel реализован здесь, а метод Compute()
здесь. Поскольку эта операционная система является критически важной для многих рабочих нагрузок, реализация довольно сложная, но основная идея заключается в том, что вычисление выгружается либо в библиотеку Eigen Tensor (если выполняется на процессоре), либо в оптимизированную реализацию GPU cuDNN.