【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介绍

用于大型Transformer的8-bit矩阵乘法介绍

原文地址:A Gentle Introduction to 8-bit Matrix Multiplication for transformers at scale using transformers, accelerate and bitsandbytes

相关博客
【深度学习】【分布式训练】Collective通信操作及Pytorch示例
【自然语言处理】【大模型】大语言模型BLOOM推理工具测试
【自然语言处理】【大模型】GLM-130B:一个开源双语预训练语言模型
【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介绍
【自然语言处理】【大模型】BLOOM:一个176B参数且可开放获取的多语言模型
【自然语言处理】【大模型】PaLM:基于Pathways的大语言模型
【自然语言处理】【chatGPT系列】大语言模型可以自我改进
【自然语言处理】【ChatGPT系列】FLAN:微调语言模型是Zero-Shot学习器
【自然语言处理】【ChatGPT系列】ChatGPT的智能来自哪里?
【自然语言处理】【ChatGPT系列】Chain of Thought:从大模型中引导出推理能力
【自然语言处理】【ChatGPT系列】InstructGPT:遵循人类反馈指令来训练语言模型
【自然语言处理】【ChatGPT系列】大模型的涌现能力

一、简介

​ 语言模型正变的越来越大,PaLM已有有540B的参数量,而OPT、GPT-3和BLOOM则大约有176B参数量。下图是近些年语言模型的尺寸。

在这里插入图片描述

​ 这些模型很难在常用设备上运行。例如,仅仅推理BLOOM-176B就需要8x 80GB A00 GPUs。而为了微调BLOOM-176B则需要72个GPU。PaLM则需要更多的资源。

​ 这些巨型模型需要太多GPUs才能运行,因此需要寻找方法来减少资源需求并保证模型的性能。已经有各种技术用来减小模型尺寸,例如量化、蒸馏等。在完成BLOOM-176B训练后,HuggingFace和BigScience逐步探索在少量GPU上运行大模型的方法。最终,设计出了Int8量化方法,该方法在不降低大模型性能的情况下,将显存占用降低了1至2倍。

二、机器学习中常用的数据类型

​ 浮点数在机器学习中也被称为"精度"。模型大小是有参数量及参数精度决定的,通常是float32、float16和bfloat16。

我们开始于不同浮点数的基本理解,在机器学习的背景下也被称为"精度"。模型的大小由其参数数量和精度决定,通常是float32、float16和bfloat16。下图:

在这里插入图片描述

​ Float32(FP32)是标准的IEEE 32-bit浮点数表示,使用这种类型可以表示范围广泛的浮点数。在FP32中,8bits被用于"指数",23bits被用于"尾数", 1 bit则用于符号位。大多数的硬件都支持FP32操作和指令。

​ 在Float16(FP16)数据类型中,5 bits被用作"指数",10 bits用于"尾数"。这使得FP16数的表示范围明显小于FP32,导致有上溢和下溢的风险。例如,若你做 10 k × 10 k 10k\times 10k 10k×10k,最终得到100k。这在FP16中是不可能的,因为其最大表示为64K。因此你会得到NaN的结果,若像神经网络那样顺序执行,所有先前的工作都会被破坏。通常,loss缩放能够一定程度上克服这个问题,但并不总是有用。

​ 因此,创建了一种新格式bfloat15(BF16)来避免这种问题。在BF16中,8bits被用于表示"指数", 7bits被用于表示"尾数"。这意味着BF16能够保留和FP32相同的动态范围,但是损失了3bits的精度。BF16可以表示巨大的数,但是精度上比FP16差。

​ 在Ampere架构中,NVIDIA也引入了TensorFloat-32(TF32)精度格式,其仅使用19 bits就合并了BF16的动态范围和FP16的精度。其目前仅在内部某些操作中使用。

