基于 LLamafactory 的异步API高效调用实现与速度对比

文章目录

    • 背景
    • 摘要
    • 简介
    • 代码实现
    • 运行结果
    • 速度对比
      • 异步调用速度
      • 同步调用速度

背景

原先经常调用各家的闭源大模型的API,如果使用同步的方式调用,速度会很慢。为了加快 API 的调用速度,决定使用异步调用 API 的方式。

摘要

通过异步方式调用大语言模型 API的方法,相较于传统同步调用方式,异步调用速度提升了约 9.41 倍。利用 LLamafactory 原生数据集加载和自定义异步工具类 AsyncAPICall 实现批量数据推理,支持调用限速和断点恢复。

简介

本文编写的代码,支持原生的 llamafactory 的数据集导入方式。
推理速度远远快于同步的 API 调用方式。基于 langchain_openai.ChatOpenAI 的 invoke 方法实现异步调用。
下述代码的主要工作介绍如下:

  • 使用 LLamafactory 的原生方法加载 数据集;
  • 封装了异步调用工具类 AsyncAPICall,限制API的调用速度,逐块推理,避免程序崩溃导致所有数据丢失;

代码实现

async_call_api.py

# pip install langchain langchain_openaiimport os
import sys
import json
import asyncioimport fire
from tqdm import tqdm
from dataclasses import dataclass
from aiolimiter import AsyncLimiter
from typing import List
import pandas as pd
from langchain_openai import ChatOpenAI
from dotenv import load_dotenvfrom llamafactory.hparams import get_train_args
from llamafactory.extras.constants import IGNORE_INDEX
from llamafactory.data.loader import _get_merged_datasetload_dotenv()class AsyncLLM:def __init__(self,model: str = "gpt-3.5-turbo",base_url: str = "http://localhost:{}/v1/".format(os.environ.get("API_PORT", 8000)),api_key: str = "{}".format(os.environ.get("API_KEY", "0")),num_per_second: int = 6,**kwargs,):self.model = modelself.base_url = base_urlself.api_key = api_keyself.num_per_second = num_per_secondself.limiter = AsyncLimiter(self.num_per_second, 1)self.llm = ChatOpenAI(model=self.model, base_url=self.base_url, api_key=self.api_key, **kwargs)async def __call__(self, text):# 限速async with self.limiter:return await self.llm.ainvoke([text])llm = AsyncLLM(base_url="http://localhost:{}/v1/".format(os.environ.get("API_PORT", 8000)),api_key="{}".format(os.environ.get("API_KEY", "0")),num_per_second=10,
)
llms = [llm]@dataclass
class AsyncAPICall:uid: str = "0"@staticmethodasync def _run_task_with_progress(task, pbar):result = await taskpbar.update(1)return result@staticmethoddef async_run(llms: List[AsyncLLM],data: List[str],keyword: str = "",output_dir: str = "output",chunk_size=500,) -> List[str]:async def infer_chunk(llms: List[AsyncLLM], data: List):results = [llms[i % len(llms)](text) for i, text in enumerate(data)]with tqdm(total=len(results)) as pbar:results = await asyncio.gather(*[AsyncAPICall._run_task_with_progress(task, pbar)for task in results])return resultsidx = 0all_df = []file_exist_skip = Falseuser_confirm = Falsewhile idx < len(data):file_path = os.path.join(output_dir, "tmp", f"{idx}.csv.temp")if os.path.exists(file_path):if not user_confirm:while True:user_response = input(f"Find {file_path} file already exists. Do you want to skip them forever?\ny or Y to skip, n or N to rerun to overwrite: ")if user_response.lower() == "y":user_confirm = Truefile_exist_skip = Truebreakelif user_response.lower() == "n":user_confirm = Truefile_exist_skip = Falsebreakif file_exist_skip:tmp_df = pd.read_csv(file_path)all_df.append(tmp_df)idx += chunk_sizecontinuetmp_data = data[idx : idx + chunk_size]loop = asyncio.get_event_loop()tmp_result = loop.run_until_complete(infer_chunk(llms=llms, data=tmp_data))tmp_result = [item.content for item in tmp_result]tmp_df = pd.DataFrame({"infer": tmp_result})if not os.path.exists(p := os.path.dirname(file_path)):os.makedirs(p, exist_ok=True)tmp_df.to_csv(file_path, index=False)all_df.append(tmp_df)idx += chunk_sizeall_df = pd.concat(all_df)return all_df["infer"]def async_api_infer(model_name_or_path: str = "",eval_dataset: str = "",template: str = "",dataset_dir: str = "data",do_predict: bool = True,predict_with_generate: bool = True,max_samples: int = None,output_dir: str = "output",chunk_size=50,
):if len(sys.argv) == 1:model_args, data_args, training_args, finetuning_args, generating_args = (get_train_args(dict(model_name_or_path=model_name_or_path,dataset_dir=dataset_dir,eval_dataset=eval_dataset,template=template,output_dir=output_dir,do_predict=True,predict_with_generate=True,max_samples=max_samples,)))else:model_args, data_args, training_args, finetuning_args, generating_args = (get_train_args())dataset = _get_merged_dataset(data_args.eval_dataset, model_args, data_args, training_args, "sft")labels = [item[0]["content"] for item in dataset["_response"]]prompts = [item[0]["content"] for item in dataset["_prompt"]]infers = AsyncAPICall.async_run(llms,prompts,chunk_size=chunk_size,output_dir=training_args.output_dir,)if not os.path.exists(training_args.output_dir):os.makedirs(training_args.output_dir, exist_ok=True)output_prediction_file = os.path.join(training_args.output_dir, "generated_predictions.jsonl")with open(output_prediction_file, "w", encoding="utf-8") as writer:res: List[str] = []for text, pred, label in zip(prompts, infers, labels):res.append(json.dumps({"prompt": text, "predict": pred, "label": label},ensure_ascii=False,))writer.write("\n".join(res))if __name__ == "__main__":fire.Fire(async_api_infer)

