当前位置: 首页 > news >正文

如何运行大模型

简介

要想了解一个模型的效果,对模型进行一些评测,或去评估是否能解决业务问题时,首要任务是如何将模型跑起来。目前有较多方式运行模型,提供client或者http能力。

名词解释

浮点数表示法

一个浮点数通常由三部分组成:符号位、指数部分和尾数部分(也称为小数部分或 mantissa)。

  1. 符号位:决定了数的正负。在大多数浮点数格式中,符号位通常占1位,0表示正数,1表示负数。
  2. 指数部分(Exponent):表示数值的大小或缩放因子。在浮点数中,指数部分采用偏移码(bias encoding)来表示,以确保0的特殊处理和指数的正负表示。例如,在IEEE 754标准的单精度(float32)中,指数部分实际占8位,但由于偏移码(通常偏移127),实际的指数范围是从-126到127。
  3. 尾数部分(Mantissa或Fraction):代表小数部分,它是一个小于1的正数,用来精确表示数值的非整数部分。在标准的浮点数表示中,尾数部分总是以1开头(这个1是隐含的,不占用实际的位),然后是若干位来表示其余的数字。例如,在float32中,尾数部分实际有23位(因为隐含了一个1),这提供了很高的精度。

以一个具体的例子来说明,假设我们有一个二进制浮点数,格式为1位符号位,8位指数部分,23位尾数部分。一个浮点数的值计算方式大致为:(-1)^符号位 × 1.尾数部分 × 2^(指数部分 - 偏移值)。

torch_dtype

在PyTorch中,torch_dtype参数是一个用于指定张量(tensor)数据类型的选项,通常可用以下取值:

  • auto:自动判断,读取模型目录的config.json中配置的torch_dtype值,如果是bfloat16且当前硬件支持bfloat16,则优先使用这种;判断是否支持float16,不支持才使用float32。
  • torch.bfloat16:1位符号位、8位指数部分、7位尾数,此种类型牺牲了一些精度,但保持了与单精度float32相同的指数范围,这在处理大范围数值和保持动态范围方面非常有利。
  • torch.float16:标准的16位浮点数格式,由1位符号位、5位指数部分、10位小数部分组成。与bfloat16相比,它有更精细的尾数精度,但在指数范围上较窄,这意味着它在处理非常大或非常小的数值时可能不够稳定。
  • torch.float32:单精度浮点数,由1位符号位、8位指数部分(使用偏移编码,偏移值为127)和23位尾数部分组成。
  • torch.float64:由1位符号位、11位指数部分(使用偏移编码,偏移值为1023)和52位尾数部分组成。
  • 其它:torch.int8, unit8, int32, int64等类型。

chat template

基础模型只提供了completion能力,在大模型发展的过程中,很多公司都推出了针对聊天的chat模型,这类模型将用户分为了system, user, assistant角色,将不同角色的输入内容用特定的token分隔开,使得模型能够更好的理解上下文,给出更精确的答复。

此外,模型在训练的时候,就提供了一些数据集,使得system中提供的消息优先级高于user,多模型的安全性也有一定的帮助(避免用户在user message中改变system message对于模型的一些设置)

transformers框架已经原生支持了这一模式,通过执行以下代码帮助拼接prompt

from transformers import AutoModelForCausalLM, AutoTokenizertokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen1.5-7B-Chat")
prompt = "Give me a short introduction to large language model."
messages = [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": prompt}
]
# 这样不进行tokenize测试下效果(直接输出拼接后的string)
# 如果想直接进行tokenize,需要传递参数:tokenize=True(默认就是), return_tensors='pt'
text = tokenizer.apply_chat_template(messages,tokenize=False,add_generation_prompt=True
)

以上代码会去加载模型目录的tokenizer_config.json文件,读取需要的信息,这里以qwen1.5模型为例,需要读取json中的以下字段:

{"chat_template": "{% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n' }}{% endif %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
}

由于指定了add_generation_prompt=True,生成的拼接后的字符串如下:

<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
Give me a short introduction to large language model.<|im_end|>
<|im_start|>assistant

CUDA_VISIBLE_DEVICES

在 PyTorch 中,你可以使用 CUDA_VISIBLE_DEVICES 环境变量来指定可见的 GPU 设备。这个环境变量的值是一个以逗号分隔的 GPU 设备索引列表,可以是单个索引,也可以是多个索引组成的列表。

