2

如何使用 tensorflow 在 GPU 上使用 fp16 进行卷积?(使用 __half 或 Eigen::half 的 python api)。

我想在 tensorflow 上用 fp16 测试一个模型,但我被卡住了。实际上,我发现 tensorflow 中的 fp16 卷积似乎将 fp32 卷积的结果转换为 fp16,这不是我需要的。

我试图给 tf.nn.conv2d 一个 fp16 格式的 fp16 输入,并给 tf.nn.conv2d 一个 fp32 格式的 fp16 输入(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卷积的结果应该不一样(在我看来)。如何使用 tensorflow 在 GPU 上使用真正的 fp16 进行卷积?(使用 __half 或 Eigen::half 的 python api)

4

2 回答 2

0

我试图弄清楚同样的事情。这是一些简单的代码,您可以使用它们来测试卷积:

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 GPU。

于 2019-10-29T14:41:03.520 回答
0

我认为您正确使用了这些操作。在您的示例中,您可以检查卷积操作是否确实具有正确的类型。

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 确实为 CPUGPU注册了 fp16 内核,因此没有理由认为正在做其他事情。我对 fp16 没有太多经验,所以我不确定零差是否“正常”,但似乎没有任何方法conv16可以使用除 fp16 卷积之外的任何东西。

于 2019-08-22T10:14:12.887 回答