上述代码支持 yaml 文件:

async.yaml

## model
model_name_or_path: qwen/Qwen2.5-7B-Instruct### method
do_predict: true### dataset
dataset_dir: ../data/
eval_dataset: alpaca_zh_demo
template: qwen### output
output_dir: output
# overwrite_output_dir: true### eval
predict_with_generate: true
max_samples: 100

运行结果

使用下述代码运行:

python async_call_api.py async.yaml

异步调用的API推理结果:

{"prompt": "识别并解释给定列表中的两个科学理论:细胞理论和日心说。", "predict": "细胞理论和日心说是两个重要的科学理论,分别属于生物学和天文学领域。\n\n1. 细胞理论:细胞理论是生物学中的一个基本理论,它描述了所有生命体的基本结构和功能单位——细胞。细胞理论包含以下三个主要观点:\n\n   a. 所有生物体都是由一个或多个细胞组成的。\n   b. 细胞是生命活动的基本单位,负责维持生物体的生命活动。\n   c. 新细胞只能由已存在的细胞分裂产生。\n\n   细胞理论的提出和发展对于生物学研究具有重要意义,它揭示了生物体的基本结构和功能单位,为生物学研究提供了理论基础。\n\n2. 日心说:日心说是一种关于太阳系结构的理论,它描述了太阳在太阳系中的中心位置,而地球和其他行星则围绕太阳旋转。日心说最初由古希腊天文学家阿里斯塔克提出,但直到16世纪,哥白尼重新提出这一理论,并得到了进一步的发展和完善。日心说的提出颠覆了传统的地心说观念,为现代天文学的发展奠定了基础。\n\n这两个科学理论分别在生物学和天文学领域中具有重要的意义,它们揭示了生命体和宇宙的基本结构和运行规律。", "label": "细胞理论是生物科学的一个理论,它认为所有生命体都是由微小的基本单元——细胞所构成。这是生物学的一个基础理论,认为细胞是所有生物的基本结构和功能单位,所有的生物都是由一个或多个细胞组成,细胞只能通过细胞分裂产生新的细胞。这一理论由薛定谔、施瓦内和雪莱于1839年首次提出。\n\n日心说是指太阳是太阳系的中心,也就是说,行星围绕太阳旋转的理论。这个理论打破了传统的地心说观点,认为地球并不是宇宙的中心。日心说的提出者是尼古拉·哥白尼,他在16世纪初发表了他的著作《天体运行论》,阐述了太阳系行星围绕太阳运行的模型,为天文学的发展做出了巨大贡献。"}
{"prompt": "输入三支篮球队的名称并生成一个适当的口号。\n输入:俄克拉荷马城雷霆队,芝加哥公牛队,布鲁克林网队。", "predict": "\"雷霆激战,公牛荣耀,网破天荒,三队齐心创辉煌!\"", "label": "输出: \"雷霆,公牛和网队:各显神通,角逐群雄!\""}
{"prompt": "比较以下两位著名人物,使用特征列表并以句子形式提供输出。\n人物1:阿尔伯特·爱因斯坦\n人物2:斯蒂芬·霍金\n特征:智力、教育、对世界的影响", "predict": "人物1:阿尔伯特·爱因斯坦\n- 智力:极高,以其相对论理论对物理学产生了深远影响。\n- 教育:获得苏黎世联邦理工学院的学位,并在普林斯顿高等研究院担任教授。\n- 对世界的影响:他的相对论理论改变了我们对时间、空间和重力的理解,对现代科技和宇宙学产生了巨大影响。\n\n人物2:斯蒂芬·霍金\n- 智力:极高,尽管患有肌萎缩性脊髓侧索硬化症,但他在宇宙学和黑洞研究方面做出了重要贡献。\n- 教育:获得牛津大学和剑桥大学的学位,并在剑桥大学担任卢卡斯数学教授。\n- 对世界的影响:他使宇宙学和黑洞研究更加普及,通过《时间简史》等书籍向大众解释复杂的科学概念,激励了无数人对科学的兴趣。", "label": "阿尔伯特·爱因斯坦和斯蒂芬·霍金都是拥有极其出色智力的人物。两人都取得过非常高的教育成就,他们推进了科学发展并在世界范围内产生了深远的影响。爱因斯坦以其相对论和质能关系公式而闻名,而霍金以其关于黑洞和宇宙的发现而著称。两位科学家都以其深厚的学识和非凡的贡献影响了世界。"}