export CUDA_VISIBLE_DEVICES=0,1  # 只使用 GPU 0 和 GPU 1
export CUDA_VISIBLE_DEVICES=0  # 只使用 GPU 0
# 不使用任何 GPU(将模型运行在 CPU 上)
export CUDA_VISIBLE_DEVICES=
export CUDA_VISIBLE_DEVICES=""  

device_map

device_map 参数是 Hugging Face transformers 库中一种用于控制模型在多个设备上分配的参数。它可以让你明确指定模型的哪些部分应该分配到哪个设备上,以优化模型的性能和内存使用。可以取以下值:

  • auto:自动选择合适的设备来分配模型,根据系统的可用资源和模型的大小智能选择设备。目前auto和balanced效果一样,但后续可能会修改。
  • balanced:均匀地将模型分配到所有可用设备上,以平衡内存使用。
  • balanced_low_0:将模型均匀地分配到除第一个 GPU 外的所有 GPU 上,并且只将不能放入其他 GPU 的部分放在 GPU 0 上。
  • sequential:将模型层按顺序分配到多个设备上,模型的第一层分配给第一个设备,第二层分配给第二个设备,依此类推。当模型层数超过可用设备数量时,会循环回到第一个设备再次分配模型层。
  • 自定义映射:你还可以提供一个字典,明确指定每个模型层应该分配到哪个设备上。

查看设备分配的情况

# 查看主设备
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
print(model.device)# 查看每一层所在的设备
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
for name, param in model.named_parameters():print(name, param.device)

max_memory

当模型在多个设备上运行时,max_memory 参数可以帮助你限制每个设备上模型的内存使用,以防止内存溢出或超出系统资源限制。你可以提供一个字典,将设备名称(例如 "cuda:0""cuda:1""cpu")映射到相应的最大内存使用量(例如 "10GB""20GB""10240MB"

max_memory = {0:'5GB', 1:'13GB'}
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", max_memory=max_memory)

查看每个卡的gpu使用情况和能分配的最大内存

# 这里的gpu_id是基于CUDA_VISIBLE_DEVICES可见设备,不是实际的卡位置
num_gpus = torch.cuda.device_count()
for gpu_id in range(num_gpus):torch.cuda.set_device(gpu_id)allocated_memory = torch.cuda.memory_allocated(gpu_id)max_memory_allocated = torch.cuda.max_memory_allocated(gpu_id)print(f"GPU {gpu_id}: Allocated Memory: {allocated_memory / 1024**3:.2f} GB, Max Allocated Memory: {max_memory_allocated / 1024**3:.2f} GB")

下载模型

驱动下载:https://www.nvidia.com/Download/index.aspx?lang=en-us

可以通过以下网站搜索模型:

  • https://huggingface.co (基于transformers架构模型的官方地址,一般新模型这里第一时间商家,可能需要翻墙)

  • https://hf-mirror.com (huggingface的镜像站)

  • https://www.modelscope.cn (阿里云推出的一个模型开放平台,国内下载速度较快,但是更新可能不是太及时)

设置pip镜像站:export PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple

运行模型的时候,如果模型不存在会自动去下载模型,你也可以提前通过以下方式将模型下载好:

通过hugging face下载

pip install huggingface_hub
# 设置镜像站
export HF_ENDPOINT=https://hf-mirror.com
# 修改模型下载的目录(可选,默认是:~/.cache/huggingface/hub)
export HF_HUB_CACHE=/data/modelscope
# 需要授权的仓库需要登录(可选:https://huggingface.co/settings/tokens)
huggingface-cli login --token <your hugging face token>
# 下载qwen1.5-7b-chat模型
huggingface-cli download --resume-download Qwen/Qwen1.5-7B-Chat

通过modelscope下载

pip install modelscope
# 修改模型下载的目录(可选,默认是:~/.modelscope)
export MODELSCOPE_CACHE=/data/modelscope# python3
from modelscope import snapshot_download
model_dir = snapshot_download('qwen/Qwen1.5-7B-Chat')

运行模型

为了支持模型的版本控制、快速下载和增量更新,huggingface下载的模型结构blobsrefssnapshot几个目录:

  • blobs: 这个目录存储了模型的实际权重数据和其他二进制文件。这些文件通常很大,包含了模型训练得到的参数。每个blob可以看作是一个独立的数据块,这样设计有利于分块下载和缓存,特别是在更新模型时,如果只有少量更改,用户只需下载变化的部分。

  • refs: 这部分涉及到版本控制。它存储了指向特定模型版本的引用,包括提交哈希(commit hashes)或者其他版本标识符。这使得用户可以明确地指定使用模型的哪个版本,同时也方便开发者维护模型的历史变更。

  • snapshot: 这个目录通常包含了一个模型版本的快照,它是一个特定时间点模型的状态。快照通常包含了指向blobs中具体权重文件的指针,确保了模型的完整下载和加载。以commit id作为文件夹。

modelscope下载下来的模型文件并没有按照这个规则,它直接存储的是模型的检查点(checkpoint,包含权重文件、状态、配置等),加载modelscope下载的模型,需要导入modelscope相应的包,如:

# huggingface 风格
from transformers import AutoModelForCausalLM, AutoTokenizer# modelscope 风格
from modelscope import AutoModelForCausalLM, AutoTokenizer

也可以直接将模型的目录作为checkpoint_path参数传入,这样huggingface就会作为checkpoint去加载模型,而不是去下载。

import os
from transformers import AutoModelForCausalLM, AutoTokenizer# modelscope下载模型的存储目录,这里以qwen1.5-7b-chat作为示例
checkpoint_path = os.getenv('MODELSCOPE_CACHE') + "/qwen/Qwen1___5-7B-Chat"
model = AutoModelForCausalLM.from_pretrained(checkpoint_path, torch_dtype="auto", device_map="auto").eval()
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)

