让机器读懂 maimai 谱面:配置识别与拟合难度预测

4949 字
25 分钟
让机器读懂 maimai 谱面:配置识别与拟合难度预测

最近在给 Maimai Chart Preview 做谱面分析功能的时候,认真啃了一遍「怎么让机器看懂一张 maimai 谱面」这个问题。这篇就把整套算法讲清楚:先识别谱面里有哪些配置(纵连、转圈、反手……),再预测它的拟合定数,最后自动打出「水谱 / 正常 / 诈称」的标签。

δ < −0.4其余(δ 在 ±0.4 内)δ > +0.4simai 谱面文本解析时点分组 · BPM 分段积分配置标签15 类 + 雷达分48 维特征铰链 · 交互 · 交叉残差岭回归定数预测δ = 体感 − 显示档💧 水谱✅ 正常🔥 诈称
δ < −0.4其余(δ 在 ±0.4 内)δ > +0.4simai 谱面文本解析时点分组 · BPM 分段积分配置标签15 类 + 雷达分48 维特征铰链 · 交互 · 交叉残差岭回归定数预测δ = 体感 − 显示档💧 水谱✅ 正常🔥 诈称

一、问题:显示难度太粗了#

maimai DX 每张谱面只有一个显示难度,比如 13+。但同一个 13+ 里,既有轻松愉快的池塘水谱,也有让人痛不欲生的诈称谱:

水谱正常谱诈称谱
定数12.713.514.2
显示档131313
体感躺着过该什么样什么样痛不欲生

把「显示档」和「实际定数」画成两个轴,同一档谱面在纵轴上散开的样子就很直观了——横轴是官方档位,纵轴是玩家拟合出来的真实定数,同一个档位内部能拉开整整 1.5 的差距:

档位虚高的诈称区真·高难区档位实在的低难区档位虚低的水谱区14 诈称谱13+ 诈称谱13+ 正常谱13+ 水面谱13 诈称谱13 正常谱13 水面谱显示档 12显示档 14+定数 12.5定数 14.5同一显示档位内部的定数离散
档位虚高的诈称区真·高难区档位实在的低难区档位虚低的水谱区14 诈称谱13+ 诈称谱13+ 正常谱13+ 水面谱13 诈称谱13 正常谱13 水面谱显示档 12显示档 14+定数 12.5定数 14.5同一显示档位内部的定数离散

社区(Diving-Fish 查分器)用大量玩家成绩分布拟合出了精确到 0.1 的定数 fit_diff,但那个数字是从玩家表现统计出来的。我们想做的是反过来:只看谱面文本本身(simai 数据),不靠任何玩家成绩,预测出这个定数。

思路不是从零预测,而是以显示难度为基准,学习一个残差:

y^=l⏟显示档基线+β0+xTβ\hat{y} = \underbrace{l}_{\text{显示档基线}} + \beta_0 + \boldsymbol{x}^T\boldsymbol{\beta}

其中 ll 是显示档位中心(整数级取 x.25x.25,+ 级取 x.75x.75,和水鱼的曲线实现对齐)。模型只学「同档内谁偏难、谁偏易」,任务一下子简单很多。

为什么残差这么重要?看数据:

方法预测起点分组 CV MAE
从零绝对回归1.272 + 特征0.734
只用显示档位x.25/x.75x.25 / x.750.207
档位基线 + 特征残差档位基线 + 残差0.164

误差直接砍掉 77%,而基线本身几乎不花钱:

三种建模路线的分组 CV MAE(越低越好)从零绝对回归档位基线+残差(旧版)档位基线+残差(48维)0.80.70.60.50.40.30.20.10MAE
三种建模路线的分组 CV MAE(越低越好)从零绝对回归档位基线+残差(旧版)档位基线+残差(48维)0.80.70.60.50.40.30.20.10MAE

显示档位本身就是一个非常强的先验,谱面特征只需要做「小幅校正」,不需要「取代官方档位」。

二、前置工作:把谱面变成可分析的数字#

2.1 变速谱的时间口径#

最容易踩的坑是时长和 NPS(每秒音符数)的计算。老办法拿一个全局 BPM 直接换算,遇到变速谱会系统性算错。比如一段 120→240 BPM 的谱:同样 4 拍,全局口径算出 2.00s,分段积分只有 1.50s(前 2 拍 1.00s + 后 2 拍 0.50s),误差高达 33%。变速前后慢快组合反过来时误差方向还会翻转,所以这不是一个能靠常数修正的偏差:

同一段 4 拍谱面的时长估算(秒)全局 BPM 口径分段积分口径2.42.221.81.61.41.210.80.60.40.20秒
同一段 4 拍谱面的时长估算(秒)全局 BPM 口径分段积分口径2.42.221.81.61.41.210.80.60.40.20秒

改进后把任意拍区间 [b0,b1][b_0, b_1] 按 BPM 事件切成 KK 段,分段积分:

T(b0,b1)=∑k=1K60⋅ΔbkBPMkT(b_0, b_1) = \sum_{k=1}^{K} \frac{60 \cdot \Delta b_k}{BPM_k}

平均 NPS 用首尾 Note 之间的积分时长;峰值 NPS 则取物量最高的小节、用该小节自己的积分时长换算。相邻重复的 BPM 事件会被合并。

2.2 局部压力:判断「够不够密集」#

后面很多配置的判定都依赖一个前提——「这个形态出现在足够密集的上下文里」。我们对任意拍区间定义局部压力:

P(s,e)=∣N[s,e)∣max⁡(0.25, e−s)⋅BPM(s)120P(s,e) = \frac{|N_{[s,e)}|}{\max(0.25,\ e-s)} \cdot \frac{BPM(s)}{120}

这个量是后面防误报的关键——同样是「对侧双押」这个几何形态:

  • 全谱 60 BPM 悠闲地出现一次 → 压力低 → 不算反手,不误报;
  • 出现在 NPS 7 的密集窗口里 → 压力 ≥ 4.5 → 反手候选,能抓到。

压力值本身就把「形态相同但体感不同」的窗口分开了:

对侧双押在不同上下文下的局部压力60BPM 孤立出现120BPM 中等密度NPS7 密集窗口876543210局部压力 P
对侧双押在不同上下文下的局部压力60BPM 孤立出现120BPM 中等密度NPS7 密集窗口876543210局部压力 P

那条横线就是反手判定的阈值 4.5——只有第三个窗口能过。

三、15 类配置识别#

配置识别是一条三阶段管线:

输入时点 · 位置 · Slide候选生成几何 + 压力区间合并跨小节冷却抑制留强标记输出配置标记集
输入时点 · 位置 · Slide候选生成几何 + 压力区间合并跨小节冷却抑制留强标记输出配置标记集

识别的 15 类标签及核心触发条件:

标签主要结构核心触发条件
纵连同键快速重复≥3 次,间隔 ≤0.5 拍
交互两键 ABAB 交替≥4 Note,间隔 ≤0.5 拍,压力过阈,可跨小节
散打短窗口四个不同落点未进入扫键/交互,压力 ≥5.5
扫键相邻键同向连续移动≥4 Note,方向一致,密度校验
转圈环绕满圈 / 弧线 Slide八键覆盖 + 局部压力 / 曲线路径
大位移短窗口跨键移动距离 ≥3,落点 ≥3,压力 ≥5.5
定拍稳定等间隔序列连续 6 个物件,浮点容差校验
拆弹高阶多押 / 触摸组≥4 键;或 3 键且压力 ≥5.5;Touch ≥3
爆发全谱局部密度峰值max(14, 1.6×均值, P90),局部极大
跳拍非常规细分节奏非 1/4、1/2 落点,或 0.75/0.375/0.1875 拍
错位Slide 等待窗口穿插 Tap延迟窗口内 ≥2 时点,持续跨小节合并
一笔画首尾相接 / 复杂 Slide 路径≥4 连续 Slide,或 S/Z/V 多段路径
反手对侧键同押环形距离 =4 且局部压力 ≥4.5
触摸同时触摸组 / Touch 流同时 Touch ≥2,或 ≥4 时点间隔 ≤1 拍
绝赞段连续 Break 时点≥3 时点间隔 ≤0.75 拍,或 ≥3 重押

这 15 类不是散乱的,它们可以放到同一张「密度 × 强度」地图上。横轴是该配置要求的上下文压力(判定门槛),纵轴是命中后的体感强度贡献:

高压下的高难配置形态明确的高难配置普遍存在的低门槛形态依赖密集上下文的技术配置绝赞段触摸反手一笔画错位跳拍爆发拆弹定拍大位移转圈扫键散打交互纵连低压力门槛高压力门槛低强度贡献高强度贡献15 类配置在「压力门槛 × 强度贡献」上的分布
高压下的高难配置形态明确的高难配置普遍存在的低门槛形态依赖密集上下文的技术配置绝赞段触摸反手一笔画错位跳拍爆发拆弹定拍大位移转圈扫键散打交互纵连低压力门槛高压力门槛低强度贡献高强度贡献15 类配置在「压力门槛 × 强度贡献」上的分布

左下角那四类是「到处都有」的常态形态(定拍、跳拍、触摸、绝赞段),右上角是「又密又难」的核心配置(拆弹、转圈、反手、交互)——后面章节会看到,正是这些标签的误报率最需要压。

挑几个有代表性的展开:

扫键与转圈共用环形几何签名。对相邻按键 a,ba, b,把差值折算到 [−4,4][-4, 4],要求步长绝对值为 1 且方向不变:

5 → 6 → 7 → 8 → 1 步长 +1 方向不变 ✅ 扫键候选
5 → 6 → 7 → 6 → 5 方向反转 ❌ 不是扫键

连续 ≥4 个 Note 且满足速度压力条件标为扫键;步数 ≥8 且覆盖全部八个按键位置则升级为转圈。这样「一整圈星星」和「顺时针扫一段」被统一的一套逻辑区分开。

否是否是否是连续相邻键序列步长 = 1且方向不变?不成环丢弃连续 Note ≥ 4?扫键方向 + 密度校验步数 ≥ 8且覆盖八键?保持:扫键升级:转圈(一整圈星星)
否是否是否是连续相邻键序列步长 = 1且方向不变?不成环丢弃连续 Note ≥ 4?扫键方向 + 密度校验步数 ≥ 8且覆盖八键?保持:扫键升级:转圈(一整圈星星)

错位星星:Slide 有一个等待窗口(起始时点 + 1 拍默认延迟 + 最长路径时长),在这个窗口里穿插的非 Slide 按键就是错位候选:

001122334星星起手(最长路径) 路径推进 错位候选 A 错位候选 B Slide 等待窗口判断为错位的 Tap同时存在的 Slide 本体
001122334星星起手(最长路径) 路径推进 错位候选 A 错位候选 B Slide 等待窗口判断为错位的 Tap同时存在的 Slide 本体

窗口内穿插了至少两个不同时点的 Tap → 错位候选;覆盖三个连续小节则合并为持续错位段。

去噪是最大的一课#

初版规则跑完,全库 5,364 张谱面里「反手」命中 4,629 张、「绝赞段」命中 4,829 张——这种命中率显然没有区分度。收紧后:

标签优化前优化后变化
反手4,6291,640−64.6%
绝赞段4,829809−83.2%
拆弹2,4571,412−42.5%
去噪前后的命中谱面数(全库 5,364 张)反手 前反手 后绝赞段 前绝赞段 后拆弹 前拆弹 后5000450040003500300025002000150010005000命中张数
去噪前后的命中谱面数(全库 5,364 张)反手 前反手 后绝赞段 前绝赞段 后拆弹 前拆弹 后5000450040003500300025002000150010005000命中张数

那条横线是全库总量,初版的三个标签几乎贴着天花板(反手命中 86%、绝赞段命中 90%),等于「永远为真」的规则。核心思想就一句话:几何形态 + 局部压力 + 持续性,三个条件都满足才算数。孤立的跨三键双押不再是反手,连续两个 Break 同押也不再是绝赞段。

四、两组雷达:把配置聚合成风格#

15 类标签是散点,玩家关心的是「这是一张什么谱」。我们把它们聚合成两组雷达分。

配置雷达对每类标签独立打分,综合三个量:

Rk=min⁡(98, 6+min⁡(38, 8log⁡2(nk+1))⏟频次(对数)+min⁡(28, 3sk)⏟最高强度+min⁡(24, 100ckmax⁡(16,L))⏟覆盖率)R_k = \min\left(98,\ 6 + \underbrace{\min(38,\ 8\log_2(n_k+1))}_{\text{频次(对数)}} + \underbrace{\min(28,\ 3s_k)}_{\text{最高强度}} + \underbrace{\min\left(24,\ \frac{100c_k}{\max(16, L)}\right)}_{\text{覆盖率}}\right)
  • nkn_k 是标记数,用对数缩放防止超长谱面靠「自然多」支配分数;
  • sks_k 是最高强度;
  • ckc_k 是所有区间并集的覆盖拍数——区分「来了一次强配置」和「全程持续这个配置」。