在输出结果中, predict 是大模型的推理结果。方便大家对比 predict 和 label,并评估大模型推理的精度。

请添加图片描述

为了避免大模型中途程序崩溃,把原始数据分块进行推理。这样即使程序中途崩溃,也能基于之前保存的分快数据继续推理,而不用重新开始推理。

速度对比

异步调用速度

下面是两个异步调用的进度条:

100%|██████████████████████████████████████████████████████████████████████████████████████████████| 50/50 [00:22<00:00, 2.27it/s]
100%|██████████████████████████████████████████████████████████████████████████████████████████████| 50/50 [00:22<00:00, 2.22it/s]

上述异步实验总共数据为100条数据,分块大小为50,故有2个进度条。100条速度44秒全部处理完成,平均处理速度 每秒处理2.2条数据。

同步调用速度

同步调用 LLM api 的代码很简单,如下所示:

infers = []
for prompt in tqdm(prompts):infers.append(llm.llm.invoke(prompt))

下面是同步调用的进度条:

100%|████████████████████████████████████████████████████████████████████████████████████████████| 100/100 [06:54<00:00, 4.15s/it]

如果使用同步调用,100条数据,总共耗时 6分54秒,平均每条耗时4.15秒。

方法推理100条数据时间
同步6分54秒
异步44秒