阻塞运行模型

模型下载页面一般提供了运行模型的测试代码,有些在github仓库中提供了运行示例。

from transformers import AutoModelForCausalLM, AutoTokenizer
device = "cuda"  # the device to load the model ontocheckpoint_path = "Qwen/Qwen1.5-7B-Chat"
model = AutoModelForCausalLM.from_pretrained(checkpoint_path,torch_dtype="auto",device_map="auto"
).eval()
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)prompt = "Give me a short introduction to large language model."
messages = [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": prompt}
]
text = tokenizer.apply_chat_template(messages,tokenize=False,add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(device)generated_ids = model.generate(model_inputs.input_ids,max_new_tokens=512
)
generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)

流式运行模型

from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from threading import Thread
device = "cuda"  # the device to load the model ontocheckpoint_path = "Qwen/Qwen1.5-7B-Chat"
model = AutoModelForCausalLM.from_pretrained(checkpoint_path,torch_dtype="auto",device_map="auto"
).eval()
tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)prompt = "Give me a short introduction to large language model."
messages = [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": prompt}
]
input_ids = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors='pt').to(device)
streamer = TextIteratorStreamer(tokenizer=tokenizer, skip_prompt=True, timeout=60.0, skip_special_tokens=True)generation_kwargs = dict(input_ids=input_ids, streamer=streamer, max_new_tokens=20)
thread = Thread(target=model.generate, kwargs=generation_kwargs)
thread.start()for new_text in streamer:print(new_text, end='', flush=True)

并行推理

通过并行计算来利用多GPU资源分为两种场景:

  • torch.nn.DataParallel:多个GPU上并行处理数据批次(data batches)来加速训练。它在单个节点上工作,自动将输入数据分割成多个部分,每个部分在不同的GPU上进行前向传播,然后将结果合并。它通常通过模型的复制来实现并行计算,每个GPU上都有一个模型副本。
  • model.parallelize:它不仅在数据层面并行,还在模型层面并行,利用了NCCL等库来协调多GPU间通信,可以对模型的不同层进行并行计算。适用于大型模型的训练,尤其是那些单个GPU内存无法容纳的模型,或者需要在多节点多GPU环境下进行分布式训练的情况。
