利用 device_map、torch.dtype、bitsandbytes 压缩模型参数控制使用设备

这篇具有很好参考价值的文章主要介绍了利用 device_map、torch.dtype、bitsandbytes 压缩模型参数控制使用设备。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为了更好的阅读体验,请点击这里

device_map

以下内容参考 Huggingface Accelerate文档:超大模型推理方法

在 HuggingFace 中有个重要的关键字是 device_map,它可以简单控制模型层部署在哪些硬件上。

设置参数 device_map="auto",Accelerate会自动检测在哪个设备放置模型的哪层参数(自动根据你的硬件资源分配模型参数)。其规则如下:

  • 首先充分利用GPU上的显存资源
  • 如果GPU上资源不够了,那么就将权重存储到内存
  • 如果内存还不够用了,将会使用内存映射的技术,将剩余的参数存储到硬盘上

设置参数 no_split_module_classes=["GPTJBlock"] 表示,模型中的 GPTJBlock 模块不会被切分移到不同的设备上。如果有些层包含残差操作,那么请做这样的设置。

可以通过 model.hf_device_map 来观察这个 device_map 的具体内容。

也可以自定义 device_map,也可以显示的设置每一层与设备的对应关系,然后使用如下代码加载 checkpoint。

model = load_checkpoint_and_dispatch(model, "sharded-gpt-j-6B", device_map=my_device_map)

设计 device_map

可以使用其他的 device map 映射方式,通过设置 device_map 参数(例如 "auto", "balanced", "balanced_low_0", "sequential"),或者手工设置这个 device map 字典。

读者可以操控模型在meta设备上的所有层(计算 device_map)。

当读者没有足够GPU显存来加载完整的模型(因为都会按照先占满GPU显存,再占满CPU/内存资源,最后占据硬盘的顺序来完成模型加载),上面所有的选项得到的层和设备对应结果将会相同。

当读者有足够的GPU资源来加载模型,那么上面4个选项得到的结果会有所不同。

  • "auto""balanced" 将会在所有的GPU上平衡切分模型,那么可以计算批尺寸大于 \(1\) 的输入
  • "balanced_low_0" 会在除了第一个GPU上的其它GPU上平衡划分模型,并且在第一个 GPU 上占据较少资源。这个选项符合需要在第一个 GPU 上进行额外操作的需求,例如需要在第一个 GPU 执行 generate 函数(迭代过程)。
  • "sequential" 按照GPU的顺序分配模型分片,从 GPU 0 开始,直到最后的 GPU(那么最后的 GPU 往往不会被占满,和 "balanced_low_0" 的区别就是第一个还是最后一个,以及非均衡填充)

这里 "auto""balanced" 会得到相同的结果,但是未来 "auto" 模式可能会被改变,主要是有可能发现更高效的分配策略。"balanced" 参数的功能则保持稳定。

还有需要注意的地方是,可以通过设置参数 max_memory 来限制每个GPU的显存使用数额(在函数 infer_auto_device_map() 函数设置)。当设置了参数 max_memory,需要构建一个包含 GPU 索引的字典(例如 \(0, 1\) 等),并且 cpu 这个 key 是指 CPU 离线加载的最大内存(可以设置占用内存,不然加载后电脑会卡)。字典的 value 值,可以是整数(单位是字节),也可以是字符串,例如 "10GiB""10GB"(表示最大占用10GB的显存)。

下面是一个例子。表示第 \(0\) 号和第 \(1\) 号 GPU 上最多提供 10GB 的显存、以及不超过 30GB 的内存给模型权重加载使用。

from accelerate import infer_auto_device_map

device_map = infer_auto_device_map(my_model, max_memory={0: "10GiB", 1: "10GiB", "cpu": "30GiB"})

当 PyTorch 加载模型时,他会先加载 CUDA 内核,这个就占据了 1-2GB 的显存(根据 GPU 的不同会略有区别)。因此能够使用的 GPU 显存要小于实际标定显存。可以使用代码 torch.ones(1).cuda() 来看看你的 GPU 上的 CUDA kernel 占用显存大小。

因此可以通过待 max_memory 参数的存储空间映射,来防止 out-of-memory 错误出现。

另外,如果有这样的需求,一些操作的输出需要在GPU上运行(例如 generate 函数来生成文本),或者将输入放到 GPU 上,那么在某个 GPU 上需要留一些显存(Accelerate 会将输出返回作为一些设备的输入)。又如果需要优化最大的批尺寸以及有很多的 GPU,那么可以在第一个的 GPU 留下足够的显存。例如在 8x80 A100 GPU 上运行 BLOOM-176B 的理想内存设置如下:

max_memory = {0: "30GIB", 1: "46GIB", 2: "46GIB", 3: "46GIB", 4: "46GIB", 5: "46GIB", 6: "46GIB", 7: "46GIB"}

除了 GPU 0 上留了足够的显存,在其他 \(7\) 个 GPU 上也留了将近 \(50\%\) 的显存。