对比之下,异步调用比同步调用快了大约 9.41 倍。

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

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

相关文章

贪心算法实例-问题分析(C++)

贪心算法实例-问题分析 饼干分配问题 有一群孩子和一堆饼干&#xff0c;每个小孩都有一个饥饿度&#xff0c;每个饼干都有一个能量值&#xff0c;当饼干的能量值大于等于小孩的饥饿度时&#xff0c;小孩可以吃饱&#xff0c;求解最多有多少个孩子可以吃饱?(注:每个小孩只能吃…

基于ZYNQ-7000系列的FPGA学习笔记7——按键控制蜂鸣器(模块化编写)

基于ZYNQ-7000系列的FPGA学习笔记7——按键控制蜂鸣器&#xff08;模块化编写&#xff09; 1. 实验要求2. 功能分析3. 模块设计4. 波形图4.1 按键消抖模块4.2 按键控制蜂鸣器模块 5.代码编写5.1 rtl代码5.2 测试代码 6. 代码仿真7. 添加约束文件并分析综合 在上期的内容中&…

linux环境GitLab服务部署安装及使用

一、GitLab介绍 GitLab是利用Ruby onRails一个开源的版本管理系统&#xff0c;实现一个自托管的Git项目仓库&#xff0c;可通过Web界面进行访问公开的或者私人项目。 二、GitLab安装 1、先安装相关依赖 yum -y install policycoreutils openssh-server openssh-clients postf…

视频码率到底是什么?详细说明

视频码率&#xff08;Video Bitrate&#xff09;是指在单位时间内&#xff08;通常是每秒&#xff09;传输或处理的视频数据量&#xff0c;用比特&#xff08;bit&#xff09;表示。它通常用来衡量视频文件的压缩程度和质量&#xff0c;码率越高&#xff0c;视频质量越好&#…

企业多套系统如何一步步实现对接完成闭环?

前言 很多企业用着用着突然发现企业内部有很多的系统&#xff0c;有ERP、CRM、MES、PLM各个系统还比较独立上线时间不一致&#xff0c;主数据包袱很重。这期就讲讲面对情况如何实施对接的案例。 客户背景 公司专注于健康科技领域&#xff0c;致力于打造国际化的专业健康管理…

MySQL大小写敏感、MySQL设置字段大小写敏感

文章目录 一、MySQL大小写敏感规则二、设置数据库及表名大小写敏感 2.1、查询库名及表名是否大小写敏感2.2、修改库名及表名大小写敏感 三、MySQL列名大小写不敏感四、lower_case_table_name与校对规则 4.1、验证校对规则影响大小写敏感4.1、验证校对规则影响排序 五、设置字段…

【HarmonyOS开发】超详细的ArkTS入门

安装DevEco Studio和新建项目就不多说了&#xff0c;可以移步官网 就可以把他们拆成这几个部分了&#xff0c;如果看不懂可以暂时忽略下面冒号后面的内容 装饰器&#xff1a;用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊的含义。如上述示例中Entry、Component和St…

如何在本地环境中模拟使用https

1.生成私钥文件&#xff0c;其中out输出路径可以自定义 openssl genrsa -out D:\localhost.key 2048 2 生成证书签名请求&#xff08;CSR&#xff09;&#xff0c;根据第一步正确指定私钥路径&#xff0c;和签名请求 openssl req -new -key D:\localhost.key -out D:\localhos…

认识自定义协议

经过前面的介绍&#xff0c;我们知道TCP/IP协议有一组五层模型&#xff0c;从上往下为应用层、传输层、网络层、数据链路层和物理层&#xff0c;且在网络中传输的数据都必须经过这几层模型的封装和分用&#xff0c;作为程序员&#xff0c;我们最经常打交道的就是应用层。程序员…

在Node.js局域网调试https的Vue项目

