浮点量化逼近策略
目錄
知識直通車
定點法(Fixed Point Approximation)
動態定點法(Dynamic Fixed Point Approximation)
動態定點法代碼
迷你浮點法(Minifloat Approximation)
迷你浮點數量化代碼
乘法變移位法(Multiplier-free arithmetic)
?乘法變移位法量化代碼
知識直通車
參考github鏈接:https://github.com/Ewenwan/MVision/tree/master/CNN/Deep_Compression/quantization/Ristretto
參考csdn鏈接:https://blog.csdn.net/yiran103/article/details/80336425
定點法(Fixed Point Approximation)
所謂定點數和浮點數, 是指在計算機中一個數的小數點的位置是固定的還是浮動的: 如果一個數中小數點的位置是固定的,則為定點數; 如果一個數中小數點的位置是浮動的,則為浮點數。 一般來說,定點格式可表示的數值的范圍有限,但要求的處理硬件比較簡單。 而浮點格式可表示的數值的范圍很大,但要求的處理硬件比較復雜。IL.FL 固定整數位二進制長度和小數位二進制長度。 最大表示數: Xmax = 2^(IL-1) - 2^(-FL)32浮點數----->8位定點(Q4.4)----->16位定點 ( Q8.8 Q9.7)例如 0 1 1 0 1 1 0 1 , FL = 2符號位: 0尾數部分全部作為整數位: 1 1 0 1 1 0 1 B = 109D 小數部分最后兩位,所以整體需要除以2^(2)則表示的數為: R = (-1)^0 * 109 * 2^(-2) = 27.25浮點數 ff 得到量化的數:放大/縮小: sff = ff * 2^(fl) 取整: round(sff)動態定點法(Dynamic Fixed Point Approximation)
CNN的不同部分具有顯著的動態范圍, 在大的層中,輸出是數以千計的積累的結果,因此網絡參數比層輸出小得多。 定點只具有有限的能力來覆蓋寬動態范圍。 使用B位來表示,其中一位符號位s, fl是分數長度(小數長度),其余為整數位, 除去符號位s,后面的都是尾數,每個尾數位數字為xin = (-1)^s * 2^(-fl)*sum(2^i * xi) , 0 =< i <= B-2 第一項確定符號, 第二項確定分數比例, 第三項確定總值大小(所有尾數對應的十進制的值)。由于網絡中的中間值具有不同的范圍,所以希望將定點數值分組為具有常數f1(不同小數長度)的組中。 所以分配給小數部分的比特數在 同一組內是恒定的,但與其他組相比是不同的。這里每個網絡層分為三組:一個用于層輸入,一個用于權重,一個用于層輸出。 這可以更好地覆蓋層激活和權重的動態范圍,因為權重通常特別小,層的輸入輸出通常比較大(上一層各個神經元累計)。 上圖為4個數 屬于 兩個不同組 的 動態定點數的例子。 注意第二組的 分數長度 是負數。 所以第二組的分數長度是-1,整數長度是9,位寬是8。 第一組的分數長度為2,整數長度為6,位寬為8.動態定點法代碼
template <typename Dtype> void BaseRistrettoLayer<Dtype>::Trim2FixedPoint_cpu_featuremap(Dtype* data, const int cnt,const int bit_width, const int rounding, int fl) {for (int index = 0; index < cnt; ++index) {// Saturate dataDtype max_data = (pow(2, bit_width - 1) - 1);Dtype min_data = -pow(2, bit_width - 1);data[index] /= pow(2, -fl);// clip (min,max)data[index] = std::max(std::min(data[index], max_data), min_data);data[index] = round(data[index]);data[index] *= pow(2, -fl);} }?
迷你浮點法(Minifloat Approximation)
不清楚浮點數二進制表示的可參考:https://blog.csdn.net/qq_20880415/article/details/99688542
IEEE-754 32位浮點數:struct MYFLOAT{bool bSign : 1; // S 符號,表示正負,1位char cExponent : 8; // E 指數,8位,存儲的時候,比實際的多 127unsigned long ulMantissa : 23; // M 尾數,23位};// B = (-1)^S * 2^E * M// 不過指數部分在存儲的時候回 加上127(使用移位存儲, 正負表達的范圍較平均)// 所以 指數部分按二進制數轉到10進制數后需要減去 127, E = E' - 127bSign --- cExponent --- ulMantissa符號位 --- 指數位 --- 尾數位迷你浮點數 16bit 5位指數部分 10位位數部分(精度, 1/(2^(10)) = 0.0009765, 精確到小數點后3位 ) 由于神經網絡的訓練是以浮點的方式完成的, 所以將這些模型壓縮成比特寬度減少的浮點數是一種直觀的方法。為了壓縮網絡并減少計算和存儲需求,Ristretto可以用比IEEE-754標準少得多的位表示浮點數。我們在16bit、8bit甚至更小的數字上都遵循這個標準,但是我們的格式在一些細節上有所不同。也就是說,根據分配給指數的比特數降低指數偏差:bias = 2^(exp_bits?1) ? 1, 這里exp_bits提供分配給指數的位數。 例如8位指數,為了表示正負,使用移位存儲,存儲的數據為 原數據+127 , 基數 bais = 2^(8-1) - 1 =127 與IEEE標準的另一個區別是我們不支持非規范化的數字,INF和NaN。INF由飽和數字代替, 非規格化數字NaN 由0代替。最后,分配給指數和尾數部分的位數不遵循特定的規則。 更確切地說,Ristretto選擇指數位,以避免發生飽和。迷你浮點數量化代碼
typedef union {float d;struct {unsigned int mantisa : 23;unsigned int exponent : 8;unsigned int sign : 1;} parts; } float_cast;template <typename Dtype> void BaseRistrettoLayer<Dtype>::Trim2MiniFloat_cpu(Dtype* data, const int cnt, const int bw_mant, const int bw_exp, const int rounding) {for (int index = 0; index < cnt; ++index) {int bias_out = pow(2, bw_exp - 1) - 1;float_cast d2;// This casts the input to single precisiond2.d = (float)data[index];int exponent=d2.parts.exponent - 127 + bias_out;double mantisa = d2.parts.mantisa;// Special case: input is zero or denormalized numberif (d2.parts.exponent == 0) {data[index] = 0;return;}// Special case: denormalized number as outputif (exponent < 0) {data[index] = 0;return;}// Saturation: input float is larger than maximum output floatint max_exp = pow(2, bw_exp) - 1;int max_mant = pow(2, bw_mant) - 1;if (exponent > max_exp) {exponent = max_exp;mantisa = max_mant;} else {// Convert mantissa from long format to short one. Cut off LSBs.//相當于動態定點中的乘以標bw_mant后rounddouble tmp = mantisa / pow(2, 23 - bw_mant);switch (rounding) {case QuantizationParameter_Rounding_NEAREST:mantisa = round(tmp);break;case QuantizationParameter_Rounding_STOCHASTIC:mantisa = floor(tmp + RandUniform_cpu());break;default:break;}}// Assemble resultdata[index] = pow(-1, d2.parts.sign) * ((mantisa + pow(2, bw_mant)) /pow(2, bw_mant)) * pow(2, exponent - bias_out);} }?
乘法變移位法(Multiplier-free arithmetic)
利用這種近似策略,乘法由位移代替。卷積運算由加法和乘法組成,其中乘法器需要更大的芯片面積。 這促使以前的研究通過使用整數冪權重來消除所有的乘法器。這些權重可以被認為是具有零尾數位的minifloat數值。 權重和層激活之間的乘法變成了位移。冪的兩個參數可以寫成如下: n=(?1)^s * 2^exp 這里exp是一個整數,對于網絡參數通常是負的。由于接近于零的小參數對網絡輸出沒有太大影響, 所以可以忽略很小的指數。因此,可能的指數值可以顯著減少。對于AlexNet,我們用{?8,?1}范圍的值取得了很好的效果。 在這種近似模式下,激活是動態定點格式?乘法變移位法量化代碼
template <typename Dtype> void BaseRistrettoLayer<Dtype>::Trim2IntegerPowerOf2_cpu(Dtype* data,const int cnt, const int min_exp, const int max_exp, const int rounding) {for (int index = 0; index < cnt; ++index) {float exponent = log2f((float)fabs(data[index]));int sign = data[index] >= 0 ? 1 : -1;switch (rounding) {case QuantizationParameter_Rounding_NEAREST:exponent = round(exponent);break;case QuantizationParameter_Rounding_STOCHASTIC:exponent = floorf(exponent + RandUniform_cpu());break;default:break;}exponent = std::max(std::min(exponent, (float)max_exp), (float)min_exp);data[index] = sign * pow(2, exponent);} }?
總結
- 上一篇: 卷积Strassen算法
- 下一篇: 移位存储详解