Как сделать свертку с fp16(Eigen::half) на тензорном потоке

Как я могу использовать tenorflow, чтобы сделать свертку, используя fp16 на GPU? (Python API, используя __half или Eigen::half).

Я хочу протестировать модель с fp16 на тензорном потоке, но я застрял. На самом деле я обнаружил, что свертка fp16 в тензорном потоке выглядит как приведение результата свертки fp32 в fp16, а это не то, что мне нужно.

Я попытался дать tf.nn.conv2d вход fp16 в формате fp16, и дать tf.nn.conv2d вход fp16 в формате fp32 (tf.cast его в fp32), а затем tf.cast результат в fp16, и они дал точно такой же результат. Но, как мне кажется, выполнение свертки в fp16 отличается от выполнения в fp32 и последующего преобразования его в fp16, я не прав? Пожалуйста, помогите мне, спасибо.

environment:
ubuntu 16.04
tensorflow 1.9.0
cuda 9.0
Tesla V100
import tensorflow as tf
import numpy as np
import os

def conv16_32(input, kernel): # fake fp16 convolution
    input = tf.cast(input, tf.float16)
    kernel = tf.cast(kernel, tf.float16)
    input = tf.cast(input, tf.float32)
    kernel = tf.cast(kernel, tf.float32)
    out = tf.nn.conv2d(input, kernel, [1,1,1,1], padding='VALID')
    out = tf.cast(out, tf.float16)
    out = tf.cast(out, tf.float64)
    return out

def conv16(input, kernel): # real fp16 convolution
    input = tf.cast(input, tf.float16)
    kernel = tf.cast(kernel, tf.float16)
    out = tf.nn.conv2d(input, kernel, [1,1,1,1], padding='VALID')
    out = tf.cast(out, tf.float64)
    return out

x = np.random.rand(16, 32, 32, 16).astype('float64')
w = np.random.rand(3, 3, 16, 16).astype('float64')
x = tf.get_variable('input', dtype=tf.float64, initializer=x)
w = tf.get_variable('weight', dtype=tf.float64, initializer=w)

out_16 = conv16(x, w)
out_16_32 = conv16_32(x, w)

os.environ['CUDA_VISIBLE_DEVICES'] = '1'
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
sess = tf.Session(config = config)
sess.run(tf.global_variables_initializer())
sess.run(tf.local_variables_initializer())
print(sess.run(tf.reduce_max(out_16_32 - out_16)))

Вышеуказанные две функции дают одинаковый результат, скажем, конечный результат печати равен нулю.

Результат свертки fp16 и свертки fp32 не должен быть одинаковым (на мой взгляд). Как я могу использовать tenorflow, чтобы сделать свертку, используя реальный fp16 на GPU? (API Python, использующий __half или Eigen:: half)

1 ответ

Я думаю, что вы используете операции правильно. В вашем примере вы можете проверить, что операции свертки действительно имеют правильный тип.

conv2d_op_16 = out_16.op.inputs[0].op
print(conv2d_op_16.name, conv2d_op_16.type, conv2d_op_16.get_attr('T'))
# Conv2D Conv2D <dtype: 'float16'>
conv2d_op_16_32 = out_16_32.op.inputs[0].op.inputs[0].op
print(conv2d_op_16_32.name, conv2d_op_16_32.type, conv2d_op_16_32.get_attr('T'))
# Conv2D_1 Conv2D <dtype: 'float32'>

А TensorFlow регистрирует ядра для fp16 для CPU и для GPU, поэтому нет оснований думать, что делает что-то еще. У меня нет большого опыта работы с fp16, поэтому я не уверен, является ли нулевая разница "нормальной", но, похоже, нет никакого способа, которым conv16 использует что-либо, кроме свертки fp16.

Я пытаюсь понять то же самое. Вот простой код, с помощью которого вы можете тестировать свертки:

import tensorflow as tf
tf.enable_eager_execution()
input = tf.cast([[[[65519], [65519], [65519], [65519]]]], tf.float16) #BHWC
filter = tf.cast([[[[65519]], [[-65519]]]], tf.float16) #HWIO
tf.print(tf.nn.conv2d(input, filter, [1,1,1,1], "VALID"))

Это должно переполняться, если свертки выполняются в fp16, но на самом деле не переполняются в Tensorflow. В результате я получаю[[[[0][0][0]]]], которые предполагают, что свертки выполняются в fp32.

Изменить: решение состоит в том, чтобы установить переменную среды:

TF_FP16_CONV_USE_FP32_COMPUTE=0

Это дает результат [[[[inf][inf][inf]]]], предполагая, что на этот раз свертка выполняется в fp16. Похоже, для этого вам понадобится как минимум графический процессор 10x0.

Другие вопросы по тегам