数据批次并行
import osimport torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from threading import Threaddevice = "cuda"  # the device to load the model onto
checkpoint_path = os.getenv('MODELSCOPE_CACHE') + "/qwen/Qwen1___5-7B-Chat"
model = AutoModelForCausalLM.from_pretrained(checkpoint_path,torch_dtype="auto",device_map="auto"
).eval()
if torch.cuda.is_available():num_gpus = torch.cuda.device_count()if num_gpus > 1:print(f"Found {num_gpus} GPUs, model is now parallelized.")model = torch.nn.DataParallel(model)tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)prompt = "Give me a short introduction to large language model."
messages = [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": prompt}
]
input_ids = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors='pt').to(device)
streamer = TextIteratorStreamer(tokenizer=tokenizer, skip_prompt=True, timeout=60.0, skip_special_tokens=True)generation_kwargs = dict(input_ids=input_ids, streamer=streamer, max_new_tokens=512)
thread = Thread(target=model.generate, kwargs=generation_kwargs)
thread.start()for new_text in streamer:print(new_text, end='', flush=True)
模型&数据批次并行

并不是所有的模型都支持这种方式

import osimport torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from threading import Threaddevice = "cuda"  # the device to load the model onto
checkpoint_path = os.getenv('MODELSCOPE_CACHE') + "/qwen/Qwen1___5-7B-Chat"
model = AutoModelForCausalLM.from_pretrained(checkpoint_path,torch_dtype="auto",device_map="auto"
).eval()
if torch.cuda.is_available():num_gpus = torch.cuda.device_count()if num_gpus > 1:print(f"Found {num_gpus} GPUs, model is now parallelized.")model.parallelize()tokenizer = AutoTokenizer.from_pretrained(checkpoint_path)prompt = "Give me a short introduction to large language model."
messages = [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": prompt}
]
input_ids = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors='pt').to(device)
streamer = TextIteratorStreamer(tokenizer=tokenizer, skip_prompt=True, timeout=60.0, skip_special_tokens=True)generation_kwargs = dict(input_ids=input_ids, streamer=streamer, max_new_tokens=512)
thread = Thread(target=model.generate, kwargs=generation_kwargs)
thread.start()for new_text in streamer:print(new_text, end='', flush=True)

相关文章:

  • Git提交和配置命令
  • 每日复盘-20240528
  • 【数据结构】AVL树——平衡二叉搜索树
  • wps能打开caj文件吗?CAJ应该如何打开?caj转pdf
  • TreeMap
  • 第六章 Python-函数基础
  • JS【详解】Map (含Map 和 Object 的区别,Map 的常用 API,Map与Object 的性能对比,Map 的应用场景和不适合的使用场景)
  • mysql 删除重复数据 关联自己 关联子查询 delete
  • 掌握ASPICE标准:汽车软件测试工程师的专业发展路径
  • vue 笔记02
  • C++ | Leetcode C++题解之第117题填充每个节点的下一个右侧节点指针II
  • 大模型中GPTs,Assistants API, 原生API的使用场景?
  • 数据分析中的列与行交换技巧
  • 【Android14 ShellTransitions】(一)开篇
  • 【乐吾乐3D可视化组态编辑器】模型类型与属性
  • [LeetCode] Wiggle Sort
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【刷算法】从上往下打印二叉树
  • 2017年终总结、随想
  • Android 架构优化~MVP 架构改造
  • Git初体验
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • react-native 安卓真机环境搭建
  • React系列之 Redux 架构模式
  • React中的“虫洞”——Context
  • ViewService——一种保证客户端与服务端同步的方法
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 从零开始的无人驾驶 1
  • 从零开始学习部署
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 微服务框架lagom
  • 运行时添加log4j2的appender
  • ​探讨元宇宙和VR虚拟现实之间的区别​
  • ‌[AI问答] Auto-sklearn‌ 与 scikit-learn 区别
  • ###STL(标准模板库)
  • #if和#ifdef区别
  • #微信小程序:微信小程序常见的配置传值
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (06)金属布线——为半导体注入生命的连接
  • (173)FPGA约束:单周期时序分析或默认时序分析
  • (2)从源码角度聊聊Jetpack Navigator的工作流程
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (ibm)Java 语言的 XPath API
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)计算机毕业设计大学生兼职系统
  • (九)One-Wire总线-DS18B20
  • (理论篇)httpmoudle和httphandler一览
  • (十六)串口UART
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (转)原始图像数据和PDF中的图像数据
  • .axf 转化 .bin文件 的方法