简介:这份资源面向希望在Windows平台搭建金融AI开发环境的研究者与量化爱好者,提供清华团队开源Kronos金融K线基础模型的本地部署源码。Kronos是首个针对金融烛台图的开源基础模型,训练数据覆盖全球45个以上交易所,采用两阶段框架设计,适合具备一定Python基础、想深入理解模型加载与推理流程的中高级用户。压缩包共4个文件,约9KB,包含Python脚本、inscode配置、HTML页面及gitignore文件,分别用于CUDA加速验证、项目配置与WebUI交互界面,结构精简便于快速上手。目前已有223人学习下载。通过这份源码,读者可完成从环境准备、依赖安装到模型加载与预测测试的完整链路,掌握GPU加速验证与数据准备的关键思路,为后续量化研究与策略测试打下可复用的工程基础。
1. Windows 上把 Kronos 金融模型跑起来:先搞清楚它到底吃什么、吐什么
Kronos 是一个面向金融时序的开源基础模型,核心能力是把 K 线序列当成一种语言来做自回归预测——输入一段 OHLCV 历史,输出未来若干根 K 线的概率分布。很多人第一次听到「金融大模型」会以为要喂新闻、财报、宏观数据,其实 Kronos 的输入非常窄:就是标准化的 K 线序列,加上时间戳和成交量。它不预测涨跌方向,它预测的是下一根 K 线的完整形态(开高低收量),方向是你自己从分布里读出来的。
在 Windows 上部署这件事,难点不在模型本身,而在环境。金融时序模型普遍依赖 PyTorch、CUDA、pandas、numpy 这一套,而 Windows 的路径分隔符、编码、CUDA 版本对齐、conda 与 pip 混用,每一个都能让你卡半天。这篇笔记面向两类人:一是有 Python 基础、想在本地跑通推理验证 Kronos 效果的量化从业者;二是想把 Kronos 接进自己回测或实盘流程、需要知道工程边界在哪的工程师。下面从环境、数据、推理、踩坑到进阶,一步步拆。
2. 环境准备:Windows 下 conda + CUDA 的最小可用组合
2.1 为什么不用系统 Python 直接 pip install
Windows 上直接拿系统 Python 装 PyTorch 是最常见的翻车起点。原因有三个:第一,系统 Python 往往被多个软件共用,pip 装多了会污染全局 site-packages,后面某个软件升级依赖直接冲突;第二,CUDA 版本的 PyTorch 需要和显卡驱动、cuDNN 对齐,系统 Python 里如果已经装了 CPU 版 torch,再装 GPU 版不会自动覆盖,而是共存,import 时加载哪个全看路径顺序,属于玄学;第三,Kronos 这类模型通常要求 Python 3.10 左右,系统 Python 版本不一定匹配。
我一般用 Miniconda 建独立环境,这是 Windows 上最省心的做法。先确认显卡驱动支持的最高 CUDA 版本:
nvidia-smi输出右上角会显示CUDA Version: 12.x,这个数字是驱动支持的上限,不是你必须装的版本。PyTorch 官方 wheel 一般滞后一到两个小版本,所以驱动显示 12.4,装 cu121 的 torch 通常没问题。
2.2 建环境、装依赖的具体命令
conda create -n kronos python=3.10 -y conda activate kronos # 先装 PyTorch,注意 --index-url 指向 cu121 而不是默认源 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # 再装金融时序常用依赖 pip install numpy pandas scipy matplotlib tqdm pip install pyyaml einops safetensors这里有几个参数值得说清楚。--index-url必须显式指定,否则 pip 默认从 PyPI 拉,拿到的是 CPU 版 torch,装完torch.cuda.is_available()返回 False,很多人到这一步才发现白装。einops和safetensors是 Kronos 权重加载常见的依赖,前者做张量重排,后者是 HuggingFace 推的安全权重格式,比 pickle 快且不会执行任意代码。
装完立刻验证:
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU only")如果第二行是 False,先别急着怀疑模型,九成是 torch 装成了 CPU 版。卸载重装:pip uninstall torch -y然后带上 index-url 重来。如果显示 True 但显存报错,检查是不是被其他进程占了,Windows 上任务管理器看专用 GPU 内存。
2.3 项目源码的目录结构与放置习惯
拿到 Kronos 项目源码后,别直接扔在桌面或中文路径下。Windows 对中文路径和空格的处理在 Python 某些库(尤其是涉及 C 扩展的)里会出问题。我习惯放在D:\projects\kronos这种纯英文短路径下。典型目录长这样:
kronos/ configs/ # 模型配置 yaml models/ # 网络结构定义 weights/ # 预训练权重 data/ # 数据与缓存 scripts/ # 推理、训练入口 requirements.txt先看requirements.txt里有没有钉死版本的包,如果有torch==2.x.x这种,以它为准,别用我上面给的通用命令。金融模型的作者经常在特定 torch 版本上验证过,跨版本可能有算子行为差异。
3. 数据管道:把 OHLCV 喂成 Kronos 认识的张量
3.1 Kronos 的输入格式到底是什么
Kronos 把每根 K 线编码成一个向量,通常包含 open、high、low、close、volume 五个通道,外加时间特征(比如小时、星期几)。关键在于归一化方式:金融价格是非平稳的,直接喂原始价格,模型在训练集外的尺度上会崩。常见做法是对每个窗口做 z-score 或者用对数收益率。
我一般会先确认源码里dataset或preprocess相关文件用的是哪种归一化。如果是 z-score,那推理时也必须用同样的均值和标准差,不能每段窗口各自算——这是最容易埋的坑,训练时用全局统计量,推理时用局部统计量,结果就是预测分布整体偏移。
3.2 从 CSV 到模型输入的完整脚本
假设你有一份 BTCUSDT 的 1 小时 K 线 CSV,列名是timestamp,open,high,low,close,volume:
import pandas as pd import numpy as np import torch def load_ohlcv(csv_path, seq_len=512): df = pd.read_csv(csv_path) df['timestamp'] = pd.to_datetime(df['timestamp']) df = df.sort_values('timestamp').reset_index(drop=True) # 五个通道,顺序必须和训练时一致 cols = ['open', 'high', 'low', 'close', 'volume'] arr = df[cols].values.astype(np.float32) # 全局 z-score:均值和标准差从训练集统计,这里假设已存在 stats.npz stats = np.load('data/stats.npz') mean, std = stats['mean'], stats['std'] arr = (arr - mean) / (std + 1e-8) # 滑窗切序列 windows = [] for i in range(len(arr) - seq_len): windows.append(arr[i:i + seq_len]) x = torch.tensor(np.stack(windows), dtype=torch.float32) return x, df x, raw = load_ohlcv('data/btc_1h.csv', seq_len=512) print(x.shape) # (N, 512, 5)逻辑说明:seq_len=512是上下文长度,Kronos 常见配置在 256 到 1024 之间,越长显存占用越大,512 是 8GB 显存能跑推理的稳妥值。stats.npz里的 mean/std 必须是训练时保存的,如果你没有,就得自己在一段足够长的历史数据上算,并且固定下来。arr[i:i+seq_len]是左闭右开,最后一个窗口的右边界是i+seq_len,循环上界写len(arr)-seq_len保证不越界。
参数怎么改:如果你的数据是日线,seq_len 可以降到 128 或 256,因为日线样本少,长窗口容易过拟合。如果是分钟线,样本多,可以上 1024,但显存要 16GB 以上。volume 通道如果量级和其他四个差太多,z-score 之后仍然可能主导梯度,可以考虑对 volume 单独做 log1p 再归一化,但前提是训练时也这么做的。
3.3 时间特征与对齐问题
有些 Kronos 变体会额外输入时间编码,比如hour_of_day、day_of_week。如果你的 CSV 时间戳是 UTC,而训练数据是某个交易所本地时间,这里就会错位。金融时序里时间错位不会报错,只会让模型预测变差,属于最难查的一类问题。我的习惯是:在数据加载函数里打印前三条和后三条时间戳,和源码里 dataset 的默认时区对一遍,确认无误再往下走。
4. 推理与验证:加载权重、跑出预测、判断结果是否可信
4.1 权重加载的两种格式与常见报错
Kronos 权重常见两种:.pt/.pth(PyTorch 原生)和.safetensors。前者用torch.load,后者用safetensors.torch.load_file。Windows 上torch.load有个经典报错:UnpicklingError: invalid load key,八成是文件下载不完整,或者被浏览器改成了.zip。先看文件大小对不对,再用file命令(Git Bash 里有)确认类型。
import torch from safetensors.torch import load_file def load_weights(path, device='cuda'): if path.endswith('.safetensors'): state = load_file(path, device=device) else: state = torch.load(path, map_location=device) return state state = load_weights('weights/kronos_base.safetensors') print(len(state), list(state.keys())[:5])map_location=device很关键。如果不写,torch 会尝试按权重保存时的设备加载,如果保存时在 cuda:0 而你现在只有 cuda:1,就会报设备不匹配。Windows 单卡用户一般写cuda或cpu即可。
4.2 跑一次完整推理
import torch from models.kronos import KronosModel # 按源码实际类名替换 device = 'cuda' if torch.cuda.is_available() else 'cpu' model = KronosModel.from_config('configs/kronos_base.yaml') state = load_weights('weights/kronos_base.safetensors', device) model.load_state_dict(state, strict=False) model.to(device).eval() with torch.no_grad(): x = x[:4].to(device) # 先跑 4 条,省显存 pred = model(x) # 形状取决于源码,常见 (B, horizon, 5) print(pred.shape)strict=False是权宜之计:如果源码的模型结构和权重有细微差异(比如多了个 buffer),strict=True 会直接报 key 不匹配。但 strict=False 也有风险,它会把不匹配的层随机初始化,推理结果可能完全错。正确做法是打印model.state_dict().keys()和state.keys()的差集,确认缺的只是无关紧要的 buffer,再决定是否放行。
model.eval()和torch.no_grad()两个都不能省。前者关掉 dropout 和 batchnorm 的训练行为,后者省显存。金融模型里如果有 dropout,不关的话每次推理结果都不一样,你会以为模型不稳定,其实是自己在采样。
4.3 怎么判断预测结果是不是「能用」
跑出pred之后,别急着画图。先做三件事:第一,把预测的 close 反归一化,和真实 close 放一起看量级对不对,如果预测值在 0 附近而真实价格是几万,说明归一化参数用错了;第二,看预测的 high 是否大于等于 low,open/close 是否在 high/low 之间,如果违反这个基本约束,说明模型输出层没有做约束,需要你自己 clip;第三,算一下预测窗口内的波动率和历史波动率的比值,如果差一个数量级,模型大概率没收敛或者权重加载错了。
这三步做完,你才能说「Kronos 在我这份数据上跑通了」。跑通不等于有效,有效性要靠回测,那是另一个话题。
5. 避坑与排查:Windows 部署 Kronos 最常见的 5 个翻车点
5.1 现象:torch.cuda.is_available()返回 False,但 nvidia-smi 正常
原因:装的是 CPU 版 torch,或者 CUDA 版本和驱动不匹配。Windows 上 pip 默认源给的是 CPU wheel,必须显式指定 index-url。另一种情况是驱动太旧,比如驱动只支持 CUDA 11.x,你装了 cu121 的 torch,就会静默回退到 CPU。
解决:pip uninstall torch torchvision torchaudio -y,然后按nvidia-smi显示的版本选对应的 index-url 重装。装完在 Python 里打印torch.version.cuda,确认不是 None。
5.2 现象:推理时显存溢出,报CUDA out of memory
原因:seq_len 太长、batch 太大,或者没有用torch.no_grad()。Kronos 这类自回归模型在推理时如果实现成了逐 token 生成,显存会随生成长度线性增长。
解决:先把 batch 降到 1,seq_len 降到 256,确认能跑再往上加。如果源码支持 KV cache,打开它。Windows 上还要注意,任务管理器显示的「共享 GPU 内存」不算可用显存,只有「专用 GPU 内存」才是。
5.3 现象:预测结果每次都不一样
原因:模型没调eval(),dropout 还在生效;或者数据加载里有随机 shuffle;或者用了torch.multinomial采样而没有固定随机种子。
解决:model.eval(),torch.manual_seed(42),np.random.seed(42),数据加载shuffle=False。如果源码本身是概率采样输出,那结果不一样是正常的,你要看的是分布的统计量而不是单次样本。
5.4 现象:中文路径下报UnicodeDecodeError或找不到文件
原因:Windows 默认编码是 GBK,Python 读文件时如果路径含中文,某些库会用系统编码打开,导致解码失败。pandas 读 CSV 时如果文件是 UTF-8 而系统是 GBK,也会报错。
解决:项目放纯英文路径。读 CSV 显式写pd.read_csv(path, encoding='utf-8')。如果源码里硬编码了路径分隔符/,Windows 上一般也能用,但保险起见用os.path.join。
5.5 现象:load_state_dict报大量 missing keys 和 unexpected keys
原因:模型定义和权重版本不匹配。可能是你拿的源码是旧版,权重是新版,或者反过来。也可能是源码里模型类有多个变体,你实例化错了。
解决:打印两边的 key 集合,看 missing 的是不是集中在某几个模块。如果 missing 的是head或embed这种关键层,别用 strict=False 硬跑,去翻源码的 README 或 config,确认模型类和权重对应关系。这一步没有捷径,只能对。
6. 进阶:把 Kronos 接进回测流程的几个实用技巧
跑通单次推理只是起点。真正要判断 Kronos 值不值得投入,得把它接进回测,看预测分布能不能转成可执行的信号。我一般这么做:把 Kronos 输出的未来 N 根 K 线分布,取 close 的中位数作为点预测,取 10% 和 90% 分位数作为区间。如果当前价格在区间下沿以下,且中位数高于当前价,视为偏多信号;反之偏空。这只是最粗的用法,但能快速验证模型输出有没有信息量。
验证方法上,我习惯做一个「打乱测试」:把输入序列的时间顺序随机打乱,再跑一次推理。如果打乱后的预测和原预测差不多,说明模型根本没在利用时序结构,那这个权重或者你的预处理就有问题。这个测试比看 loss 曲线直观得多。
还有一个技巧是控制变量:固定同一段历史,只改 volume 通道的值(比如全部置零),看预测变化多大。如果 volume 置零后预测几乎不变,说明模型对成交量不敏感,那你在实盘里就不必花大力气清洗 volume 数据。这种敏感性分析能帮你把工程精力花在刀刃上。
最后说个我自己的教训。我一开始在 Windows 上跑 Kronos,图省事用了系统 Python,装完 torch 发现是 CPU 版,重装 GPU 版之后又和已有的 numpy 版本冲突,numpy 降级导致 pandas 报错,折腾了一下午。后来老老实实 conda 建环境,所有依赖一次装齐,再没出过这类问题。Windows 上做深度学习,环境隔离不是可选项,是必选项。另外,每次跑出「看起来不错」的结果,先别激动,用打乱测试和反归一化检查过一遍,能省掉很多后面回测时的困惑。希望帮到你。
本文还有配套的精品资源,点击获取