BFloat16

BFloat16(BF16),是第三代英特尔® 至强® 可扩展处理器 Cooper Lake 阿里云hfg7规格族 及其后续处理器上支持的一种计算格式和指令,用于加速深度学习的训练和推理。其与其他常用数据格式的比较:

img_1.png

使用条件与方法

使用条件:云实例要求第三代英特尔® 至强® 可扩展处理器 Cooper Lake 阿里云hfg7规格族,并且需要采用 oneDNN 编译优化的 DeepRec,才能提供 BF16 指令加速,具体编译可以在 oneDNN 章节了解详情。

使用方法:由于推荐场景对模型精度的要求极其严苛,所以为了提升模型性能的同时,兼顾模型精度,用户可以通过以下方式,自由控制 BF16 计算图。

  • 步骤 1:在 tf.variable_scope(…) 之后添加 .keep_weights(dtype=tf.float32),用于保持当前权重为 FP32 类型;

  • 步骤 2:添加 tf.cast(…, dtype=tf.bfloat16) 将 inputs tensor 转换为 BF16 类型;

  • 步骤 3:添加 tf.cast(…, dtype=tf.float32) 将 outputs tensor 转换为 FP32 类型。

with tf.variable_scope(…).keep_weights(dtype=tf.float32):
  inputs_bf16 = tf.cast(inputs, dtype=tf.bfloat16)
  … // BF16 graph, FP32 weights
  outputs = tf.cast(outputs_bf16, dtype=tf.float32)

代码示例:

import tensorflow as tf

inputs = tf.ones([4, 8], tf.float32)

with tf.variable_scope('dnn', reuse=tf.AUTO_REUSE).keep_weights(dtype=tf.float32):
  # cast inputs to BF16
  inputs = tf.cast(inputs, dtype=tf.bfloat16)
  outputs = tf.layers.dense(inputs, units=8, activation=tf.nn.relu)
  outputs = tf.layers.dense(inputs, units=1, activation=None)
  # cast ouputs to FP32
  outputs = tf.cast(outputs, dtype=tf.float32)

  outputs = tf.nn.softmax(outputs)

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  print(sess.run(outputs))

特别提醒:依据调参经验,通常在多层DNN 网络中最后一层DNN网络,对模型的精度影响最大,并且计算比重较低,所以可以将最后一层DNN网络转换成 FP32 类型运行,既可以提升模型计算速度,又能保留模型精度。

为了与 BF16 量化前的模型精度保持一致,DeepRec 在 variable_scope 中提供了 keep_weights(dtype=dtypes.float32) 方法,使用该方法后,该变量域中所有变量将会以 FP32 的格式保存,可以显著减少变量的累计求和误差。并且在图中自动添加 cast 操作,将其转换为 BF16 格式进行计算。由于引入的 cast 操作会带来额外的计算开销,为了减小这部分开销,DeepRec 会自动将 cast 算子和就近的算子进行融合,提高运行速度。 DeepRec将进行下列 cast 相关算子的融合操作:

  • MatMul + Cast

  • Concat + Cast

  • Split + Cast

性能对比

使用 DeepRec Modelzoo 中模型,对比 DeepRec 开启 BF16 后对比 FP32 的性能提升。Modelzoo 中模型通过添加 --bf16 参数启用 BF16 特性。

测试机器使用阿里云 ECS 云服务器,Intel Xeon Cooper Lake CPU,规格为 ecs.hfg7.2xlarge

  • 硬件配置:

    • Intel(R) Xeon(R) Platinum 8369HC CPU @ 3.30GHz

    • CPU(s): 8

    • Socket(s): 1

    • Core(s) per socket: 4

    • Thread(s) per core: 2

    • Memory: 32G

  • 软件配置:

    • kernel: 4.18.0-348.2.1.el8_5.x86_64

    • OS: CentOS Linux release 8.5.2111

    • GCC: 8.5.0

    • Docker: 20.10.12

    • Python: 3.6.8

性能结果:

Throughput

WDL

DeepFM

DSSM

FP32

15792.49

30718.6

114436.87

FP32+BF16

22633.8

34554.67

125995.83

Speedup

1.43x

1.12x

1.10x

BF16 对模型训练的 AUC 结果影响很小,具体细节可以从 Modelzoo 中的每个模型的说明文档中找到差异。