-
Why? 为什么我们需要使用FP16半精度进行Inference
-
NVidia是如何处理float与FP16的转换的,和其他普通的框架处理精度问题之间的区别是什么?
在IEEE-754的描述中单精度浮点数(Nvidia习惯称为FP32,C++标准中的float)是4个字节,包括1位符号、8位指数和23位尾数。\(value = sign × exponent × fraction\) 浮点数的实际值\(value\),等于符号位(sign bit)乘以指数偏移值(exponent bias)再乘以分数值(fraction)。
NVidia在2002年提出了半精度浮点数FP16,只使用2个字节16位,包括1位符号、5位指数和10位尾数,动态范围是 \( 2^{-30}\sim 2^{31} \)也就是 \(10^{-9}\sim 10^9 \),精度是 \( \rm lg2^{11} \),大约3个十进制有效数字。NVidia的方案已经被IEEE-754采纳。Google的TensorFlow则比较简单粗暴,把单精度的后16位砍掉,也就是1位符号、8位指数和7位尾数。动态范围和单精度相同,精度只有 \( \rm lg 2^8 \),2个有效数字。-
NVidia的float与FP16的转换方法:
float –> FP161 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
typedef unsigned short half; half nvFloat2Half(float m) { unsigned long m2 = *(unsigned long*)(&m); // 强制把float转为unsigned long // 截取后23位尾数,右移13位,剩余10位;符号位直接右移16位; // 指数位麻烦一些,截取指数的8位先右移13位(左边多出3位不管了) // 之前是0~255表示-127~128, 调整之后变成0~31表示-15~16 // 因此要减去127-15=112(在左移10位的位置). unsigned short t = ((m2 & 0x007fffff) >> 13) | ((m2 & 0x80000000) >> 16) | (((m2 & 0x7f800000) >> 13) - (112 << 10)); if(m2 & 0x1000) t++; // 四舍五入(尾数被截掉部分的最高位为1, 则尾数剩余部分+1) half h = *(half*)(&t); // 强制转为half return h ; }
FP16 –> float
1 2 3 4 5 6 7 8 9 10 11 12 13
float nvHalf2Float(half n) { unsigned short frac = (n & 0x3ff) | 0x400; int exp = ((n & 0x7c00) >> 10) - 25; float m; if(frac == 0 && exp == 0x1f) m = INFINITY; else if (frac || exp) m = frac * pow(2, exp); else m = 0; return (n & 0x8000) ? -m : m; }
-
tensorrt FP16 C++
最新推荐文章于 2024-05-15 17:04:24 发布