​ 在机器学习的术语中FP32被称为全精度(4 bytes),BF16和FP16则称为半精度(2 bytes)。int8(INT8)数据类型则是由8 bits表示的数,其能够存储 2 8 2^8 28个不同的值([0,255]或者[-128, 127])

​ 理想情况下,训练和推理应该在FP32上进行,但是其比FP16/BF16慢两倍。因此,采用一种混合精度的方法,模型权重仍然是FP32,前向和后向传播则使用FP16/BF16,从而加快训练速度。P16/BF16被用来更新FP32权重。

​ 可以通过参数量乘以浮点数精度的大小来计算模型所占用的bytes量。例如,若模型使用bfloat16版本的BLOOM-176B模型,那么模型大小为176*10**9 x 2 bytes = 352GB!这个量级对于适配少量GPU来说相当有挑战。

​ 但是我们是否可以使用不同的数据类型以更少的存储空间来保存这些权重?一种称为量化的方法被广泛的应用于Deep Learning。

三、模型量化介绍

​ 通过实验发现,在推理中使用2 bytes的BF16/FP16精度能够几乎达到4 bytes的FP32精度相同的效果,而且模型尺寸可以减少一半。若能够进一步削减那就太棒了,但是在更低的精度上推理质量开始急剧下降。为了解决这个问题,我们引入了8 bits量化。该方法使用四分之一的精度,这样仅需要1/4的模型尺寸!但是,其不是通过丢掉另外一半bits来实现的。

​ 量化基本上是从一种数据类型"舍入"为另一种数据类型来完成的。例如,若一个数量类型范围0…9,另一个范围则是0…4。那么第一个数据类型中的"4"将会被舍入为第二种数据类型中的"2"。然而,若第一种数据类型中的"3",其会位于第二种数据类型的1和2之间,然后通常会被舍入为"2"。也就是说第一种数据类型中的"4"和"3"都会对应第二种数据类型中的"2"。这表明量化是可能带来信息丢失的噪音过程,一种有损压缩。

​ 有两种常见的8-bit量化技术:zero-point量化和absolute maximum(absmax)量化。zero-point量化和absmax量化会将浮点数值映射至更加紧凑的int8(1 byte)值。这些方法首先会将输入按照量化常数进行缩放,从而实现规范化。

​ 举例来说,在zero-point量化中,若范围是 [ − 1.0 … 1.0 ] [-1.0\dots1.0] [1.01.0]并希望量化至范围 [ − 127 … 127 ] [-127\dots127] [127127]。那么应该按照因子127进行缩放,然后四舍五入至8-bit精度。为了还原原始值,需要将int8的值除以量化因子127。例如,0.3被缩放为 0.3 × 127 = 38.1 0.3\times127=38.1 0.3×127=38.1,然后四舍五入为38。若要恢复,则 38 / 127 = 0.2992 38/127=0.2992 38/127=0.2992。在这个例子中量化误差为0.008。随着这些微小的误差在模型各个层中传播,会逐步积累和增长并导致性能下降。

​ 再来看看absmax量化的细节。为了在absmax量化中完成fp16和int8的映射,需要先除以张量中的绝对最大值(令整个张量介于-1至1之间),然后在乘以目标数据类型的总范围。例如,在一个向量上应用absmax量化,该向量为
v = [ 1.2 − 0.5 − 4.3 1.2 − 3.1 0.8 2.4 5.4 ] v=\begin{bmatrix} 1.2&-0.5&-4.3&1.2&-3.1&0.8&2.4&5.4 \end{bmatrix} v=[1.20.54.31.23.10.82.45.4]
从向量中选择最大值,即5.4。而int8的范围为[-127,127],所以量化过程为 v / 5.4 × 127 = v × 127 5.4 ≈ v × 23.5 v/5.4\times 127=v\times\frac{127}{5.4}\approx v\times 23.5 v/5.4×127=v×5.4127v×23.5,即整个向量乘以缩放因子23.5。最终得到的量化后向量为
[ 28 − 12 − 101 28 − 73 19 56 127 ] \begin{bmatrix} 28&-12&-101&28&-73&19&56&127 \end{bmatrix} [281210128731956127]
在这里插入图片描述

