简介

最近有了突如其来的想法,如何把caffe的变得更小更快。后来翻到Nvidia开发caffe-float16,同时也看到它的论文。看完大致了解一番后,就做一下记录。

该工作的目标是,减少网络的所需的内存大小和提升网络的 inference(推理)速度。nvidia通过才用自己开发的 float16 半精度 cuda_fp16.h 数据类型,在forwardbackward propagation中代替 float 32 bits的单精度数据类型。因此,在降低网络的数据的 precision 时候,导致产生了网络 accuracy 降低和 gradient 消失无法收敛的问题。当然,我在这里并不想重复的写出文中所有的点(因为其中总体的idea在量化quantization 方面是“general” 的),仅对该工作我觉得特有的点或感兴趣的点进行简述。

Mixed Precision



caffe-float16 中的Blob重写,改为datadiff分别用不同的数据类型表示,这可以选着你所需的精确的数据类型:

//blob.hpp
protected:
Blob(Type data_type, Type diff_type)
: data_tensor_(make_shared<Tensor>(data_type)),
diff_tensor_(make_shared<Tensor>(diff_type)),
count_(0) {}

Master-Weights(F32)-->float2half的实现就是每次this->blobs_[0]->template gpu_data<Ftype>(); 中做一次类型转换:

//conv_layer.cu
const Ftype* weight = this->blobs_[0]->template gpu_data<Ftype>();
//blob.hpp
template<typename Dtype>
const Dtype* gpu_data() const {
convert_data(tp<Dtype>());
return static_cast<const Dtype*>(data_tensor_->synced_mem()->gpu_data());
} void convert_data(Type new_data_type) const {
data_tensor_->convert(new_data_type);
}
//tensor.cpp
void Tensor::convert(Type new_type) {
if (new_type == type_) {
return;
}
const shared_ptr<SyncedMemory>& current_mem = synced_mem();
shared_ptr<SyncedMemory>& new_mem = synced_arrays_->at(new_type); if (!new_mem || !new_mem->is_valid()) {
const std::size_t new_cap = even(count_) * tsize(new_type);
if (!new_mem || new_mem->size() != new_cap) {
new_mem = make_shared<SyncedMemory>(new_cap);
}
const bool data_gpu = Caffe::mode() == Caffe::GPU;
if (current_mem->head() != SyncedMemory::UNINITIALIZED) {
copy_helper(data_gpu, count_,
data_gpu ? current_mem->gpu_data() : current_mem->cpu_data(),
type_,
data_gpu ? new_mem->mutable_gpu_data() : new_mem->mutable_cpu_data(),
new_type);
}
} // we just trust its current status otherwise
type_ = new_type;
new_mem->validate();
}

神经网络的 quantization 一般可分 activation、weight 部分,当然也存在继续对不同类型 layerweghit 进行 quantization 的。而 Nvidia 则提出了 gradient 也是要 quantization 。上图是文中的整个方法的流程图,为了防止用无法拟合,采用全精度的 flaot32 来保存完整的权重信息(其他文章又叫 full precision shadow weight ),每次 forward 是都做copyround/quantization 。 这是有两个原因:

  1. 因为 gradient x learning rate < \(2^{-24}\) ,小于float16 范围,导致梯度消失无法更新。

    2.由于浮点型的特性,相加时会进行小数点对齐(即对其 exponent)。由于float16 表示的weightfloat16表示的 gradient 相差2048倍(因为float16mantissa 只有 10bits,有右移超过11bits ,即2048倍),则 gradient 变成0。float16 各个部分:



    除非指数位全是0,否则就会假定隐藏的起始位是1。因此只有10位 mantissa在内存中被显示出来,而总精度是11位。据IEEE 754的说法,虽然尾数只有10位,但是尾数精度是11位的(log10(211) ≈ 3.311 十进制数).

Weight Update,会对diff进行类型转换

//blob.hpp