需求&#xff1a; 最近在测试在网页端&#xff08;HTML5&#xff09;调用移动设备的定位等权限功能&#xff0c;发现某些功能是必须保证域名在https下的否则会出现不正常现象。 解决&#xff1a; 1.在线生成和证书 访问&#xff1a;CSR文件生成工具-中国数字证书CHINASSL …

接口性能优化宝典:解决性能瓶颈的策略与实践

目录 一、直面索引 &#xff08;一&#xff09;索引优化的常见场景 &#xff08;二&#xff09;如何检查索引的使用情况 &#xff08;三&#xff09;如何避免索引失效 &#xff08;四&#xff09;强制选择索引 二、提升 SQL 执行效率 &#xff08;一&#xff09;避免不必…

linux 系列服务器 高并发下ulimit优化文档

系统输入 ulimit -a 结果如下 解除 Linux 系统的最大进程数 要解除或提高 Linux 系统的最大进程数&#xff0c;可以修改 ulimit 设置和 /etc/security/limits.conf 文件中的限制。 临时修改 ulimit 设置 可以使用 ulimit 命令来查看和修改当前会话的最大进程数&#xff1a; 查…

LinuxTCP编程详解

目录 一、创建套接字 二、绑定套接字 示例 三、监听套接字 四、等待套接字 五、服务器端示例 六、连接套接字 七、客户端示例 八、Send和Recv C/S模式&#xff1a;Client客户端、Server服务器 TCP编程基于socket套接字实现&#xff0c;因此也习惯称为Socket编程 一、…

我的后疫情时代DevOps

看了一下上一篇博文写作时间是四月份&#xff0c;一晃眼已经快八个月了&#xff0c;这段时间解决了什么&#xff1f;好像没什么起眼的事情&#xff0c;只有两件事情印象深刻&#xff1a; 1&#xff09;没钱&#xff1b; 2&#xff09;裁员。 如果你所在的单位是私营企业&#x…

增量预训练网络安全大模型的一次尝试

一、背景 探索使用网络安全知识&#xff0c;对开源基模型进行增强&#xff0c;评估是否能使基模型在网络安全领域表现出更好地专业度。 项目基于云起无垠SecGPT开源项目&#xff0c;在hugeface开源数据集的基础上&#xff0c;增加了自有预训练数据&#xff0c;进行增量预训练…

多线程---创建及方法

*线程创建的方式&#xff1a; 1.继承Thread类&#xff0c;重写run方法。 2.实现Runnable接口&#xff0c;重写run方法。 实际这两个run方法都重写的是Runnable中的run方法 简化方法&#xff1a; 1.匿名内部类创建Thread 子类对象 Thread thread new Thread(){Overridepub…

365天深度学习训练营-第P7周:马铃薯病害识别(VGG-16复现)

文为「365天深度学习训练营」内部文章 参考本文所写记录性文章&#xff0c;请在文章开头带上「&#x1f449;声明」 &#x1f37a; 要求&#xff1a; 自己搭建VGG-16网络框架【达成√】调用官方的VGG-16网络框架【达成√】如何查看模型的参数量以及相关指标【达成√】 &#…

【联表查询】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…

诗集鉴赏别有韵味——单例模式与工厂模式的浪漫邂逅

原文节选&#xff0c;出自两汉乐府诗集的《陌上桑》 日出东南隅&#xff0c;照我秦氏楼。秦氏有好女&#xff0c;自名为罗敷。罗敷喜蚕桑&#xff0c;采桑城南隅。青丝为笼系&#xff0c;桂枝为笼钩。头上倭堕髻&#xff0c;耳中明月珠。缃绮为下裙&#xff0c;紫绮为上襦。行者…

重学SpringBoot3-RestTemplate配置与使用详解

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-RestTemplate配置与使用详解 1. 简介2. 环境要求3. 基础配置3.1 添加依赖3.2 RestTemplate配置类 4. 高级配置4.1 自定义连接池配置4.2 错误处理配置 5.…