​ 为了还原原始值,可以使用全精度的int8数除以量化因子23.5。但是由于四舍五入的原因,会丢失一些精度。

​ 这些技巧能够以多种方式组合。例如,当涉及矩阵乘法时,ow-wise或者vector-wise量化可以使得结果更加准确。以矩阵乘法 A × B = C A\times B=C A×B=C为例,相对于使用每个张量的绝对最大值来规范张量,vector-vise量化则会寻找矩阵A每行的绝对最大值和矩阵B每列的绝对最大值。然后通过除以这些绝对最大值向量来规范化矩阵A和B。然后执行 A × B A\times B A×B来得到C。为了最终返回FP16精度的值,通过计算A和B绝对最大值向量的外积来反规范化。

​ 这些技术虽然能够量化模型,但是在较大模型上会带来性能下降。Hugging Face Transformers和Accelerate库集成了一种称为LLM.int8()的8-bit量化算法,能够在176B参数量模型上使用且不降低模型效果。

四、LLM.int8()简介

​ 理解Transformer中与规模相关的涌现特性对于理解为什么传统量化方式在大模型中失败至关重要。性能的下降是由异常特征值导致的,会在后面解释这一情况。LLM.int8()算法本质上可以由三个步骤来完成矩阵乘法:

  1. 对输入的hidden states逐列的提取异常值(即大于某个阈值的值);
  2. 分别对FP16中的异常值和INT8中的非异常值执行矩阵乘法;
  3. 对非异常的结果进行反量化,并将两者结果合并来获得最终的FP16结果;

三个步骤如下图所示:

在这里插入图片描述

1. 异常值特征

​ 在整个分布之外的值,称为异常值。异常值检测被广泛使用,而拥有特征分布的先验知识有助于异常值检测任务。

​ 具体来说,我们观察到经典的量化算法在超过6B参数量的transformer模型上失效了。虽然在较小的模型上也能观测到较大的异常值特征。但是,我们观察到一个参数量的阈值,transformer中的异常值会系统性的出现在每个层中。

​ 由于8-bit精度的局限性,因此仅使用几个特别大的值来量化向量将导致非常差的结果。此外,transformer架构的内在特征就是将所有的元素连接在一起,这将导致错误跨越多层传播并被加剧。因此,开发出了混合精度分解来实现这种极端异常值的量化。

2. MatMul内部

​ 一旦得到hidden state,使用自定义阈值来抽取异常值并分解矩阵为上述两部分。我们发现使用6作为阈值进行抽取可以完整的恢复推理性能。异常值部分以fp16实现,所以是经典的矩阵乘法;而8-bit则是通过vector-wise量化将模型权重和hidden state量化至8-bit的精度。即hidden-state使用row-wise量化,模型权重使用column-wise量化。经过这个步骤后,再将结果反量化并以半精度返回。
在这里插入图片描述

3. 零退化意味着什么

​ 如何评估性能下降?8-bit模型到底损失了多少性能?这里在8-bit模型和native模型上运行了常见的基准,分别针对OPT-175B和BLOOM-176B。

  • 对于OPT-175B

在这里插入图片描述

  • 对于BLOOM-176B

在这里插入图片描述

​ 可以看到这些模型的性能下降为0,因为这些指标的绝对差值小于标准误差。

4. 比native模型更快?

​ LLM.int8()方法的主要目标在不降低性能的情况下,使得大模型更容易被使用。但是,如果该方法非常的慢则就不实用了。所以,我们对多个模型的生成速度进行了基准测试。实验发现使用LLM.int8()的BLOOM-176B要比fp16版本慢15%至23%,这是一个可以接受的范围。但是较小的模型下降会更多。开发人员正在逐步优化这个问题。

在这里插入图片描述