// The "update" method is used for parameter blobs in a Net, which are stored
// as TBlob<float> or TBlob<double> -- hence we do not define it for
// TBlob<int> or TBlob<unsigned int>.
void Blob::Update() {
convert_diff(data_type()); // align data&diff types
shared_ptr<SyncedMemory>& data_mem = data_tensor_->mutable_synced_mem();
const shared_ptr<SyncedMemory>& diff_mem = diff_tensor_->synced_mem();
// We will perform update based on where the data is located.
switch (data_mem->head()) {
case SyncedMemory::HEAD_AT_CPU:
// perform computation on CPU
cpu_axpy(count_, data_type(), -1.F,
diff_mem->cpu_data(), data_mem->mutable_cpu_data());
break;
case SyncedMemory::HEAD_AT_GPU:
case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
gpu_axpy(count_, data_type(), -1.F,
diff_mem->gpu_data(), data_mem->mutable_gpu_data());
#else
NO_GPU;
#endif
break;
default:
LOG(FATAL) << "Syncedmem not initialized.";
}
CHECK(is_current_data_valid());
CHECK(is_current_diff_valid());
}

Lose Scaling

从上面float16 各个部分位宽可以得到,float16 可以表示的范围是\([2^{-24},2^{15}]\)(exponent表示范围是\([2^{-14},2^{15}]\),其中 mantissa10bits)。但是 activationgradient的分布却在 \([2^{-60},2^{-10}]\),在float16 中有非常大的表示范围并没用,同时导致大多数的activation gradient变成0。因此,对activation gradientforward后,backward propagation前做scaling/shift 。并且,在链式法则backward propagation 中的所有activation gradient按想用的量进行scaling



具体操作(因为activation gradientscaling,那么也要对learning rateweight_decayscaling):

#caffe train_val.prototxt
#To sfift gradients dE/dX we will scale up the loss function by constant (e.g. by 1000):
layer {
type: "SoftMaxWithLoss"
loo_weight: 1000.
}
#and adjust learning rate and weights decay accordingly base_lr: 0.00001 #(original value is 0.01, 0.01 / 1000)
weight_decay: 0.5 #(original value is 0.0005, 0.5 * 1000)

其中decay_weight公式为:

\[\omega_i \leftarrow \omega_i - \eta{{\partial E}\over \partial \omega_i} - \eta\lambda \omega_i
\]

而在softmax_loss_layer.cu的实现为:

template <typename Ftype, typename Btype>
void SoftmaxWithLossLayer<Ftype, Btype>::Backward_gpu(const vector<Blob*>& top,
const vector<bool>& propagate_down, const vector<Blob*>& bottom) {
...
float loss_weight = float(top[0]->cpu_diff<Btype>()[0]) /
get_normalizer(normalization_, valid_count);
if (this->parent_net() != NULL) {
loss_weight *= this->parent_net()->global_grad_scale();
}
caffe_gpu_scal<Btype>(prob_->count(), loss_weight , bottom_diff);
}
}

FP16 Master Weight Storage

在该论文之外,Nvidia还考虑避免每次foward都复制权重,用float16进行权重更新的问题。

  • 最核心一点就是避免gradient \(\eta{{\partial E}\over \partial \omega_i}=\eta\Delta \omega_i\)消失。

那么Nvidia提出对momentum SGD 进行改进

  1. Compute momentum \(H\) : \(H(t+1)=m*H(t)-\lambda \Delta W(t)\)
  2. Update wights with \(H\): \(W(t+1)=W(t)+H(t+1)\)

假设\(\lambda\)为常数,把式①展开:

\[H(t+1)=m*H(t)-\lambda \Delta W(t)=m*(m*H(t-1)-\lambda \Delta W(t-1))-\lambda \Delta W(t)
\]

\[=-\lambda [\Delta W(t)+m\Delta W(t-1)+m^2\Delta W(t-2)+m^k\Delta W(t-k)+...]
\]

