如何在 Huggingface 中不保存文件就向多模态聊天机器人传递数据?
可以。使用本地 Hugging Face Transformers 推理时,可以直接把内存中的图片对象、音频波形或视频帧传给模型的 processor,无须先保存成媒体文件。支持哪些数据类型、使用什么参数,取决于模型、processor 和 Transformers 版本。
调用远程推理 API 时,则需要按服务端协议传输数据,不能直接发送 Python 对象。
为什么示例中经常使用路径或 URL?
路径和 URL 便于演示媒体数据的获取过程。processor 仍需对媒体内容进行解码和预处理,再转换成模型需要的张量。多模态聊天模板和媒体预处理通常由 AutoProcessor 加载的 processor 负责。官方多模态聊天文档
数据已经在内存中时,可以采用两种方式:
- 如果 processor 支持从
messages中读取媒体对象,就直接填入对象。 - 先生成聊天提示词,再通过 processor 的媒体参数传入实际数据。
部分模型无法直接从聊天消息中提取内存数据,也可以使用第二种方式。
音频:通过内存缓冲区传入波形
以 Qwen/Qwen2-Audio-7B-Instruct 为例,可以用 BytesIO 包装内存中的音频字节,解码成 NumPy 波形后传给 processor。
以下示例使用 Transformers v4.57.1 的接口,音频参数名为 audios。运行时需要安装对应版本的 transformers,以及 PyTorch、librosa 和音频解码依赖。使用 device_map="auto" 还需要安装 accelerate。Qwen2Audio v4.57.1 文档
from io import BytesIO
import librosa
from transformers import AutoProcessor, Qwen2AudioForConditionalGeneration
model_id = "Qwen/Qwen2-Audio-7B-Instruct"
# 在应用初始化时加载,供后续请求复用。
processor = AutoProcessor.from_pretrained(model_id)
model = Qwen2AudioForConditionalGeneration.from_pretrained(
model_id,
device_map="auto",
)
def answer_audio(audio_bytes: bytes) -> str:
# audio_bytes 是内存中的 WAV、MP3 等编码数据。
target_sr = processor.feature_extractor.sampling_rate
waveform, _ = librosa.load(
BytesIO(audio_bytes),
sr=target_sr,
mono=True,
)
# 此处只生成提示词,音频数据通过下面的 audios 参数传入。
messages = [
{
"role": "user",
"content": [
{"type": "audio"},
{"type": "text", "text": "What do you hear in this audio?"},
],
}
]
prompt = processor.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
inputs = processor(
text=prompt,
audios=[waveform],
sampling_rate=target_sr,
padding=True,
return_tensors="pt",
).to(model.device)
output_ids = model.generate(**inputs, max_new_tokens=128)
new_ids = output_ids[:, inputs["input_ids"].shape[1]:]
return processor.batch_decode(
new_ids,
skip_special_tokens=True,
)[0]
调用时,将上传接口、网络响应或其他数据源提供的 bytes 传给 answer_audio(),无须创建临时音频文件。
处理前需要区分两类数据:
- WAV、MP3 等编码后的字节,需要先解码。
- 录音设备直接提供的波形数组,确认声道、采样率和数值格式符合要求后,就可以直接传给 processor。
设置 sampling_rate 参数不会自动对已有数组进行重采样。示例中的重采样由 librosa.load(..., sr=target_sr) 完成。
参数名也要与安装版本对应。上述版本使用 audios,当前 main 文档中的 Qwen2Audio 示例使用 audio,实际调用应以安装版本的接口为准。当前 Qwen2Audio 文档
图片:直接传入 PIL 对象
如果 processor 支持直接读取图片对象,可以将图片放在消息的 image 字段中。Transformers 的通用处理逻辑会读取该字段,具体模型仍需支持图片输入。Processor 实现
下面假设 image_bytes 是内存中的图片字节,processor 已加载为对应视觉聊天模型的 processor:
from io import BytesIO
from PIL import Image
image = Image.open(BytesIO(image_bytes)).convert("RGB")
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": image},
{"type": "text", "text": "Describe this image."},
],
}
]
inputs = processor.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt",
)
如果已有 PIL.Image.Image 对象,可以直接使用,省去 BytesIO 和图片解码步骤。这里需要使用视觉模型的 processor,不能沿用前面的 Qwen2-Audio processor。
视频:传入已经解码的视频帧
支持视频输入的 processor 也可以接收已解码的视频数据。官方文档给出了通过 {"type": "video", "video": video_array} 传入内存对象的用法。视频输入文档
下面假设 frames 是按时间顺序排列、尺寸一致的 RGB NumPy 图像列表:
import numpy as np
# 常见布局:(num_frames, height, width, 3)
video_array = np.stack(frames, axis=0)
messages = [
{
"role": "user",
"content": [
{"type": "video", "video": video_array},
{"type": "text", "text": "Summarize this video."},
],
}
]
随后,用对应视频模型的 processor.apply_chat_template() 生成输入。部分模型还需要帧率或时间戳等信息,需按模型文档提供;仅有帧数组未必能保留完整的时间信息。
MP4 等视频的原始字节不能与已解码的帧数组直接互换,仍需通过支持内存输入的解码器处理。
使用时还要注意什么?
messages 的格式并非所有 Hugging Face 接口通用的协议。本地 Transformers、模型专用辅助库和远程聊天 API 可能接受不同字段。应使用接口明确支持的媒体字段或参数,不要直接把 url 的值替换成 bytes、NumPy 数组或 BytesIO。
多模态模型支持的输入范围也不同。支持图片的聊天模型不一定支持音频或视频;即使是同一类媒体,不同 processor 接受的对象类型也可能不同。
调用远程 API 时,通常需要使用接口支持的 Base64、data URL、二进制上传或其他传输格式。这些方式都需要服务端明确支持。本地 processor 能接收 PIL 对象,并不代表远程接口也能接收。
省去中间文件可以减少媒体文件的读写,解码、重采样、图像预处理和张量传输仍然需要完成。处理长音频和视频时,还要控制内存占用。以上示例省去了业务媒体临时文件;from_pretrained() 下载模型和配置时,仍可能写入本地缓存。
备注:内容仅供参考。