五、Transformers集成

1. 使用

​ 本文重点描述的模块是Linear8bitLt,你可以直接从bitsandbytes库中引入。其来自于经典的torch.nn模块,并使用下面的代码来轻易的使用和部署。

​ 下面是一个使用bitsandbytes将一个小模型转换为int8类型。

  • 正确的引入
import torch
import torch.nn as nnfrom bitsandbytes.nn import Linear8bitLt
  • 先定义一个fp16的模型
fp16_model = nn.Sequential(nn.Linear(64, 64),nn.Linear(64, 64))
  • 假设该模型已经完成训练,保存模型
torch.save(fp16_model.state_dict(), "model.pt")
  • 现在再定义一个int8模型
int8_model = nn.Sequential(Linear8bitLt(64, 64, has_fp16_weights=False),Linear8bitLt(64, 64, has_fp16_weights=False)
)

添加参数has_fp16_weights很重要。默认值为True,其被用于Int8/FP16混合精度训练。然而,这里关注的是推理,所以将其设置为False

  • 现在将fp16的模型加载至int8模型中
int8_model.load_state_dict(torch.load("model.pt"))
# print(int8_model[0].weight)
int8_model = int8_model.to("cuda:0") # 执行该代码时会进行量化
# print(int8_model[0].weight)

通过输出print(int8_model[0].weight)可以看到模型被量化为Int8类型,那么怎么还原为FP16权重呢?

(int8_model[0].weight.CB * int8_model[0].weight.SCB) / 127
  • 使用int8模型进行推理
input_ = torch.randn((1,64), dtype=torch.float16)
hidden_states = int8_model(input_.to(torch.device('cuda:0')))

2. 你只需要accelerate

​ 当使用大模型时,acceleate库包含了有用的程序。init_empty_weights方法特别有用,因为任何模型(无论大小)都可以作为上下文管理器使用此方法进行初始化,而无需为模型权重分配任何内存。

import torch.nn as nn
from accelerate import init_empty_weightswith init_empty_weights():model = nn.Sequential(*[nn.Linear(100000, 100000) for _ in range(1000)])

这个初始化的模型会被放置至Pytorch的元设备上,其是一种不用分配存储空间来表示shape和dtype的潜在机制。

起初,该函数在.from_pretrained函数中被调用,并将所有参数重写为torch.nn.Parameter。但是,这不符合我们的需求,因为希望在Linear8bitLt模块中保留Int8Params类。因此我们将

module._parameters[name] = nn.Parameter(module._parameters[name].to(torch.device("meta")))

修改为

param_cls = type(module._parameters[name])
kwargs = module._parameters[name].__dict__
module._parameters[name] = param_cls(module._parameters[name].to(torch.device("meta")), **kwargs)

通过这个修改,我们可以通过自定义函数在没有任何内存消耗的情况下,利用这个上下文管理器将所有的nn.Linear替换为bnb.nn.Linear8bitLt

def replace_8bit_linear(model, threshold=6.0, module_to_not_convert="lm_head"):for name, module in model.named_children():if len(list(module.children())) > 0:# 递归replace_8bit_linear(module, threshold, module_to_not_convert)if isinstance(module, nn.Linear) and name != module_to_not_convert:with init_empty_weights():model._modules[name] = bnb.nn.Linear8bitLt(module.in_features,module.out_features,module.bias is not None,has_fp16_weights=False,threshold=threshold,)return model

该函数会递归的将元设备上的所有nn.Linear替换为Linear8bitLt模块。属性has_fp16_weights必须被设置为False,以便加载int8权重和量化信息。

3. 如何在transformers中使用

