超参

超参 - 汇总

  • batch size,见..
  • 步长
  • 多机多卡 - 见deep-learning/优化 - 分布式集群

难点

• 超参数范围广泛:

随着模型复杂度的提升,模型中可供调节的超参数数量及数值范围也在增多。例如,在CIFAR-10数据集上训练的ResNet模型有16个可调的超参数[8],当多数超参数的取值为连续域的情况下,如此少量的超参数仍然可能造成组合爆炸。因此,最近也出现了以谷歌的Vizier为代表的系统,采用优化的搜索及学习算法为模型自动适配合适的超参数值的集合。

如何选择合适的超参

此外,由于超参较多,而每一个超参分布范围较广,使得超参调优的耗时较长,特别是针对ImageNet这种超大数据集的情况。前文提过,CIFAR-10数据集上训练的ResNet模型就有16个超参。

随着项目进展,团队还引入了很多新的关键技术,如后面将会提到的LARS算法、分层同步算法、梯度融合策略,Batch Norm替换等都会增加模型超参数量,如何在可接受的时间内寻找到较优解,是机智团队面临的第三个重大挑战。

参数步长由粗到细

调优参数值先以较大步长进行划分,可以减少参数组合数量,当确定大的最优范围之后再逐渐细化调整,例如在调整学习速率时,采取较大步长测试发现:学习率lr较大时,收敛速度前期快、后期平缓,lr较小时,前期平缓、后期较快,根据这个规律继续做细微调整,最终得到多个不同区间的最佳学习速率;

低精度调参

在低精度训练过程中,遇到的最大的一个问题就是精度丢失的问题,通过分析相关数据,放大低精度表示边缘数值,保证参数的有效性是回归高精度计算的重要方法;

初始化数据的调参

随着网络层数的增多,由于激活函数的非线性,初始化参数使得模型变得不容易收敛,可以像VGGNet那样通过首先训练一个浅层的网络,再通过浅层网络的参数递进初始化深层网络参数,也可以根据输入输出通道数的范围来初始化初始值,一般以输入通道数较为常见;对于全连接网络层则采用高斯分布即可;对于shortcut的batch norm,参数gamma初始化为零。

模型参数

• 参数量大:

深度神经网络由于层次很多,参数量往往很大。ResNet-50有2500万参数量,AlexNet有6200万的参数量,而VGG-16参数量则达到1.38亿,有的语言模型参数量甚至超过10个亿[5]。

分布式集群

为了弥补性能的差距,一些研究人员已经把工作重点放在探索如何有效利用大规模并行处理器训练深度神经网络上面。

大多数扩展ImageNet训练的成功方法,都使用了同步随机梯度下降(SGD)。但是,为了扩展同步SGD,必须增加每次迭代中使用的批量的大小。

多机多卡扩展性差

深度训练通常采用数据并行模式,数据并行模式将样本分配给不同的GPU进行训练。相比模型并行,数据并行简单且可扩展,成为目前主流的分布式训练方式。

分布式训练数据并行模式下,经典的部署方式是独立的参数服务器(Parameter Server)来做训练过程中梯度的收集、分发和更新工作,每一次迭代所有的GPU都要与PS多次通信来获取、更新参数;当节点超过一定数量时,PS的带宽以及处理能力将成为整个系统的瓶颈

AI训练系统和传统后台系统之间的一个最主要区别是,传统后台系统可以通过增加节点的方式来分担访问请求,节点之间没有强相关的关系;而AI训练系统在训练模型时需要参与训练的所有节点都不断的与模型参数服务器交换和更新数据

所以如何在架构部署和算法层面减少对带宽需求,控制多机扩展中参数传输对训练速度的影响,使AI训练集群性能可线性扩展,是AI面临的另一项挑战。

疑问: 通常参数服务器是CPU还是GPU?

  • 官方示例cifar10_multi_gpu中,采用CPU作为参数服务器进行收集(average_gradients)、分发、更新参数(apply_gradients)

超大规模GPU集群(1024+GPUs)线性扩展能力

参数更新去中心化

数据并行训练方式下,每一次迭代都需要做梯度规约,以TensorFlow为代表的经典分布式训练部署方式中,中心化的参数服务器(Parameter Server)承担了梯度的收集、平均和分发工作,这样的部署方式下PS的访问带宽容易成为瓶颈,严重影响可扩展性,机智团队最初应对方法是引入HPC领域常用的去中心化的Allreduce方式,然而目前流行的NCCL2或baidu-allreduce中的Allreduce采用的基于环形拓扑的通信方式,在超大规模GPU集群场景下数据通信会有很大的延时开销。

机智团队进一步将Allreduce算法进行了改进,并成功的部署在1024+GPUs的异构集群中,达到了理想的扩展效率。

利用分层同步和梯度分段融合优化Ring Allreduce

好复杂,后面再看…

扩展阅读

-4分钟训练ImageNet!腾讯创纪录 | 机器之心

BLAS

什么是BLAS

BLAS是 Basic Linear Algebra Subprograms (基本线性代数子程序)的首字母缩写,主要用来做基础的矩阵计算,或者是向量计算。它分为三级:

  • BLAS 1级,主要是向量与向量的计算
  • BLAS 2级,主要是矩阵和向量的计算
  • BLAS 3级,主要是矩阵和矩阵的计算,最典型的是A矩阵*B矩阵,得到一个C矩阵。

