引言
当前在边缘加速器上保护模型推理免受比特翻转的方法可分为两类,具体取决于它们是否修改模型结构。
- 第一类,通过修改模型结构来增强模型鲁棒性,包括专门的激活函数、压缩和量化。然而,这些方法通常会损害精度,并且在不同模型之间缺乏通用性。
- 第二类,保留模型结构,通过冗余实现保护,例如三模冗余 (TMR)和 ECC 。尽管这些方法可以保留精度和通用性,但它们要么会产生显著的开销(TMR 为 2 倍),要么提供的纠错能力有限(ECC)。
SAVE属于第二类,而且无需重新训练模型或牺牲精度的前提下
观察到的现象
第一,硬件上“并非所有比特都生而平等”。
现代边缘AI加速器(如NVIDIA Orin)提供了少量但完全无比特翻转的“安全岛”内存;同时,部分GPU内存比特本身由于制造工艺等因素,在实际中也不易翻转,可被视为可靠内存。第二,软件层面“并非所有比特翻转都是致命或静默的”。
由于模型计算中的非线性激活函数等特性,比特翻转对推理结果的影响程度不同。部分比特是鲁棒比特(Robust Bits),其翻转对最终精度几乎没有影响;
与通用计算不同,模型计算的非线性使得某些比特翻转对模型精度的影响很小。
部分比特是范围比特(Ranging Bits),其错误可以通过简单的输出范围检查被检测到;
可能导致NaN并将模型精度降低至0
剩余的是脆弱比特(Vulnerable Bits),它们会静默地破坏模型输出且难以察觉。
导致精度逐渐下降;eg 尾数最后几位对结果影响甚微,因此可直接指定为鲁棒比特
基于以上背景与洞察,本研究的目标是提出一种不修改模型结构、不牺牲精度、且性能开销低的软件实现的容错系统SAVE
基本思想&洞察
在判单 某一个数值 的 某一个比特 属于哪一种比特的时候;通过测试所有比特位来寻找所有鲁棒比特和范围比特是不切实际的,因为比特位的数量庞大(1亿参数,每个参数32bit)。
每翻转一个比特,理论上都需要执行一次完整的模型推理过程,以评估其对最终结果(如分类准确率)的影响。一个复杂的深度学习模型推理可能涉及数千甚至数万次浮点运算。
穷举法是:离线、动态测试(实际运行推理)
SAVE是:离线、静态分析
同时,单个比特位的鲁棒性不能直接推广到多个比特位(多个比特同时翻转)
正因为这种复杂性,SAVE 并没有试图穷举多比特翻转,而是将比特分为了三种类型:Robust Bits;Ranging Bits;Vulnerable Bits(穷举370天,SAVE10s)
Robust Bits:不用通过反转一个,然后执行一次完整的推理过程分析最终结果来验证。只需要分析GPU核函数的数学性质,即可确定一些鲁棒比特;and 尾数最后几位直接指定为鲁棒比特。
Ranging Bits:见下“选择阶段”
SAVE系统的工作流程
SAVE系统的工作流程包含四个阶段:选择、分配、验证和编辑。整个系统构建在PyTorch框架之上。
PS:第一步首先通过离线 静态 分析识别出模型中“每一个”数值(包括输入、输出(激活值)和中间值,不包括模型参数(第二步会被故意放到不可靠内存),KV Cache)的三种比特类型。
第二步,模型运行时;在执行每一个GPU核函数时,将输入矩阵(一般就是指激活值;模型权重都放到不可靠内存)分块。SAVE优先将包含更多“类型-2”数值(即包含脆弱比特的数值)的分块分配到可靠内存,剩余的分块分配到不可靠内存。 需要注意的是,在第二步中为了最大限度的利用可靠内存,SAVE采用“就地计算”的方法。
“类型-2”数值的定义(§3.2)是包含脆弱比特的数值。而“类型-1”数值是全部由鲁棒比特和范围比特构成的数值。
决策的直接单元是“类型-2”数值,而非直接统计脆弱比特本身
第三步的验证内容是动态的。首先对所有涉及的计算块异步验证其模型权重(通过与CPU端的完整的模型参数是否一致来检验)。然后,对于“输入存放在不可靠内存中”的那些计算块,额外启动 范围比特 的GPU核函数验证,并对脆弱比特部分发起异步的“混合精度 重计算”验证。
第四步恢复阶段,若在第三步中检测出来了至少有一个比特除错,则从中断的模型层开始重新启动推理计算
第一阶段:选择阶段
此阶段为一次性离线静态分析,旨在识别模型中每个数值的三种比特类型(鲁棒比特、范围比特、脆弱比特)。
范围分析(离线的):SAVE将模型视为一个有向无环计算图,从已知的输入数据范围(例如,图像像素值范围0-1)开始,推导出模型中每一个中间值(张量中每个元素)的合法输出值范围。
对于输入输出关系非单调或输出范围过宽的操作符,SAVE会将其输出范围划分为多个区间进行更精细的分析。
比特属性分析:利用计算出的值范围,SAVE识别两种非脆弱比特。回到论文了解细节
参数是按照IEEE754的格式
- 范围比特的识别:对于一个给定 值范围,SAVE找出在该范围内所有可能取值都必须相同的那些比特(通常是符号位和部分受范围边界约束的指数位)。这些比特即为范围比特,它们的正确性可以通过检查计算值是否落在预定范围内来快速验证。
- 鲁棒比特的识别:SAVE识别两类鲁棒比特。
一是基于数值的鲁棒比特:对于一个值范围的上下界,如果翻转其尾数部分的某个比特所引起的数值变化小于一个保守的阈值(论文选用10^-5),则该比特被认为是鲁棒的。取上下界鲁棒比特的交集作为该值范围的鲁棒比特集。
二是基于操作符的鲁棒比特:某些操作符(特别是激活函数)会引入额外的鲁棒性。例如,对于ReLU函数,只要输入为负,无论其具体负值是多少,输出恒为0。因此,对于负输入,除符号位外的其它比特都可被视为鲁棒比特。
分析结果被存储在一个比特属性缓存中,供运行时快速查询,以加速验证过程。
第二阶段:分配阶段
此阶段负责运行时GPU内存的管理与数据放置策略。由于现有计算接口不支持比特级内存管理,SAVE采用矩阵分块的策略。
- 分区与评估:在执行GPU核函数时,SAVE将输入矩阵分区为若干子矩阵块。评估每个子矩阵块中所含脆弱比特的比例。
- 优先级分配:分配策略的核心是优先将脆弱比特比例更高的计算(子矩阵块)放置在可靠内存中,模型参数(权重矩阵)则被有意地始终放置在不可靠内存中,因为CPU端保存有它们的完好副本
- 就地计算与内存管理:为了优化有限可靠内存的使用,SAVE采用就地计算技术,eg在矩阵乘法中用输出结果覆盖输入张量。
同时,它需要仔细管理输出内存的放置。根据输入和输出所在的内存类型(可靠/不可靠),存在四种计算场景。
SAVE确保在CPU端已安全存储副本后,才进行内存的原地修改,并同步更新验证状态。
**鲁棒比特计数(Robust Bit Count)图例:从深橙色到浅橙色再到白色,表示矩阵块中鲁棒比特(Robust Bit)**的数量从“多(More)”到“少(Less)”。
- 绿色边框 (Case 1):表示输入和输出都存储在可靠内存中。
- 蓝色边框 (Case 2):表示输入存储在可靠内存,输出存储在不可靠内存中。
- 灰色边框 (Case 3):表示输入存储在不可靠内存,输出存储在可靠内存中。
- 黄色边框 (Case 4):表示输入和输出都存储在不可靠内存中。
第三阶段:验证阶段
此阶段负责高效检测发生在不可靠内存计算中的错误。
在大模型运行时,显存、内存中读存放哪些数据:
显存:核心计算区
- 推理阶段:模型参数(也称“权重矩阵”,权重、偏置),KV Cache,激活值(也称“中间值、特征图”,计算后立即释放),临时缓冲区(矩阵乘法临时空间等)
- 训练阶段:模型参数,KV Cache,完整激活值,梯度,优化器状态,临时缓冲区
内存:数据与调度中枢
模型参数(模型过大时,内存存储完整权重,显存按需交换),训练数据&批次,优化器状态,系统与框架开销
异步权重验证:对于模型参数(权重),SAVE在每层计算后,利用GPU的DMA引擎异步地将其从GPU不可靠内存拷贝回CPU(从GPU拷贝回CPU???干什么?),由CPU线程与原始副本进行一致性比对。此过程与下一层的GPU计算重叠,几乎不引入额外延迟。没看懂
对于“模型参数”而言,模型参数都被完整的存放在了CPU端的内存中,即显存中的模型参数在CPU端都有副本,可以用这个副本来检验GPU中的“模型参数 数据”是否出错
对于存储在不可靠内存中的中间值
验证 范围比特:,SAVE利用选择阶段推导出的合法范围来验证其范围比特。
它启动一个轻量级的GPU核函数,快速检查每个值是否落在预定的区间内,并将结果标记矩阵传回CPU进行最终确认(检查是否全部为零)。该核函数可与原计算核函数融合,以减少开销。混合精度 重计算验证 脆弱比特:对于涉及脆弱比特的计算结果,需要进行重计算来验证正确性。为了最小化CPU重计算的负担,SAVE采用混合精度整数计算进行轻量级验证。由于范围比特已通过范围检查,鲁棒比特可被忽略,真正需要验证的比特数已减半。
结果的符号位通过对x和y的符号位行异或运算得到。
结果的指数位通过对x和y的指数部分进行加法或减法运算得到。
确定了新的指数位,重新估计鲁棒位,并移除鲁棒位以进行进一步的低精度验证。
结果的尾数通过将x和y的尾数(不包括鲁棒位)转换为16位或8位整数(低精度和混合精度),然后执行乘法或除法来计算
SAVE将剩余的尾数比特转换为16位或8位整数,利用CPU的SIMD指令并行执行整数乘法或除法(详见论文),并与从GPU结果中提取的符号位、指数位组合,生成一个低精度的验证结果进行比对。对于加法和减法等轻量操作,则直接进行重算。
第四阶段:恢复阶段
当在验证阶段检测到错误(比特翻转)时,SAVE执行错误恢复。其策略是简单地从中断的模型层开始重新启动推理计算,利用可靠内存和已验证的正确数据,确保最终得到正确结果。