from transformers import AutoTokenizer, AutoModelForCausalLMdef inference(payload, model, tokenizer):input_ids = tokenizer(payload, return_tensors="pt").input_ids.to(model.device)print(f"输入:\n    {payload}")logits = model.generate(input_ids, num_beams=1, max_new_tokens=128)print(f"生成:\n    {tokenizer.decode(logits[0].tolist()[len(input_ids[0]):])}")model_name = "bigscience/bloomz-7b1-mt"
payload = "一个传奇的开端,一个不灭的神话,这不仅仅是一部电影,而是作为一个走进新时代的标签,永远彪炳史册。你认为这句话的立场是赞扬、中立还是批评?"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model_8bit = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", load_in_8bit=True)
model_native = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
# 比较推理结果
inference(payload, model_8bit, tokenizer)
inference(payload, model_native, tokenizer)
# 计算显存节约程度
mem_fp16 = model_native.get_memory_footprint()
mem_int8 = model_8bit.get_memory_footprint()
print(mem_fp16/mem_int8)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/62181.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单易懂------使用PyTorch实现Chatbot

本系列文章通过通俗易懂的方式介绍强化学习的基本概念,虽然语言通俗,但是内容依旧非常严谨性。文中用很多的公式,对数学公式头疼的读者可能会被吓住,但是如果读者一步一步follow下来,就会发现公式的推导非常自然&#…

Hugging Face Transformer:从原理到实战的全面指南

一、前言 前面我面介绍过ChatGPT的基本原理以及预训练大语言模型的发展史,我们知道ChatGPT和所有预训练大语言模型的核心是什么?其实就是 Transformer,Hugging Face 的火爆离不开他们开源的这个 Transformers 库。这个开源库里有数万个我们可…

[AI存储] NLP 和 AI: 数据存储在推动生成式 AI 工具中的作用

很少有技术现象像新一批生成式 AI 工具(如Dall-E和ChatGPT)那样迅速流行起来。似乎一夜之间,这些引人入胜的新应用正被各种各样的人用于不同的应用 – 从内容原创到邮件和报告撰写、软件开发、内容研究等等。 特别是ChatGPT,由于其…

微信小程序云开发实现一对一即时聊天,发送语音、文件、图片、朋友圈、添加好友等功能

点此源码获取 前几篇博文 前几篇文章实现了,利用云开发的即时通信demo做的仿照微信好友聊天。 本篇文章主要在前几篇的基础上进行的修改,实现 即时一对一聊天,群聊天,发送文件图片,发送语音,更换聊天背景…

微信小程序目前最新的授权登录接口-2021年10月份

微信小程序目前最新的授权登录接口-2021年10月份 效果图: 说明:首先我们需要在app.js里用云函数获取到openid,然后在用户点击登录的时候用获取到的openid去用户表里查询是否有该用户,如果没有就把用户信息写入数据库里的用户表,再…

微信小程序原生接入腾讯云im(单聊,列表,聊天界面,自定义消息,自动回复)

微信小程序原生接入腾讯云im(单聊,列表,聊天界面,自定义消息,自动回复) 发送图片语音消息传送→ 文章目录 1.项目需求 2.参考文档 3.效果图 4.初始化 集成SDK 5.登录 6.会话列表 7.聊天页面 8.遇到的问…

微信小程序接入腾讯云IM即时通讯(发送消息开发步骤)

微信小程序接入腾讯云IM即时通讯(聊天窗口) 1.效果图: 2.功能点 : 1.布局要分左右两边布局,如果是自己为发送消息方,都在右边,对方发送的消息在左边。 2.腾讯云返回的是时间戳,需…

uniapp小程序接入腾讯IM聊天