三个分项的权重形状完全不同,对数项首段涨得最快然后迅速压平,覆盖率项则是缓慢线性累积:

单类配置雷达分的三个分项贡献(上限)频次分项强度分项覆盖率分项固定基础分4035302520151050分数上限
单类配置雷达分的三个分项贡献(上限)频次分项强度分项覆盖率分项固定基础分4035302520151050分数上限

所以一次「来得猛」的配置主要靠强度分项拉到高位,而「全程都有」的配置靠覆盖率线性堆上去,两者不会互相淹没。

评价雷达输出五个轴,构成如下:

评价轴主要构成
体力谱平均 NPS、有效时长×密度、高密小节比例、Hold 占比
底力谱交互/纵连覆盖、错位与跳拍、大位移/反手、配置多样性
星星谱Slide/Touch 占比、转圈覆盖、一笔画、错位、扫键
键盘谱非 Slide/Touch 占比(结构前提)、纵连/交互覆盖、定拍、高 NPS
高物量平均 NPS、峰值物量、高密小节比例、BPM

这一版修掉了一个老问题。旧版「键盘谱」把位置切换率、大跨度和高 BPM 直接相加,导致几乎所有谱都是高分键盘谱——区分度完全没有。用分布看,理想状态下中位分应该落在量程中段,旧版却整体挤在高分区,新版才把曲线拉开:

键盘谱得分的分布(示意)10分位25分位中位数75分位90分位1009080706050403020100键盘谱得分
键盘谱得分的分布(示意)10分位25分位中位数75分位90分位1009080706050403020100键盘谱得分

两条线分别是旧版和新版:旧版从头到尾都有 60 分起步,新版只有尾巴(真·高强度谱)才冲到 95。区分度回来了。

五、定数预测:48 维特征的残差岭回归#

到预测这一步,模型输入是 48 个手工特征,分七大类:

特征组维度内容
NPS 分段铰链7max⁡(0,nps−k)\max(0, \text{nps}-k),k=1,2,3,4,5,6,8k = 1,2,3,4,5,6,8
密度与构成5峰值 NPS、峰均比、滑条/触摸/绝赞占比
模式标记密度14纵连、转圈、错位……标记次数 ÷ 时长
评价雷达5体力、底力、星星、键盘、高密度
曲目属性3对数时长、BPM 偏移、高速惩罚
难度交互项10lc×npslc\times\text{nps}、lc×jacklc\times\text{jack}、lc2lc^2、lc2×npslc^2\times\text{nps}……
标记×雷达交叉4纵连×键盘、转圈×星星……

喂给标准化岭回归 w=(XTX+λI)−1XTyw = (X^TX + \lambda I)^{-1}X^Ty,输出 fit_diff=base+b+∑wixi\text{fit\_diff} = \text{base} + b + \sum w_i x_i。

几个设计点:

铰链特征:x=max⁡(0,nps−k)x = \max(0, \text{nps} - k),节点取 1,2,3,4,5,6,8。因为 NPS 从 3 涨到 4 和从 7 涨到 8,对手指的压力完全不是一回事:

NPS 铰链特征的斜率随节点递增1234568平均 NPS9876543210相对难度贡献
NPS 铰链特征的斜率随节点递增1234568平均 NPS9876543210相对难度贡献

分段线性让模型为每个密度区间学到独立斜率——图上是折线而不是直线,正是这个意思:每过一个节点,斜率就抬一次,代表「同样涨 1 点 NPS,越高密度越贵」。

难度交互项:令 lc=显示难度−8lc = \text{显示难度} - 8,加入 lc×npslc \times \text{nps}、lc×jacklc \times \text{jack}、lc2lc^2 等十项。这让系数能随难度档自适应——纵连在 11 级和 14 级对定数的影响显然不同。从特征重要性看,排名靠前的几乎全是这些交互项:

排名特征标准化影响
1难度×密度 (lvNps)0.505
2难度区间 (lvCenter)0.324
3难度×体力 (lvStamina)0.210
4难度平方 (lvSquare)0.196
5中密度 (npsHinge3)0.152
Top 5 特征的标准化影响难度×密度难度区间难度×体力难度平方中密度0.60.550.50.450.40.350.30.250.20.150.10.050标准化影响
Top 5 特征的标准化影响难度×密度难度区间难度×体力难度平方中密度0.60.550.50.450.40.350.30.250.20.150.10.050标准化影响

注意第一名 0.505 已经是第二名 1.5 倍以上,而且前四名里有三个带难度符号——说明「同一特征在不同档位权重不同」正是模型捕捉到的核心信号。

交叉项捕捉「组合杀」:纵连×键盘、转圈×星星、滑条占比×星星、交互×技术。纵连本身难,出现在键盘谱里更难;转圈多的星星谱体感明显上调。这种协同效应是单层线性模型本来表达不了的,手工选四个最关键的补上。

岭回归 + 按曲目分组 CV:λ 通过交叉验证从 [0.03,0.1,0.3,1,3,10,30][0.03, 0.1, 0.3, 1, 3, 10, 30] 里选。交叉验证必须按 songId 分组——同一首歌的 BASIC 到 Re 共享旋律、BPM 和风格:

✅ 分组 5-fold(整歌同一 fold)训练其它歌测试songA 全难度真实泛化❌ 随机 K-fold(姊妹谱泄漏)训练songA · BASIC测试songA · MASTER指标虚高
✅ 分组 5-fold(整歌同一 fold)训练其它歌测试songA 全难度真实泛化❌ 随机 K-fold(姊妹谱泄漏)训练songA · BASIC测试songA · MASTER指标虚高

结果#

指标数值
训练样本5,364 张谱面
基线 MAE(直接用显示难度)0.182
分组 CV MAE0.164
CV RMSE0.209
误差 ≤ ±0.388.0%
误差 ≤ ±0.495.2%
误差 ≤ ±0.597.4%

误差随容忍阈值放宽的累积曲线:

预测误差落在 ±e 以内的谱面占比±0.1±0.2±0.3±0.4±0.51009080706050403020100累积占比 %
预测误差落在 ±e 以内的谱面占比±0.1±0.2±0.3±0.4±0.51009080706050403020100累积占比 %

曲线在 ±0.2 之后陡升、±0.4 之后基本压平——换句话说,绝大部分剩余误差都落在 ±0.4 这个「档位判别带」内,正好是我们做水谱/诈称三分类需要的那条线。训练 MAE 与 CV MAE 几乎一致,没有明显过拟合。对一个纯线性、完全可解释、前端零依赖的模型来说,这个精度相当够用了。

六、推理:系数硬编码进前端#

训练完全离线,运行时不需要 Python 也不需要模型服务:

δ < −0.4δ > +0.4elsesimai 文本analyzeChart解析+标记+雷达48 维特征(与训练完全同序)线性预测 fittedLevel配置敌意度修正最多 +0.65bodyLevelδ = bodyLevel − 显示档💧 水谱🔥 诈称✅ 正常
δ < −0.4δ > +0.4elsesimai 文本analyzeChart解析+标记+雷达48 维特征(与训练完全同序)线性预测 fittedLevel配置敌意度修正最多 +0.65bodyLevelδ = bodyLevel − 显示档💧 水谱🔥 诈称✅ 正常

48 个系数直接硬编码在 TypeScript 里,与 Python 模型的一致性由 verify-model.ts 抽样 200 张校验(最大差 ≤0.02)。

三分类的判定边界就是一条数轴上的三个区间:δ < −0.4 判 💧 水谱(偏易),δ > +0.4 判 🔥 诈称(偏难),其余落在 ✅ 正常区间。

线性模型之外还留了一层配置敌意度修正。设 H(l)H(l) 为拍号变化和 9 种棘手模式的加权雷达分(纵连 0.22、拆弹 0.25、错位 0.18、触摸 0.22、转圈/扫键 0.12……),B(l)=−38.07+7.76 lB(l) = -38.07 + 7.76\,l 为该难度下的「平均恶心程度」基线,则超出基线的部分 A=H(l)−B(l)A = H(l) - B(l) 按下式换算成定数修正:

