ChatTS的一些理解

news/2025/10/10 22:40:48/文章来源:https://www.cnblogs.com/dingxingdi/p/19134007

Dingxingdi: 请你仔细阅读/workspace/ChatTS里面的文件,理解他们的含义逻辑和结构。现在我需要你告诉我,ChatTS在处理一段既包含文本又包含时间序列的输入的时候,是怎么精确地分别哪些是文本哪些是时序,从而对两者分别进行编码的?

GitHub Copilot: 我先在 ChatTS 代码库里快速检索和阅读与输入解析和编码相关的模块(如 encoding_utils、llm_utils、vllm 相关脚本),以确认它如何区分文本与时序并分别编码。预期结果是找到明确的分割标记、数据结构或预处理函数,以及相应的编码流程。

搜索与 **/*.py 匹配的文件,124 匹配项

我将阅读 ChatTS 中与编码最相关的模块(尤其是 encoding_utils.py、llm_utils.py、以及 vLLM 推理脚本)来确认它如何在混合输入中区分文本与时序并分别编码,预期找出明确的分隔标记、正则或数据结构及编码流程。

读取 [](file:///workspace/ChatTS/chatts/utils/encoding_utils.py)

我会继续阅读 llm_utils.py、inference_tsmllm_vllm.py 和 vllm_stream_qa.py 以找到混合输入的解析与推理流程,确认 协议如何在模型侧被识别与编码。

读取 [](file:///workspace/ChatTS/chatts/utils/llm_utils.py)

我将继续查看 vLLM 侧的时间序列支持 chatts_vllm.py,以及推理脚本 inference_tsmllm_vllm.py 和 vllm_stream_qa.py,以确认如何将 标签绑定到 multi_modal_data.timeseries。

读取 [](file:///workspace/ChatTS/chatts/vllm/chatts_vllm.py#1-1),行 1 到 400

读取 [](file:///workspace/ChatTS/chatts/vllm/chatts_vllm.py#400-400),行 400 到 612

我将查看 ChatTS 推理工具 inference_tsmllm_vllm.py 和 vllm_stream_qa.py,了解如何把 占位符与 multi_modal_data.timeseries 对齐,以及模型前处理是否使用 <ts><ts/>

读取 [](file:///workspace/ChatTS/chatts/utils/inference_tsmllm_vllm.py)

读取 [](file:///workspace/ChatTS/chatts/utils/vllm_stream_qa.py)

搜索 <ts> (**/ChatTS/**)、20 结果的文本

读取 [](file:///workspace/ChatTS/chatts/utils/inference_tsmllm_deepspeed.py)

读取 [](file:///workspace/ChatTS/README.md#1-1),行 1 到 260

我已梳理 ChatTS 里与“混合输入(文本+时间序列)解析与编码”相关的模块,下面结合关键文件讲清它是如何精确区分文本与时序并分别编码、再对齐融合的。

结论速览

  • 文本与时序的“分界”靠两层机制:
    1. 文本里使用专用占位标记对时序插槽定位:<ts><ts/>(以及可选的内嵌数据形式 <ts>...[JSON 数组]...<ts/>)。
    2. 同时将真实的时间序列以结构化字段传入:multi_modal_data.timeseries=[np.ndarray,...](或 HF 的 processor(..., timeseries=...))。
  • 编码时:
    • 文本走普通 tokenizer/chat template 流程,生成 token 序列。
    • 时间序列走“值保留”的数值归一化与补齐(sp/no 等),再按 patch 切分,经 MLP 编成 patch 级别的嵌入。
    • 最后用占位 token(配置里的 ts_token_start_index)把“时序 patch 嵌入”插到文本 token 嵌入中指定的位置,形成统一的输入。

ChatTS 如何识别文本 vs 时间序列

  1. 占位标记与计数
  • 任何要在提示词中插入时序的位置,用成对标记写成:<ts><ts/>
    例如:"TS1 长度 256: <ts><ts/>; TS2 长度 128: <ts><ts/>"
  • 这串标记不会包含数值本身,只负责“数量与位置”的定位。编码与对齐流程会用这些标记确定每段时序的注入点和需要的占位长度。
  • 相关代码:
    • encoding_utils.py: 生成含占位的 prompt(如 no_encoding/sp_encoding 返回 <ts><ts/>)。
    • chatts_vllm.py:
      • Qwen2TSDummyInputsBuilder.get_dummy_text()<ts><ts/> 为每个时序生成“虚拟占位文本”,说明这种标记是 vLLM/Processor 侧约定的“插槽”。
  1. 结构化传参
  • 真正的时间序列以数组形式单独传入推理引擎,而不是放在文本里:
    • vLLM 路径:在请求里传 { "prompt": "...", "multi_modal_data": {"timeseries": [ts1, ts2, ...]} },见 llm_utils.py 的 LLMClient.llm_batch_generate()
    • Transformers 路径:用 AutoProcessor 直接传 processor(text=[...], timeseries=[...]),见 inference_tsmllm_deepspeed.py。
  • 若用户把数值直接写进文本(<ts>...[JSON 数组]...<ts/>),会先解析并“挖出”数组,然后把文本替换成占位标记 <ts><ts/>,再把数组放进 multi_modal_data
    • chatts/utils/vllm_stream_qa.py: extract_and_remove_ts() 用正则抓取 <ts>...<ts/> 之间的 JSON,抽出为数组,并把文本替换为 <ts><ts/>,确保“文本只留占位,数组进入结构化通道”。

这两层机制保证了:

  • 文本里只保留占位信息(位置、数量),不混入原始数据;
  • 时序数据通过专用字段传给处理器/模型编码器。

编码与对齐的完整流程

  1. 文本侧
  • 走普通 tokenizer/chat template:
    • chatts/utils/llm_utils.py: LLMClient._apply_chat_template()
    • vLLM 和 HF 两条路径都会得到包含占位 token 的 input_ids。
  1. 时间序列侧(值保留编码 + 补齐)
  • 归一化与遮罩(Mask):
    • 训练/示例中常用的“sp”编码,会为每个时间步生成“值+mask(=1)”的对,便于变长对齐与有效长度计算;见 README.md“About sp Encoding”和 encoding_utils.sp_encoding() 的思路说明(推理时实际由 AutoProcessor 完成该标准化与打包)。
  • 打包到 HF/vLLM 期望的结构:
    • chatts/vllm/chatts_vllm.py: Qwen2TSMultiModalProcessor._call_hf_processor()timeseries 交给 HF Processor,产出 (ts_tokens, encoded_ts_arrays)
      • encoded_ts_arrays 是带有值与 mask 的张量集合(代码注释:“每个元素是 (value, mask)”)。
      • ts_tokens 是与时序插槽对齐的已分词占位序列。
  • patch 切分与 MLP 编码:
    • TimeSeriesEmbedding.forward() 将每段时序按 patch_size 切块,支持位置编码或位置索引(可选),再过多层 MLP 得到每个 patch 的向量表示;有效长度由最后一维的 mask 求和得到(mask = x[:, :, -1]),自动忽略 padding。
    • 返回每条时序的 patch 嵌入以及每条的 patch 数 patch_cnt
  1. 与文本对齐并融合
  • 占位 token 与 patch 数的匹配:
    • Qwen2TSMultiModalProcessor._get_prompt_updates() 基于 encoded_ts_arrays 推出每条时序的 patch_cnt,如果文本中 <ts><ts/> 转成的占位 token 数不足,就自动“扩充”占位,确保“占位数量 == patch 数量”。
    • 这里用到配置里的占位 token id:config.ts_token_start_index(即“时序起始占位 token”)。
  • 嵌入合并:
    • Qwen2TSForCausalLM.get_input_embeddings() 中,通过 merge_multimodal_embeddings(input_ids, text_embeds, ts_embeds, ts_token_start_index) 将“时序 patch 嵌入”按占位 token 的位置插入到文本嵌入序列中,得到统一的输入。
    • 这一步是“区分编码、精确对齐”的落点:文本与时序先独立编码,再通过占位 token 的位置进行无歧义的拼接。

关键代码指引(可快速定位)

  • 标记与提示拼装:
    • encoding_utils.py(<ts><ts/> 的生产与替换、可选值内嵌 <ts>...<ts/>
    • chatts/utils/vllm_stream_qa.py: extract_and_remove_ts()(从文本里提取数值并替换为占位)
  • vLLM 多模态接入与处理:
    • chatts_vllm.py
      • Processor 与 Parser:Qwen2TSMultiModalProcessor / Qwen2TSDataParser
      • Prompt 占位扩展与替换:_get_prompt_updates()
      • 时序编码器:TimeSeriesEmbedding(patchify + MLP)
      • 融合:Qwen2TSForCausalLM.get_input_embeddings()merge_multimodal_embeddings
  • 推理封装:
    • vLLM 批量:chatts/utils/llm_utils.py: LLMClient.llm_batch_generate()(构造 multi_modal_data.timeseries
    • HF+DeepSpeed:inference_tsmllm_deepspeed.py(AutoProcessor(..., timeseries=...)

小结

  • ChatTS 用 <ts><ts/> 在文本里精确标注“时序插槽”,而真实时序通过结构化字段传入;如果时序被写进文本(<ts>...[JSON]...<ts/>),会先提取出来再把文本改回占位。
  • 编码层面,文本和时序各走各的编码器:文本走 tokenizer;时序走值保留编码+patch MLP。最后以占位 token 为锚,把时序 patch 嵌入插入到文本嵌入序列中,保证两者既独立编码又能严格对齐。这样既不混淆模态边界,又能在一个自回归序列里共同推理。

Dingxingdi: 所以用户的输入一定要是某种符合格式的输入吗?也就是时间序列在用户输入里面一定要符合某种格式,否则无法识别?

GitHub Copilot: 好问题。我来直接说结论,并给出可用的输入格式选项。

  • 是的:要让 ChatTS 把“某一段输入”当作时间序列模态来处理,输入需要满足它约定的标记与结构化形式;否则这段内容只会被当作普通文本,模型不会把它当作时间序列去编码与对齐。
  • 不是必须把数值写进文本里,但必须在文本中放“时序占位符”,并通过结构化字段把数值提供给模型。

以下是三种被支持的方式(任选其一,推荐前两种):

  1. 推荐:文本里放占位,时序用结构化字段传
  • 文本中为每条时序放一个占位对:<ts><ts/>(一个时间序列就是一对)。
  • 同时把真实时序数组通过结构化字段传给推理端:
    • vLLM 路径:传入 {"prompt": "...", "multi_modal_data": {"timeseries": [ts1, ts2, ...]}},见 llm_utils.py。
    • Transformers 路径:AutoProcessor(...). __call__(text=[...], timeseries=[...]),见 inference_tsmllm_deepspeed.py。
  • 要点:占位对的个数必须与传入的时间序列个数一致;每条时序只需要一对 <ts><ts/>,内部会按 patch 自动扩展对齐。
  1. 互动场景(仅限 vllm_stream_qa.py 脚本):文本内内嵌 JSON 数组
  • 你可以在输入里直接写:<ts>[1.2, 0.7, ...]<ts/> 或二维/带 mask 的列表。
  • vllm_stream_qa.py 的 extract_and_remove_ts() 会自动:
    • 用正则提取 <ts>...<ts/> 之间的 JSON 数组并转成 numpy;
    • 把文本中这段替换成占位对 <ts><ts/>
    • 以结构化的 multi_modal_data.timeseries 传入模型。
  • 这让“把数值写进文本里”的用法也能被识别,但仅在该脚本路径下生效。
  1. 仅文本(无占位、无结构化时序)
  • 这时模型只把它当纯文本 LLM 使用,不会加载时序编码器,也无法进行“基于数值序列”的分析。

补充说明与常见陷阱

  • 必需的“格式”是什么?
    • 至少要有“每条时序一对”的占位符:<ts><ts/>(文本侧定位与对齐锚点)。
    • 加上结构化的时序数组(API/processor 侧,以 list 或 numpy 数组传入)。这两者需一一对应。
  • 不能只给占位不传数组,也不能只传数组不放占位。前者会没有可插入的编码,后者则没有插槽可对齐。
  • 多条时序:文本里写多对 <ts><ts/>,结构化里给等量数组;长度不同也支持(依靠 mask 补齐与 patch 化)。
  • 限制:一次输入的时序条数受 limit_mm_per_prompt 限制(代码示例里常见 15 或 50);过短序列(如 <64)虽能解析,但效果在 README 里提示可能较弱。
  • 你不需要在文本里手写归一化或 mask,“值保留编码”会由 Processor/模型内的时序编码器自动完成(见 chatts_vllm.py 的 TimeSeriesEmbeddingQwen2TSMultiModalProcessor)。

简单示例(推荐方式)

  • 文本:"我有两段序列。TS1: <ts><ts/>;TS2: <ts><ts/>。请比较它们的峰值时间差别。"
  • 结构化:timeseries=[np.array([...]), np.array([...])]
  • 推理时 vLLM/Transformers 会:
    • 把文本 token 化;
    • 把时序做值保留编码 + patch 化;
    • 按占位符把时序嵌入插进对应位置,一起送入模型。

结论

  • 要让 ChatTS 识别“这是时序,不是普通文本”,你需要遵守它的“占位符 + 结构化时序”约定;或者在特定脚本(vllm_stream_qa.py)里用 <ts>JSON 数组<ts/>,它会自动转成标准形式。否则它只会把你输入当普通文本处理。

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

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

相关文章

2025秋_9

今天依旧Java

10月10日

上午英语课学第三单元,下午写数据结构作业

[P2201 数列编辑器 // HDU-4699 Editor] 题解

lougu 看不到,遂写博客 题目描述 小 Z 是一个爱好数学的小学生。最近,他在研究一些关于整数数列的性质。为了方便他的研究,小 Z 希望实现一个叫做 "Open Continuous Lines Processor" 的数列编辑器。 一开…

centos网络打流测试 - 指南

centos网络打流测试 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", …

一生一芯学习:基础设施(2)

一生一芯学习:基础设施(2)指令执行的踪迹 - itrace 首先写好一个环形缓冲区的代码,把反汇编的字符串存到环形缓冲区中,然后执行完代码在打印出来。 #include <stdio.h> #include <stdlib.h> #include &…

实验报告3(使用单链表简单实现图书管理系统)

一、实验目的: 使用单链表实现案例2.3的图书管理系统,要求实现查找、插入、删除和计数功能。要求包含主函数,用c语言或者c++实现。 二、实验仪器或设备: 操作系统:Windows11 编程环境:Dev-cpp 5.11 三、算法总体…

【黑马python】2.Python 字符串

参考链接黑马-2.Python 字符串 08-字符串的三种定义方式tbd

FineReport自定义登录系统技术 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

实验报告2(简单实现图书馆管理系统)

一、实验目的:、 实现书上图书馆管理系统 (1) 主函数 (2) 修改:根据指定的ISBN,修改图书的价格 (3) 排序:将图书按照价格由低到高进行排序。 (4) 计数:统计文件中的图书数量 要求:用c语…

实验报告1(switch语句,二维数组)

一、实验目的: 熟练使用switch语句 熟练使用二维数组 二、实验仪器或设备: 操作系统:Windows11 编程环境:Dev-cpp 5.11 三、算法总体设计 (1)项目一:运输公司对用户计算运费 用到的算法的目的:计算并输出基于给…

【实现自己的 kafka!】kafka 的关键概念

kafka 的诞生 现在是在 2000 年代后期,你的名字叫做 Jay Kreps,你就职于 LinkedIn 公司。 LinkedIn 作为社交网络平台,用户规模和数据量现在快速增长,同时内部存在多种数据传递和处理需求,比如用户行为跟踪、日志…

12. 对话框

一、对话框对话框窗口是一个用来完成简单任务或者和用户进行临时交互的顶层窗口,通常用于输入信息、确认信息或者提示信息。Qt Quick 提供了一系列的标准对话框,如 FileDialog、ColorDialog、MessageDialog、FontDia…

2024ICPC区域赛香港站

define时间:#define int long long #define ind long double #define yes cout << "Yes" #define no cout << "No" #define pii pair<long long, long long> #define all(x) (…

AI产品经理要了解的算法有哪些?

中世纪拉丁语“algorismus”指的是用印度数字进行四个基本数学运算——加法,减法,乘法和除法的程序和捷径。后来,术语“算法”被人们用作表示任何逐步的逻辑过程,并成为计算逻辑的核心。 算法的历史可以分为三个阶…

一位印度小哥逆袭成为谷歌数据科学家的心路历程 - 教程

一位印度小哥逆袭成为谷歌数据科学家的心路历程 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&q…

基于selenium的网页自动搜索

第一节 通过简单的百度网页打开学习selenium库的基本功能。1 from selenium import webdriver2 from selenium.webdriver.chrome.service import Service3 from selenium.webdriver.chrome.options import Options4 fr…

MacOS Nginx

查看是否安装:brew info nginx 安装:brew install nginx 卸载:brew uninstall nginx 查看版本:nginx -v 安装目录:/opt/homebrew/Cellar/nginx/1.29.0 (27 files, 2.5MB) 根目录:Docroot is: /opt/homebrew/var/…

缓存的击穿、雪崩、穿透在你项目中的场景是什么

在我们的 OJ 平台中,为了保护数据库、提升响应速度,我设计了一套缓存防护体系: 缓存穿透: 针对恶意请求或不存在的题目 ID,我们用布隆过滤器提前过滤掉无效请求,误判率控制在 0.13% 以下,保护数据库不被大量无效…

[WC2021] 表达式求值

给定一个式子,包含 >,<,? 或者 \([0,m)\) 中的一个数字。其中每个数字代表一个数。 > 代表返回两边的最大值,< 代表返回两边的最小值,? 表示你要在上文的两个符号中选择一个符号替换它。 假设有 \(…

Set集合

无索引 Hashset主注意: LinkedHashset: 存取有顺序其余和hashset一样