腾讯IM中创建项目 拿到AppID 与 密钥 下载demo 将debug 文件夹拖到自己项目中 在第一个js文件中填入自己项目的APPID 与密钥 下载(tim-wx-sdk 上传文件 的依赖 看项目所需 import TIM from tim-wx-sdk; import TIMUploadPlugin from tim-upload-plugin; let optio…

用最通俗易懂的语言告诉你什么是信息熵

图和公式都挂了,想看原文的小伙伴去公众号后台回复”信息熵”即可 假设有个考试作弊团伙,需要连续不断地向外传递4选1单选题的答案。直接传递ABCD的ascii码的话,每个答案需要8个bit的二进制编码,从传输的角度,这显然有…

外景黑纱婚纱照高级感十足

婚纱照不止有自然小清新(&还可以暗黑高级 看腻了千篇一律的白色婚纱礼服 炎追求个性、与众不同的仙女们不妨尝试一下黑色婚纱🖤 黑纱不一定只有酷和暗黑 也有法式赫本风的复古优雅 满满的氛围感和高级感 🎬 女生的拖尾黑裙神秘又优雅 …

福州一般拍一套婚纱照要多少钱

随着婚纱摄影行业发展以来,近几年选择婚纱照旅拍的准新人越来越多了。相信每一位新人对于这些都是第一次没有经验的,所以也不太清楚行情,不知道选择怎么样的套餐,那小编就来科普一下现在旅拍的行情,让你们在选择婚纱照…

晒一下我和老婆的婚纱照!

两人一起的 老婆单人的 老婆就是大名鼎鼎的晴天有雨啊! 我单人的

冬天拍婚纱照注意事项 拍出养眼婚纱照

寒冷的冬天来了,对于北方人就要迎接雪花了,在寒冷的冬季进行婚纱摄影,会美丽但是也会“冻人”哦!下面 南京婚纱摄影工作室给大家介绍冬天拍婚纱照注意事项,准新人们可以参考一下哦! 冬天拍婚纱照注意事项 拍出养眼婚纱照 婚纱照 冬…

国内唯美花海 新娘绝美婚纱照

每位新郎都希望有一位仙女般的妻子,新娘也都希望自己是花丛中的花仙子。一组浪漫唯美的花海婚纱照无疑是诠释新人们花仙子梦的最好渠道。下面 青岛摄影工作室 就来给大家盘点一下国内那些唯美的花海。 1、云南罗平油菜花 云南省的罗平。从每年的二月份开始就进入了油…

W ndoWS十p啥意思,婚纱照相册10P是什么意思?入册多少张最合适?

原标题:婚纱照相册10P是什么意思?入册多少张最合适? 在预定婚纱照时,都会谈及相册P数,尺寸大小等等一些相册信息,一般相册的页数有7、10、12、15、20p还有30p的,而我们常见就为10P,那么&#xf…

婚纱照效果 取景很重要

新人们都希望可以有绝美的婚纱照,却又为自身没有绝对的优势而感到沮丧。其实漂亮的婚纱照通过选择适合景点选取一样可以拍出来。 婚纱照应该在选景的时候注意哪些问题,很多人渴望拥有类似杨幂结婚时一样的明星婚纱照,首先在选景的时候&#…

简约复古婚纱照拍摄攻略

当下拍婚纱照也要紧跟时尚潮流,当下拍婚纱照流行什么呢?简约、复古是当下婚纱照拍摄关键词。将时尚复古元素相结合也是当下最受年轻人亲睐的婚纱照拍摄形式,那么如何拍摄简约复古婚纱照呢?下面就跟无锡婚纱摄影一起来看看简约复古…

Photoshop透明婚纱照抠图处理

透明婚纱照抠图处理教程:先需要用钢笔等工具把人物部分抠出来包括婚纱部分。然后把抠出的人物图层复制一层,在通道选区婚纱部分较为清晰的通道并调出选区,回到图层面板后把选区反选按删除高光部分以外的图像,得到的图像就是我们抠…

九宫格摆法_九宫格婚纱照摆法图片与技巧

婚纱照挂法已不再是单一的排版,九宫格婚纱照开始逐渐流行在新人之中。下面来看看九宫格婚纱照摆法图片,以及九宫格婚纱照摆法的技巧。 一、九宫格婚纱照摆法 九宫格最常见的挂法就是方方正正的33的形式,当然除了九宫格的挂法之外,…