如何在张量流上使用fp16(Eigen :: half)进行卷积

时间:2019-08-21 12:01:17

标签: python c++ tensorflow half-precision-float

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

我想在tensorflow上使用fp16测试模型,但遇到问题。实际上,我发现张量流中的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卷积的结果不应该相同(在我看来)。 如何在GPU上使用真实的fp16使用Tensorflow进行卷积? (使用__half或Eigen :: half的python API)

2 个答案:

答案 0 :(得分: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确实为fp16 for CPUfor GPU注册了内核,因此没有理由认为自己在做其他事情。我没有fp16的丰富经验,所以我不确定零差是否为“正常”,但是conv16似乎没有任何其他方式可以使用fp16卷积。

答案 1 :(得分: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"))

如果在f16中完成卷积,则应该溢出,但实际上在Tensorflow中不会溢出。我得到的结果是[[[[[0] [0] [0]]]]],表明卷积是在f32中执行的。