因此新的公式:

  1. Compute momentum \(H\) : \(H(t+1)=m*H(t)-\color{#F00}{\cancel{\lambda}}\Delta W(t)\)
  2. Update wights with \(H\): \(W(t+1)=W(t)+\color{#F00}{\lambda} H(t+1)\)

这样可以避免\(H\)在\(\lambda \Delta W(t)\)消失时,momentum不断的消失。因为新的公式避免了\(\Delta W(t)\)的消失,而且momentum会不断更新。

ps:这里Nvidia解释是 Moment works as average of gradients.

Nvidia的总结

懒癌犯了- -!!

Mixed Precision Training —— caffe-float16的更多相关文章

  1. [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架

    [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架 目录 [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架 0x00 摘要 0x01 架构图 ...

  2. [源码解析] 深度学习分布式训练框架 horovod (6) --- 后台线程架构

    [源码解析] 深度学习分布式训练框架 horovod (6) --- 后台线程架构 目录 [源码解析] 深度学习分布式训练框架 horovod (6) --- 后台线程架构 0x00 摘要 0x01 ...

  3. 使用 PyTorch Lightning 将深度学习管道速度提高 10 倍

    ​  前言  本文介绍了如何使用 PyTorch Lightning 构建高效且快速的深度学习管道,主要包括有为什么优化深度学习管道很重要.使用 PyTorch Lightning 加快实验周期的六种 ...

  4. Bag of Tricks for Image Classification with Convolutional Neural Networks笔记

    以下内容摘自<Bag of Tricks for Image Classification with Convolutional Neural Networks>. 1 高效训练 1.1 ...

  5. Pytorch原生AMP支持使用方法(1.6版本)

    AMP:Automatic mixed precision,自动混合精度,可以在神经网络推理过程中,针对不同的层,采用不同的数据精度进行计算,从而实现节省显存和加快速度的目的. 在Pytorch 1. ...

  6. [源码解析] 深度学习分布式训练框架 Horovod (1) --- 基础知识

    [源码解析] 深度学习分布式训练框架 Horovod --- (1) 基础知识 目录 [源码解析] 深度学习分布式训练框架 Horovod --- (1) 基础知识 0x00 摘要 0x01 分布式并 ...

  7. 用NVIDIA A100 GPUs提高计算机视觉

    用NVIDIA A100 GPUs提高计算机视觉 Improving Computer Vision with NVIDIA A100 GPUs 在2020年英伟达GPU技术会议的主题演讲中,英伟达创 ...

  8. 基于OpenSeq2Seq的NLP与语音识别混合精度训练

    基于OpenSeq2Seq的NLP与语音识别混合精度训练 Mixed Precision Training for NLP and Speech Recognition with OpenSeq2Se ...

  9. 用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割

    用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割 Accelerating Medical Image Segmentation with NVIDIA Tensor ...

随机推荐

  1. 数据可视化基础专题(十四):pyecharts 基础(一)简单上手

    1.引言 文档位于 https://pyecharts.org/#/zh-cn/intro 示例位于 https://gallery.pyecharts.org/#/README echarts 官网 ...

  2. python之爬虫(四)之 Requests库的基本使用

    什么是Requests Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库如果你看过上篇文章关于urllib库的使用,你会发现,其 ...

  3. hihoCoder 1041 国庆出游 最详细的解题报告

    题目来源:国庆出游 解题思路(下面是大神的写的): 把题目中的序列称作S,树称作T.那么对于S中的任意节点x,x的子孙节点如果在S出现的话,那么这个子孙节点的位置是有一定要求的:x的所有子孙节点在S中 ...

  4. Python 实现邮件发送功能(初级)

    在我们日常项目中,会经常使用到邮件的发送功能,如何利用Python发送邮件也是一项必备的技能.本文主要讲述利用Python来发送邮件的一些基本操作. 本章主要包含知识点: 邮件发送原理简述即常用smt ...

  5. IOS10 window.navigator.geolocation.getCurrentPosition 无法定位问题

    在iOS 10中,苹果对webkit定位权限进行了修改,所有定位请求的页面必须是https协议的. 如果是非https网页,在http协议下通过HTML5原生定位接口会返回错误,也就是无法正常定位到用 ...

  6. 并发编程之synchronized(二)------jvm对synchronized的优化

    一.锁的粗化 看如下代码 public class Test { StringBuffer stb = new StringBuffer(); public void test1(){ //jvm的优 ...

  7. static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  8. linux $* 和$@例子

    参见ibm网站示例: https://www.ibm.com/developerworks/cn/linux/l-bash-parameters.html 示例: [ian@pinguino ~]$ ...

  9. html 转义和反转义

    public static void main(String[] args) {// String html = "<img style=\"width: 100%; hei ...

  10. xctf-web fakebook

    点join注册账号 进入view.php发现参数no存在sql注入,但是过滤了select关键字,用内联注释绕过 在users表的data字段发现了用序列化存储的用户信息 然后就卡在这里了....看了 ...