为什么BLAS是一个非常重要的库或者接口,是因为它是很多科学计算的核心之一。每年做超级计算机的排行榜,都要做LINPACK测试,该测试很多部分就是做BLAS 3级矩阵和矩阵的计算。此外,还有很多科学和工程的模拟,在转换后都变成了一种矩阵上的操作。如果你把矩阵优化的特别好的话,对整个应用的提升,都是非常有帮助的。

广泛用于LAPACK

疑问: 有没有tensor计算?
tensor计算要转化成以上三个级别的计算吗?没有。numpy支持高阶矩阵(多维数组)计算。

注意subprograms这个词,表示线性代数库的子项目

BLAS与深度学习

经过测试,发现Alexnet大部分的时间花费在卷积层(Conv Layer),另外不少时间花在了全连接层(FC layer)。

  • 卷基层目前通用的实现是展成矩阵,变成矩阵与矩阵的乘法,就是BLAS 3级。
  • 全连接层一般是变成一个矩阵和向量的乘法,也落成了BLAS操作。

也就是说,基于矩阵类学习的深度学习,有90%或者更多的时间是通过BLAS来操作的。当然,随着新的算法出现,卷积层对3*3的卷积核有专门的算法,或者用FFT类类算法也可以做,但是在通用上,展矩阵来做也非常广泛。

疑问: cuda也兼容BLAS接口吗?还是自己独立的接口?

BLAS实现

BLAS只是定义了接口,但是具体的实现其实有很多种。从商业的角度来讲,存在很多商业版本。基本上为了搭配自己的硬件,对其做了更优的优化。常见的商业版本有

  • Intel MKL
  • AMD ACML
  • NVIDIA CUBLAS
  • IBM ESSL

开源

  • GotoBLAS:(2010年中止开发)
  • ATLAS: 美国一所学校开发
  • OpenBLAS: 基于GotoBLAS
  • BLIS: 基于GotoBLAS扩展出来的一个项目

cuda编程模型

基础

unified memory architecture

accessible from cpu and GPU

编程模型

Kernels

CUDA C通过允许程序员定义C函数(称为内核)来扩展C,当调用时,由N个不同的CUDA线程并行执行N次,而不是像常规的C函数那样只执行一次。

kernel function用global关键字标记,这样的函数能够在GPU上并行计算。

使用global声明说明符定义内核,并使用新的<<<…执行配置语法(参见C语言扩展)。执行内核的每个线程都有一个惟一的线程ID,该ID可以通过内置的threadIdx变量在内核中访问。

以下是两个N维向量的加法,$ C = A + B $:

传统写法

1
// todo

cuda编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 首先定义内核函数 (采用__global__关键字)
__global__ void VecAdd(float* A, float* B, float* C)
{
int i = threadIdx.x;
C[i] = A[i] + B[i];
}

int main()
{
...
// Kernel invocation with N threads
VecAdd<<<1, N>>>(A, B, C);
...
}

这里,N个线程同时计算VecAdd,进行元素加和。

疑问
并行度设置为1呢,设置为其他数值呢?

效果对比

源码下载

线程层次结构

在cuda中,线程ID(threadIdx)是一个三维向量,因此可用一维、二维或三维线程索引来标识线程,从而形成一维、二维或三维线程块(thread block)。这方便我们进行向量、矩阵或卷的计算。

线程的索引及其线程ID以一种简单的方式相互关联:

  • 一维块,线程的索引 = 线程ID
  • 大小是$(D_x, D_y)$的二维块,索引为$(x, y)$的线程对应的线程ID是$(x + y Dx)$
  • 大小是$(Dx, Dy, Dz)$的三维块,索引为$(x, y, z)$的线程对应的线程ID是$(x + y Dx + z Dx Dy)$

例如,下面的代码将大小为$N \times N$的两个矩阵相加,$C = A + B$ :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Kernel definition
__global__ void MatAdd(float A[N][N], float B[N][N],
float C[N][N])
{
int i = threadIdx.x; // i,j 对应矩阵的两个维度
int j = threadIdx.y;
C[i][j] = A[i][j] + B[i][j];
}

int main()
{
...
// Kernel invocation with one block of N * N * 1 threads
int numBlocks = 1; // 为什么numBlocks=1?
dim3 threadsPerBlock(N, N);
MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C); // 这里采用
...
}

FAQ

内核是函数?

是的,kernel is function,能够并行的function。

##

扩展阅读

词汇

流处理器,Stream Processor

SMX

Streaming Multiprocessors

CUDA执行模型是SIMT(单指令多线程),G80会在物理上把若干个(G80是8个)流处理器组合到一起,共享缓存,形成一个SM单元(Stream Multiprocessor)。因为单个SM里的所有流处理器共享缓存,所以CUDA模型里,会把多个Thread打包成一个Block,让Block内的线程在同一个SM里跑(实际执行粒度是Wrap,Block内的Threads不是同时跑的,但是线程切换开销极低,这个和CPU不同,因为显卡的寄存器数量巨大,可以理解为拥有很多个超线程的CPU,对超线程技术有了解很好理解,因为Intel的超线程技术就是分时调用Decoder但每个线程独享寄存器),Block内的线程间可以数据同步,不同Block间线程则不行。YouTube上有个视频很形象的把这种模型类比成OpenMP+MPI。

control 控制单元

DMA

GPUDirect allows you to DMA directly to GPU host memory.