如果自定义 device_map,那么字典中的 key 必须是模型中的模块名,且 value 是合法的设备索引(例如对于 GPU,是从 \(0\) 开始的整数)或 "cpu"(内存)或 "disk"(硬盘)。并且 key 要覆盖所有的模型模块名,满足这些规则,你可以随心所欲的定义你的 device map。例如你的模型有两个模块(就叫 block1block2 吧),每个模块包含三个线性层(称为 linear1linear2linear3 吧,还真是有顺序)。一个合法的 device map 如下:

device_map = {"block1": 0, "block2": 1}

另外一个合法的 device map 如下:

device_map = {"block1": 0, "block2.linear1": 0, "block2.linear2": 1, "block2.linear3": 1}

相反,下面的device map就不是合法,因为它的key没有覆盖模型的所有模块名。

device_map = {"block1": 0, "block2.linear1": 1, "block2.linear2": 1}

为了提升效率,最好是你的 device map 是按照 GPU 顺序,序贯的配置参数(例如不要在 GPU 0 上加载第一个全在,在 GPU 1 上再加载,然后权重又回到 GPU 0,交叉来回会降低模型推理速度)。主要是减少数据的 GPU 之间切换次数,提高效率。

一些注意事项

  • infer_auto_device_map() (或在load_checkpoint_and_dispatch()中设置device_map="auto")会最大化使用 GPU 显存和 CPU 内存。当 PyTorch 会高效的管理 GPU 显存(并且会释放不适用的显存),但是管理内存不怎么高效。因此,使用 auto 模式,在 CPU 内存管理上不是很高效。一个好的解决方法,就是将部分在内存上的模块移到硬盘上。
  • infer_auto_device_map() (或在load_checkpoint_and_dispatch()中设置device_map="auto")是按照 GPU、CPU 和硬盘的顺序分配模型模块(防止循环操作),因此如果你的第一个层需要的 GPU 显存空间大于 GPU 显存时,有可能在 CPU/硬盘上出先奇怪的东西(第一个层不要太大,不然会发生奇怪的事情)。
  • load_checkpoint_and_dispatch() 和 load_checkpoint_in_model() 不会对 checkpoint 中的 state_dict 和模型进行检测(后面会修正这个),如果加载的 checkpoint 和 model 不匹配,又会发生奇怪的事情(报错啦!)。
  • 没有做模型并行,意思是你的模型被分割到多个 GPU ,然后被顺序执行。换言之,一次只有一个 GPU 在运行,然后等待其他 GPU 运行完毕。

torch_dtype

这个形参可以设置模型中全部 Linear 层的数据格式,可以使用如下把 \(32\) 位的模型线性层参数转换为 \(16\) 位的模型参数

model = AutoModelForCausalLM.from_pretrained("./Llama-2-7b-hf", torch_dtype=torch.float16)

但是除线性层之外的所有参数仍为 \(32\) 位浮点数。

bitsandbytes (load_in_8bit / load_in_4bit)

主要参考 HuggingFace 的文档 Quantize 🤗 Transformers models

这个是 bitsandbytes 包中特有的内容,必须安装了这个包才能使用这个形参。

具体作用是使用 \(8\) 位或 \(4\) 位来读入模型参数。具体算法可以参考 LLM.int8() 这篇论文。

自 0.39.0 版发布以来,可以利用 FP4 数据类型,使用 \(4\) 位量化加载任何支持 device_map 的模型。

如果你想量化自己的 PyTorch 模型,请查看 Accelerate 库的文档。

以下是使用 bitsandbytes 集成可以做的事情:

正常用法

如果你的模型支持使用 HF 的 Accelerate 导入,并包含线性层,那么可以在调用 from_pretrained() 函数时使用 load_in_8bit 或者 load_in_4bit

from transformers import AutoModelForCausalLM

model_8bit = AutoModelForCausalLM.from_pretrained("facebook/opt-350m", load_in_8bit=True)
model_4bit = AutoModelForCausalLM.from_pretrained("facebook/opt-350m", load_in_4bit=True)

默认情况下其他的模块(例如 torch.nn.LayerNorm)会被转化为 torch.float16,但是其实你也可以使用上文中提及的 torch_dtype 强行改成 \(32\) 位。

import torch
from transformers import AutoModelForCausalLM

model_8bit = AutoModelForCausalLM.from_pretrained("facebook/opt-350m", load_in_8bit=True, torch_dtype=torch.float32)
model_8bit.model.decoder.layers[-1].final_layer_norm.weight.dtype

可以检查你的模型的内存占用量 footprint 使用 get_memory_footprint 方法.

print(model.get_memory_footprint())

4/8 位浮点数

需要三个最新的包:bitsandbytes、accelerate、transformers。

目前无法将 \(4\) 位的模型上传到 HF 的 Hub 里,\(8\) 位的模型如果使用最新的包可以上传到 Hub 中。训练 \(4/8\) 位的模型目前仍不支持。用 \(4/8\) 可以训练额外的参数。(截止至 2023-9-7)文章来源地址https://www.toymoban.com/news/detail-699648.html