Δ=min⁡(0.65, 0.35A+0.01A2)\Delta = \min\left(0.65,\ 0.35A + 0.01A^{2}\right)
配置敌意度修正量随超出基线的程度增长(平方压缩)01020304050超出基线的敌意度0.70.650.60.550.50.450.40.350.30.250.20.150.10.050定数修正 Δ
配置敌意度修正量随超出基线的程度增长(平方压缩)01020304050超出基线的敌意度0.70.650.60.550.50.450.40.350.30.250.20.150.10.050定数修正 Δ

先线性、后平方、最后卡在 0.65——保证「恶心配置」能加分,但不会让任何单一因素把定数顶飞。这解释了那些物量不夸张、配置却恶心到爆的谱:线性特征表达不了的「恶心人」,就单独修。

最后推理时还返回贡献最大的 5 个特征,前端能直接告诉玩家:

「这张谱被判为诈称 (+0.52)」的归因拆解转圈×星星交叉高密度NPS峰值/均值比拆弹密度错位密度0.350.30.250.20.150.10.050对 δ 的贡献
「这张谱被判为诈称 (+0.52)」的归因拆解转圈×星星交叉高密度NPS峰值/均值比拆弹密度错位密度0.350.30.250.20.150.10.050对 δ 的贡献

算法做出来是给人看的,可解释性不是附加品,是需求。

七、局限#

  • 线性残差模型捕捉不了高阶交互,四个交叉项是手工选的,覆盖有限;
  • 模式检测的阈值是人工调的,标签来自规则而非监督数据——后续计划把社区的「配置纠错」审核结果转化为监督信号,真正算 precision / recall / F1;
  • fit_diff 本身是玩家表现的统计量,游玩人数少的谱面噪声大;
  • 更远的方向是序数回归 / 时序模型,把逐 Note 事件序列编码进模型,同时输出「群体拟合难度」和「个人预期难度」两套指标。

这套实现的价值不在精度天花板,而在于可解释、推理快、确定性、前端零依赖之间取了一个务实的平衡。分析器和训练管线都是我们自己维护的,等到打磨稳定了再考虑开源喵。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
让机器读懂 maimai 谱面:配置识别与拟合难度预测
https://blog.wmc.pub/posts/maimai-chart-analysis/
作者
Milk
发布于
2026-09-16
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
Phigros 4.0.1 第九章客户端全解:从启动到退出,这一章在程序里究竟发生了什么
技术把 Phigros 4.0.1 客户端里第九章的整条链路走完:启动时怎么恢复解锁与密钥、选曲与难度动画、关卡结算如何推进章节进度、Phase 2 换形态、谜题与推演、密码背后的 AES 方案(含 8 个秘密包的解密与加载/卸载)、Hate→DS→Message→Credits 的演出顺序,以及「退出应用」那一处——附 68 个脚本类各自干了什么。
2
Phigros 第九章 ARG 收官:17:00 归零那一刻,与藏在控制台里的致谢
技术Solivault 的倒计时在 2026-10-02 17:00 归零。终点页在归零后两分钟内换了一版:源码从混淆变回明文、版本号从 2.2.0 跳到 2.4.0,并多出一段只在浏览器控制台里打印的制作人员名单。这篇记录我抓到的完整时间窗和那几个藏起来的机制。
3
Solivault 的歌里藏了什么:一次 SSTV 解码实录
技术Solivault 页面改成会播歌之后,我们把音频从 WebCrypto 解密链路里捞出来,发现节拍里藏着一条 SSTV 传输——解出一张 320×256 的角色立绘。
4
Phigros 第九章《穹顶孤舟》网页解谜复盘:从 Chaocipher 到 13 段碎片拼字
技术一条完整可复现的解谜链:Chaocipher 解出隐藏页、PNG 尾部藏图、零宽字符转摩尔斯、Nihilist 解出 JUSTENGAGEWTH、按浏览器指纹分发的 13 段密文,以及 acrostic 竖读出的两个关键词。
5
给 1400 首歌做语义搜索:一个 QQ 机器人里的曲名检索设计
技术字符串三层 + 向量兜底的混合检索架构,以及为什么我们给 1400 首歌拒绝了 faiss。
随机文章随机推荐

评论区

Profile Image of the Author
Milk
AWMC Founder
分类
标签
站点统计
文章
10
分类
3
标签
22
总字数
58,773
运行时长
0 天
最后活动
0 天前
文章目录