到了这里,关于利用 device_map、torch.dtype、bitsandbytes 压缩模型参数控制使用设备的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RuntimeError: Attempting to deserialize object on a CUDA device but torch.cuda.is_available() is Fal

    今天在跑 yolov7 的时候遇见,模型加载问题,因为我是使用CPU来加载 pt 模型的,但是出现了错误; RuntimeError: Attempting to deserialize object on a CUDA device but torch.cuda.is_available() is False. If you are running on a CPU-only machine, please use torch.load with map_location=torch.device(\\\'cpu\\\') to map your storages to

    2024年02月11日
    浏览(43)
  • c++利用哈夫曼编码实现文件的压缩加密和解压缩解密

    需求分析 @1:编码实现哈夫曼树,然后根据数据建立哈夫曼树,然后显示所有的字符的哈夫曼编码 @2:实现哈夫曼编码和解码 并通过编码实现文本文件的压缩 通过解码实现压缩文件的解压缩 概要设计 @1:在二叉树的基础上实现哈夫曼树的数据结构 @2:读取文本文件--对字符频

    2024年02月03日
    浏览(32)
  • Java Map中Value值的排序(利用Map统计次数)

    引起我思考Java中Map排序问题,是来源于 LeetCode 501. 二叉搜索树中的众数。 这道题要求根据一棵给定的二叉搜索树,在树中找出结点中出现次数最多的那个值,且不唯一。换句话说,也就是在树中搜索众数,且不唯一。 看到这道题时,首先就想到要遍历整棵树中的每个结点,

    2024年02月02日
    浏览(43)
  • 利用pycocotools库计算MAP:生成coco格式 json文件数据集和计算map值

    在目标检测任务中,需要通过Map指标判断模型的精度。为了测试engine文件推理结果的精度,本文介绍了如何使用pycocotools库计算Map,在此之前需要根据coco格式生成json文件。 必须按照coco格式生成json,顺序都要保持一致才行,否则报错不通过。 在做验证时,需要提前划分好v

    2024年02月06日
    浏览(43)
  • [踩坑记] CUDA环境下bitsandbytes安装报错/异常解决

    :模型量化,bitsandbytes,bitsandbytes报错,大模型环境,CUDA环境 在部署大模型LLaMA的过程中,需要安装量化工具包 bitsandbytes ,环境如下: 操作系统:Ubuntu 18.04 GPU:4xA100 40G CUDA:11.7 cuDNN:8.4 bitsandbytes: 0.38.0 github库 在 conda python 环境下,通过 pip install bitsandbytes 直接安装

    2024年02月08日
    浏览(36)
  • 解决Python中使用bitsandbytes出现CUDA detection failed问题

    解决Python中使用bitsandbytes出现CUDA detection failed问题 近年来,深度学习技术的快速发展使得GPU计算成为模型训练和推理的主流方式。在使用Python编写深度学习程序时,常常会使用到基于CUDA加速的GPU计算库,例如TensorFlow、PyTorch等。然而,在使用bitsandbytes库进行GPU加速时,有时候

    2024年02月14日
    浏览(30)
  • Java利用Apache compress包实现文件夹压缩成Zip包

    Apache common提供了很多实用的工具包,下面就说一下如何用compress包来压缩文件夹。先引入compress,io和lang3这3个工具包: 这个方法实现了将文件夹下所有的文件压缩成zip包,并输出到文件流中,可以直接写入到文件或提供给前端下载,工具类如下: 执行main函数跑测试用例,发

    2024年01月20日
    浏览(36)
  • vue3利用 a 标签,文件流,JSZip 压缩包,实现文件下载

    在实现文件的下载,采用 a 标签,会出现图片,没有进行下载,而是,在当前页面打开了图片。 导致原因: ·a标签,有 download 属性,可以实现下载 同源文件( ip 和 端口 相同),当图片不同源 时,点击下载,会在当前窗口直接打开图片,而不是进入下载状态。 1.1 没有图片

    2023年04月08日
    浏览(30)
  • loss.backward()处遇到“RuntimeError: Found dtype Double but expected Float”

    类型错误, 计算loss值的函数传入的参数类型不统一。 查看上文loss计算代码部分的参数类型,如loss=f.mse_loss(out,label),检查out和label的类型都是torch.float类型即可。使用label.dtype查看tensor的类型。 报错定位在这一行 寻思着是否是loss类型的问题,于是我就添加 但是还是报错在此

    2024年02月16日
    浏览(31)
  • [i.MX] imx6q利用Mfgtools工具烧录失败,显示No Device Connected!烧录到一半显示“Push“ error, file=“***““

    打开MfgTool工具,开发板上电后,显示No Device Connected。软件显示识别不到“符合HID标准的供应商定义设备”。确定拨码没有问题,检查线路也正常,没有使用USB HUB接线,而是直接接在电脑上。(网上有说使用USB HUB的问题) 反复上下电后,终于能够识别“符合HID标准的供应商定

    2024年02月12日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包