diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..daf5560 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +__pycache__/ +*.pyc +.DS_Store +.vscode/ +.cursor/ +.claude/ +.specstory/ +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fdeb136 --- /dev/null +++ b/README.md @@ -0,0 +1,673 @@ +
+ +![logo](./images/logo.png) + +
+ + +
+ +![visitors](https://visitor-badge.laobi.icu/badge?page_id=jingyaogong/minimind-o) +[![GitHub Repo stars](https://img.shields.io/github/stars/jingyaogong/minimind-o?style=social)](https://github.com/jingyaogong/minimind-o/stargazers) +[![GitHub Code License](https://img.shields.io/github/license/jingyaogong/minimind-o?v=1)](LICENSE) +[![GitHub last commit](https://img.shields.io/github/last-commit/jingyaogong/minimind-o)](https://github.com/jingyaogong/minimind-o/commits/master) +[![GitHub pull request](https://img.shields.io/badge/PRs-welcome-blue)](https://github.com/jingyaogong/minimind-o/pulls) +[![Collection](https://img.shields.io/badge/🤗-MiniMind--O%20%20Collection-blue)](https://huggingface.co/collections/jingyaogong/minimind-o) +[![Technical Report](https://img.shields.io/badge/Technical%20Report-arXiv-red)](http://arxiv.org/abs/2605.03937) + +
+ +
+

"大道至简"

+
+ +
+ +中文 | [English](./README_en.md) + +
+ +* 此开源项目旨在从 0 完整实现一个小规模的端到端 Omni 模型,单一权重同时支持文 / 音 / 图三模态输入与文本 / 流式语音输出。 +* 其中 `minimind-3o` 仅 ~0.1B,普通个人 GPU 即可完成训练、CPU即可快速推理,是当前公开模型中规模最小的完整 Omni 实现(或之一)。 +* 开源 mini 与 full 两套训练数据:mini 单卡 3090 上约 2 小时跑通完整链路,便于入门;full 与发布权重对应。 +* 开源 Omni 模型的完整代码与技术报告,覆盖 Thinker–Talker 双路径、流式语音生成、实时打断、近似双工交互、音色克隆与电话模式 WebUI。 +* 所有核心算法代码均从 0 使用 PyTorch 原生实现,不依赖三方框架提供的高层抽象。 +* MiniMind-O 进一步延续了 [MiniMind](https://github.com/jingyaogong/minimind)(语言)与 [MiniMind-V](https://github.com/jingyaogong/minimind-v)(视觉多模态)的设计范式。 + +> 注:"约 2 小时" 指 mini 数据集在单张 NVIDIA RTX 3090 上跑完 SFT 的实测耗时。 + +--- + +
+ +[📄 MiniMind-O Technical Report](http://arxiv.org/abs/2605.03937) + +https://github.com/user-attachments/assets/10cbcc5f-4e70-45cf-bdc5-d6361e40bb86 + +[🔗 在线体验](https://modelscope.cn/studios/gongjy/MiniMind-O)  |  [🔗 视频介绍](https://www.bilibili.com/video/BV121421h71e) + + +
+ +--- + +# 📌 项目介绍 + +继 [MiniMind](https://github.com/jingyaogong/minimind)(LLM)和 [MiniMind-V](https://github.com/jingyaogong/minimind-v)(VLM)之后,MiniMind-O 是这个系列的第三站。所谓 Omni,就是让一个模型同时具备听、看、说的多模态交互能力:接收文本、语音和视觉信号,输出文本与流式语音。 + +或许 GPT-4o 让人第一次感受到足够自然的流式语音交互形态,随后 Mini-Omni2、Moshi、GLM-4-Voice、Qwen3-Omni 等开源工作陆续出现。但如果目标不是直接调用这些参数庞大的现成权重,而是从 0 读懂、训练、改动一个完整 Omni 模型,开源社区仍然急缺足够轻量、链路完整的起点。要把语音真正纳入 Omni 模型,一种做法是把 ASR、LLM、TTS 串成级联链路:语音先转文字,LLM 处理后再合成语音。这条路工程上直接,但中间多了一次文本转写,延迟、语气和情绪信息都会受到影响。 + +MiniMind-O 尝试补上已知的空位:让语音和文本在 hidden state 层面直接连通,在主 backbone 仅 0.1B 的规模下保留端到端 Omni 链路。Talker 侧采用 MTP(Multi-Token Prediction)一次预测多层 Mimi codes,再配合 VAD 支持实时打断与近似双工交互,这是足够实用的工程路线之一。本项目的代码、模型权重、训练数据和技术报告全部完整开源,单张 RTX 3090 上约 2 小时即可跑通 mini 数据集训练。目标依旧:让每个人都能从第一行代码读起,自己动手,从 0 训练一个能听、能看、能思考、能说的模型: + +![](images/omni_io_flow.png) + +😊 一起感受创造的乐趣吧! + +--- + +#### 🎉 项目包含以下内容 + +- 提供完整的 MiniMind-O 结构代码:Thinker、独立 Talker、audio / vision projector、Mimi codebook 接口以及 MTP audio head。 +- 提供 SFT 全链路训练流程,覆盖 T2A、I2T、A2A 三类数据,支持全参数训练、音频投影层训练、视觉投影层训练与 DDP 多卡训练。 +- 提供 mini 与 full 两套训练数据:mini 便于快速入门,单卡 3090 上约 2 小时可跑通;full 与发布权重对应,覆盖中文语音与图像任务。 +- 提供多种内置音色、unseen 音色与任意参考音频的音色克隆能力,便于复现音色控制实验。 +- 提供完整的推理与 Demo 工具,支持 CLI 推理、Web UI、流式播放、barge-in 打断和电话模式。 +- 关键模块均从 0 用 PyTorch 原生实现,不依赖三方高层封装;同时兼容 `transformers` Tokenizer 与原生权重格式。 +- 配套技术报告覆盖架构、训练曲线、CER / WER 评估、音色克隆相似度与跨模型对比,链接见顶部 Tech Report 区。 + +#### 🎉 已发布模型列表 + +| 模型 | 参数(主干) | Release | +|---|---|---| +| minimind-3o | ~0.1B | 2026.05.05 | +| minimind-3o-moe | ~0.3B-A0.1B | 2026.05.05 | + +--- + +#### 👉 更新日志 + +
+ 🔥 2026-05-05 + +- MiniMind-O 首次开源,发布 `minimind-3o`(115M)与 `minimind-3o-moe`(312M-A115M) +- Thinker–Talker 双路径架构,Talker 采用 MTP 预测多层 Mimi codes,支持 24 kHz 流式语音生成与 barge-in 打断 +- 音频编解码器采用 Mimi(8 层 codebook,12.5 Hz,24 kHz),Talker 在 codebook 接口上使用共享主体与轻量 adapter +- 语音 / 视觉特征分别由冻结的 SenseVoice-Small 与 SigLIP2 编码,再通过两层 MLP projector 注入 MiniMind 隐空间 +- 同步发布 mini 与 full 两套训练数据,mini 单卡 3090 ~2h 即可跑通整条 Thinker–Talker 链路 +- 内置 5 个 voice prompt + 7 个 unseen voice prompt,提供音色克隆与电话模式 WebUI + +
+ + +# 📌 快速开始 + +
+分享本人的软硬件配置(仅供参考) + +* CPU: Intel(R) Core(TM) i9-10980XE CPU @ 3.00GHz +* RAM: 128 GB +* GPU: NVIDIA GeForce RTX 3090(24GB) * 8 +* Ubuntu==20.04 +* CUDA==12.2 +* Python==3.10 +* [requirements.txt](./requirements.txt) + +
+ +## 第0步(必须) + +### 1' 环境准备 + +```bash +# 克隆仓库代码 +git clone --depth 1 https://github.com/jingyaogong/minimind-o +# 安装必要依赖 +pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +``` + +### 2' 下载资源 + +```bash +# 下载 SenseVoice-Small 语音编码器到 ./model/SenseVoiceSmall +modelscope download --model gongjy/SenseVoiceSmall --local_dir ./model/SenseVoiceSmall +# 下载 SigLIP2 视觉编码器到 ./model/siglip2-base-p32-256-ve +modelscope download --model gongjy/siglip2-base-p32-256-ve --local_dir ./model/siglip2-base-p32-256-ve +# 下载 Mimi 音频编解码器到 ./model/mimi +modelscope download --model gongjy/mimi --local_dir ./model/mimi +# 下载 CAMPPlus 说话人编码器到 ./model/campplus +modelscope download --model gongjy/campplus --local_dir ./model/campplus +# 下载 MiniMind 语言模型权重到 ./out 目录下(作为训练 Omni 的基座语言模型) +modelscope download --model gongjy/minimind-3o-pytorch llm_768.pth --local_dir ./out +``` + +注:也可从 [ModelScope Collection](https://modelscope.cn/collections/gongjy/MiniMind-O) 或 [HuggingFace Collection](https://huggingface.co/collections/jingyaogong/minimind-o) 选择对应内容 `git clone`(需LFS)下载,此处不再赘述。 + +完成后,结构应如下: + +```text +minimind-o/ +├── model/ +│ ├── SenseVoiceSmall/ +│ ├── siglip2-base-p32-256-ve/ +│ ├── mimi/ +│ ├── campplus/ +│ └── ... +├── out/ +│ └── llm_768.pth +└── ... +``` + +## Ⅰ 🚀 模型推理 + +### 1' 下载发布权重 + +```bash +# 下载发布权重到 ./out 目录下 +modelscope download --model gongjy/minimind-3o-pytorch --local_dir ./out +``` + +### 2' 命令行问答 + +```bash +python eval_omni.py --load_from model --weight sft_omni +``` + +如果使用 transformers 格式模型,可先下载模型目录: + +```bash +git clone https://huggingface.co/jingyaogong/minimind-3o +python eval_omni.py --load_from minimind-3o +``` + +### 3' 启动 WebUI(可选) + +```bash +# ⚠️ 须先将 transformers 格式模型文件夹复制到 ./scripts/ 目录下,web_demo_omni 脚本会自动扫描该目录下包含权重文件的子文件夹,如不存在则报错 +cp -r minimind-3o ./scripts/minimind-3o +cd scripts && python web_demo_omni.py +``` + +## Ⅱ 🛠️ 模型训练 + +
+注:提前测试Torch是否可用cuda + +```python +import torch +print(torch.cuda.is_available()) +``` + +如果不可用,请自行去[torch_stable](https://download.pytorch.org/whl/torch_stable.html)下载whl文件安装。 + +
+ +### 1' 下载数据 + +快速开始时,推荐从[数据集链接](https://huggingface.co/datasets/jingyaogong/minimind-o_dataset)只下载 `_mini` 数据集,并放到 `./dataset` 下。 + +### 2' 开始训练 + +推荐 mini 训练管线如下,默认在 `trainer/` 目录下执行,可直接 `cd trainer && bash train.sh`: + +```bash +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_t2a_mini.parquet --epochs 1 --batch_size 40 --use_compile 1 --from_weight llm --save_weight sft_zero --max_seq_len 512 --use_wandb --use_moe 0 +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_a2a_mini.parquet --epochs 1 --batch_size 40 --use_compile 0 --from_weight sft_zero --save_weight sft_zero --max_seq_len 640 --mode audio_proj --use_wandb --use_moe 0 +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 2e-5 --data_path ../dataset/sft_a2a_mini.parquet --epochs 1 --batch_size 16 --use_compile 0 --from_weight sft_zero --save_weight sft_zero --max_seq_len 768 --use_wandb --use_moe 0 +``` + +### 3' 测试已训练模型(可选) + +确保需要测试的模型 `*.pth` 文件已保存于 `./out/` 目录下。 + +```bash +python eval_omni.py --weight sft_omni +``` + +# 📌 模型细节 + +MiniMind-O 的基座语言模型来自孪生项目 [MiniMind](https://github.com/jingyaogong/minimind),LLM 的结构与训练细节可移步该项目查阅。即使不了解 LLM 细节,也可直接参照上方"快速开始"流程训练一个 MiniMind-O。 + +## Ⅰ 架构总览 + +![](./images/architecture.jpg) + +MiniMind-O 的主体由 Thinker 和 Talker 两条路径组成。Thinker 负责理解文本、语音和图像输入,并生成语义层面的文本回复;Talker 则在 Thinker 给出的语义条件上,通过 MTP 同步预测多层 Mimi audio codes,最后由音频解码器还原成流式语音。这样做的目的不是把 ASR、LLM、TTS 简单串起来,而是在一个统一序列里同时保留文本推理、语音输出和流式交互能力。 + +文本输入直接进入语言主干;语音和图像分别经过 Audio Encoder 与 Vision Encoder 提取特征,再映射到 MiniMind 的隐空间中。音色信息由 Speaker Encoder 或参考音频 codes 提供,推理时可以配合 VAD 实现边听边答、实时打断和近似双工交互。更细的 projector 结构、序列排布和训练目标在后文展开,代码层面的实现细节可直接参考 `model/model_omni.py` 与[技术报告](http://arxiv.org/abs/2605.03937)。 + +![](./images/input_token_layout.jpg) + +图中展示了文本 token、语音特征、图像特征和音色条件在输入序列中的布局方式。 + +## Ⅱ Thinker 侧多模态理解 + +Thinker 负责统一接收文本、语音和图像信息,并生成语义层面的文本回复。文本 token 直接进入语言主干,语音和图像特征则通过对应 projector 注入到占位符位置,使不同模态最终落到同一条序列中建模。 + +## Ⅲ 中间层 Bridge + +Thinker 向 Talker 传递的表征取自中间层,而不是 embedding 层或最后一层。embedding 层语义信息不足,最后一层又更贴近 next-token prediction 目标;中间层通常已经融合了上下文和跨模态信息,同时还没有被 LM head 过度塑形,更适合作为语音生成的条件。默认 `bridge_layer = num_hidden_layers // 2 - 1`,不同规模下也可以通过配置调整。 + +## Ⅳ Talker 侧语音生成 + +Talker 负责把 Thinker 给出的语义状态转成 8 层 Mimi codebook 序列。这里采用 MTP 形式同时预测多个 audio codebook,而不是把每层 codebook 拆成独立的长链路;为了控制 0.1B 模型中的额外参数量,音频 embedding 和输出 head 采用共享主体加轻量 codebook adapter 的形式。这样既保留不同 codebook 的分布差异,也避免为每一层 codebook 复制一整套参数。 + +## Ⅴ 序列格式与流式解码 + +![](./images/sequence_format.jpg) + +MiniMind-O 将文本 token 与 8 路 audio-code stream 放在同一个训练样本中:Thinker 负责文本序列,Talker 负责音频 code 序列,语音、图像和音色条件都通过占位符或 reference codes 注入。回复开始之后才计算目标文本和目标音频的损失,因此 reference 与 conditioning 区域只提供条件,不作为重构目标。 + +流式生成时,模型一边产生文本 token,一边通过 MTP 和延迟调度补齐 8 层 Mimi codes。Mimi 解码器可以增量恢复 24 kHz 波形,因此语音播放不必等待完整回答结束。 + +## Ⅵ 音色控制 + +音色控制采用 in-context voice cloning 的方式完成:参考音频先被编码成 voice prompt,作为上下文条件喂给 Talker,而不是通过微调权重或改写文本 prompt 来指定音色。模型也可以同时使用 speaker embedding 提供更稳定的说话人约束;推理时更换音色只需要替换这些条件信息,Thinker prompt 与 Talker 权重保持不变。 + +默认 release 带有 5 个内置 voice prompt(dylan、eric、serena、uncle_fu、vivian),另保留 7 个 unseen prompt 用于评估(arthur、chelsie、cherry、ethan、jennifer、momo、moon)。 + +## Ⅶ 模块与参数规模 + +MiniMind-O 所说的 0.1B,指 Thinker、Talker 和两路 projector 组成的可训练主体;落到具体发布版本上,`minimind-3o` 约 113M,`minimind-3o-moe` 约 315M。Audio Encoder、Vision Encoder 和 Speech Codec 属于冻结的外部旁路模型,负责特征提取或音频编解码,合计约 425M 参数,不计入 active MiniMind-O 参数。 + +下表按发布模型统计主要模块参数,Trainable 参数按 PyTorch 模块统计,tied embedding 去重计入。 + +| 统计口径 | minimind-3o | minimind-3o-moe | +|---|---:|---:| +| 可训练主体 | 113.13M | 314.89M | +| 冻结外部模块 | 424.70M | 424.70M | +| 运行时总加载 | 537.83M | 739.59M | + +| 模块 | 具体实现 | 关键配置 | 状态 / 参数 (~3o / ~3o-moe) | +|---|---|---|---| +| Thinker | MiniMind Transformer | 8 layers, hidden 768 | trainable, 63.91M / 198.42M | +| Talker | 独立 MiniMind blocks | 4 layers, 8 codebook heads | trainable, 47.05M / 114.30M | +| Audio projector | `MMAudioProjector` | 512 → 768 | trainable, 0.99M | +| Vision projector | `MMVisionProjector` | 768 → 768 | trainable, 1.18M | +| Audio encoder | SenseVoice-Small | 16 kHz speech features | frozen, 234.00M | +| Vision encoder | SigLIP2 base-p32-256 | 256×256 image, 64 tokens | frozen, 94.55M | +| Speech codec | Mimi | 8 codebooks, 12.5 Hz, 24 kHz | frozen, 96.15M | +| Speaker condition | CAM++ embedding | 192-d speaker vector | precomputed | + +# 📌 实验 + +## Ⅰ 数据集 + +数据集下载:[ModelScope](https://www.modelscope.cn/datasets/gongjy/minimind-o_dataset) | [HuggingFace](https://huggingface.co/datasets/jingyaogong/minimind-o_dataset) + +所有语音数据都统一转成 Mimi codes 存储,8 层 codebook,帧率 12.5 Hz;图像统一 resize 到 256×256,由 SigLIP2 P32 编码为 64 个 patch token。训练数据主要来自公开 omni / speech instruction 数据,包括 [VoiceAssistant-400K](https://huggingface.co/datasets/gpt-omni/VoiceAssistant-400K)、[UltraChat-300K-SLAM-Omni](https://huggingface.co/datasets/worstchan/UltraChat-300K-SLAM-Omni) 等;同时基于 Qwen3-TTS 进行了大量多说话人音频合成,并用 CAM++ 提取 speaker embedding 作为音色条件。I2T 数据与 [MiniMind-V](https://github.com/jingyaogong/minimind-v) 使用的视觉指令数据来源一致,原始组成和引用可参考该项目说明。 + +仓库提供 **mini** 与 **full** 两套训练数据。mini 从 full 中按"英文 + 无视觉"筛出,配 `train_sft_omni.py` 的默认 `--data_path` 即可使用;它的目标是用较低成本跑通 Thinker–Talker、Mimi 编解码、序列布局和音色注入链路,而不是复现发布模型的中文语音能力。中文 Talker 要同时处理更复杂的字音映射、韵律停顿和多说话人稳定性,明显比英文更难,不能依赖单卡 3090 约 2 小时的 mini 训练完成。 + +full 数据集与发布的 `minimind-3o` / `minimind-3o-moe` 权重对应,覆盖中英文 T2A / A2A 与图像 I2T。规模与中英文比例见下表,是论文中 CER / 音色相似度等指标的实际训练源。 + +其中 T2A 表示 Text-to-Audio,A2A 表示 Audio-to-Audio,I2T 表示 Image-to-Text。 + +| 数据集 | 子集 | 输入语音 | 输出语音 | 备注 | +|---|---|---|---|---| +| `sft_t2a_mini` | 英文 T2A | — | 约 470.14 h | mini 入门用 | +| `sft_a2a_mini` | 英文 A2A | 约 74.64 h | 约 56.60 h | mini 入门用 | +| `sft_t2a` | 中英 T2A | — | 约 1636.01 h | full 训练 | +| `sft_a2a` | 中英 A2A | 约 1711.97 h | 约 423.40 h | full 训练 | +| `sft_i2t` | 图像 I2T | — | — | full 训练 | + +`sft_t2a` 中中文、英文、混合样本占比分别为 45.7%、46.5%、7.8%;`sft_a2a` 中三者分别为 70.8%、21.2%、8.0%。这个分布会直接反映到行为上:短中文和短英文回答通常较稳定,较长英文语音更容易出现读音漂移和漏词。mini 子集只保留英文,因此即便参数量和数据量都收得很紧,单语种内部的 CER 表现仍能维持在可用范围。 + +## Ⅱ 训练 + +训练入口是 `train_sft_omni.py`,推荐流程可直接参考 `trainer/train.sh`。当前 full 训练不拆复杂的多阶段预训练,而是按数据流逐步接入能力: + +![](./images/training_pipeline.jpg) + +- `sft_t2a`:先对齐文本到语音输出,让 Talker 学会在 Thinker 语义条件下生成 Mimi codes; +- `sft_a2a`:再接入语音输入,使模型从 speech instruction 进入同一套 Thinker–Talker 回复链路; +- `sft_i2t`:最后对齐视觉路径,其中 `vision_proj` 模式只更新视觉投影层,避免图像数据过度改写语言和语音能力。 + +训练模式里,`all` 会更新 MiniMind / Talker / projector,`audio_proj` 和 `vision_proj` 只用于单独对齐对应投影层;SenseVoice-Small、SigLIP2 和 Mimi 始终冻结。Dense 与 MoE 版本沿用同一套数据顺序。mini 命令只用于快速跑通链路,默认单卡 3090 约 2 小时完成;发布权重对应 full 数据训练。 + +下面给出 full 训练过程中的 T2A 与 A2A loss 曲线(仅供参考): + +![](./images/t2a_training_curves.jpg) + +> `sft_t2a`:文本到语音输出链路 + +![](./images/a2a_training_curves.jpg) + +> `sft_a2a`:接入语音输入后的 loss + +T2A 曲线已去掉早期不兼容权重 resume 造成的异常尖峰;MoE 总参数更多但 active 参数与 dense 接近,更适合作为容量分配实验参考。 + +## Ⅲ 模型权重 + +| 模型格式 | ModelScope | HuggingFace | +|---|---|---| +| PyTorch (`*.pth`) | [minimind-3o-pytorch](https://www.modelscope.cn/models/gongjy/minimind-3o-pytorch) | [minimind-3o-pytorch](https://huggingface.co/jingyaogong/minimind-3o-pytorch) | +| Transformers | [minimind-o collection](https://modelscope.cn/collections/gongjy/MiniMind-O) | [minimind-o collection](https://huggingface.co/collections/jingyaogong/minimind-o) | + +> Transformers 版本包含 `minimind-3o` 与 `minimind-3o-moe`,适合直接用于 `eval_omni.py` 和 WebUI 推理;原生 PyTorch 权重主要用于训练、复现实验和继续微调。 + +# 📌 评估 + +Omni 模型目前可能还没有统一的评估口径,不同工作的 LLM 主干、音频合成器和系统目标都不一样:有的看重 LLM 本身的知识和推理,会报告 MMLU、HumanEval 等指标;有的看重流式速度和音质,有的强调语音一致性指标,也有的更关注自然交互或更大范围的 Omni 生成。这些工作大都基于 SOTA 开源 LLM 续训,而 MiniMind 的 0.06B 主干在复杂知识问答、数学推理、代码生成或开放式长回答上显然不可能形成竞争力,Talker 的自然度、韵律和稳定性也弱于成规模的系统。 + +所以这里无法追求综合榜单,而是落实到几项更可复现的局部评估和 use cases:Talker hidden size 消融、音色克隆相似度、相同问题和相同 ASR 流程下的 CER / WER 对比,以及 A2A、I2A 和实时交互样例。CER / WER 主要用来观察文本一致性,音质、自然度和人类偏好则留给定性样例和实际试听判断。 + +## Ⅰ Talker Hidden Size 消融 + +如果只看语音生成,Talker 做到 1024 / 2048 维、或者继续加深层数一定会更稳。但 MiniMind-O 要把完整 Omni 链路压在 0.1B 左右,不能把大部分参数都交给声学端。Thinker / Talker 解耦后,语言理解和跨模态融合主要由 Thinker 承担,Talker 只在语义条件上渲染 Mimi codes,这让小 Talker 成为可能。这里的渲染不是只预测语义 token 再交给外部声学生成器,而是由 Talker 直接生成可解码的 Mimi acoustic codes;真正的瓶颈也就在输出端:Talker 面对的是 8 层 Mimi codebook,而不是单一路径的 next-token prediction。 + +384 维最诱人,dense 版本可以压到 88M 左右;512 维也更轻。但表中结果说明,小并不自动等于划算:短句还能维持,中长句更容易出现漏词、重复和发音漂移。768 维最后留下来,是因为它和 MiniMind 主干维度一致,可以用 Thinker 后 4 层初始化;参数仍在 0.1B 左右,训练成本没有明显增加,一致性却稳定得多。 + +| Variant | Talker hidden | Params | Avg CER ↓ | Short ↓ | Mid / Long ↓ | +|---|---|---|---|---|---| +| Dense | 768 | 115.29M | **0.0897** | 0.1528 | 0.0874 / 0.0675 | +| Dense | 512 | 96.13M | 0.1745 | 0.2709 | 0.2455 / 0.0976 | +| Dense | 384 | 88.72M | 0.2767 | 0.3904 | 0.1865 / 0.4046 | +| MoE | 768 | 317.05M-A115.33M | **0.0900** | 0.2075 | 0.0533 / 0.0271 | +| MoE | 512 | 261.32M-A96.17M | 0.1265 | 0.0711 | 0.1490 / 0.1464 | +| MoE | 384 | 240.04M-A88.75M | 0.3280 | 0.3757 | 0.2777 / 0.4313 | + +Dense 和 MoE 的 CER 不宜直接横向比较:同一问题下,两个 Thinker 生成的内容和长度可能不同,Talker 面对的合成难度也不同。更有意义的是看同一架构内部的趋势,768 都明显优于 512 和 384。 + +## Ⅱ 音色克隆相似度 + +音色克隆是当前版本里比较 Beta 的能力。没说错的话,多数开源 Omni 模型只支持固定输出音色,而 minimind-3o 尝试把多音色生成塞到同一套 Talker 里完成。这个目标比“能说话”更难,因为模型不仅要把内容说对,还要在生成 Mimi codes 时保留说话人的音色线索。 + +目前效果还谈不上高保真克隆,同一个参考音色在不同问题上并不总能保持一致,长句里也容易被发音和节奏问题带偏。但基本的男女声差异、语调倾向和一部分韵律特征是能区分出来的。 +下面的 CAM++ speaker embedding 余弦相似度只作为自动化参考:Seen 组来自 `voices.pt` 中 5 个内置音色,Unseen 组来自 `voices_unseen.pt` 中 7 个训练时未见过的音色;每个音色使用同一组文本问题,只替换音色条件。 + +逐音色细分如下: + +| Split | Speaker | Dense ↑ | MoE ↑ | +|---|---|---|---| +| Seen | dylan | 0.6997 | 0.6837 | +| Seen | eric | 0.5289 | 0.4232 | +| Seen | serena | 0.7092 | 0.7041 | +| Seen | uncle_fu | 0.7241 | 0.7337 | +| Seen | vivian | 0.5744 | 0.5888 | +| Unseen | arthur | 0.7171 | 0.6750 | +| Unseen | chelsie | 0.6437 | 0.6240 | +| Unseen | cherry | 0.5689 | 0.5678 | +| Unseen | ethan | 0.4783 | 0.4847 | +| Unseen | jennifer | 0.4749 | 0.4003 | +| Unseen | momo | 0.6470 | 0.5720 | +| Unseen | moon | 0.4282 | 0.6673 | + +总体上,minimind-3o 与 minimind-3o-moe 的平均结果接近,也都略高于早期 baseline;这说明音色保持不主要由 inactive expert 容量决定,更直接的影响来自 reference 片段质量、CAM++ embedding 的可分性,以及 Talker 生成音频本身是否稳定。单个音色里,uncle_fu、serena、arthur 这类声音更容易保持住,至少一个版本能超过 0.70;eric、moon 等 outlier 则更容易受生成质量影响。换句话说,这个能力已经能区分一部分音色特征,但距离“给一段参考音频就稳定复刻”的产品级体验还有距离。 + +### 音色克隆消融实验(音频播放) + +为了让试听更直观,这里固定 seed=42、temperature=0.7,对每个音色展示 1 个生成样例,唯一变化的是参考音频 codes 和 speaker embedding。作为对照,下面先给出不施加任何参考音色条件时的 default 输出(朗读文本对所有样例一致): + +https://github.com/user-attachments/assets/b31fd8f2-e3af-4fed-ba19-65424b59bec6 + +#### Seen 音色 +Seen 表示训练数据中出现过的音色,用来观察模型对熟悉说话人的保持情况。 + + + + + + + + +
说话人参考音色输出结果平均
dylan + +https://github.com/user-attachments/assets/070ea3ab-0e8e-4aa0-84b5-af8d3c4e2725 + + + +https://github.com/user-attachments/assets/eb2da7ed-173c-47e9-9431-7bdb5a9b7385 + +0.6712
eric + +https://github.com/user-attachments/assets/c74aa5dc-1edd-44c1-9546-6e57194c2f60 + + + +https://github.com/user-attachments/assets/f3fa8906-4e14-4610-a9d9-c16c915ca1b3 + +0.4430
serena + +https://github.com/user-attachments/assets/0eeeac87-fa70-4025-b66e-1f0197f2b434 + + + +https://github.com/user-attachments/assets/c5901dca-4b2a-47f5-9b30-c89de54f908e + +0.6600
uncle_fu + +https://github.com/user-attachments/assets/fdd1bb28-6648-44bf-8bcb-4509e709e347 + + + +https://github.com/user-attachments/assets/95b480f1-f015-4712-8d7c-17db465f6584 + +0.6632
vivian + +https://github.com/user-attachments/assets/f64731c4-67a3-4e18-b7d7-61bf44ef4bdd + + + +https://github.com/user-attachments/assets/3f1cc9bb-16d2-4ce0-a473-40676cf4523e + +0.5320
+ +#### Unseen 音色 +Unseen 表示训练时没有见过的音色,用来观察模型能否把新的参考音色0样本直接迁移到生成语音里。 + + + + + + + + + + +
说话人参考音色输出结果平均
arthur + +https://github.com/user-attachments/assets/46e720ba-c413-4731-8275-a9a2cb483690 + + + +https://github.com/user-attachments/assets/f489b463-c7d7-4ad6-9e4d-53333172c66c + +0.6479
chelsie + +https://github.com/user-attachments/assets/3430ecdb-6de8-4fb0-a6a7-ad82bdce01a1 + + + +https://github.com/user-attachments/assets/e598dbc2-ba28-4c38-b52d-6fa6c2349a5b + +0.5975
cherry + +https://github.com/user-attachments/assets/f9166af6-3a98-42f3-9cf8-ad105eea87d6 + + + +https://github.com/user-attachments/assets/eccca693-4708-409a-88f7-85eb25f66fe6 + +0.5418
ethan + +https://github.com/user-attachments/assets/9c992505-2046-483e-a7cf-50ec18a5e329 + + + +https://github.com/user-attachments/assets/98013c5e-f5b5-4e1a-bc0e-a0f0be5d3240 + +0.4323
jennifer + +https://github.com/user-attachments/assets/924b035d-5c7c-45a5-a8f8-5dbdc18f71db + + + +https://github.com/user-attachments/assets/853d1370-0065-4567-9a71-dc88a6a34d56 + +0.4052
momo + +https://github.com/user-attachments/assets/7e97f524-da6d-4a2f-9095-e7f99262f4a5 + + + +https://github.com/user-attachments/assets/4c193c8f-8750-4424-acba-2bd13089a634 + +0.5968
moon + +https://github.com/user-attachments/assets/527df88a-adc0-48d3-9a6a-827ca1ba7fb0 + + + +https://github.com/user-attachments/assets/3f533e26-1ad8-4ab3-baf1-21267734d3ee + +0.5874
+ +## Ⅲ 跨模型英文 T2A 对比 + +这里选了 20 个英文问题,并统一加上 `Answer briefly in one short sentence` 约束。这样做不是为了考察开放式英文能力,而是尽量把回答长度压到同一范围内;三套模型生成音频后,再统一用 Qwen3-ASR 转写,并与目标文本计算 CER / WER,用来比较 Talker 的文本一致性。 + +| 长度桶 | [Mini-Omni](https://huggingface.co/gpt-omni/mini-omni) CER/WER | [Mini-Omni2](https://huggingface.co/gpt-omni/mini-omni2) CER/WER | minimind-3o CER/WER | +|---|---|---|---| +| short (≤15w) | 0.0195 / 0.0384 (n=8) | 0.0503 / 0.0584 (n=14) | 0.0531 / 0.0417 (n=8) | +| mid (16–30w) | 0.0038 / 0.0052 (n=12) | 0.0062 / 0.0076 (n=6) | 0.1327 / 0.1420 (n=11) | +| long (31–60w) | — | — | 0.0431 / 0.0508 (n=1) | + +≤15 词的短回复里,minimind-3o 已经接近 Mini-Omni2;真正拉开差距的是 16–30 词段。这个长度已经不是简单短语,Talker 需要在一个完整短句里同时维持发音、节奏和词面一致性,也是当前 0.1B Talker 最容易暴露不稳定性的区间。 + +## Ⅳ 跨模型视觉语言对比 + +[Mini-Omni](https://huggingface.co/gpt-omni/mini-omni) 不支持 VL 路径,因此这里只比较 [Mini-Omni2](https://huggingface.co/gpt-omni/mini-omni2)(0.5B)和 minimind-3o(0.1B)。9 张合成图像上,两个模型分别生成英文回答,再统一转写并计算 CER / WER,作为视觉到语音链路的一致性参考。 + +| Model | Params | Avg CER ↓ | Avg WER ↓ | +|---|---|---|---| +| [Mini-Omni2](https://huggingface.co/gpt-omni/mini-omni2) | 0.5B | 0.7609 | 0.9756 | +| minimind-3o | 0.1B | 0.8241 | 1.0293 | + +这个数值不能当作开放式图像描述的绝对正确率。视觉描述存在大量等价表达,同义改写和描述顺序都会影响 CER / WER,数值整体偏高是预期现象。在同一自动流程下,minimind-3o 落后于 Mini-Omni2,但仍处在同一数量级,同时参数约为后者的 1/5。 + +## Ⅴ 样例 + +![](./images/qual_a2a.jpg) + +语音到语音样例直接以真实语音作为输入,由 Thinker 组织语义,再由 Talker 渲染成语音。短回答仍然是当前更稳的区间,中文解释型问题通常能生成较连贯的回答,英文的发音和节奏相对更稳定。 + + + + + + +
+ +https://github.com/user-attachments/assets/c85809b2-4787-4656-9c7e-55b693798494 + + + +https://github.com/user-attachments/assets/354a5eec-c147-4d18-8c7a-942bd2a0b4b0 + +
+ +![](./images/image2audio_qualitative.jpg) + +图像问答样例把视觉编码、文本生成和语音渲染串在同一条链路里。当前模型通常能抓住主体物体和大致场景,但细粒度空间关系、数量和属性仍容易出错,因此更适合作为小模型 omni pipeline 的可复现基线。 + + + + + + +
+ +https://github.com/user-attachments/assets/244e08b0-5b12-449e-a7a2-2a2139c5d62d + + + +https://github.com/user-attachments/assets/3e8d0a76-282d-4a9d-9726-a954cf80198a + +
+ +## Ⅵ 实时交互 + +![](./images/realtime_interaction.jpg) + +最后是实时交互界面。用户停止说话后,Thinker 先完成语义侧的 prefill,Talker 随后开始逐步产生音频 code,Mimi decoder 则边接收 code 边写出 24 kHz 波形。Barge-in 的例子展示了另一条更接近真实对话的路径:当用户在模型说话过程中再次开口,系统会中断当前生成,重新进入 prefill–reply 流程。这里的中断检测仍然只是简单 VAD 阈值,还谈不上语义级打断;但从工程闭环看,系统已经能从 speaking 状态退回 listening 状态,并处理下一轮输入。 + +### 🧩 未来值得改进的方面 + +当前模型和大规模 Omni 系统在各方面仍有差距,这一点不需要回避。长语音自然度、复杂视觉推理、开放式英文中长回答和音色稳定性,都还不是它擅长的区间。视觉路径更接近紧凑的 vision-to-speech 链路,MoE 版本也更像一次容量分配实验,而不是同算力最优解。 + +这些限制也给出了后续方向:更长的 ICL 上下文、更细的 prosody 监督、更强的视觉编码器、更稳定的音色条件,以及对 Bridge layer 和 MTP codebook interface 的系统扫描,都值得继续做。 + +话说回来,MiniMind-O 的价值也正在这里。它把一个完整 Omni 闭环压到 0.1B 量级,并把代码、权重和主要训练数据放在同一个可检查对象里;这意味着它不只是一个 demo,而是一个足够小、足够透明、可以从头复现和继续改造的基线。对于想理解 Thinker–Talker 解耦、MTP codebook interface、in-context voice cloning 和 middle hidden bridge 这些细节的人来说,它提供的是一套可以真正动手验证的设计经验。 + +# 📌 致谢 + +> [!TIP] +> 如果您觉得 `MiniMind-O` 对您有所帮助,可以在 GitHub 上加一个⭐
+> 水平有限难免存在未知的纰漏,欢迎所有人在 Issues 交流指正或提交 PR 改进项目
+> 您的支持就是持续改进项目的动力,谢谢! + +## 🤝贡献者 + + + + + +## 😊鸣谢 + +- [MiniMind](https://github.com/jingyaogong/minimind) / [MiniMind-V](https://github.com/jingyaogong/minimind-v)(基座、数据) +- [Qwen2.5-Omni / Qwen3-Omni](https://github.com/QwenLM/Qwen2.5-Omni)(灵感、数据) +- [Mini-Omni / Mini-Omni2](https://github.com/gpt-omni/mini-omni)(灵感、数据) +- [SLAM-Omni](https://aclanthology.org/2025.findings-acl.115/)(数据) +- [SenseVoice](https://arxiv.org/abs/2407.04051)(组件) +- [Mimi / Moshi](https://arxiv.org/abs/2410.00037)(组件) + +## 🫶支持者 + + + + + + github contribution grid snake animation + + + + + + + + github contribution grid snake animation + + + + + + + Star History Chart + + +# 🎓 引用 + +如果您觉得 MiniMind-O 对您的研究或工作有所帮助,请引用: + +```bibtex +% 引用 MiniMind-O 技术报告:用于讨论模型架构、训练方法与实验结论。 +@article{minimind-o-report, + title = {MiniMind-O Technical Report: An Open Small-Scale Speech-Native Omni Model}, + author = {Jingyao Gong}, + journal = {arXiv preprint arXiv:2605.03937}, + year = {2026} +} + +% 引用 MiniMind-O GitHub 仓库:用于指代开源代码与发布权重。 +@misc{minimind-o, + title = {MiniMind-O: Train a Tiny Omni Model from Scratch}, + author = {Jingyao Gong}, + year = {2026}, + url = {https://github.com/jingyaogong/minimind-o}, + note = {GitHub repository, accessed 2026} +} +``` + +# 📜 许可协议 + +本仓库遵循 [Apache-2.0 License](LICENSE) 开源协议。 diff --git a/README_en.md b/README_en.md new file mode 100644 index 0000000..d8d9b05 --- /dev/null +++ b/README_en.md @@ -0,0 +1,675 @@ +
+ +![logo](./images/logo.png) + +
+ + +
+ +![visitors](https://visitor-badge.laobi.icu/badge?page_id=jingyaogong/minimind-o) +[![GitHub Repo stars](https://img.shields.io/github/stars/jingyaogong/minimind-o?style=social)](https://github.com/jingyaogong/minimind-o/stargazers) +[![GitHub Code License](https://img.shields.io/github/license/jingyaogong/minimind-o?v=1)](LICENSE) +[![GitHub last commit](https://img.shields.io/github/last-commit/jingyaogong/minimind-o)](https://github.com/jingyaogong/minimind-o/commits/master) +[![GitHub pull request](https://img.shields.io/badge/PRs-welcome-blue)](https://github.com/jingyaogong/minimind-o/pulls) +[![Collection](https://img.shields.io/badge/🤗-MiniMind--O%20%20Collection-blue)](https://huggingface.co/collections/jingyaogong/minimind-o) +[![Technical Report](https://img.shields.io/badge/Technical%20Report-arXiv-red)](http://arxiv.org/abs/2605.03937) + +
+ +
+

"Less is More"

+
+ +
+ +[中文](./README.md) | English + +
+ +* This project implements a small end-to-end Omni model from scratch, where a single set of weights jointly handles text / audio / image inputs and produces text / streaming-speech outputs. +* `minimind-3o` has only ~0.1B parameters: it can be trained on a consumer GPU and runs quickly on CPU, making it one of the smallest fully-functional Omni implementations publicly available. +* Two training datasets are released, `mini` and `full`. `mini` runs the full pipeline in about 2 hours on a single RTX 3090 and is intended for getting started; `full` corresponds to the released weights. +* The full codebase and technical report are released, covering the Thinker–Talker dual-path architecture, streaming speech generation, real-time barge-in, near-duplex interaction, voice cloning and a phone-mode WebUI. +* All core algorithmic components are implemented from scratch in native PyTorch and do not rely on high-level abstractions from third-party frameworks. +* MiniMind-O continues the design philosophy of [MiniMind](https://github.com/jingyaogong/minimind) (language) and [MiniMind-V](https://github.com/jingyaogong/minimind-v) (vision-language). + +> Note: "about 2 hours" refers to the measured time of running SFT on the mini dataset using a single NVIDIA RTX 3090. + +--- + +
+ +[📄 MiniMind-O Technical Report](http://arxiv.org/abs/2605.03937) + +https://github.com/user-attachments/assets/10cbcc5f-4e70-45cf-bdc5-d6361e40bb86 + +[🔗 Online Demo](https://modelscope.cn/studios/gongjy/MiniMind-O)  |  [🔗 Video Intro](https://www.bilibili.com/video/BV121421h71e) + +
+ +--- + +# 📌 Project Introduction + +After [MiniMind](https://github.com/jingyaogong/minimind) (LLM) and [MiniMind-V](https://github.com/jingyaogong/minimind-v) (VLM), MiniMind-O is the third stop in this series. By "Omni" we mean a model that can listen, see and speak at the same time: it takes text, speech and visual signals as inputs, and produces text together with streaming speech. + +GPT-4o was probably the first system that made natural streaming voice interaction feel real. Since then, open-source projects such as Mini-Omni2, Moshi, GLM-4-Voice and Qwen3-Omni have gradually appeared. However, if the goal is not just to call ready-made checkpoints with billions of parameters, but to fully understand, train and modify a complete Omni model from scratch, the open-source community still lacks a sufficiently lightweight starting point with an end-to-end pipeline. A common way to bring speech into an Omni model is to chain ASR, LLM and TTS into a cascade: speech is first transcribed to text, the LLM processes it, and the answer is then synthesized back to speech. This is straightforward from an engineering perspective, but it adds an extra transcription step and noticeably hurts latency, prosody and emotional cues. + +MiniMind-O attempts to fill this gap: speech and text are connected directly at the hidden-state level, while the trainable backbone remains only ~0.1B parameters and the end-to-end Omni pipeline is preserved. The Talker side adopts MTP (Multi-Token Prediction) to predict multiple Mimi codebook layers at once, and combines it with VAD to support real-time barge-in and near-duplex interaction—a practical engineering route for a tiny Omni model. The code, model weights, training data and technical report are all open-sourced. A single RTX 3090 can finish training on the mini dataset in about 2 hours. The goal remains the same: let everyone read the project from the first line of code, and train, from scratch, a model that can listen, see, think and speak: + +![](images/omni_io_flow.png) + +😊 Enjoy building. + +--- + +#### 🎉 What this project provides + +- A complete MiniMind-O architecture: Thinker, an independent Talker, audio / vision projectors, the Mimi codebook interface and the MTP audio head. +- A full SFT pipeline that covers T2A, I2T and A2A data, supporting full-parameter training, audio-projector-only training, vision-projector-only training, and DDP multi-GPU training. +- Two training datasets, `mini` and `full`. `mini` is meant for quick onboarding and runs the pipeline in ~2 hours on a single RTX 3090; `full` matches the released weights and covers Chinese speech and image tasks. +- Multiple built-in voice prompts, unseen voice prompts and voice cloning from arbitrary reference audio, making voice-control experiments easy to reproduce. +- A complete inference and demo toolkit: CLI, Web UI, streaming playback, barge-in interruption and a phone-mode demo. +- Key modules are written from scratch in native PyTorch without high-level third-party wrappers, while remaining compatible with `transformers` tokenizers and native weight formats. +- A companion technical report covers architecture, training curves, CER / WER evaluation, voice-cloning similarity and cross-model comparisons. See the Tech Report badge at the top. + +#### 🎉 Released models + +| Model | Backbone params | Release | +|---|---|---| +| minimind-3o | ~0.1B | 2026.05.05 | +| minimind-3o-moe | ~0.3B-A0.1B | 2026.05.05 | + +--- + +#### 👉 Update Log + +
+ 🔥 2026-05-05 + +- First release of MiniMind-O: `minimind-3o` (115M) and `minimind-3o-moe` (312M-A115M). +- Thinker–Talker dual-path architecture. Talker uses MTP to predict multi-codebook Mimi codes and supports 24 kHz streaming speech generation and barge-in. +- Audio codec is Mimi (8 codebooks, 12.5 Hz, 24 kHz). Talker uses a shared backbone plus lightweight adapters at the codebook interface. +- Speech and visual features are extracted by frozen SenseVoice-Small and SigLIP2 respectively, and injected into the MiniMind hidden space through two-layer MLP projectors. +- Mini and full training datasets are released alongside; mini runs the full Thinker–Talker pipeline in ~2h on a single RTX 3090. +- 5 built-in voice prompts and 7 unseen voice prompts, with voice cloning and a phone-mode WebUI included. + +
+ + +# 📌 Quick Start + +
+Reference hardware / software setup + +* CPU: Intel(R) Core(TM) i9-10980XE CPU @ 3.00GHz +* RAM: 128 GB +* GPU: NVIDIA GeForce RTX 3090 (24GB) * 8 +* Ubuntu==20.04 +* CUDA==12.2 +* Python==3.10 +* [requirements.txt](./requirements.txt) + +
+ +## Step 0 (required) + +### 1' Environment + +```bash +# Clone the repository +git clone --depth 1 https://github.com/jingyaogong/minimind-o +# Install dependencies +pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +``` + +### 2' Download resources + +```bash +# Download SenseVoice-Small audio encoder to ./model/SenseVoiceSmall +modelscope download --model gongjy/SenseVoiceSmall --local_dir ./model/SenseVoiceSmall +# Download SigLIP2 vision encoder to ./model/siglip2-base-p32-256-ve +modelscope download --model gongjy/siglip2-base-p32-256-ve --local_dir ./model/siglip2-base-p32-256-ve +# Download Mimi audio codec to ./model/mimi +modelscope download --model gongjy/mimi --local_dir ./model/mimi +# Download CAM++ speaker encoder to ./model/campplus +modelscope download --model gongjy/campplus --local_dir ./model/campplus +# Download MiniMind LLM weights to ./out (used as the language backbone for training Omni) +modelscope download --model gongjy/minimind-3o-pytorch llm_768.pth --local_dir ./out +``` + +You can also `git clone` the corresponding repos from the [ModelScope Collection](https://modelscope.cn/collections/gongjy/MiniMind-O) or [HuggingFace Collection](https://huggingface.co/collections/jingyaogong/minimind-o) (LFS required); details omitted here. + +After downloading, the directory should look like: + +```text +minimind-o/ +├── model/ +│ ├── SenseVoiceSmall/ +│ ├── siglip2-base-p32-256-ve/ +│ ├── mimi/ +│ ├── campplus/ +│ └── ... +├── out/ +│ └── llm_768.pth +└── ... +``` + +## Ⅰ 🚀 Inference + +### 1' Download released weights + +```bash +# Download released weights to ./out +modelscope download --model gongjy/minimind-3o-pytorch --local_dir ./out +``` + +### 2' Command-line chat + +```bash +python eval_omni.py --load_from model --weight sft_omni +``` + +To use the Transformers-format model, download the model directory first: + +```bash +git clone https://huggingface.co/jingyaogong/minimind-3o +python eval_omni.py --load_from minimind-3o +``` + +### 3' Launch WebUI (optional) + +```bash +# ⚠️ Copy the Transformers-format model folder into ./scripts/. The web_demo_omni script +# automatically scans this directory for sub-folders that contain weight files; it +# raises an error if none is found. +cp -r minimind-3o ./scripts/minimind-3o +cd scripts && python web_demo_omni.py +``` + +## Ⅱ 🛠️ Training + +
+Verify Torch is using CUDA + +```python +import torch +print(torch.cuda.is_available()) +``` + +If unavailable, please download the matching `.whl` from [torch_stable](https://download.pytorch.org/whl/torch_stable.html) and install it manually. + +
+ +### 1' Download data + +For a quick start, downloading only the `_mini` parquet files from the [dataset link](https://huggingface.co/datasets/jingyaogong/minimind-o_dataset) and placing them under `./dataset` is enough. + +### 2' Train + +The recommended mini training pipeline is shown below. It is meant to be run from the `trainer/` directory; equivalently, run `cd trainer && bash train.sh`: + +```bash +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_t2a_mini.parquet --epochs 1 --batch_size 40 --use_compile 1 --from_weight llm --save_weight sft_zero --max_seq_len 512 --use_wandb --use_moe 0 +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_a2a_mini.parquet --epochs 1 --batch_size 40 --use_compile 0 --from_weight sft_zero --save_weight sft_zero --max_seq_len 640 --mode audio_proj --use_wandb --use_moe 0 +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 2e-5 --data_path ../dataset/sft_a2a_mini.parquet --epochs 1 --batch_size 16 --use_compile 0 --from_weight sft_zero --save_weight sft_zero --max_seq_len 768 --use_wandb --use_moe 0 +``` + +### 3' Test the trained model (optional) + +Make sure the model `*.pth` to be tested is placed under `./out/`. + +```bash +python eval_omni.py --weight sft_omni +``` + +# 📌 Model Details + +The language backbone of MiniMind-O comes from the sister project [MiniMind](https://github.com/jingyaogong/minimind). For LLM architecture and training details, please refer to that repository. Even without going into the LLM internals, you can still follow the Quick Start section above to train MiniMind-O end-to-end. + +## Ⅰ Architecture overview + +![](./images/architecture.jpg) + +MiniMind-O consists of two paths: Thinker and Talker. Thinker is responsible for understanding text, speech and image inputs and producing a semantic-level text reply. Talker takes the semantic conditions from Thinker and uses MTP to jointly predict multi-codebook Mimi audio codes, which the audio decoder finally restores into streaming speech. The point is not to chain ASR, LLM and TTS together, but to keep text reasoning, speech generation and streaming interaction inside a single unified sequence. + +Text inputs go directly into the language backbone; speech and images are first encoded by the Audio Encoder and Vision Encoder respectively, and then projected into the MiniMind hidden space. Voice information is provided either by a Speaker Encoder or by reference-audio codes; combined with VAD at inference time, this enables listen-while-speaking, real-time barge-in and near-duplex interaction. Later sections describe the projectors, sequence layout and training objectives in more detail; for code-level details, please refer to `model/model_omni.py` and the [technical report](http://arxiv.org/abs/2605.03937). + +![](./images/input_token_layout.jpg) + +The figure above shows how text tokens, speech features, image features and voice conditions are laid out in the input sequence. + +## Ⅱ Multimodal understanding on the Thinker side + +Thinker receives text, speech and image information uniformly and produces a semantic-level text reply. Text tokens enter the language backbone directly, while speech and image features are injected into placeholder positions through their respective projectors, so that all modalities are eventually modeled within the same sequence. + +## Ⅲ Middle-layer Bridge + +The representation passed from Thinker to Talker is taken from a middle layer rather than the embedding layer or the final layer. Embedding layers carry too little semantic information, while the final layer is overly shaped towards next-token prediction. A middle layer typically already fuses contextual and cross-modal information without being over-tuned by the LM head, which makes it a better conditioning source for speech generation. By default `bridge_layer = num_hidden_layers // 2 - 1`, and it can be adjusted through configuration at different scales. + +## Ⅳ Speech generation on the Talker side + +Talker turns the semantic states from Thinker into 8 streams of Mimi codebook codes. It uses MTP to predict multiple audio codebooks simultaneously, instead of running each codebook through a separate long path. To control the additional parameter count inside a 0.1B model, the audio embedding and output head share a common backbone with lightweight per-codebook adapters. This preserves the distributional differences between codebooks while avoiding a full parameter copy for each codebook layer. + +## Ⅴ Sequence format and streaming decoding + +![](./images/sequence_format.jpg) + +MiniMind-O places text tokens and 8 audio-code streams in the same training sample: Thinker handles the text sequence, Talker handles the audio-code sequence, and speech / image / voice conditions are injected through placeholders or reference codes. Loss on target text and target audio is computed only after the reply starts; reference and conditioning regions serve only as conditions and are not part of the reconstruction target. + +For streaming generation, the model emits text tokens while simultaneously filling in 8 layers of Mimi codes via MTP and a delay schedule. The Mimi decoder can incrementally reconstruct the 24 kHz waveform, so playback does not have to wait for the full reply to finish. + +## Ⅵ Voice control + +Voice control is realized through in-context voice cloning: reference audio is first encoded into a voice prompt, and then fed to Talker as a contextual condition, instead of fine-tuning weights or rewriting the text prompt to specify a voice. The model can additionally use a speaker embedding to provide a more stable speaker constraint; switching the voice at inference time only requires changing these conditioning inputs, while the Thinker prompt and Talker weights remain unchanged. + +The default release ships with 5 built-in voice prompts (dylan, eric, serena, uncle_fu, vivian), and reserves 7 unseen prompts for evaluation (arthur, chelsie, cherry, ethan, jennifer, momo, moon). + +## Ⅶ Modules and parameter scale + +The "0.1B" referenced for MiniMind-O denotes the trainable backbone composed of Thinker, Talker and the two projectors. For the released checkpoints, `minimind-3o` is about 113M and `minimind-3o-moe` is about 315M. The Audio Encoder, Vision Encoder and Speech Codec are frozen external side modules used only for feature extraction or audio (de)coding; together they contain about 425M parameters and are not counted as active MiniMind-O parameters. + +The table below counts the main module sizes per released model. Trainable counts are based on PyTorch modules, with tied embeddings deduplicated. + +| Counting scope | minimind-3o | minimind-3o-moe | +|---|---:|---:| +| Trainable backbone | 113.13M | 314.89M | +| Frozen external modules | 424.70M | 424.70M | +| Total loaded at runtime | 537.83M | 739.59M | + +| Module | Implementation | Key configuration | Status / params (~3o / ~3o-moe) | +|---|---|---|---| +| Thinker | MiniMind Transformer | 8 layers, hidden 768 | trainable, 63.91M / 198.42M | +| Talker | Standalone MiniMind blocks | 4 layers, 8 codebook heads | trainable, 47.05M / 114.30M | +| Audio projector | `MMAudioProjector` | 512 → 768 | trainable, 0.99M | +| Vision projector | `MMVisionProjector` | 768 → 768 | trainable, 1.18M | +| Audio encoder | SenseVoice-Small | 16 kHz speech features | frozen, 234.00M | +| Vision encoder | SigLIP2 base-p32-256 | 256×256 image, 64 tokens | frozen, 94.55M | +| Speech codec | Mimi | 8 codebooks, 12.5 Hz, 24 kHz | frozen, 96.15M | +| Speaker condition | CAM++ embedding | 192-d speaker vector | precomputed | + +# 📌 Experiments + +## Ⅰ Datasets + +Dataset download: [ModelScope](https://www.modelscope.cn/datasets/gongjy/minimind-o_dataset) | [HuggingFace](https://huggingface.co/datasets/jingyaogong/minimind-o_dataset) + +All speech data is stored uniformly as Mimi codes (8 codebooks, 12.5 Hz frame rate). Images are resized uniformly to 256×256 and encoded by SigLIP2 P32 into 64 patch tokens. The training data mainly comes from public Omni / speech-instruction corpora, including [VoiceAssistant-400K](https://huggingface.co/datasets/gpt-omni/VoiceAssistant-400K), [UltraChat-300K-SLAM-Omni](https://huggingface.co/datasets/worstchan/UltraChat-300K-SLAM-Omni) and others. A large amount of multi-speaker audio is additionally synthesized with Qwen3-TTS, and CAM++ is used to extract speaker embeddings as voice conditions. The I2T data follows the same source as the visual instruction data used in [MiniMind-V](https://github.com/jingyaogong/minimind-v); please refer to that project for the original composition and citations. + +The repository ships two training sets, **mini** and **full**. The mini set is filtered from full using the "English + no-vision" criteria and works with `train_sft_omni.py` using the default `--data_path`. Its goal is to verify the Thinker–Talker pipeline, Mimi (de)coding, sequence layout and voice-injection path at low cost, rather than to reproduce the Chinese speech ability of the released models. A Chinese Talker has to handle more complex grapheme-to-phoneme mapping, prosodic pauses and multi-speaker stability, which is clearly harder than English and cannot be expected to converge within ~2 hours on a single RTX 3090. + +The full set corresponds to the released `minimind-3o` / `minimind-3o-moe` checkpoints and covers Chinese-English T2A / A2A as well as image-to-text. Sizes and language ratios are listed below; this is the actual training source behind the CER / voice-similarity numbers reported in the paper. + +T2A means Text-to-Audio, A2A means Audio-to-Audio, and I2T means Image-to-Text. + +| Dataset | Subset | Input speech | Output speech | Note | +|---|---|---|---|---| +| `sft_t2a_mini` | English T2A | — | ~470.14 h | mini onboarding | +| `sft_a2a_mini` | English A2A | ~74.64 h | ~56.60 h | mini onboarding | +| `sft_t2a` | zh+en T2A | — | ~1636.01 h | full training | +| `sft_a2a` | zh+en A2A | ~1711.97 h | ~423.40 h | full training | +| `sft_i2t` | Image I2T | — | — | full training | + +In `sft_t2a`, Chinese / English / mixed samples account for 45.7% / 46.5% / 7.8% respectively; in `sft_a2a` the ratios are 70.8% / 21.2% / 8.0%. This distribution is directly reflected in behavior: short Chinese and short English replies are usually stable, while longer English speech is more prone to mispronunciation and word omissions. The mini subset keeps only English, so even with a tight budget on parameters and data, the within-language CER stays in a usable range. + +## Ⅱ Training + +The training entry point is `train_sft_omni.py`, and the recommended pipeline can be found in `trainer/train.sh`. Full training is not split into multiple complex pretraining stages; instead, capabilities are introduced incrementally along the data flow: + +![](./images/training_pipeline.jpg) + +- `sft_t2a`: align text with speech output first, so that Talker learns to generate Mimi codes under Thinker's semantic conditions; +- `sft_a2a`: bring in speech inputs, so that the model can enter the same Thinker–Talker reply path from speech instructions; +- `sft_i2t`: align the visual path last; the `vision_proj` mode updates only the vision projector to avoid image data overwriting language and speech abilities. + +Among training modes, `all` updates MiniMind / Talker / projectors, while `audio_proj` and `vision_proj` are used solely to align the corresponding projector. SenseVoice-Small, SigLIP2 and Mimi are kept frozen throughout. The Dense and MoE variants share the same data ordering. The mini commands are meant only to make the pipeline runnable end-to-end and finish in ~2 hours on a single RTX 3090 by default; the released weights correspond to full training. + +T2A and A2A loss curves during full training are shown below for reference: + +![](./images/t2a_training_curves.jpg) + +> `sft_t2a`: text-to-speech-output path + +![](./images/a2a_training_curves.jpg) + +> `sft_a2a`: loss after speech inputs are added + +Early spikes caused by an incompatible weight resume have been removed from the T2A curve. The MoE variant has more total parameters but a similar number of active parameters compared with Dense, which makes it more useful as a capacity-allocation reference. + +## Ⅲ Model weights + +| Format | ModelScope | HuggingFace | +|---|---|---| +| PyTorch (`*.pth`) | [minimind-3o-pytorch](https://www.modelscope.cn/models/gongjy/minimind-3o-pytorch) | [minimind-3o-pytorch](https://huggingface.co/jingyaogong/minimind-3o-pytorch) | +| Transformers | [minimind-o collection](https://modelscope.cn/collections/gongjy/MiniMind-O) | [minimind-o collection](https://huggingface.co/collections/jingyaogong/minimind-o) | + +> The Transformers version contains both `minimind-3o` and `minimind-3o-moe` and is suitable for direct use with `eval_omni.py` and the WebUI. The native PyTorch weights are mainly intended for training, reproducing experiments and continued fine-tuning. + +# 📌 Evaluation + +There is currently no unified evaluation protocol for Omni models: different works differ in the LLM backbone, the audio synthesizer and the system goal. Some focus on the LLM's own knowledge and reasoning and report MMLU, HumanEval and related benchmarks; some emphasize streaming latency and audio quality; some highlight speech-consistency metrics; and others focus on natural interaction or broader Omni generation. Most of these systems are continually trained from a state-of-the-art open-source LLM, while MiniMind's 0.06B backbone is clearly not competitive on complex knowledge QA, math reasoning, code generation or long open-ended replies, and the Talker's naturalness, prosody and stability are also weaker than those of full-scale systems. + +Therefore, the goal here is not to chase a comprehensive leaderboard, but to focus on a few more reproducible local evaluations and use cases: a Talker hidden-size ablation, voice-cloning similarity, CER / WER comparisons under identical questions and identical ASR pipelines, and qualitative A2A, I2A and real-time interaction examples. CER / WER are mainly used to inspect text consistency, while audio quality, naturalness and human preference are left to qualitative samples and actual listening tests. + +## Ⅰ Talker hidden-size ablation + +If only speech generation is considered, scaling Talker to 1024 / 2048 hidden size or stacking more layers would obviously be more stable. But MiniMind-O has to fit the entire Omni pipeline within ~0.1B parameters, and cannot afford to allocate most of the budget to the acoustic side. Once Thinker and Talker are decoupled, language understanding and cross-modal fusion are mainly carried by Thinker, while Talker only renders Mimi codes from semantic conditions; this makes a small Talker possible. The rendering here is not "predict semantic tokens and hand them off to an external acoustic model"—Talker directly produces decodable Mimi acoustic codes, so the real bottleneck is at the output side: it has to handle 8 Mimi codebooks rather than a single next-token-prediction stream. + +384-d is tempting, since the dense version compresses to ~88M; 512-d is also lighter. But the table below shows that smaller does not automatically mean better allocated: short utterances remain acceptable, but medium-to-long ones are more prone to word drops, repetitions and pronunciation drift. 768-d was kept in the end because it matches the MiniMind backbone width and can be initialized from the last 4 layers of Thinker; the parameter count remains around 0.1B, training cost does not increase noticeably, and consistency is clearly more stable. + +| Variant | Talker hidden | Params | Avg CER ↓ | Short ↓ | Mid / Long ↓ | +|---|---|---|---|---|---| +| Dense | 768 | 115.29M | **0.0897** | 0.1528 | 0.0874 / 0.0675 | +| Dense | 512 | 96.13M | 0.1745 | 0.2709 | 0.2455 / 0.0976 | +| Dense | 384 | 88.72M | 0.2767 | 0.3904 | 0.1865 / 0.4046 | +| MoE | 768 | 317.05M-A115.33M | **0.0900** | 0.2075 | 0.0533 / 0.0271 | +| MoE | 512 | 261.32M-A96.17M | 0.1265 | 0.0711 | 0.1490 / 0.1464 | +| MoE | 384 | 240.04M-A88.75M | 0.3280 | 0.3757 | 0.2777 / 0.4313 | + +Dense and MoE CERs should not be compared directly across architectures: under the same question, the two Thinkers may produce different content with different lengths, leading to different synthesis difficulty for Talker. What matters more is the within-architecture trend: 768 clearly outperforms 512 and 384 in both cases. + +## Ⅱ Voice-cloning similarity + +Voice cloning is one of the more beta-quality features in this release. To our knowledge, most open-source Omni models support only fixed output voices, while minimind-3o tries to fit multi-voice generation into a single Talker. This goal is harder than simply "being able to talk", because the model needs not only to say the right content, but also to preserve speaker cues while generating Mimi codes. + +Quality has not yet reached high-fidelity cloning: the same reference voice does not always stay consistent across questions, and longer utterances can drift because of pronunciation and rhythm issues. But basic male / female differences, intonation tendencies and parts of the prosody are distinguishable. + +The CAM++ speaker-embedding cosine similarity below is only an automatic reference. Seen comes from the 5 built-in voices in `voices.pt`; Unseen comes from 7 voices in `voices_unseen.pt` that were never seen during training. Each voice uses the same set of text questions and only the voice condition is swapped. + +Per-speaker breakdown: + +| Split | Speaker | Dense ↑ | MoE ↑ | +|---|---|---|---| +| Seen | dylan | 0.6997 | 0.6837 | +| Seen | eric | 0.5289 | 0.4232 | +| Seen | serena | 0.7092 | 0.7041 | +| Seen | uncle_fu | 0.7241 | 0.7337 | +| Seen | vivian | 0.5744 | 0.5888 | +| Unseen | arthur | 0.7171 | 0.6750 | +| Unseen | chelsie | 0.6437 | 0.6240 | +| Unseen | cherry | 0.5689 | 0.5678 | +| Unseen | ethan | 0.4783 | 0.4847 | +| Unseen | jennifer | 0.4749 | 0.4003 | +| Unseen | momo | 0.6470 | 0.5720 | +| Unseen | moon | 0.4282 | 0.6673 | + +Overall, minimind-3o and minimind-3o-moe land at similar averages, both slightly above the early baseline. This suggests that voice retention is not primarily determined by inactive expert capacity; the more direct factors are reference-clip quality, the separability of CAM++ embeddings, and the stability of Talker generation itself. Per-speaker, voices like uncle_fu, serena and arthur are easier to preserve, with at least one variant exceeding 0.70; outliers like eric and moon are more sensitive to generation quality. In other words, this capability already separates some speaker characteristics, but is still some distance away from a product-level "given a reference clip, faithfully reproduce its timbre" experience. + +### Voice-cloning ablation samples (audio playback) + +For a more direct listening test, seed=42 and temperature=0.7 are fixed, and one generated sample is shown per voice. The only variables are the reference audio codes and speaker embedding. As a control, the default output without any reference voice condition is shown first (the spoken text is identical across all samples): + +https://github.com/user-attachments/assets/b31fd8f2-e3af-4fed-ba19-65424b59bec6 + +#### Seen voices +"Seen" means voices that appeared in training data, used to inspect how well the model preserves familiar speakers. + + + + + + + + +
SpeakerReferenceOutputAvg
dylan + +https://github.com/user-attachments/assets/070ea3ab-0e8e-4aa0-84b5-af8d3c4e2725 + + + +https://github.com/user-attachments/assets/eb2da7ed-173c-47e9-9431-7bdb5a9b7385 + +0.6712
eric + +https://github.com/user-attachments/assets/c74aa5dc-1edd-44c1-9546-6e57194c2f60 + + + +https://github.com/user-attachments/assets/f3fa8906-4e14-4610-a9d9-c16c915ca1b3 + +0.4430
serena + +https://github.com/user-attachments/assets/0eeeac87-fa70-4025-b66e-1f0197f2b434 + + + +https://github.com/user-attachments/assets/c5901dca-4b2a-47f5-9b30-c89de54f908e + +0.6600
uncle_fu + +https://github.com/user-attachments/assets/fdd1bb28-6648-44bf-8bcb-4509e709e347 + + + +https://github.com/user-attachments/assets/95b480f1-f015-4712-8d7c-17db465f6584 + +0.6632
vivian + +https://github.com/user-attachments/assets/f64731c4-67a3-4e18-b7d7-61bf44ef4bdd + + + +https://github.com/user-attachments/assets/3f1cc9bb-16d2-4ce0-a473-40676cf4523e + +0.5320
+ +#### Unseen voices +"Unseen" means voices not seen during training, used to inspect zero-shot transfer of a new reference voice into generated speech. + + + + + + + + + + +
SpeakerReferenceOutputAvg
arthur + +https://github.com/user-attachments/assets/46e720ba-c413-4731-8275-a9a2cb483690 + + + +https://github.com/user-attachments/assets/f489b463-c7d7-4ad6-9e4d-53333172c66c + +0.6479
chelsie + +https://github.com/user-attachments/assets/3430ecdb-6de8-4fb0-a6a7-ad82bdce01a1 + + + +https://github.com/user-attachments/assets/e598dbc2-ba28-4c38-b52d-6fa6c2349a5b + +0.5975
cherry + +https://github.com/user-attachments/assets/f9166af6-3a98-42f3-9cf8-ad105eea87d6 + + + +https://github.com/user-attachments/assets/eccca693-4708-409a-88f7-85eb25f66fe6 + +0.5418
ethan + +https://github.com/user-attachments/assets/9c992505-2046-483e-a7cf-50ec18a5e329 + + + +https://github.com/user-attachments/assets/98013c5e-f5b5-4e1a-bc0e-a0f0be5d3240 + +0.4323
jennifer + +https://github.com/user-attachments/assets/924b035d-5c7c-45a5-a8f8-5dbdc18f71db + + + +https://github.com/user-attachments/assets/853d1370-0065-4567-9a71-dc88a6a34d56 + +0.4052
momo + +https://github.com/user-attachments/assets/7e97f524-da6d-4a2f-9095-e7f99262f4a5 + + + +https://github.com/user-attachments/assets/4c193c8f-8750-4424-acba-2bd13089a634 + +0.5968
moon + +https://github.com/user-attachments/assets/527df88a-adc0-48d3-9a6a-827ca1ba7fb0 + + + +https://github.com/user-attachments/assets/3f533e26-1ad8-4ab3-baf1-21267734d3ee + +0.5874
+ +## Ⅲ Cross-model English T2A comparison + +We selected 20 English questions, all constrained by `Answer briefly in one short sentence`. The intent is not to evaluate open-ended English ability, but to keep response lengths within a similar range. The three models then synthesize audio, which is uniformly transcribed by Qwen3-ASR; CER / WER between transcription and target text are used to compare Talker-side textual consistency. + +| Length bucket | [Mini-Omni](https://huggingface.co/gpt-omni/mini-omni) CER/WER | [Mini-Omni2](https://huggingface.co/gpt-omni/mini-omni2) CER/WER | minimind-3o CER/WER | +|---|---|---|---| +| short (≤15w) | 0.0195 / 0.0384 (n=8) | 0.0503 / 0.0584 (n=14) | 0.0531 / 0.0417 (n=8) | +| mid (16–30w) | 0.0038 / 0.0052 (n=12) | 0.0062 / 0.0076 (n=6) | 0.1327 / 0.1420 (n=11) | +| long (31–60w) | — | — | 0.0431 / 0.0508 (n=1) | + +For replies of ≤15 words, minimind-3o is already close to Mini-Omni2; the gap really opens up at 16–30 words. This length is no longer a simple phrase, and Talker must keep pronunciation, rhythm and surface form consistent in a complete short sentence simultaneously. This is also the regime where the current 0.1B Talker most easily exposes its instability. + +## Ⅳ Cross-model vision-language comparison + +[Mini-Omni](https://huggingface.co/gpt-omni/mini-omni) does not support a VL path, so the comparison is between [Mini-Omni2](https://huggingface.co/gpt-omni/mini-omni2) (0.5B) and minimind-3o (0.1B). On 9 synthetic images, both models generate English answers, which are then uniformly transcribed and used to compute CER / WER as a vision-to-speech consistency reference. + +| Model | Params | Avg CER ↓ | Avg WER ↓ | +|---|---|---|---| +| [Mini-Omni2](https://huggingface.co/gpt-omni/mini-omni2) | 0.5B | 0.7609 | 0.9756 | +| minimind-3o | 0.1B | 0.8241 | 1.0293 | + +These numbers should not be read as the absolute correctness of open-ended image description. Image captioning has many equivalent expressions, and synonym choices and word order both affect CER / WER, so high absolute values are expected. Under the same automatic pipeline, minimind-3o trails behind Mini-Omni2 but stays in the same order of magnitude, with roughly 1/5 the parameters. + +## Ⅴ Qualitative samples + +![](./images/qual_a2a.jpg) + +In speech-to-speech samples, the input is real speech, Thinker organizes the semantics, and Talker renders speech. Short replies are again the more stable regime; Chinese explanatory questions usually produce coherent answers, while English pronunciation and rhythm are relatively more stable. + + + + + + +
+ +https://github.com/user-attachments/assets/c85809b2-4787-4656-9c7e-55b693798494 + + + +https://github.com/user-attachments/assets/354a5eec-c147-4d18-8c7a-942bd2a0b4b0 + +
+ +![](./images/image2audio_qualitative.jpg) + +The image-QA samples chain visual encoding, text generation and speech rendering inside the same path. The current model usually captures the main object and the rough scene, but fine-grained spatial relations, counts and attributes are still often wrong, which makes it more suitable as a reproducible baseline for tiny-model Omni pipelines. + + + + + + +
+ +https://github.com/user-attachments/assets/244e08b0-5b12-449e-a7a2-2a2139c5d62d + + + +https://github.com/user-attachments/assets/3e8d0a76-282d-4a9d-9726-a954cf80198a + +
+ +## Ⅵ Real-time interaction + +![](./images/realtime_interaction.jpg) + +This is the real-time interaction interface. Once the user stops speaking, Thinker first finishes the semantic-side prefill, Talker then starts to emit audio codes incrementally, and the Mimi decoder writes the 24 kHz waveform as it receives codes. The barge-in example shows another path that is closer to a real conversation: when the user starts speaking again while the model is talking, the system interrupts the current generation and re-enters the prefill–reply flow. Interruption detection here is still based on a simple VAD threshold, not yet semantic-level barge-in; but from an engineering loop-closure perspective, the system can already fall back from speaking to listening and process the next turn. + +### 🧩 Possible future improvements + +The current model still has clear gaps compared with large-scale Omni systems, and there is no need to gloss over them. Long-form speech naturalness, complex visual reasoning, open-ended English mid/long replies and voice stability are not its strong areas. The visual path is closer to a compact vision-to-speech link, and the MoE variant looks more like a capacity-allocation experiment than a same-FLOP optimum. + +These limitations also point to several follow-ups: longer ICL contexts, finer prosody supervision, stronger vision encoders, more stable voice conditions, and systematic sweeps over the Bridge layer and the MTP codebook interface—all of which are worth continuing. + +That said, the value of MiniMind-O lies exactly here. It compresses an entire Omni loop into the 0.1B regime, and ships code, weights and the main training data inside the same inspectable artifact. This means it is not just a demo, but a baseline small enough, transparent enough, and reproducible enough to rebuild from scratch and modify further. For people who want to understand Thinker–Talker decoupling, the MTP codebook interface, in-context voice cloning, and the middle-hidden bridge, it offers a set of design choices that can actually be verified by hand. + +# 📌 Acknowledgements + +> [!TIP] +> If you find `MiniMind-O` helpful, consider giving us a ⭐ on GitHub.
+> Given limited bandwidth there will inevitably be unknown bugs. Discussions, corrections and PRs in Issues are welcome.
+> Your support is what keeps the project moving—thank you! + +## 🤝 Contributors + + + + + +## 😊 Credits + +- [MiniMind](https://github.com/jingyaogong/minimind) / [MiniMind-V](https://github.com/jingyaogong/minimind-v) (backbone, data) +- [Qwen2.5-Omni / Qwen3-Omni](https://github.com/QwenLM/Qwen2.5-Omni) (inspiration, data) +- [Mini-Omni / Mini-Omni2](https://github.com/gpt-omni/mini-omni) (inspiration, data) +- [SLAM-Omni](https://aclanthology.org/2025.findings-acl.115/) (data) +- [SenseVoice](https://arxiv.org/abs/2407.04051) (component) +- [Mimi / Moshi](https://arxiv.org/abs/2410.00037) (component) + +## 🫶 Supporters + + + + + + github contribution grid snake animation + + + + + + + + github contribution grid snake animation + + + + + + + Star History Chart + + +# 🎓 Citation + +If MiniMind-O helps your research or work, please cite: + +```bibtex +% Cite the technical report when referencing the model design or experimental results. +@article{minimind-o-report, + title = {MiniMind-O Technical Report: An Open Small-Scale Speech-Native Omni Model}, + author = {Jingyao Gong}, + journal = {arXiv preprint arXiv:2605.03937}, + year = {2026} +} + +% Cite the GitHub repo when referencing the open-source codebase or released weights. +@misc{minimind-o, + title = {MiniMind-O: Train a Tiny Omni Model from Scratch}, + author = {Jingyao Gong}, + year = {2026}, + url = {https://github.com/jingyaogong/minimind-o}, + note = {GitHub repository, accessed 2026} +} +``` + +# 📜 License + +This repository is released under the [Apache-2.0 License](LICENSE). diff --git a/dataset/eval_omni/audio-en-01_what_do_you_usually_like_to_eat.mp3 b/dataset/eval_omni/audio-en-01_what_do_you_usually_like_to_eat.mp3 new file mode 100644 index 0000000..fe96868 Binary files /dev/null and b/dataset/eval_omni/audio-en-01_what_do_you_usually_like_to_eat.mp3 differ diff --git a/dataset/eval_omni/audio-en-02_what_fields_does_artificial_intelligence_include.mp3 b/dataset/eval_omni/audio-en-02_what_fields_does_artificial_intelligence_include.mp3 new file mode 100644 index 0000000..d508c27 Binary files /dev/null and b/dataset/eval_omni/audio-en-02_what_fields_does_artificial_intelligence_include.mp3 differ diff --git a/dataset/eval_omni/audio-en-03_please_tell_me_a_story_about_a_little_cat.mp3 b/dataset/eval_omni/audio-en-03_please_tell_me_a_story_about_a_little_cat.mp3 new file mode 100644 index 0000000..6420764 Binary files /dev/null and b/dataset/eval_omni/audio-en-03_please_tell_me_a_story_about_a_little_cat.mp3 differ diff --git a/dataset/eval_omni/audio-en-04_please_introduce_spring_in_one_sentence.mp3 b/dataset/eval_omni/audio-en-04_please_introduce_spring_in_one_sentence.mp3 new file mode 100644 index 0000000..e0019b0 Binary files /dev/null and b/dataset/eval_omni/audio-en-04_please_introduce_spring_in_one_sentence.mp3 differ diff --git a/dataset/eval_omni/audio-en-05_why_is_the_sky_blue.mp3 b/dataset/eval_omni/audio-en-05_why_is_the_sky_blue.mp3 new file mode 100644 index 0000000..6d06d90 Binary files /dev/null and b/dataset/eval_omni/audio-en-05_why_is_the_sky_blue.mp3 differ diff --git a/dataset/eval_omni/audio-en-06_how_can_we_maintain_a_healthy_lifestyle.mp3 b/dataset/eval_omni/audio-en-06_how_can_we_maintain_a_healthy_lifestyle.mp3 new file mode 100644 index 0000000..396ca31 Binary files /dev/null and b/dataset/eval_omni/audio-en-06_how_can_we_maintain_a_healthy_lifestyle.mp3 differ diff --git a/dataset/eval_omni/audio-en-07_what_is_a_black_hole.mp3 b/dataset/eval_omni/audio-en-07_what_is_a_black_hole.mp3 new file mode 100644 index 0000000..8825408 Binary files /dev/null and b/dataset/eval_omni/audio-en-07_what_is_a_black_hole.mp3 differ diff --git a/dataset/eval_omni/audio-en-08_why_do_cats_like_catching_mice.mp3 b/dataset/eval_omni/audio-en-08_why_do_cats_like_catching_mice.mp3 new file mode 100644 index 0000000..838b376 Binary files /dev/null and b/dataset/eval_omni/audio-en-08_why_do_cats_like_catching_mice.mp3 differ diff --git a/dataset/eval_omni/audio-en-09_how_can_i_improve_my_study_efficiency.mp3 b/dataset/eval_omni/audio-en-09_how_can_i_improve_my_study_efficiency.mp3 new file mode 100644 index 0000000..cb22af6 Binary files /dev/null and b/dataset/eval_omni/audio-en-09_how_can_i_improve_my_study_efficiency.mp3 differ diff --git a/dataset/eval_omni/audio-en-10_how_many_kinds_of_animals_are_there_on_earth.mp3 b/dataset/eval_omni/audio-en-10_how_many_kinds_of_animals_are_there_on_earth.mp3 new file mode 100644 index 0000000..f9763b4 Binary files /dev/null and b/dataset/eval_omni/audio-en-10_how_many_kinds_of_animals_are_there_on_earth.mp3 differ diff --git a/dataset/eval_omni/audio-en-11_why_does_it_snow_in_winter.mp3 b/dataset/eval_omni/audio-en-11_why_does_it_snow_in_winter.mp3 new file mode 100644 index 0000000..210aefa Binary files /dev/null and b/dataset/eval_omni/audio-en-11_why_does_it_snow_in_winter.mp3 differ diff --git a/dataset/eval_omni/audio-en-12_how_do_i_make_a_good_cup_of_coffee.mp3 b/dataset/eval_omni/audio-en-12_how_do_i_make_a_good_cup_of_coffee.mp3 new file mode 100644 index 0000000..f193c09 Binary files /dev/null and b/dataset/eval_omni/audio-en-12_how_do_i_make_a_good_cup_of_coffee.mp3 differ diff --git a/dataset/eval_omni/audio-en-13_what_is_quantum_mechanics.mp3 b/dataset/eval_omni/audio-en-13_what_is_quantum_mechanics.mp3 new file mode 100644 index 0000000..c78df84 Binary files /dev/null and b/dataset/eval_omni/audio-en-13_what_is_quantum_mechanics.mp3 differ diff --git a/dataset/eval_omni/audio-en-14_why_do_stars_twinkle.mp3 b/dataset/eval_omni/audio-en-14_why_do_stars_twinkle.mp3 new file mode 100644 index 0000000..91bf76e Binary files /dev/null and b/dataset/eval_omni/audio-en-14_why_do_stars_twinkle.mp3 differ diff --git a/dataset/eval_omni/audio-zh-01_你平时喜欢吃什么.mp3 b/dataset/eval_omni/audio-zh-01_你平时喜欢吃什么.mp3 new file mode 100644 index 0000000..daed9e3 Binary files /dev/null and b/dataset/eval_omni/audio-zh-01_你平时喜欢吃什么.mp3 differ diff --git a/dataset/eval_omni/audio-zh-02_人工智能包含哪些领域.mp3 b/dataset/eval_omni/audio-zh-02_人工智能包含哪些领域.mp3 new file mode 100644 index 0000000..8dbeaa6 Binary files /dev/null and b/dataset/eval_omni/audio-zh-02_人工智能包含哪些领域.mp3 differ diff --git a/dataset/eval_omni/audio-zh-03_给我讲一个关于小猫的故事吧.mp3 b/dataset/eval_omni/audio-zh-03_给我讲一个关于小猫的故事吧.mp3 new file mode 100644 index 0000000..1ef92ea Binary files /dev/null and b/dataset/eval_omni/audio-zh-03_给我讲一个关于小猫的故事吧.mp3 differ diff --git a/dataset/eval_omni/audio-zh-04_请用一句话介绍一下春天.mp3 b/dataset/eval_omni/audio-zh-04_请用一句话介绍一下春天.mp3 new file mode 100644 index 0000000..6d62cc4 Binary files /dev/null and b/dataset/eval_omni/audio-zh-04_请用一句话介绍一下春天.mp3 differ diff --git a/dataset/eval_omni/audio-zh-05_为什么天空是蓝色的.mp3 b/dataset/eval_omni/audio-zh-05_为什么天空是蓝色的.mp3 new file mode 100644 index 0000000..9e9b76c Binary files /dev/null and b/dataset/eval_omni/audio-zh-05_为什么天空是蓝色的.mp3 differ diff --git a/dataset/eval_omni/audio-zh-06_如何保持健康的生活方式.mp3 b/dataset/eval_omni/audio-zh-06_如何保持健康的生活方式.mp3 new file mode 100644 index 0000000..1a4dac5 Binary files /dev/null and b/dataset/eval_omni/audio-zh-06_如何保持健康的生活方式.mp3 differ diff --git a/dataset/eval_omni/audio-zh-07_什么是黑洞.mp3 b/dataset/eval_omni/audio-zh-07_什么是黑洞.mp3 new file mode 100644 index 0000000..987d788 Binary files /dev/null and b/dataset/eval_omni/audio-zh-07_什么是黑洞.mp3 differ diff --git a/dataset/eval_omni/audio-zh-08_为什么猫喜欢抓老鼠.mp3 b/dataset/eval_omni/audio-zh-08_为什么猫喜欢抓老鼠.mp3 new file mode 100644 index 0000000..0637d02 Binary files /dev/null and b/dataset/eval_omni/audio-zh-08_为什么猫喜欢抓老鼠.mp3 differ diff --git a/dataset/eval_omni/audio-zh-09_怎样才能提高学习效率.mp3 b/dataset/eval_omni/audio-zh-09_怎样才能提高学习效率.mp3 new file mode 100644 index 0000000..0fec232 Binary files /dev/null and b/dataset/eval_omni/audio-zh-09_怎样才能提高学习效率.mp3 differ diff --git a/dataset/eval_omni/audio-zh-10_地球上有多少种动物.mp3 b/dataset/eval_omni/audio-zh-10_地球上有多少种动物.mp3 new file mode 100644 index 0000000..6d9d478 Binary files /dev/null and b/dataset/eval_omni/audio-zh-10_地球上有多少种动物.mp3 differ diff --git a/dataset/eval_omni/audio-zh-11_为什么冬天会下雪.mp3 b/dataset/eval_omni/audio-zh-11_为什么冬天会下雪.mp3 new file mode 100644 index 0000000..8e85e70 Binary files /dev/null and b/dataset/eval_omni/audio-zh-11_为什么冬天会下雪.mp3 differ diff --git a/dataset/eval_omni/audio-zh-12_如何制作一杯美味的咖啡.mp3 b/dataset/eval_omni/audio-zh-12_如何制作一杯美味的咖啡.mp3 new file mode 100644 index 0000000..d4a3c76 Binary files /dev/null and b/dataset/eval_omni/audio-zh-12_如何制作一杯美味的咖啡.mp3 differ diff --git a/dataset/eval_omni/audio-zh-13_什么是量子力学.mp3 b/dataset/eval_omni/audio-zh-13_什么是量子力学.mp3 new file mode 100644 index 0000000..852df5f Binary files /dev/null and b/dataset/eval_omni/audio-zh-13_什么是量子力学.mp3 differ diff --git a/dataset/eval_omni/audio-zh-14_为什么星星会闪烁.mp3 b/dataset/eval_omni/audio-zh-14_为什么星星会闪烁.mp3 new file mode 100644 index 0000000..3b08eb5 Binary files /dev/null and b/dataset/eval_omni/audio-zh-14_为什么星星会闪烁.mp3 differ diff --git a/dataset/eval_omni/image-01-orange-cat-moon-desk.jpg b/dataset/eval_omni/image-01-orange-cat-moon-desk.jpg new file mode 100644 index 0000000..e070ca8 Binary files /dev/null and b/dataset/eval_omni/image-01-orange-cat-moon-desk.jpg differ diff --git a/dataset/eval_omni/image-02-fruit-basket-apples-bananas-oranges.jpg b/dataset/eval_omni/image-02-fruit-basket-apples-bananas-oranges.jpg new file mode 100644 index 0000000..722569e Binary files /dev/null and b/dataset/eval_omni/image-02-fruit-basket-apples-bananas-oranges.jpg differ diff --git a/dataset/eval_omni/image-03-panda-holding-hello-sign.jpg b/dataset/eval_omni/image-03-panda-holding-hello-sign.jpg new file mode 100644 index 0000000..0447da4 Binary files /dev/null and b/dataset/eval_omni/image-03-panda-holding-hello-sign.jpg differ diff --git a/dataset/eval_omni/image-04-astronaut-riding-bicycle.jpg b/dataset/eval_omni/image-04-astronaut-riding-bicycle.jpg new file mode 100644 index 0000000..692d196 Binary files /dev/null and b/dataset/eval_omni/image-04-astronaut-riding-bicycle.jpg differ diff --git a/dataset/eval_omni/image-05-robot-chef-making-breakfast.jpg b/dataset/eval_omni/image-05-robot-chef-making-breakfast.jpg new file mode 100644 index 0000000..f9af531 Binary files /dev/null and b/dataset/eval_omni/image-05-robot-chef-making-breakfast.jpg differ diff --git a/dataset/eval_omni/image-06-green-dinosaur-toy-balloons.jpg b/dataset/eval_omni/image-06-green-dinosaur-toy-balloons.jpg new file mode 100644 index 0000000..17672c0 Binary files /dev/null and b/dataset/eval_omni/image-06-green-dinosaur-toy-balloons.jpg differ diff --git a/dataset/eval_omni/image-07-golden-retriever-red-scarf-snow.jpg b/dataset/eval_omni/image-07-golden-retriever-red-scarf-snow.jpg new file mode 100644 index 0000000..83ef90e Binary files /dev/null and b/dataset/eval_omni/image-07-golden-retriever-red-scarf-snow.jpg differ diff --git a/dataset/eval_omni/image-08-coffee-cup-laptop.jpg b/dataset/eval_omni/image-08-coffee-cup-laptop.jpg new file mode 100644 index 0000000..f60a2d4 Binary files /dev/null and b/dataset/eval_omni/image-08-coffee-cup-laptop.jpg differ diff --git a/dataset/eval_omni/image-09-white-rabbit-grass.jpg b/dataset/eval_omni/image-09-white-rabbit-grass.jpg new file mode 100644 index 0000000..e66219e Binary files /dev/null and b/dataset/eval_omni/image-09-white-rabbit-grass.jpg differ diff --git a/dataset/eval_omni/img-01_图中是什么东西.mp3 b/dataset/eval_omni/img-01_图中是什么东西.mp3 new file mode 100644 index 0000000..2439c8b Binary files /dev/null and b/dataset/eval_omni/img-01_图中是什么东西.mp3 differ diff --git a/dataset/eval_omni/img-02_图中有什么.mp3 b/dataset/eval_omni/img-02_图中有什么.mp3 new file mode 100644 index 0000000..d7c8101 Binary files /dev/null and b/dataset/eval_omni/img-02_图中有什么.mp3 differ diff --git a/dataset/eval_omni/img-03_描述一下这个图.mp3 b/dataset/eval_omni/img-03_描述一下这个图.mp3 new file mode 100644 index 0000000..0f846a8 Binary files /dev/null and b/dataset/eval_omni/img-03_描述一下这个图.mp3 differ diff --git a/dataset/eval_omni/img-04_这张图片里有几个物体.mp3 b/dataset/eval_omni/img-04_这张图片里有几个物体.mp3 new file mode 100644 index 0000000..fcf5d3b Binary files /dev/null and b/dataset/eval_omni/img-04_这张图片里有几个物体.mp3 differ diff --git a/dataset/eval_omni/img-05_图片中的场景是什么.mp3 b/dataset/eval_omni/img-05_图片中的场景是什么.mp3 new file mode 100644 index 0000000..34e38bc Binary files /dev/null and b/dataset/eval_omni/img-05_图片中的场景是什么.mp3 differ diff --git a/dataset/eval_omni/img-06_请告诉我图里的内容.mp3 b/dataset/eval_omni/img-06_请告诉我图里的内容.mp3 new file mode 100644 index 0000000..cf2df4a Binary files /dev/null and b/dataset/eval_omni/img-06_请告诉我图里的内容.mp3 differ diff --git a/dataset/eval_omni/img-07_please_describe_this_image.mp3 b/dataset/eval_omni/img-07_please_describe_this_image.mp3 new file mode 100644 index 0000000..fec2a24 Binary files /dev/null and b/dataset/eval_omni/img-07_please_describe_this_image.mp3 differ diff --git a/dataset/omni_dataset.py b/dataset/omni_dataset.py new file mode 100644 index 0000000..359bc1b --- /dev/null +++ b/dataset/omni_dataset.py @@ -0,0 +1,344 @@ +import torch +import os +import math +import random +import numpy as np +import soundfile as sf +import librosa +import json +import io +from PIL import Image +from scipy.signal import resample +from torch.utils.data import Dataset +import pyarrow as pa +import pyarrow.parquet as pq + +os.environ["TOKENIZERS_PARALLELISM"] = "false" + +def pre_processing_chat(conversations, add_system_ratio=0.2): + if any(conv.get('tools') for conv in conversations): return conversations + + SYSTEM_PROMPTS = [ + "你是一个知识丰富的AI,尽力为用户提供准确的信息。", + "你是minimind,一个小巧但有用的语言模型。", + "你是一个专业的AI助手,请提供有价值的回答。", + "你是minimind,请尽力帮助用户解决问题。", + "你是一个可靠的AI,请给出准确的回答。", + "You are a helpful AI assistant.", + "You are minimind, a lightweight intelligent assistant.", + "You are a friendly chatbot. Please answer the user's questions carefully.", + "You are a knowledgeable AI. Try your best to provide accurate information.", + "You are minimind, a small but useful language model." + ] + if conversations[0].get('role') != 'system': + if random.random() < add_system_ratio: + return [{'role': 'system', 'content': random.choice(SYSTEM_PROMPTS)}] + conversations + return conversations + +def post_processing_chat(prompt_content, empty_think_ratio=0.2): + if '\n\n\n\n' in prompt_content and random.random() > empty_think_ratio: + prompt_content = prompt_content.replace('\n\n\n\n', '') + return prompt_content + + +class OmniDataset(Dataset): + def __init__(self, data_path, tokenizer, audio_processor=None, vision_processor=None, + max_length=1200, audio_special_token='<|audio_pad|>', image_special_token='<|image_pad|>', + audio_stop_token=2050, # <|audio_stop|> + audio_pad_token=2049, # <|audio_pad|> + audio_spk_token=2051, # <|audio_spk|> + audio_vocab_size=2112, # 2048 mimi codes + 64 special tokens + scheduled_sampling=0.05, + image_token_len=64): + super().__init__() + tables = [pa.Table.from_batches(pq.ParquetFile(p.strip()).iter_batches()) for p in data_path.split(',')] + tables = [t.cast(pa.schema([f.with_type(pa.large_string()) if pa.types.is_string(f.type) else f for f in t.schema])) for t in tables] + self.table = pa.concat_tables(tables, promote_options='default') + self.tokenizer = tokenizer + self.audio_processor = audio_processor + self.vision_processor = vision_processor + self.max_length = max_length + self.audio_token = audio_special_token + self.image_token_len = image_token_len + self.image_token = image_special_token * image_token_len + self.audio_stop_token = audio_stop_token + self.audio_pad_token = audio_pad_token + self.audio_spk_token = audio_spk_token + self.audio_vocab_size = audio_vocab_size + self.scheduled_sampling_prob = scheduled_sampling + self.text_vocab_size = len(tokenizer) + self.image_token_id = tokenizer.encode(image_special_token, add_special_tokens=False)[0] + self.audio_token_id = tokenizer.encode(audio_special_token, add_special_tokens=False)[0] + self.think_end_ids = tokenizer.encode('\n\n', add_special_tokens=False) + self.bos_id = tokenizer(f'{tokenizer.bos_token}assistant\n', add_special_tokens=False).input_ids + self.eos_id = tokenizer(f'{tokenizer.eos_token}\n', add_special_tokens=False).input_ids + + def __len__(self): + return len(self.table) + + @staticmethod + def process_audio(audio_path, audio_processor): + """加载音频并预处理成fbank,返回 (fbank (T,560), valid_len=encoder输出帧数)""" + wav, sr = sf.read(audio_path) + if wav.ndim > 1: wav = wav.mean(axis=1) + if sr != 16000: wav = librosa.resample(wav.astype(float), orig_sr=sr, target_sr=16000) + inputs = audio_processor(wav.astype(np.float32), sampling_rate=16000, return_tensors="pt", return_attention_mask=True) + valid_len = inputs.attention_mask.sum().item() + return inputs.input_features.squeeze(0), valid_len + + def augment_wav(self, wav, sr=16000): + # 随机变速(0.7~1.6x):改变音频时长和音调,覆盖快/慢语速 + if random.random() < 0.5: + speed = random.uniform(0.7, 1.6) + wav = resample(wav, int(len(wav) / speed)).astype(np.float32) + # 随机加噪:叠加轻微高斯白噪声,模拟录音环境差异 + if random.random() < 0.3: + noise = np.random.randn(len(wav)).astype(np.float32) * random.uniform(0.001, 0.01) + wav = wav + noise + # 随机音量:缩放振幅0.8~1.2倍,模拟说话音量变化 + if random.random() < 0.3: + wav = wav * random.uniform(0.8, 1.2) + # 随机时间遮蔽:将0.25秒片段置零,模拟短暂静音/丢包 + if random.random() < 0.2 and len(wav) > sr: + start = random.randint(0, len(wav) - sr // 4) + wav[start:start + sr // 4] = 0 + # 随机低通滤波:移动平均模糊高频,模拟电话/低质量麦克风 + if random.random() < 0.2: + k = random.choice([3, 5, 7]) + wav = np.convolve(wav, np.ones(k) / k, mode='same').astype(np.float32) + # 随机混响:合成指数衰减脉冲响应卷积,模拟房间反射/回声 + if random.random() < 0.3: + ir_len = int(sr * random.uniform(0.05, 0.2)) + ir = np.random.randn(ir_len).astype(np.float32) * np.exp(-np.linspace(0, 10, ir_len)) + ir[0] = 1.0 + ir /= np.sqrt(np.sum(ir ** 2) + 1e-6) + wav = np.convolve(wav, ir, mode='same').astype(np.float32) + # 随机粉红噪声:1/f噪声模拟房间环境底噪(空调/远处人声) + if random.random() < 0.2: + pink = np.cumsum(np.random.randn(len(wav))).astype(np.float32) + pink /= np.max(np.abs(pink)) + 1e-6 + wav = wav + pink * random.uniform(0.003, 0.015) + return np.clip(wav, -1.0, 1.0).astype(np.float32) + + def augment_mel(self, fbank): + # fbank: (T, 560) — SenseVoice LFR 后的特征(时间维在前,频率维在后) + T, D = fbank.shape + # SpecAugment频率遮蔽:随机抹掉1~64维,防止对特定频段过拟合 + if random.random() < 0.5: + f = random.randint(1, 64) + f0 = random.randint(0, D - f) + fbank[:, f0:f0 + f] = 0 + # SpecAugment时间遮蔽:随机抹掉1~min(10,T)帧,提升对不完整输入的容错 + if random.random() < 0.5 and T > 1: + t = random.randint(1, min(10, T)) + t0 = random.randint(0, T - t) + fbank[t0:t0 + t, :] = 0 + return fbank + + def load_audio_inputs(self, audio_bytes): + if not audio_bytes: return None, 0 + wav, sr = sf.read(io.BytesIO(audio_bytes)) + if wav.ndim > 1: wav = wav.mean(axis=1) + if sr != 16000: wav = librosa.resample(wav.astype(float), orig_sr=sr, target_sr=16000) + wav = self.augment_wav(wav.astype(np.float32)) + inputs = self.audio_processor(wav, sampling_rate=16000, return_tensors="pt", return_attention_mask=True) + valid_len = inputs.attention_mask.sum().item() + return self.augment_mel(inputs.input_features.squeeze(0)), valid_len + + def load_image_inputs(self, image_bytes): + if not image_bytes or self.vision_processor is None: return None + image = Image.open(io.BytesIO(image_bytes)).convert('RGB') + inputs = self.vision_processor(images=image, return_tensors="pt") + if hasattr(inputs, 'keys'): return {k: v for k, v in inputs.items()} + return inputs.pixel_values + + def create_chat_prompt(self, conversations, audio_features_length=0): + conversations = pre_processing_chat(conversations) + messages = [] + is_last_user = lambda i: i == max(j for j, t in enumerate(conversations) if t['role'] == 'user') + for idx, turn in enumerate(conversations): + role, content = turn['role'], turn['content'] + if role == 'user' and is_last_user(idx) and audio_features_length > 0: + ap = self.audio_token * audio_features_length + r = random.random() + if r < 0.4: content = ap + elif r < 0.6: content = content + elif r < 0.8: content = ap + '\n\n' + content + else: content = content + '\n\n' + ap + if '' in content: + r = random.random() + if r < 0.2: content = '\n' + content.replace('', '').strip() + elif r < 0.4: content = '\n\n' + content.replace('', '').strip() + elif r < 0.6: content = content.replace('', '').strip() + '\n' + '' + else: content = content.replace('', '').strip() + '\n\n' + '' + messages.append({"role": role, "content": content}) + prompt = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False) + return post_processing_chat(prompt) + + + def generate_text_labels(self, input_ids): + labels = [-100] * len(input_ids) + ranges = [] + i = 0 + while i < len(input_ids): + if input_ids[i:i + len(self.bos_id)] == self.bos_id: + start = i + len(self.bos_id) + end = start + while end < len(input_ids): + if input_ids[end:end + len(self.eos_id)] == self.eos_id: + break + end += 1 + ranges.append((start, end)) + for j in range(start, min(end + len(self.eos_id), self.max_length)): + labels[j] = input_ids[j] + i = end + len(self.eos_id) if end < len(input_ids) else len(input_ids) + else: + i += 1 + return labels, ranges + + def apply_scheduled_sampling(self, input_ids, audio_labels, text_labels): + """Scheduled Sampling: 用随机值替代部分GT,让模型学会从错误历史中恢复""" + if self.scheduled_sampling_prob <= 0: + return input_ids + audio_mask = (audio_labels != -100).any(dim=0) & (torch.rand(input_ids.size(1)) < self.scheduled_sampling_prob) + for i in range(8): + input_ids[i] = torch.where(audio_mask, torch.randint(0, self.audio_vocab_size, input_ids[i].shape), input_ids[i]) + # 保护 image token 的连续性 + text_mask = (text_labels != -100) & (input_ids[8] != self.image_token_id) & (torch.rand(input_ids.size(1)) < self.scheduled_sampling_prob) + input_ids[8] = torch.where(text_mask, torch.randint(0, self.text_vocab_size, input_ids[8].shape), input_ids[8]) + return input_ids + + def __getitem__(self, index: int): + conversations = json.loads(self.table['conversations'][index].as_py()) + question_audios = self.table['question_audios'][index].as_py() if 'question_audios' in self.table.column_names else [] + answer_audios = self.table['answer_audios'][index].as_py() if 'answer_audios' in self.table.column_names else [] + image_bytes = self.table['image_bytes'][index].as_py() if 'image_bytes' in self.table.column_names else [] + if image_bytes and not isinstance(image_bytes, list): image_bytes = [image_bytes] + ref_audios = self.table['ref_audios'][index].as_py() if 'ref_audios' in self.table.column_names else [] + spk_emb_raw = self.table['spk_emb'][index].as_py() if 'spk_emb' in self.table.column_names else [] + + # 随机截断到某一轮(每轮=user+assistant) + asst_indices = [i for i, t in enumerate(conversations) if t['role'] == 'assistant'] + if len(asst_indices) > 1: + rand_idx = random.randint(0, len(asst_indices) - 1) + # 从随机轮次开始,向前回退直到长度安全 + for i in range(rand_idx, -1, -1): + conversations = conversations[:asst_indices[i] + 1] + test_prompt = self.create_chat_prompt(conversations, 0) + if len(self.tokenizer(test_prompt).input_ids) + 100 < self.max_length: + break + + # 加载最后一个user的图像(按user轮次索引访问,与audio一致) + pixel_values = None + user_count = sum(1 for t in conversations if t['role'] == 'user') + if image_bytes and len(image_bytes) > 0 and self.vision_processor: + pixel_values = self.load_image_inputs(image_bytes[0]) + + # 只加载最后一个user的audio(按user轮次索引访问) + audio_inputs, audio_len, audio_features_length = None, 0, 0 + user_count = sum(1 for t in conversations if t['role'] == 'user') + if question_audios and user_count > 0 and user_count <= len(question_audios) and self.audio_processor: + audio_bytes = question_audios[user_count - 1] + if audio_bytes: + mel, valid_len = self.load_audio_inputs(audio_bytes) + if mel is not None: + audio_inputs = mel.unsqueeze(0) + audio_len = valid_len + audio_features_length = valid_len or 1 + + # 混合训练时,无音频样本返回dummy tensor保持batch索引尽可能对齐 (SenseVoice: T x 560) + if audio_inputs is None and self.audio_processor: + audio_inputs = torch.zeros(1, 1, 560) + audio_len = 0 + if pixel_values is None and self.vision_processor: + pixel_values = {'pixel_values': torch.zeros(1, 3, 256, 256)} + + # 从answer_audios获取最后一个assistant的音频codes + last_audio_codes = None + asst_count = sum(1 for t in conversations if t['role'] == 'assistant') + if answer_audios and asst_count > 0 and asst_count <= len(answer_audios): + tokens = answer_audios[asst_count - 1] + if tokens: + audio_codes_8layers = [[] for _ in range(8)] + for i in range(0, len(tokens) - 7, 8): + for j in range(8): audio_codes_8layers[j].append(tokens[i + j]) + for layer in audio_codes_8layers: layer.append(self.audio_stop_token) + last_audio_codes = audio_codes_8layers + + # 生成prompt (text input_ids) + prompt = self.create_chat_prompt(conversations, audio_features_length) + if pixel_values is not None: prompt = prompt.replace('', self.image_token) + input_ids = self.tokenizer(prompt).input_ids[:self.max_length] + + # PAD input_ids到max_length + input_ids += [self.tokenizer.pad_token_id] * (self.max_length - len(input_ids)) + + # 生成labels(只训练最后一个assistant) + text_labels, assistant_ranges = self.generate_text_labels(input_ids) + for start, end in assistant_ranges[:-1]: + text_labels[start:end] = [-100] * (end - start) + + # 生成7层audio targets(只填充最后一个assistant) + Y_audio_layers = [[self.audio_pad_token] * self.max_length for _ in range(8)] + audio_labels = [[-100] * self.max_length for _ in range(8)] + if assistant_ranges and last_audio_codes: + assistant_start, assistant_end = assistant_ranges[-1] + for pos in range(assistant_start, min(assistant_end, assistant_start + 50)): + if input_ids[pos:pos + len(self.think_end_ids)] == self.think_end_ids: + assistant_start = pos + len(self.think_end_ids) + break + # spk_emb 占位 + ref_codes 右对齐(50% 概率 drop ref_codes,只保留 spk) + has_spk = bool(spk_emb_raw) + has_ref = bool(ref_audios) and random.random() > 0.5 + spk_reserve = 1 if has_spk else 0 + if has_ref: + ref_codes = [[] for _ in range(8)] + for i in range(0, len(ref_audios) - 7, 8): + for j in range(8): ref_codes[j].append(ref_audios[i + j]) + ref_len = len(ref_codes[0]) + ref_start = max(spk_reserve, assistant_start - ref_len) + for layer_idx in range(8): + codes = ref_codes[layer_idx][-(assistant_start - ref_start):] if ref_len > (assistant_start - ref_start) else ref_codes[layer_idx] + for i, code in enumerate(codes): + Y_audio_layers[layer_idx][ref_start + i] = code + else: + ref_start = assistant_start + if has_spk and ref_start > 0: + spk_pos = ref_start - 1 + for layer_idx in range(8): + Y_audio_layers[layer_idx][spk_pos] = self.audio_spk_token + # target codes 填充到 assistant_start 之后(参与 loss) + for layer_idx in range(8): + codes = last_audio_codes[layer_idx] + start_pos = assistant_start + layer_idx + 1 + for i, code in enumerate(codes): + if start_pos + i < self.max_length: + Y_audio_layers[layer_idx][start_pos + i] = code + audio_labels[layer_idx][start_pos + i] = code + + # 构造9路输入:input_ids = (9, T) = 8路audio + 1路text + X_audio = torch.tensor([layer[:-1] for layer in Y_audio_layers], dtype=torch.long) # (8, T-1) + X_text = torch.tensor(input_ids[:-1], dtype=torch.long) # (T-1,) + input_ids = torch.cat((X_audio, X_text.unsqueeze(0)), dim=0) # (9, T-1) + text_labels = torch.tensor(text_labels[1:], dtype=torch.long) # (T-1,) + audio_labels = torch.tensor([layer[1:] for layer in audio_labels], dtype=torch.long) # (8, T-1) + + input_ids = self.apply_scheduled_sampling(input_ids, audio_labels, text_labels) + spk_emb = torch.tensor(spk_emb_raw, dtype=torch.float32) if spk_emb_raw else torch.zeros(192) + return input_ids, text_labels, audio_labels, audio_inputs, audio_len, pixel_values, spk_emb + + +# 测试parquet数据读取 +if __name__ == '__main__': + for path in ['sft_a2a.parquet']: + if not os.path.exists(path): continue + t = pa.Table.from_batches(pq.ParquetFile(path).iter_batches()) + conversations = json.loads(t['conversations'][0].as_py()) + answer_audios = t['answer_audios'][0].as_py() if 'answer_audios' in t.column_names else [] + user_msg = conversations[0] + asst_msg = conversations[1] if len(conversations) > 1 else {} + print(f'{path}: {len(t)}条, 列{t.column_names}') + print(f' User: {user_msg["content"][:50]}...') + print(f' Asst: {asst_msg.get("content", "")[:50]}...') + if answer_audios: + print(f' answer_audios: {len(answer_audios)}轮, 首轮{len(answer_audios[0])}tokens') \ No newline at end of file diff --git a/eval_omni.py b/eval_omni.py new file mode 100644 index 0000000..e12d6f9 --- /dev/null +++ b/eval_omni.py @@ -0,0 +1,244 @@ +import argparse +import os +import random +import time +import warnings +import torch +import soundfile as sf +from PIL import Image +from pydub import AudioSegment +from transformers import AutoTokenizer, AutoModelForCausalLM, MimiModel +from model.model_omni import MiniMindOmni, OmniConfig +from dataset.omni_dataset import OmniDataset +from trainer.trainer_utils import setup_seed, log_model_params +warnings.filterwarnings('ignore') + + +def init_model(args): + tokenizer = AutoTokenizer.from_pretrained(args.load_from) + if 'model' in args.load_from: + moe_suffix = '_moe' if args.use_moe else '' + ckp = f'./{args.save_dir}/{args.weight}_{args.hidden_size}{moe_suffix}.pth' + model = MiniMindOmni( + OmniConfig( + hidden_size=args.hidden_size, + num_hidden_layers=args.num_hidden_layers, + use_moe=bool(args.use_moe) + ), + audio_encoder_path="./model/SenseVoiceSmall", + vision_model_path="./model/siglip2-base-p32-256-ve" + ) + model.load_state_dict(torch.load(ckp, map_location=args.device), strict=False) + else: + model = AutoModelForCausalLM.from_pretrained(args.load_from, trust_remote_code=True) + model.audio_encoder, model.audio_processor = MiniMindOmni.load_sensevoice("./model/SenseVoiceSmall") + model.vision_encoder, model.vision_processor = MiniMindOmni.load_vision("./model/siglip2-base-p32-256-ve") + log_model_params(model) + if model.audio_encoder is not None: model.audio_encoder.to(args.device) + if model.vision_encoder is not None: model.vision_encoder.to(args.device) + model.mimi_model = MimiModel.from_pretrained("./model/mimi").eval() + return model.half().eval().to(args.device), tokenizer + + +def eval_sample(model, tokenizer, args, idx, prompt, audio_inputs, output_name, pixel_values=None, history=None, audio_lens=None, ref_codes=None, spk_emb=None): + messages = (history or []) + [{"role": "user", "content": prompt}] + inputs_text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, open_thinking=bool(args.open_thinking)) + x = torch.tensor(tokenizer(inputs_text).data['input_ids'], dtype=torch.long, device=args.device)[None, ...] + + audio_frames = [] + with torch.no_grad(): + res_y = model.generate(x, tokenizer.eos_token_id, max_new_tokens=args.max_new_tokens, + temperature=args.temperature, top_p=args.top_p, stream=True, + return_audio_codes=True, open_thinking=bool(args.open_thinking), + audio_inputs=audio_inputs, audio_lens=audio_lens, pixel_values=pixel_values, + ref_codes=ref_codes, spk_emb=spk_emb) + print('📒 [Thinker]: ', end='', flush=True) + history_idx = 0 + for y, audio_frame in res_y: + if y is not None: + answer = tokenizer.decode(y[0].tolist(), skip_special_tokens=True) + if answer and answer[-1] != '�': + print(answer[history_idx:], end='', flush=True) + history_idx = len(answer) + if audio_frame: + audio_frames.append(audio_frame) + print() + + if audio_frames: + print(f'🎹 [Talker]: {len(audio_frames)} frames', end=" ") + if args.decode_audio: + try: + codes = [f for f in audio_frames if f and len(f) == 8] + if not codes: + print('⚠️ 生成的Mimi codes为空,跳过保存。') + return + mimi_codes = torch.tensor(codes, dtype=torch.long).T.unsqueeze(0).to(args.device) + filtered = torch.where(mimi_codes >= 2049, torch.zeros_like(mimi_codes), mimi_codes) + audio = model.mimi_model.decode(filtered).audio_values + output_path = os.path.join(args.output_dir, output_name) + wav_path = output_path.rsplit('.', 1)[0] + '.wav' + sf.write(wav_path, audio.squeeze().float().cpu().numpy(), 24000) + AudioSegment.from_wav(wav_path).export(output_path, format='mp3', bitrate='64k') + os.remove(wav_path) + print(f'| Audio decoded to: {output_path}') + except Exception as e: + print(f'⚠️ 保存音频失败: {str(e)}') + else: + print("(decode_audio=off)\n") + + +def main(): + parser = argparse.ArgumentParser(description="MiniMind-O Chat") + parser.add_argument('--load_from', default='model', type=str, help="模型加载路径(model=原生torch权重)") + parser.add_argument('--save_dir', default='out', type=str, help="模型权重目录") + parser.add_argument('--weight', default='sft_omni', type=str, help="权重名称前缀") + parser.add_argument('--hidden_size', default=768, type=int, help="隐藏层维度") + parser.add_argument('--num_hidden_layers', default=8, type=int, help="隐藏层数量") + parser.add_argument('--use_moe', default=0, type=int, choices=[0, 1], help="是否使用MoE架构") + parser.add_argument('--max_new_tokens', default=512, type=int, help="最大生成长度") + parser.add_argument('--temperature', default=0.7, type=float, help="Thinker生成温度") + parser.add_argument('--top_p', default=0.85, type=float, help="nucleus采样阈值") + parser.add_argument('--output_dir', default='./output_audio/', type=str, help="输出音频保存目录") + parser.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu', type=str, help="运行设备") + parser.add_argument('--audio_dir', default='./dataset/eval_omni/', type=str, help="测试音频目录") + parser.add_argument('--image_dir', default='./dataset/eval_omni/', type=str, help="测试图像目录") + parser.add_argument('--open_thinking', default=0, type=int, help="是否开启思考模式(0=否,1=是)(思考模式下禁用audio输出)") + parser.add_argument('--decode_audio', default=1, type=int, help="是否解码音频输出(0=否,1=是)") + parser.add_argument('--mode', default='0', type=str, help="评估模式:-1=all 0=text 1=multi 2=audio 3=clone 4=image 5=mix(逗号组合,如 2,5)") + parser.add_argument('--prompt_lang', default=0, type=int, choices=[0, 1, 2], help="问题语言:0=英文 1=中文 2=英文+中文") + args = parser.parse_args() + modes = set(args.mode.replace(',', '').replace('-1', '012345')) + + os.makedirs(args.output_dir, exist_ok=True) + model, tokenizer = init_model(args) + setup_seed(int(time.time()) % 31415926) + + if '0' in modes: + print('\n\n==================== text -> {text, audio} ====================') + test_prompts_en = [ + "Tell me an interesting fact about space.", "How do I make a cup of coffee?", "What's the weather like today?", + "Will it rain tomorrow?", "Tell me a joke.", "Can you sing a song for me?", "Please introduce yourself." + ] + test_prompts_zh = [ + "告诉我一个关于太空的有趣事实。", "如何制作一杯咖啡?", "今天的天气怎么样?", + "明天会下雨吗?", "给我讲个笑话吧", "你能为我唱首歌吗?", "介绍一下你自己" + ] + test_prompts = [test_prompts_en, test_prompts_zh, test_prompts_en + test_prompts_zh][args.prompt_lang] + for idx, prompt in enumerate(test_prompts): + print(f'\n📝 [text-{idx+1}]: {prompt}') + eval_sample(model, tokenizer, args, idx, prompt, None, f"text-{idx:02d}.mp3") + + if '1' in modes: + print('\n\n==================== multi-turn -> {text, audio} ====================') + multi_turn_tests_zh = [ + { + "history": [ + {"role": "user", "content": "你好"}, + {"role": "assistant", "content": "你好!有什么可以帮你的吗?"} + ], + "prompt": "我想找点事做,你有什么建议吗?" + }, + { + "history": [ + {"role": "user", "content": "你好"}, + {"role": "assistant", "content": "你好!有什么可以帮你的吗?"}, + {"role": "user", "content": "我想找点事做,你有什么建议吗?"}, + {"role": "assistant", "content": "可以听听音乐或者看看书,放松一下心情。"} + ], + "prompt": "好的,那我去照做了,谢谢你" + } + ] + multi_turn_tests_en = [ + { + "history": [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hello! How can I help you?"} + ], + "prompt": "I want to find something to do. Do you have any suggestions?" + }, + { + "history": [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hello! How can I help you?"}, + {"role": "user", "content": "I want to find something to do. Do you have any suggestions?"}, + {"role": "assistant", "content": "You can listen to music or read a book to relax a little."} + ], + "prompt": "Okay, I will try that. Thank you." + } + ] + multi_turn_tests = [multi_turn_tests_en, multi_turn_tests_zh, multi_turn_tests_en + multi_turn_tests_zh][args.prompt_lang] + for idx, test in enumerate(multi_turn_tests): + print(f'\n💬 [multi-{idx+1}]') + for msg in test["history"]: print(f' {msg["role"]}: {msg["content"]}') + print(f' user: {test["prompt"]}') + eval_sample(model, tokenizer, args, idx, test["prompt"], None, f"multi-{idx:02d}.mp3", history=test["history"]) + + if '2' in modes: + print('\n\n==================== audio -> {text, audio} ====================') + audio_files_en = sorted([f for f in os.listdir(args.audio_dir) if f.startswith('audio-en-') and f.lower().endswith(('.mp3', '.wav'))]) + audio_files_zh = sorted([f for f in os.listdir(args.audio_dir) if f.startswith('audio-zh-') and f.lower().endswith(('.mp3', '.wav'))]) + audio_files = [audio_files_en, audio_files_zh, audio_files_en + audio_files_zh][args.prompt_lang] + for idx, audio_file in enumerate(audio_files): + print(f'\n🎤 [audio-{idx+1}]: {audio_file}') + mel, valid_len = OmniDataset.process_audio(os.path.join(args.audio_dir, audio_file), model.audio_processor) + audio_inputs = mel.unsqueeze(0).to(args.device) + audio_lens = torch.tensor([valid_len], device=args.device) + audio_token_len = valid_len or 1 + prompt = model.config.audio_special_token * audio_token_len + eval_sample(model, tokenizer, args, idx, prompt, audio_inputs, f"audio-{idx:02d}-{os.path.splitext(audio_file)[0]}.mp3", audio_lens=audio_lens) + + if '3' in modes: + print('\n\n==================== clone voice -> {text, audio} ====================') + clone_prompts_en = ["Hello, please introduce yourself.", "What's the weather like today?", "Tell me a joke."] + clone_prompts_zh = ["你好,请介绍一下你自己。", "今天天气怎么样?", "给我讲个笑话吧。"] + clone_prompts = [clone_prompts_en, clone_prompts_zh, clone_prompts_en + clone_prompts_zh][args.prompt_lang] + voices_pt = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'model', 'speaker', 'voices_unseen.pt') + voices = [('default', None, None)] + if os.path.exists(voices_pt): + voice_data = torch.load(voices_pt, map_location=args.device) + for speaker, v in sorted(voice_data.items()): + rc = v['ref_codes'].unsqueeze(0).to(args.device) + se = v['spk_emb'].half().unsqueeze(0).to(args.device) if 'spk_emb' in v else None + voices.append((speaker, rc, se)) + for speaker, rc, se in voices: + info = f'ref_codes: {rc.shape[2]} frames, spk_emb: {"+" if se is not None else "-"}' if rc is not None else ('spk_emb only' if se is not None else 'default') + print(f'\n🎵 [clone: {speaker}] {info}') + for idx, prompt in enumerate(clone_prompts): + print(f' 📝 [text-{idx+1}]: {prompt}') + history = [{"role": "system", "content": "你是一个专业的语音助手,请用给定的音色风格来回答用户的问题。请尽量详细地回答,给出有价值的信息。"}] + eval_sample(model, tokenizer, args, idx, prompt, None, f"clone-{speaker}-{idx:02d}.mp3", ref_codes=rc, history=history, spk_emb=se) + + if '4' in modes: + print('\n\n==================== image -> {text, audio} ====================') + image_files = sorted([f for f in os.listdir(args.image_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))]) + for idx, image_file in enumerate(image_files): + print(f'\n🖼️ [image-{idx+1}]: {image_file}') + image = Image.open(os.path.join(args.image_dir, image_file)).convert('RGB') + pixel_values = {k: v.to(args.device) for k, v in model.vision_processor(images=image, return_tensors="pt").items()} + prompts = [["Please describe this image."], ["请描述这张图片"], ["Please describe this image.", "请描述这张图片"]][args.prompt_lang] + for lang_idx, prompt_text in enumerate(prompts): + prompt = prompt_text + "\n\n" + model.config.image_special_token * model.config.image_token_len + eval_sample(model, tokenizer, args, idx, prompt, None, f"image-{idx:02d}-{lang_idx}-{os.path.splitext(image_file)[0]}.mp3", pixel_values=pixel_values) + + if '5' in modes: + print('\n\n==================== text+audio+image -> {text, audio} ====================') + img_audio_files = sorted([f for f in os.listdir(args.audio_dir) if f.startswith('img-') and f.lower().endswith(('.mp3', '.wav'))]) + image_files = sorted([f for f in os.listdir(args.image_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))]) + text_hints = [["Please answer me: "], ["请回答我:"], ["Please answer me: ", "请回答我:"]][args.prompt_lang] + for idx, image_file in enumerate(image_files): + audio_file = random.choice(img_audio_files) + image = Image.open(os.path.join(args.image_dir, image_file)).convert('RGB') + pixel_values = {k: v.to(args.device) for k, v in model.vision_processor(images=image, return_tensors="pt").items()} + for lang_idx, text_hint in enumerate(text_hints): + print(f'\n🌀 [mix-{idx+1}-{lang_idx}]: {text_hint} | {audio_file} | {image_file}') + mel, valid_len = OmniDataset.process_audio(os.path.join(args.audio_dir, audio_file), model.audio_processor) + audio_inputs = mel.unsqueeze(0).to(args.device) + audio_lens = torch.tensor([valid_len], device=args.device) + audio_token_len = valid_len or 1 + prompt = text_hint + model.config.audio_special_token * audio_token_len + "\n\n" + model.config.image_special_token * model.config.image_token_len + eval_sample(model, tokenizer, args, idx, prompt, audio_inputs, f"mix-{idx:02d}-{lang_idx}-{os.path.splitext(image_file)[0]}.mp3", pixel_values=pixel_values, audio_lens=audio_lens) + + +if __name__ == "__main__": + main() + diff --git a/images/a2a_training_curves.jpg b/images/a2a_training_curves.jpg new file mode 100644 index 0000000..c432e87 Binary files /dev/null and b/images/a2a_training_curves.jpg differ diff --git a/images/architecture.jpg b/images/architecture.jpg new file mode 100644 index 0000000..cdd9e72 Binary files /dev/null and b/images/architecture.jpg differ diff --git a/images/image2audio_qualitative.jpg b/images/image2audio_qualitative.jpg new file mode 100644 index 0000000..4b3b0bc Binary files /dev/null and b/images/image2audio_qualitative.jpg differ diff --git a/images/input_token_layout.jpg b/images/input_token_layout.jpg new file mode 100644 index 0000000..05aec12 Binary files /dev/null and b/images/input_token_layout.jpg differ diff --git a/images/logo.png b/images/logo.png new file mode 100644 index 0000000..4ed1d22 Binary files /dev/null and b/images/logo.png differ diff --git a/images/omni_io_flow.png b/images/omni_io_flow.png new file mode 100644 index 0000000..4afdcc2 Binary files /dev/null and b/images/omni_io_flow.png differ diff --git a/images/qual_a2a.jpg b/images/qual_a2a.jpg new file mode 100644 index 0000000..972cdde Binary files /dev/null and b/images/qual_a2a.jpg differ diff --git a/images/realtime_interaction.jpg b/images/realtime_interaction.jpg new file mode 100644 index 0000000..ed0b84f Binary files /dev/null and b/images/realtime_interaction.jpg differ diff --git a/images/sequence_format.jpg b/images/sequence_format.jpg new file mode 100644 index 0000000..fd7ae88 Binary files /dev/null and b/images/sequence_format.jpg differ diff --git a/images/t2a_training_curves.jpg b/images/t2a_training_curves.jpg new file mode 100644 index 0000000..1f4beed Binary files /dev/null and b/images/t2a_training_curves.jpg differ diff --git a/images/training_pipeline.jpg b/images/training_pipeline.jpg new file mode 100644 index 0000000..b9f65d8 Binary files /dev/null and b/images/training_pipeline.jpg differ diff --git a/model/__init__.py b/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/model/model_minimind.py b/model/model_minimind.py new file mode 100755 index 0000000..35bb66f --- /dev/null +++ b/model/model_minimind.py @@ -0,0 +1,287 @@ +import math, torch, torch.nn.functional as F +from torch import nn +from transformers.activations import ACT2FN +from transformers import PreTrainedModel, GenerationMixin, PretrainedConfig +from transformers.modeling_outputs import MoeCausalLMOutputWithPast + +# 🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏 +# MiniMind Config +# 🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏 +class MiniMindConfig(PretrainedConfig): + model_type = "minimind" + def __init__(self, hidden_size=768, num_hidden_layers=8, use_moe=False, **kwargs): + super().__init__(**kwargs) + self.hidden_size = hidden_size + self.num_hidden_layers = num_hidden_layers + self.use_moe = use_moe + self.dropout = kwargs.get("dropout", 0.0) + self.vocab_size = kwargs.get("vocab_size", 6400) + self.bos_token_id = kwargs.get("bos_token_id", 1) + self.eos_token_id = kwargs.get("eos_token_id", 2) + self.flash_attn = kwargs.get("flash_attn", True) + self.num_attention_heads = kwargs.get("num_attention_heads", 8) + self.num_key_value_heads = kwargs.get("num_key_value_heads", 4) + self.head_dim = kwargs.get("head_dim", self.hidden_size // self.num_attention_heads) + self.hidden_act = kwargs.get("hidden_act", 'silu') + self.intermediate_size = kwargs.get("intermediate_size", math.ceil(hidden_size * math.pi / 64) * 64) + self.max_position_embeddings = kwargs.get("max_position_embeddings", 32768) + self.rms_norm_eps = kwargs.get("rms_norm_eps", 1e-6) + self.rope_theta = kwargs.get("rope_theta", 1e6) + self.tie_word_embeddings = kwargs.get("tie_word_embeddings", True) + self.inference_rope_scaling = kwargs.get("inference_rope_scaling", False) + self.rope_scaling = { + "beta_fast": 32, + "beta_slow": 1, + "factor": 16, + "original_max_position_embeddings": 2048, + "attention_factor": 1.0, + "type": "yarn" + } if self.inference_rope_scaling else None + ### MoE specific configs (ignored if use_moe = False) + self.num_experts = kwargs.get("num_experts", 4) + self.num_experts_per_tok = kwargs.get("num_experts_per_tok", 1) + self.moe_intermediate_size = kwargs.get("moe_intermediate_size", self.intermediate_size) + self.norm_topk_prob = kwargs.get("norm_topk_prob", True) + self.router_aux_loss_coef = kwargs.get("router_aux_loss_coef", 5e-4) + +# 🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏 +# MiniMind Model +# 🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏🌎🌍🌏 +class RMSNorm(torch.nn.Module): + def __init__(self, dim: int, eps: float = 1e-5): + super().__init__() + self.eps = eps + self.weight = nn.Parameter(torch.ones(dim)) + + def norm(self, x): + return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) + + def forward(self, x): + return (self.weight * self.norm(x.float())).type_as(x) + +def precompute_freqs_cis(dim: int, end: int = int(32 * 1024), rope_base: float = 1e6, rope_scaling: dict = None): + freqs, attn_factor = 1.0 / (rope_base ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)), 1.0 + if rope_scaling is not None: # YaRN: f'(i) = f(i)((1-γ) + γ/s), where γ∈[0,1] is linear ramp + orig_max, factor, beta_fast, beta_slow, attn_factor = ( + rope_scaling.get("original_max_position_embeddings", 2048), rope_scaling.get("factor", 16), + rope_scaling.get("beta_fast", 32.0), rope_scaling.get("beta_slow", 1.0), rope_scaling.get("attention_factor", 1.0) + ) + if end / orig_max > 1.0: + inv_dim = lambda b: (dim * math.log(orig_max / (b * 2 * math.pi))) / (2 * math.log(rope_base)) + low, high = max(math.floor(inv_dim(beta_fast)), 0), min(math.ceil(inv_dim(beta_slow)), dim // 2 - 1) + ramp = torch.clamp((torch.arange(dim // 2, device=freqs.device).float() - low) / max(high - low, 0.001), 0, 1) + freqs = freqs * (1 - ramp + ramp / factor) + t = torch.arange(end, device=freqs.device) + freqs = torch.outer(t, freqs).float() + freqs_cos = torch.cat([torch.cos(freqs), torch.cos(freqs)], dim=-1) * attn_factor + freqs_sin = torch.cat([torch.sin(freqs), torch.sin(freqs)], dim=-1) * attn_factor + return freqs_cos, freqs_sin + +def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1): + def rotate_half(x): return torch.cat((-x[..., x.shape[-1] // 2:], x[..., : x.shape[-1] // 2]), dim=-1) + q_embed = ((q * cos.unsqueeze(unsqueeze_dim)) + (rotate_half(q) * sin.unsqueeze(unsqueeze_dim))).to(q.dtype) + k_embed = ((k * cos.unsqueeze(unsqueeze_dim)) + (rotate_half(k) * sin.unsqueeze(unsqueeze_dim))).to(k.dtype) + return q_embed, k_embed + +def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor: + bs, slen, num_key_value_heads, head_dim = x.shape + if n_rep == 1: return x + return (x[:, :, :, None, :].expand(bs, slen, num_key_value_heads, n_rep, head_dim).reshape(bs, slen, num_key_value_heads * n_rep, head_dim)) + +class Attention(nn.Module): + def __init__(self, config: MiniMindConfig): + super().__init__() + self.num_key_value_heads = config.num_attention_heads if config.num_key_value_heads is None else config.num_key_value_heads + self.n_local_heads = config.num_attention_heads + self.n_local_kv_heads = self.num_key_value_heads + self.n_rep = self.n_local_heads // self.n_local_kv_heads + self.head_dim = config.head_dim + self.is_causal = True + self.q_proj = nn.Linear(config.hidden_size, config.num_attention_heads * self.head_dim, bias=False) + self.k_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False) + self.v_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False) + self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False) + self.q_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps) + self.k_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps) + self.attn_dropout = nn.Dropout(config.dropout) + self.resid_dropout = nn.Dropout(config.dropout) + self.dropout = config.dropout + self.flash = hasattr(torch.nn.functional, 'scaled_dot_product_attention') and config.flash_attn + + def forward(self, x, position_embeddings, past_key_value=None, use_cache=False, attention_mask=None): + bsz, seq_len, _ = x.shape + xq, xk, xv = self.q_proj(x), self.k_proj(x), self.v_proj(x) + xq = xq.view(bsz, seq_len, self.n_local_heads, self.head_dim) + xk = xk.view(bsz, seq_len, self.n_local_kv_heads, self.head_dim) + xv = xv.view(bsz, seq_len, self.n_local_kv_heads, self.head_dim) + xq, xk = self.q_norm(xq), self.k_norm(xk) + cos, sin = position_embeddings + xq, xk = apply_rotary_pos_emb(xq, xk, cos, sin) + if past_key_value is not None: + xk = torch.cat([past_key_value[0], xk], dim=1) + xv = torch.cat([past_key_value[1], xv], dim=1) + past_kv = (xk, xv) if use_cache else None + xq, xk, xv = (xq.transpose(1, 2), repeat_kv(xk, self.n_rep).transpose(1, 2), repeat_kv(xv, self.n_rep).transpose(1, 2)) + if self.flash and (seq_len > 1) and (not self.is_causal or past_key_value is None) and (attention_mask is None or torch.all(attention_mask == 1)): + output = F.scaled_dot_product_attention(xq, xk, xv, dropout_p=self.dropout if self.training else 0.0, is_causal=self.is_causal) + else: + scores = (xq @ xk.transpose(-2, -1)) / math.sqrt(self.head_dim) + if self.is_causal: scores[:, :, :, -seq_len:] += torch.full((seq_len, seq_len), float("-inf"), device=scores.device).triu(1) + if attention_mask is not None: scores += (1.0 - attention_mask.unsqueeze(1).unsqueeze(2)) * -1e9 + output = self.attn_dropout(F.softmax(scores.float(), dim=-1).type_as(xq)) @ xv + output = output.transpose(1, 2).reshape(bsz, seq_len, -1) + output = self.resid_dropout(self.o_proj(output)) + return output, past_kv + +class FeedForward(nn.Module): + def __init__(self, config: MiniMindConfig, intermediate_size: int = None): + super().__init__() + intermediate_size = intermediate_size or config.intermediate_size + self.gate_proj = nn.Linear(config.hidden_size, intermediate_size, bias=False) + self.down_proj = nn.Linear(intermediate_size, config.hidden_size, bias=False) + self.up_proj = nn.Linear(config.hidden_size, intermediate_size, bias=False) + self.act_fn = ACT2FN[config.hidden_act] + + def forward(self, x): + return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x)) + +class MOEFeedForward(nn.Module): + def __init__(self, config: MiniMindConfig): + super().__init__() + self.config = config + self.gate = nn.Linear(config.hidden_size, config.num_experts, bias=False) + self.experts = nn.ModuleList([FeedForward(config, intermediate_size=config.moe_intermediate_size) for _ in range(config.num_experts)]) + self.act_fn = ACT2FN[config.hidden_act] + + def forward(self, x): + batch_size, seq_len, hidden_dim = x.shape + x_flat = x.view(-1, hidden_dim) + scores = F.softmax(self.gate(x_flat), dim=-1) + topk_weight, topk_idx = torch.topk(scores, k=self.config.num_experts_per_tok, dim=-1, sorted=False) + if self.config.norm_topk_prob: topk_weight = topk_weight / (topk_weight.sum(dim=-1, keepdim=True) + 1e-20) + y = torch.zeros_like(x_flat) + for i, expert in enumerate(self.experts): + mask = (topk_idx == i) + if mask.any(): + token_idx = mask.any(dim=-1).nonzero().flatten() + weight = topk_weight[mask].view(-1, 1) + y.index_add_(0, token_idx, (expert(x_flat[token_idx]) * weight).to(y.dtype)) + elif self.training: + y[0, 0] += 0 * sum(p.sum() for p in expert.parameters()) + if self.training and self.config.router_aux_loss_coef > 0: + load = F.one_hot(topk_idx, self.config.num_experts).float().mean(0) + self.aux_loss = (load * scores.mean(0)).sum() * self.config.num_experts * self.config.router_aux_loss_coef + else: + self.aux_loss = scores.new_zeros(1).squeeze() + return y.view(batch_size, seq_len, hidden_dim) + +class MiniMindBlock(nn.Module): + def __init__(self, layer_id: int, config: MiniMindConfig): + super().__init__() + self.self_attn = Attention(config) + self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) + self.post_attention_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) + self.mlp = FeedForward(config) if not config.use_moe else MOEFeedForward(config) + + def forward(self, hidden_states, position_embeddings, past_key_value=None, use_cache=False, attention_mask=None): + residual = hidden_states + hidden_states, present_key_value = self.self_attn( + self.input_layernorm(hidden_states), position_embeddings, + past_key_value, use_cache, attention_mask + ) + hidden_states += residual + hidden_states = hidden_states + self.mlp(self.post_attention_layernorm(hidden_states)) + return hidden_states, present_key_value + +class MiniMindModel(nn.Module): + def __init__(self, config: MiniMindConfig): + super().__init__() + self.config = config + self.vocab_size, self.num_hidden_layers = config.vocab_size, config.num_hidden_layers + self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) + self.dropout = nn.Dropout(config.dropout) + self.layers = nn.ModuleList([MiniMindBlock(l, config) for l in range(self.num_hidden_layers)]) + self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) + freqs_cos, freqs_sin = precompute_freqs_cis(dim=config.head_dim, end=config.max_position_embeddings, rope_base=config.rope_theta, rope_scaling=config.rope_scaling) + self.register_buffer("freqs_cos", freqs_cos, persistent=False) + self.register_buffer("freqs_sin", freqs_sin, persistent=False) + + def forward(self, input_ids, attention_mask=None, past_key_values=None, use_cache=False, **kwargs): + batch_size, seq_length = input_ids.shape + if hasattr(past_key_values, 'layers'): past_key_values = None + past_key_values = past_key_values or [None] * len(self.layers) + start_pos = past_key_values[0][0].shape[1] if past_key_values[0] is not None else 0 + hidden_states = self.dropout(self.embed_tokens(input_ids)) + # Recompute RoPE buffers lost during meta-device init (transformers>=5.x) + if self.freqs_cos[0, 0] == 0: + freqs_cos, freqs_sin = precompute_freqs_cis(dim=self.config.head_dim, end=self.config.max_position_embeddings, rope_base=self.config.rope_theta, rope_scaling=self.config.rope_scaling) + self.freqs_cos, self.freqs_sin = freqs_cos.to(hidden_states.device), freqs_sin.to(hidden_states.device) + position_embeddings = (self.freqs_cos[start_pos:start_pos + seq_length], self.freqs_sin[start_pos:start_pos + seq_length]) + presents = [] + for layer, past_key_value in zip(self.layers, past_key_values): + hidden_states, present = layer( + hidden_states, + position_embeddings, + past_key_value=past_key_value, + use_cache=use_cache, + attention_mask=attention_mask + ) + presents.append(present) + hidden_states = self.norm(hidden_states) + aux_loss = sum([l.mlp.aux_loss for l in self.layers if isinstance(l.mlp, MOEFeedForward)], hidden_states.new_zeros(1).squeeze()) + return hidden_states, presents, aux_loss + +class MiniMindForCausalLM(PreTrainedModel, GenerationMixin): + config_class = MiniMindConfig + _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"} + def __init__(self, config: MiniMindConfig = None): + self.config = config or MiniMindConfig() + super().__init__(self.config) + self.model = MiniMindModel(self.config) + self.lm_head = nn.Linear(self.config.hidden_size, self.config.vocab_size, bias=False) + if self.config.tie_word_embeddings: self.model.embed_tokens.weight = self.lm_head.weight + self.post_init() + + def forward(self, input_ids, attention_mask=None, past_key_values=None, use_cache=False, logits_to_keep=0, labels=None, **kwargs): + hidden_states, past_key_values, aux_loss = self.model(input_ids, attention_mask, past_key_values, use_cache, **kwargs) + slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep + logits = self.lm_head(hidden_states[:, slice_indices, :]) + loss = None + if labels is not None: + x, y = logits[..., :-1, :].contiguous(), labels[..., 1:].contiguous() + loss = F.cross_entropy(x.view(-1, x.size(-1)), y.view(-1), ignore_index=-100) + return MoeCausalLMOutputWithPast(loss=loss, aux_loss=aux_loss, logits=logits, past_key_values=past_key_values, hidden_states=hidden_states) + + # https://github.com/jingyaogong/minimind/discussions/611 + @torch.inference_mode() + def generate(self, inputs=None, attention_mask=None, max_new_tokens=8192, temperature=0.85, top_p=0.85, top_k=50, eos_token_id=2, streamer=None, use_cache=True, num_return_sequences=1, do_sample=True, repetition_penalty=1.0, **kwargs): + input_ids = kwargs.pop("input_ids", inputs).repeat(num_return_sequences, 1) + attention_mask = attention_mask.repeat(num_return_sequences, 1) if attention_mask is not None else None + past_key_values = kwargs.pop("past_key_values", None) + finished = torch.zeros(input_ids.shape[0], dtype=torch.bool, device=input_ids.device) + if streamer: streamer.put(input_ids.cpu()) + for _ in range(max_new_tokens): + past_len = past_key_values[0][0].shape[1] if past_key_values else 0 + outputs = self.forward(input_ids[:, past_len:], attention_mask, past_key_values, use_cache=use_cache, **kwargs) + attention_mask = torch.cat([attention_mask, attention_mask.new_ones(attention_mask.shape[0], 1)], -1) if attention_mask is not None else None + logits = outputs.logits[:, -1, :] / temperature + if repetition_penalty != 1.0: + for i in range(input_ids.shape[0]): logits[i, torch.unique(input_ids[i])] /= repetition_penalty + if top_k > 0: + logits[logits < torch.topk(logits, top_k)[0][..., -1, None]] = -float('inf') + if top_p < 1.0: + sorted_logits, sorted_indices = torch.sort(logits, descending=True) + mask = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1) > top_p + mask[..., 1:], mask[..., 0] = mask[..., :-1].clone(), 0 + logits[mask.scatter(1, sorted_indices, mask)] = -float('inf') + next_token = torch.multinomial(torch.softmax(logits, dim=-1), num_samples=1) if do_sample else torch.argmax(logits, dim=-1, keepdim=True) + if eos_token_id is not None: next_token = torch.where(finished.unsqueeze(-1), next_token.new_full((next_token.shape[0], 1), eos_token_id), next_token) + input_ids = torch.cat([input_ids, next_token], dim=-1) + past_key_values = outputs.past_key_values if use_cache else None + if streamer: streamer.put(next_token.cpu()) + if eos_token_id is not None: + finished |= next_token.squeeze(-1).eq(eos_token_id) + if finished.all(): break + if streamer: streamer.end() + if kwargs.get("return_kv"): return {'generated_ids': input_ids, 'past_kv': past_key_values} + return input_ids \ No newline at end of file diff --git a/model/model_omni.py b/model/model_omni.py new file mode 100644 index 0000000..0077aab --- /dev/null +++ b/model/model_omni.py @@ -0,0 +1,461 @@ +import os, math, torch, soundfile as sf, librosa, warnings, numpy as np, onnxruntime as ort, logging, contextlib, io +from types import SimpleNamespace +from torch import nn +from torch.nn import functional as F +from transformers.modeling_outputs import MoeCausalLMOutputWithPast +from transformers import SiglipImageProcessor, SiglipVisionModel, logging as hf_logging +from .model_minimind import * + + +class OmniConfig(MiniMindConfig): + model_type = "minimind-o" + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.num_talker_hidden_layers = kwargs.get("num_talker_hidden_layers", 4) + self.talker_hidden_size = kwargs.get("talker_hidden_size", 768) + self.audio_ids = kwargs.get("audio_ids", [16]) # "<|audio_pad|>" token id + self.audio_special_token = kwargs.get("audio_special_token", "<|audio_pad|>") + self.audio_hidden_size = kwargs.get("audio_hidden_size", 512) + self.audio_vocab_size = kwargs.get("audio_vocab_size", 2112) + self.audio_pad_token = kwargs.get("audio_pad_token", 2049) + self.audio_stop_token = kwargs.get("audio_stop_token", 2050) + self.audio_spk_token = kwargs.get("audio_spk_token", 2051) + self.spk_emb_size = kwargs.get("spk_emb_size", 192) + self.think_end_ids = kwargs.get("think_end_ids", [26, 234, 234]) # \n\n + self.image_ids = kwargs.get("image_ids", [12]) # "<|image_pad|>" token id + self.image_special_token = kwargs.get("image_special_token", "<|image_pad|>") + self.image_hidden_size = kwargs.get("image_hidden_size", 768) + self.image_token_len = kwargs.get("image_token_len", 64) + self.bridge_layer = kwargs.get("bridge_layer", self.num_hidden_layers // 2 - 1) + +class MMAudioProjector(nn.Module): + def __init__(self, in_dim, out_dim): + super().__init__() + self.mlp = nn.Sequential( + nn.LayerNorm(in_dim), + nn.Linear(in_dim, out_dim), + nn.GELU(), + nn.Linear(out_dim, out_dim), + ) + def forward(self, x): + return self.mlp(x) + + +class MMVisionProjector(nn.Module): + def __init__(self, in_dim, out_dim, source_tokens=64, target_tokens=64): + super().__init__() + self.mlp = nn.Sequential( + nn.LayerNorm(in_dim), + nn.Linear(in_dim, out_dim), + nn.GELU(), + nn.Linear(out_dim, out_dim), + ) + def forward(self, x): + return self.mlp(x) + + +class TalkerHead(nn.Module): + def __init__(self, in_features, out_features, num_layers=8, rank=256): + super().__init__() + self.num_layers = num_layers + self.base = nn.Linear(in_features, out_features, bias=False) + self.adapters = nn.ModuleList([nn.Sequential(nn.Linear(in_features, rank, bias=False), nn.GELU(), nn.Linear(rank, out_features, bias=False)) for _ in range(num_layers)]) + def forward(self, x): + base_out = self.base(x) + return [base_out + adapter(x) for adapter in self.adapters] + + +class TalkerEmbedding(nn.Module): + def __init__(self, num_embeddings, embedding_dim, num_layers=8, rank=256): + super().__init__() + self.num_layers = num_layers + self.base = nn.Embedding(num_embeddings, embedding_dim) + self.adapters = nn.ModuleList([nn.Sequential(nn.Embedding(num_embeddings, rank), nn.GELU(), nn.Linear(rank, embedding_dim, bias=False)) for _ in range(num_layers)]) + def forward(self, x): + base_out = self.base(x) + return sum(base_out[:, i, :] + self.adapters[i](x[:, i, :]) for i in range(len(self.adapters))) / self.num_layers + +class SenseVoiceAudioProcessor: + def __init__(self, frontend): self.frontend = frontend + def __call__(self, wav, sampling_rate=16000, return_tensors="pt", return_attention_mask=True, **kwargs): + if isinstance(wav, np.ndarray): wav = torch.from_numpy(wav).float() + if wav.dim() == 1: wav = wav.unsqueeze(0) + with torch.no_grad(): + fbank, flen = self.frontend(wav, torch.tensor([wav.size(1)])) + return SimpleNamespace(input_features=fbank, attention_mask=(torch.arange(fbank.size(1)) < flen[0]).long().unsqueeze(0)) + + +class TalkerModule(nn.Module): + def __init__(self, config): + super().__init__() + self.talker_config = MiniMindConfig(hidden_size=config.talker_hidden_size, use_moe=config.use_moe) + self.layers = nn.ModuleList([MiniMindBlock(l, self.talker_config) for l in range(config.num_talker_hidden_layers)]) + self.norm = RMSNorm(config.talker_hidden_size, eps=config.rms_norm_eps) + self.lm_head = TalkerHead(config.talker_hidden_size, config.audio_vocab_size) + self.embed_tokens = TalkerEmbedding(config.audio_vocab_size, config.talker_hidden_size) + self.codec_proj = nn.Sequential(nn.Linear(config.talker_hidden_size, config.talker_hidden_size), nn.GELU(), nn.Linear(config.talker_hidden_size, config.talker_hidden_size), RMSNorm(config.talker_hidden_size, eps=config.rms_norm_eps)) + self.embed_proj = nn.Sequential(nn.Linear(config.hidden_size, config.hidden_size), nn.GELU(), nn.Linear(config.hidden_size, config.talker_hidden_size), RMSNorm(config.talker_hidden_size, eps=config.rms_norm_eps)) + self.text_scale, self.audio_scale = nn.Parameter(torch.tensor(3.0)), nn.Parameter(torch.tensor(1.0)) + self.spk_proj = nn.Linear(config.spk_emb_size, config.talker_hidden_size, bias=False) + freqs_cos, freqs_sin = precompute_freqs_cis(dim=self.talker_config.head_dim, end=config.max_position_embeddings, rope_base=config.rope_theta, rope_scaling=config.rope_scaling) + self.register_buffer("freqs_cos", freqs_cos, persistent=False) + self.register_buffer("freqs_sin", freqs_sin, persistent=False) + + +class MiniMindOmni(MiniMindForCausalLM): + config_class = OmniConfig + def __init__(self, config: OmniConfig = None, audio_encoder_path="./model/SenseVoiceSmall", vision_model_path="./model/siglip2-base-p32-256-ve"): + config = config or OmniConfig() + super().__init__(config) + object.__setattr__(self, 'thinker', self.model) # alias: self.thinker == self.model + object.__setattr__(self.model, 'lm_head', self.lm_head) # alias: self.thinker.lm_head == self.lm_head + self.talker = TalkerModule(config) + self.audio_proj = MMAudioProjector(config.audio_hidden_size, config.hidden_size) + self.vision_proj = MMVisionProjector(config.image_hidden_size, config.hidden_size, target_tokens=config.image_token_len) + self.audio_pad_token, self.audio_stop_token, self.audio_spk_token = config.audio_pad_token, config.audio_stop_token, config.audio_spk_token + audio_encoder, audio_processor = self.load_sensevoice(audio_encoder_path) + object.__setattr__(self, 'audio_encoder', audio_encoder) + object.__setattr__(self, 'audio_processor', audio_processor) + vision_encoder, vision_processor = self.load_vision(vision_model_path) + object.__setattr__(self, 'vision_encoder', vision_encoder) + object.__setattr__(self, 'vision_processor', vision_processor) + + @staticmethod + def load_sensevoice(path): + if not os.path.exists(path): + warnings.warn(f"[MiniMindOmni] SenseVoice path not found: {path}") + return None, None + logging.getLogger().setLevel(logging.ERROR) + hf_logging.set_verbosity_error() + with contextlib.redirect_stdout(io.StringIO()): + from funasr import AutoModel + m = AutoModel(model=path, trust_remote_code=True, disable_update=True, device="cpu") + encoder, frontend = m.model.encoder, m.kwargs["frontend"] + for p in encoder.parameters(): p.requires_grad = False + return encoder.eval().float(), SenseVoiceAudioProcessor(frontend.eval()) + + @torch.compiler.disable + def encode_audio_inputs(self, audio_inputs, audio_lens=None): + if (audio_inputs is None) or (self.audio_encoder is None) or (not audio_inputs.any()): return None + batch_mask = audio_inputs.flatten(1).any(1) + enc_dtype = next(self.audio_encoder.parameters()).dtype + valid_fbank = audio_inputs[batch_mask].to(dtype=enc_dtype) + if audio_lens is not None: + valid_lens = audio_lens[batch_mask].to(valid_fbank.device) + else: + valid_lens = torch.tensor([valid_fbank.size(1)] * valid_fbank.size(0), device=valid_fbank.device) + with torch.no_grad(): + emb, _ = self.audio_encoder(valid_fbank, valid_lens) + proj_dtype = next(self.audio_proj.parameters()).dtype + emb_list = [self.audio_proj(emb[i, :max(1, min(valid_lens[i].item(), emb.size(1)))].unsqueeze(0).to(proj_dtype)).squeeze(0) for i in range(emb.size(0))] + if batch_mask.all(): return emb_list + out = [None] * audio_inputs.size(0) + j = 0 + for i in range(audio_inputs.size(0)): + if batch_mask[i]: + out[i] = emb_list[j] + j += 1 + return out + + @torch.compiler.disable + def inject_audio_features(self, tokens, h, audio_feats, seqlen): + if audio_feats is None or not self.config.audio_ids: + return h + marker = self.config.audio_ids[0] + out = [] + for b in range(h.size(0)): + hb, seq, i = h[b], tokens[b].tolist(), 0 + af = audio_feats[b] if audio_feats[b] is not None else None + while i < len(seq): + if seq[i] == marker: + start = i + while i < len(seq) and seq[i] == marker: + i += 1 + if af is not None: + inject_len = min(af.size(0), i - start) + hb = torch.cat((hb[:start], af[:inject_len], hb[start + inject_len:]), dim=0) + af = None + else: + i += 1 + out.append(hb) + return torch.stack(out) + + @staticmethod + def load_vision(path): + if path is None or not os.path.exists(path): + warnings.warn(f"[MiniMindOmni] Vision model path not found: {path}. vision_encoder will be None!") + return None, None + hf_logging.set_verbosity_error() + try: + model = SiglipVisionModel.from_pretrained(path) + except (RuntimeError, ValueError): + return None, None + processor = SiglipImageProcessor.from_pretrained(path) + for p in model.parameters(): + p.requires_grad = False + return model.eval(), processor + + @torch.compiler.disable + def get_image_embeddings(self, image_inputs): + if hasattr(image_inputs, 'keys'): + image_inputs = {k: v.squeeze(1) if v.ndim > 2 and v.shape[1] == 1 else v for k, v in image_inputs.items()} + pixel_attention_mask = image_inputs.get('pixel_attention_mask') + if pixel_attention_mask is not None and not pixel_attention_mask.any(): + pv = image_inputs['pixel_values'] + return pv.new_zeros(pv.size(0), pv.size(1), self.config.image_hidden_size) + with torch.no_grad(): + outputs = self.vision_encoder(**image_inputs) + return outputs.last_hidden_state + + @torch.compiler.disable + def encode_image_inputs(self, pixel_values): + if pixel_values is None or self.vision_encoder is None: return None + mask = pixel_values.flatten(1).any(1) + if not mask.any(): return pixel_values.new_zeros(pixel_values.size(0), self.config.image_token_len, self.config.hidden_size) + with torch.no_grad(): emb = self.vision_encoder(pixel_values=pixel_values[mask]).last_hidden_state + if emb.dim() == 2: emb = emb.unsqueeze(0) + emb = self.vision_proj(emb) + if mask.all(): return emb + idx = mask.nonzero().view(-1, 1, 1).expand_as(emb) + return emb.new_zeros(pixel_values.size(0), *emb.shape[1:]).scatter(0, idx, emb) + + @torch.compiler.disable + def count_vision_proj(self, tokens, h, vision_tensors=None, seqlen=512): + if vision_tensors is None or not self.config.image_ids: + return h + marker, vf = self.config.image_ids[0], vision_tensors + if vf.dim() == 3: + vf = vf.unsqueeze(1) + out = [] + for b in range(h.size(0)): + hb, seq, k, i = h[b], tokens[b].tolist(), 0, 0 + while i < len(seq): + if seq[i] == marker: + start = i + while i < len(seq) and seq[i] == marker: + i += 1 + if k < vf.size(1): + hb = torch.cat((hb[:start], vf[b][k][:i - start], hb[i:]), dim=0)[:seqlen] + k += 1 + else: + i += 1 + out.append(hb) + return torch.stack(out) + + def forward(self, input_ids, attention_mask=None, past_key_values=None, use_cache=False, logits_to_keep=0, audio_inputs=None, audio_lens=None, pixel_values=None, **args): + if len(input_ids.shape) == 2: + batch_size, seq_length = input_ids.shape + text_ids = input_ids + audio_ids = torch.full((batch_size, 8, seq_length), self.audio_pad_token, dtype=torch.long, device=input_ids.device) + else: + batch_size, _, seq_length = input_ids.shape + text_ids, audio_ids = input_ids[:, 8, :], input_ids[:, :8, :] + if hasattr(past_key_values, 'layers'): past_key_values = None + n_thinker, n_talker = len(self.thinker.layers), len(self.talker.layers) + past_key_values = past_key_values or ([None] * (n_thinker + n_talker)) + start_pos = past_key_values[0][0].shape[1] if past_key_values[0] is not None else 0 + # Recompute RoPE buffers lost during meta-device init (transformers>=5.x) + if self.thinker.freqs_cos[0, 0] == 0: + freqs_cos, freqs_sin = precompute_freqs_cis(dim=self.config.head_dim, end=self.config.max_position_embeddings, rope_base=self.config.rope_theta, rope_scaling=self.config.rope_scaling) + self.thinker.freqs_cos, self.thinker.freqs_sin = freqs_cos.to(input_ids.device), freqs_sin.to(input_ids.device) + if self.talker.freqs_cos[0, 0] == 0: + freqs_cos, freqs_sin = precompute_freqs_cis(dim=self.talker.talker_config.head_dim, end=self.config.max_position_embeddings, rope_base=self.config.rope_theta, rope_scaling=self.config.rope_scaling) + self.talker.freqs_cos, self.talker.freqs_sin = freqs_cos.to(input_ids.device), freqs_sin.to(input_ids.device) + presents = [] + + # ======= Thinker: text-only input, output text logits ======= + hidden_states = self.thinker.dropout(self.thinker.embed_tokens(text_ids)) + position_embeddings = (self.thinker.freqs_cos[start_pos:start_pos + seq_length], self.thinker.freqs_sin[start_pos:start_pos + seq_length]) + if audio_inputs is not None and start_pos == 0: + audio_features = self.encode_audio_inputs(audio_inputs, audio_lens) + hidden_states = self.inject_audio_features(text_ids, hidden_states, audio_features, seq_length) + if pixel_values is not None and start_pos == 0: + if hasattr(pixel_values, 'keys'): + img_emb = self.get_image_embeddings(pixel_values).to(hidden_states.dtype) + vision_tensors = self.vision_proj(img_emb) + else: + if len(pixel_values.shape) == 6: + pixel_values = pixel_values.squeeze(2) + if len(pixel_values.shape) == 4: + pixel_values = pixel_values.unsqueeze(1) + bs, num, c, im_h, im_w = pixel_values.shape + stack_dim = 1 if bs > 1 else 0 + vision_tensors = torch.stack([ + self.encode_image_inputs(pixel_values[:, i, :, :, :]) + for i in range(num) + ], dim=stack_dim) + hidden_states = self.count_vision_proj(tokens=text_ids, h=hidden_states, vision_tensors=vision_tensors, seqlen=seq_length) + bridge_states = hidden_states + for i, (layer, past_key_value) in enumerate(zip(self.thinker.layers, past_key_values[:n_thinker])): + hidden_states, present = layer(hidden_states, position_embeddings, past_key_value=past_key_value, use_cache=use_cache, attention_mask=attention_mask) + presents.append(present) + if i == self.config.bridge_layer: bridge_states = hidden_states + h_thinker = self.thinker.norm(hidden_states) + + # ======= Talker: thinker hidden + audio codes, output audio logits ======= + talker_emb = self.talker.embed_tokens(audio_ids) + spk_emb = args.get('spk_emb', None) + if spk_emb is not None: + spk_mask = (audio_ids[:, 0, :] == self.audio_spk_token).unsqueeze(-1) + talker_emb = torch.where(spk_mask, self.talker.spk_proj(spk_emb).unsqueeze(1), talker_emb) + hidden_states = self.talker.embed_proj(bridge_states) * self.talker.text_scale + self.talker.codec_proj(talker_emb) * self.talker.audio_scale + talker_pos_emb = (self.talker.freqs_cos[start_pos:start_pos + seq_length], self.talker.freqs_sin[start_pos:start_pos + seq_length]) + for layer, past_key_value in zip(self.talker.layers, past_key_values[n_thinker:]): + hidden_states, present = layer(hidden_states, talker_pos_emb, past_key_value=past_key_value, use_cache=use_cache, attention_mask=attention_mask) + presents.append(present) + h_talker = self.talker.norm(hidden_states) + + slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep + aux_loss = sum(l.mlp.aux_loss for l in list(self.thinker.layers) + list(self.talker.layers) if isinstance(l.mlp, MOEFeedForward)) + aux_loss += sum(p.sum() for p in self.audio_proj.parameters()) * 0 + sum(p.sum() for p in self.vision_proj.parameters()) * 0 + sum(p.sum() for p in self.talker.lm_head.adapters.parameters()) * 0 + sum(p.sum() for p in self.talker.spk_proj.parameters()) * 0 # dummy gradient + text_logits = self.thinker.lm_head(h_thinker[:, slice_indices, :]) + audio_logits = self.talker.lm_head(h_talker[:, slice_indices, :]) + + out = MoeCausalLMOutputWithPast(aux_loss=aux_loss, logits=text_logits, past_key_values=presents) + out.audio_logits = audio_logits + return out + + @torch.inference_mode() + def generate(self, input_ids, eos_token_id=2, max_new_tokens=1024, temperature=0.75, top_p=0.90, + stream=False, rp=1., use_cache=True, return_audio_codes=False, **args): + if stream: + return self.stream_generate(input_ids, eos_token_id, max_new_tokens, temperature, top_p, rp, use_cache, return_audio_codes, **args) + tokens = list(self.stream_generate(input_ids, eos_token_id, max_new_tokens, temperature, top_p, rp, use_cache, return_audio_codes, **args)) + return tokens[-1] if tokens else input_ids + + def stream_generate(self, input_ids, eos_token_id, max_new_tokens, temperature, top_p, rp, use_cache, return_audio_codes=False, **args): + start_pos, past_kvs, text_finished, first_finished = input_ids.shape[1], None, False, True + audio_codes = [[] for _ in range(8)] + audio_stop_pos = [None] * 8 + audio_buffer = torch.full((1, 8, start_pos), self.audio_pad_token, dtype=torch.long, device=input_ids.device) + spk_emb = args.get('spk_emb', None) + ref_codes = args.get('ref_codes', None) + ref_len = ref_codes.shape[2] if ref_codes is not None else 0 + spk_reserve = 1 if spk_emb is not None else 0 + fill_end = start_pos + fill_start = max(spk_reserve, start_pos - ref_len) + if ref_codes is not None and fill_start < fill_end: + audio_buffer[:, :, fill_start:fill_end] = ref_codes[:, :, -(fill_end - fill_start):] + if spk_emb is not None and fill_start > 0: + audio_buffer[:, :, fill_start - 1] = self.audio_spk_token + think_end_step, generated_tokens = None, ([] if args.get('open_thinking', False) else None) + while input_ids.shape[1] < start_pos + max_new_tokens: + if past_kvs is None or not use_cache: + out = self.forward(torch.cat((audio_buffer, input_ids.unsqueeze(1)), dim=1), past_key_values=past_kvs, use_cache=use_cache, **args) + else: + out = self.forward(torch.cat((audio_buffer[:, :, -1:], input_ids[:, -1:].unsqueeze(1)), dim=1), past_key_values=past_kvs, use_cache=use_cache, **args) + past_kvs = out.past_key_values + + logits = out.logits[0, -1, :].clone() / (temperature + 1e-9) + logits[list(set(input_ids[0].tolist()))] /= rp + if top_p and top_p < 1.0: + sorted_l, sorted_i = torch.sort(logits, descending=True) + mask = torch.cumsum(F.softmax(sorted_l, dim=-1), dim=-1) > top_p + mask[1:], mask[0] = mask[:-1].clone(), False + logits[sorted_i[mask]] = -float('Inf') + text_token = torch.multinomial(F.softmax(logits, dim=-1), 1).item() + + if text_finished: + text_token = args.get('enter_token_id', 201) if first_finished else args.get('pad_token_id', 0) + first_finished = False + + step = input_ids.shape[1] - start_pos # 已生成token数(0=首次,此时模型处理prompt末尾token) + audio_step = step - 1 # 延迟1步:输出第1个text时无audio,输出第2个text时layer0开始 + if generated_tokens is not None: + generated_tokens.append(text_token) + if not think_end_step and generated_tokens[-len(self.config.think_end_ids):] == list(self.config.think_end_ids): think_end_step = step + 2 + audio_step = (step - think_end_step) if think_end_step else -1 + for i, al in enumerate(out.audio_logits): + if audio_step < i: + audio_codes[i].append(self.audio_pad_token) + else: + logits_i = al[0, -1, :].clone() / 0.2 + for prev_code in audio_codes[i][-3:]: logits_i[prev_code] /= 1.05 + top_val, top_idx = logits_i.topk(50) + code = top_idx[torch.multinomial(F.softmax(top_val, dim=-1), 1)].item() + audio_codes[i].append(code) + if audio_stop_pos[i] is None and code >= 2048: audio_stop_pos[i] = len(audio_codes[i]) - 1 + + if text_finished and audio_codes[7][-1] == self.audio_stop_token: break + + input_ids = torch.cat((input_ids, torch.tensor([[text_token]], device=input_ids.device)), dim=1) + audio_buffer = torch.cat((audio_buffer, torch.full((1, 8, 1), self.audio_pad_token, dtype=torch.long, device=input_ids.device)), dim=2) + for i in range(min(audio_step + 1, 8)): audio_buffer[0, i, -1] = audio_codes[i][-1] + + audio_frame = None + if return_audio_codes and audio_step >= 7: + frame = [audio_codes[i][step - 7 + i] for i in range(8)] + active_layers = sum(1 for i in range(8) if audio_stop_pos[i] is None or step - 7 + i < audio_stop_pos[i]) + if active_layers >= 8: audio_frame = frame + if not text_finished: + yield input_ids[:, start_pos:], audio_frame + if text_token == eos_token_id: text_finished = True + else: + yield None, audio_frame + + +# ==== Realtime VAD (与模型本体零耦合,纯工程层) ==== +class SileroVAD: + def __init__(self, path): + opts = ort.SessionOptions() + opts.inter_op_num_threads = opts.intra_op_num_threads = 1 + opts.log_severity_level = 4 + self.session = ort.InferenceSession(path, providers=["CPUExecutionProvider"], sess_options=opts) + self.h, self.c = np.zeros((2, 1, 64), dtype=np.float32), np.zeros((2, 1, 64), dtype=np.float32) + + def reset(self): + self.h[:], self.c[:] = 0, 0 + + def __call__(self, chunk, sr=16000): + out, self.h, self.c = self.session.run(None, {"input": chunk.reshape(1, -1).astype(np.float32), "h": self.h, "c": self.c, "sr": np.array(sr, dtype="int64")}) + return float(out[0][0]) + + +class RealtimeSession: + def __init__(self, vad_path, sr=16000, threshold=0.8, min_speech_ms=128, min_silence_ms=800): + self.vad, self.sr, self.threshold = SileroVAD(vad_path), sr, threshold + self.min_speech, self.min_silence = int(sr * min_speech_ms / 1000), int(sr * min_silence_ms / 1000) + self.reset() + + def reset(self): + self.vad.reset() + self.buffer, self.ring, self.speaking, self.generating, self.interrupt = [], [], False, False, False + self.speech_samples = self.silence_samples = self.tail_silence = 0 + + def push_chunk(self, chunk, W=1024): + for i in range(0, max(len(chunk), 1), W): + w = chunk[i:i + W] + if len(w) < W: + w = np.pad(w, (0, W - len(w))) + prob = self.vad(w, self.sr) + if prob > self.threshold: + self.silence_samples = self.tail_silence = 0 + self.speech_samples += len(w) + self.buffer.append(w) + if self.speech_samples >= self.min_speech and not self.speaking: + self.speaking = True + self.buffer = self.ring + self.buffer + self.ring = [] + if self.generating and self.speaking: + self.interrupt = True + return 'interrupt' + elif self.speaking: + self.silence_samples += len(w) + self.tail_silence += 1 + self.buffer.append(w) + if self.silence_samples >= self.min_silence: + if self.tail_silence > 1: + del self.buffer[-(self.tail_silence - 1):] + self.speaking, self.speech_samples, self.silence_samples, self.tail_silence = False, 0, 0, 0 + return 'speech_end' + else: + if self.speech_samples > 0: + self.buffer.clear() + self.speech_samples = 0 + self.ring = [w] + return 'listening' + + def get_audio(self): + audio = np.concatenate(self.buffer) if self.buffer else np.array([], dtype=np.float32) + self.buffer.clear() + return audio \ No newline at end of file diff --git a/model/speaker/voice_clone.pt b/model/speaker/voice_clone.pt new file mode 100644 index 0000000..61e4d86 Binary files /dev/null and b/model/speaker/voice_clone.pt differ diff --git a/model/speaker/voices.pt b/model/speaker/voices.pt new file mode 100644 index 0000000..6e4ed47 Binary files /dev/null and b/model/speaker/voices.pt differ diff --git a/model/speaker/voices_unseen.pt b/model/speaker/voices_unseen.pt new file mode 100644 index 0000000..04faa70 Binary files /dev/null and b/model/speaker/voices_unseen.pt differ diff --git a/model/tokenizer.json b/model/tokenizer.json new file mode 100644 index 0000000..e6ca86c --- /dev/null +++ b/model/tokenizer.json @@ -0,0 +1,31191 @@ +{ + "version": "1.0", + "truncation": null, + "padding": null, + "added_tokens": [ + { + "id": 0, + "content": "<|endoftext|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 1, + "content": "<|im_start|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 2, + "content": "<|im_end|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 3, + "content": "<|object_ref_start|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 4, + "content": "<|object_ref_end|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 5, + "content": "<|box_start|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 6, + "content": "<|box_end|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 7, + "content": "<|quad_start|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 8, + "content": "<|quad_end|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 9, + "content": "<|vision_start|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 10, + "content": "<|vision_end|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 11, + "content": "<|vision_pad|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 12, + "content": "<|image_pad|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 13, + "content": "<|video_pad|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 14, + "content": "<|audio_start|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 15, + "content": "<|audio_end|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 16, + "content": "<|audio_pad|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 17, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 18, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 19, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 20, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": true + }, + { + "id": 21, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 22, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 23, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 24, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 25, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 26, + "content": "", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 27, + "content": "<|buffer1|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 28, + "content": "<|buffer2|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 29, + "content": "<|buffer3|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 30, + "content": "<|buffer4|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 31, + "content": "<|buffer5|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 32, + "content": "<|buffer6|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 33, + "content": "<|buffer7|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 34, + "content": "<|buffer8|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + }, + { + "id": 35, + "content": "<|buffer9|>", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false, + "special": false + } + ], + "normalizer": null, + "pre_tokenizer": { + "type": "ByteLevel", + "add_prefix_space": false, + "trim_offsets": true, + "use_regex": true + }, + "post_processor": null, + "decoder": { + "type": "ByteLevel", + "add_prefix_space": true, + "trim_offsets": true, + "use_regex": true + }, + "model": { + "type": "BPE", + "dropout": null, + "unk_token": null, + "continuing_subword_prefix": null, + "end_of_word_suffix": null, + "fuse_unk": false, + "byte_fallback": false, + "ignore_merges": false, + "vocab": { + "<|endoftext|>": 0, + "<|im_start|>": 1, + "<|im_end|>": 2, + "<|object_ref_start|>": 3, + "<|object_ref_end|>": 4, + "<|box_start|>": 5, + "<|box_end|>": 6, + "<|quad_start|>": 7, + "<|quad_end|>": 8, + "<|vision_start|>": 9, + "<|vision_end|>": 10, + "<|vision_pad|>": 11, + "<|image_pad|>": 12, + "<|video_pad|>": 13, + "<|audio_start|>": 14, + "<|audio_end|>": 15, + "<|audio_pad|>": 16, + "": 17, + "": 18, + "": 19, + "": 20, + "": 21, + "": 22, + "": 23, + "": 24, + "": 25, + "": 26, + "<|buffer1|>": 27, + "<|buffer2|>": 28, + "<|buffer3|>": 29, + "<|buffer4|>": 30, + "<|buffer5|>": 31, + "<|buffer6|>": 32, + "<|buffer7|>": 33, + "<|buffer8|>": 34, + "<|buffer9|>": 35, + "!": 36, + "\"": 37, + "#": 38, + "$": 39, + "%": 40, + "&": 41, + "'": 42, + "(": 43, + ")": 44, + "*": 45, + "+": 46, + ",": 47, + "-": 48, + ".": 49, + "/": 50, + "0": 51, + "1": 52, + "2": 53, + "3": 54, + "4": 55, + "5": 56, + "6": 57, + "7": 58, + "8": 59, + "9": 60, + ":": 61, + ";": 62, + "<": 63, + "=": 64, + ">": 65, + "?": 66, + "@": 67, + "A": 68, + "B": 69, + "C": 70, + "D": 71, + "E": 72, + "F": 73, + "G": 74, + "H": 75, + "I": 76, + "J": 77, + "K": 78, + "L": 79, + "M": 80, + "N": 81, + "O": 82, + "P": 83, + "Q": 84, + "R": 85, + "S": 86, + "T": 87, + "U": 88, + "V": 89, + "W": 90, + "X": 91, + "Y": 92, + "Z": 93, + "[": 94, + "\\": 95, + "]": 96, + "^": 97, + "_": 98, + "`": 99, + "a": 100, + "b": 101, + "c": 102, + "d": 103, + "e": 104, + "f": 105, + "g": 106, + "h": 107, + "i": 108, + "j": 109, + "k": 110, + "l": 111, + "m": 112, + "n": 113, + "o": 114, + "p": 115, + "q": 116, + "r": 117, + "s": 118, + "t": 119, + "u": 120, + "v": 121, + "w": 122, + "x": 123, + "y": 124, + "z": 125, + "{": 126, + "|": 127, + "}": 128, + "~": 129, + "¡": 130, + "¢": 131, + "£": 132, + "¤": 133, + "¥": 134, + "¦": 135, + "§": 136, + "¨": 137, + "©": 138, + "ª": 139, + "«": 140, + "¬": 141, + "®": 142, + "¯": 143, + "°": 144, + "±": 145, + "²": 146, + "³": 147, + "´": 148, + "µ": 149, + "¶": 150, + "·": 151, + "¸": 152, + "¹": 153, + "º": 154, + "»": 155, + "¼": 156, + "½": 157, + "¾": 158, + "¿": 159, + "À": 160, + "Á": 161, + "Â": 162, + "Ã": 163, + "Ä": 164, + "Å": 165, + "Æ": 166, + "Ç": 167, + "È": 168, + "É": 169, + "Ê": 170, + "Ë": 171, + "Ì": 172, + "Í": 173, + "Î": 174, + "Ï": 175, + "Ð": 176, + "Ñ": 177, + "Ò": 178, + "Ó": 179, + "Ô": 180, + "Õ": 181, + "Ö": 182, + "×": 183, + "Ø": 184, + "Ù": 185, + "Ú": 186, + "Û": 187, + "Ü": 188, + "Ý": 189, + "Þ": 190, + "ß": 191, + "à": 192, + "á": 193, + "â": 194, + "ã": 195, + "ä": 196, + "å": 197, + "æ": 198, + "ç": 199, + "è": 200, + "é": 201, + "ê": 202, + "ë": 203, + "ì": 204, + "í": 205, + "î": 206, + "ï": 207, + "ð": 208, + "ñ": 209, + "ò": 210, + "ó": 211, + "ô": 212, + "õ": 213, + "ö": 214, + "÷": 215, + "ø": 216, + "ù": 217, + "ú": 218, + "û": 219, + "ü": 220, + "ý": 221, + "þ": 222, + "ÿ": 223, + "Ā": 224, + "ā": 225, + "Ă": 226, + "ă": 227, + "Ą": 228, + "ą": 229, + "Ć": 230, + "ć": 231, + "Ĉ": 232, + "ĉ": 233, + "Ċ": 234, + "ċ": 235, + "Č": 236, + "č": 237, + "Ď": 238, + "ď": 239, + "Đ": 240, + "đ": 241, + "Ē": 242, + "ē": 243, + "Ĕ": 244, + "ĕ": 245, + "Ė": 246, + "ė": 247, + "Ę": 248, + "ę": 249, + "Ě": 250, + "ě": 251, + "Ĝ": 252, + "ĝ": 253, + "Ğ": 254, + "ğ": 255, + "Ġ": 256, + "ġ": 257, + "Ģ": 258, + "ģ": 259, + "Ĥ": 260, + "ĥ": 261, + "Ħ": 262, + "ħ": 263, + "Ĩ": 264, + "ĩ": 265, + "Ī": 266, + "ī": 267, + "Ĭ": 268, + "ĭ": 269, + "Į": 270, + "į": 271, + "İ": 272, + "ı": 273, + "IJ": 274, + "ij": 275, + "Ĵ": 276, + "ĵ": 277, + "Ķ": 278, + "ķ": 279, + "ĸ": 280, + "Ĺ": 281, + "ĺ": 282, + "Ļ": 283, + "ļ": 284, + "Ľ": 285, + "ľ": 286, + "Ŀ": 287, + "ŀ": 288, + "Ł": 289, + "ł": 290, + "Ń": 291, + "ï¼": 292, + "ä¸": 293, + "ï¼Į": 294, + "çļ": 295, + "çļĦ": 296, + "Ġt": 297, + "ãĢ": 298, + "Ġa": 299, + "he": 300, + "in": 301, + "ãĢĤ": 302, + "äº": 303, + "ä»": 304, + "åı": 305, + "re": 306, + "ä½": 307, + "æľ": 308, + "Ġthe": 309, + "on": 310, + "er": 311, + "**": 312, + "è¿": 313, + "at": 314, + "åħ": 315, + "Ġs": 316, + "Ġc": 317, + "åĪ": 318, + "Ġo": 319, + "å®": 320, + "çĶ": 321, + "æĪ": 322, + "åĬ": 323, + "is": 324, + "an": 325, + "ou": 326, + "en": 327, + "åľ": 328, + "es": 329, + "å¤": 330, + "åĴ": 331, + "ĠĠ": 332, + "æĺ": 333, + "åĴĮ": 334, + "åIJ": 335, + "or": 336, + "ï¼ļ": 337, + "it": 338, + "Ġw": 339, + "æķ": 340, + "Ġf": 341, + "ç»": 342, + "è¯": 343, + "Ġp": 344, + "ãĢģ": 345, + "Ġan": 346, + "æĸ": 347, + "è®": 348, + "ä¸Ģ": 349, + "ing": 350, + "Ġm": 351, + "al": 352, + "Ġin": 353, + "Ġof": 354, + "ĥ½": 355, + "éĢ": 356, + "æĺ¯": 357, + "以": 358, + "Ġ**": 359, + "åı¯": 360, + "le": 361, + "åŃ": 362, + "Ġb": 363, + "Ġto": 364, + "ä¹": 365, + "个": 366, + "åº": 367, + "ç͍": 368, + "åľ¨": 369, + "ar": 370, + "ion": 371, + "èĥ½": 372, + "Ġd": 373, + "**ï¼ļ": 374, + "ic": 375, + "Ġand": 376, + "å°": 377, + "æĹ": 378, + "ä¼": 379, + "éĩ": 380, + "å¸": 381, + "ed": 382, + "åĮ": 383, + "è§": 384, + "Ġy": 385, + "åĽ": 386, + "è¡": 387, + "ä¸Ń": 388, + "å¯": 389, + "as": 390, + "æķ°": 391, + "åĩ": 392, + "ro": 393, + "è¿Ļ": 394, + "Ġis": 395, + "¦Ĥ": 396, + "æĿ": 397, + "æľī": 398, + "Ġn": 399, + "å¼": 400, + "Ġyou": 401, + "æĢ": 402, + "ä¿": 403, + "å¦Ĥ": 404, + "æŀ": 405, + "Ġl": 406, + "åĨ": 407, + "çŃ": 408, + "çİ": 409, + "åı¯ä»¥": 410, + "éĹ": 411, + "ç§": 412, + "Ġe": 413, + "è¦": 414, + "çĽ": 415, + "çī": 416, + "ä¸į": 417, + "为": 418, + "çļĦæ": 419, + "è¦ģ": 420, + "çIJ": 421, + "Ġre": 422, + "äºĨ": 423, + "å¹": 424, + "ent": 425, + "ct": 426, + "å·": 427, + "æı": 428, + "Ġh": 429, + "èĢ": 430, + "é¢": 431, + "st": 432, + "æ³": 433, + "æī": 434, + "ĊĠĠ": 435, + "æĬ": 436, + "ä¾": 437, + "æł": 438, + "et": 439, + "æį": 440, + "ä½ł": 441, + "ĠI": 442, + "æĹ¶": 443, + "om": 444, + "Ġth": 445, + "el": 446, + "人": 447, + "åį": 448, + "``": 449, + "Ġ=": 450, + "ow": 451, + "Ġ\\": 452, + "å½": 453, + "æıIJ": 454, + "çIJĨ": 455, + "äºİ": 456, + "um": 457, + "å¾": 458, + "æĪĸ": 459, + "id": 460, + "对": 461, + "âĢ": 462, + "æĪij": 463, + "çŃī": 464, + "æİ": 465, + "ä¸Ģ个": 466, + "im": 467, + "éĢļ": 468, + "éĿ": 469, + "æĢ§": 470, + "è´": 471, + "è¿ĩ": 472, + "ce": 473, + "è¡Į": 474, + "ç®": 475, + "使": 476, + "æĮ": 477, + "ve": 478, + "æĶ": 479, + "èĩ": 480, + "æĭ": 481, + "Ġ1": 482, + "Ġu": 483, + "æĸ¹": 484, + "il": 485, + "çĶŁ": 486, + "æį®": 487, + "æĪIJ": 488, + "ation": 489, + "åѦ": 490, + "æĽ": 491, + "Ġg": 492, + "æŃ": 493, + "åĵ": 494, + "th": 495, + "ers": 496, + "am": 497, + "ä½ľ": 498, + "ig": 499, + "ol": 500, + "ut": 501, + "å¿": 502, + "Ġfor": 503, + "ä¸ĭ": 504, + "æĦ": 505, + "éľ": 506, + "if": 507, + "ä¼ļ": 508, + "æĿ¥": 509, + "å¥": 510, + "åĮĸ": 511, + "åΰ": 512, + "åĪĨ": 513, + "è¾": 514, + "å®ļ": 515, + "ä¿Ŀ": 516, + "æĽ´": 517, + "äºĽ": 518, + "ç¡": 519, + "æµ": 520, + "éĻ": 521, + "Ġor": 522, + "Ġas": 523, + "éľĢ": 524, + "å±": 525, + "们": 526, + "ĠT": 527, + "ç¨": 528, + "ĠA": 529, + "ä½ĵ": 530, + "åĬ¨": 531, + "åıij": 532, + "使ç͍": 533, + "è§£": 534, + "ĠC": 535, + "Ġ\"": 536, + "qu": 537, + "è¿Ľ": 538, + "æŀľ": 539, + "Ġpro": 540, + "Ġcan": 541, + "ly": 542, + "ur": 543, + "éĺ": 544, + "åºĶ": 545, + "Ġ2": 546, + "åij": 547, + "Ġ-": 548, + "ul": 549, + "ch": 550, + "åŁ": 551, + "ä¸İ": 552, + "åľ°": 553, + "ï¼ī": 554, + "ï¼Ī": 555, + "æ³ķ": 556, + "ate": 557, + "èµ": 558, + "ra": 559, + "åIJĮ": 560, + "é«": 561, + "ä¸Ĭ": 562, + "Ġit": 563, + "é«ĺ": 564, + "大": 565, + "ç½": 566, + "ç¡®": 567, + "ith": 568, + "æķ°æį®": 569, + "ist": 570, + "åIJĪ": 571, + "ab": 572, + "se": 573, + "æĸĩ": 574, + "è½": 575, + "æ¯": 576, + "度": 577, + "åı¯èĥ½": 578, + "èĩª": 579, + "Ġst": 580, + "å®ŀ": 581, + "Ġhe": 582, + "ge": 583, + "éĩı": 584, + "计": 585, + "ç¨ĭ": 586, + "çº": 587, + "åİ": 588, + "æ°": 589, + "å¤ļ": 590, + "Ġthat": 591, + "æĥ": 592, + "ot": 593, + "åĬĽ": 594, + "åĬł": 595, + "çݰ": 596, + "åĩº": 597, + "ç¤": 598, + "Ġwith": 599, + "Ġbe": 600, + "ç³": 601, + "ä¸ļ": 602, + "Ġv": 603, + "ç§į": 604, + "éĿ¢": 605, + "常": 606, + "éĹ´": 607, + "åĬ¡": 608, + "设": 609, + "æĬĢ": 610, + "est": 611, + "å»": 612, + "ä¾Ľ": 613, + "Ġcon": 614, + "éĹ®": 615, + "åĬ©": 616, + "æľ¯": 617, + "æ´": 618, + "Ġon": 619, + "æ±": 620, + "Ġmo": 621, + "æĻ": 622, + "å°Ĩ": 623, + "```": 624, + "å¢": 625, + "表": 626, + "éģ": 627, + "'s": 628, + "åħ³": 629, + "éľĢè¦ģ": 630, + "ĠS": 631, + "é¢ĺ": 632, + "å·¥": 633, + "##": 634, + "建": 635, + "åħ¶": 636, + "éĩį": 637, + "æľº": 638, + "ir": 639, + "æĸ°": 640, + "çī©": 641, + "éĢļè¿ĩ": 642, + "åħ¨": 643, + "æıIJä¾Ľ": 644, + "ä¿¡": 645, + "çĦ": 646, + "ç³»": 647, + "æĮģ": 648, + "çł": 649, + "å¼ı": 650, + "un": 651, + "ver": 652, + "pe": 653, + "Ġwh": 654, + "ay": 655, + "ç´": 656, + "æģ": 657, + "好": 658, + "åĢ": 659, + "ç«": 660, + "å¦Ĥæŀľ": 661, + "èī": 662, + "åŃIJ": 663, + "æ¸": 664, + "Ġ(": 665, + "ĠH": 666, + "çĦ¶": 667, + "iv": 668, + "åģ": 669, + "00": 670, + "åħ·": 671, + "ç±": 672, + "Ł¥": 673, + "ld": 674, + "çĤ": 675, + "ib": 676, + "è°": 677, + "ä»ĸ": 678, + "é¡": 679, + "ç®Ĺ": 680, + "ãģ": 681, + "åζ": 682, + "âĢľ": 683, + "âĢĿ": 684, + "ä¹ł": 685, + "åŀ": 686, + "em": 687, + "ide": 688, + "æľ¬": 689, + "æĪ·": 690, + "ĠP": 691, + "è¶": 692, + "ke": 693, + "nt": 694, + "æľĢ": 695, + "åĮħ": 696, + "å®ĥ": 697, + "çī¹": 698, + "Ġcom": 699, + "å°ı": 700, + "åIJİ": 701, + "åŃĹ": 702, + "帮": 703, + "èĢĮ": 704, + "æķĪ": 705, + "èĢħ": 706, + "Ġ3": 707, + "igh": 708, + "é£": 709, + "åĵģ": 710, + "代": 711, + "ä»¶": 712, + "de": 713, + "umb": 714, + "yth": 715, + "éļ": 716, + "åħ¥": 717, + "Ĥ¨": 718, + "Ġex": 719, + "ä¹Ł": 720, + "æ²": 721, + "lo": 722, + "缮": 723, + "åŀĭ": 724, + "ĊĠĠĠ": 725, + "å®¶": 726, + "å¼Ģ": 727, + "and": 728, + "¨¡": 729, + "Ġne": 730, + "åĽł": 731, + "Ġare": 732, + "æĦı": 733, + "æ¬": 734, + "çļĦç": 735, + "Ġha": 736, + "pp": 737, + "åī": 738, + "帮åĬ©": 739, + "ain": 740, + "te": 741, + "æĬĢæľ¯": 742, + "å®ī": 743, + "ython": 744, + "ure": 745, + "éĹ®é¢ĺ": 746, + "å±ķ": 747, + "ĠĠĠĠ": 748, + "çĤ¹": 749, + "The": 750, + "éĶ": 751, + "ä¾ĭ": 752, + "çĿ": 753, + "Ġ+": 754, + "æģ¯": 755, + "æľį": 756, + "Ġr": 757, + "éĢī": 758, + "模": 759, + "ä½Ĩ": 760, + "è¯Ń": 761, + "Ġse": 762, + "åĽ½": 763, + "åĨħ": 764, + "Ġme": 765, + "åĪĽ": 766, + "绣": 767, + "åŃ¦ä¹ł": 768, + "Ġby": 769, + "éķ": 770, + "her": 771, + "èĤ": 772, + "æĪij们": 773, + "ight": 774, + "ä¹ĭ": 775, + "ï¼Ł": 776, + "æĦŁ": 777, + "ç±»": 778, + "æ±Ĥ": 779, + "æ´»": 780, + "ess": 781, + "è¿ĻäºĽ": 782, + "éª": 783, + "éĽ": 784, + "ad": 785, + "åħ¬": 786, + "Ġde": 787, + "for": 788, + "Ġal": 789, + "产": 790, + "ment": 791, + "ç¼": 792, + "ç½ij": 793, + "ĠThe": 794, + "å¿ĥ": 795, + "Ġwe": 796, + "å¹¶": 797, + "ç¬": 798, + "æīĢ": 799, + "缸": 800, + "Ġ\\(": 801, + "çķ": 802, + "åĻ": 803, + "åĿ": 804, + "æľŁ": 805, + "int": 806, + "æ·": 807, + "Ġ`": 808, + "ri": 809, + "nd": 810, + "ant": 811, + "ase": 812, + "ç»ı": 813, + "çݯ": 814, + "è¿Ľè¡Į": 815, + "ç»ĵ": 816, + "èĬ": 817, + "å¹³": 818, + "ç͵": 819, + "ç©": 820, + "Ġnumb": 821, + "éĢĤ": 822, + "lp": 823, + "oo": 824, + "ction": 825, + "交": 826, + "åύ": 827, + "åıĺ": 828, + "æłĩ": 829, + "主": 830, + "æº": 831, + "us": 832, + "ple": 833, + "age": 834, + "å¤Ħ": 835, + "çĿĢ": 836, + "æľįåĬ¡": 837, + "éĥ": 838, + "ä¿¡æģ¯": 839, + "xt": 840, + "Ġprov": 841, + "æİ¥": 842, + "Ġhelp": 843, + "èµĦ": 844, + "åĨ³": 845, + "Ġus": 846, + "å½±": 847, + "Ġle": 848, + "æĤ¨": 849, + "以ä¸ĭ": 850, + "æĺİ": 851, + "éĩĮ": 852, + "çľ": 853, + "è·": 854, + "Ġsu": 855, + "æĥħ": 856, + "éĥ½": 857, + "åľº": 858, + "ç¡®ä¿Ŀ": 859, + "ä»İ": 860, + "éķ¿": 861, + "Wh": 862, + "ç͍æĪ·": 863, + "éĥ¨": 864, + "rom": 865, + "ort": 866, + "åıĬ": 867, + "èģ": 868, + "ĠM": 869, + "ity": 870, + "å¤Ł": 871, + "åĪĹ": 872, + "å¾Ĺ": 873, + "Ġhave": 874, + "pt": 875, + "form": 876, + "åŁº": 877, + "å®īåħ¨": 878, + "å̼": 879, + "è°ĥ": 880, + "æĭ¬": 881, + "èĻ": 882, + "ific": 883, + "ive": 884, + "社": 885, + "çłģ": 886, + "ter": 887, + "ä½ķ": 888, + "res": 889, + "Ġch": 890, + "éĢīæĭ": 891, + "ck": 892, + "Ġqu": 893, + "éĢīæĭ©": 894, + "计ç®Ĺ": 895, + "é¦": 896, + "æºIJ": 897, + "åĮħæĭ¬": 898, + "å½ĵ": 899, + "Ġ5": 900, + "end": 901, + "管": 902, + "çİĩ": 903, + "è¿Ļ个": 904, + "æıIJé«ĺ": 905, + "ame": 906, + "ç³»ç»Ł": 907, + "åºĶç͍": 908, + "è¿ĺ": 909, + "æķ´": 910, + "æĹ¶éĹ´": 911, + "è®®": 912, + "art": 913, + "Ġk": 914, + "ould": 915, + "ind": 916, + "ä½į": 917, + "ä»»": 918, + "æ¯ı": 919, + "è´¨": 920, + "åĵį": 921, + "ue": 922, + "ä¸ĢäºĽ": 923, + "å¤į": 924, + "ies": 925, + "ial": 926, + "Ġpo": 927, + "äºĭ": 928, + "å¸Ĥ": 929, + "èī²": 930, + "åŃĺ": 931, + "Ġ4": 932, + "åĮº": 933, + "èĥ½å¤Ł": 934, + "示": 935, + "å¹´": 936, + "è¢": 937, + "åīį": 938, + "åIJį": 939, + "ç´ł": 940, + "ç»Ħ": 941, + "ang": 942, + "æ¡": 943, + "ence": 944, + "ĠF": 945, + "Ġspe": 946, + "Ġwor": 947, + "ich": 948, + "æ¶": 949, + "éħ": 950, + "å¢ŀ": 951, + "æ°´": 952, + "æŃ¥": 953, + "å¢ĥ": 954, + "out": 955, + "æŀĦ": 956, + "ĠE": 957, + "å¤ĦçIJĨ": 958, + "ill": 959, + "请": 960, + "Ġfrom": 961, + "å°ij": 962, + "éĩįè¦ģ": 963, + "ere": 964, + "项": 965, + "Ġco": 966, + "æ¯Ķ": 967, + "æŃ£": 968, + "ä¸įåIJĮ": 969, + "åĩ½": 970, + "åĪ©": 971, + "éĤ": 972, + "被": 973, + "åĩĨ": 974, + "Ġab": 975, + "æĶ¯": 976, + "ä»ħ": 977, + "ĠB": 978, + "æł¹": 979, + "è¯Ĩ": 980, + "ä¹Ī": 981, + "导": 982, + "åħĥ": 983, + "åĩ½æķ°": 984, + "ä¼ĺ": 985, + "\\)": 986, + "ions": 987, + "Ġ$": 988, + "æĹł": 989, + "注": 990, + "éĿŀ": 991, + "pl": 992, + "天": 993, + "æŁ¥": 994, + "æĬ¤": 995, + "æµģ": 996, + "æµĭ": 997, + "è§Ħ": 998, + "his": 999, + "çĸ": 1000, + "æĺ¯ä¸Ģ个": 1001, + "ĠAl": 1002, + "Ġthis": 1003, + "ĠW": 1004, + "ult": 1005, + "åºı": 1006, + "线": 1007, + "è¯ķ": 1008, + "åıijå±ķ": 1009, + "å½±åĵį": 1010, + "###": 1011, + "ç¾": 1012, + "ç½®": 1013, + "åŁŁ": 1014, + "åıĸ": 1015, + "ine": 1016, + "Ġprovide": 1017, + "é£İ": 1018, + "å½¢": 1019, + "Ġspec": 1020, + "ä¼ł": 1021, + "å¤ĩ": 1022, + "强": 1023, + "ike": 1024, + "act": 1025, + "åħ·ä½ĵ": 1026, + "Ġnot": 1027, + "iz": 1028, + "容": 1029, + "low": 1030, + "Ġass": 1031, + "é¢Ĩ": 1032, + "éªĮ": 1033, + "Ġma": 1034, + "åĩı": 1035, + "Ġmore": 1036, + "åķ": 1037, + "管çIJĨ": 1038, + "Ġyour": 1039, + "æĢģ": 1040, + "Ġdo": 1041, + "ä¸ŃçļĦ": 1042, + "æ»": 1043, + "ĠG": 1044, + "Ġwhich": 1045, + "æŀIJ": 1046, + "Ġte": 1047, + "Ġnumber": 1048, + "able": 1049, + "è¿°": 1050, + "aba": 1051, + "è¾ĵ": 1052, + "æĹ¥": 1053, + "æł¹æį®": 1054, + "åĽŀ": 1055, + "çŃĸ": 1056, + "Ġen": 1057, + "ĠD": 1058, + "éĵ": 1059, + "Ġpr": 1060, + "è¨": 1061, + "çݯå¢ĥ": 1062, + "ibaba": 1063, + "æĸ¹æ³ķ": 1064, + "åĪ«": 1065, + "åįķ": 1066, + "per": 1067, + "rou": 1068, + "ٳ": 1069, + "Ġneed": 1070, + "è¿IJ": 1071, + "ring": 1072, + "æł¼": 1073, + "put": 1074, + "ä¸įä»ħ": 1075, + "ust": 1076, + "æ£": 1077, + "ite": 1078, + "ance": 1079, + "æĻº": 1080, + "éĽĨ": 1081, + "åıĹ": 1082, + "æŃ¤": 1083, + "our": 1084, + "å¤ĸ": 1085, + "代çłģ": 1086, + "ç§ij": 1087, + "åIJij": 1088, + "èĬĤ": 1089, + "éĢļ常": 1090, + "设计": 1091, + "å§": 1092, + "are": 1093, + "æ·±": 1094, + "ated": 1095, + "ical": 1096, + "æŁ": 1097, + "Ġlike": 1098, + "oud": 1099, + "é¢Ħ": 1100, + "éĢŁ": 1101, + "èĢĥ": 1102, + "now": 1103, + "è¨Ģ": 1104, + "åĪĨæŀIJ": 1105, + "ans": 1106, + "åĽ¾": 1107, + "ĊĠĠĠĠĠĠ": 1108, + "个人": 1109, + "cl": 1110, + "Ġwas": 1111, + "åĬŁ": 1112, + "av": 1113, + "ĠHow": 1114, + "çŁ¥": 1115, + "ç»Ļ": 1116, + "æĮĩ": 1117, + "åijĺ": 1118, + "æ®": 1119, + "åıĤ": 1120, + "ç»Ń": 1121, + "Ġso": 1122, + "loud": 1123, + "ä¾ĭå¦Ĥ": 1124, + "æĥ³": 1125, + "ice": 1126, + "åIJĮæĹ¶": 1127, + "Ġsh": 1128, + "æł·": 1129, + "cc": 1130, + "ud": 1131, + "论": 1132, + "åıį": 1133, + "çģ": 1134, + "模åŀĭ": 1135, + "ä¹ī": 1136, + "è¾¾": 1137, + "äºij": 1138, + "缴": 1139, + "çĥ": 1140, + "ĠAlibaba": 1141, + "èĥ": 1142, + "çIJĥ": 1143, + "Ġtr": 1144, + "å·±": 1145, + "What": 1146, + "æķĻ": 1147, + "unction": 1148, + "Ġany": 1149, + "åĨµ": 1150, + "æīĭ": 1151, + "Ġassist": 1152, + "èĩªå·±": 1153, + "ast": 1154, + "\\[": 1155, + "ary": 1156, + "åĽ¢": 1157, + "å·´": 1158, + "å·¥ä½ľ": 1159, + "符": 1160, + "建议": 1161, + "è±": 1162, + "ag": 1163, + "éĻħ": 1164, + "Ġint": 1165, + "èį": 1166, + "次": 1167, + "æĶ¹": 1168, + "ãĤ": 1169, + "æī¾": 1170, + "ç¥": 1171, + "og": 1172, + "åħ±": 1173, + "ime": 1174, + "ap": 1175, + "formation": 1176, + "Ġ0": 1177, + "Ġres": 1178, + "åģ¥": 1179, + "ä»·": 1180, + "两": 1181, + "æĻ¯": 1182, + "è¿ĩç¨ĭ": 1183, + "åİŁ": 1184, + "å¦Ĥä½ķ": 1185, + "è§Ĥ": 1186, + "ther": 1187, + "æĸ¹å¼ı": 1188, + "èIJ": 1189, + "Ġ20": 1190, + "ç«ĭ": 1191, + "转": 1192, + "Ġ'": 1193, + "å¼ķ": 1194, + "æ°Ķ": 1195, + "Ġabout": 1196, + "Ġspecific": 1197, + "ĠL": 1198, + "å®ŀçݰ": 1199, + "Ġad": 1200, + "çIJĨè§£": 1201, + "让": 1202, + "æĺ¯ä¸Ģ": 1203, + "æľª": 1204, + "ç͍äºİ": 1205, + "ç¼ĸ": 1206, + "lease": 1207, + "Ġim": 1208, + "é¢ĨåŁŁ": 1209, + "æİ¨": 1210, + "éĺ¿": 1211, + "Ġknow": 1212, + "è§Ĩ": 1213, + "ä½łçļĦ": 1214, + "è§£åĨ³": 1215, + "绾": 1216, + "Ġinformation": 1217, + "10": 1218, + "Ġpl": 1219, + "åĩıå°ij": 1220, + "åħī": 1221, + "å®Į": 1222, + "text": 1223, + "åħĪ": 1224, + "Ġar": 1225, + "åĨĻ": 1226, + "éļı": 1227, + "æĶ¶": 1228, + "ç½ij绾": 1229, + "æĻºèĥ½": 1230, + "ĠN": 1231, + "éĺŁ": 1232, + "客": 1233, + "èµ·": 1234, + "Ġfunction": 1235, + "ĠJ": 1236, + "king": 1237, + "éĤ£": 1238, + "ore": 1239, + "çͱ": 1240, + "Ġif": 1241, + "ations": 1242, + "ĠR": 1243, + "康": 1244, + "åĪĻ": 1245, + "ĠCh": 1246, + "éľĢæ±Ĥ": 1247, + "Ġat": 1248, + "åħį": 1249, + "ener": 1250, + "æĶ¾": 1251, + "æĶ¯æĮģ": 1252, + "Ġup": 1253, + "ç²": 1254, + "æľĽ": 1255, + "ä¸ĵ": 1256, + "说": 1257, + "ä¿ĿæĮģ": 1258, + "ount": 1259, + "çĬ": 1260, + "åİĨ": 1261, + "urn": 1262, + "Ġwhe": 1263, + "ĠThis": 1264, + "éĩij": 1265, + "all": 1266, + "éģĵ": 1267, + "ach": 1268, + "so": 1269, + "Ġfe": 1270, + "éŁ³": 1271, + "ell": 1272, + "空": 1273, + "çłĶ": 1274, + "ks": 1275, + "åķĨ": 1276, + "åį³": 1277, + "è§Ĵ": 1278, + "ç¾İ": 1279, + "åģ¥åº·": 1280, + "社ä¼ļ": 1281, + "è¯Ńè¨Ģ": 1282, + "tain": 1283, + "å£": 1284, + "ange": 1285, + "èº": 1286, + "ata": 1287, + "èĦ": 1288, + "ign": 1289, + "Ġhas": 1290, + "è®°": 1291, + "ect": 1292, + "ä¹IJ": 1293, + "èİ": 1294, + "çĹ": 1295, + "身": 1296, + "Ġret": 1297, + "çĶŁæ´»": 1298, + "ile": 1299, + "æİ§": 1300, + "ous": 1301, + "ç»´": 1302, + "éĴ": 1303, + "å§ĭ": 1304, + "éĺ¿éĩĮ": 1305, + "产åĵģ": 1306, + "éĿŀ常": 1307, + "è£": 1308, + "erm": 1309, + "åĸ": 1310, + "ç®Ģ": 1311, + "os": 1312, + "ĠIf": 1313, + "å¸Ĥåľº": 1314, + "Ġj": 1315, + "å¿«": 1316, + "段": 1317, + "od": 1318, + "Ġser": 1319, + "pr": 1320, + "äºĨè§£": 1321, + "Ġ6": 1322, + "åįĩ": 1323, + "å°±": 1324, + "èµĦæºIJ": 1325, + "èĩ´": 1326, + "缮æłĩ": 1327, + "åı°": 1328, + "rac": 1329, + "象": 1330, + "主è¦ģ": 1331, + "åIJ¦": 1332, + "æĸĻ": 1333, + "èĥ½åĬĽ": 1334, + "æĸŃ": 1335, + "çľĭ": 1336, + "à¸": 1337, + "Ġcre": 1338, + "AI": 1339, + "du": 1340, + "æ£Ģ": 1341, + "Ġits": 1342, + "ĠIt": 1343, + "以åıĬ": 1344, + "è¯ģ": 1345, + "ä»ĸ们": 1346, + "ĠCloud": 1347, + "ail": 1348, + "python": 1349, + "对äºİ": 1350, + "éĻIJ": 1351, + "ub": 1352, + "ä¼ģ": 1353, + "20": 1354, + "åı¸": 1355, + "游": 1356, + "æĵ": 1357, + "é£Ł": 1358, + "满": 1359, + "æĸĩåĮĸ": 1360, + "()": 1361, + "Ġbut": 1362, + "ç»Ĩ": 1363, + "ï¼ģ": 1364, + "å¸Į": 1365, + "Ġmy": 1366, + "ip": 1367, + "ä¼ģä¸ļ": 1368, + "èģĶ": 1369, + "ĠO": 1370, + "é¡¹çĽ®": 1371, + "æķ°åŃĹ": 1372, + "çķĮ": 1373, + "éģ¿": 1374, + "åĩł": 1375, + "}{": 1376, + "ong": 1377, + "ä¿ĿæĬ¤": 1378, + "æĪĺ": 1379, + "è¯Ħ": 1380, + "ä»Ģ": 1381, + "ree": 1382, + "æĢ»": 1383, + "积": 1384, + "该": 1385, + "éħį": 1386, + "ĊĠĠĠĠĠĠĠ": 1387, + "ass": 1388, + "ever": 1389, + "Ġx": 1390, + "ces": 1391, + "æĽ´åĬł": 1392, + "Ġquest": 1393, + "éĢł": 1394, + "Ġgener": 1395, + "æĿ¡": 1396, + "frac": 1397, + "Ġreturn": 1398, + "èĮ": 1399, + "ie": 1400, + "Ġ\\)": 1401, + "çĬ¶": 1402, + "ä»Ģä¹Ī": 1403, + "æ¡Ī": 1404, + "ä¸ŃåĽ½": 1405, + "Ġun": 1406, + "åIJ«": 1407, + "é¦ĸ": 1408, + "广": 1409, + "æĺĵ": 1410, + "ä¹ĭéĹ´": 1411, + "åĽ¢éĺŁ": 1412, + "èĪ": 1413, + "Ġhow": 1414, + "vel": 1415, + "Ġsc": 1416, + "Ġfind": 1417, + "Ġuse": 1418, + "é©": 1419, + "没": 1420, + "Ġ10": 1421, + "'m": 1422, + "å¯Ĩ": 1423, + "çĶŁæĪIJ": 1424, + "èĻij": 1425, + "port": 1426, + "oc": 1427, + "'re": 1428, + "åħ¬åı¸": 1429, + "åĬŁèĥ½": 1430, + "æķħ": 1431, + "Ġincl": 1432, + "ard": 1433, + "ates": 1434, + "èĤ²": 1435, + "åħĥç´ł": 1436, + "éĻ©": 1437, + "Ġval": 1438, + "åĮ»": 1439, + "æķĪçİĩ": 1440, + "ä¸ī": 1441, + "èĩªçĦ¶": 1442, + "com": 1443, + "æĢ§åĴĮ": 1444, + "'t": 1445, + "æį¢": 1446, + "æ¸ħ": 1447, + "Ġapp": 1448, + "ç´¢": 1449, + "éģ¿åħį": 1450, + "æĿĤ": 1451, + "Ġ[": 1452, + "Ġdes": 1453, + "æĿĥ": 1454, + "å®ĥ们": 1455, + "è¶³": 1456, + "åĨħ容": 1457, + "èĭ": 1458, + "Ġlist": 1459, + "åIJĦ": 1460, + "æĺ¯åIJ¦": 1461, + "æĸ¹éĿ¢": 1462, + "Ġequ": 1463, + "没æľī": 1464, + "ĠChin": 1465, + "çĭ": 1466, + "æİĴ": 1467, + "åĪĴ": 1468, + "çĻ": 1469, + "æīĢæľī": 1470, + "认": 1471, + "éĶ®": 1472, + "äºĴ": 1473, + "è¯į": 1474, + "ire": 1475, + "åij³": 1476, + "æĸ½": 1477, + "è®Ń": 1478, + "æľīæķĪ": 1479, + "é¥": 1480, + "Ġfol": 1481, + "ari": 1482, + "带": 1483, + "æ´»åĬ¨": 1484, + "è´¹": 1485, + "In": 1486, + "æŀģ": 1487, + "ä½İ": 1488, + "ace": 1489, + "ely": 1490, + "Ġcode": 1491, + "åħ¶ä»ĸ": 1492, + "irst": 1493, + "Ġwill": 1494, + "ä¼ĺåĮĸ": 1495, + "ç»ĥ": 1496, + "å¼Ģåıij": 1497, + "车": 1498, + "享": 1499, + "Ġmost": 1500, + "Ġsom": 1501, + "Ġfollow": 1502, + "Ġdet": 1503, + "çĪ": 1504, + "å¤įæĿĤ": 1505, + "op": 1506, + "åºĵ": 1507, + "Ġform": 1508, + "书": 1509, + "æĢĿ": 1510, + "ç©¶": 1511, + "iven": 1512, + "å¸ĮæľĽ": 1513, + "åĪĹ表": 1514, + "Ġcomm": 1515, + "è¿Ļç§į": 1516, + "åĿĹ": 1517, + "gh": 1518, + "è½»": 1519, + "åŃĹ符": 1520, + "çķ¥": 1521, + "ep": 1522, + "Ġacc": 1523, + "ĠHowever": 1524, + "ally": 1525, + "è¾ĥ": 1526, + "åij½": 1527, + "åĽłä¸º": 1528, + "è¶£": 1529, + "ç¦": 1530, + "èĢĥèĻij": 1531, + "è¯Ŀ": 1532, + "ord": 1533, + "Ġmod": 1534, + "ĠU": 1535, + "Ġcomp": 1536, + "ãĥ": 1537, + "Ġ7": 1538, + "èİ·": 1539, + "åĥ": 1540, + "å¢ŀåĬł": 1541, + "įä½ľ": 1542, + "ĠPython": 1543, + "è·¯": 1544, + "ac": 1545, + "åijĬ": 1546, + "åıª": 1547, + "以ä¸ĭæĺ¯": 1548, + "ä½łåı¯ä»¥": 1549, + "çłĶç©¶": 1550, + "è°ĥæķ´": 1551, + "Ġro": 1552, + "Ġdata": 1553, + "çĸĹ": 1554, + "读": 1555, + "å¾ĭ": 1556, + "ry": 1557, + "è§ģ": 1558, + "print": 1559, + "è£ħ": 1560, + "ample": 1561, + "\\]": 1562, + "Ġsent": 1563, + "Ġmight": 1564, + "ä¿ĥ": 1565, + "çīĩ": 1566, + "ħ§": 1567, + "Ġ\\]": 1568, + "æĮģç»Ń": 1569, + ".\"": 1570, + "åı¯èĥ½ä¼ļ": 1571, + "åı£": 1572, + "å¸ĥ": 1573, + "å¾Ī": 1574, + "åı·": 1575, + "éĵ¾": 1576, + "ello": 1577, + "çī¹å®ļ": 1578, + "æĸĩä»¶": 1579, + "--": 1580, + "èĩ³": 1581, + "ãĢĬ": 1582, + "æĪı": 1583, + "ãĢĭ": 1584, + "urre": 1585, + "åĥı": 1586, + "ems": 1587, + "åħ´": 1588, + "ä¸ĸ": 1589, + "è´¨éĩı": 1590, + "æ¼": 1591, + "ress": 1592, + "éϤ": 1593, + "æĥħåĨµ": 1594, + "Ġ{": 1595, + "Ġ8": 1596, + "çĦ¶åIJİ": 1597, + "å¦Ĥæŀľä½ł": 1598, + "åı¥": 1599, + "Python": 1600, + "ph": 1601, + "Hello": 1602, + "æŁIJ": 1603, + "å¹³åı°": 1604, + "æĶ¿": 1605, + "ertain": 1606, + "å½ķ": 1607, + "imes": 1608, + "Ġpart": 1609, + "ound": 1610, + "wen": 1611, + "Ġnumbers": 1612, + "Ġper": 1613, + "åĪĽå»º": 1614, + "sw": 1615, + "åıĺåĮĸ": 1616, + "fere": 1617, + "å·¥åħ·": 1618, + "Ġnew": 1619, + "éĺ²": 1620, + "Ġwhat": 1621, + "angu": 1622, + "Ġone": 1623, + "æķĻèĤ²": 1624, + "Ġserv": 1625, + "Ġname": 1626, + "ff": 1627, + "Ġ#": 1628, + "ç®Ĺæ³ķ": 1629, + "误": 1630, + "åħħ": 1631, + "ition": 1632, + "con": 1633, + "one": 1634, + "Ġtheir": 1635, + "å®ŀéĻħ": 1636, + "ack": 1637, + "ft": 1638, + "é»": 1639, + "ram": 1640, + "åĪ©ç͍": 1641, + "æīĵ": 1642, + "æĵįä½ľ": 1643, + "Ġsol": 1644, + "æ¶Ī": 1645, + "ç§°": 1646, + "):": 1647, + "æĪĸèĢħ": 1648, + "Ġother": 1649, + "Ġall": 1650, + "Ġusing": 1651, + "ript": 1652, + "Qwen": 1653, + "çļĦæĺ¯": 1654, + "ear": 1655, + "ç»ĵæŀľ": 1656, + "ä¿ĥè¿Ľ": 1657, + "Ġcl": 1658, + "æľªæĿ¥": 1659, + "ak": 1660, + "æİ§åζ": 1661, + "ory": 1662, + "çŁ": 1663, + "Ġlangu": 1664, + "Ġsy": 1665, + "åĽłæŃ¤": 1666, + "éĢĤåIJĪ": 1667, + "vers": 1668, + "Ġinto": 1669, + "Ġinte": 1670, + "To": 1671, + "Ġman": 1672, + "温": 1673, + "好çļĦ": 1674, + "æľīåĬ©": 1675, + "23": 1676, + "ç»ĵæŀĦ": 1677, + "æĿIJ": 1678, + "èIJ¥": 1679, + "Ġvari": 1680, + "æľīåĬ©äºİ": 1681, + "Ġem": 1682, + "Ġcol": 1683, + "Ġcont": 1684, + "ng": 1685, + "ç«Ļ": 1686, + "æĸ°çļĦ": 1687, + "æıı": 1688, + "ish": 1689, + "Ġoff": 1690, + "询": 1691, + "åİ»": 1692, + "缸åħ³": 1693, + "æĺ¾": 1694, + "Ġout": 1695, + "ased": 1696, + "èŀ": 1697, + "rite": 1698, + "æµİ": 1699, + "æĺŁ": 1700, + "Ġbet": 1701, + "æķħäºĭ": 1702, + "åı²": 1703, + "Ġansw": 1704, + "为äºĨ": 1705, + "èĩªå·±çļĦ": 1706, + "Ġworld": 1707, + "设å¤ĩ": 1708, + "Ġwould": 1709, + "Ġfirst": 1710, + "æĬķ": 1711, + "第": 1712, + "Ġev": 1713, + "åģļ": 1714, + "æ£ĢæŁ¥": 1715, + "ç»ıæµİ": 1716, + "Ġty": 1717, + "ä¸ĵä¸ļ": 1718, + "åĶ": 1719, + "帮åĬ©ä½ł": 1720, + "ĠÐ": 1721, + "软": 1722, + "缴æİ¥": 1723, + "èij": 1724, + "å¼Ģå§ĭ": 1725, + "ia": 1726, + "Ġinclud": 1727, + "æľĪ": 1728, + "ç²¾": 1729, + "åĽ´": 1730, + "ments": 1731, + "ature": 1732, + "éĩĬ": 1733, + "Ġterm": 1734, + "åĤ¨": 1735, + "urrent": 1736, + "ru": 1737, + "__": 1738, + "ç±»åŀĭ": 1739, + "ä»»åĬ¡": 1740, + "Ġ*": 1741, + "åĵª": 1742, + "Ġcal": 1743, + "Ġalso": 1744, + "éĶĻ": 1745, + "Ġam": 1746, + "we": 1747, + "ä½ľä¸º": 1748, + "Ġanswer": 1749, + "念": 1750, + "游æĪı": 1751, + "æľºåύ": 1752, + "day": 1753, + "Ġlet": 1754, + "Ġused": 1755, + "çĹħ": 1756, + "åIJ¸": 1757, + "两个": 1758, + "æī¾åΰ": 1759, + "级": 1760, + "Ġsum": 1761, + "åħ³ç³»": 1762, + "ne": 1763, + "注æĦı": 1764, + "Ġplease": 1765, + "Ġeach": 1766, + "çļĦæĥ": 1767, + "导èĩ´": 1768, + "é¢ij": 1769, + "ç¥ŀ": 1770, + "å¤ļ个": 1771, + "Ġsp": 1772, + "便": 1773, + "èŀį": 1774, + "Ġexp": 1775, + "è¾ĵåħ¥": 1776, + "åŁİ": 1777, + "è¿ŀ": 1778, + "æıIJåįĩ": 1779, + "ten": 1780, + "åİĭ": 1781, + "eth": 1782, + "åħ³éĶ®": 1783, + "éĥ¨åĪĨ": 1784, + "çŃĸçķ¥": 1785, + "Ġref": 1786, + "cul": 1787, + "å±Ĥ": 1788, + "æ¢": 1789, + "Ġsentence": 1790, + "çĮ": 1791, + "许": 1792, + "éĩĩ": 1793, + "骤": 1794, + "ning": 1795, + "ä½ĵéªĮ": 1796, + "è¿ľ": 1797, + "ify": 1798, + "ertainly": 1799, + "表达": 1800, + "æĽ´å¤ļ": 1801, + "lic": 1802, + "ä¼°": 1803, + "å·²": 1804, + "åĪĽæĸ°": 1805, + "Ġche": 1806, + "ating": 1807, + "éĶĢ": 1808, + "èĩªåĬ¨": 1809, + "èīº": 1810, + "èı": 1811, + "\",": 1812, + "串": 1813, + "ç¢": 1814, + "ä¿®": 1815, + "æĤ": 1816, + "Ġ|": 1817, + "Ġwant": 1818, + "Ġcurrent": 1819, + "Ġexample": 1820, + "ç¨ĭåºı": 1821, + "欢": 1822, + "Ġlar": 1823, + "nder": 1824, + "æĺ¯ä¸Ģç§į": 1825, + "çĽij": 1826, + "份": 1827, + "Ġwhere": 1828, + "é£İéĻ©": 1829, + "Ġprovid": 1830, + "ath": 1831, + "å¼Ĥ": 1832, + "å¯Į": 1833, + "çļĦæĬ": 1834, + "çĥŃ": 1835, + "æĢ§èĥ½": 1836, + "red": 1837, + "çĭ¬": 1838, + "åĩĨç¡®": 1839, + "æ¯ı个": 1840, + "äºĮ": 1841, + "æ°ij": 1842, + "ss": 1843, + "Ġquestions": 1844, + "æij": 1845, + "Ġdif": 1846, + "Ġbo": 1847, + "Ġsuch": 1848, + "æµ·": 1849, + "éĻį": 1850, + "Ġtext": 1851, + "åIJĪä½ľ": 1852, + "Ġloo": 1853, + "Ġtw": 1854, + "失": 1855, + "客æĪ·": 1856, + "è®Ńç»ĥ": 1857, + "éŨ": 1858, + "ijæĪĺ": 1859, + "ices": 1860, + "Ġ9": 1861, + "ä¸įåIJĮçļĦ": 1862, + "åĮħåIJ«": 1863, + "ject": 1864, + "æĴ": 1865, + "åĬ¿": 1866, + "å°Ŀ": 1867, + "ä»»ä½ķ": 1868, + "Ġmodel": 1869, + "ents": 1870, + "æİ¢": 1871, + "Ġchar": 1872, + "é¦ĸåħĪ": 1873, + "åİĨåı²": 1874, + "ä¸Ķ": 1875, + "人类": 1876, + "ĠHere": 1877, + "æĪIJæľ¬": 1878, + "å¾Ģ": 1879, + "date": 1880, + "ds": 1881, + "æIJ": 1882, + "Ġprint": 1883, + "èĬ±": 1884, + "ual": 1885, + "è´Ń": 1886, + "å¾®": 1887, + "ĠY": 1888, + "Ġtas": 1889, + "Ġtrans": 1890, + "ĠAI": 1891, + "è¿Ļæł·": 1892, + "å®ļæľŁ": 1893, + "æ¯Ķå¦Ĥ": 1894, + "olog": 1895, + "æĸĩæľ¬": 1896, + "设置": 1897, + "å·´å·´": 1898, + "éĺ¿éĩĮå·´å·´": 1899, + "au": 1900, + "Ġfollowing": 1901, + "è¾¹": 1902, + "Ġlanguage": 1903, + "Ġhere": 1904, + "Ġele": 1905, + "æııè¿°": 1906, + "les": 1907, + "éļ¾": 1908, + "èģĮ": 1909, + "ava": 1910, + "软件": 1911, + "о": 1912, + "约": 1913, + "Certainly": 1914, + "Ġcap": 1915, + "è¡Į为": 1916, + "Ġrange": 1917, + "¿Ģ": 1918, + "ative": 1919, + "ues": 1920, + ").": 1921, + "Ġtech": 1922, + "æ³Ľ": 1923, + "Ġfeel": 1924, + "ĊĠ": 1925, + "ĠSt": 1926, + "Can": 1927, + "000": 1928, + "bs": 1929, + "ful": 1930, + "It": 1931, + "å°½": 1932, + "åŃĺåĤ¨": 1933, + "æ²»": 1934, + "åIJĹ": 1935, + "æĬ¥": 1936, + "建ç«ĭ": 1937, + "åıĤä¸İ": 1938, + "å¿«éĢŁ": 1939, + "åĩ»": 1940, + "çª": 1941, + "è¯Ħä¼°": 1942, + "Ġunder": 1943, + "å°Ŀè¯ķ": 1944, + "ors": 1945, + "Ġtra": 1946, + "Ġ/": 1947, + "çļĦæĥħ": 1948, + "éļıçĿĢ": 1949, + "ä¸ĸçķĮ": 1950, + "Ġrec": 1951, + "æĮī": 1952, + "Ġtime": 1953, + "头": 1954, + "Ġstring": 1955, + "åħ³äºİ": 1956, + "åħ·ä½ĵçļĦ": 1957, + "è¾ĵåĩº": 1958, + "åŁºæľ¬": 1959, + "åħ·æľī": 1960, + "åĪĿ": 1961, + "çħ§": 1962, + "åĬ±": 1963, + "ä¸Ģç§į": 1964, + "åŃĹ符串": 1965, + "cript": 1966, + "èĮĥ": 1967, + "ä½łå¥½": 1968, + "Ġsim": 1969, + "以ä¸ĭæĺ¯ä¸ĢäºĽ": 1970, + "æŃ¥éª¤": 1971, + "æ¤": 1972, + "Ġ<": 1973, + "Ġvalue": 1974, + "Ġthere": 1975, + "ery": 1976, + "款": 1977, + "æ¿Ģ": 1978, + "eng": 1979, + "æĭŁ": 1980, + "Ġdis": 1981, + "ray": 1982, + "Ġcontext": 1983, + "åıĭ": 1984, + "å¾ģ": 1985, + "æĹħ": 1986, + "æµĭè¯ķ": 1987, + "Ġdiffere": 1988, + "ll": 1989, + "åħ´è¶£": 1990, + "ç§ijåѦ": 1991, + "åħ¨çIJĥ": 1992, + "Ġinter": 1993, + "èĥĮ": 1994, + "æĬĢæľ¯çļĦ": 1995, + "å®ĮæĪIJ": 1996, + "æĸ¹æ¡Ī": 1997, + "ital": 1998, + "ä¸įæĸŃ": 1999, + "ark": 2000, + "Ġph": 2001, + "çĽĬ": 2002, + "led": 2003, + "Ġi": 2004, + "æļ": 2005, + "ç¿": 2006, + "å·®": 2007, + "éļIJ": 2008, + "ber": 2009, + "ä¹ĭéĹ´çļĦ": 2010, + "ĠQwen": 2011, + "ays": 2012, + "ĠK": 2013, + "å¿ħ": 2014, + "éĤ£ä¹Ī": 2015, + "åij¨": 2016, + "å¹¿æ³Ľ": 2017, + "roup": 2018, + "erson": 2019, + "ics": 2020, + "çŁ¥è¯Ĩ": 2021, + "ç§ijæĬĢ": 2022, + "ese": 2023, + "æıIJä¾ĽäºĨ": 2024, + "å¸Ī": 2025, + "times": 2026, + "è´Ł": 2027, + "åħ»": 2028, + "Ġnum": 2029, + "ict": 2030, + "è¡Įä¸ļ": 2031, + "Ġtechn": 2032, + "éĶĻ误": 2033, + "çļĦæķ°æį®": 2034, + "è¿Ķ": 2035, + "èī¯": 2036, + "',": 2037, + "Ġcheck": 2038, + "),": 2039, + "ails": 2040, + "æĿ¡ä»¶": 2041, + "ä»·å̼": 2042, + "çα": 2043, + "è¿ij": 2044, + "Ġfree": 2045, + "gram": 2046, + "åħĭ": 2047, + "ç±³": 2048, + "Ġhis": 2049, + "åŃĺåľ¨": 2050, + "hen": 2051, + "ç®Ģåįķ": 2052, + "访": 2053, + "声": 2054, + "ular": 2055, + "gest": 2056, + "ĠFor": 2057, + "åı¤": 2058, + "ç§ģ": 2059, + "åįı": 2060, + "åŁºäºİ": 2061, + "æĽ´å¥½": 2062, + "Ġadd": 2063, + "As": 2064, + "è´£": 2065, + "æķ°ç»Ħ": 2066, + "Ġrel": 2067, + "å®ļä¹ī": 2068, + "çīĮ": 2069, + "她": 2070, + "è¯ĨåĪ«": 2071, + "人工": 2072, + "åŁºç¡": 2073, + "åŁºç¡Ģ": 2074, + "表示": 2075, + "çĶ»": 2076, + "离": 2077, + "Ġcomput": 2078, + "row": 2079, + "åºĶ该": 2080, + "ä»İèĢĮ": 2081, + "è¶Ĭ": 2082, + "è¿ĶåĽŀ": 2083, + "èijĹ": 2084, + "æĿIJæĸĻ": 2085, + "æŀĹ": 2086, + "Ġresult": 2087, + "æĺ¯åľ¨": 2088, + "ç¼ĸç¨ĭ": 2089, + "(\"": 2090, + "andom": 2091, + "def": 2092, + "è§£éĩĬ": 2093, + "åģĩ": 2094, + "Ġì": 2095, + "é¼": 2096, + "ï¼īï¼Į": 2097, + "éģĩ": 2098, + "ient": 2099, + "cial": 2100, + "ens": 2101, + "Ġtwo": 2102, + "寻": 2103, + "Ġsome": 2104, + "Ġthem": 2105, + "QL": 2106, + "ern": 2107, + "ä¸ĬçļĦ": 2108, + "Ġwho": 2109, + "Ġthey": 2110, + "ä½Ĩæĺ¯": 2111, + "å¢ŀ强": 2112, + "åı¯èĥ½éľĢè¦ģ": 2113, + "ä¼Ĺ": 2114, + "velop": 2115, + "åŁİå¸Ĥ": 2116, + "æĤ¨çļĦ": 2117, + "е": 2118, + "der": 2119, + "åĭ": 2120, + "åħ±åIJĮ": 2121, + "Ġtoday": 2122, + "å¡": 2123, + "Ġcharact": 2124, + "fore": 2125, + "åľºæĻ¯": 2126, + "åĽłç´ł": 2127, + "Ġseems": 2128, + "ble": 2129, + "ç§»": 2130, + "羣": 2131, + "ather": 2132, + "rough": 2133, + "丰": 2134, + "numb": 2135, + "))": 2136, + "ater": 2137, + "cess": 2138, + "Ġset": 2139, + "çģµ": 2140, + "Ġover": 2141, + "ize": 2142, + "åŁ¹": 2143, + "è¦ģæ±Ĥ": 2144, + "åĿĩ": 2145, + "Ġdetails": 2146, + "æĬķèµĦ": 2147, + "èħ": 2148, + "ç»ĩ": 2149, + "ĠIn": 2150, + "ility": 2151, + "èĢģ": 2152, + "ween": 2153, + "å¤ļç§į": 2154, + "Ġequation": 2155, + "ible": 2156, + "rit": 2157, + "åı¯ä»¥éĢļè¿ĩ": 2158, + "Ġpop": 2159, + "Ġincluding": 2160, + "æĽ´å¥½åľ°": 2161, + "èīºæľ¯": 2162, + "its": 2163, + "Ġknown": 2164, + "Ġpre": 2165, + "ood": 2166, + "ç½ijç«Ļ": 2167, + "Ġprogram": 2168, + "çŃĶ": 2169, + "ities": 2170, + "转æį¢": 2171, + "Ġbetween": 2172, + "Ġtasks": 2173, + "éĺ¿éĩĮäºij": 2174, + "è¶ħ": 2175, + "ĠChina": 2176, + "示ä¾ĭ": 2177, + "çϽ": 2178, + "太": 2179, + "ï¼īãĢĤ": 2180, + "ç¡®å®ļ": 2181, + "Ġ19": 2182, + "产çĶŁ": 2183, + "æŃ¢": 2184, + "Ġsign": 2185, + "ŁéĢļ": 2186, + "Ġyear": 2187, + "Ġpe": 2188, + "访éĹ®": 2189, + "ook": 2190, + "æī§": 2191, + "Ġdevelop": 2192, + "ï¼ī**ï¼ļ": 2193, + "Ġthan": 2194, + "duct": 2195, + "èĭ±": 2196, + "Ġthese": 2197, + "Ġperson": 2198, + "循": 2199, + "äºī": 2200, + "Ġcalcul": 2201, + "é¢Ħæµĭ": 2202, + "rib": 2203, + "æĮijæĪĺ": 2204, + "åIJ¬": 2205, + "Ġboth": 2206, + "Ġdon": 2207, + "12": 2208, + "tal": 2209, + "åıijçݰ": 2210, + "ys": 2211, + "**:": 2212, + "åĮºåĿĹ": 2213, + "çļĦå½±åĵį": 2214, + "å¨": 2215, + "Ġ12": 2216, + "çīĪ": 2217, + "stand": 2218, + "ç»Ī": 2219, + "åĸľ": 2220, + "list": 2221, + "ä¹°": 2222, + "åĨį": 2223, + "This": 2224, + "人工æĻºèĥ½": 2225, + "åĮ»çĸĹ": 2226, + "ä»ĺ": 2227, + "å±ŀ": 2228, + "æ¼Ķ": 2229, + "çļĦ人": 2230, + "çļĦæł": 2231, + "计åĪĴ": 2232, + "pon": 2233, + "è¿ĩç¨ĭä¸Ń": 2234, + "丰å¯Į": 2235, + "模å¼ı": 2236, + "åIJĦç§į": 2237, + "Ġwhen": 2238, + "积æŀģ": 2239, + "åĩĨå¤ĩ": 2240, + "ä¸Ĭè¿°": 2241, + "è¿IJè¡Į": 2242, + "æĪIJ为": 2243, + "è¯ī": 2244, + "è§ī": 2245, + "Ġtotal": 2246, + "èįIJ": 2247, + "æ¦Ĥ": 2248, + "é¸": 2249, + "Ġcons": 2250, + "éĺ³": 2251, + "ä½ľç͍": 2252, + "åŃ©": 2253, + "ä¸įæĺ¯": 2254, + "é«ĺæķĪ": 2255, + "Ġtyp": 2256, + "ower": 2257, + "æ·»": 2258, + "讨": 2259, + "а": 2260, + "稳": 2261, + "åĨ³çŃĸ": 2262, + "to": 2263, + "éĿĻ": 2264, + "Ġra": 2265, + "åı¯ä»¥ä½¿ç͍": 2266, + "Ġdiv": 2267, + "带æĿ¥": 2268, + "è¾ij": 2269, + "åºŃ": 2270, + "è®°å½ķ": 2271, + "stem": 2272, + "Ġgre": 2273, + "çļĦæī": 2274, + "è¯Ĺ": 2275, + "Ġlast": 2276, + "Ġ100": 2277, + "åѦçĶŁ": 2278, + "端": 2279, + "Ġinteg": 2280, + "Ġgiven": 2281, + "Ġsub": 2282, + "ĠChinese": 2283, + "è¥": 2284, + "Ġadv": 2285, + "社交": 2286, + "令": 2287, + "Ġcould": 2288, + "Who": 2289, + "è¿IJåĬ¨": 2290, + "èıľ": 2291, + "atest": 2292, + "åıĤæķ°": 2293, + "pro": 2294, + "Ġvers": 2295, + "å®Ŀ": 2296, + "çļĦéĩįè¦ģ": 2297, + "èϽ": 2298, + "ĠV": 2299, + "çĶŁçī©": 2300, + "Ġdifferent": 2301, + "å±±": 2302, + "ä½įç½®": 2303, + "西": 2304, + "ise": 2305, + "大çļĦ": 2306, + "é¦Ī": 2307, + "Ġgenerate": 2308, + "Ġthrough": 2309, + "Ġdoes": 2310, + "è´¢": 2311, + "ªæĸ½": 2312, + "è½½": 2313, + "19": 2314, + "umn": 2315, + "15": 2316, + "éĺħ": 2317, + "Ġind": 2318, + "详": 2319, + "Ġcount": 2320, + "ÑĤ": 2321, + "Sure": 2322, + "åĩłä¸ª": 2323, + "åĵģçīĮ": 2324, + "æ³ķå¾ĭ": 2325, + "æī§è¡Į": 2326, + "åħ¸": 2327, + "èϽçĦ¶": 2328, + "çģ«": 2329, + "èĥĮæĻ¯": 2330, + "éŁ³ä¹IJ": 2331, + "ä¸ļåĬ¡": 2332, + "æĸ¯": 2333, + "So": 2334, + "åħ¶ä¸Ń": 2335, + "éĢŁåº¦": 2336, + "ä¼¼": 2337, + "æİ¨èįIJ": 2338, + "Ġmult": 2339, + "åĽ½éĻħ": 2340, + "çĬ¶æĢģ": 2341, + "åĪ¶ä½ľ": 2342, + "Ġsqu": 2343, + "Ġtechnolog": 2344, + "æŀ¶": 2345, + "åıįé¦Ī": 2346, + "Ġstart": 2347, + "绿": 2348, + "è¡¡": 2349, + "Ġget": 2350, + "è§Ĵèī²": 2351, + "åĽ½å®¶": 2352, + "get": 2353, + "xim": 2354, + "Ġdesign": 2355, + "co": 2356, + "åį°": 2357, + "If": 2358, + "满足": 2359, + "ä¸Ģä¸ĭ": 2360, + "å®¶åºŃ": 2361, + "ç»Ħç»ĩ": 2362, + "交æµģ": 2363, + "riend": 2364, + "çļĦæĦ": 2365, + "Ġmat": 2366, + "èİ·åıĸ": 2367, + "ĠCould": 2368, + "追": 2369, + "å¼ł": 2370, + "ob": 2371, + "ä¼łç»Ł": 2372, + "æłij": 2373, + "é¼ĵ": 2374, + "Ġunderstand": 2375, + "Ġcor": 2376, + "å¿ĥçIJĨ": 2377, + "æİªæĸ½": 2378, + "Ġgo": 2379, + "Ġhum": 2380, + "èį¯": 2381, + "交æĺĵ": 2382, + "round": 2383, + "Ġimport": 2384, + "çļĦä¸Ģ": 2385, + "èĻļ": 2386, + "稳å®ļ": 2387, + "ä»ĭ": 2388, + "è¿Ļä¸Ģ": 2389, + "åĩºçݰ": 2390, + "и": 2391, + "ational": 2392, + "nce": 2393, + "é¢Ŀ": 2394, + "cre": 2395, + "Ġno": 2396, + "Ġart": 2397, + "åĪĩ": 2398, + "åª": 2399, + "Ġel": 2400, + "常è§ģ": 2401, + "çļĦæĮ": 2402, + "éĩıåŃIJ": 2403, + "Ġke": 2404, + "åĽĽ": 2405, + "å²": 2406, + "å½Ĵ": 2407, + "社åĮº": 2408, + "Ġmain": 2409, + "è¿ŀæİ¥": 2410, + "åľ¨çº¿": 2411, + "饮": 2412, + "å½ĵçĦ¶": 2413, + "åĶ®": 2414, + "ç»´æĬ¤": 2415, + "çļĦå®īåħ¨": 2416, + "çŁŃ": 2417, + "è¯ij": 2418, + "Ġlatest": 2419, + "æĥħæĦŁ": 2420, + "ç«ŀ": 2421, + "ian": 2422, + "Ġthen": 2423, + "æ¶ī": 2424, + "æ¤į": 2425, + "éĩıçļĦ": 2426, + "Ġ2023": 2427, + "Ġrem": 2428, + "wor": 2429, + "å±Ģ": 2430, + "num": 2431, + "红": 2432, + "Ġbeen": 2433, + "ake": 2434, + "åħ³æ³¨": 2435, + "æĪIJåijĺ": 2436, + "ick": 2437, + "()`": 2438, + "详ç»Ĩ": 2439, + "ident": 2440, + "æłĩåĩĨ": 2441, + "Ġsignific": 2442, + "è¿ĻéĩĮ": 2443, + "sq": 2444, + "èĮĥåĽ´": 2445, + "ength": 2446, + "iness": 2447, + "æķĪæŀľ": 2448, + "å¹²": 2449, + "çİ©": 2450, + "Ġcomple": 2451, + "æ´ģ": 2452, + "æĬĢèĥ½": 2453, + "Ġoften": 2454, + "ages": 2455, + "çļĦæĶ": 2456, + "éļIJç§ģ": 2457, + "rest": 2458, + "Ġrefer": 2459, + "èĽ": 2460, + "æ³¢": 2461, + "éĥ½æĺ¯": 2462, + ");": 2463, + "åŃ©åŃIJ": 2464, + "ç´§": 2465, + "æħ": 2466, + "éĢı": 2467, + "å°Ķ": 2468, + "åĸĦ": 2469, + "hes": 2470, + "æĿ¾": 2471, + "ä¾Ŀ": 2472, + "åĢĻ": 2473, + "Ġservices": 2474, + "ç͵åŃIJ": 2475, + "æŃ£ç¡®": 2476, + "å·²ç»ı": 2477, + "æīį": 2478, + "çļĦæİ": 2479, + "æľºåζ": 2480, + "çī¹åĪ«": 2481, + "åĪĽéĢł": 2482, + "使å¾Ĺ": 2483, + "åªĴ": 2484, + "11": 2485, + "Ġdep": 2486, + "Is": 2487, + "ä¸ĢæŃ¥": 2488, + "åĵªäºĽ": 2489, + "èģĶç³»": 2490, + "é¼ĵåĬ±": 2491, + "å¥ĩ": 2492, + "éĢĤåºĶ": 2493, + "ï¼Ľ": 2494, + "çļĦæĬĢ": 2495, + "个æĢ§": 2496, + "ç¿»": 2497, + "Ġjust": 2498, + "Ġbus": 2499, + "Ġreg": 2500, + "å½¢æĪIJ": 2501, + "ĠĠĠĠĠ": 2502, + "here": 2503, + "çªģ": 2504, + "éģĩåΰ": 2505, + "âĢĿãĢĤ": 2506, + "Write": 2507, + "çļĦéĹ®é¢ĺ": 2508, + "åĪ»": 2509, + "lect": 2510, + "ä¹ĭä¸Ģ": 2511, + "å¾Ħ": 2512, + "人们": 2513, + "Ġfact": 2514, + "æ·»åĬł": 2515, + "åijĺå·¥": 2516, + "交éĢļ": 2517, + "ose": 2518, + "é¾": 2519, + "ove": 2520, + "ax": 2521, + "Ġvarious": 2522, + "Ġbased": 2523, + "åĬłåħ¥": 2524, + "cept": 2525, + "æĪIJåĬŁ": 2526, + "ĠGroup": 2527, + "åĮºåĿĹéĵ¾": 2528, + "50": 2529, + "åģı": 2530, + "çļĦä¿¡æģ¯": 2531, + "Ġoper": 2532, + "name": 2533, + "åIJ¸å¼ķ": 2534, + "æ¹": 2535, + "åį¡": 2536, + "ract": 2537, + "æİ¢ç´¢": 2538, + "æĽ´æĸ°": 2539, + "Ġlear": 2540, + "空éĹ´": 2541, + "inal": 2542, + "æ¶īåıĬ": 2543, + "æ½": 2544, + "ural": 2545, + "ars": 2546, + "Ġmany": 2547, + "blem": 2548, + "Ġsk": 2549, + "è¿ĺèĥ½": 2550, + "ä¸ĭéĿ¢": 2551, + "æľīæīĢ": 2552, + "Ġcreate": 2553, + "æĺİç¡®": 2554, + "ince": 2555, + "æŀĦ建": 2556, + "ings": 2557, + "æĹłæ³ķ": 2558, + "éĢIJ": 2559, + "æĦıåij³": 2560, + "æĪ¿": 2561, + "å¾Ĺåΰ": 2562, + "人çļĦ": 2563, + "åªĴä½ĵ": 2564, + "âĢĿï¼Į": 2565, + "Ġsupp": 2566, + "è¿Ļæĺ¯": 2567, + "æĴŃ": 2568, + "old": 2569, + "åıĹåΰ": 2570, + "çı": 2571, + "çĦ¶èĢĮ": 2572, + "pect": 2573, + "Ġsquare": 2574, + "计ç®Ĺæľº": 2575, + "ude": 2576, + "æŃ¤å¤ĸ": 2577, + "è¿İ": 2578, + "ically": 2579, + "æİĴåºı": 2580, + "æľĢåIJİ": 2581, + "Ġprocess": 2582, + "ause": 2583, + "éĩijèŀį": 2584, + "害": 2585, + "Ġloc": 2586, + "åĪĨ享": 2587, + "æ²¹": 2588, + "Ġsm": 2589, + "çļĦæ°": 2590, + "课": 2591, + "ä»·æł¼": 2592, + "页": 2593, + "dition": 2594, + "åΤ": 2595, + "æŁIJäºĽ": 2596, + "Ġbl": 2597, + "æķ°æį®åºĵ": 2598, + ",\"": 2599, + "·": 2600, + "çī¹å¾ģ": 2601, + "éļľ": 2602, + "åģĩ设": 2603, + "åįĹ": 2604, + "Given": 2605, + "Ġexpress": 2606, + "åijĬè¯ī": 2607, + "Ġfil": 2608, + "åĽ¾åĥı": 2609, + "ci": 2610, + "Ġquestion": 2611, + "个æĢ§åĮĸ": 2612, + "ç¯": 2613, + "ced": 2614, + "ĠĊ": 2615, + "é¤": 2616, + "Ġpos": 2617, + "elf": 2618, + "å°¤": 2619, + "å¦ĤæŀľæĤ¨": 2620, + "æĪijåı¯ä»¥": 2621, + "å®ļçļĦ": 2622, + "èĦij": 2623, + "Ġlead": 2624, + "æĦıåij³çĿĢ": 2625, + "Ġproblem": 2626, + "å±ħ": 2627, + "ix": 2628, + "æŃĮ": 2629, + "Ġtable": 2630, + "è´¦": 2631, + "群": 2632, + "ited": 2633, + "åľ°åĮº": 2634, + "çĶŁäº§": 2635, + "çļĦåıijå±ķ": 2636, + "æĶ¶éĽĨ": 2637, + "èµĽ": 2638, + "åıĪ": 2639, + "审": 2640, + "ä¸Ģ次": 2641, + "Ġcommon": 2642, + "ople": 2643, + "å·§": 2644, + "ution": 2645, + "Ġappro": 2646, + "æĶ¿çŃĸ": 2647, + "é¤IJ": 2648, + "çݰ代": 2649, + "è§ĦåĪĴ": 2650, + "åŃ£": 2651, + "ä½ı": 2652, + "Ġtransl": 2653, + "Ġdef": 2654, + "Ġpar": 2655, + "å¾·": 2656, + "çݯä¿Ŀ": 2657, + "主é¢ĺ": 2658, + "Ġbusiness": 2659, + "表çݰ": 2660, + "ãģ®": 2661, + "èĤ¡": 2662, + "深度": 2663, + "Ġmake": 2664, + "æŁĵ": 2665, + "éĺħ读": 2666, + "ĠUn": 2667, + "éģµ": 2668, + "èIJ½": 2669, + "ĠEng": 2670, + "讲": 2671, + "å¾ħ": 2672, + "Ġever": 2673, + "Ġact": 2674, + "çļĦæ¸": 2675, + "è§£åĨ³æĸ¹æ¡Ī": 2676, + "Ġrandom": 2677, + "çļĦåºĶç͍": 2678, + "èģĶç½ij": 2679, + "iqu": 2680, + "æ¶Īè´¹": 2681, + "irect": 2682, + "æľºåύåŃ¦ä¹ł": 2683, + "100": 2684, + "lish": 2685, + "Ġinv": 2686, + "åIJĪéĢĤ": 2687, + "rt": 2688, + "mer": 2689, + "åĪĽå»ºä¸Ģ个": 2690, + "éĻįä½İ": 2691, + "é²": 2692, + "å®ĺ": 2693, + "ĠPlease": 2694, + "string": 2695, + "èİ·å¾Ĺ": 2696, + "æľºä¼ļ": 2697, + "èĤī": 2698, + "室": 2699, + "æĭ¥": 2700, + "许å¤ļ": 2701, + "..": 2702, + "æľĭ": 2703, + "æĿ¥è¯´": 2704, + "ec": 2705, + "Ġonly": 2706, + "ç»ĵåIJĪ": 2707, + "é¦Ļ": 2708, + "ä¸Ńå¿ĥ": 2709, + "åıijçĶŁ": 2710, + "$.": 2711, + "åIJĪçIJĨ": 2712, + "åİĭåĬĽ": 2713, + "åħħ满": 2714, + "çĨ": 2715, + "verage": 2716, + "ï¼īãĢģ": 2717, + "Ġproduct": 2718, + "val": 2719, + "ost": 2720, + "icial": 2721, + "å¦": 2722, + "çļĦæŃ": 2723, + "Ġcompan": 2724, + "è§Ĩé¢ij": 2725, + "ts": 2726, + "ula": 2727, + "mat": 2728, + "iel": 2729, + "Ġweather": 2730, + "æ¯į": 2731, + "å¤ľ": 2732, + "Ġfriend": 2733, + "Ġrequ": 2734, + "ä½ľåĵģ": 2735, + "度åĴĮ": 2736, + "æł¸": 2737, + "ily": 2738, + "numbers": 2739, + "imal": 2740, + "åıĺéĩı": 2741, + "Ġask": 2742, + "Ġwide": 2743, + "æİ¨åĬ¨": 2744, + "Ġinst": 2745, + "ĠWor": 2746, + "è´Ńä¹°": 2747, + "н": 2748, + "åIJ¯": 2749, + "åįİ": 2750, + "Ġcloud": 2751, + "æĹ¢": 2752, + "èĥ½æºIJ": 2753, + "ized": 2754, + "refore": 2755, + "Ġpopul": 2756, + "è¿ĽæŃ¥": 2757, + "uring": 2758, + "è¿ĺæĺ¯": 2759, + "Al": 2760, + "á»": 2761, + "eters": 2762, + "å°Ħ": 2763, + "up": 2764, + "åζå®ļ": 2765, + "åĦ": 2766, + "ething": 2767, + "ute": 2768, + "åľ¨è¿Ļ个": 2769, + "å¸ģ": 2770, + "èĽĭ": 2771, + "Ġrespon": 2772, + "Ġway": 2773, + "ç³ĸ": 2774, + "app": 2775, + "ç͵影": 2776, + "æľĭåıĭ": 2777, + "Ġeven": 2778, + "æĢ§çļĦ": 2779, + "æ²ŁéĢļ": 2780, + "çļĦæĥħåĨµ": 2781, + "Ġfound": 2782, + "éĢĴ": 2783, + "Ġdec": 2784, + "çļĦæ¯": 2785, + "hip": 2786, + "åĽŃ": 2787, + "Ġeff": 2788, + "è¡Ģ": 2789, + "ç«ŀäºī": 2790, + "own": 2791, + "'d": 2792, + "Ñģ": 2793, + "Ġsignificant": 2794, + "Ġlight": 2795, + "èĻļæĭŁ": 2796, + "Ġmean": 2797, + "Ġsomething": 2798, + "ash": 2799, + "åĪĨç±»": 2800, + "严": 2801, + "ç»§": 2802, + "Ġnam": 2803, + "欢è¿İ": 2804, + "æĹłè®º": 2805, + "æĿ¿": 2806, + "Ġreal": 2807, + "Ġarea": 2808, + "åķ¡": 2809, + "çͱäºİ": 2810, + "å¿Ĺ": 2811, + "ç¼ĵ": 2812, + "ife": 2813, + "Ġdig": 2814, + "å°Ĭ": 2815, + "é£ŀ": 2816, + "士": 2817, + "Ġ\\[": 2818, + "身ä½ĵ": 2819, + "éªĮè¯ģ": 2820, + "æĺł": 2821, + "éĥ½æľī": 2822, + "Ġstand": 2823, + "ER": 2824, + "éķ¿æľŁ": 2825, + "quence": 2826, + "举": 2827, + "ving": 2828, + "arr": 2829, + "Ġhapp": 2830, + "æĶ¯ä»ĺ": 2831, + "Ġaccur": 2832, + "Ġher": 2833, + "对象": 2834, + "æī©": 2835, + "æŁ¥è¯¢": 2836, + "æĥ³è¦ģ": 2837, + "loy": 2838, + "奶": 2839, + "Ġaccess": 2840, + "Ġsystem": 2841, + "Ġofficial": 2842, + "顺": 2843, + "Ġhigh": 2844, + "Ġcapital": 2845, + "票": 2846, + "åĴĸ": 2847, + "å½ĵåīį": 2848, + "éĢģ": 2849, + "è·µ": 2850, + "import": 2851, + "rect": 2852, + "Ġpartic": 2853, + "Ġdesigned": 2854, + "æķ´æķ°": 2855, + "综": 2856, + "ĠCan": 2857, + "竳": 2858, + "EC": 2859, + "'ll": 2860, + "å±ķ示": 2861, + "循çݯ": 2862, + "cy": 2863, + "log": 2864, + "Ġplay": 2865, + "Ġsolve": 2866, + "æ¡£": 2867, + "åĴĸåķ¡": 2868, + "æĸ¹åIJij": 2869, + "çļĦåľ°": 2870, + "å®ŀè·µ": 2871, + "转æį¢ä¸º": 2872, + "ild": 2873, + "çķĻ": 2874, + "å®Ī": 2875, + "Ġ15": 2876, + "èģĮä¸ļ": 2877, + "å°¤åħ¶": 2878, + "æĭī": 2879, + "åıĮ": 2880, + "äºĨä¸Ģ个": 2881, + "Ġclass": 2882, + "Ġformula": 2883, + "äºĶ": 2884, + "çļĦæĹ¶éĹ´": 2885, + "æĶ¹åıĺ": 2886, + "Ġstate": 2887, + "ç»į": 2888, + "çIJĨ论": 2889, + "Ġpoint": 2890, + "Ġent": 2891, + "St": 2892, + "Ġday": 2893, + "æįŁ": 2894, + "缺": 2895, + "ately": 2896, + "æī¿": 2897, + "èµ·æĿ¥": 2898, + "Ġtry": 2899, + "Ġsupport": 2900, + "Ġrele": 2901, + "ä¼ļè®®": 2902, + "è¶ĭ": 2903, + "ä¹Łæĺ¯": 2904, + "ources": 2905, + "ending": 2906, + "çļĦæµ": 2907, + "Ġversion": 2908, + "Ġline": 2909, + "py": 2910, + "é£İæł¼": 2911, + "ting": 2912, + "¨è¯¢": 2913, + "åĴ¨è¯¢": 2914, + "ĠHe": 2915, + "临": 2916, + "åł": 2917, + "çļĦæ³": 2918, + "ä»ĭç»į": 2919, + "éĩįè¦ģçļĦ": 2920, + "30": 2921, + "оÐ": 2922, + "大å°ı": 2923, + "Ġtop": 2924, + "è´§": 2925, + "马": 2926, + "Ġbest": 2927, + "ç»Ļåĩº": 2928, + "åĨ·": 2929, + "è¿Ľä¸ĢæŃ¥": 2930, + "è§ĦåĪĻ": 2931, + "ov": 2932, + "æ¦Ĥ念": 2933, + "æĪIJéķ¿": 2934, + "èĹ": 2935, + "Ġperform": 2936, + "Ġcreated": 2937, + "ç¼ĸåĨĻ": 2938, + "Ġide": 2939, + "çŁ³": 2940, + "ľç´¢": 2941, + "èĩªæĪij": 2942, + "Ġwork": 2943, + "light": 2944, + "cle": 2945, + "âĢĻ": 2946, + "Ġinterest": 2947, + "彩": 2948, + "arch": 2949, + "å®īè£ħ": 2950, + "tern": 2951, + "åĪĽä½ľ": 2952, + "25": 2953, + "Ġlong": 2954, + "çī¹çĤ¹": 2955, + "Ġrep": 2956, + "ience": 2957, + "Ġinput": 2958, + "éĺ¶": 2959, + "责任": 2960, + "Ġbook": 2961, + "Ġdirect": 2962, + "ĊĠĠĠĠĠĠĠĠĠĠĠ": 2963, + "å½¢å¼ı": 2964, + "åºĹ": 2965, + "ublic": 2966, + "ìĿ": 2967, + "ĠWorld": 2968, + "åĨ²": 2969, + "çĭ¬çī¹": 2970, + "æķ°æį®çļĦ": 2971, + "æīĢ以": 2972, + "Ġshould": 2973, + "寻æī¾": 2974, + "Ġpeople": 2975, + "ĠEnglish": 2976, + "æķ°åѦ": 2977, + "ruct": 2978, + "ox": 2979, + "åĨħéĥ¨": 2980, + "è¶ĭåĬ¿": 2981, + "Ġcomputing": 2982, + "çī¹å®ļçļĦ": 2983, + "çļ®": 2984, + "éĨ": 2985, + "åĨ³å®ļ": 2986, + "æľįåĬ¡åύ": 2987, + "åĽ°": 2988, + "ä¹ĺ": 2989, + "åIJįç§°": 2990, + "ator": 2991, + "硬": 2992, + "å¿ħè¦ģ": 2993, + "Ġlarge": 2994, + "Ġmin": 2995, + "Ġassistance": 2996, + "äºĭä»¶": 2997, + "çļĦæ²": 2998, + "åľ°çIJĥ": 2999, + "çĽĸ": 3000, + "åıĬæĹ¶": 3001, + "Ġwebs": 3002, + "gy": 3003, + "èī¯å¥½çļĦ": 3004, + "ming": 3005, + "é": 3006, + "ç¿»è¯ij": 3007, + "16": 3008, + "éĻ¢": 3009, + "ink": 3010, + "æķ°éĩı": 3011, + "Ġupdate": 3012, + "æĺ¯ä»Ģä¹Ī": 3013, + "åĦ¿": 3014, + "èĬĤçĤ¹": 3015, + "åĨľ": 3016, + "Ġquery": 3017, + "çī©çIJĨ": 3018, + "女": 3019, + "æķ£": 3020, + "æĿİ": 3021, + "Ġtechnology": 3022, + "Ġarray": 3023, + "åľŁ": 3024, + "Ġstr": 3025, + "Ġvalues": 3026, + "Script": 3027, + "ÑĢ": 3028, + "hed": 3029, + "å¯Ł": 3030, + "ä¸įä¼ļ": 3031, + "ows": 3032, + "çĽĺ": 3033, + "éĵ¶": 3034, + "åķĨåĵģ": 3035, + "Ġlargest": 3036, + "eb": 3037, + "é¢ľ": 3038, + "åı¶": 3039, + "str": 3040, + "ctions": 3041, + "æ²»çĸĹ": 3042, + "raph": 3043, + "Ġbu": 3044, + "åĸľæ¬¢": 3045, + "æ°´å¹³": 3046, + "æ½ľ": 3047, + "Ġword": 3048, + "触": 3049, + "çĸ¾": 3050, + "éĴŁ": 3051, + "Ġwhile": 3052, + "Ġexper": 3053, + "æľºæŀĦ": 3054, + "çļĦæĻ": 3055, + "åĽº": 3056, + "Ġcharacter": 3057, + "ides": 3058, + "Ġac": 3059, + "èģļ": 3060, + "Java": 3061, + "Ġstep": 3062, + "illion": 3063, + "认为": 3064, + "éĿ¢å¯¹": 3065, + "ert": 3066, + "Ġdist": 3067, + "ew": 3068, + "ric": 3069, + "ons": 3070, + "梦": 3071, + "æĤ£": 3072, + "å®Įåħ¨": 3073, + "èĮ¶": 3074, + "cd": 3075, + "åij½ä»¤": 3076, + "éħįç½®": 3077, + "fter": 3078, + "è·Ŀ": 3079, + "Ġpol": 3080, + "Ġlearning": 3081, + "æĺ¯æĮĩ": 3082, + "Ġcalculate": 3083, + "âĢĶ": 3084, + "课ç¨ĭ": 3085, + "Ġ}": 3086, + "顾": 3087, + "Ġcorrect": 3088, + "ä¸Ģèµ·": 3089, + "Ġla": 3090, + "ĠçļĦ": 3091, + "ä¿®æĶ¹": 3092, + "word": 3093, + "Ġread": 3094, + "14": 3095, + "äºĴåĬ¨": 3096, + "Ġcent": 3097, + "^{": 3098, + "ustom": 3099, + "arth": 3100, + "Ġwell": 3101, + "æĺ¥": 3102, + "ä¾ĭåŃIJ": 3103, + "åĮĹ": 3104, + "Ġelse": 3105, + "åijĬè¯īæĪij": 3106, + "éĩĩç͍": 3107, + "ا": 3108, + "对è¯Ŀ": 3109, + "åı¯ä»¥åľ¨": 3110, + "çļĦæĸ¹æ³ķ": 3111, + "åı¯ä»¥å¸®åĬ©": 3112, + "ough": 3113, + "çѾ": 3114, + "itive": 3115, + "æ¸IJ": 3116, + "é»ij": 3117, + "èĢħçļĦ": 3118, + "çĮ®": 3119, + "SS": 3120, + "åıįåºĶ": 3121, + "æĺ¾ç¤º": 3122, + "讨论": 3123, + "Ġsec": 3124, + "Ġlooking": 3125, + "çļĦå·¥ä½ľ": 3126, + "ĠAd": 3127, + "\\(": 3128, + "ah": 3129, + "åĬłå¯Ĩ": 3130, + "Ġhuman": 3131, + "æ¡Ĩ": 3132, + "éĤ®": 3133, + "æĪijå°Ĩ": 3134, + "ä½ĵçݰ": 3135, + "Ġeng": 3136, + "éĶĢåĶ®": 3137, + "line": 3138, + "\\).": 3139, + "çĶŁæĢģ": 3140, + "åIJĮæĹ¶ä¹Ł": 3141, + "éĴĪ": 3142, + "åºľ": 3143, + "äºĨä¸Ģ": 3144, + "åŁ¹è®Ń": 3145, + "æĸĩæ¡£": 3146, + "伤": 3147, + "éģį": 3148, + "\":": 3149, + "Ġsun": 3150, + "iss": 3151, + "æĭ¥æľī": 3152, + "åºĶ对": 3153, + "åı¯æĮģç»Ń": 3154, + "çļĦæľĢ": 3155, + "Ġposs": 3156, + "çļĦæĬĢæľ¯": 3157, + "çĶŁåij½": 3158, + "ik": 3159, + "åĬŀ": 3160, + "Ġrun": 3161, + "æµģç¨ĭ": 3162, + "Ġpopulation": 3163, + "ases": 3164, + "æĹ¥å¸¸": 3165, + "å¹¶ä¸į": 3166, + "Ġpersonal": 3167, + "Ġaccurate": 3168, + "ploy": 3169, + "Ġbre": 3170, + "Ġgu": 3171, + "éħĴ": 3172, + "丽": 3173, + "èĪª": 3174, + "Ġsimple": 3175, + "éļıæľº": 3176, + "æIJľç´¢": 3177, + "åı¦": 3178, + "åį³ä½¿": 3179, + "æĹ©": 3180, + "SQL": 3181, + "gle": 3182, + "åı¥åŃIJ": 3183, + "comm": 3184, + "éĩĩåıĸ": 3185, + "ethod": 3186, + "éķ¿åº¦": 3187, + "è¿ĻæľīåĬ©äºİ": 3188, + "PI": 3189, + "Ġchange": 3190, + "Ġshe": 3191, + "ãģĻ": 3192, + "ren": 3193, + "ç»§ç»Ń": 3194, + "æĦŁåΰ": 3195, + "æł¡": 3196, + "Ġ16": 3197, + "çł´": 3198, + "设å®ļ": 3199, + "wn": 3200, + "hether": 3201, + "Ġmess": 3202, + "Ġ30": 3203, + "è´¢åĬ¡": 3204, + "Ġ==": 3205, + "é¢ľèī²": 3206, + "æĹ¨": 3207, + "è¾¾åΰ": 3208, + "alth": 3209, + "çĸ¾çĹħ": 3210, + "ï¼Į`": 3211, + "vert": 3212, + "Ġë": 3213, + "åĬª": 3214, + "代表": 3215, + "itions": 3216, + "Ġtype": 3217, + "éĢıæĺİ": 3218, + "ome": 3219, + "ĠWhether": 3220, + "æĦ¿": 3221, + "æĢ¥": 3222, + "\")": 3223, + "æĥħåĨµä¸ĭ": 3224, + "ef": 3225, + "æ¸ħæĻ": 3226, + "ĠWhat": 3227, + "平衡": 3228, + "Ġmulti": 3229, + "lob": 3230, + "åĪĨå¸ĥ": 3231, + "é²ľ": 3232, + "Ġaverage": 3233, + "iron": 3234, + "Ġcolumn": 3235, + "æİ¥åıĹ": 3236, + "SE": 3237, + "Ġest": 3238, + "æĹħè¡Į": 3239, + "Ġdesc": 3240, + "Ġpass": 3241, + "éĻIJåζ": 3242, + "Ġz": 3243, + "æĺ¾èijĹ": 3244, + "æ·±åħ¥": 3245, + "å°Ĭéĩį": 3246, + "èĴ": 3247, + "åģļåĩº": 3248, + "gan": 3249, + "Ġhappy": 3250, + "åĬªåĬĽ": 3251, + "ç§»åĬ¨": 3252, + "Ġlength": 3253, + "ability": 3254, + "以ä¸ĭåĩłä¸ª": 3255, + "å¸ĮæľĽè¿ĻäºĽ": 3256, + "ument": 3257, + "ĊĠĠĠĠ": 3258, + "ç¨İ": 3259, + "ä½Ĩä¸į": 3260, + "éĢ»": 3261, + "ä½łåı¯ä»¥ä½¿ç͍": 3262, + "Ġ>": 3263, + "ined": 3264, + "绪": 3265, + "è°ĥç͍": 3266, + "ied": 3267, + "缩": 3268, + "大éĩı": 3269, + "Ġpoem": 3270, + "ures": 3271, + "ique": 3272, + "ven": 3273, + "追æ±Ĥ": 3274, + "akes": 3275, + "æĹ¨åľ¨": 3276, + "Ġpower": 3277, + "éĢ»è¾ij": 3278, + "resent": 3279, + "åı¯èĥ½çļĦ": 3280, + "è¿Ļåı¯èĥ½": 3281, + "Ġsequence": 3282, + "çļĦçĶŁæ´»": 3283, + "ĠZ": 3284, + "¢å¤į": 3285, + "Ġgood": 3286, + "ex": 3287, + "åºıåĪĹ": 3288, + "rans": 3289, + "ç³»åĪĹ": 3290, + "ä½³": 3291, + "äºļ": 3292, + "als": 3293, + "You": 3294, + "ä¾ĽåºĶ": 3295, + "åĬ¨çī©": 3296, + "Ġob": 3297, + "Ġhist": 3298, + "é»Ħ": 3299, + "Ġconvers": 3300, + "æĪij们åı¯ä»¥": 3301, + "é©¶": 3302, + "åī§": 3303, + "ug": 3304, + "符åIJĪ": 3305, + "Ġshort": 3306, + "Ġag": 3307, + "举": 3308, + "my": 3309, + "ware": 3310, + "Ġkey": 3311, + "å¤ļæł·": 3312, + "ç쵿´»": 3313, + "Ġwrite": 3314, + "Ġpur": 3315, + "22": 3316, + "æīĵåį°": 3317, + "ç¡®å®ŀ": 3318, + "Ġhand": 3319, + "çݩ家": 3320, + "éģĹ": 3321, + "à¹": 3322, + "çļĦæ·": 3323, + "ç§ĺ": 3324, + "els": 3325, + "Ġprim": 3326, + "Ġlife": 3327, + "çļĦç¼": 3328, + "çĽijæİ§": 3329, + "社交åªĴä½ĵ": 3330, + "Ġ25": 3331, + "åħ¨éĿ¢": 3332, + "èĵ": 3333, + "Ġgrow": 3334, + "å®ŀéªĮ": 3335, + "aut": 3336, + "æķı": 3337, + "Ġexpl": 3338, + "çĵ": 3339, + "åºķ": 3340, + "çĤ¹åĩ»": 3341, + "çݰå®ŀ": 3342, + "ç®ĢåįķçļĦ": 3343, + "ç»ıéªĮ": 3344, + "è·¨": 3345, + "èĹı": 3346, + "éĩįå¤į": 3347, + "æĶ¿åºľ": 3348, + "Ġsoft": 3349, + "æijĦ": 3350, + "éŃ": 3351, + "çľ¼": 3352, + "é£Łçī©": 3353, + "Ġwords": 3354, + "伦": 3355, + "ĠSQL": 3356, + "åIJİçļĦ": 3357, + "Ġyears": 3358, + "亮": 3359, + "æĥ¯": 3360, + "æ¤įçī©": 3361, + "ä¼ij": 3362, + "æ¸ħæĻ°": 3363, + "Ġaround": 3364, + "称为": 3365, + "ä¸ĩ": 3366, + "éļıæĹ¶": 3367, + "å¿Ĩ": 3368, + "åĬ¨æĢģ": 3369, + "'ve": 3370, + "ĊĊĠĠĠ": 3371, + "Ġapplic": 3372, + "以ä¸Ĭ": 3373, + "Ġwebsite": 3374, + "æīĭæľº": 3375, + "Yes": 3376, + "viron": 3377, + "Ġmethod": 3378, + "Ġcompany": 3379, + "RO": 3380, + "åİŁåĽł": 3381, + "ä¹Łåı¯ä»¥": 3382, + "sqrt": 3383, + "è§Ħ模": 3384, + "ps": 3385, + "Ġwrit": 3386, + "äºij计ç®Ĺ": 3387, + "Ġoutput": 3388, + "Ġdat": 3389, + "Ġgive": 3390, + "ets": 3391, + "Ġimpro": 3392, + "\\),": 3393, + "å¼ĢæĶ¾": 3394, + "æ³ķè§Ħ": 3395, + "æĻ®": 3396, + "æĮīçħ§": 3397, + "18": 3398, + "éĹ»": 3399, + "æģ¢å¤į": 3400, + "Ġtem": 3401, + "è°¢": 3402, + "æľĢç»Ī": 3403, + "Ġ//": 3404, + "Here": 3405, + "æĶ¹è¿Ľ": 3406, + "Ġinf": 3407, + "cdot": 3408, + "æĹ¥æľŁ": 3409, + "ĠJava": 3410, + "å®ĩ": 3411, + "For": 3412, + "Ġjo": 3413, + "æĦıè¯Ĩ": 3414, + "以确ä¿Ŀ": 3415, + "ien": 3416, + "æĽ´å¤ļçļĦ": 3417, + "æļĸ": 3418, + "ream": 3419, + "++": 3420, + "AR": 3421, + "21": 3422, + "éĻIJäºİ": 3423, + "æĬĢå·§": 3424, + "åĨ°": 3425, + "å¹³åĿĩ": 3426, + "çļĦè¿ĩç¨ĭ": 3427, + "ä¸įè¦ģ": 3428, + "Ġreferring": 3429, + "ç¦ı": 3430, + "æĻļ": 3431, + "å°ıæĺİ": 3432, + "太éĺ³": 3433, + "ä¸įåı¯": 3434, + "åĮ»çĶŁ": 3435, + "Ġsmall": 3436, + "æ¯Ĵ": 3437, + "ç©¿": 3438, + "Ġmeans": 3439, + "ML": 3440, + "ze": 3441, + "享åıĹ": 3442, + "çīĽ": 3443, + "çļĦæ¦Ĥ": 3444, + "ines": 3445, + "Ġ使ç͍": 3446, + "How": 3447, + "人åijĺ": 3448, + "åĿļ": 3449, + "ĠTo": 3450, + "èĦļ": 3451, + "Ġglob": 3452, + "æ£Ģæµĭ": 3453, + "èµĸ": 3454, + "ode": 3455, + "强è°ĥ": 3456, + "模åŀĭçļĦ": 3457, + "('": 3458, + "vie": 3459, + "ç£": 3460, + "Ġbr": 3461, + "Ġsumm": 3462, + "Ġchat": 3463, + "é±": 3464, + "ä¹łæĥ¯": 3465, + "ellig": 3466, + "亲": 3467, + "Ġparticular": 3468, + "çĹĩ": 3469, + "Ġins": 3470, + "æķ´ä¸ª": 3471, + "çļĦåŃ¦ä¹ł": 3472, + "çļĦæĦı": 3473, + "Ġmark": 3474, + "è¿Ļä¸įä»ħ": 3475, + "æĬ¥åijĬ": 3476, + "ploye": 3477, + "éħ¸": 3478, + "çļĦéľĢæ±Ĥ": 3479, + "etwor": 3480, + "驾": 3481, + "Ch": 3482, + "çļĦæı": 3483, + "Ġåľ¨": 3484, + "è¸": 3485, + "ety": 3486, + "绿èī²": 3487, + "Ġexpression": 3488, + "read": 3489, + "Ġcharacters": 3490, + "èµ°": 3491, + "Ġstory": 3492, + "åĵįåºĶ": 3493, + "è¡ĮåĬ¨": 3494, + ")^": 3495, + "ĠPro": 3496, + "Ġ18": 3497, + "Ġbetter": 3498, + "åįģ": 3499, + "Ġdevelopment": 3500, + "以ä¸ĭæĺ¯ä¸Ģ个": 3501, + "æĿŁ": 3502, + "èIJ¥éĶĢ": 3503, + "æľīæķĪåľ°": 3504, + "é±¼": 3505, + "äºĴèģĶç½ij": 3506, + "æĭħ": 3507, + "深度åŃ¦ä¹ł": 3508, + "Ġprovided": 3509, + "éĺ²æŃ¢": 3510, + "Ġ11": 3511, + "æĸĩ竳": 3512, + "çĬ¶åĨµ": 3513, + "å¢ŀéķ¿": 3514, + "Ġorder": 3515, + "ç»ĦæĪIJ": 3516, + "éĺµ": 3517, + "Ġmed": 3518, + "鼨": 3519, + "éĢī项": 3520, + "Ġenviron": 3521, + "oh": 3522, + "建设": 3523, + "Ġadvice": 3524, + "Ġend": 3525, + "íķ": 3526, + "Ġfam": 3527, + "Ġfin": 3528, + "Ġusers": 3529, + "ç»ĻæĪij": 3530, + "verse": 3531, + "Ġelements": 3532, + "ĠEx": 3533, + "----": 3534, + "éĿł": 3535, + "请æ±Ĥ": 3536, + "rase": 3537, + "æ±½": 3538, + "æĢ»ä¹ĭ": 3539, + "å®ŀæĹ¶": 3540, + "ives": 3541, + "åħģ": 3542, + "æıIJåĩº": 3543, + "çŃij": 3544, + "Ġconst": 3545, + "ius": 3546, + "å¯Ĩçłģ": 3547, + "æ´Ĺ": 3548, + "Ġconf": 3549, + "Ġshare": 3550, + "Therefore": 3551, + "24": 3552, + "森": 3553, + "commend": 3554, + "pi": 3555, + "ä¾Ŀèµĸ": 3556, + "çĩ": 3557, + "缸åIJĮ": 3558, + "pen": 3559, + "å®Ļ": 3560, + "çīĪæľ¬": 3561, + "Ġarr": 3562, + "åħ¬åħ±": 3563, + "è§Ī": 3564, + "åĬĽéĩı": 3565, + "Ġident": 3566, + "çĨŁ": 3567, + "æ¶²": 3568, + "åij¼": 3569, + "æł¼å¼ı": 3570, + "inu": 3571, + "çļĦåħ·ä½ĵ": 3572, + "åIJĪéĢĤçļĦ": 3573, + "çİĭ": 3574, + "大æķ°æį®": 3575, + "Ġvis": 3576, + "åľĨ": 3577, + "æĢĿèĢĥ": 3578, + "æĽ²": 3579, + "ç§ĭ": 3580, + "Ġtypically": 3581, + "åħģ许": 3582, + "çĮ«": 3583, + "Ġcomplex": 3584, + "æħ§": 3585, + "Ġmaking": 3586, + "ional": 3587, + "ward": 3588, + "Ġsecon": 3589, + "Ġrecommend": 3590, + "å®ĩå®Ļ": 3591, + "//": 3592, + "æŃ£åľ¨": 3593, + "gin": 3594, + "åĪĨéħį": 3595, + "è·Ŀ离": 3596, + "ä¸įèĥ½": 3597, + "Ġcommun": 3598, + "åĿĢ": 3599, + "å¾Ģå¾Ģ": 3600, + ":**": 3601, + "ä¼´": 3602, + "ries": 3603, + "io": 3604, + "éĽĨåĽ¢": 3605, + "ä¸Ńæĸĩ": 3606, + "ember": 3607, + "Ġorig": 3608, + "å¼Ĥ常": 3609, + "ways": 3610, + "åŁ¹åħ»": 3611, + "Ġinclude": 3612, + "èľ": 3613, + "Ġsug": 3614, + "åĭĩ": 3615, + "Ġtrying": 3616, + "Ġmessage": 3617, + "ror": 3618, + "ä½Ļ": 3619, + "Ġsym": 3620, + "æ··": 3621, + "åģľ": 3622, + "çī©è´¨": 3623, + "Ġmach": 3624, + "ergy": 3625, + "ç²ī": 3626, + "Ġdid": 3627, + "çļĦæķħäºĭ": 3628, + "Ġevery": 3629, + "åĽ¢éĺŁæĪIJåijĺ": 3630, + "ãģĹ": 3631, + "å±ķçݰ": 3632, + "éĢIJæ¸IJ": 3633, + "Ġsing": 3634, + "ious": 3635, + "çļĦç¬": 3636, + "Ġallow": 3637, + "Ġtell": 3638, + "ĠUnited": 3639, + "礼": 3640, + "Ġsame": 3641, + "ç®±": 3642, + "ipp": 3643, + "建çŃij": 3644, + "èĢIJ": 3645, + "çļĦåħ³éĶ®": 3646, + "æĦıä¹ī": 3647, + "çľĭåΰ": 3648, + "èĩªçĦ¶è¯Ńè¨Ģ": 3649, + "Ġcustom": 3650, + "Ġprime": 3651, + "atform": 3652, + "ential": 3653, + "å¦Ĥæŀľä½łæľī": 3654, + "Ġservice": 3655, + "urity": 3656, + "ilities": 3657, + "Ġsee": 3658, + "èŃ": 3659, + "天æ°Ķ": 3660, + "éĥ½èĥ½": 3661, + "Ġbec": 3662, + "é¥®é£Ł": 3663, + "æ¶Īè´¹èĢħ": 3664, + "erv": 3665, + "Ġfeature": 3666, + "ä½łèĥ½": 3667, + "Ġupd": 3668, + "çļĦ主è¦ģ": 3669, + "å®ŀæĸ½": 3670, + "ä»į": 3671, + "Ġmay": 3672, + "çŁ¥éģĵ": 3673, + "è§£åĨ³éĹ®é¢ĺ": 3674, + "ale": 3675, + "Ġterms": 3676, + "å®ĺæĸ¹": 3677, + "éĢĶ": 3678, + "çľł": 3679, + "æĹı": 3680, + "éľ²": 3681, + "å¥ĸ": 3682, + "Ġcontain": 3683, + "ç͍çļĦ": 3684, + "åĪĨéĴŁ": 3685, + "ä¸Ģå®ļ": 3686, + "Ġfl": 3687, + "温度": 3688, + "ai": 3689, + "Ġright": 3690, + "¹ģ": 3691, + "æĶ»": 3692, + "ãĤĴ": 3693, + "åĪĽæĦı": 3694, + "è¿Ļå°Ĩ": 3695, + "åı¯ä»¥æıIJä¾Ľ": 3696, + "æľºåĻ¨äºº": 3697, + "Ġwid": 3698, + "å¦Ĥä¸ĭ": 3699, + "Ġstud": 3700, + "ĠSh": 3701, + "åΤæĸŃ": 3702, + "æīįèĥ½": 3703, + "鼶": 3704, + "驾驶": 3705, + "Ġcat": 3706, + "ç¥ŀç»ı": 3707, + "ä»Ĭ": 3708, + "air": 3709, + "çļĦæ·±": 3710, + "åķĨä¸ļ": 3711, + "\".": 3712, + "$,": 3713, + "æĪijä¼ļ": 3714, + "ç±į": 3715, + "Ġsky": 3716, + "以便": 3717, + "è¡¥": 3718, + "èª": 3719, + "çIJĨè§£åĴĮ": 3720, + "çĻ»": 3721, + "å¤įæĿĤçļĦ": 3722, + "æī©å±ķ": 3723, + "Ġav": 3724, + "æĪijçļĦ": 3725, + "å½ĵçĦ¶åı¯ä»¥": 3726, + "å·®å¼Ĥ": 3727, + "Ġstor": 3728, + "æ±ĩ": 3729, + "áº": 3730, + "è¾ħ": 3731, + "åĽ°éļ¾": 3732, + "Ġfile": 3733, + "è¶³å¤Ł": 3734, + "Ġcle": 3735, + "ivid": 3736, + "å°±æĺ¯": 3737, + "yn": 3738, + "ç»ĥä¹ł": 3739, + "åĨ³äºİ": 3740, + "åıĸåĨ³äºİ": 3741, + "æĦŁåħ´è¶£": 3742, + "åįĬ": 3743, + "æĽ¿": 3744, + "ä¿¡åı·": 3745, + "ç»ıåİĨ": 3746, + "isp": 3747, + "èī¾": 3748, + "13": 3749, + "åĮĸçļĦ": 3750, + "Ġcar": 3751, + "Of": 3752, + "æ°§": 3753, + "å·¥ç¨ĭ": 3754, + "ä¸įè¿ĩ": 3755, + "Ġcost": 3756, + "count": 3757, + "æħ¢": 3758, + "Ġcour": 3759, + "åĮºåŁŁ": 3760, + "çĽ¸å¯¹": 3761, + "Ġbusinesses": 3762, + "éĤ®ä»¶": 3763, + "AT": 3764, + "°": 3765, + "Ġroot": 3766, + "rix": 3767, + "Ġuser": 3768, + "é¾Ħ": 3769, + "atic": 3770, + "Ġ50": 3771, + "ç³»ç»ŁçļĦ": 3772, + "æŁ¥çľĭ": 3773, + "ä¸ī个": 3774, + "Ġanal": 3775, + "说æĺİ": 3776, + "åĵĪ": 3777, + "èĢĹ": 3778, + "çļĦ模": 3779, + "ithm": 3780, + "99": 3781, + "Ġens": 3782, + "Ġatt": 3783, + "Ġelement": 3784, + "Ġthree": 3785, + "çļĦç¾": 3786, + "Ġcult": 3787, + "æŁIJ个": 3788, + "Ġproviding": 3789, + "æ¯Ķè¾ĥ": 3790, + "Ġintern": 3791, + "ä¼łè¾ĵ": 3792, + "常è§ģçļĦ": 3793, + "ively": 3794, + "Ġ\\(\\": 3795, + "è§Ĩè§ī": 3796, + "utions": 3797, + "æĢģ度": 3798, + "Ġproject": 3799, + "ull": 3800, + "èĩªçͱ": 3801, + "纸": 3802, + "éĺ¶æ®µ": 3803, + "åıįæĺł": 3804, + "èįī": 3805, + "Ġmu": 3806, + "sh": 3807, + "纪": 3808, + "Th": 3809, + "Ġsuggest": 3810, + "itle": 3811, + "ç½²": 3812, + "éĢļè¿ĩä¸Ĭè¿°": 3813, + "åħ±äº«": 3814, + "æĿ¥æºIJ": 3815, + "ä¼ĺåĬ¿": 3816, + "åİŁçIJĨ": 3817, + "åŃ¦ä¹łåĴĮ": 3818, + "å¤ı": 3819, + "ãģ¾": 3820, + "elligence": 3821, + "çĭ¬çī¹çļĦ": 3822, + "ëĭ": 3823, + "å̾": 3824, + "ivers": 3825, + "åĨħåŃĺ": 3826, + "容æĺĵ": 3827, + "Ġ14": 3828, + "Ġenergy": 3829, + "å¨ģ": 3830, + "Ġprom": 3831, + "Ġidea": 3832, + "å®īåħ¨æĢ§": 3833, + "ructure": 3834, + "Ġsides": 3835, + "çģ¯": 3836, + "],": 3837, + "result": 3838, + "Ġmil": 3839, + "æĮĩçļĦæĺ¯": 3840, + "åĮħæĭ¬ä½Ĩä¸į": 3841, + "åĮħæĭ¬ä½Ĩä¸įéĻIJäºİ": 3842, + "ves": 3843, + "å®ŀä¾ĭ": 3844, + "Ġtrue": 3845, + "ä»ĸçļĦ": 3846, + "ä½ĵçļĦ": 3847, + "Ġfiel": 3848, + "ense": 3849, + "ote": 3850, + "`ï¼Į": 3851, + "èĨ": 3852, + "Ġwere": 3853, + "åĽŀçŃĶ": 3854, + "ç¬Ķ": 3855, + "ont": 3856, + "ĠTh": 3857, + "ç»ĦåIJĪ": 3858, + "ht": 3859, + "饰": 3860, + "other": 3861, + "ÙĦ": 3862, + "æĸĩåѦ": 3863, + "ximately": 3864, + "è´¡": 3865, + "æ¯ı个人": 3866, + "Ġimportant": 3867, + "Ġmillion": 3868, + "ç»ĺ": 3869, + "è·¯å¾Ħ": 3870, + "oogle": 3871, + "Ġprogramming": 3872, + "60": 3873, + "sum": 3874, + "ä¸Ģ段": 3875, + "æľīä¸Ģ个": 3876, + "åħ¬å¼ı": 3877, + "æĹłè®ºæĺ¯": 3878, + "订": 3879, + "æĬĹ": 3880, + "è§Ĵ度": 3881, + "Ġenvironment": 3882, + "ID": 3883, + "Ģæľī": 3884, + "åĨĮ": 3885, + "Ġremain": 3886, + "Ġplatform": 3887, + "son": 3888, + "æľĢ大": 3889, + "Ġopt": 3890, + "çļĦåŁºæľ¬": 3891, + "èĩ³åħ³": 3892, + "èį¯çī©": 3893, + "arm": 3894, + "æıĴ": 3895, + "ibr": 3896, + "è´¡çĮ®": 3897, + "Ġafter": 3898, + "ohn": 3899, + "æ±ł": 3900, + "Ġback": 3901, + "åįļ": 3902, + "Ġseries": 3903, + "time": 3904, + "ĠSp": 3905, + "ering": 3906, + "Ġnetwor": 3907, + "uss": 3908, + "Ġdescrib": 3909, + "é«ĺçļĦ": 3910, + "ĠYou": 3911, + "alse": 3912, + "ä»ĸ们çļĦ": 3913, + "åĮ¹": 3914, + "ty": 3915, + "æ¥": 3916, + "è¯ļ": 3917, + "æĿij": 3918, + "æľ¨": 3919, + "çļĦæ°´": 3920, + "æķĪçİĩåĴĮ": 3921, + "å»¶": 3922, + "aves": 3923, + "perature": 3924, + "æĪijæĥ³": 3925, + "ything": 3926, + "ä¹İ": 3927, + "æĶ¹åĸĦ": 3928, + "éĵ¶è¡Į": 3929, + "ï¼ļâĢľ": 3930, + "磩": 3931, + "åıĺå¾Ĺ": 3932, + "宽": 3933, + "åı¯ä»¥å¸®åĬ©ä½ł": 3934, + "éģįåİĨ": 3935, + "èĢĮä¸įæĺ¯": 3936, + "Ġsn": 3937, + "ank": 3938, + "æ´ŀ": 3939, + "è¿Ļæĺ¯ä¸Ģ个": 3940, + "¡çľł": 3941, + "Ġè¾ĵåĩº": 3942, + "____": 3943, + "京": 3944, + "Do": 3945, + "Ġsur": 3946, + "Ġalways": 3947, + "è±Ĩ": 3948, + "Ġwater": 3949, + "stit": 3950, + "éĿĴ": 3951, + "Ġlim": 3952, + "Ġhig": 3953, + "`,": 3954, + "ma": 3955, + "Ġpossible": 3956, + "Ġcir": 3957, + "Ġstatement": 3958, + "Ġmachine": 3959, + "Ġinteger": 3960, + "èĵĿ": 3961, + "ĠAs": 3962, + "Ġhour": 3963, + "丰å¯ĮçļĦ": 3964, + "Ġinterested": 3965, + "±ä¹IJ": 3966, + "泡": 3967, + "åįĥ": 3968, + "综åIJĪ": 3969, + "ãĢĤâĢĿ": 3970, + "åįķè¯į": 3971, + "Ġcity": 3972, + "let": 3973, + "åĨĽ": 3974, + "Ġinde": 3975, + "Ġdeterm": 3976, + "寻æ±Ĥ": 3977, + "è¿Ľåħ¥": 3978, + "Ġbeaut": 3979, + "Ġair": 3980, + "л": 3981, + "Ġdi": 3982, + "Ġhealth": 3983, + "é¡»": 3984, + "ness": 3985, + "碳": 3986, + "å°ģ": 3987, + "çļĦæ´": 3988, + "`.": 3989, + "æĢİ": 3990, + "ä½įäºİ": 3991, + "汽车": 3992, + "çݯå¢ĥä¸Ń": 3993, + "åĬĽåĴĮ": 3994, + "å½ĵåľ°": 3995, + "Ġobject": 3996, + "atural": 3997, + "å®Įæķ´": 3998, + "ĠFr": 3999, + "çͳ": 4000, + "Ġvary": 4001, + "Ġcontent": 4002, + "Ġbit": 4003, + "Ġour": 4004, + "çļĦçĥ": 4005, + "Ġunique": 4006, + "Ġwithout": 4007, + "åĴĮè°": 4008, + "è¯ŃéŁ³": 4009, + "åĪł": 4010, + "伦çIJĨ": 4011, + "ource": 4012, + "åĿı": 4013, + "ave": 4014, + "çļĦæĸ¹å¼ı": 4015, + "Ġbeing": 4016, + "Ġcase": 4017, + "æ¯ıä¸Ģ": 4018, + "Ġsal": 4019, + "èĤ¤": 4020, + "Ġbel": 4021, + "èĪĴ": 4022, + "Ġsecurity": 4023, + "ç¹ģ": 4024, + "ually": 4025, + "éĩįæĸ°": 4026, + "çŃīé¢ĨåŁŁ": 4027, + "é¢ĺ缮": 4028, + "çļĦæ¯ı": 4029, + "çļĦ使ç͍": 4030, + "éĿ©": 4031, + "ĠSe": 4032, + "ãģ«": 4033, + "Ġscript": 4034, + "éĤ£äºĽ": 4035, + "Ġmon": 4036, + "æ®Ĭ": 4037, + "身份": 4038, + "Ġap": 4039, + "ĠRe": 4040, + "':": 4041, + "ç͵èĦij": 4042, + "è§Ĥå¯Ł": 4043, + "è¿ĻæĦıåij³çĿĢ": 4044, + "æ¡Ĩæŀ¶": 4045, + "çļĦç¼ĸ": 4046, + "Ġincludes": 4047, + "åı³": 4048, + "Ġlog": 4049, + "à¤": 4050, + "çļĦç¾İ": 4051, + "Ġer": 4052, + "Ġimp": 4053, + "Ġ13": 4054, + "çľģ": 4055, + "ner": 4056, + "书ç±į": 4057, + "ç®Ĭ": 4058, + "åıĤåĬł": 4059, + "Ġdate": 4060, + "åıijå¸ĥ": 4061, + "çļĦæīĢæľī": 4062, + "è¯Ĭ": 4063, + "Ġpublic": 4064, + "éĥ¨ç½²": 4065, + "ross": 4066, + "ãģĦ": 4067, + "åĪłéϤ": 4068, + "Ġ%": 4069, + "ization": 4070, + "ä¼łæĴŃ": 4071, + "Ġmaxim": 4072, + "reate": 4073, + "box": 4074, + "çij": 4075, + "Ġdown": 4076, + "èѦ": 4077, + "Ġcourse": 4078, + "sole": 4079, + "ĠåĴĮ": 4080, + "座": 4081, + "以ä¸ĭåĩł": 4082, + "ĠMy": 4083, + "ĠQ": 4084, + "ï¼īï¼ļ": 4085, + "éĢļä¿¡": 4086, + "Re": 4087, + "è½®": 4088, + "ä¸Ĭä¸ĭ": 4089, + "ç¥Ŀ": 4090, + "::": 4091, + "gor": 4092, + "èĸ": 4093, + "å¹´é¾Ħ": 4094, + "Ġinvol": 4095, + "Ġtemperature": 4096, + "ä¿¡ä»»": 4097, + "æıIJéĹ®": 4098, + "Ġprof": 4099, + "ier": 4100, + "è®°å¿Ĩ": 4101, + "森æŀĹ": 4102, + "ä¸įä»ħèĥ½å¤Ł": 4103, + "æĤ£èĢħ": 4104, + "ger": 4105, + "ç»Ļå®ļ": 4106, + "çļĦå°ı": 4107, + "è¯Ńåı¥": 4108, + "ä½łéľĢè¦ģ": 4109, + "éģµå®Ī": 4110, + "Ġrole": 4111, + "åŃĹåħ¸": 4112, + "Ġreleased": 4113, + "Ex": 4114, + "bin": 4115, + "ECT": 4116, + "oy": 4117, + "æ¿Ģåıij": 4118, + "çĶļ": 4119, + "Ġconvert": 4120, + "Ġcommand": 4121, + "åĮ¹éħį": 4122, + "ployees": 4123, + "çļĦçŁ¥": 4124, + "éº": 4125, + "é¦Ĩ": 4126, + "å¡ij": 4127, + "Ġpositive": 4128, + "ON": 4129, + "Ġconcept": 4130, + "ĠEarth": 4131, + "Ġfriendly": 4132, + "åı¯éĿł": 4133, + "å°ıæĹ¶": 4134, + "åºĶç͍ç¨ĭåºı": 4135, + "è¿Ļ段": 4136, + "Ġindivid": 4137, + "ws": 4138, + "Ġnamed": 4139, + "Ġplan": 4140, + "Ġsort": 4141, + "Ġdoc": 4142, + "è§ĤçĤ¹": 4143, + "ãģ§": 4144, + "Ġiss": 4145, + "ä¸ĭçļĦ": 4146, + "设æĸ½": 4147, + "åĴĮè°IJ": 4148, + "空æ°Ķ": 4149, + "æij©": 4150, + "åĬ³": 4151, + "请æıIJä¾Ľ": 4152, + "Ġsources": 4153, + "ĠCom": 4154, + "å¤įæĿĤ度": 4155, + "apt": 4156, + "Ġsecond": 4157, + "æĶ»åĩ»": 4158, + "')": 4159, + "Ġown": 4160, + "Ġmeaning": 4161, + "éĢĢ": 4162, + "ä½Ľ": 4163, + "è¡£": 4164, + "cho": 4165, + "æľīä»Ģä¹Ī": 4166, + "æ²³": 4167, + "缸åºĶ": 4168, + "èĶ": 4169, + "çļĦå̼": 4170, + "Ġeffect": 4171, + "LECT": 4172, + "set": 4173, + "ogn": 4174, + "HT": 4175, + "Ġ```": 4176, + "åIJĥ": 4177, + "æĥħ绪": 4178, + "éķĩ": 4179, + "ocus": 4180, + "éĢĤå½ĵ": 4181, + "å¿ħé¡»": 4182, + "çĶļèĩ³": 4183, + "åĽ¾çīĩ": 4184, + "çļĦæĮĩ": 4185, + "çļĦåŁºç¡Ģ": 4186, + "Ġphys": 4187, + "ç»Ŀ": 4188, + "ality": 4189, + "æīĵå¼Ģ": 4190, + "Ġtre": 4191, + "Ġbecause": 4192, + "irt": 4193, + "çļĦæĥħåĨµä¸ĭ": 4194, + "Ġpat": 4195, + "Ġ200": 4196, + "åĮĸåѦ": 4197, + "åģı好": 4198, + "éĹŃ": 4199, + "åľ°åĿĢ": 4200, + "iter": 4201, + "aging": 4202, + "å¦Ĥä½ķåľ¨": 4203, + "Ġtake": 4204, + "åIJį为": 4205, + "åį´": 4206, + "åĪĨåĪ«": 4207, + "çļĦçľ": 4208, + "Ġplace": 4209, + "æľĢè¿ij": 4210, + "Ġ$\\": 4211, + "á": 4212, + "number": 4213, + "Ġmaximum": 4214, + "ç¯ĩ": 4215, + "rue": 4216, + "ĠInt": 4217, + "Ġ24": 4218, + "èĥŀ": 4219, + "case": 4220, + "Ġdeep": 4221, + "ROM": 4222, + "sc": 4223, + "Ġincre": 4224, + "ĠWe": 4225, + "éĥ½ä¼ļ": 4226, + "ç²¾ç¥ŀ": 4227, + "å©": 4228, + "åįıä½ľ": 4229, + "åı¯ä»¥æĺ¯": 4230, + "çŃīå¤ļ个": 4231, + "çĽijæµĭ": 4232, + "åĴĮæľįåĬ¡": 4233, + "Ġdue": 4234, + "Ġear": 4235, + "è¿IJèIJ¥": 4236, + "of": 4237, + "污": 4238, + "é«ĺ度": 4239, + "æī¹": 4240, + "çĸ«": 4241, + "Ġindividual": 4242, + "èĩªçĦ¶è¯Ńè¨Ģå¤ĦçIJĨ": 4243, + "ele": 4244, + "sive": 4245, + "ä¹Łèĥ½": 4246, + "æĻºæħ§": 4247, + "sort": 4248, + "af": 4249, + "åħ¬åı¸çļĦ": 4250, + "ç͍æĿ¥": 4251, + "ç̧": 4252, + "èļ": 4253, + "Ġexact": 4254, + "åŃĹæ¯į": 4255, + "place": 4256, + "å¾Īå¤ļ": 4257, + "æĽ´åĩĨç¡®": 4258, + "æľīæīĢ帮åĬ©": 4259, + "ä¹ĭåīį": 4260, + "Ġtakes": 4261, + "ral": 4262, + "å®ĥ们çļĦ": 4263, + "çİ°åľ¨": 4264, + "å¹ķ": 4265, + "Ġduring": 4266, + "ç¼ĸè¾ij": 4267, + "gre": 4268, + "仿": 4269, + "æľīä¸Ģ": 4270, + "ters": 4271, + "Ġsince": 4272, + "ention": 4273, + "æĬ½": 4274, + "页éĿ¢": 4275, + "æ½ľåľ¨": 4276, + "è¯Ńæ³ķ": 4277, + "Ġfocus": 4278, + "æĢĿç»´": 4279, + "Ġlocated": 4280, + "!\"": 4281, + "纳": 4282, + "缸åħ³çļĦ": 4283, + "é¡¶": 4284, + "Ġhelpful": 4285, + "Ġfactor": 4286, + "angle": 4287, + "缴åΰ": 4288, + "ĠStates": 4289, + "湿": 4290, + "æį·": 4291, + "éĵģ": 4292, + "è´Łè´£": 4293, + "iction": 4294, + "Ġnatural": 4295, + "Ġorgan": 4296, + "娱ä¹IJ": 4297, + "ches": 4298, + "Ġleft": 4299, + "Ġblue": 4300, + "ok": 4301, + "é»ĺ": 4302, + "Ġmade": 4303, + "Ġprovides": 4304, + "pose": 4305, + "çļĦçģ": 4306, + "éķľ": 4307, + "åĿIJ": 4308, + "æĿ¥å®ŀçݰ": 4309, + "indows": 4310, + "abilities": 4311, + "请注æĦı": 4312, + "Ġteam": 4313, + "éĢłæĪIJ": 4314, + "çļĦèĥ½åĬĽ": 4315, + "éĨĴ": 4316, + "ç͍æĪ·çļĦ": 4317, + "æĹĭ": 4318, + "race": 4319, + "Ġphrase": 4320, + "17": 4321, + "对æĸ¹": 4322, + "Ġage": 4323, + "ĠAnd": 4324, + "çļĦé£İéĻ©": 4325, + "Ġaut": 4326, + "åį«": 4327, + "ä¸Ģèĩ´": 4328, + "ç»Ĩèĥŀ": 4329, + "æĬĢæľ¯çļĦåıijå±ķ": 4330, + "çĶŁæĪIJä¸Ģ个": 4331, + "Ġdivis": 4332, + "å°¤åħ¶æĺ¯": 4333, + "çŃīæĸ¹éĿ¢": 4334, + "Ġasking": 4335, + "缮çļĦ": 4336, + "第äºĮ": 4337, + "èĪĴéĢĤ": 4338, + "æĬĺ": 4339, + "Ġrecent": 4340, + "åįıè®®": 4341, + "è¾Ĩ": 4342, + "å·¨": 4343, + "åīĤ": 4344, + "the": 4345, + "æķ°çļĦ": 4346, + "Ġheart": 4347, + "value": 4348, + "éĿ¢ä¸´": 4349, + "å®ŀéĻħä¸Ĭ": 4350, + "æĿ¯": 4351, + "dd": 4352, + "ĠX": 4353, + "è´µ": 4354, + "è¯ģæĺİ": 4355, + "å±ı": 4356, + "new": 4357, + "ody": 4358, + "éģµå¾ª": 4359, + "Ġglobal": 4360, + "Ġdepending": 4361, + "åĩĢ": 4362, + "å·¦": 4363, + "ĠØ": 4364, + "æĽ´åħ·ä½ĵçļĦ": 4365, + "帮åĬ©æĤ¨": 4366, + "缸äºĴ": 4367, + "Ġreturns": 4368, + "å¸ĮæľĽè¿ĻäºĽå»ºè®®": 4369, + "Ġimprove": 4370, + "Ġmuch": 4371, + "ä»ĸ人": 4372, + "å¼¹": 4373, + "45": 4374, + "åįķä½į": 4375, + "çĦ¦": 4376, + "åĪĹ表ä¸Ń": 4377, + "`ãĢĤ": 4378, + "conom": 4379, + "èĥ½éĩı": 4380, + "Ġcalled": 4381, + "Ġrate": 4382, + "é¢Ħç®Ĺ": 4383, + "èĩ³åħ³éĩįè¦ģ": 4384, + "acc": 4385, + "ç»ĵæĿŁ": 4386, + "åı¯ä»¥èĢĥèĻij": 4387, + "ç»ĨèĬĤ": 4388, + "Ġve": 4389, + "å¯¹ä½ł": 4390, + "ĠThese": 4391, + "è¿ĩ度": 4392, + "ified": 4393, + "è¯Ħä»·": 4394, + "æķ°æį®éĽĨ": 4395, + "é«ĺè´¨éĩı": 4396, + "èŀįåIJĪ": 4397, + "åİŁåĪĻ": 4398, + "Ġapproximately": 4399, + "Ġrepresent": 4400, + "ificial": 4401, + "ason": 4402, + "æĸ¹ç¨ĭ": 4403, + "Ġsocial": 4404, + "Ġfig": 4405, + "SELECT": 4406, + "####": 4407, + "人æ°ij": 4408, + "ãĢij": 4409, + "hest": 4410, + "ãĢIJ": 4411, + "Ġdigital": 4412, + "以ä¸ĭåĩłä¸ªæĸ¹éĿ¢": 4413, + "Ġclo": 4414, + "å°Ĩåħ¶": 4415, + "çļĦåĨħ容": 4416, + "æı¡": 4417, + "random": 4418, + "å·ŀ": 4419, + "è´Ńçī©": 4420, + "hou": 4421, + "éĥ¨éŨ": 4422, + "Ġsnipp": 4423, + "tract": 4424, + "çݰ象": 4425, + "Ġneeds": 4426, + "æķ°åĪĹ": 4427, + "Ġgame": 4428, + "Ġtri": 4429, + "è´´": 4430, + "çĭ¬ç«ĭ": 4431, + "å¿ħè¦ģçļĦ": 4432, + "èĪŀ": 4433, + "æ¸ħæ´ģ": 4434, + "象å¾ģ": 4435, + "è½»æĿ¾": 4436, + "鸡": 4437, + "åĨ²çªģ": 4438, + "Ġgeneral": 4439, + "Ġblo": 4440, + "çļĦæĦŁ": 4441, + "åĨħçļĦ": 4442, + "Ñĥ": 4443, + "ins": 4444, + "çļĦçŁ¥è¯Ĩ": 4445, + "模åĿĹ": 4446, + "éϤäºĨ": 4447, + "è¦Ĩ": 4448, + "yle": 4449, + "Ġintelligence": 4450, + "åĨĻä½ľ": 4451, + "Ġed": 4452, + "Ġrad": 4453, + "cing": 4454, + "Please": 4455, + "ved": 4456, + "èĥ½å¸®åĬ©ä½ł": 4457, + "é¢Ŀå¤ĸ": 4458, + "Ġmarket": 4459, + "Ġnight": 4460, + "input": 4461, + "æķ°æį®åĪĨæŀIJ": 4462, + "æ´²": 4463, + "me": 4464, + "wh": 4465, + "Ġeas": 4466, + "å¤ļå°ij": 4467, + "ãģ¯": 4468, + "ock": 4469, + "Create": 4470, + "Ġloop": 4471, + "When": 4472, + "çħ": 4473, + "gs": 4474, + "åı¯ä»¥æł¹æį®": 4475, + "Ġrecogn": 4476, + "æĸ°éĹ»": 4477, + "Ġ\\),": 4478, + "ä¼ijæģ¯": 4479, + "ĠMa": 4480, + "ä¸Ĭä¸ĭæĸĩ": 4481, + "Ġmem": 4482, + "åĬłå¼º": 4483, + "æ±Ł": 4484, + "éĺ¿éĩĮå·´å·´éĽĨåĽ¢": 4485, + "Ġsingle": 4486, + "æ´ĭ": 4487, + "PU": 4488, + "imate": 4489, + "çļĦæ²ŁéĢļ": 4490, + "åľ¨æŁIJäºĽ": 4491, + "浪": 4492, + "ä¸Ŀ": 4493, + "表éĿ¢": 4494, + "èī¯å¥½": 4495, + "æŀ¶æŀĦ": 4496, + "éĺ»": 4497, + "ãģª": 4498, + "åı¯æĮģç»Ńåıijå±ķ": 4499, + "Ġenh": 4500, + "sql": 4501, + "çŃīäºİ": 4502, + "Ġmultiple": 4503, + "plic": 4504, + "æĸĩåŃĹ": 4505, + "ä¹Łä¼ļ": 4506, + "éĩİ": 4507, + "ountain": 4508, + "200": 4509, + "ling": 4510, + "åζéĢł": 4511, + "âĢĿï¼Ī": 4512, + "Ġdisc": 4513, + "API": 4514, + "Ġ17": 4515, + "èĻ«": 4516, + "éļĶ": 4517, + "plify": 4518, + "Ġclar": 4519, + "åΰäºĨ": 4520, + "æ¹ĸ": 4521, + "Ġder": 4522, + "Ġrequest": 4523, + "æĹ¶åĢĻ": 4524, + "æľªæĿ¥çļĦ": 4525, + "ç½Ĺ": 4526, + "åħ¬å¹³": 4527, + "Ġchang": 4528, + "æĮĩ导": 4529, + "åı«": 4530, + "Ġless": 4531, + "æľīçĿĢ": 4532, + "çļĦæł¸": 4533, + "pper": 4534, + "ony": 4535, + "éĢĤç͍äºİ": 4536, + "ĠÃ": 4537, + "å®ļä½į": 4538, + "æľīè¶£": 4539, + "çļĦæŃ£": 4540, + "JavaScript": 4541, + "åĬ¨åĬĽ": 4542, + "und": 4543, + "ðŁ": 4544, + "å¼ĢåıijèĢħ": 4545, + "ç¾İåĽ½": 4546, + "èά": 4547, + "...": 4548, + "åĪĨ为": 4549, + "çļĦæĮijæĪĺ": 4550, + "Ġamount": 4551, + "Ġfilm": 4552, + "å°½éĩı": 4553, + "çļĦæ³ķ": 4554, + "Ġprimary": 4555, + "Ġpopular": 4556, + "ume": 4557, + "self": 4558, + "80": 4559, + "æ¯ĶèµĽ": 4560, + "åIJįåŃĹ": 4561, + "å¡«": 4562, + "èĩªåĬ¨åĮĸ": 4563, + "Ġes": 4564, + "磩éĺµ": 4565, + "rehen": 4566, + "łéģĵ": 4567, + "为ä»Ģä¹Ī": 4568, + "çıŃ": 4569, + "Ġcapabilities": 4570, + "Ġsnippet": 4571, + "è·Ł": 4572, + "" + ], + [ + "in", + "ed" + ], + [ + "ç»", + "ª" + ], + [ + "è°ĥ", + "ç͍" + ], + [ + "i", + "ed" + ], + [ + "ç¼", + "©" + ], + [ + "大", + "éĩı" + ], + [ + "Ġpo", + "em" + ], + [ + "ure", + "s" + ], + [ + "iqu", + "e" + ], + [ + "v", + "en" + ], + [ + "追", + "æ±Ĥ" + ], + [ + "ak", + "es" + ], + [ + "æĹ¨", + "åľ¨" + ], + [ + "Ġp", + "ower" + ], + [ + "éĢ»", + "è¾ij" + ], + [ + "res", + "ent" + ], + [ + "åı¯èĥ½", + "çļĦ" + ], + [ + "è¿Ļ", + "åı¯èĥ½" + ], + [ + "Ġse", + "quence" + ], + [ + "çļĦ", + "çĶŁæ´»" + ], + [ + "Ġ", + "Z" + ], + [ + "¢", + "å¤į" + ], + [ + "Ġg", + "ood" + ], + [ + "e", + "x" + ], + [ + "åºı", + "åĪĹ" + ], + [ + "r", + "ans" + ], + [ + "ç³»", + "åĪĹ" + ], + [ + "ä½", + "³" + ], + [ + "äº", + "ļ" + ], + [ + "al", + "s" + ], + [ + "Y", + "ou" + ], + [ + "ä¾Ľ", + "åºĶ" + ], + [ + "åĬ¨", + "çī©" + ], + [ + "Ġo", + "b" + ], + [ + "Ġh", + "ist" + ], + [ + "é»", + "Ħ" + ], + [ + "Ġcon", + "vers" + ], + [ + "æĪij们", + "åı¯ä»¥" + ], + [ + "é©", + "¶" + ], + [ + "åī", + "§" + ], + [ + "u", + "g" + ], + [ + "符", + "åIJĪ" + ], + [ + "Ġsh", + "ort" + ], + [ + "Ġa", + "g" + ], + [ + "ä¸", + "¾" + ], + [ + "m", + "y" + ], + [ + "w", + "are" + ], + [ + "Ġke", + "y" + ], + [ + "å¤ļ", + "æł·" + ], + [ + "çģµ", + "æ´»" + ], + [ + "Ġw", + "rite" + ], + [ + "Ġp", + "ur" + ], + [ + "2", + "2" + ], + [ + "æīĵ", + "åį°" + ], + [ + "ç¡®", + "å®ŀ" + ], + [ + "Ġh", + "and" + ], + [ + "çİ©", + "å®¶" + ], + [ + "éģ", + "Ĺ" + ], + [ + "à", + "¹" + ], + [ + "çļĦæ", + "·" + ], + [ + "ç§", + "ĺ" + ], + [ + "el", + "s" + ], + [ + "Ġpr", + "im" + ], + [ + "Ġl", + "ife" + ], + [ + "çļĦç", + "¼" + ], + [ + "çĽij", + "æİ§" + ], + [ + "社交", + "åªĴä½ĵ" + ], + [ + "Ġ2", + "5" + ], + [ + "åħ¨", + "éĿ¢" + ], + [ + "è", + "ĵ" + ], + [ + "Ġg", + "row" + ], + [ + "å®ŀ", + "éªĮ" + ], + [ + "a", + "ut" + ], + [ + "æķ", + "ı" + ], + [ + "Ġex", + "pl" + ], + [ + "ç", + "ĵ" + ], + [ + "åº", + "ķ" + ], + [ + "çĤ¹", + "åĩ»" + ], + [ + "çݰ", + "å®ŀ" + ], + [ + "ç®Ģåįķ", + "çļĦ" + ], + [ + "ç»ı", + "éªĮ" + ], + [ + "è·", + "¨" + ], + [ + "èĹ", + "ı" + ], + [ + "éĩį", + "å¤į" + ], + [ + "æĶ¿", + "åºľ" + ], + [ + "Ġso", + "ft" + ], + [ + "æij", + "Ħ" + ], + [ + "é", + "Ń" + ], + [ + "çľ", + "¼" + ], + [ + "é£Ł", + "çī©" + ], + [ + "Ġwor", + "ds" + ], + [ + "ä¼", + "¦" + ], + [ + "ĠS", + "QL" + ], + [ + "åIJİ", + "çļĦ" + ], + [ + "Ġyear", + "s" + ], + [ + "äº", + "®" + ], + [ + "æĥ", + "¯" + ], + [ + "æ¤į", + "çī©" + ], + [ + "ä¼", + "ij" + ], + [ + "æ¸ħæĻ", + "°" + ], + [ + "Ġa", + "round" + ], + [ + "ç§°", + "为" + ], + [ + "ä¸", + "ĩ" + ], + [ + "éļı", + "æĹ¶" + ], + [ + "å¿", + "Ĩ" + ], + [ + "åĬ¨", + "æĢģ" + ], + [ + "'", + "ve" + ], + [ + "Ċ", + "ĊĠĠĠ" + ], + [ + "Ġapp", + "lic" + ], + [ + "以", + "ä¸Ĭ" + ], + [ + "Ġwebs", + "ite" + ], + [ + "æīĭ", + "æľº" + ], + [ + "Y", + "es" + ], + [ + "v", + "iron" + ], + [ + "Ġm", + "ethod" + ], + [ + "Ġcompan", + "y" + ], + [ + "R", + "O" + ], + [ + "åİŁ", + "åĽł" + ], + [ + "ä¹Ł", + "åı¯ä»¥" + ], + [ + "sq", + "rt" + ], + [ + "è§Ħ", + "模" + ], + [ + "p", + "s" + ], + [ + "Ġw", + "rit" + ], + [ + "äºij", + "计ç®Ĺ" + ], + [ + "Ġout", + "put" + ], + [ + "Ġd", + "at" + ], + [ + "Ġg", + "ive" + ], + [ + "et", + "s" + ], + [ + "Ġim", + "pro" + ], + [ + "\\)", + "," + ], + [ + "å¼Ģ", + "æĶ¾" + ], + [ + "æ³ķ", + "è§Ħ" + ], + [ + "æĻ", + "®" + ], + [ + "æĮī", + "çħ§" + ], + [ + "1", + "8" + ], + [ + "éĹ", + "»" + ], + [ + "æģ", + "¢å¤į" + ], + [ + "Ġt", + "em" + ], + [ + "è°", + "¢" + ], + [ + "æľĢ", + "ç»Ī" + ], + [ + "Ġ/", + "/" + ], + [ + "H", + "ere" + ], + [ + "æĶ¹", + "è¿Ľ" + ], + [ + "Ġin", + "f" + ], + [ + "cd", + "ot" + ], + [ + "æĹ¥", + "æľŁ" + ], + [ + "ĠJ", + "ava" + ], + [ + "å®", + "ĩ" + ], + [ + "F", + "or" + ], + [ + "Ġj", + "o" + ], + [ + "æĦı", + "è¯Ĩ" + ], + [ + "以", + "ç¡®ä¿Ŀ" + ], + [ + "i", + "en" + ], + [ + "æĽ´å¤ļ", + "çļĦ" + ], + [ + "æļ", + "ĸ" + ], + [ + "re", + "am" + ], + [ + "+", + "+" + ], + [ + "A", + "R" + ], + [ + "2", + "1" + ], + [ + "éĻIJ", + "äºİ" + ], + [ + "æĬĢ", + "å·§" + ], + [ + "åĨ", + "°" + ], + [ + "å¹³", + "åĿĩ" + ], + [ + "çļĦ", + "è¿ĩç¨ĭ" + ], + [ + "ä¸į", + "è¦ģ" + ], + [ + "Ġrefer", + "ring" + ], + [ + "ç¦", + "ı" + ], + [ + "æĻ", + "ļ" + ], + [ + "å°ı", + "æĺİ" + ], + [ + "太", + "éĺ³" + ], + [ + "ä¸į", + "åı¯" + ], + [ + "åĮ»", + "çĶŁ" + ], + [ + "Ġsm", + "all" + ], + [ + "æ¯", + "Ĵ" + ], + [ + "ç©", + "¿" + ], + [ + "Ġme", + "ans" + ], + [ + "M", + "L" + ], + [ + "z", + "e" + ], + [ + "享", + "åıĹ" + ], + [ + "çī", + "Ľ" + ], + [ + "çļĦæ", + "¦Ĥ" + ], + [ + "in", + "es" + ], + [ + "Ġ", + "使ç͍" + ], + [ + "H", + "ow" + ], + [ + "人", + "åijĺ" + ], + [ + "åĿ", + "ļ" + ], + [ + "ĠT", + "o" + ], + [ + "èĦ", + "ļ" + ], + [ + "Ġg", + "lob" + ], + [ + "æ£Ģ", + "æµĭ" + ], + [ + "èµ", + "ĸ" + ], + [ + "o", + "de" + ], + [ + "强", + "è°ĥ" + ], + [ + "模åŀĭ", + "çļĦ" + ], + [ + "(", + "'" + ], + [ + "v", + "ie" + ], + [ + "ç", + "£" + ], + [ + "Ġb", + "r" + ], + [ + "Ġsum", + "m" + ], + [ + "Ġch", + "at" + ], + [ + "é", + "±" + ], + [ + "ä¹ł", + "æĥ¯" + ], + [ + "ell", + "ig" + ], + [ + "äº", + "²" + ], + [ + "Ġpartic", + "ular" + ], + [ + "çĹ", + "ĩ" + ], + [ + "Ġin", + "s" + ], + [ + "æķ´", + "个" + ], + [ + "çļĦ", + "åŃ¦ä¹ł" + ], + [ + "çļĦæĦ", + "ı" + ], + [ + "Ġm", + "ark" + ], + [ + "è¿Ļ", + "ä¸įä»ħ" + ], + [ + "æĬ¥", + "åijĬ" + ], + [ + "ploy", + "e" + ], + [ + "éħ", + "¸" + ], + [ + "çļĦ", + "éľĢæ±Ĥ" + ], + [ + "et", + "wor" + ], + [ + "é©", + "¾" + ], + [ + "C", + "h" + ], + [ + "çļĦæ", + "ı" + ], + [ + "Ġ", + "åľ¨" + ], + [ + "è", + "¸" + ], + [ + "et", + "y" + ], + [ + "绿", + "èī²" + ], + [ + "Ġexpress", + "ion" + ], + [ + "re", + "ad" + ], + [ + "Ġcharact", + "ers" + ], + [ + "èµ", + "°" + ], + [ + "Ġst", + "ory" + ], + [ + "åĵį", + "åºĶ" + ], + [ + "è¡Į", + "åĬ¨" + ], + [ + ")", + "^" + ], + [ + "ĠP", + "ro" + ], + [ + "Ġ1", + "8" + ], + [ + "Ġbet", + "ter" + ], + [ + "åį", + "ģ" + ], + [ + "Ġdevelop", + "ment" + ], + [ + "以ä¸ĭ", + "æĺ¯ä¸Ģ个" + ], + [ + "æĿ", + "Ł" + ], + [ + "èIJ¥", + "éĶĢ" + ], + [ + "æľīæķĪ", + "åľ°" + ], + [ + "é±", + "¼" + ], + [ + "äºĴ", + "èģĶç½ij" + ], + [ + "æĭ", + "ħ" + ], + [ + "深度", + "åŃ¦ä¹ł" + ], + [ + "Ġprovid", + "ed" + ], + [ + "éĺ²", + "æŃ¢" + ], + [ + "Ġ1", + "1" + ], + [ + "æĸĩ", + "竳" + ], + [ + "çĬ¶", + "åĨµ" + ], + [ + "å¢ŀ", + "éķ¿" + ], + [ + "Ġor", + "der" + ], + [ + "ç»Ħ", + "æĪIJ" + ], + [ + "éĺ", + "µ" + ], + [ + "Ġm", + "ed" + ], + [ + "éĽ", + "¨" + ], + [ + "éĢī", + "项" + ], + [ + "Ġen", + "viron" + ], + [ + "o", + "h" + ], + [ + "建", + "设" + ], + [ + "Ġadv", + "ice" + ], + [ + "Ġ", + "end" + ], + [ + "í", + "ķ" + ], + [ + "Ġf", + "am" + ], + [ + "Ġf", + "in" + ], + [ + "Ġus", + "ers" + ], + [ + "ç»Ļ", + "æĪij" + ], + [ + "vers", + "e" + ], + [ + "Ġele", + "ments" + ], + [ + "ĠE", + "x" + ], + [ + "--", + "--" + ], + [ + "éĿ", + "ł" + ], + [ + "请", + "æ±Ĥ" + ], + [ + "r", + "ase" + ], + [ + "æ±", + "½" + ], + [ + "æĢ»", + "ä¹ĭ" + ], + [ + "å®ŀ", + "æĹ¶" + ], + [ + "iv", + "es" + ], + [ + "åħ", + "ģ" + ], + [ + "æıIJ", + "åĩº" + ], + [ + "çŃ", + "ij" + ], + [ + "Ġcon", + "st" + ], + [ + "i", + "us" + ], + [ + "å¯Ĩ", + "çłģ" + ], + [ + "æ´", + "Ĺ" + ], + [ + "Ġcon", + "f" + ], + [ + "Ġsh", + "are" + ], + [ + "The", + "refore" + ], + [ + "2", + "4" + ], + [ + "æ£", + "®" + ], + [ + "comm", + "end" + ], + [ + "p", + "i" + ], + [ + "ä¾Ŀ", + "èµĸ" + ], + [ + "ç", + "ĩ" + ], + [ + "缸", + "åIJĮ" + ], + [ + "p", + "en" + ], + [ + "å®", + "Ļ" + ], + [ + "çīĪ", + "æľ¬" + ], + [ + "Ġar", + "r" + ], + [ + "åħ¬", + "åħ±" + ], + [ + "è§", + "Ī" + ], + [ + "åĬĽ", + "éĩı" + ], + [ + "Ġ", + "ident" + ], + [ + "çĨ", + "Ł" + ], + [ + "æ¶", + "²" + ], + [ + "åij", + "¼" + ], + [ + "æł¼", + "å¼ı" + ], + [ + "in", + "u" + ], + [ + "çļĦ", + "åħ·ä½ĵ" + ], + [ + "åIJĪéĢĤ", + "çļĦ" + ], + [ + "çİ", + "ĭ" + ], + [ + "大", + "æķ°æį®" + ], + [ + "Ġv", + "is" + ], + [ + "åľ", + "Ĩ" + ], + [ + "æĢĿ", + "èĢĥ" + ], + [ + "æĽ", + "²" + ], + [ + "ç§", + "ĭ" + ], + [ + "Ġtyp", + "ically" + ], + [ + "åħģ", + "许" + ], + [ + "çĮ", + "«" + ], + [ + "Ġcomple", + "x" + ], + [ + "æ", + "ħ§" + ], + [ + "Ġma", + "king" + ], + [ + "ion", + "al" + ], + [ + "w", + "ard" + ], + [ + "Ġse", + "con" + ], + [ + "Ġre", + "commend" + ], + [ + "å®ĩ", + "å®Ļ" + ], + [ + "/", + "/" + ], + [ + "æŃ£", + "åľ¨" + ], + [ + "g", + "in" + ], + [ + "åĪĨ", + "éħį" + ], + [ + "è·Ŀ", + "离" + ], + [ + "ä¸į", + "èĥ½" + ], + [ + "Ġcomm", + "un" + ], + [ + "åĿ", + "Ģ" + ], + [ + "å¾Ģ", + "å¾Ģ" + ], + [ + ":", + "**" + ], + [ + "ä¼", + "´" + ], + [ + "ri", + "es" + ], + [ + "i", + "o" + ], + [ + "éĽĨ", + "åĽ¢" + ], + [ + "ä¸Ń", + "æĸĩ" + ], + [ + "em", + "ber" + ], + [ + "Ġor", + "ig" + ], + [ + "å¼Ĥ", + "常" + ], + [ + "w", + "ays" + ], + [ + "åŁ¹", + "åħ»" + ], + [ + "Ġincl", + "ude" + ], + [ + "è", + "ľ" + ], + [ + "Ġsu", + "g" + ], + [ + "åĭ", + "ĩ" + ], + [ + "Ġtry", + "ing" + ], + [ + "Ġmess", + "age" + ], + [ + "r", + "or" + ], + [ + "ä½", + "Ļ" + ], + [ + "Ġsy", + "m" + ], + [ + "æ·", + "·" + ], + [ + "åģ", + "ľ" + ], + [ + "çī©", + "è´¨" + ], + [ + "Ġma", + "ch" + ], + [ + "er", + "gy" + ], + [ + "ç²", + "ī" + ], + [ + "Ġd", + "id" + ], + [ + "çļĦ", + "æķħäºĭ" + ], + [ + "Ġever", + "y" + ], + [ + "åĽ¢éĺŁ", + "æĪIJåijĺ" + ], + [ + "ãģ", + "Ĺ" + ], + [ + "å±ķ", + "çݰ" + ], + [ + "éĢIJ", + "æ¸IJ" + ], + [ + "Ġs", + "ing" + ], + [ + "i", + "ous" + ], + [ + "çļĦç", + "¬" + ], + [ + "Ġal", + "low" + ], + [ + "Ġt", + "ell" + ], + [ + "ĠUn", + "ited" + ], + [ + "ç¤", + "¼" + ], + [ + "Ġs", + "ame" + ], + [ + "ç®", + "±" + ], + [ + "i", + "pp" + ], + [ + "建", + "çŃij" + ], + [ + "èĢ", + "IJ" + ], + [ + "çļĦ", + "åħ³éĶ®" + ], + [ + "æĦı", + "ä¹ī" + ], + [ + "çľĭ", + "åΰ" + ], + [ + "èĩªçĦ¶", + "è¯Ńè¨Ģ" + ], + [ + "Ġc", + "ustom" + ], + [ + "Ġpr", + "ime" + ], + [ + "at", + "form" + ], + [ + "ent", + "ial" + ], + [ + "å¦Ĥæŀľä½ł", + "æľī" + ], + [ + "Ġserv", + "ice" + ], + [ + "ur", + "ity" + ], + [ + "il", + "ities" + ], + [ + "Ġse", + "e" + ], + [ + "è", + "Ń" + ], + [ + "天", + "æ°Ķ" + ], + [ + "éĥ½", + "èĥ½" + ], + [ + "Ġbe", + "c" + ], + [ + "饮", + "é£Ł" + ], + [ + "æ¶Īè´¹", + "èĢħ" + ], + [ + "er", + "v" + ], + [ + "Ġfe", + "ature" + ], + [ + "ä½ł", + "èĥ½" + ], + [ + "Ġup", + "d" + ], + [ + "çļĦ", + "主è¦ģ" + ], + [ + "å®ŀ", + "æĸ½" + ], + [ + "ä»", + "į" + ], + [ + "Ġm", + "ay" + ], + [ + "çŁ¥", + "éģĵ" + ], + [ + "è§£åĨ³", + "éĹ®é¢ĺ" + ], + [ + "al", + "e" + ], + [ + "Ġterm", + "s" + ], + [ + "å®ĺ", + "æĸ¹" + ], + [ + "éĢ", + "Ķ" + ], + [ + "çľ", + "ł" + ], + [ + "æĹ", + "ı" + ], + [ + "éľ", + "²" + ], + [ + "å¥", + "ĸ" + ], + [ + "Ġcon", + "tain" + ], + [ + "ç͍", + "çļĦ" + ], + [ + "åĪĨ", + "éĴŁ" + ], + [ + "ä¸Ģ", + "å®ļ" + ], + [ + "Ġf", + "l" + ], + [ + "温", + "度" + ], + [ + "a", + "i" + ], + [ + "Ġr", + "ight" + ], + [ + "¹", + "ģ" + ], + [ + "æĶ", + "»" + ], + [ + "ãĤ", + "Ĵ" + ], + [ + "åĪĽ", + "æĦı" + ], + [ + "è¿Ļ", + "å°Ĩ" + ], + [ + "åı¯ä»¥", + "æıIJä¾Ľ" + ], + [ + "æľºåύ", + "人" + ], + [ + "Ġw", + "id" + ], + [ + "å¦Ĥ", + "ä¸ĭ" + ], + [ + "Ġst", + "ud" + ], + [ + "ĠS", + "h" + ], + [ + "åΤ", + "æĸŃ" + ], + [ + "æīį", + "èĥ½" + ], + [ + "éĽ", + "¶" + ], + [ + "驾", + "é©¶" + ], + [ + "Ġc", + "at" + ], + [ + "ç¥ŀ", + "ç»ı" + ], + [ + "ä»", + "Ĭ" + ], + [ + "a", + "ir" + ], + [ + "çļĦæ·", + "±" + ], + [ + "åķĨ", + "ä¸ļ" + ], + [ + "\"", + "." + ], + [ + "$", + "," + ], + [ + "æĪij", + "ä¼ļ" + ], + [ + "ç±", + "į" + ], + [ + "Ġsk", + "y" + ], + [ + "以", + "便" + ], + [ + "è¡", + "¥" + ], + [ + "è", + "ª" + ], + [ + "çIJĨè§£", + "åĴĮ" + ], + [ + "çĻ", + "»" + ], + [ + "å¤įæĿĤ", + "çļĦ" + ], + [ + "æī©", + "å±ķ" + ], + [ + "Ġa", + "v" + ], + [ + "æĪij", + "çļĦ" + ], + [ + "å½ĵçĦ¶", + "åı¯ä»¥" + ], + [ + "å·®", + "å¼Ĥ" + ], + [ + "Ġst", + "or" + ], + [ + "æ±", + "ĩ" + ], + [ + "á", + "º" + ], + [ + "è¾", + "ħ" + ], + [ + "åĽ°", + "éļ¾" + ], + [ + "Ġf", + "ile" + ], + [ + "è¶³", + "å¤Ł" + ], + [ + "Ġc", + "le" + ], + [ + "iv", + "id" + ], + [ + "å°±", + "æĺ¯" + ], + [ + "y", + "n" + ], + [ + "ç»ĥ", + "ä¹ł" + ], + [ + "åĨ³", + "äºİ" + ], + [ + "åıĸ", + "åĨ³äºİ" + ], + [ + "æĦŁ", + "åħ´è¶£" + ], + [ + "åį", + "Ĭ" + ], + [ + "æĽ", + "¿" + ], + [ + "ä¿¡", + "åı·" + ], + [ + "ç»ı", + "åİĨ" + ], + [ + "is", + "p" + ], + [ + "èī", + "¾" + ], + [ + "1", + "3" + ], + [ + "åĮĸ", + "çļĦ" + ], + [ + "Ġc", + "ar" + ], + [ + "O", + "f" + ], + [ + "æ°", + "§" + ], + [ + "å·¥", + "ç¨ĭ" + ], + [ + "ä¸į", + "è¿ĩ" + ], + [ + "Ġco", + "st" + ], + [ + "c", + "ount" + ], + [ + "æħ", + "¢" + ], + [ + "Ġc", + "our" + ], + [ + "åĮº", + "åŁŁ" + ], + [ + "缸", + "对" + ], + [ + "Ġbusiness", + "es" + ], + [ + "éĤ®", + "ä»¶" + ], + [ + "A", + "T" + ], + [ + "Â", + "°" + ], + [ + "Ġro", + "ot" + ], + [ + "ri", + "x" + ], + [ + "Ġus", + "er" + ], + [ + "é¾", + "Ħ" + ], + [ + "at", + "ic" + ], + [ + "Ġ5", + "0" + ], + [ + "ç³»ç»Ł", + "çļĦ" + ], + [ + "æŁ¥", + "çľĭ" + ], + [ + "ä¸ī", + "个" + ], + [ + "Ġan", + "al" + ], + [ + "说", + "æĺİ" + ], + [ + "åĵ", + "Ī" + ], + [ + "èĢ", + "Ĺ" + ], + [ + "çļĦæ", + "¨¡" + ], + [ + "ith", + "m" + ], + [ + "9", + "9" + ], + [ + "Ġen", + "s" + ], + [ + "Ġat", + "t" + ], + [ + "Ġele", + "ment" + ], + [ + "Ġth", + "ree" + ], + [ + "çļĦç", + "¾" + ], + [ + "Ġc", + "ult" + ], + [ + "æŁIJ", + "个" + ], + [ + "Ġprovid", + "ing" + ], + [ + "æ¯Ķ", + "è¾ĥ" + ], + [ + "Ġinter", + "n" + ], + [ + "ä¼ł", + "è¾ĵ" + ], + [ + "常è§ģ", + "çļĦ" + ], + [ + "iv", + "ely" + ], + [ + "Ġ\\(", + "\\" + ], + [ + "è§Ĩ", + "è§ī" + ], + [ + "ut", + "ions" + ], + [ + "æĢģ", + "度" + ], + [ + "Ġpro", + "ject" + ], + [ + "ul", + "l" + ], + [ + "èĩª", + "çͱ" + ], + [ + "çº", + "¸" + ], + [ + "éĺ¶", + "段" + ], + [ + "åıį", + "æĺł" + ], + [ + "èį", + "ī" + ], + [ + "Ġm", + "u" + ], + [ + "s", + "h" + ], + [ + "çº", + "ª" + ], + [ + "T", + "h" + ], + [ + "Ġsug", + "gest" + ], + [ + "it", + "le" + ], + [ + "ç½", + "²" + ], + [ + "éĢļè¿ĩ", + "ä¸Ĭè¿°" + ], + [ + "åħ±", + "享" + ], + [ + "æĿ¥", + "æºIJ" + ], + [ + "ä¼ĺ", + "åĬ¿" + ], + [ + "åİŁ", + "çIJĨ" + ], + [ + "åŃ¦ä¹ł", + "åĴĮ" + ], + [ + "å¤", + "ı" + ], + [ + "ãģ", + "¾" + ], + [ + "ellig", + "ence" + ], + [ + "çĭ¬çī¹", + "çļĦ" + ], + [ + "ë", + "ĭ" + ], + [ + "åĢ", + "¾" + ], + [ + "iv", + "ers" + ], + [ + "åĨħ", + "åŃĺ" + ], + [ + "容", + "æĺĵ" + ], + [ + "Ġ1", + "4" + ], + [ + "Ġen", + "ergy" + ], + [ + "å¨", + "ģ" + ], + [ + "Ġpro", + "m" + ], + [ + "Ġide", + "a" + ], + [ + "å®īåħ¨", + "æĢ§" + ], + [ + "ruct", + "ure" + ], + [ + "Ġs", + "ides" + ], + [ + "çģ", + "¯" + ], + [ + "]", + "," + ], + [ + "res", + "ult" + ], + [ + "Ġm", + "il" + ], + [ + "æĮĩ", + "çļĦæĺ¯" + ], + [ + "åĮħæĭ¬", + "ä½Ĩä¸į" + ], + [ + "åĮħæĭ¬ä½Ĩä¸į", + "éĻIJäºİ" + ], + [ + "v", + "es" + ], + [ + "å®ŀ", + "ä¾ĭ" + ], + [ + "Ġtr", + "ue" + ], + [ + "ä»ĸ", + "çļĦ" + ], + [ + "ä½ĵ", + "çļĦ" + ], + [ + "Ġf", + "iel" + ], + [ + "en", + "se" + ], + [ + "ot", + "e" + ], + [ + "`", + "ï¼Į" + ], + [ + "è", + "Ĩ" + ], + [ + "Ġwe", + "re" + ], + [ + "åĽŀ", + "çŃĶ" + ], + [ + "ç¬", + "Ķ" + ], + [ + "on", + "t" + ], + [ + "ĠT", + "h" + ], + [ + "ç»Ħ", + "åIJĪ" + ], + [ + "h", + "t" + ], + [ + "é¥", + "°" + ], + [ + "ot", + "her" + ], + [ + "Ù", + "Ħ" + ], + [ + "æĸĩ", + "åѦ" + ], + [ + "xim", + "ately" + ], + [ + "è´", + "¡" + ], + [ + "æ¯ı", + "个人" + ], + [ + "Ġimport", + "ant" + ], + [ + "Ġm", + "illion" + ], + [ + "ç»", + "ĺ" + ], + [ + "è·¯", + "å¾Ħ" + ], + [ + "oo", + "gle" + ], + [ + "Ġprogram", + "ming" + ], + [ + "6", + "0" + ], + [ + "s", + "um" + ], + [ + "ä¸Ģ", + "段" + ], + [ + "æľī", + "ä¸Ģ个" + ], + [ + "åħ¬", + "å¼ı" + ], + [ + "æĹłè®º", + "æĺ¯" + ], + [ + "è®", + "¢" + ], + [ + "æĬ", + "Ĺ" + ], + [ + "è§Ĵ", + "度" + ], + [ + "Ġenviron", + "ment" + ], + [ + "I", + "D" + ], + [ + "Ģ", + "æľī" + ], + [ + "åĨ", + "Į" + ], + [ + "Ġrem", + "ain" + ], + [ + "Ġpl", + "atform" + ], + [ + "s", + "on" + ], + [ + "æľĢ", + "大" + ], + [ + "Ġo", + "pt" + ], + [ + "çļĦ", + "åŁºæľ¬" + ], + [ + "èĩ³", + "åħ³" + ], + [ + "èį¯", + "çī©" + ], + [ + "ar", + "m" + ], + [ + "æı", + "Ĵ" + ], + [ + "ib", + "r" + ], + [ + "è´¡", + "çĮ®" + ], + [ + "Ġa", + "fter" + ], + [ + "oh", + "n" + ], + [ + "æ±", + "ł" + ], + [ + "Ġb", + "ack" + ], + [ + "åį", + "ļ" + ], + [ + "Ġser", + "ies" + ], + [ + "t", + "ime" + ], + [ + "ĠS", + "p" + ], + [ + "er", + "ing" + ], + [ + "Ġn", + "etwor" + ], + [ + "us", + "s" + ], + [ + "Ġdesc", + "rib" + ], + [ + "é«ĺ", + "çļĦ" + ], + [ + "ĠY", + "ou" + ], + [ + "al", + "se" + ], + [ + "ä»ĸ们", + "çļĦ" + ], + [ + "åĮ", + "¹" + ], + [ + "t", + "y" + ], + [ + "æ", + "¥" + ], + [ + "è¯", + "ļ" + ], + [ + "æĿ", + "ij" + ], + [ + "æľ", + "¨" + ], + [ + "çļĦæ°", + "´" + ], + [ + "æķĪçİĩ", + "åĴĮ" + ], + [ + "å»", + "¶" + ], + [ + "av", + "es" + ], + [ + "per", + "ature" + ], + [ + "æĪij", + "æĥ³" + ], + [ + "yth", + "ing" + ], + [ + "ä¹", + "İ" + ], + [ + "æĶ¹", + "åĸĦ" + ], + [ + "éĵ¶", + "è¡Į" + ], + [ + "ï¼ļ", + "âĢľ" + ], + [ + "çŁ", + "©" + ], + [ + "åıĺ", + "å¾Ĺ" + ], + [ + "å®", + "½" + ], + [ + "åı¯ä»¥", + "帮åĬ©ä½ł" + ], + [ + "éģį", + "åİĨ" + ], + [ + "èĢĮ", + "ä¸įæĺ¯" + ], + [ + "Ġs", + "n" + ], + [ + "an", + "k" + ], + [ + "æ´", + "ŀ" + ], + [ + "è¿Ļ", + "æĺ¯ä¸Ģ个" + ], + [ + "¡", + "çľł" + ], + [ + "Ġ", + "è¾ĵåĩº" + ], + [ + "__", + "__" + ], + [ + "äº", + "¬" + ], + [ + "D", + "o" + ], + [ + "Ġs", + "ur" + ], + [ + "Ġal", + "ways" + ], + [ + "è±", + "Ĩ" + ], + [ + "Ġw", + "ater" + ], + [ + "st", + "it" + ], + [ + "éĿ", + "Ĵ" + ], + [ + "Ġl", + "im" + ], + [ + "Ġh", + "ig" + ], + [ + "`", + "," + ], + [ + "m", + "a" + ], + [ + "Ġposs", + "ible" + ], + [ + "Ġc", + "ir" + ], + [ + "Ġstate", + "ment" + ], + [ + "Ġmach", + "ine" + ], + [ + "Ġinteg", + "er" + ], + [ + "èĵ", + "Ŀ" + ], + [ + "ĠA", + "s" + ], + [ + "Ġh", + "our" + ], + [ + "丰å¯Į", + "çļĦ" + ], + [ + "Ġinterest", + "ed" + ], + [ + "±", + "ä¹IJ" + ], + [ + "æ³", + "¡" + ], + [ + "åį", + "ĥ" + ], + [ + "综", + "åIJĪ" + ], + [ + "ãĢĤ", + "âĢĿ" + ], + [ + "åįķ", + "è¯į" + ], + [ + "Ġc", + "ity" + ], + [ + "le", + "t" + ], + [ + "åĨ", + "Ľ" + ], + [ + "Ġin", + "de" + ], + [ + "Ġdet", + "erm" + ], + [ + "寻", + "æ±Ĥ" + ], + [ + "è¿Ľ", + "åħ¥" + ], + [ + "Ġbe", + "aut" + ], + [ + "Ġa", + "ir" + ], + [ + "Ð", + "»" + ], + [ + "Ġd", + "i" + ], + [ + "Ġhe", + "alth" + ], + [ + "é¡", + "»" + ], + [ + "n", + "ess" + ], + [ + "ç¢", + "³" + ], + [ + "å°", + "ģ" + ], + [ + "çļĦæ", + "´" + ], + [ + "`", + "." + ], + [ + "æĢ", + "İ" + ], + [ + "ä½į", + "äºİ" + ], + [ + "æ±½", + "车" + ], + [ + "çݯå¢ĥ", + "ä¸Ń" + ], + [ + "åĬĽ", + "åĴĮ" + ], + [ + "å½ĵ", + "åľ°" + ], + [ + "Ġob", + "ject" + ], + [ + "at", + "ural" + ], + [ + "å®Į", + "æķ´" + ], + [ + "ĠF", + "r" + ], + [ + "çĶ", + "³" + ], + [ + "Ġv", + "ary" + ], + [ + "Ġcont", + "ent" + ], + [ + "Ġb", + "it" + ], + [ + "Ġo", + "ur" + ], + [ + "çļĦç", + "ĥ" + ], + [ + "Ġun", + "ique" + ], + [ + "Ġwith", + "out" + ], + [ + "åĴĮ", + "è°" + ], + [ + "è¯Ń", + "éŁ³" + ], + [ + "åĪ", + "ł" + ], + [ + "伦", + "çIJĨ" + ], + [ + "our", + "ce" + ], + [ + "åĿ", + "ı" + ], + [ + "a", + "ve" + ], + [ + "çļĦ", + "æĸ¹å¼ı" + ], + [ + "Ġbe", + "ing" + ], + [ + "Ġc", + "ase" + ], + [ + "æ¯ı", + "ä¸Ģ" + ], + [ + "Ġs", + "al" + ], + [ + "èĤ", + "¤" + ], + [ + "Ġb", + "el" + ], + [ + "èĪ", + "Ĵ" + ], + [ + "Ġsec", + "urity" + ], + [ + "ç", + "¹ģ" + ], + [ + "u", + "ally" + ], + [ + "éĩį", + "æĸ°" + ], + [ + "çŃī", + "é¢ĨåŁŁ" + ], + [ + "é¢ĺ", + "缮" + ], + [ + "çļĦæ¯", + "ı" + ], + [ + "çļĦ", + "使ç͍" + ], + [ + "éĿ", + "©" + ], + [ + "ĠS", + "e" + ], + [ + "ãģ", + "«" + ], + [ + "Ġsc", + "ript" + ], + [ + "éĤ£", + "äºĽ" + ], + [ + "Ġm", + "on" + ], + [ + "æ®", + "Ĭ" + ], + [ + "身", + "份" + ], + [ + "Ġa", + "p" + ], + [ + "ĠR", + "e" + ], + [ + "'", + ":" + ], + [ + "ç͵", + "èĦij" + ], + [ + "è§Ĥ", + "å¯Ł" + ], + [ + "è¿Ļ", + "æĦıåij³çĿĢ" + ], + [ + "æ¡Ĩ", + "æŀ¶" + ], + [ + "çļĦç¼", + "ĸ" + ], + [ + "Ġinclud", + "es" + ], + [ + "åı", + "³" + ], + [ + "Ġl", + "og" + ], + [ + "à", + "¤" + ], + [ + "çļĦç¾", + "İ" + ], + [ + "Ġ", + "er" + ], + [ + "Ġim", + "p" + ], + [ + "Ġ1", + "3" + ], + [ + "çľ", + "ģ" + ], + [ + "n", + "er" + ], + [ + "书", + "ç±į" + ], + [ + "çī¹", + "æ®Ĭ" + ], + [ + "åıĤ", + "åĬł" + ], + [ + "Ġd", + "ate" + ], + [ + "åıij", + "å¸ĥ" + ], + [ + "çļĦæī", + "Ģæľī" + ], + [ + "è¯", + "Ĭ" + ], + [ + "Ġp", + "ublic" + ], + [ + "éĥ¨", + "ç½²" + ], + [ + "ro", + "ss" + ], + [ + "ãģ", + "Ħ" + ], + [ + "åĪł", + "éϤ" + ], + [ + "Ġ", + "%" + ], + [ + "iz", + "ation" + ], + [ + "ä¼ł", + "æĴŃ" + ], + [ + "Ġma", + "xim" + ], + [ + "re", + "ate" + ], + [ + "b", + "ox" + ], + [ + "ç", + "ij" + ], + [ + "Ġd", + "own" + ], + [ + "èŃ", + "¦" + ], + [ + "Ġcour", + "se" + ], + [ + "so", + "le" + ], + [ + "Ġ", + "åĴĮ" + ], + [ + "åº", + "§" + ], + [ + "以ä¸ĭ", + "åĩł" + ], + [ + "ĠM", + "y" + ], + [ + "Ġ", + "Q" + ], + [ + "ï¼ī", + "ï¼ļ" + ], + [ + "éĢļ", + "ä¿¡" + ], + [ + "R", + "e" + ], + [ + "è½", + "®" + ], + [ + "ä¸Ĭ", + "ä¸ĭ" + ], + [ + "ç¥", + "Ŀ" + ], + [ + ":", + ":" + ], + [ + "g", + "or" + ], + [ + "è", + "ĸ" + ], + [ + "å¹´", + "é¾Ħ" + ], + [ + "Ġinv", + "ol" + ], + [ + "Ġtem", + "perature" + ], + [ + "ä¿¡", + "ä»»" + ], + [ + "æıIJ", + "éĹ®" + ], + [ + "Ġpro", + "f" + ], + [ + "i", + "er" + ], + [ + "è®°", + "å¿Ĩ" + ], + [ + "森", + "æŀĹ" + ], + [ + "ä¸įä»ħ", + "èĥ½å¤Ł" + ], + [ + "æĤ£", + "èĢħ" + ], + [ + "g", + "er" + ], + [ + "ç»Ļ", + "å®ļ" + ], + [ + "çļĦ", + "å°ı" + ], + [ + "è¯Ń", + "åı¥" + ], + [ + "ä½ł", + "éľĢè¦ģ" + ], + [ + "éģµ", + "å®Ī" + ], + [ + "Ġro", + "le" + ], + [ + "åŃĹ", + "åħ¸" + ], + [ + "Ġrele", + "ased" + ], + [ + "E", + "x" + ], + [ + "b", + "in" + ], + [ + "EC", + "T" + ], + [ + "o", + "y" + ], + [ + "æ¿Ģ", + "åıij" + ], + [ + "çĶ", + "ļ" + ], + [ + "Ġcon", + "vert" + ], + [ + "Ġcomm", + "and" + ], + [ + "åĮ¹", + "éħį" + ], + [ + "ploye", + "es" + ], + [ + "çļĦç", + "Ł¥" + ], + [ + "é", + "º" + ], + [ + "é¦", + "Ĩ" + ], + [ + "å¡", + "ij" + ], + [ + "Ġpos", + "itive" + ], + [ + "O", + "N" + ], + [ + "Ġcon", + "cept" + ], + [ + "ĠE", + "arth" + ], + [ + "Ġfriend", + "ly" + ], + [ + "åı¯", + "éĿł" + ], + [ + "å°ı", + "æĹ¶" + ], + [ + "åºĶç͍", + "ç¨ĭåºı" + ], + [ + "è¿Ļ", + "段" + ], + [ + "Ġind", + "ivid" + ], + [ + "w", + "s" + ], + [ + "Ġnam", + "ed" + ], + [ + "Ġpl", + "an" + ], + [ + "Ġs", + "ort" + ], + [ + "Ġdo", + "c" + ], + [ + "è§Ĥ", + "çĤ¹" + ], + [ + "ãģ", + "§" + ], + [ + "Ġis", + "s" + ], + [ + "ä¸ĭ", + "çļĦ" + ], + [ + "设", + "æĸ½" + ], + [ + "åĴĮè°", + "IJ" + ], + [ + "空", + "æ°Ķ" + ], + [ + "æij", + "©" + ], + [ + "åĬ", + "³" + ], + [ + "请", + "æıIJä¾Ľ" + ], + [ + "Ġs", + "ources" + ], + [ + "ĠC", + "om" + ], + [ + "å¤įæĿĤ", + "度" + ], + [ + "a", + "pt" + ], + [ + "Ġsecon", + "d" + ], + [ + "æĶ»", + "åĩ»" + ], + [ + "'", + ")" + ], + [ + "Ġo", + "wn" + ], + [ + "Ġmean", + "ing" + ], + [ + "éĢ", + "Ģ" + ], + [ + "ä½", + "Ľ" + ], + [ + "è¡", + "£" + ], + [ + "ch", + "o" + ], + [ + "æľī", + "ä»Ģä¹Ī" + ], + [ + "æ²", + "³" + ], + [ + "缸", + "åºĶ" + ], + [ + "è", + "Ķ" + ], + [ + "çļĦ", + "å̼" + ], + [ + "Ġeff", + "ect" + ], + [ + "L", + "ECT" + ], + [ + "s", + "et" + ], + [ + "og", + "n" + ], + [ + "H", + "T" + ], + [ + "Ġ", + "```" + ], + [ + "åIJ", + "ĥ" + ], + [ + "æĥħ", + "绪" + ], + [ + "éķ", + "ĩ" + ], + [ + "oc", + "us" + ], + [ + "éĢĤ", + "å½ĵ" + ], + [ + "å¿ħ", + "é¡»" + ], + [ + "çĶļ", + "èĩ³" + ], + [ + "åĽ¾", + "çīĩ" + ], + [ + "çļĦæĮ", + "ĩ" + ], + [ + "çļĦ", + "åŁºç¡Ģ" + ], + [ + "Ġph", + "ys" + ], + [ + "ç»", + "Ŀ" + ], + [ + "al", + "ity" + ], + [ + "æīĵ", + "å¼Ģ" + ], + [ + "Ġt", + "re" + ], + [ + "Ġbec", + "ause" + ], + [ + "ir", + "t" + ], + [ + "çļĦæĥħåĨµ", + "ä¸ĭ" + ], + [ + "Ġp", + "at" + ], + [ + "Ġ2", + "00" + ], + [ + "åĮĸ", + "åѦ" + ], + [ + "åģı", + "好" + ], + [ + "éĹ", + "Ń" + ], + [ + "åľ°", + "åĿĢ" + ], + [ + "it", + "er" + ], + [ + "ag", + "ing" + ], + [ + "å¦Ĥä½ķ", + "åľ¨" + ], + [ + "Ġt", + "ake" + ], + [ + "åIJį", + "为" + ], + [ + "åį", + "´" + ], + [ + "åĪĨ", + "åĪ«" + ], + [ + "çļĦç", + "ľ" + ], + [ + "Ġpl", + "ace" + ], + [ + "æľĢ", + "è¿ij" + ], + [ + "Ġ$", + "\\" + ], + [ + "Ã", + "¡" + ], + [ + "numb", + "er" + ], + [ + "Ġmaxim", + "um" + ], + [ + "ç¯", + "ĩ" + ], + [ + "r", + "ue" + ], + [ + "ĠI", + "nt" + ], + [ + "Ġ2", + "4" + ], + [ + "èĥ", + "ŀ" + ], + [ + "c", + "ase" + ], + [ + "Ġde", + "ep" + ], + [ + "RO", + "M" + ], + [ + "s", + "c" + ], + [ + "Ġin", + "cre" + ], + [ + "ĠW", + "e" + ], + [ + "éĥ½", + "ä¼ļ" + ], + [ + "ç²¾", + "ç¥ŀ" + ], + [ + "å", + "©" + ], + [ + "åįı", + "ä½ľ" + ], + [ + "åı¯ä»¥", + "æĺ¯" + ], + [ + "çŃī", + "å¤ļ个" + ], + [ + "çĽij", + "æµĭ" + ], + [ + "åĴĮ", + "æľįåĬ¡" + ], + [ + "Ġd", + "ue" + ], + [ + "Ġe", + "ar" + ], + [ + "è¿IJ", + "èIJ¥" + ], + [ + "o", + "f" + ], + [ + "æ±", + "¡" + ], + [ + "é«ĺ", + "度" + ], + [ + "æī", + "¹" + ], + [ + "çĸ", + "«" + ], + [ + "Ġindivid", + "ual" + ], + [ + "èĩªçĦ¶è¯Ńè¨Ģ", + "å¤ĦçIJĨ" + ], + [ + "e", + "le" + ], + [ + "s", + "ive" + ], + [ + "ä¹Ł", + "èĥ½" + ], + [ + "æĻº", + "æħ§" + ], + [ + "s", + "ort" + ], + [ + "a", + "f" + ], + [ + "åħ¬åı¸", + "çļĦ" + ], + [ + "ç͍", + "æĿ¥" + ], + [ + "çī¹", + "æĢ§" + ], + [ + "è", + "ļ" + ], + [ + "Ġex", + "act" + ], + [ + "åŃĹ", + "æ¯į" + ], + [ + "pl", + "ace" + ], + [ + "å¾Ī", + "å¤ļ" + ], + [ + "æĽ´", + "åĩĨç¡®" + ], + [ + "æľīæīĢ", + "帮åĬ©" + ], + [ + "ä¹ĭ", + "åīį" + ], + [ + "Ġt", + "akes" + ], + [ + "r", + "al" + ], + [ + "å®ĥ们", + "çļĦ" + ], + [ + "çݰ", + "åľ¨" + ], + [ + "å¹", + "ķ" + ], + [ + "Ġd", + "uring" + ], + [ + "ç¼ĸ", + "è¾ij" + ], + [ + "g", + "re" + ], + [ + "ä»", + "¿" + ], + [ + "æľī", + "ä¸Ģ" + ], + [ + "t", + "ers" + ], + [ + "Ġs", + "ince" + ], + [ + "ent", + "ion" + ], + [ + "æĬ", + "½" + ], + [ + "页", + "éĿ¢" + ], + [ + "æ½ľ", + "åľ¨" + ], + [ + "è¯Ń", + "æ³ķ" + ], + [ + "Ġf", + "ocus" + ], + [ + "æĢĿ", + "ç»´" + ], + [ + "Ġloc", + "ated" + ], + [ + "!", + "\"" + ], + [ + "çº", + "³" + ], + [ + "缸åħ³", + "çļĦ" + ], + [ + "é¡", + "¶" + ], + [ + "Ġhelp", + "ful" + ], + [ + "Ġfact", + "or" + ], + [ + "ang", + "le" + ], + [ + "缴", + "åΰ" + ], + [ + "ĠSt", + "ates" + ], + [ + "æ¹", + "¿" + ], + [ + "æį", + "·" + ], + [ + "éĵ", + "ģ" + ], + [ + "è´Ł", + "è´£" + ], + [ + "ict", + "ion" + ], + [ + "Ġn", + "atural" + ], + [ + "Ġor", + "gan" + ], + [ + "å¨", + "±ä¹IJ" + ], + [ + "c", + "hes" + ], + [ + "Ġle", + "ft" + ], + [ + "Ġbl", + "ue" + ], + [ + "o", + "k" + ], + [ + "é»", + "ĺ" + ], + [ + "Ġma", + "de" + ], + [ + "Ġprovid", + "es" + ], + [ + "p", + "ose" + ], + [ + "çļĦç", + "ģ" + ], + [ + "éķ", + "ľ" + ], + [ + "åĿ", + "IJ" + ], + [ + "æĿ¥", + "å®ŀçݰ" + ], + [ + "ind", + "ows" + ], + [ + "ab", + "ilities" + ], + [ + "请", + "注æĦı" + ], + [ + "Ġte", + "am" + ], + [ + "éĢł", + "æĪIJ" + ], + [ + "çļĦ", + "èĥ½åĬĽ" + ], + [ + "éĨ", + "Ĵ" + ], + [ + "ç͍æĪ·", + "çļĦ" + ], + [ + "æĹ", + "ĭ" + ], + [ + "ra", + "ce" + ], + [ + "Ġph", + "rase" + ], + [ + "1", + "7" + ], + [ + "对", + "æĸ¹" + ], + [ + "Ġa", + "ge" + ], + [ + "ĠA", + "nd" + ], + [ + "çļĦ", + "é£İéĻ©" + ], + [ + "Ġa", + "ut" + ], + [ + "åį", + "«" + ], + [ + "ä¸Ģ", + "èĩ´" + ], + [ + "ç»Ĩ", + "èĥŀ" + ], + [ + "æĬĢæľ¯çļĦ", + "åıijå±ķ" + ], + [ + "çĶŁæĪIJ", + "ä¸Ģ个" + ], + [ + "Ġdiv", + "is" + ], + [ + "å°¤åħ¶", + "æĺ¯" + ], + [ + "çŃī", + "æĸ¹éĿ¢" + ], + [ + "Ġas", + "king" + ], + [ + "缮", + "çļĦ" + ], + [ + "第", + "äºĮ" + ], + [ + "èĪĴ", + "éĢĤ" + ], + [ + "æĬ", + "ĺ" + ], + [ + "Ġrec", + "ent" + ], + [ + "åįı", + "è®®" + ], + [ + "è¾", + "Ĩ" + ], + [ + "å·", + "¨" + ], + [ + "åī", + "Ĥ" + ], + [ + "t", + "he" + ], + [ + "æķ°", + "çļĦ" + ], + [ + "Ġhe", + "art" + ], + [ + "val", + "ue" + ], + [ + "éĿ¢", + "临" + ], + [ + "å®ŀéĻħ", + "ä¸Ĭ" + ], + [ + "æĿ", + "¯" + ], + [ + "d", + "d" + ], + [ + "Ġ", + "X" + ], + [ + "è´", + "µ" + ], + [ + "è¯ģ", + "æĺİ" + ], + [ + "å±", + "ı" + ], + [ + "ne", + "w" + ], + [ + "od", + "y" + ], + [ + "éģµ", + "循" + ], + [ + "Ġglob", + "al" + ], + [ + "Ġdep", + "ending" + ], + [ + "åĩ", + "Ģ" + ], + [ + "å·", + "¦" + ], + [ + "Ġ", + "Ø" + ], + [ + "æĽ´", + "åħ·ä½ĵçļĦ" + ], + [ + "帮åĬ©", + "æĤ¨" + ], + [ + "缸", + "äºĴ" + ], + [ + "Ġreturn", + "s" + ], + [ + "å¸ĮæľĽè¿ĻäºĽ", + "建议" + ], + [ + "Ġimpro", + "ve" + ], + [ + "Ġmu", + "ch" + ], + [ + "ä»ĸ", + "人" + ], + [ + "å¼", + "¹" + ], + [ + "4", + "5" + ], + [ + "åįķ", + "ä½į" + ], + [ + "çĦ", + "¦" + ], + [ + "åĪĹ表", + "ä¸Ń" + ], + [ + "`", + "ãĢĤ" + ], + [ + "con", + "om" + ], + [ + "èĥ½", + "éĩı" + ], + [ + "Ġcal", + "led" + ], + [ + "Ġr", + "ate" + ], + [ + "é¢Ħ", + "ç®Ĺ" + ], + [ + "èĩ³åħ³", + "éĩįè¦ģ" + ], + [ + "a", + "cc" + ], + [ + "ç»ĵ", + "æĿŁ" + ], + [ + "åı¯ä»¥", + "èĢĥèĻij" + ], + [ + "ç»Ĩ", + "èĬĤ" + ], + [ + "Ġ", + "ve" + ], + [ + "对", + "ä½ł" + ], + [ + "ĠThe", + "se" + ], + [ + "è¿ĩ", + "度" + ], + [ + "if", + "ied" + ], + [ + "è¯Ħ", + "ä»·" + ], + [ + "æķ°æį®", + "éĽĨ" + ], + [ + "é«ĺ", + "è´¨éĩı" + ], + [ + "èŀį", + "åIJĪ" + ], + [ + "åİŁ", + "åĪĻ" + ], + [ + "Ġappro", + "ximately" + ], + [ + "Ġrep", + "resent" + ], + [ + "ific", + "ial" + ], + [ + "as", + "on" + ], + [ + "æĸ¹", + "ç¨ĭ" + ], + [ + "Ġso", + "cial" + ], + [ + "Ġf", + "ig" + ], + [ + "SE", + "LECT" + ], + [ + "##", + "##" + ], + [ + "人", + "æ°ij" + ], + [ + "ãĢ", + "ij" + ], + [ + "he", + "st" + ], + [ + "ãĢ", + "IJ" + ], + [ + "Ġdig", + "ital" + ], + [ + "以ä¸ĭåĩłä¸ª", + "æĸ¹éĿ¢" + ], + [ + "Ġc", + "lo" + ], + [ + "å°Ĩ", + "åħ¶" + ], + [ + "çļĦ", + "åĨħ容" + ], + [ + "æı", + "¡" + ], + [ + "r", + "andom" + ], + [ + "å·", + "ŀ" + ], + [ + "è´Ń", + "çī©" + ], + [ + "h", + "ou" + ], + [ + "éĥ¨", + "éŨ" + ], + [ + "Ġsn", + "ipp" + ], + [ + "t", + "ract" + ], + [ + "çݰ", + "象" + ], + [ + "Ġneed", + "s" + ], + [ + "æķ°", + "åĪĹ" + ], + [ + "Ġg", + "ame" + ], + [ + "Ġt", + "ri" + ], + [ + "è´", + "´" + ], + [ + "çĭ¬", + "ç«ĭ" + ], + [ + "å¿ħè¦ģ", + "çļĦ" + ], + [ + "èĪ", + "ŀ" + ], + [ + "æ¸ħ", + "æ´ģ" + ], + [ + "象", + "å¾ģ" + ], + [ + "è½»", + "æĿ¾" + ], + [ + "é¸", + "¡" + ], + [ + "åĨ²", + "çªģ" + ], + [ + "Ġgener", + "al" + ], + [ + "Ġb", + "lo" + ], + [ + "çļĦæĦ", + "Ł" + ], + [ + "åĨħ", + "çļĦ" + ], + [ + "Ñ", + "ĥ" + ], + [ + "in", + "s" + ], + [ + "çļĦçŁ¥", + "è¯Ĩ" + ], + [ + "模", + "åĿĹ" + ], + [ + "éϤ", + "äºĨ" + ], + [ + "è¦", + "Ĩ" + ], + [ + "y", + "le" + ], + [ + "Ġint", + "elligence" + ], + [ + "åĨĻ", + "ä½ľ" + ], + [ + "Ġ", + "ed" + ], + [ + "Ġra", + "d" + ], + [ + "c", + "ing" + ], + [ + "P", + "lease" + ], + [ + "v", + "ed" + ], + [ + "èĥ½", + "帮åĬ©ä½ł" + ], + [ + "é¢Ŀ", + "å¤ĸ" + ], + [ + "Ġmark", + "et" + ], + [ + "Ġn", + "ight" + ], + [ + "in", + "put" + ], + [ + "æķ°æį®", + "åĪĨæŀIJ" + ], + [ + "æ´", + "²" + ], + [ + "m", + "e" + ], + [ + "w", + "h" + ], + [ + "Ġe", + "as" + ], + [ + "å¤ļ", + "å°ij" + ], + [ + "ãģ", + "¯" + ], + [ + "o", + "ck" + ], + [ + "C", + "reate" + ], + [ + "Ġloo", + "p" + ], + [ + "W", + "hen" + ], + [ + "ç", + "ħ" + ], + [ + "g", + "s" + ], + [ + "åı¯ä»¥", + "æł¹æį®" + ], + [ + "Ġrec", + "ogn" + ], + [ + "æĸ°", + "éĹ»" + ], + [ + "Ġ\\)", + "," + ], + [ + "ä¼ij", + "æģ¯" + ], + [ + "ĠM", + "a" + ], + [ + "ä¸Ĭä¸ĭ", + "æĸĩ" + ], + [ + "Ġm", + "em" + ], + [ + "åĬł", + "强" + ], + [ + "æ±", + "Ł" + ], + [ + "éĺ¿éĩĮå·´å·´", + "éĽĨåĽ¢" + ], + [ + "Ġsing", + "le" + ], + [ + "æ´", + "ĭ" + ], + [ + "P", + "U" + ], + [ + "im", + "ate" + ], + [ + "çļĦæ²", + "ŁéĢļ" + ], + [ + "åľ¨", + "æŁIJäºĽ" + ], + [ + "æµ", + "ª" + ], + [ + "ä¸", + "Ŀ" + ], + [ + "表", + "éĿ¢" + ], + [ + "èī¯", + "好" + ], + [ + "æŀ¶", + "æŀĦ" + ], + [ + "éĺ", + "»" + ], + [ + "ãģ", + "ª" + ], + [ + "åı¯æĮģç»Ń", + "åıijå±ķ" + ], + [ + "Ġen", + "h" + ], + [ + "sq", + "l" + ], + [ + "çŃī", + "äºİ" + ], + [ + "Ġmulti", + "ple" + ], + [ + "pl", + "ic" + ], + [ + "æĸĩ", + "åŃĹ" + ], + [ + "ä¹Ł", + "ä¼ļ" + ], + [ + "éĩ", + "İ" + ], + [ + "ount", + "ain" + ], + [ + "2", + "00" + ], + [ + "l", + "ing" + ], + [ + "åζ", + "éĢł" + ], + [ + "âĢĿ", + "ï¼Ī" + ], + [ + "Ġdis", + "c" + ], + [ + "A", + "PI" + ], + [ + "Ġ1", + "7" + ], + [ + "èĻ", + "«" + ], + [ + "éļ", + "Ķ" + ], + [ + "pl", + "ify" + ], + [ + "Ġcl", + "ar" + ], + [ + "åΰ", + "äºĨ" + ], + [ + "æ¹", + "ĸ" + ], + [ + "Ġd", + "er" + ], + [ + "Ġrequ", + "est" + ], + [ + "æĹ¶", + "åĢĻ" + ], + [ + "æľªæĿ¥", + "çļĦ" + ], + [ + "ç½", + "Ĺ" + ], + [ + "åħ¬", + "å¹³" + ], + [ + "Ġch", + "ang" + ], + [ + "æĮĩ", + "导" + ], + [ + "åı", + "«" + ], + [ + "Ġl", + "ess" + ], + [ + "æľī", + "çĿĢ" + ], + [ + "çļĦæł", + "¸" + ], + [ + "pp", + "er" + ], + [ + "on", + "y" + ], + [ + "éĢĤ", + "ç͍äºİ" + ], + [ + "Ġ", + "Ã" + ], + [ + "å®ļ", + "ä½į" + ], + [ + "æľī", + "è¶£" + ], + [ + "çļĦæŃ", + "£" + ], + [ + "Java", + "Script" + ], + [ + "åĬ¨", + "åĬĽ" + ], + [ + "un", + "d" + ], + [ + "ð", + "Ł" + ], + [ + "å¼Ģåıij", + "èĢħ" + ], + [ + "ç¾İ", + "åĽ½" + ], + [ + "èĪ", + "¬" + ], + [ + "..", + "." + ], + [ + "åĪĨ", + "为" + ], + [ + "çļĦæĮ", + "ijæĪĺ" + ], + [ + "Ġam", + "ount" + ], + [ + "Ġfil", + "m" + ], + [ + "å°½", + "éĩı" + ], + [ + "çļĦæ³", + "ķ" + ], + [ + "Ġprim", + "ary" + ], + [ + "Ġpop", + "ular" + ], + [ + "um", + "e" + ], + [ + "s", + "elf" + ], + [ + "8", + "0" + ], + [ + "æ¯Ķ", + "èµĽ" + ], + [ + "åIJį", + "åŃĹ" + ], + [ + "å¡", + "«" + ], + [ + "èĩªåĬ¨", + "åĮĸ" + ], + [ + "Ġ", + "es" + ], + [ + "磩", + "éĺµ" + ], + [ + "re", + "hen" + ], + [ + "ł", + "éģĵ" + ], + [ + "为", + "ä»Ģä¹Ī" + ], + [ + "çı", + "Ń" + ], + [ + "Ġcap", + "abilities" + ], + [ + "Ġsnipp", + "et" + ], + [ + "è·", + "Ł" + ], + [ + "<", + "/" + ], + [ + "Ð", + "º" + ], + [ + "è¸", + "ª" + ], + [ + "en", + "ces" + ], + [ + "Ġwid", + "ely" + ], + [ + "Ġd", + "el" + ], + [ + "Ġcon", + "c" + ], + [ + "n", + "ov" + ], + [ + "åİ", + "ļ" + ], + [ + "è¿Ļ个", + "éĹ®é¢ĺ" + ], + [ + "éĢļ常", + "ä¼ļ" + ], + [ + "ä¿Ŀ", + "åŃĺ" + ], + [ + "at", + "s" + ], + [ + "è¿ľ", + "ç¨ĭ" + ], + [ + "t", + "ed" + ], + [ + "è´¦", + "æĪ·" + ], + [ + "åħ¬", + "ä¼Ĺ" + ], + [ + "O", + "S" + ], + [ + "id", + "er" + ], + [ + "Ġg", + "roup" + ], + [ + "e", + "xt" + ], + [ + "Ġn", + "ature" + ], + [ + "e", + "k" + ], + [ + "表达", + "å¼ı" + ], + [ + "Ġunderstand", + "ing" + ], + [ + "ile", + "d" + ], + [ + "Ġeff", + "ic" + ], + [ + "çͳ", + "请" + ], + [ + "è¯Ĭ", + "æĸŃ" + ], + [ + "模", + "æĭŁ" + ], + [ + "ip", + "ly" + ], + [ + "ç͍æĪ·", + "ä½ĵéªĮ" + ], + [ + "ol", + "s" + ], + [ + "éĽĨ", + "æĪIJ" + ], + [ + "éģĵ", + "å¾·" + ], + [ + "é¾", + "Ļ" + ], + [ + "com", + "e" + ], + [ + "4", + "0" + ], + [ + "Ġhist", + "ory" + ], + [ + "æİ", + "Ī" + ], + [ + "污", + "æŁĵ" + ], + [ + "é", + "Ł" + ], + [ + "æıIJé«ĺ", + "äºĨ" + ], + [ + "Ġrel", + "ated" + ], + [ + "å¥", + "Ĺ" + ], + [ + "åı¯èĥ½", + "æĺ¯" + ], + [ + "æ¯ı", + "天" + ], + [ + "an", + "cial" + ], + [ + "åģ", + "¶" + ], + [ + "Ġsc", + "ient" + ], + [ + "éĿŀ常", + "éĩįè¦ģ" + ], + [ + "ch", + "ie" + ], + [ + "åı¯ä»¥", + "ç͍äºİ" + ], + [ + "Ġorig", + "inal" + ], + [ + "æľĢ", + "å°ı" + ], + [ + "èĤ", + "Į" + ], + [ + "åij¼", + "åIJ¸" + ], + [ + "Ġno", + "vel" + ], + [ + "Ġupd", + "ates" + ], + [ + "çļĦæĥħ", + "æĦŁ" + ], + [ + "ä½ĵ", + "ç³»" + ], + [ + "èĭ±", + "è¯Ń" + ], + [ + "Al", + "ibaba" + ], + [ + "éĢĴ", + "å½Ĵ" + ], + [ + "çļĦ", + "ä¸Ģ个" + ], + [ + "ist", + "s" + ], + [ + "Ġs", + "ize" + ], + [ + "åľ¨", + "å®ŀéĻħ" + ], + [ + "d", + "ata" + ], + [ + "æĪIJ", + "为äºĨ" + ], + [ + "Ġf", + "ur" + ], + [ + "æ»", + "¤" + ], + [ + "Ġrequ", + "ire" + ], + [ + "æķı", + "æĦŁ" + ], + [ + "æİ", + "Į" + ], + [ + "ãĤ", + "ĭ" + ], + [ + "Ġt", + "est" + ], + [ + "åıij", + "éĢģ" + ], + [ + "Ġap", + "pe" + ], + [ + "Ġk", + "il" + ], + [ + "ç¨ĭ", + "度" + ], + [ + "Ġfe", + "w" + ], + [ + "æ°Ķ", + "åĢĻ" + ], + [ + "ç¡®", + "认" + ], + [ + "æĪIJ", + "æŀľ" + ], + [ + "Ġne", + "xt" + ], + [ + "ä¸ĵ", + "注" + ], + [ + "Ġvari", + "able" + ], + [ + "Ġbe", + "gin" + ], + [ + "box", + "ed" + ], + [ + "Ġs", + "il" + ], + [ + "åѦ", + "æł¡" + ], + [ + "Ġfeature", + "s" + ], + [ + "æĿ¥", + "èĩª" + ], + [ + "çļĦæĻ", + "º" + ], + [ + "å®ī", + "æİĴ" + ], + [ + "å®", + "ģ" + ], + [ + "èµĦ", + "éĩij" + ], + [ + "p", + "ar" + ], + [ + "èī²", + "彩" + ], + [ + "让", + "æĪij们" + ], + [ + "am", + "es" + ], + [ + "èIJ¥", + "åħ»" + ], + [ + "Ġf", + "ore" + ], + [ + "Ġe", + "conom" + ], + [ + "Ġg", + "ent" + ], + [ + "é©", + "±" + ], + [ + "å±ŀ", + "æĢ§" + ], + [ + "ç®Ģ", + "æ´ģ" + ], + [ + "Ġcomp", + "rehen" + ], + [ + "ç«ĭ", + "åį³" + ], + [ + "Ġ\\)", + "." + ], + [ + "Ġequ", + "al" + ], + [ + "çļĦ", + "代çłģ" + ], + [ + "Ġbeaut", + "y" + ], + [ + "大", + "è§Ħ模" + ], + [ + "åIJij", + "éĩı" + ], + [ + "ı", + "è§Ī" + ], + [ + "æľ", + "«" + ], + [ + "re", + "qu" + ], + [ + "åΰ", + "çļĦ" + ], + [ + "åĬł", + "éĢŁ" + ], + [ + "_", + "{" + ], + [ + "Ù", + "Ĭ" + ], + [ + "Ġd", + "ays" + ], + [ + "è´", + "¥" + ], + [ + "Ġstand", + "ard" + ], + [ + "ĠA", + "n" + ], + [ + "è´¹", + "ç͍" + ], + [ + "èijĹ", + "åIJį" + ], + [ + "æ±", + "ī" + ], + [ + "m", + "in" + ], + [ + "çļĦç", + "²" + ], + [ + "Ġlangu", + "ages" + ], + [ + "irt", + "ual" + ], + [ + "Ġp", + "res" + ], + [ + "Ġv", + "ast" + ], + [ + "ĥ", + "åľ" + ], + [ + "pl", + "es" + ], + [ + "çļĦæ´", + "»" + ], + [ + "åĵ", + "²" + ], + [ + "çļĦæĶ", + "¯" + ], + [ + "硬", + "ä»¶" + ], + [ + "é¢Ĩ", + "导" + ], + [ + "èĤ¡", + "票" + ], + [ + "åŀ", + "ĥåľ" + ], + [ + "åŀĥåľ", + "¾" + ], + [ + "åı¯ä»¥", + "æıIJé«ĺ" + ], + [ + "éĹ®é¢ĺ", + "æĪĸ" + ], + [ + "Ġre", + "se" + ], + [ + "æ²", + "ī" + ], + [ + "è·", + "³" + ], + [ + "Ġ", + "ê" + ], + [ + "æİ¥", + "æĶ¶" + ], + [ + "ut", + "ure" + ], + [ + "on", + "d" + ], + [ + "Ġha", + "d" + ], + [ + "满", + "æĦı" + ], + [ + "Ġav", + "ail" + ], + [ + "A", + "B" + ], + [ + "Ġpro", + "per" + ], + [ + "I", + "P" + ], + [ + "æľī", + "çļĦ" + ], + [ + "u", + "th" + ], + [ + "æĶ¶", + "åħ¥" + ], + [ + "Ġt", + "imes" + ], + [ + "è§Ħ", + "å®ļ" + ], + [ + "ç´§", + "æĢ¥" + ], + [ + "Ġloo", + "k" + ], + [ + "绣", + "计" + ], + [ + "Ġm", + "et" + ], + [ + "å²", + "ģ" + ], + [ + "Ġcon", + "d" + ], + [ + "ãĥ", + "¼" + ], + [ + "Ġassist", + "ant" + ], + [ + "dition", + "ally" + ], + [ + "Ġfiel", + "d" + ], + [ + "交", + "äºĴ" + ], + [ + "åĪĿ", + "å§ĭ" + ], + [ + "èĽĭ", + "çϽ" + ], + [ + "å", + "³" + ], + [ + "Ġs", + "ay" + ], + [ + "çĸ", + "ij" + ], + [ + "æĶ¾", + "æĿ¾" + ], + [ + "æ¶", + "¦" + ], + [ + "Ġsoft", + "ware" + ], + [ + "æľīæķĪ", + "çļĦ" + ], + [ + "æ", + "©" + ], + [ + "çĶŁ", + "éķ¿" + ], + [ + "é¢ij", + "çİĩ" + ], + [ + "T", + "rans" + ], + [ + "注", + "åĨĮ" + ], + [ + "èĭ±", + "æĸĩ" + ], + [ + "æİ¢", + "讨" + ], + [ + "顺", + "åºı" + ], + [ + "Ġl", + "and" + ], + [ + "Ġnot", + "e" + ], + [ + "p", + "os" + ], + [ + "\"", + "?" + ], + [ + "æŁ¥", + "æī¾" + ], + [ + "人", + "çī©" + ], + [ + "æĹ¶", + "æľŁ" + ], + [ + "in", + "ing" + ], + [ + "iz", + "es" + ], + [ + "åĢ", + "Ĵ" + ], + [ + "Ġgener", + "ating" + ], + [ + "Ġsome", + "one" + ], + [ + "Ġfur", + "ther" + ], + [ + "ãģ", + "Į" + ], + [ + "主", + "ä¹ī" + ], + [ + "åIJ«", + "ä¹ī" + ], + [ + "个人", + "çļĦ" + ], + [ + "åľ¨", + "ä¸Ģ个" + ], + [ + "ro", + "id" + ], + [ + "Ġn", + "ow" + ], + [ + "åį", + "·" + ], + [ + "车", + "è¾Ĩ" + ], + [ + "t", + "a" + ], + [ + "åĴĮ", + "管çIJĨ" + ], + [ + "åĢ", + "ĭ" + ], + [ + "çĹ", + "Ľ" + ], + [ + "ä»į", + "çĦ¶" + ], + [ + "è¦Ĩ", + "çĽĸ" + ], + [ + "Ġn", + "p" + ], + [ + "Ġo", + "ld" + ], + [ + "ãģ", + "¦" + ], + [ + "å½¢", + "象" + ], + [ + "èĢĮ", + "æĺ¯" + ], + [ + "Ġform", + "at" + ], + [ + "Ġfin", + "ancial" + ], + [ + "åı¥", + "è¯Ŀ" + ], + [ + "çĻ", + "¾" + ], + [ + "H", + "e" + ], + [ + "l", + "u" + ], + [ + "åĨ", + "Ĵ" + ], + [ + "æ·±", + "åĪ»" + ], + [ + "å¹¶", + "ä¸Ķ" + ], + [ + "av", + "as" + ], + [ + "åIJ¯", + "åĬ¨" + ], + [ + "å¹", + "»" + ], + [ + "Ġdat", + "ab" + ], + [ + "æľĢ", + "ä½³" + ], + [ + "Ġnam", + "es" + ], + [ + "ç¬", + "ij" + ], + [ + "âĢĿ", + "ãĢģ" + ], + [ + "Ġcomput", + "er" + ], + [ + "Ġab", + "ility" + ], + [ + "Ġparticular", + "ly" + ], + [ + "åıĸ", + "å¾Ĺ" + ], + [ + "çļĦç¬", + "¬" + ], + [ + "avas", + "cript" + ], + [ + "o", + "ice" + ], + [ + "Ġdoc", + "ument" + ], + [ + "ĠL", + "et" + ], + [ + "Ġapplic", + "ations" + ], + [ + "缮", + "åīį" + ], + [ + "Ġcle", + "ar" + ], + [ + "isp", + "ers" + ], + [ + "Ã", + "³" + ], + [ + "ç«", + "¥" + ], + [ + "è¿Ļæł·", + "çļĦ" + ], + [ + "帮", + "æĪij" + ], + [ + "çľĭ", + "èµ·æĿ¥" + ], + [ + "å¹", + "¸" + ], + [ + "å°ı", + "说" + ], + [ + "pl", + "ay" + ], + [ + "d", + "ict" + ], + [ + "is", + "hed" + ], + [ + "Ġp", + "ract" + ], + [ + "çķĮ", + "éĿ¢" + ], + [ + "Ġa", + "chie" + ], + [ + "Ġ", + "å°Ĩ" + ], + [ + "çłĶ", + "åıij" + ], + [ + "æ¼", + "«" + ], + [ + "åı¤", + "代" + ], + [ + "çļĦç¼ĸ", + "ç¨ĭ" + ], + [ + "y", + "stem" + ], + [ + "Ġwith", + "in" + ], + [ + "ç£", + "ģ" + ], + [ + "Ġm", + "ar" + ], + [ + "r", + "ight" + ], + [ + "ä¿®", + "å¤į" + ], + [ + "led", + "ge" + ], + [ + "åį", + "ł" + ], + [ + "E", + "O" + ], + [ + "æĪij们", + "çļĦ" + ], + [ + "çī©", + "æµģ" + ], + [ + "ãģ¾", + "ãģĻ" + ], + [ + "Ġcount", + "ry" + ], + [ + "Ġse", + "ver" + ], + [ + "å±ŀ", + "äºİ" + ], + [ + "N", + "A" + ], + [ + "ĠN", + "ew" + ], + [ + "åıĬ", + "åħ¶" + ], + [ + "Ġpoint", + "s" + ], + [ + "ç´", + "¯" + ], + [ + "å°½", + "åĬĽ" + ], + [ + "æĢİ", + "ä¹Ī" + ], + [ + "d", + "ing" + ], + [ + "çļĦæł¸", + "å¿ĥ" + ], + [ + "大", + "éĩıçļĦ" + ], + [ + "Ġev", + "ent" + ], + [ + "an", + "e" + ], + [ + "Ġth", + "ose" + ], + [ + "éĢīæĭ©", + "åIJĪéĢĤçļĦ" + ], + [ + "Ġinteg", + "ers" + ], + [ + "Ġ", + "æĺ¯" + ], + [ + "ab", + "les" + ], + [ + "çŃĶ", + "æ¡Ī" + ], + [ + "l", + "and" + ], + [ + "m", + "l" + ], + [ + "ãģ", + "¨" + ], + [ + "Ġder", + "iv" + ], + [ + "âĢĿãĢģ", + "âĢľ" + ], + [ + "he", + "l" + ], + [ + "Ġ", + "â" + ], + [ + "åħ", + "°" + ], + [ + "æ··", + "åIJĪ" + ], + [ + "æĢĿ", + "æĥ³" + ], + [ + "å¸ĥ", + "å±Ģ" + ], + [ + "çī", + "Ļ" + ], + [ + "æģ", + "¶" + ], + [ + "å¼ķ", + "èµ·" + ], + [ + "Ġ", + "å¦Ĥæŀľ" + ], + [ + "å°", + "¾" + ], + [ + "è¿Ļ个", + "åĩ½æķ°" + ], + [ + "è§Ĥ", + "ä¼Ĺ" + ], + [ + "åŁº", + "åĽł" + ], + [ + "ER", + "E" + ], + [ + "L", + "et" + ], + [ + "çļĦ", + "æķ°åŃĹ" + ], + [ + "è¿", + "Ń" + ], + [ + "çª", + "Ĺ" + ], + [ + "lo", + "ad" + ], + [ + "Ġdeterm", + "ine" + ], + [ + "om", + "s" + ], + [ + "åѦ", + "ä¼ļ" + ], + [ + "èĦ", + "Ĥ" + ], + [ + "if", + "t" + ], + [ + "éĿ¢", + "积" + ], + [ + "å®¶", + "人" + ], + [ + "äº", + "Ī" + ], + [ + "Ġp", + "ot" + ], + [ + "ä½ł", + "对" + ], + [ + "iv", + "ing" + ], + [ + "ç±»åŀĭ", + "çļĦ" + ], + [ + "严", + "éĩį" + ], + [ + "Ġsever", + "al" + ], + [ + "çĬ", + "¯" + ], + [ + "ç±»", + "ä¼¼" + ], + [ + "us", + "h" + ], + [ + "第", + "ä¸Ģ" + ], + [ + "è¾ħ", + "åĬ©" + ], + [ + "ä¾ĽåºĶ", + "éĵ¾" + ], + [ + "åĴĮ", + "对" + ], + [ + "æ»", + "ij" + ], + [ + "Ġequ", + "ations" + ], + [ + "ä¸ĵä¸ļ", + "çļĦ" + ], + [ + "çļĦ", + "åıĺåĮĸ" + ], + [ + "Ġsol", + "utions" + ], + [ + "Ġb", + "ase" + ], + [ + "Ġcommun", + "ic" + ], + [ + "æį", + "ķ" + ], + [ + "åѦ", + "èĢħ" + ], + [ + "Ġl", + "ove" + ], + [ + "çļĦæł", + "ĩ" + ], + [ + "Ġhig", + "hest" + ], + [ + "j", + "avascript" + ], + [ + "Ġrec", + "ord" + ], + [ + "Ġm", + "ath" + ], + [ + "an", + "ced" + ], + [ + "Ġpur", + "pose" + ], + [ + "çļĦ", + "æĢ»" + ], + [ + "é£Ł", + "åĵģ" + ], + [ + "AI", + "æĬĢæľ¯" + ], + [ + "H", + "ERE" + ], + [ + "he", + "mat" + ], + [ + "åĢ", + "º" + ], + [ + "Ã", + "Ń" + ], + [ + "Ġac", + "ross" + ], + [ + "ä¸ī", + "è§Ĵ" + ], + [ + "æ¶īåıĬ", + "åΰ" + ], + [ + "Ġwor", + "king" + ], + [ + "is", + "m" + ], + [ + "stit", + "ute" + ], + [ + "éĴĪ", + "对" + ], + [ + "Ġin", + "it" + ], + [ + "a", + "iled" + ], + [ + "n", + "ot" + ], + [ + "çĽ", + "IJ" + ], + [ + "æĺĵ", + "äºİ" + ], + [ + "Ġlead", + "ing" + ], + [ + "Ġv", + "ery" + ], + [ + "Ġon", + "line" + ], + [ + "o", + "ot" + ], + [ + "ç´¢", + "å¼ķ" + ], + [ + "è´§", + "å¸ģ" + ], + [ + "çĥ", + "¤" + ], + [ + "é¸", + "Ł" + ], + [ + "ĠP", + "r" + ], + [ + "ut", + "es" + ], + [ + "çļĦ", + "åħĥç´ł" + ], + [ + "en", + "e" + ], + [ + "è¶ħ", + "è¿ĩ" + ], + [ + "严", + "æł¼" + ], + [ + "æĿĥ", + "éĻIJ" + ], + [ + "fere", + "nce" + ], + [ + "Ġsym", + "b" + ], + [ + "st", + "ems" + ], + [ + "åľ¨", + "äºİ" + ], + [ + "'", + "]" + ], + [ + "Ġem", + "b" + ], + [ + "a", + "ces" + ], + [ + "r", + "and" + ], + [ + "Ġcol", + "or" + ], + [ + "çļĦçĥ", + "Ń" + ], + [ + "N", + "ow" + ], + [ + "d", + "iv" + ], + [ + "é¢", + "Ĺ" + ], + [ + "çļĦåľ°", + "æĸ¹" + ], + [ + "ä¿Ŀ", + "éĻ©" + ], + [ + "çī©", + "åĵģ" + ], + [ + "æłĩ", + "çѾ" + ], + [ + "Ġrespon", + "se" + ], + [ + "Ġm", + "eters" + ], + [ + "Python", + "ä¸Ń" + ], + [ + "è¯", + "º" + ], + [ + "å¤ĸ", + "éĥ¨" + ], + [ + "Ġin", + "nov" + ], + [ + "è¿Ľ", + "åζ" + ], + [ + "èĭ", + "ı" + ], + [ + "oo", + "ks" + ], + [ + "ç¼", + "ĺ" + ], + [ + "çļ®", + "èĤ¤" + ], + [ + "让", + "人" + ], + [ + "Ġsol", + "ution" + ], + [ + "et", + "er" + ], + [ + "çĻ»", + "å½ķ" + ], + [ + "Ġ", + "Wh" + ], + [ + "客", + "æľį" + ], + [ + "åİŁ", + "å§ĭ" + ], + [ + "åĴĮ", + "åºĶç͍" + ], + [ + "èĢIJ", + "å¿ĥ" + ], + [ + "b", + "y" + ], + [ + "çļĦ", + "缮æłĩ" + ], + [ + "ä¿Ŀ", + "è¯ģ" + ], + [ + "ä½ł", + "æĥ³è¦ģ" + ], + [ + "容", + "åύ" + ], + [ + "p", + "re" + ], + [ + "çĹħ", + "æ¯Ĵ" + ], + [ + "Ġimp", + "act" + ], + [ + "æıIJ", + "åıĸ" + ], + [ + "æĥ", + "ł" + ], + [ + "--", + "-" + ], + [ + "ers", + "hip" + ], + [ + "åĴĮ", + "社ä¼ļ" + ], + [ + "åħħ", + "åĪĨ" + ], + [ + "æķ°", + "å̼" + ], + [ + "Ġcond", + "itions" + ], + [ + "id", + "d" + ], + [ + "åºĶç͍", + "äºİ" + ], + [ + "è£ħ", + "饰" + ], + [ + "é¢ĨåŁŁ", + "çļĦ" + ], + [ + "Ġrespon", + "s" + ], + [ + "çļĦæ³ķ", + "å¾ĭ" + ], + [ + "æĬ", + "Ĭ" + ], + [ + "åĵ²", + "åѦ" + ], + [ + "强", + "大çļĦ" + ], + [ + "Ġse", + "lect" + ], + [ + "c", + "os" + ], + [ + "çī¹", + "èī²" + ], + [ + "ĠThe", + "y" + ], + [ + "线", + "æĢ§" + ], + [ + "Ġs", + "ide" + ], + [ + "æµ", + "ĵ" + ], + [ + "æķ´", + "ä½ĵ" + ], + [ + "W", + "eb" + ], + [ + "Ġsp", + "ace" + ], + [ + "Ġart", + "ic" + ], + [ + "éĵ¾", + "æİ¥" + ], + [ + "Ġjo", + "ke" + ], + [ + "w", + "ay" + ], + [ + "çļĦ", + "æľįåĬ¡" + ], + [ + "Ġmodel", + "s" + ], + [ + "å±ħ", + "æ°ij" + ], + [ + "ï¼Į", + "âĢľ" + ], + [ + "æĹħ", + "游" + ], + [ + "åĴĮ", + "åĪĨæŀIJ" + ], + [ + "æµ", + "ıè§Ī" + ], + [ + "l", + "ess" + ], + [ + "Ġp", + "ain" + ], + [ + "çļĦæ¦Ĥ", + "念" + ], + [ + "çī¹åĪ«", + "æĺ¯" + ], + [ + "Ġperform", + "ance" + ], + [ + "id", + "i" + ], + [ + "=", + "\"" + ], + [ + "r", + "on" + ], + [ + "Ġm", + "ind" + ], + [ + "Ġvari", + "ety" + ], + [ + "Ù", + "ħ" + ], + [ + "ĠB", + "e" + ], + [ + "Ġst", + "ore" + ], + [ + "æŃ£", + "常" + ], + [ + "Ġpro", + "du" + ], + [ + "广", + "åijĬ" + ], + [ + "çĶ", + "²" + ], + [ + "æľĢ", + "æĸ°çļĦ" + ], + [ + "顾", + "客" + ], + [ + "æĺ¯", + "ä¸ŃåĽ½" + ], + [ + "Ġrel", + "ations" + ], + [ + "Ġm", + "ountain" + ], + [ + "Ġfact", + "ors" + ], + [ + "è´", + "·" + ], + [ + "æ·±", + "è¿ľ" + ], + [ + "å°", + "¼" + ], + [ + "Ġgent", + "le" + ], + [ + "Ġmil", + "es" + ], + [ + "Ġbe", + "fore" + ], + [ + "çĥ", + "Ī" + ], + [ + "ar", + "ray" + ], + [ + "æķ", + "ij" + ], + [ + "Ġv", + "ol" + ], + [ + "Ġens", + "ure" + ], + [ + "æ¬", + "§" + ], + [ + "æľīæīĢ", + "ä¸įåIJĮ" + ], + [ + "Ġknow", + "ledge" + ], + [ + "ç¢", + "į" + ], + [ + "åıįæĺł", + "äºĨ" + ], + [ + "è°", + "Ī" + ], + [ + "Ġo", + "pen" + ], + [ + "åĨľ", + "ä¸ļ" + ], + [ + "è¿ŀ", + "ç»Ń" + ], + [ + "éĶ", + "ģ" + ], + [ + "ĠAd", + "ditionally" + ], + [ + "ç»ı", + "åħ¸" + ], + [ + "Ġb", + "ro" + ], + [ + "æĮ", + "¥" + ], + [ + "ä¸ĭ", + "æĿ¥" + ], + [ + "ä¹Ł", + "åı¯èĥ½" + ], + [ + "ä¸ĭ", + "éĻį" + ], + [ + "éĽĨ", + "åIJĪ" + ], + [ + "ag", + "raph" + ], + [ + "Ġexp", + "lo" + ], + [ + "mer", + "ic" + ], + [ + "Ġl", + "en" + ], + [ + "éĽ", + "ª" + ], + [ + "Ñ", + "ĭ" + ], + [ + "Ġst", + "ructure" + ], + [ + "Ġman", + "age" + ], + [ + "æŀĦ", + "æĪIJ" + ], + [ + "ä»·å̼", + "è§Ĥ" + ], + [ + "T", + "P" + ], + [ + "ä¾", + "§" + ], + [ + "设计", + "ä¸Ģ个" + ], + [ + "åĬ©", + "æīĭ" + ], + [ + "Ġre", + "st" + ], + [ + "ä½ł", + "åı¯èĥ½éľĢè¦ģ" + ], + [ + "Ġde", + "v" + ], + [ + "åĮ", + "Ģ" + ], + [ + "P", + "ro" + ], + [ + "çŃī", + "æĸ¹å¼ı" + ], + [ + "çĩ", + "ĥ" + ], + [ + "Ġg", + "old" + ], + [ + "åıĤ", + "èĢĥ" + ], + [ + "o", + "ber" + ], + [ + "Ġ\\", + "," + ], + [ + "Ġdevelop", + "ed" + ], + [ + "è¯Ĺ", + "æŃĮ" + ], + [ + "c", + "ent" + ], + [ + "è¿", + "¹" + ], + [ + "大", + "åѦ" + ], + [ + "ĠD", + "e" + ], + [ + "cc", + "ess" + ], + [ + "Ġ", + "&" + ], + [ + "Ġm", + "us" + ], + [ + "æ°", + "¸" + ], + [ + "ct", + "or" + ], + [ + "æ¸", + "łéģĵ" + ], + [ + "éļIJç§ģ", + "ä¿ĿæĬ¤" + ], + [ + "acc", + "i" + ], + [ + "æĢ§", + "è´¨" + ], + [ + "æŃ£ç¡®", + "çļĦ" + ], + [ + "åį", + "±" + ], + [ + "åIJĪ", + "åIJĮ" + ], + [ + "æłĩ", + "é¢ĺ" + ], + [ + "b", + "e" + ], + [ + "è¿", + "ħ" + ], + [ + "åį", + "Ī" + ], + [ + "iv", + "ity" + ], + [ + "ib", + "on" + ], + [ + "æ³", + "Ħ" + ], + [ + "转", + "åĮĸ" + ], + [ + "éĺ¿éĩĮå·´å·´", + "äºij" + ], + [ + "ç͵", + "æ±ł" + ], + [ + "get", + "her" + ], + [ + "çļĦ", + "建议" + ], + [ + "Ġto", + "gether" + ], + [ + "a", + "de" + ], + [ + "Ġl", + "ibr" + ], + [ + "çļĦæı", + "IJ" + ], + [ + "Ġhour", + "s" + ], + [ + "ç»", + "©" + ], + [ + "大", + "åľ°" + ], + [ + "Ġle", + "vel" + ], + [ + "ibon", + "acci" + ], + [ + "éĺ³", + "åħī" + ], + [ + "Ġavail", + "able" + ], + [ + "Ġus", + "es" + ], + [ + "Ġanal", + "y" + ], + [ + "çļĦæĬ", + "ķ" + ], + [ + "åij¨", + "æľŁ" + ], + [ + "èĦļ", + "æľ¬" + ], + [ + "Ġident", + "ify" + ], + [ + "ä¹", + "ħ" + ], + [ + "Ñ", + "ı" + ], + [ + "æľ", + "Ŀ" + ], + [ + "ĠC", + "on" + ], + [ + "is", + "ion" + ], + [ + "å®ļ", + "åζ" + ], + [ + "éľĢè¦ģ", + "注æĦı" + ], + [ + "åħĥç´ł", + "çļĦ" + ], + [ + "Ġart", + "ificial" + ], + [ + "Ġma", + "x" + ], + [ + "ern", + "et" + ], + [ + "ãģ", + "Ł" + ], + [ + "çļĦç", + "©" + ], + [ + "å¹³", + "æĸ¹" + ], + [ + "æŁ", + "Ķ" + ], + [ + "I", + "N" + ], + [ + "羣", + "å®ŀ" + ], + [ + "æ²", + "¿" + ], + [ + "æĪIJ", + "å°±" + ], + [ + "ä¼ĺ", + "åħĪ" + ], + [ + "Ġres", + "ources" + ], + [ + "Ġb", + "as" + ], + [ + "å°", + "º" + ], + [ + "æIJ", + "Ń" + ], + [ + "åĮºåĿĹéĵ¾", + "æĬĢæľ¯" + ], + [ + "æī", + "®" + ], + [ + "Ġmon", + "th" + ], + [ + "å¥", + "ı" + ], + [ + "第", + "ä¸ī" + ], + [ + "Ġref", + "lect" + ], + [ + "çļĦä¸Ģ", + "éĥ¨åĪĨ" + ], + [ + "çļĦ", + "åģ¥åº·" + ], + [ + "è®°", + "ä½ı" + ], + [ + "梦", + "æĥ³" + ], + [ + "é¢Ħ", + "éĺ²" + ], + [ + "æķ", + "¸" + ], + [ + "Ġch", + "all" + ], + [ + "Ġcont", + "rib" + ], + [ + "S", + "ON" + ], + [ + "大", + "åŀĭ" + ], + [ + "缸", + "ä¼¼" + ], + [ + "Ġs", + "ense" + ], + [ + "an", + "ces" + ], + [ + "æ¼", + "ı" + ], + [ + "ç¥ŀç»ı", + "ç½ij绾" + ], + [ + "Ġb", + "ir" + ], + [ + "æľ¬", + "身" + ], + [ + "æ¶", + "µ" + ], + [ + "Ġgre", + "at" + ], + [ + "}", + "\\)" + ], + [ + "Ġgrow", + "th" + ], + [ + "Ġbe", + "h" + ], + [ + "Ġpar", + "agraph" + ], + [ + "è¿Ń", + "代" + ], + [ + "åĬĽ", + "çļĦ" + ], + [ + "å¹´", + "è½»" + ], + [ + "Ġc", + "all" + ], + [ + "Ġstep", + "s" + ], + [ + "3", + "3" + ], + [ + "ar", + "get" + ], + [ + "éĵ", + "º" + ], + [ + "åı¦", + "ä¸Ģ个" + ], + [ + "Ġexpl", + "ain" + ], + [ + ")", + "`" + ], + [ + "Ġan", + "n" + ], + [ + "ot", + "al" + ], + [ + "æīĢ", + "éľĢ" + ], + [ + "åį", + "ĸ" + ], + [ + "æ²", + "Ļ" + ], + [ + "Ġcir", + "cle" + ], + [ + "Ġclar", + "ify" + ], + [ + "m", + "an" + ], + [ + "i", + "ver" + ], + [ + "r", + "ame" + ], + [ + "èĢĮ", + "ä¸Ķ" + ], + [ + "èµĦ", + "产" + ], + [ + "ä¹", + "¡" + ], + [ + "Ġv", + "er" + ], + [ + "Ġ6", + "0" + ], + [ + "A", + "nd" + ], + [ + "ĠS", + "o" + ], + [ + "Ġ4", + "0" + ], + [ + "声", + "éŁ³" + ], + [ + "åĴĮ", + "æĬĢæľ¯" + ], + [ + "al", + "cul" + ], + [ + "æĭ", + "į" + ], + [ + "æ¯ı", + "次" + ], + [ + "æľī", + "åħ³" + ], + [ + "iv", + "al" + ], + [ + "çļĦç", + "Ł" + ], + [ + "èĩ³", + "å°ij" + ], + [ + "Ġre", + "du" + ], + [ + "æĹ¶", + "çļĦ" + ], + [ + "å¹´", + "çļĦ" + ], + [ + "Ġind", + "ust" + ], + [ + "Ġexper", + "ience" + ], + [ + "åĪ", + "¸" + ], + [ + "两", + "ç§į" + ], + [ + "æł¸", + "å¿ĥ" + ], + [ + "gor", + "ithm" + ], + [ + "æİĮ", + "æı¡" + ], + [ + "çģ", + "¾" + ], + [ + "大", + "å¤ļ" + ], + [ + "å·¥", + "ä¸ļ" + ], + [ + "ac", + "y" + ], + [ + "ãĢĭ", + "ï¼Ī" + ], + [ + "inu", + "x" + ], + [ + "9", + "0" + ], + [ + "è¿Ľ", + "å±ķ" + ], + [ + "çļĦæ¸", + "©" + ], + [ + "åħħ满", + "äºĨ" + ], + [ + "éŃ", + "Ķ" + ], + [ + "Ð", + "¼" + ], + [ + "Ġsu", + "bs" + ], + [ + "Python", + "çļĦ" + ], + [ + "åĪĨ", + "åŃIJ" + ], + [ + "S", + "T" + ], + [ + "æľī", + "äºĽ" + ], + [ + "温", + "æļĸ" + ], + [ + "Ġs", + "it" + ], + [ + "Ġdet", + "ailed" + ], + [ + "å¿", + "ĺ" + ], + [ + "Ġhe", + "ad" + ], + [ + "éĻ", + "Ħ" + ], + [ + "å®ĥ", + "çļĦ" + ], + [ + "æĸ¹", + "便" + ], + [ + "ĠJava", + "Script" + ], + [ + "Ġw", + "on" + ], + [ + "让", + "ä½ł" + ], + [ + "Ġtransl", + "ate" + ], + [ + "æĺ¯åIJ¦", + "æľī" + ], + [ + "Ġv", + "irtual" + ], + [ + "C", + "om" + ], + [ + "以", + "åħ¶" + ], + [ + "as", + "ure" + ], + [ + "ut", + "put" + ], + [ + "Ã", + "ł" + ], + [ + "Ġfollow", + "s" + ], + [ + "Ľ", + "åĽ´" + ], + [ + "ä¿ĥè¿Ľ", + "äºĨ" + ], + [ + "ä½ĵçݰ", + "äºĨ" + ], + [ + "æĹ¶", + "代" + ], + [ + "Ġmat", + "hemat" + ], + [ + "Ġf", + "uture" + ], + [ + "æĿİ", + "æĺİ" + ], + [ + "L", + "P" + ], + [ + "è¿", + "Ŀ" + ], + [ + "æĸ°", + "é²ľ" + ], + [ + "注", + "éĩį" + ], + [ + "Ġpat", + "tern" + ], + [ + "éļ¾", + "以" + ], + [ + "éĢīæĭ©", + "ä¸Ģ个" + ], + [ + "ç¼ĸ", + "çłģ" + ], + [ + "f", + "ace" + ], + [ + "o", + "id" + ], + [ + "ur", + "y" + ], + [ + "éĩį", + "è§Ĩ" + ], + [ + "çļĦ模", + "åŀĭ" + ], + [ + "è¿IJ", + "ç®Ĺ" + ], + [ + "c", + "er" + ], + [ + "i", + "od" + ], + [ + "Ġc", + "r" + ], + [ + "(", + "-" + ], + [ + "Ġnew", + "s" + ], + [ + "ĠFr", + "ance" + ], + [ + "åº", + "Ĭ" + ], + [ + "ä¸ĵ", + "å®¶" + ], + [ + "Ġrese", + "arch" + ], + [ + "Ġgre", + "ater" + ], + [ + "a", + "pe" + ], + [ + "åıª", + "æľī" + ], + [ + "åĵĪ", + "å¸Į" + ], + [ + "èģ", + "Ĭ" + ], + [ + "é«ĺ", + "级" + ], + [ + "èĶ", + "¬" + ], + [ + "å¢", + "Ļ" + ], + [ + "Ġspec", + "ial" + ], + [ + "Ġdirect", + "ly" + ], + [ + "å®īåħ¨", + "çļĦ" + ], + [ + "ç²", + "Ĵ" + ], + [ + "å¤ĩ", + "份" + ], + [ + "åĪ", + "º" + ], + [ + "Ġpr", + "ice" + ], + [ + "çĭ", + "Ĺ" + ], + [ + "Ġt", + "itle" + ], + [ + "æľī", + "åĵªäºĽ" + ], + [ + "Ġrelations", + "hip" + ], + [ + "Ñ", + "Į" + ], + [ + "ä¾", + "µ" + ], + [ + "çŃī", + "å¾ħ" + ], + [ + "ord", + "ing" + ], + [ + "åľ°", + "çĤ¹" + ], + [ + "Ġme", + "et" + ], + [ + "ä¼ł", + "éĢĴ" + ], + [ + "çļĦ", + "æĢģ度" + ], + [ + "æķħ", + "éļľ" + ], + [ + "Ġre", + "verse" + ], + [ + "et", + "ic" + ], + [ + "交", + "æį¢" + ], + [ + "Ġm", + "ust" + ], + [ + "é«ĺ", + "åħ´" + ], + [ + "ound", + "s" + ], + [ + "é¢Ħ", + "æľŁ" + ], + [ + "Ġfe", + "et" + ], + [ + "åĪĿ", + "åѦèĢħ" + ], + [ + "ä¼", + "Ļ" + ], + [ + "æıIJä¾Ľ", + "ä¸Ģ个" + ], + [ + "åİ", + "ħ" + ], + [ + "çļĦæµ", + "ģ" + ], + [ + "t", + "le" + ], + [ + "ç¼ĸç¨ĭ", + "è¯Ńè¨Ģ" + ], + [ + "åŃ£", + "èĬĤ" + ], + [ + "éĩıåŃIJ", + "计ç®Ĺ" + ], + [ + "u", + "ro" + ], + [ + "Ġse", + "a" + ], + [ + "æ¯Ķ", + "çī¹" + ], + [ + "æľī", + "éĻIJ" + ], + [ + "Ġar", + "g" + ], + [ + "F", + "ROM" + ], + [ + "Ġh", + "ome" + ], + [ + "Ġdisc", + "uss" + ], + [ + "åħ·", + "å¤ĩ" + ], + [ + "æļ", + "Ĺ" + ], + [ + "Ġpre", + "v" + ], + [ + "æī®", + "æ¼Ķ" + ], + [ + "åľ°", + "æĸ¹" + ], + [ + "åĮĹ", + "京" + ], + [ + "å®ģ", + "éĿĻ" + ], + [ + "è§Ħ", + "èĮĥ" + ], + [ + "社ä¼ļ", + "çļĦ" + ], + [ + "缮", + "å½ķ" + ], + [ + "åѦ", + "æľ¯" + ], + [ + "Ġm", + "ix" + ], + [ + "åľ¨", + "å¤ĦçIJĨ" + ], + [ + "ic", + "ro" + ], + [ + "ke", + "y" + ], + [ + "Ġwe", + "b" + ], + [ + "æĵįä½ľ", + "ç³»ç»Ł" + ], + [ + "¹", + "æŀľ" + ], + [ + "çī©", + "èģĶç½ij" + ], + [ + "æľįåĬ¡", + "çļĦ" + ], + [ + "æıIJ", + "åīį" + ], + [ + "çĤ", + "İ" + ], + [ + "åIJĮ", + "æł·" + ], + [ + "Ġem", + "ployees" + ], + [ + "Ġc", + "ut" + ], + [ + "åIJĪ", + "è§Ħ" + ], + [ + "çļĦ", + "éĢīæĭ©" + ], + [ + "å¥", + "¥" + ], + [ + "c", + "ol" + ], + [ + "ç§", + "Ģ" + ], + [ + "è¿Ľ", + "ç¨ĭ" + ], + [ + "}", + "}" + ], + [ + "æķĻ", + "åѦ" + ], + [ + "éĶ", + "ħ" + ], + [ + "u", + "c" + ], + [ + "ä¿¡", + "ç͍" + ], + [ + "ä¿¡æģ¯", + "çļĦ" + ], + [ + "çļĦæİ", + "¨" + ], + [ + "åĢ", + "į" + ], + [ + "éļľ", + "ç¢į" + ], + [ + "or", + "n" + ], + [ + "ĠI", + "nd" + ], + [ + "æľª", + "çŁ¥" + ], + [ + "çķ", + "ħ" + ], + [ + "è½»", + "è½»" + ], + [ + "ä¿Ŀ", + "éļľ" + ], + [ + "ĠA", + "pp" + ], + [ + "ä¸Ģ", + "ç³»åĪĹ" + ], + [ + "åĨ", + "¬" + ], + [ + "æĪij们", + "é¦ĸåħĪ" + ], + [ + "大å¤ļ", + "æķ°" + ], + [ + "çļĦ", + "产åĵģ" + ], + [ + "å½¢", + "çĬ¶" + ], + [ + "Ġallow", + "s" + ], + [ + "ol", + "ution" + ], + [ + "Ġs", + "ource" + ], + [ + "Ġo", + "cc" + ], + [ + "åIJĪ", + "æ³ķ" + ], + [ + "åľ¨", + "Pythonä¸Ń" + ], + [ + "大", + "约" + ], + [ + "Ġan", + "other" + ], + [ + "éĢIJ", + "æŃ¥" + ], + [ + "使ç͍", + "äºĨ" + ], + [ + "åĿĩ", + "åĮĢ" + ], + [ + "çļĦ", + "å¿ĥ" + ], + [ + "Ġw", + "ind" + ], + [ + "æĸ", + "Ĺ" + ], + [ + "Ġl", + "ower" + ], + [ + "è§£", + "æŀIJ" + ], + [ + "çĿ", + "£" + ], + [ + "å°", + "ļ" + ], + [ + "æģ", + "Ĵ" + ], + [ + "çĿ", + "¡çľł" + ], + [ + "ĠA", + "meric" + ], + [ + "è·", + "ij" + ], + [ + "âĢĶ", + "âĢĶ" + ], + [ + "ant", + "s" + ], + [ + "çīĽ", + "奶" + ], + [ + "æ°§", + "åĮĸ" + ], + [ + "m", + "od" + ], + [ + "å½¢", + "çļĦ" + ], + [ + "çļĦç©", + "º" + ], + [ + "Ð", + "²" + ], + [ + "al", + "es" + ], + [ + "an", + "a" + ], + [ + "æıIJä¾Ľ", + "æĽ´" + ], + [ + "Ġs", + "or" + ], + [ + "èĥ", + "ģ" + ], + [ + "i", + "et" + ], + [ + "ç§", + "Ĵ" + ], + [ + "ch", + "ange" + ], + [ + "Ġcount", + "ries" + ], + [ + "è¿ħ", + "éĢŁ" + ], + [ + "èͬ", + "èıľ" + ], + [ + "Ġth", + "ough" + ], + [ + "ĠC", + "EO" + ], + [ + "ide", + "red" + ], + [ + "èĭ", + "¹æŀľ" + ], + [ + "Ġmult", + "iply" + ], + [ + "Ġwrit", + "ing" + ], + [ + "j", + "or" + ], + [ + "çĥ", + "§" + ], + [ + "Ġtas", + "k" + ], + [ + "s", + "er" + ], + [ + "Ġdec", + "imal" + ], + [ + "int", + "er" + ], + [ + "æ³ķå¾ĭ", + "æ³ķè§Ħ" + ], + [ + "Ġphys", + "ical" + ], + [ + "Ġrad", + "ius" + ], + [ + "è·", + "ĥ" + ], + [ + "å°½", + "管" + ], + [ + "羣", + "æŃ£" + ], + [ + "æĢ»", + "ç»ĵ" + ], + [ + "å¯¹ä½ł", + "æľīæīĢ帮åĬ©" + ], + [ + "çļĦç²", + "¾" + ], + [ + "è§Ĩ", + "为" + ], + [ + "éĹ®é¢ĺ", + "çļĦ" + ], + [ + "Ġcons", + "idered" + ], + [ + "å¦ĤæŀľæĤ¨", + "æľī" + ], + [ + "Ġtop", + "ics" + ], + [ + "æĸĩ", + "æĺİ" + ], + [ + "Ġg", + "ra" + ], + [ + "è°", + "±" + ], + [ + "èĥ", + "ľ" + ], + [ + "åŁºæľ¬", + "çļĦ" + ], + [ + "dition", + "al" + ], + [ + "åı¯ä»¥", + "å°Ĩ" + ], + [ + "Ġre", + "d" + ], + [ + "ä¸ĭéĿ¢", + "æĺ¯ä¸Ģ个" + ], + [ + "æĹ¥å¸¸", + "çĶŁæ´»" + ], + [ + "å¦Ĥæŀľ", + "æľī" + ], + [ + "è¿ĺ", + "æľī" + ], + [ + "Ġpr", + "iv" + ], + [ + "å¨ģ", + "èĥģ" + ], + [ + "æĦŁ", + "åıĹ" + ], + [ + "åľ¨", + "ä¸ŃåĽ½" + ], + [ + "Ġadd", + "ition" + ], + [ + ")", + "\\)" + ], + [ + "ä¸ĸ", + "纪" + ], + [ + "ĠO", + "ct" + ], + [ + "讲", + "è¿°" + ], + [ + "Ġh", + "y" + ], + [ + "t", + "on" + ], + [ + "u", + "le" + ], + [ + "L", + "e" + ], + [ + "ĠA", + "re" + ], + [ + "Ġres", + "pect" + ], + [ + "ç«ŀäºī", + "åĬĽ" + ], + [ + "æī", + "ĺ" + ], + [ + "äºĨè§£", + "ä¸Ģä¸ĭ" + ], + [ + "读", + "åıĸ" + ], + [ + "以", + "æıIJé«ĺ" + ], + [ + "å°¤åħ¶", + "æĺ¯åľ¨" + ], + [ + "éĿ¢", + "çļĦ" + ], + [ + "读", + "èĢħ" + ], + [ + "ä¸ĵ注", + "äºİ" + ], + [ + "Ġtyp", + "es" + ], + [ + "2", + "9" + ], + [ + "A", + "re" + ], + [ + "使", + "åħ¶" + ], + [ + "am", + "b" + ], + [ + "Ġst", + "ill" + ], + [ + "å±ı", + "å¹ķ" + ], + [ + "çĤ", + "¼" + ], + [ + "æĽ´åĩĨç¡®", + "åľ°" + ], + [ + "Ġpot", + "ential" + ], + [ + "ç¡®ä¿Ŀ", + "æīĢæľī" + ], + [ + "W", + "e" + ], + [ + "åĮº", + "åĪ«" + ], + [ + "顺", + "åĪ©" + ], + [ + "åħ", + "Ń" + ], + [ + "线", + "ç¨ĭ" + ], + [ + "g", + "en" + ], + [ + "0", + "1" + ], + [ + "Ġhe", + "ight" + ], + [ + "Ġmo", + "on" + ], + [ + "vers", + "ed" + ], + [ + "èı", + "Į" + ], + [ + "ا", + "ÙĦ" + ], + [ + "]", + ")" + ], + [ + "vie", + "w" + ], + [ + "Ġit", + "er" + ], + [ + "Ġanswer", + "ing" + ], + [ + "l", + "ished" + ], + [ + "z", + "hou" + ], + [ + "æľŁ", + "æľĽ" + ], + [ + "l", + "ate" + ], + [ + "éĩįè¦ģ", + "çļĦæĺ¯" + ], + [ + "ç®Ģ", + "åĮĸ" + ], + [ + "k", + "es" + ], + [ + "åı¯ä»¥", + "æľīæķĪ" + ], + [ + "ä¸Ĭ", + "æīĢ" + ], + [ + "ĠThe", + "refore" + ], + [ + "L", + "E" + ], + [ + "çļĦæ¯", + "Ķ" + ], + [ + "èĩªåĬ¨", + "驾驶" + ], + [ + "Ġdatab", + "ase" + ], + [ + "ar", + "a" + ], + [ + "Ġoff", + "er" + ], + [ + "ist", + "ics" + ], + [ + "åı¯èĥ½ä¼ļ", + "导èĩ´" + ], + [ + "好", + "å¥ĩ" + ], + [ + "èĦ", + "±" + ], + [ + "çļĦç", + "Ī" + ], + [ + "èĤ", + "¥" + ], + [ + "Ġcontain", + "s" + ], + [ + "åºĶç͍", + "ä¸Ń" + ], + [ + "Ġun", + "its" + ], + [ + "åĴĮ", + "建议" + ], + [ + "çļĦ", + "ç»ĵæŀľ" + ], + [ + "ĠJ", + "ack" + ], + [ + "æī", + "°" + ], + [ + "使ç͍", + "çļĦ" + ], + [ + "æĮĩ", + "æłĩ" + ], + [ + "åĨį", + "次" + ], + [ + "Ġs", + "ong" + ], + [ + "ro", + "p" + ], + [ + "Ġrel", + "i" + ], + [ + "ren", + "ch" + ], + [ + "Ġ", + "ess" + ], + [ + "æİ¨", + "广" + ], + [ + "åĴĮ", + "æĸĩåĮĸ" + ], + [ + "çĤ", + "Ĵ" + ], + [ + "å¡ij", + "æĸĻ" + ], + [ + "A", + "N" + ], + [ + "m", + "ax" + ], + [ + "çļĦæ", + "¶" + ], + [ + "度", + "çļĦ" + ], + [ + "å®ĥ", + "ä¸įä»ħ" + ], + [ + "Ġnum", + "er" + ], + [ + "E", + "ach" + ], + [ + "Ġs", + "elf" + ], + [ + "éķ¿", + "æĹ¶éĹ´" + ], + [ + "ä»Ĭ", + "天" + ], + [ + "çĪ", + "¶" + ], + [ + "çļĦ", + "帮åĬ©" + ], + [ + "Ġst", + "at" + ], + [ + "and", + "as" + ], + [ + "Ġsy", + "stems" + ], + [ + "Ġadd", + "ress" + ], + [ + "Ġan", + "ything" + ], + [ + "n", + "ces" + ], + [ + "å¹", + "ħ" + ], + [ + "æī¾", + "åĩº" + ], + [ + "Ġnum", + "py" + ], + [ + "Ġf", + "low" + ], + [ + "ç§", + "Ł" + ], + [ + "åĶ", + "¯" + ], + [ + "éĹ®é¢ĺæĪĸ", + "éľĢè¦ģ" + ], + [ + "W", + "HERE" + ], + [ + "åĦ¿", + "ç«¥" + ], + [ + "Ġqu", + "ick" + ], + [ + "è¯į", + "æ±ĩ" + ], + [ + "6", + "4" + ], + [ + "am", + "ent" + ], + [ + "ĠA", + "r" + ], + [ + "ç»Ļ", + "å®ļçļĦ" + ], + [ + "ne", + "y" + ], + [ + "ä¸ĬæīĢ", + "è¿°" + ], + [ + "å¼", + "±" + ], + [ + "å¤ļæł·", + "æĢ§" + ], + [ + "为", + "ç͍æĪ·" + ], + [ + "Ġu", + "nt" + ], + [ + "Ġdescrib", + "e" + ], + [ + "çļĦ", + "åħī" + ], + [ + "åĬł", + "è½½" + ], + [ + "a", + "res" + ], + [ + "çļĦ", + "éķ¿åº¦" + ], + [ + "åĪĨ", + "è§£" + ], + [ + "èİ", + "ī" + ], + [ + "æĽ´å¥½åľ°", + "çIJĨè§£" + ], + [ + "a", + "w" + ], + [ + "id", + "ed" + ], + [ + "è¿ĩ", + "滤" + ], + [ + "20", + "23" + ], + [ + "éĿ¢", + "è¯ķ" + ], + [ + "仿", + "ä½Ľ" + ], + [ + "综", + "ä¸ĬæīĢè¿°" + ], + [ + "认", + "è¯Ĩ" + ], + [ + "产çĶŁ", + "äºĨ" + ], + [ + "åĽº", + "å®ļ" + ], + [ + "çļĦæ´»", + "åĬ¨" + ], + [ + "C", + "D" + ], + [ + "Ġp", + "ie" + ], + [ + "æķ°æį®", + "å®īåħ¨" + ], + [ + "æĦŁ", + "åıĹåΰ" + ], + [ + "èµĦ", + "æĸĻ" + ], + [ + "an", + "ation" + ], + [ + "Ġ+", + "=" + ], + [ + "iz", + "ing" + ], + [ + "Ġlet", + "ter" + ], + [ + "e", + "ad" + ], + [ + "èª", + "ī" + ], + [ + "b", + "ash" + ], + [ + "Ġf", + "our" + ], + [ + "Ġal", + "gorithm" + ], + [ + "U", + "n" + ], + [ + "ä¸Ģ", + "份" + ], + [ + "æĢ", + "Ģ" + ], + [ + "St", + "ring" + ], + [ + "or", + "k" + ], + [ + "ar", + "ily" + ], + [ + "é", + "¹" + ], + [ + "æĪij们", + "éľĢè¦ģ" + ], + [ + "æ¢", + "¯" + ], + [ + "åĬŀ", + "åħ¬" + ], + [ + "or", + "m" + ], + [ + "ä½ł", + "们" + ], + [ + "æ¯", + "Ľ" + ], + [ + "b", + "an" + ], + [ + "w", + "w" + ], + [ + "Ġsor", + "ry" + ], + [ + "æĿ¥", + "è¶Ĭ" + ], + [ + "ad", + "r" + ], + [ + "Ġa", + "im" + ], + [ + "h", + "ang" + ], + [ + "h", + "ai" + ], + [ + "帮åĬ©", + "ç͍æĪ·" + ], + [ + "åij¨", + "åĽ´" + ], + [ + "è¶Ĭ", + "æĿ¥è¶Ĭ" + ], + [ + "ç»", + "ķ" + ], + [ + "éĢļ常", + "éľĢè¦ģ" + ], + [ + "Ċ", + "ĊĠĠ" + ], + [ + "åħĪ", + "è¿Ľ" + ], + [ + "ĠOct", + "ober" + ], + [ + "g", + "o" + ], + [ + "ro", + "du" + ], + [ + "éĴ", + "±" + ], + [ + "Ù", + "Ĩ" + ], + [ + "请", + "éĹ®" + ], + [ + "Ġex", + "t" + ], + [ + "Ġim", + "age" + ], + [ + "æ¶Ī", + "èĢĹ" + ], + [ + "çļĦæŃ", + "¥" + ], + [ + "Ġeng", + "aging" + ], + [ + "J", + "ohn" + ], + [ + "ç»ı", + "è¿ĩ" + ], + [ + "æIJ", + "º" + ], + [ + "å¾Ī", + "é«ĺåħ´" + ], + [ + "A", + "r" + ], + [ + "åı¯ä»¥", + "å°Ŀè¯ķ" + ], + [ + "çļĦç", + "Ĭ" + ], + [ + "Ġcolumn", + "s" + ], + [ + "çݯå¢ĥ", + "ä¿ĿæĬ¤" + ], + [ + "éĴ", + "¥" + ], + [ + "ä¸Ģ", + "ä½į" + ], + [ + "éĢļè¿ĩ", + "è¿ĻäºĽ" + ], + [ + "Ġsent", + "ences" + ], + [ + "å±Ĥ", + "次" + ], + [ + "an", + "ish" + ], + [ + "失", + "è´¥" + ], + [ + "ou", + "n" + ], + [ + "ï¼ļ", + "**" + ], + [ + "Ġwe", + "ek" + ], + [ + "in", + "ary" + ], + [ + "Ġcre", + "ative" + ], + [ + "åĩº", + "äºĨ" + ], + [ + "ver", + "n" + ], + [ + "转", + "åŀĭ" + ], + [ + "7", + "5" + ], + [ + "æįŁ", + "失" + ], + [ + "åĴĮ", + "使ç͍" + ], + [ + "Ġwrit", + "ten" + ], + [ + "Ġd", + "em" + ], + [ + "认", + "è¯ģ" + ], + [ + "ĠÐ", + "²" + ], + [ + "A", + "P" + ], + [ + "åĨħ", + "ç½®" + ], + [ + "Ġwor", + "ks" + ], + [ + "ç¾İ", + "好" + ], + [ + "çļĦ", + "ç͍æĪ·" + ], + [ + "å¾®", + "ä¿¡" + ], + [ + "å®", + "£" + ], + [ + "Ġiss", + "ues" + ], + [ + ":", + "\"," + ], + [ + "Ġal", + "ong" + ], + [ + "av", + "or" + ], + [ + "a", + "king" + ], + [ + "Ġprint", + "s" + ], + [ + "Ġstart", + "ed" + ], + [ + "Ġkil", + "om" + ], + [ + "æĪIJ", + "åĪĨ" + ], + [ + "被", + "称为" + ], + [ + "驱", + "åĬ¨" + ], + [ + "R", + "L" + ], + [ + "ä¸į", + "è¶³" + ], + [ + "Ð", + "´" + ], + [ + "è", + "²" + ], + [ + "çļĦ", + "åı¯" + ], + [ + "Ġf", + "lo" + ], + [ + "æİĴ", + "æĶ¾" + ], + [ + "Ġbre", + "ak" + ], + [ + "A", + "n" + ], + [ + "Ġc", + "ru" + ], + [ + "ä¸ĭ", + "è½½" + ], + [ + "äºĭ", + "项" + ], + [ + "è¿", + "·" + ], + [ + "æŃ£", + "å¼ı" + ], + [ + "éĴ", + "®" + ], + [ + "Ġd", + "en" + ], + [ + "åĩı", + "è½»" + ], + [ + "å®", + "«" + ], + [ + "M", + "y" + ], + [ + "S", + "C" + ], + [ + "alcul", + "ate" + ], + [ + "ro", + "l" + ], + [ + "æıIJä¾Ľ", + "æĽ´åĬł" + ], + [ + "Ġmed", + "ia" + ], + [ + "ang", + "zhou" + ], + [ + "æĽ¿", + "æį¢" + ], + [ + "f", + "unction" + ], + [ + "er", + "o" + ], + [ + "è½", + "´" + ], + [ + "éª", + "¨" + ], + [ + "am", + "p" + ], + [ + "è°", + "·" + ], + [ + "ë", + "Ĭ" + ], + [ + "Ġem", + "ot" + ], + [ + "Ġnetwor", + "k" + ], + [ + "Ġb", + "ooks" + ], + [ + "åı¯ä»¥", + "åĩıå°ij" + ], + [ + "éĩĬ", + "æĶ¾" + ], + [ + "f", + "rom" + ], + [ + "ĠP", + "ar" + ], + [ + "èĴ", + "¸" + ], + [ + "Ġsumm", + "ary" + ], + [ + "now", + "n" + ], + [ + "åĴĮ", + "ç»´æĬ¤" + ], + [ + "æĹł", + "éĻIJ" + ], + [ + "åıĮ", + "æĸ¹" + ], + [ + "ä¸Ģ", + "èά" + ], + [ + "æĹ", + "¦" + ], + [ + "ç³»ç»Ł", + "ä¸Ń" + ], + [ + "Ġf", + "ull" + ], + [ + "Ġm", + "iss" + ], + [ + "Ġlet", + "ters" + ], + [ + "çα", + "好" + ], + [ + "u", + "ation" + ], + [ + "çļĦæ", + "ĭ" + ], + [ + "åī", + "ª" + ], + [ + "åĽ¾", + "å½¢" + ], + [ + "æĪĺ", + "çķ¥" + ], + [ + "Ġachie", + "ve" + ], + [ + "C", + "l" + ], + [ + "åĴĮ", + "ä¼ĺåĮĸ" + ], + [ + "è¿ĩ", + "åİ»" + ], + [ + "éĢĤ", + "éĩı" + ], + [ + "ä¸įä»ħ", + "ä»ħ" + ], + [ + "du", + "le" + ], + [ + "以ä¸ĭæĺ¯ä¸ĢäºĽ", + "建议" + ], + [ + "åı", + "ī" + ], + [ + "é", + "½" + ], + [ + "Ġ**", + "ãĢĬ" + ], + [ + "çļĦæī", + "ĭ" + ], + [ + "幸", + "ç¦ı" + ], + [ + "çĶ", + "ľ" + ], + [ + "æĹ¥", + "å¿Ĺ" + ], + [ + "al", + "k" + ], + [ + "Ġle", + "g" + ], + [ + "ä¾ĭåŃIJ", + "ä¸Ń" + ], + [ + "Ġen", + "c" + ], + [ + "ç§ijåѦ", + "å®¶" + ], + [ + "欢è¿İ", + "éļıæĹ¶" + ], + [ + "çīĪ", + "æĿĥ" + ], + [ + "ĊĠĠĠĠ", + "ĊĠĠĠ" + ], + [ + "S", + "p" + ], + [ + "Ġinit", + "ial" + ], + [ + "Ġs", + "ure" + ], + [ + "Ġf", + "ac" + ], + [ + "åĩ", + "Ŀ" + ], + [ + "å¿", + "½" + ], + [ + "Ġtransl", + "ation" + ], + [ + "Ġfam", + "ily" + ], + [ + "便", + "æį·" + ], + [ + "Ġconvers", + "ations" + ], + [ + "æļ", + "´" + ], + [ + "ĠG", + "re" + ], + [ + "r", + "ation" + ], + [ + "¹", + "é¥" + ], + [ + "è¿Ļ", + "两个" + ], + [ + "ä¼¼", + "ä¹İ" + ], + [ + "æµ", + "®" + ], + [ + "¹é¥", + "ª" + ], + [ + "åĽŀ", + "æĶ¶" + ], + [ + "N", + "LP" + ], + [ + "æĭ", + "¼" + ], + [ + "Ġd", + "r" + ], + [ + "Ġexper", + "ien" + ], + [ + "æĵ", + "İ" + ], + [ + "m", + "ed" + ], + [ + "Ġprof", + "ess" + ], + [ + "F", + "irst" + ], + [ + "Ġinf", + "lu" + ], + [ + "å¯", + "¸" + ], + [ + "ä¸į", + "ç¡®å®ļ" + ], + [ + "I", + "nt" + ], + [ + "ä¼ł", + "æĦŁ" + ], + [ + "Ġfunction", + "s" + ], + [ + "å£", + "¤" + ], + [ + "åĽ¢éĺŁ", + "åIJĪä½ľ" + ], + [ + "以ä¸ĭåĩł", + "ç§į" + ], + [ + "l", + "ike" + ], + [ + "åĬ¨", + "ä½ľ" + ], + [ + "é£ŀ", + "è¡Į" + ], + [ + "Ġd", + "ream" + ], + [ + "èĢ", + "³" + ], + [ + "å¼ķ", + "æĵİ" + ], + [ + "Ġp", + "a" + ], + [ + "å¤ļ", + "æĸ¹éĿ¢" + ], + [ + "产", + "ä¸ļ" + ], + [ + "W", + "here" + ], + [ + "Ġa", + "uth" + ], + [ + "åįĩ", + "级" + ], + [ + "æ´", + "¾" + ], + [ + "Ġgre", + "en" + ], + [ + "ç¾İ", + "åij³" + ], + [ + "os", + "p" + ], + [ + "æĹ¶", + "åħī" + ], + [ + "end", + "s" + ], + [ + "Ġcult", + "ural" + ], + [ + "Ġkilom", + "eters" + ], + [ + "ãĤ", + "Į" + ], + [ + "æ¥", + "¼" + ], + [ + "e", + "ch" + ], + [ + "æ", + "¾" + ], + [ + "èĩª", + "身" + ], + [ + "åŃIJ", + "çļĦ" + ], + [ + "åī", + "©" + ], + [ + "ff", + "ic" + ], + [ + "7", + "0" + ], + [ + "Ġf", + "ra" + ], + [ + "Ġqu", + "e" + ], + [ + "产åĵģ", + "çļĦ" + ], + [ + "ëĭ", + "¤" + ], + [ + "Ġst", + "re" + ], + [ + ")", + "ï¼Į" + ], + [ + "S", + "ub" + ], + [ + "çĽij", + "çĿ£" + ], + [ + "it", + "ing" + ], + [ + "ä½ĵ", + "èĤ²" + ], + [ + "è¿Ļ段", + "代çłģ" + ], + [ + "è´·", + "款" + ], + [ + "å®¶", + "å±ħ" + ], + [ + "Ġsh", + "ow" + ], + [ + "AT", + "E" + ], + [ + "iction", + "ary" + ], + [ + "çĶ", + "·" + ], + [ + "im", + "plify" + ], + [ + "æµģ", + "éĩı" + ], + [ + "ç¾İ", + "丽" + ], + [ + "Ġsc", + "ience" + ], + [ + "Ġoper", + "ations" + ], + [ + "群", + "ä½ĵ" + ], + [ + "Ġmin", + "utes" + ], + [ + "i", + "um" + ], + [ + "æĹ¶éĹ´", + "åĴĮ" + ], + [ + "Ġmain", + "tain" + ], + [ + "erv", + "ice" + ], + [ + "ä¼Ļ", + "ä¼´" + ], + [ + "2", + "8" + ], + [ + "ä½ľ", + "èĢħ" + ], + [ + "Ġmo", + "vie" + ], + [ + "{", + "\\" + ], + [ + "Ġdiffere", + "nce" + ], + [ + "è´Ł", + "éĿ¢" + ], + [ + "Ġ", + "éĢīæĭ©" + ], + [ + "le", + "ft" + ], + [ + "éĽ", + "ħ" + ], + [ + "ç͵", + "è¯Ŀ" + ], + [ + "Ġcons", + "ider" + ], + [ + "æİ¥", + "触" + ], + [ + "c", + "el" + ], + [ + "Wh", + "y" + ], + [ + "ĠF", + "rench" + ], + [ + "Ġpre", + "c" + ], + [ + "Ġlead", + "ership" + ], + [ + "为", + "æĤ¨" + ], + [ + "åĪĹ", + "åĩº" + ], + [ + "R", + "E" + ], + [ + "ç½ij", + "页" + ], + [ + "W", + "indows" + ], + [ + "ä¿Ŀ", + "çķĻ" + ], + [ + "æĮī", + "éĴ®" + ], + [ + "çļĦæ¦Ĥ", + "çİĩ" + ], + [ + "åı£", + "åij³" + ], + [ + "Ġmat", + "rix" + ], + [ + "How", + "ever" + ], + [ + "ĠÐ", + "¿" + ], + [ + "èĮĥåĽ´", + "åĨħ" + ], + [ + "Ġhist", + "or" + ], + [ + "çĹĩ", + "çĬ¶" + ], + [ + "æŃ", + "»" + ], + [ + "çī©", + "ä½ĵ" + ], + [ + "æīĭ", + "段" + ], + [ + "ä¹IJ", + "æĦı" + ], + [ + "ä¼łæĦŁ", + "åύ" + ], + [ + "æķ", + "¢" + ], + [ + "Ġma", + "jor" + ], + [ + "Ġloc", + "al" + ], + [ + "å¥", + "ij" + ], + [ + "Ġpro", + "te" + ], + [ + "æľ¬", + "书" + ], + [ + "ç»ı", + "常" + ], + [ + "åľŁ", + "壤" + ], + [ + "Ġsymb", + "ol" + ], + [ + "æľŁ", + "å¾ħ" + ], + [ + "ä¹IJ", + "è¶£" + ], + [ + "Ġcont", + "in" + ], + [ + "Ġit", + "ems" + ], + [ + "çı", + "į" + ], + [ + "å³", + "°" + ], + [ + "D", + "P" + ], + [ + "Ġp", + "red" + ], + [ + "Ġ20", + "22" + ], + [ + "æıĴ", + "åħ¥" + ], + [ + "l", + "st" + ], + [ + "Ġ2", + "1" + ], + [ + "åī", + "²" + ], + [ + "èµĦæºIJ", + "çļĦ" + ], + [ + "éĤ", + "Ģ" + ], + [ + "è¿Ļ", + "åı¥è¯Ŀ" + ], + [ + "è¾ĥ", + "é«ĺ" + ], + [ + "çļĦæ¸", + "¸" + ], + [ + "æ±", + "ģ" + ], + [ + "ill", + "s" + ], + [ + "æĬķèµĦ", + "èĢħ" + ], + [ + "åı¯èĥ½", + "导èĩ´" + ], + [ + "ç±»", + "åĪ«" + ], + [ + "éĿŀ常", + "éĩįè¦ģçļĦ" + ], + [ + "Ġpass", + "word" + ], + [ + "Ġear", + "ly" + ], + [ + "Ġha", + "ven" + ], + [ + "åĻ", + "ª" + ], + [ + "éģĵ", + "è·¯" + ], + [ + "Ġlear", + "n" + ], + [ + "æ¿Ģ", + "åĬ±" + ], + [ + "åĩº", + "è¡Į" + ], + [ + "e", + "e" + ], + [ + "ra", + "ft" + ], + [ + "pl", + "ot" + ], + [ + "åĴĮ", + "çIJĨè§£" + ], + [ + "Ġele", + "ct" + ], + [ + "n", + "ect" + ], + [ + "Ġfig", + "ure" + ], + [ + "çļĦæĶ¯", + "æĮģ" + ], + [ + "Ġspec", + "ify" + ], + [ + "ate", + "g" + ], + [ + "大", + "èĦij" + ], + [ + "ĠH", + "angzhou" + ], + [ + "æĢ»", + "æĺ¯" + ], + [ + "Ġmod", + "ern" + ], + [ + "ëĬ", + "Ķ" + ], + [ + "Ġ\\)", + ":" + ], + [ + "Ġcru", + "cial" + ], + [ + "æľĢ", + "æĸ°" + ], + [ + "m", + "p" + ], + [ + "è§£", + "çŃĶ" + ], + [ + "ir", + "d" + ], + [ + "ĠĠĠĠ", + "ĠĠĠĠ" + ], + [ + "åı¯ä»¥", + "被" + ], + [ + "æŁIJ", + "ç§į" + ], + [ + "ç³", + "ķ" + ], + [ + "Ġconst", + "ant" + ], + [ + "4", + "8" + ], + [ + "A", + "C" + ], + [ + "éľĢè¦ģ", + "èĢĥèĻij" + ], + [ + "Ġex", + "ec" + ], + [ + "æľŁ", + "éĹ´" + ], + [ + "ä¼ĺ", + "æĥł" + ], + [ + "Ġe", + "t" + ], + [ + "缸", + "ä¿¡" + ], + [ + "ĠE", + "l" + ], + [ + "Ġper", + "iod" + ], + [ + "Ġse", + "cre" + ], + [ + "æĪijæĥ³", + "äºĨè§£ä¸Ģä¸ĭ" + ], + [ + "带æĿ¥", + "äºĨ" + ], + [ + "Ġapplic", + "ation" + ], + [ + "çļĦ", + "åĩĨç¡®" + ], + [ + "åįķ", + "åħĥ" + ], + [ + "èĢĥ", + "è¯ķ" + ], + [ + "ĠU", + "S" + ], + [ + "åĪĽ", + "ä¸ļ" + ], + [ + "æĦŁ", + "åĴĮ" + ], + [ + "询", + "éĹ®" + ], + [ + "éĽĨ", + "ä¸Ń" + ], + [ + "ap", + "an" + ], + [ + "P", + "r" + ], + [ + "æĹ", + "§" + ], + [ + "è´", + "Ŀ" + ], + [ + "à¸", + "²" + ], + [ + "æıIJ", + "éĨĴ" + ], + [ + "è°ĥ", + "èĬĤ" + ], + [ + "åı¤", + "èĢģ" + ], + [ + "Ġrecogn", + "ized" + ], + [ + "稳å®ļ", + "æĢ§" + ], + [ + "åıij", + "æĮ¥" + ], + [ + "I", + "T" + ], + [ + "å", + "ĺ" + ], + [ + "åľ¨", + "使ç͍" + ], + [ + "åıijå±ķ", + "çļĦ" + ], + [ + "æĭ", + "Į" + ], + [ + "çĿ", + "Ľ" + ], + [ + "Ġch", + "ild" + ], + [ + "ï¼Į", + "\"" + ], + [ + "çļĦæ", + "Ľ" + ], + [ + "å¿«", + "ä¹IJ" + ], + [ + "Ġag", + "ain" + ], + [ + "Ġf", + "requ" + ], + [ + "çļĦæ", + "ĵ" + ], + [ + "为", + "ä½ł" + ], + [ + "审", + "æŁ¥" + ], + [ + "ĠI", + "s" + ], + [ + "åIJİ", + "ç»Ń" + ], + [ + "Ġtra", + "ditional" + ], + [ + "Ġc", + "rit" + ], + [ + "ä¸Ģ", + "æĹ¦" + ], + [ + "èĥ½åĬĽ", + "åĴĮ" + ], + [ + "he", + "ad" + ], + [ + "å¿ĥçIJĨ", + "åģ¥åº·" + ], + [ + "Ġex", + "ist" + ], + [ + "Ġsu", + "ccess" + ], + [ + "强", + "大" + ], + [ + "æĿĥ", + "çĽĬ" + ], + [ + "è¾¹", + "ç¼ĺ" + ], + [ + "at", + "ch" + ], + [ + "ol", + "l" + ], + [ + "å͝", + "ä¸Ģ" + ], + [ + "çļĦçĬ", + "¶" + ], + [ + "Ġn", + "on" + ], + [ + "éĢļè¿ĩ", + "åĪĨæŀIJ" + ], + [ + "æ°´", + "æŀľ" + ], + [ + "çļĦæĮĩ", + "导" + ], + [ + "G", + "oogle" + ], + [ + "çļĦéĩįè¦ģ", + "æĢ§" + ], + [ + "Ġb", + "en" + ], + [ + "s", + "in" + ], + [ + "å¤", + "«" + ], + [ + "çĥ", + "Ł" + ], + [ + "æıIJ", + "交" + ], + [ + "æĽ´", + "æľīæķĪåľ°" + ], + [ + "æĹł", + "éľĢ" + ], + [ + "Ġse", + "arch" + ], + [ + "çIJĨ", + "念" + ], + [ + "Ġqu", + "adr" + ], + [ + "èģĮ", + "ä½į" + ], + [ + "Ġsumm", + "ar" + ], + [ + "Ġchall", + "eng" + ], + [ + "å°±", + "ä¸ļ" + ], + [ + "c", + "m" + ], + [ + "æĦŁ", + "æŁĵ" + ], + [ + "åij", + "¢" + ], + [ + "Ġderiv", + "ative" + ], + [ + "èµ", + "ı" + ], + [ + "Ġcon", + "sole" + ], + [ + "ç͵", + "åķĨ" + ], + [ + "ç¥ŀ", + "ç§ĺ" + ], + [ + "Ġdist", + "ance" + ], + [ + "ĠSp", + "anish" + ], + [ + "Ġeffic", + "ient" + ], + [ + "ç½", + "ª" + ], + [ + "å̼", + "å¾Ĺ" + ], + [ + "åĽ¢éĺŁ", + "çļĦ" + ], + [ + "æĶ¿", + "æ²»" + ], + [ + "hel", + "lo" + ], + [ + "a", + "ction" + ], + [ + "le", + "x" + ], + [ + "oo", + "se" + ], + [ + "Ġfound", + "er" + ], + [ + "大", + "äºİ" + ], + [ + "Ġstud", + "ents" + ], + [ + "çĦ¦", + "èĻij" + ], + [ + "ä½Ĩ", + "åľ¨" + ], + [ + "Ġcustom", + "er" + ], + [ + ":", + "//" + ], + [ + "A", + "M" + ], + [ + "å§", + "ĵ" + ], + [ + "æ°ĶåĢĻ", + "åıĺåĮĸ" + ], + [ + "Ġeconom", + "ic" + ], + [ + "s", + "ion" + ], + [ + "ä¸Ģ", + "å®ļçļĦ" + ], + [ + "éħ", + "±" + ], + [ + "Ġg", + "en" + ], + [ + "Ġd", + "ance" + ], + [ + "èĦ", + "ī" + ], + [ + "表", + "æĺİ" + ], + [ + "èĩªçĦ¶", + "çļĦ" + ], + [ + "Ġcont", + "inu" + ], + [ + "à", + "¦" + ], + [ + "ener", + "ate" + ], + [ + "éĤĢ", + "请" + ], + [ + "Ġqu", + "ant" + ], + [ + "åķĨ", + "åĬ¡" + ], + [ + "认", + "çŁ¥" + ], + [ + "Ġke", + "ep" + ], + [ + "en", + "n" + ], + [ + "éĺ¿éĩĮäºij", + "çļĦ" + ], + [ + "Ġindividual", + "s" + ], + [ + "Ġc", + "ertain" + ], + [ + "ad", + "d" + ], + [ + "Ġsu", + "st" + ], + [ + "end", + "er" + ], + [ + "Ġ", + "æµĭè¯ķ" + ], + [ + "è¿", + "Ł" + ], + [ + "åī", + "¯" + ], + [ + "us", + "ion" + ], + [ + "so", + "ft" + ], + [ + "Ġlim", + "it" + ], + [ + "Ñ", + "ĩ" + ], + [ + "è¿", + "ģ" + ], + [ + "æĿ¥", + "计ç®Ĺ" + ], + [ + "ight", + "s" + ], + [ + "æ³", + "³" + ], + [ + "çļĦæĻº", + "èĥ½" + ], + [ + "çļĦæĬķ", + "èµĦ" + ], + [ + "N", + "o" + ], + [ + "帮åĬ©", + "ä¼ģä¸ļ" + ], + [ + "Ġresult", + "s" + ], + [ + "æ", + "ĩ" + ], + [ + "æķ°æį®", + "å¤ĦçIJĨ" + ], + [ + "ĠP", + "er" + ], + [ + "Ġfam", + "ous" + ], + [ + "åħ¬", + "åĽŃ" + ], + [ + "çļĦæ", + "Ł" + ], + [ + "åħ³", + "éĹŃ" + ], + [ + "请", + "åijĬè¯īæĪij" + ], + [ + "os", + "s" + ], + [ + "Ġ<", + "=" + ], + [ + "çł´", + "åĿı" + ], + [ + "çĶŁ", + "ç´ł" + ], + [ + "æĪij们", + "使ç͍" + ], + [ + "w", + "here" + ], + [ + "ä¸Ģ", + "天" + ], + [ + "æİ¥", + "è¿ij" + ], + [ + "EC", + "S" + ], + [ + "c", + "ient" + ], + [ + "ĠS", + "he" + ], + [ + "f", + "ile" + ], + [ + "olog", + "y" + ], + [ + "ç¼ĸåĨĻ", + "ä¸Ģ个" + ], + [ + "}", + "}{" + ], + [ + "åı¯èĥ½", + "åľ¨" + ], + [ + "Ñģ", + "ÑĤ" + ], + [ + "Ġconvers", + "ation" + ], + [ + "Ġmus", + "ic" + ], + [ + "çļĦ", + "åİĨåı²" + ], + [ + "缸åºĶ", + "çļĦ" + ], + [ + "è¿Ļ", + "åĮħæĭ¬" + ], + [ + "æŃ", + "¦" + ], + [ + "åħ³éĶ®", + "è¯į" + ], + [ + "Ġtra", + "vel" + ], + [ + "Ġsust", + "ain" + ], + [ + "æµıè§Ī", + "åύ" + ], + [ + "\"", + "\"" + ], + [ + "çļĦç", + "¥" + ], + [ + "Ġdes", + "cript" + ], + [ + "Ġproduct", + "s" + ], + [ + "f", + "ect" + ], + [ + "Ġab", + "ove" + ], + [ + "Ġsub", + "ject" + ], + [ + "Ġre", + "ce" + ], + [ + "æĪIJ", + "çĨŁ" + ], + [ + "è¾¾", + "æij©" + ], + [ + "è´¦", + "åı·" + ], + [ + "L", + "L" + ], + [ + "Ġh", + "o" + ], + [ + "èį", + "·" + ], + [ + "åĽłä¸º", + "å®ĥ" + ], + [ + "in", + "a" + ], + [ + "ç´§", + "å¼ł" + ], + [ + "è°", + "ľ" + ], + [ + "æĶ¾", + "åľ¨" + ], + [ + "Ġs", + "at" + ], + [ + "Ġcan", + "not" + ], + [ + "èĤĮ", + "èĤī" + ], + [ + "a", + "us" + ], + [ + "æıIJ", + "示" + ], + [ + "åľ¨è¿Ļ个", + "ä¾ĭåŃIJä¸Ń" + ], + [ + "Ġc", + "y" + ], + [ + "Ġ3", + "6" + ], + [ + "ad", + "em" + ], + [ + "èıľ", + "åįķ" + ], + [ + "çļĦæĵ", + "įä½ľ" + ], + [ + "cl", + "ass" + ], + [ + "è¾ĥ", + "ä½İ" + ], + [ + "g", + "ative" + ], + [ + "p", + "o" + ], + [ + "å¼", + "ĥ" + ], + [ + "Ù", + "Ī" + ], + [ + "ç", + "Ĵ" + ], + [ + "re", + "l" + ], + [ + "ent", + "ly" + ], + [ + "æ¤", + "Ĵ" + ], + [ + "æ¶µ", + "çĽĸ" + ], + [ + "an", + "n" + ], + [ + "便", + "åĪ©" + ], + [ + "çļĦæŃ¥", + "骤" + ], + [ + "6", + "7" + ], + [ + "Ġ", + "." + ], + [ + "åĢ", + "Ł" + ], + [ + "çļĦ", + "çݯå¢ĥ" + ], + [ + "åĽŀ", + "å½Ĵ" + ], + [ + "Ġcol", + "ors" + ], + [ + "Ġfriend", + "s" + ], + [ + "ht", + "ml" + ], + [ + "æĭ", + "ĵ" + ], + [ + "éĿŀ常", + "æľī" + ], + [ + "æĪĸ", + "æľįåĬ¡" + ], + [ + "è", + "©" + ], + [ + "ä¹", + "ı" + ], + [ + "åľ¨", + "ä¸įåIJĮ" + ], + [ + "审", + "计" + ], + [ + "1", + "23" + ], + [ + "åĵª", + "个" + ], + [ + "å²", + "Ľ" + ], + [ + "Ġcomple", + "te" + ], + [ + "Ġprocess", + "ing" + ], + [ + "ĠSh", + "ang" + ], + [ + "Ġcomprehen", + "sive" + ], + [ + "ou", + "se" + ], + [ + "å½", + "¼" + ], + [ + "åIJĪ", + "约" + ], + [ + "æ¡", + "Į" + ], + [ + "æĮģç»Ń", + "åŃ¦ä¹ł" + ], + [ + "Ġind", + "ic" + ], + [ + "Ġbu", + "ild" + ], + [ + "HT", + "ML" + ], + [ + "æ³Ħ", + "éľ²" + ], + [ + "Ġcom", + "b" + ], + [ + "ç", + "ĺ" + ], + [ + "ç͍", + "æĢ§" + ], + [ + "å·²", + "çŁ¥" + ], + [ + "ĠY", + "ork" + ], + [ + "æĽ´", + "容æĺĵ" + ], + [ + "符", + "åı·" + ], + [ + "æ¶Ī", + "æģ¯" + ], + [ + "Ġw", + "arm" + ], + [ + "çº", + "¯" + ], + [ + "åĮ»", + "åѦ" + ], + [ + "å¤Ħ", + "äºİ" + ], + [ + "d", + "a" + ], + [ + "Ġf", + "all" + ], + [ + "Ġl", + "iter" + ], + [ + "ĠP", + "l" + ], + [ + "Ġc", + "ur" + ], + [ + "çļĦ", + "设计" + ], + [ + "ĠO", + "r" + ], + [ + "ç»ĦæĪIJ", + "éĥ¨åĪĨ" + ], + [ + "ç¼ĵ", + "è§£" + ], + [ + "Ġprim", + "arily" + ], + [ + "åĨħ", + "å¿ĥ" + ], + [ + "çIJĨ", + "æĥ³" + ], + [ + "åĩº", + "çļĦ" + ], + [ + "æ±", + "¤" + ], + [ + "表", + "æł¼" + ], + [ + "erm", + "an" + ], + [ + "ht", + "t" + ], + [ + "è¿Ļ", + "使å¾Ĺ" + ], + [ + "æ¡Ī", + "ä¾ĭ" + ], + [ + "å©", + "ļ" + ], + [ + "éľĢè¦ģ注æĦı", + "çļĦæĺ¯" + ], + [ + "Ġint", + "rodu" + ], + [ + "åİ»", + "éϤ" + ], + [ + "Ġcompan", + "ies" + ], + [ + "d", + "own" + ], + [ + "Ġex", + "change" + ], + [ + "åĴĮ", + "åıijå±ķ" + ], + [ + "å¾Ī", + "ä¹IJæĦı" + ], + [ + "é¸", + "£" + ], + [ + "Ġsubs", + "idi" + ], + [ + "æĽ´", + "é«ĺæķĪ" + ], + [ + "个人", + "ä¿¡æģ¯" + ], + [ + "Python", + "代çłģ" + ], + [ + "ä¸ĭéĿ¢", + "æĺ¯" + ], + [ + "g", + "ing" + ], + [ + "æĮĩ", + "å®ļ" + ], + [ + "åį«", + "çĶŁ" + ], + [ + "æĻ", + "¨" + ], + [ + "çļĦç", + "Ŀ" + ], + [ + "åºĶç͍", + "åľºæĻ¯" + ], + [ + "i", + "or" + ], + [ + "æī", + "«" + ], + [ + "ç͍æĪ·", + "åı¯ä»¥" + ], + [ + "ĠG", + "oogle" + ], + [ + "o", + "in" + ], + [ + "ä¸į", + "éľĢè¦ģ" + ], + [ + "æıIJä¾Ľ", + "帮åĬ©" + ], + [ + "es", + "p" + ], + [ + "æıIJä¾Ľ", + "çļĦ" + ], + [ + "ç»Ļ", + "äºĪ" + ], + [ + "ĠZ", + "hang" + ], + [ + "ëĭ", + "Ī" + ], + [ + "åı¯èĥ½", + "æľī" + ], + [ + "Ġst", + "yle" + ] + ] + } +} \ No newline at end of file diff --git a/model/tokenizer_config.json b/model/tokenizer_config.json new file mode 100644 index 0000000..e46c14a --- /dev/null +++ b/model/tokenizer_config.json @@ -0,0 +1,335 @@ +{ + "add_bos_token": false, + "add_eos_token": false, + "add_prefix_space": false, + "added_tokens_decoder": { + "0": { + "content": "<|endoftext|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "1": { + "content": "<|im_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "2": { + "content": "<|im_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "3": { + "content": "<|object_ref_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "4": { + "content": "<|object_ref_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "5": { + "content": "<|box_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "6": { + "content": "<|box_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "7": { + "content": "<|quad_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "8": { + "content": "<|quad_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "9": { + "content": "<|vision_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "10": { + "content": "<|vision_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "11": { + "content": "<|vision_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "12": { + "content": "<|image_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "13": { + "content": "<|video_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "14": { + "content": "<|audio_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "15": { + "content": "<|audio_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "16": { + "content": "<|audio_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "17": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "18": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "19": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "20": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "21": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "22": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "23": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "24": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "25": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "26": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "27": { + "content": "<|buffer1|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "28": { + "content": "<|buffer2|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "29": { + "content": "<|buffer3|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "30": { + "content": "<|buffer4|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "31": { + "content": "<|buffer5|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "32": { + "content": "<|buffer6|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "33": { + "content": "<|buffer7|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "34": { + "content": "<|buffer8|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "35": { + "content": "<|buffer9|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + } + }, + "additional_special_tokens": [ + "<|im_start|>", + "<|im_end|>", + "<|object_ref_start|>", + "<|object_ref_end|>", + "<|box_start|>", + "<|box_end|>", + "<|quad_start|>", + "<|quad_end|>", + "<|vision_start|>", + "<|vision_end|>", + "<|vision_pad|>", + "<|image_pad|>", + "<|video_pad|>", + "<|audio_start|>", + "<|audio_end|>", + "<|audio_pad|>", + "", + "", + "", + "" + ], + "bos_token": "<|im_start|>", + "clean_up_tokenization_spaces": false, + "eos_token": "<|im_end|>", + "legacy": true, + "model_max_length": 131072, + "pad_token": "<|endoftext|>", + "sp_model_kwargs": {}, + "spaces_between_special_tokens": false, + "unk_token": "<|endoftext|>", + "image_token": "<|image_pad|>", + "audio_token": "<|audio_pad|>", + "video_token": "<|video_pad|>", + "vision_bos_token": "<|vision_start|>", + "vision_eos_token": "<|vision_end|>", + "audio_bos_token": "<|audio_start|>", + "audio_eos_token": "<|audio_end|>", + "chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {{- messages[0].content + '\\n\\n' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within XML tags:\\n\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n\\n\\nFor each function call, return a json object with function name and arguments within XML tags:\\n\\n{\\\"name\\\": , \\\"arguments\\\": }\\n<|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0].content + '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for message in messages[::-1] %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- if ns.multi_step_tool and message.role == \"user\" and message.content is string and not(message.content.startswith('') and message.content.endswith('')) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n{%- endfor %}\n{%- for message in messages %}\n {%- if message.content is string %}\n {%- set content = message.content %}\n {%- else %}\n {%- set content = '' %}\n {%- endif %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) %}\n {{- '<|im_start|>' + message.role + '\\n' + content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set reasoning_content = '' %}\n {%- if message.reasoning_content is string %}\n {%- set reasoning_content = message.reasoning_content %}\n {%- else %}\n {%- if '' in content %}\n {%- set reasoning_content = content.split('')[0].rstrip('\\n').split('')[-1].lstrip('\\n') %}\n {%- set content = content.split('')[-1].lstrip('\\n') %}\n {%- endif %}\n {%- endif %}\n {%- if true %}\n {{- '<|im_start|>' + message.role + '\\n\\n' + reasoning_content.strip('\\n') + '\\n\\n\\n' + content.lstrip('\\n') }}\n {%- endif %}\n {%- if message.tool_calls %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {%- if tool_call.arguments is string %}\n {{- tool_call.arguments }}\n {%- else %}\n {{- tool_call.arguments | tojson }}\n {%- endif %}\n {{- '}\\n' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n\\n' }}\n {{- content }}\n {{- '\\n' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n {%- if open_thinking is defined and open_thinking is true %}\n {{- '\\n' }}\n {%- else %}\n {{- '\\n\\n\\n\\n' }}\n {%- endif %}\n{%- endif %}", + "tokenizer_class": "PreTrainedTokenizerFast" +} \ No newline at end of file diff --git a/model/vad/silero_vad.onnx b/model/vad/silero_vad.onnx new file mode 100644 index 0000000..5c21912 Binary files /dev/null and b/model/vad/silero_vad.onnx differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8d40548 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +soundfile==0.13.1 +librosa==0.11.0 +pyarrow==23.0.1 +snac==1.2.1 +transformers==4.57.6 +speechbrain==1.1.0 +onnxruntime==1.23.2 +funasr==1.3.1 +flask-sock==0.7.0 +swanlab==0.7.11 +# torch==2.6 +# torchaudio==2.6 +# torchvision==0.21.0 \ No newline at end of file diff --git a/scripts/convert_omni.py b/scripts/convert_omni.py new file mode 100644 index 0000000..96e84e2 --- /dev/null +++ b/scripts/convert_omni.py @@ -0,0 +1,51 @@ +import os +import sys + +__package__ = "scripts" +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +import json +import torch +import transformers +import warnings +from transformers import AutoTokenizer, AutoModelForCausalLM +from model.model_omni import MiniMindOmni, OmniConfig + +warnings.filterwarnings('ignore', category=UserWarning) + + +def convert_torch2transformers(torch_path, transformers_path, lm_config, dtype=torch.bfloat16): + OmniConfig.register_for_auto_class() + MiniMindOmni.register_for_auto_class("AutoModelForCausalLM") + model = MiniMindOmni(lm_config, audio_encoder_path="../model/SenseVoiceSmall", vision_model_path="../model/siglip2-base-p32-256-ve") + state_dict = torch.load(torch_path, map_location='cpu') + model.load_state_dict(state_dict, strict=False) + model = model.to(dtype) + params = sum(p.numel() for p in model.parameters()) / 1e6 + print(f'模型参数: {params:.2f}M') + del model.audio_encoder + del model.vision_encoder + model.save_pretrained(transformers_path, safe_serialization=False) + tokenizer = AutoTokenizer.from_pretrained('../model/') + tokenizer.save_pretrained(transformers_path) + config_path = os.path.join(transformers_path, "config.json") + config = json.load(open(config_path, 'r', encoding='utf-8')) + config['tie_word_embeddings'] = True + if int(transformers.__version__.split('.')[0]) >= 5: + tokenizer_config_path = os.path.join(transformers_path, "tokenizer_config.json") + json.dump({**json.load(open(tokenizer_config_path, 'r', encoding='utf-8')), "tokenizer_class": "PreTrainedTokenizerFast", "extra_special_tokens": {}}, open(tokenizer_config_path, 'w', encoding='utf-8'), indent=2, ensure_ascii=False) + config['rope_theta'] = lm_config.rope_theta; config['rope_scaling'] = None; config.pop('rope_parameters', None) + json.dump(config, open(config_path, 'w', encoding='utf-8'), indent=2, ensure_ascii=False) + print(f"已保存为 Transformers 格式: {transformers_path}") + + +def convert_transformers2torch(transformers_path, torch_path): + model = AutoModelForCausalLM.from_pretrained(transformers_path, trust_remote_code=True) + torch.save(model.state_dict(), torch_path) + print(f"已保存为 PyTorch 格式: {torch_path}") + + +if __name__ == '__main__': + lm_config = OmniConfig(hidden_size=768, num_hidden_layers=8, use_moe=False) + torch_path = f"../out/sft_omni_{lm_config.hidden_size}{'_moe' if lm_config.use_moe else ''}.pth" + transformers_path = '../minimind-3o' + convert_torch2transformers(torch_path, transformers_path, lm_config) diff --git a/scripts/web_demo_omni.py b/scripts/web_demo_omni.py new file mode 100644 index 0000000..2abdffd --- /dev/null +++ b/scripts/web_demo_omni.py @@ -0,0 +1,294 @@ +import os +import sys + +__package__ = "scripts" +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +import argparse +import base64 +import io +import tempfile +import warnings +import logging +import contextlib +import librosa +import soundfile as sf +import numpy as np +import torch +import gradio as gr +from queue import Queue +from threading import Thread, Lock +from PIL import Image +from pydub import AudioSegment +from transformers import AutoTokenizer, AutoModelForCausalLM, MimiModel, TextStreamer +from model.model_omni import MiniMindOmni, OmniConfig +from dataset.omni_dataset import OmniDataset +from trainer.trainer_utils import setup_seed, log_model_params +logging.getLogger().setLevel(logging.ERROR) +with contextlib.redirect_stdout(io.StringIO()): + from funasr import AutoModel + from funasr.utils.postprocess_utils import rich_transcription_postprocess + +warnings.filterwarnings('ignore') + +model_lock = Lock() + +model, tokenizer, device, mimi_model, asr_model = None, None, None, None, None +voices_data = {} +builtin_voices, clone_voices = set(), set() + + +def frames_to_mimi(frames): + codes = [f for f in frames if f and len(f) == 8] + if not codes: + return None + return torch.tensor(codes, dtype=torch.long).T.unsqueeze(0) + + +def decode_mimi(mimi_codes): + if mimi_codes is None or mimi_codes.numel() == 0: + return None + filtered = torch.where(mimi_codes >= 2049, torch.zeros_like(mimi_codes), mimi_codes).to(next(mimi_model.parameters()).device) + with torch.no_grad(): + audio = mimi_model.decode(filtered).audio_values + return audio.squeeze().float().cpu().numpy() + + +def scan_hf_models(base_dir): + models = {} + base_dir = os.path.abspath(base_dir) + for d in sorted(os.listdir(base_dir), reverse=True): + full_path = os.path.join(base_dir, d) + if not os.path.isdir(full_path) or d.startswith('.') or d.startswith('_'): + continue + files = set(os.listdir(full_path)) + has_model = bool(files & {'pytorch_model.bin', 'model.safetensors', 'pytorch_model.bin.index.json', 'model.safetensors.index.json'}) + if has_model: + models[d] = full_path + return models + + +def load_hf_model(model_path): + global model, tokenizer + with model_lock: + [sys.modules.pop(k) for k in list(sys.modules) if 'transformers_modules' in k] + tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) + model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True) + vision_encoder, vision_processor = MiniMindOmni.load_vision(args.vision_model) + audio_encoder, audio_processor = MiniMindOmni.load_sensevoice(args.audio_encoder) + object.__setattr__(model, 'vision_encoder', vision_encoder) + object.__setattr__(model, 'vision_processor', vision_processor) + object.__setattr__(model, 'audio_encoder', audio_encoder) + object.__setattr__(model, 'audio_processor', audio_processor) + model = model.half().eval().to(device) + if model.audio_encoder: + model.audio_encoder.to(device) + if model.vision_encoder: + model.vision_encoder.to(device) + name = os.path.basename(model_path) + params = sum(p.numel() for p in model.parameters()) / 1e6 + print(f'已加载 {name},参数量:{params:.2f}M') + return f"已加载: {name} ({params:.2f}M)" + + +def load_voices(): + global voices_data, builtin_voices, clone_voices + voices_data = {} + builtin_voices, clone_voices = set(), set() + for name, is_builtin in [('voices.pt', True), ('voices_unseen.pt', False)]: + path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'model', 'speaker', name) + if os.path.exists(path): + data = torch.load(path, map_location=device) + for speaker, v in data.items(): + if speaker not in voices_data: + voices_data[speaker] = v + (builtin_voices if is_builtin else clone_voices).add(speaker) + + +def chat_stream(prompt, audio_input=None, image_input=None, voice_name="default", history=None, temperature=0.85, max_tokens=512): + audio_inputs, audio_lens, pixel_values, ref_codes, spk_emb = None, None, None, None, None + asr_result = [None] + asr_thread = None + + if audio_input is not None: + sr, samples = audio_input + if len(samples.shape) > 1: + samples = samples.mean(axis=1) + if samples.dtype != np.float32: + samples = samples.astype(np.float32) / max(np.abs(samples).max(), 1) + if sr != 16000: + samples = librosa.resample(samples.astype(float), orig_sr=sr, target_sr=16000).astype(np.float32) + inputs = model.audio_processor(samples, sampling_rate=16000, return_tensors="pt", return_attention_mask=True) + mel = inputs.input_features.squeeze(0) + valid_len = inputs.attention_mask.sum().item() + audio_inputs = mel.unsqueeze(0).to(device) + audio_lens = torch.tensor([valid_len], device=device) + audio_token_len = valid_len or 1 + prompt = model.config.audio_special_token * audio_token_len + samples_for_asr = samples.copy() + def _do_asr(): + r = asr_model.generate(input=samples_for_asr, cache={}, language='auto', use_itn=True) + asr_result[0] = rich_transcription_postprocess(r[0]['text']).strip() if r else '' + print(f'[ASR] {asr_result[0]}') + asr_thread = Thread(target=_do_asr) + asr_thread.start() + + if image_input is not None: + image = Image.open(image_input).convert('RGB') if isinstance(image_input, str) else image_input.convert('RGB') + pixel_values = {k: v.to(device) for k, v in model.vision_processor(images=image, return_tensors="pt").items()} + prompt = (prompt + "\n\n" if prompt else "") + model.config.image_special_token * model.config.image_token_len + + if voice_name != "default" and voice_name in voices_data: + v = voices_data[voice_name] + ref_codes = v['ref_codes'].unsqueeze(0).to(device) + spk_emb = v['spk_emb'].half().unsqueeze(0).to(device) if 'spk_emb' in v else None + + messages = (history or []) + [{"role": "user", "content": prompt}] + open_thinking = bool(args.open_thinking) + inputs_text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, open_thinking=open_thinking) + x = torch.tensor(tokenizer(inputs_text).data['input_ids'], dtype=torch.long, device=device)[None, ...] + + audio_frames = [] + with torch.no_grad(): + with model_lock: + res = model.generate(x, tokenizer.eos_token_id, max_new_tokens=max_tokens, + temperature=temperature, top_p=args.top_p, stream=True, + return_audio_codes=True, open_thinking=open_thinking, + audio_inputs=audio_inputs, audio_lens=audio_lens, pixel_values=pixel_values, + ref_codes=ref_codes, spk_emb=spk_emb) + history_idx = 0 + for y, audio_frame in res: + text_chunk = None + if y is not None: + answer = tokenizer.decode(y[0].tolist(), skip_special_tokens=True) + if answer and answer[-1] != '�' and len(answer) > history_idx: + text_chunk = answer[history_idx:] + history_idx = len(answer) + if audio_frame: + audio_frames.append(audio_frame) + yield text_chunk, None, None + + if asr_thread: + asr_thread.join() + + if audio_frames and mimi_model: + yield None, "loading_audio", None + mimi_codes = frames_to_mimi(audio_frames) + audio_np = decode_mimi(mimi_codes) + if audio_np is not None: + yield None, (24000, audio_np), asr_result[0] + return + yield None, None, asr_result[0] + + +def launch_gradio(server_name="0.0.0.0", server_port=8888): + voice_choices = [("default", "default")] + for s in sorted(builtin_voices): voice_choices.append((f"[内置] {s}", s)) + for s in sorted(clone_voices): voice_choices.append((f"[克隆] {s}", s)) + + def respond(message, audio, voice, chat_history, model_history, max_turns): + text = message.get("text", "") if isinstance(message, dict) else (message or "") + files = message.get("files", []) if isinstance(message, dict) else [] + img_path = next((f for f in files if any(f.lower().endswith(e) for e in ('.png','.jpg','.jpeg','.gif','.bmp','.webp'))), None) + + if not text and audio is None and img_path is None: + yield chat_history + [{"role": "assistant", "content": "请输入文本、上传图片或录制音频"}], gr.update(), gr.update(), model_history, "" + return + + if audio is not None: + sr, samples = audio + display_samples = samples.mean(axis=1) if len(samples.shape) > 1 else samples + wav_path = tempfile.NamedTemporaryFile(suffix='.wav', delete=False).name + sf.write(wav_path, display_samples, sr) + chat_history = chat_history + [{"role": "user", "content": {"path": wav_path}}] + elif img_path: + chat_history = chat_history + [{"role": "user", "content": {"path": img_path}}, {"role": "user", "content": text or "请描述这张图片"}] + else: + chat_history = chat_history + [{"role": "user", "content": text}] + + response_text = "" + final_audio = None + asr_text = None + chat_history = chat_history + [{"role": "assistant", "content": ""}] + yield chat_history, gr.update(value=None), None, model_history, "" + + hist = model_history[-(max_turns * 2):] if max_turns > 0 else [] + for text_chunk, audio_data, asr in chat_stream( + text or "", audio_input=audio, image_input=img_path, + voice_name=voice, history=hist, temperature=0.7, max_tokens=512 + ): + if text_chunk: + response_text += text_chunk + chat_history[-1]["content"] = response_text + yield chat_history, gr.update(), None, model_history, gr.update() + if audio_data == "loading_audio": + yield chat_history, gr.update(), None, model_history, '
正在组装语音(受限于 Gradio 特性,此处为非流式解码)...
' + elif audio_data: + final_audio = audio_data + if asr is not None: + asr_text = asr + + user_text = asr_text if asr_text else (text or "") + if user_text: + model_history = model_history + [{"role": "user", "content": user_text}] + if response_text: + model_history = model_history + [{"role": "assistant", "content": response_text}] + + yield chat_history, final_audio if final_audio else gr.update(), None, model_history, "" + + with gr.Blocks(title="MiniMind-O", js="()=>{new MutationObserver(()=>{const m=document.getElementById('mic-box');if(!m)return;const h=!!m.querySelector('audio');document.body.classList.toggle('has-audio',h);const t=document.querySelector('textarea');if(t){t.placeholder=h?'已加载语音,点击发送':'输入文本';t.disabled=h}}).observe(document.body,{childList:true,subtree:true})}", css=".app{padding-top:6px!important} #component-0{gap:6px!important} #component-1{padding:2px 0!important;margin:0!important;min-height:0!important;border:none!important} #component-1 .padding{padding:0!important} #chatbox img{max-width:120px!important;max-height:120px!important;border-radius:8px} textarea{overflow-y:hidden!important;height:auto!important;min-height:30px!important;max-height:60px!important} #mic-box{max-height:150px!important;overflow:hidden!important} #mic-box .wrap span.or,#mic-box .wrap span:first-child{display:none!important} #mic-box .wrap{font-size:0!important;min-height:40px!important;padding:8px!important} #mic-box .wrap::after{content:'上传/录音';font-size:14px!important} #mic-box .mic-select{display:none!important} .has-audio textarea{opacity:0.4!important;pointer-events:none!important} .has-audio .upload-button,.has-audio [data-testid='upload-button']{display:none!important} @keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}") as demo: + gr.HTML('
MiniMind-O text / image / audio → text + audio
') + + chatbot = gr.Chatbot(label="", height=380, elem_id="chatbox", type="messages") + model_history = gr.State([]) + audio_status = gr.HTML("", elem_id="audio-status") + audio_out = gr.Audio(label="语音回复", autoplay=True, elem_id="audio-out") + with gr.Row(equal_height=True): + with gr.Column(scale=0, min_width=160): + aud = gr.Audio(sources=["upload", "microphone"], type="numpy", show_label=False, elem_id="mic-box") + with gr.Column(scale=4): + msg = gr.MultimodalTextbox(placeholder="输入文本", show_label=False, submit_btn="发送") + with gr.Row(): + voice_dd = gr.Dropdown(choices=voice_choices, value="default", label="音色选择", scale=0, min_width=140) + turns_dd = gr.Dropdown(choices=[0, 2, 4, 6, 8], value=0, label="多轮记忆", scale=0, min_width=120) + if model_dict: + model_dd = gr.Dropdown(choices=list(model_dict.keys()), value=current_model_name, label="模型选择", scale=1, min_width=180) + status = gr.Textbox(value=f"已加载: {current_model_name}", label="状态", interactive=False, scale=2) + model_dd.change(lambda n: load_hf_model(model_dict[n]), [model_dd], [status]) + + msg.submit(respond, [msg, aud, voice_dd, chatbot, model_history, turns_dd], [chatbot, audio_out, aud, model_history, audio_status]) + + demo.queue().launch(server_name=server_name, server_port=server_port) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="MiniMind-O Gradio Demo") + parser.add_argument('--load_from', default='./', type=str, help="transformers模型扫描目录") + parser.add_argument('--audio_encoder', default='../model/SenseVoiceSmall', type=str) + parser.add_argument('--vision_model', default='../model/siglip2-base-p32-256-ve', type=str) + parser.add_argument('--mimi_path', default='../model/mimi', type=str) + parser.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu', type=str) + parser.add_argument('--open_thinking', default=0, type=int, choices=[0, 1]) + parser.add_argument('--top_p', default=0.85, type=float) + parser.add_argument('--port', default=8888, type=int) + args = parser.parse_args() + + device = args.device + model_dict = scan_hf_models(args.load_from) + if not model_dict: + print(f"未在 {os.path.abspath(args.load_from)} 找到 transformers 模型") + exit(1) + current_model_name = list(model_dict.keys())[0] + load_hf_model(model_dict[current_model_name]) + + try: + mimi_model = MimiModel.from_pretrained(args.mimi_path).eval() + print('Mimi model loaded') + except Exception: + mimi_model = None + print('Mimi model not found, audio output disabled') + + with contextlib.redirect_stdout(io.StringIO()): + asr_model = AutoModel(model=args.audio_encoder, trust_remote_code=True, device=device, disable_update=True) + load_voices() + print(f'Voices loaded: {list(voices_data.keys()) or "none"}') + launch_gradio(server_port=args.port) diff --git a/trainer/train.sh b/trainer/train.sh new file mode 100644 index 0000000..64f210a --- /dev/null +++ b/trainer/train.sh @@ -0,0 +1,25 @@ +## ==================== Full dataset training pipeline ==================== +## Suggested full training pipeline (Dense, 4x GPU) +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_t2a.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --use_wandb --use_moe 0 # ~45min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_a2a.parquet --epochs 1 --batch_size 32 --use_compile 0 --from_weight sft_omni --max_seq_len 1024 --mode audio_proj --use_wandb --use_moe 0 # ~25min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-5 --data_path ../dataset/sft_a2a.parquet --epochs 3 --batch_size 32 --use_compile 0 --from_weight sft_omni --max_seq_len 1024 --use_wandb --use_moe 0 # 3 * ~25min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-5 --data_path ../dataset/sft_i2t.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --max_seq_len 768 --mode vision_proj --use_wandb --use_moe 0 # ~45min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_i2t.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --max_seq_len 768 --use_wandb --use_moe 0 # ~45min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_a2a.parquet --epochs 1 --batch_size 32 --use_compile 0 --from_weight sft_omni --max_seq_len 1024 --use_wandb --use_moe 0 # ~25min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_i2t.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --max_seq_len 768 --mode vision_proj --use_wandb --use_moe 0 # ~45min + +## Suggested full training pipeline (MoE, 4x GPU) +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_t2a.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --use_wandb --use_moe 1 # ~45min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_a2a.parquet --epochs 1 --batch_size 32 --use_compile 0 --from_weight sft_omni --max_seq_len 1024 --mode audio_proj --use_wandb --use_moe 1 # ~25min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-5 --data_path ../dataset/sft_a2a.parquet --epochs 3 --batch_size 32 --use_compile 0 --from_weight sft_omni --max_seq_len 1024 --use_wandb --use_moe 1 # 3 * ~25min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-5 --data_path ../dataset/sft_i2t.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --max_seq_len 768 --mode vision_proj --use_wandb --use_moe 1 # ~45min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_i2t.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --max_seq_len 768 --use_wandb --use_moe 1 # ~45min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_a2a.parquet --epochs 1 --batch_size 32 --use_compile 0 --from_weight sft_omni --max_seq_len 1024 --use_wandb --use_moe 1 # ~25min +# CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --master_port 29560 --nproc_per_node 4 train_sft_omni.py --learning_rate 5e-6 --data_path ../dataset/sft_i2t.parquet --epochs 1 --batch_size 32 --use_compile 1 --from_weight sft_omni --max_seq_len 768 --mode vision_proj --use_wandb --use_moe 1 # ~45min + + +# ==================== (Recommend) Mini dataset training pipeline ==================== +# Time estimates below are based on 1x RTX 3090, for reference only and may not be entirely accurate. +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_t2a_mini.parquet --epochs 1 --batch_size 40 --use_compile 1 --from_weight llm --save_weight sft_zero --max_seq_len 512 --use_wandb --use_moe 0 # epochs * 60min +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 5e-4 --data_path ../dataset/sft_a2a_mini.parquet --epochs 1 --batch_size 40 --use_compile 0 --from_weight sft_zero --save_weight sft_zero --max_seq_len 640 --mode audio_proj --use_wandb --use_moe 0 # epochs * 15min +CUDA_VISIBLE_DEVICES=0 torchrun --master_port 29560 --nproc_per_node 1 train_sft_omni.py --learning_rate 2e-5 --data_path ../dataset/sft_a2a_mini.parquet --epochs 1 --batch_size 16 --use_compile 0 --from_weight sft_zero --save_weight sft_zero --max_seq_len 768 --use_wandb --use_moe 0 # 2 * ~15min \ No newline at end of file diff --git a/trainer/train_sft_omni.py b/trainer/train_sft_omni.py new file mode 100644 index 0000000..9df8c8e --- /dev/null +++ b/trainer/train_sft_omni.py @@ -0,0 +1,263 @@ +import os +import sys + +__package__ = "trainer" +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +import argparse +import time +import warnings +import torch +import torch.nn as nn +import torch.distributed as dist +from contextlib import nullcontext +from torch import optim +from torch.nn.parallel import DistributedDataParallel +from torch.utils.data import DataLoader, DistributedSampler +from model.model_omni import OmniConfig +from dataset.omni_dataset import OmniDataset +from trainer.trainer_utils import get_lr, Logger, is_main_process, init_distributed_mode, setup_seed, init_omni_model, omni_checkpoint, SkipBatchSampler, log_model_params + +warnings.filterwarnings('ignore') + + +def omni_collate_fn(batch): + """自定义collate函数,处理变长audio_inputs和pixel_values""" + input_ids, labels, audio_labels, audio_inputs, audio_lens, pixel_values, spk_emb = zip(*batch) + input_ids = torch.stack(input_ids) + labels = torch.stack(labels) + audio_labels = torch.stack(audio_labels) + audio_lens = torch.tensor(audio_lens, dtype=torch.long) + valid_audios = [a for a in audio_inputs if a is not None] + if valid_audios: + max_t = max(a.size(1) for a in valid_audios) + padded = [a if a.size(1) == max_t else torch.nn.functional.pad(a, (0, 0, 0, max_t - a.size(1))) for a in valid_audios] + audio_inputs = torch.cat(padded, dim=0) + else: + audio_inputs = None + valid_images = [p for p in pixel_values if p is not None] + if valid_images: + if hasattr(valid_images[0], 'keys'): + keys = set.intersection(*[set(d.keys()) for d in valid_images]) + pixel_values = {k: torch.cat([d[k] for d in valid_images], dim=0) for k in keys} + else: + pixel_values = torch.cat(valid_images, dim=0) + else: + pixel_values = None + spk_emb = torch.stack(spk_emb) + return input_ids, labels, audio_labels, audio_inputs, audio_lens, pixel_values, spk_emb + + +def train_epoch(epoch, loader, iters, start_step=0, wandb=None): + start_time = time.time() + last_step = start_step + for step, (input_ids, labels, audio_labels, audio_inputs, audio_lens, pixel_values, spk_emb) in enumerate(loader, start=start_step + 1): + input_ids = input_ids.to(args.device) + labels = labels.to(args.device) + audio_labels = audio_labels.to(args.device) + audio_lens = audio_lens.to(args.device) + if audio_inputs is not None: + audio_inputs = audio_inputs.to(args.device) + if pixel_values is not None: + if hasattr(pixel_values, 'keys'): + pixel_values = {k: v.to(args.device) for k, v in pixel_values.items()} + else: + pixel_values = pixel_values.to(args.device) + spk_emb = spk_emb.to(args.device) + last_step = step + lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + + with autocast_ctx: + res = model(input_ids, audio_inputs=audio_inputs, audio_lens=audio_lens, pixel_values=pixel_values, spk_emb=spk_emb) + loss_fct = nn.CrossEntropyLoss(reduction='none') + + # Text loss + text_loss_raw = loss_fct(res.logits.view(-1, res.logits.size(-1)), labels.view(-1)) + text_mask = (labels.view(-1) != -100).float() + text_loss = (text_loss_raw * text_mask).sum() / (text_mask.sum() + 1e-9) + + # Audio loss + audio_loss = res.audio_logits[0].sum() * 0 + for i, al in enumerate(res.audio_logits): + al_flat = al.view(-1, al.size(-1)) + target_flat = audio_labels[:, i, :].reshape(-1) + layer_loss = loss_fct(al_flat, target_flat) + valid_mask = (target_flat != -100).float() + stop_mask = (target_flat == 2050).float() + weighted_loss = layer_loss * valid_mask * (1 + stop_mask * 9) + msum = valid_mask.sum() + if msum > 0: + audio_loss = audio_loss + weighted_loss.sum() / (msum + 1e-9) + audio_loss = audio_loss / 8 + + loss = (text_loss + audio_loss + res.aux_loss) / args.accumulation_steps + + scaler.scale(loss).backward() + if step % args.accumulation_steps == 0: + scaler.unscale_(optimizer) + torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip) + scaler.step(optimizer) + scaler.update() + optimizer.zero_grad(set_to_none=True) + + if step % args.log_interval == 0 or step == iters: + spend_time = time.time() - start_time + current_loss = loss.item() * args.accumulation_steps + text_loss_val = text_loss.item() if isinstance(text_loss, torch.Tensor) else 0 + audio_loss_val = audio_loss.item() if isinstance(audio_loss, torch.Tensor) else 0 + current_lr = optimizer.param_groups[-1]['lr'] + eta_min = spend_time / max(step - start_step, 1) * (iters - step) // 60 + Logger(f'Epoch:[{epoch+1}/{args.epochs}]({step}/{iters}), loss: {current_loss:.4f}, text: {text_loss_val:.4f}, audio: {audio_loss_val:.4f}, lr: {current_lr:.8f}, epoch_time: {eta_min:.1f}min') + if wandb: + wandb.log({"loss": current_loss, "text_loss": text_loss_val, + "audio_loss": audio_loss_val, "lr": current_lr, "epoch_time": eta_min}) + + if (step % args.save_interval == 0 or step == iters) and is_main_process(): + model.eval() + moe_suffix = '_moe' if omni_config.use_moe else '' + ckp = f'{args.save_dir}/{args.save_weight}_{omni_config.hidden_size}{moe_suffix}.pth' + raw_model = model.module if isinstance(model, DistributedDataParallel) else model + raw_model = getattr(raw_model, '_orig_mod', raw_model) + clean_state_dict = {k: v for k, v in raw_model.state_dict().items() if not k.startswith('audio_encoder.')} + torch.save({k: v.half().cpu() for k, v in clean_state_dict.items()}, ckp) + omni_checkpoint(omni_config, weight=args.save_weight, model=model, optimizer=optimizer, + epoch=epoch, step=step, wandb=wandb, save_dir='../checkpoints', scaler=scaler) + model.train() + + del input_ids, labels, audio_labels, audio_inputs, audio_lens, pixel_values, spk_emb, res, loss + + if last_step > start_step and last_step % args.accumulation_steps != 0: + scaler.unscale_(optimizer) + torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip) + scaler.step(optimizer) + scaler.update() + optimizer.zero_grad(set_to_none=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="MiniMind-O SFT") + parser.add_argument("--save_dir", type=str, default="../out", help="模型保存目录") + parser.add_argument('--save_weight', default='sft_omni', type=str, help="保存权重的前缀名") + parser.add_argument("--epochs", type=int, default=15, help="训练轮数") + parser.add_argument("--batch_size", type=int, default=32, help="batch size") + parser.add_argument("--learning_rate", type=float, default=5e-4, help="初始学习率") + parser.add_argument("--device", type=str, default="cuda:0" if torch.cuda.is_available() else "cpu", help="训练设备") + parser.add_argument("--dtype", type=str, default="bfloat16", help="混合精度类型") + parser.add_argument("--num_workers", type=int, default=4, help="数据加载线程数") + parser.add_argument("--accumulation_steps", type=int, default=1, help="梯度累积步数") + parser.add_argument("--grad_clip", type=float, default=1.0, help="梯度裁剪阈值") + parser.add_argument("--log_interval", type=int, default=100, help="日志打印间隔") + parser.add_argument("--save_interval", type=int, default=1000, help="模型保存间隔") + parser.add_argument('--hidden_size', default=768, type=int, help="隐藏层维度") + parser.add_argument('--num_hidden_layers', default=8, type=int, help="隐藏层数量") + parser.add_argument('--max_seq_len', default=512, type=int, help="训练的最大截断长度") + parser.add_argument('--use_moe', default=0, type=int, choices=[0, 1], help="是否使用MoE架构") + parser.add_argument("--data_path", type=str, default="../dataset/train_t2a_mini.parquet", help="训练数据路径(parquet格式)") + parser.add_argument("--audio_encoder_dir", type=str, default="../model/SenseVoiceSmall", help="音频encoder路径(SenseVoice)") + parser.add_argument("--vision_dir", type=str, default="../model/siglip2-base-p32-256-ve", help="CLIP视觉模型路径") + parser.add_argument('--from_weight', default='llm', type=str, help="基于哪个权重训练,为none则不基于任何权重训练") + parser.add_argument('--from_resume', default=0, type=int, choices=[0, 1], help="是否自动检测&续训(0=否,1=是)") + parser.add_argument('--freeze_backbone', default='none', type=str, choices=['none', 'all', 'last1'], help="冻结主干模型: none=全量训练, all=只训练audio层, last1=只训练最后1层+audio层") + parser.add_argument('--mode', default='all', type=str, choices=['all', 'audio_proj', 'vision_proj'], help="训练模式: all=全量训练, audio_proj=只训练audio_proj, vision_proj=只训练vision_proj") + parser.add_argument("--use_wandb", action="store_true", help="是否使用wandb") + parser.add_argument("--wandb_project", type=str, default="MiniMind-O-SFT", help="wandb项目名") + parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1], help="是否使用torch.compile加速(0=否,1=是)") + args = parser.parse_args() + + # ========== 1. 初始化环境和随机种子 ========== + local_rank = init_distributed_mode() + if dist.is_initialized(): + args.device = f"cuda:{local_rank}" + setup_seed(42 + (dist.get_rank() if dist.is_initialized() else 0)) + + # ========== 2. 配置目录、模型参数、检查ckp ========== + os.makedirs(args.save_dir, exist_ok=True) + omni_config = OmniConfig( + hidden_size=args.hidden_size, + num_hidden_layers=args.num_hidden_layers, + use_moe=bool(args.use_moe) + ) + ckp_data = omni_checkpoint(omni_config, weight=args.save_weight, save_dir='../checkpoints') if args.from_resume==1 else None + + # ========== 3. 设置混合精度 ========== + device_type = "cuda" if "cuda" in args.device else "cpu" + dtype = torch.bfloat16 if args.dtype == "bfloat16" else torch.float16 + autocast_ctx = nullcontext() if device_type == "cpu" else torch.cuda.amp.autocast(dtype=dtype) + + # ========== 4. 配wandb ========== + wandb = None + if args.use_wandb and is_main_process(): + import swanlab as wandb + wandb_id = ckp_data.get('wandb_id') if ckp_data else None + resume = 'must' if wandb_id else None + wandb_run_name = f"MiniMind-O-SFT-Epoch-{args.epochs}-BatchSize-{args.batch_size}-LR-{args.learning_rate}" + wandb.init(project=args.wandb_project, name=wandb_run_name, id=wandb_id, resume=resume) + + # ========== 5. 定义模型、数据、优化器 ========== + model, tokenizer = init_omni_model(omni_config, from_weight=args.from_weight, + audio_encoder_path=args.audio_encoder_dir, + vision_model_path=args.vision_dir, + save_dir=args.save_dir, device=args.device, + freeze_backbone=args.freeze_backbone, from_resume=args.from_resume) + + if args.use_compile == 1: + model = torch.compile(model) + + if model.audio_encoder is not None: model.audio_encoder.to(args.device) + if model.vision_encoder is not None: model.vision_encoder.to(args.device) + + if args.mode == 'audio_proj': + for p in model.parameters(): p.requires_grad = False + for p in model.audio_proj.parameters(): p.requires_grad = True + elif args.mode == 'vision_proj': + for p in model.parameters(): p.requires_grad = False + for p in model.vision_proj.parameters(): p.requires_grad = True + log_model_params(model) + trainable = sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e6 + Logger(f'Trainable: {trainable:.2f}M | Mode: {args.mode} | Freeze: {args.freeze_backbone} | Compile: {"on" if args.use_compile else "off"}') + + # scheduled_sampling 现在会自动保护 image/audio token 的连续性 + train_ds = OmniDataset( + args.data_path, + tokenizer, + audio_processor=model.audio_processor, + vision_processor=model.vision_processor, + max_length=args.max_seq_len, + image_token_len=model.config.image_token_len + ) + + train_sampler = DistributedSampler(train_ds) if dist.is_initialized() else None + scaler = torch.cuda.amp.GradScaler(enabled=(args.dtype == 'float16')) + optimizer = optim.AdamW(model.parameters(), lr=args.learning_rate) + + # ========== 6. 从ckp恢复状态 ========== + start_epoch, start_step = 0, 0 + if ckp_data: + model.load_state_dict(ckp_data['model'], strict=False) + optimizer.load_state_dict(ckp_data['optimizer']) + scaler.load_state_dict(ckp_data['scaler']) + start_epoch = ckp_data['epoch'] + start_step = ckp_data.get('step', 0) + + # ========== 7. DDP包模型 ========== + if dist.is_initialized(): + model = DistributedDataParallel(model, device_ids=[local_rank]) + + # ========== 8. 开始训练 ========== + for epoch in range(start_epoch, args.epochs): + train_sampler and train_sampler.set_epoch(epoch) + setup_seed(42 + epoch); indices = torch.randperm(len(train_ds)).tolist() + skip = start_step if (epoch == start_epoch and start_step > 0) else 0 + batch_sampler = SkipBatchSampler(train_sampler or indices, args.batch_size, skip) + loader = DataLoader(train_ds, batch_sampler=batch_sampler, collate_fn=omni_collate_fn, num_workers=args.num_workers, pin_memory=True) + if skip > 0: + Logger(f'Epoch [{epoch + 1}/{args.epochs}]: 跳过前{start_step}个step,从step {start_step + 1}开始') + train_epoch(epoch, loader, len(loader) + skip, start_step, wandb) + else: + train_epoch(epoch, loader, len(loader), 0, wandb) + + # ========== 9. 清理分布进程 ========== + if dist.is_initialized(): dist.destroy_process_group() + diff --git a/trainer/trainer_utils.py b/trainer/trainer_utils.py new file mode 100644 index 0000000..59afa41 --- /dev/null +++ b/trainer/trainer_utils.py @@ -0,0 +1,200 @@ +""" +训练工具函数集合 +""" +import os +import random +import math +import numpy as np +import torch +import torch.distributed as dist +from torch.utils.data import Sampler +from transformers import AutoTokenizer +from model.model_omni import MiniMindOmni + + + +def is_main_process(): + return not dist.is_initialized() or dist.get_rank() == 0 + + +def Logger(content): + if is_main_process(): + print(content) + + +def get_lr(current_step, total_steps, lr): + # 与 mmv 保持一致:初始 lr=1.0*base_lr,最终 lr=0.1*base_lr + return lr * (0.1 + 0.45 * (1 + math.cos(math.pi * current_step / total_steps))) + + +def init_distributed_mode(): + if int(os.environ.get("RANK", -1)) == -1: + return 0 # 非DDP模式 + + dist.init_process_group(backend="nccl") + local_rank = int(os.environ["LOCAL_RANK"]) + torch.cuda.set_device(local_rank) + return local_rank + + +def setup_seed(seed: int): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + + +def log_model_params(model, ignore_patterns=['audio_encoder', 'vision_encoder']): + def should_count(n): return not any(p in n for p in ignore_patterns) + total = sum(p.numel() for n, p in model.named_parameters() if should_count(n)) / 1e6 + cfg = model.config + n_routed = getattr(cfg, 'n_routed_experts', getattr(cfg, 'num_experts', 0)) + n_active = getattr(cfg, 'num_experts_per_tok', 0) + n_shared = getattr(cfg, 'n_shared_experts', 0) + expert = sum(p.numel() for n, p in model.named_parameters() if 'mlp.experts.0.' in n and should_count(n)) / 1e6 + shared_expert = sum(p.numel() for n, p in model.named_parameters() if 'mlp.shared_experts.0.' in n and should_count(n)) / 1e6 + base = total - (expert * n_routed) - (shared_expert * n_shared) + active = base + (expert * n_active) + (shared_expert * n_shared) + if active < total: Logger(f'Model Params: {total:.2f}M-A{active:.2f}M') + else: Logger(f'Model Params: {total:.2f}M') + + +def init_omni_model(omni_config, from_weight='full_sft', tokenizer_path='../model', audio_encoder_path='../model/SenseVoiceSmall', vision_model_path='../model/siglip2-base-p32-256-ve', save_dir='../out', device='cuda', freeze_backbone='none', from_resume=0): + tokenizer = AutoTokenizer.from_pretrained(tokenizer_path) + model = MiniMindOmni(omni_config, audio_encoder_path=audio_encoder_path, vision_model_path=vision_model_path) + + if from_weight != 'none': + moe_suffix = '_moe' if omni_config.use_moe else '' + weight_path = f'{save_dir}/{from_weight}_{omni_config.hidden_size}{moe_suffix}.pth' + if os.path.exists(weight_path): + weights = torch.load(weight_path, map_location=device) + param_shapes = {k: v.shape for k, v in model.named_parameters()} + incompatible = {k for k, v in weights.items() if k in param_shapes and v.shape != param_shapes[k]} + if incompatible: + Logger(f'跳过shape不匹配的权重: {incompatible}') + weights = {k: v for k, v in weights.items() if k not in incompatible} + model.load_state_dict(weights, strict=False) + Logger(f'已加载权重: {weight_path}') + if from_resume == 0 and omni_config.talker_hidden_size == omni_config.hidden_size: + n_talker = omni_config.num_talker_hidden_layers + n_thinker = len(model.thinker.layers) + has_talker = any(k.startswith('talker.layers.') for k in weights) + if not has_talker and n_talker > 0: + for i in range(n_talker): + src = n_thinker - n_talker + i + model.talker.layers[i].load_state_dict(model.thinker.layers[src].state_dict()) + Logger(f'Talker层初始化: 复制thinker layers[{n_thinker-n_talker}:{n_thinker}] → talker layers[0:{n_talker}]') + + # 冻结策略 + if freeze_backbone == 'all': + # 冻结整个主干模型 + for param in model.model.parameters(): + param.requires_grad = False + elif freeze_backbone == 'last1': + # 冻结除了最后1层之外的所有层 + for param in model.model.parameters(): + param.requires_grad = False + # 打开最后1层 + if hasattr(model.model, 'layers') and len(model.model.layers) > 0: + for param in model.model.layers[-1].parameters(): + param.requires_grad = True + return model.to(device), tokenizer + + +def omni_checkpoint(omni_config, weight='pretrain_omni', model=None, optimizer=None, epoch=0, step=0, wandb=None, save_dir='../checkpoints', **kwargs): + os.makedirs(save_dir, exist_ok=True) + moe_path = '_moe' if omni_config.use_moe else '' + ckp_path = f'{save_dir}/{weight}_{omni_config.hidden_size}{moe_path}.pth' + resume_path = f'{save_dir}/{weight}_{omni_config.hidden_size}{moe_path}_resume.pth' + + if model is not None: + from torch.nn.parallel import DistributedDataParallel + raw_model = model.module if isinstance(model, DistributedDataParallel) else model + raw_model = getattr(raw_model, '_orig_mod', raw_model) + # 移除冻结的 audio_encoder / vision_encoder 参数(不需要保存,从预训练路径重新加载) + clean_state_dict = {k: v for k, v in raw_model.state_dict().items() if not k.startswith('audio_encoder.') and not k.startswith('vision_encoder.')} + state_dict = {k: v.half().cpu() for k, v in clean_state_dict.items()} + ckp_tmp = ckp_path + '.tmp' + torch.save(state_dict, ckp_tmp) + os.replace(ckp_tmp, ckp_path) + + wandb_id = None + if wandb: + if hasattr(wandb, 'get_run'): + run = wandb.get_run() + wandb_id = getattr(run, 'id', None) if run else None + else: + wandb_id = getattr(wandb, 'id', None) + + resume_data = { + 'model': state_dict, + 'optimizer': optimizer.state_dict(), + 'epoch': epoch, + 'step': step, + 'world_size': dist.get_world_size() if dist.is_initialized() else 1, + 'wandb_id': wandb_id + } + for key, value in kwargs.items(): + if value is not None: + if hasattr(value, 'state_dict'): + if isinstance(value, DistributedDataParallel): + resume_data[key] = value.module.state_dict() + else: + resume_data[key] = value.state_dict() + else: + resume_data[key] = value + + resume_tmp = resume_path + '.tmp' + torch.save(resume_data, resume_tmp) + os.replace(resume_tmp, resume_path) + else: # 加载模式 + if os.path.exists(resume_path): + ckp_data = torch.load(resume_path, map_location='cpu') + saved_ws = ckp_data.get('world_size', 1) + current_ws = dist.get_world_size() if dist.is_initialized() else 1 + if saved_ws != current_ws: + ckp_data['step'] = ckp_data['step'] * saved_ws // current_ws + Logger(f'GPU数量变化({saved_ws}→{current_ws}),step已自动转换为{ckp_data["step"]}') + return ckp_data + return None + + +def vlm_collate_fn(batch): + input_ids = torch.stack([b[0] for b in batch]) + labels = torch.stack([b[1] for b in batch]) + pixel_data = [b[2] for b in batch] + if hasattr(pixel_data[0], 'keys'): + pixel_values = {k: torch.stack([d[k] for d in pixel_data]) for k in pixel_data[0].keys()} + else: + pixel_values = torch.stack(pixel_data) + return input_ids, labels, pixel_values + + +class SkipBatchSampler(Sampler): + def __init__(self, sampler, batch_size, skip_batches=0): + self.sampler = sampler + self.batch_size = batch_size + self.skip_batches = skip_batches + + def __iter__(self): + batch = [] + skipped = 0 + for idx in self.sampler: + batch.append(idx) + if len(batch) == self.batch_size: + if skipped < self.skip_batches: + skipped += 1 + batch = [] + continue + yield batch + batch = [] + if len(batch) > 0 and skipped >= self.skip_batches: + yield batch + + def __len__(self): + total_batches = (len(self.sampler) + self.batch_size - 1) // self.batch_size + return max(0, total_batches - self.skip_batches) + diff --git a/webui/web_demo.html b/webui/web_demo.html new file mode 100644 index 0000000..b73d484 --- /dev/null +++ b/webui/web_demo.html @@ -0,0 +1,727 @@ + + + + + +MiniMind-O + + + + +
+
+
+ logo +
multimodal inference · text / image / audio → text + audio
+
+
+ + +
+ +
+
+ +
+
+
+
+
+ +
+
+ + +
+ +
+
+ +
+
Clone Voice
请自然朗读下面这句话。建议 3 到 6 秒,环境尽量安静。录完后会自动做 1.5 倍速、提取 `CAM++` speaker embedding 与 `Mimi` ref codes,然后立即加入音色列表。
今天阳光很好,欢迎来到我的语音克隆小实验。
点击 start 开始录音
+ +
+
+
live voice · bidirectional streaming
+
+ + + connecting +
+
+
+ + +
init
00:00:00
+
+
latest · awaiting first turnspeak naturally — model responds with voice and text
+
+ + +
+
session transcript 0 turns
+
+ + + + diff --git a/webui/web_demo.py b/webui/web_demo.py new file mode 100644 index 0000000..97e9aa0 --- /dev/null +++ b/webui/web_demo.py @@ -0,0 +1,502 @@ +import argparse, os, sys, json, time, math, torch, threading, queue, base64, io, logging, contextlib +import numpy as np +import torchaudio +from flask import Flask, request, Response, send_from_directory +from flask_cors import CORS +from flask_sock import Sock +from PIL import Image +from pydub import AudioSegment +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from transformers import AutoTokenizer, AutoModelForCausalLM +from model.model_omni import MiniMindOmni, RealtimeSession +from trainer.trainer_utils import log_model_params +logging.getLogger().setLevel(logging.ERROR) +with contextlib.redirect_stdout(io.StringIO()): + from funasr import AutoModel + from funasr.utils.postprocess_utils import rich_transcription_postprocess + +app = Flask(__name__, static_folder='.') +CORS(app) +sock = Sock(app) +M = {} # model / tokenizer / device / mimi / asr / cfg +V = {} # voice_name -> {ref_codes, spk_emb} +V_builtin, V_unseen, V_manual = [], [], [] +MODEL_LOCK = threading.Lock() +SAMPLES_PER_FRAME = 1920 +REF_FRAMES = 300 +CLONE_VOICE = 'voice_clone' +CLONE_FILE = 'voice_clone.pt' + +# -------- helpers -------- +def sse(d): return f"data: {json.dumps(d)}\n\n" + +def scan_hf_models(base_dir): + models = {} + base_dir = os.path.abspath(base_dir) + for d in sorted(os.listdir(base_dir), reverse=True): + full_path = os.path.join(base_dir, d) + if not os.path.isdir(full_path) or d.startswith('.') or d.startswith('_'): + continue + files = set(os.listdir(full_path)) + has_model = bool(files & {'pytorch_model.bin', 'model.safetensors', 'pytorch_model.bin.index.json', 'model.safetensors.index.json'}) + if has_model: + models[d] = full_path + return models + +def asr_run(samples): + r = M['asr'].generate(input=samples, cache={}, language='auto', use_itn=True) + return rich_transcription_postprocess(r[0]['text']).strip() if r else '' + +def prep_audio(samples): + m = M['model'] + proc = m.audio_processor(samples, sampling_rate=16000, return_tensors="pt", return_attention_mask=True) + mel = proc.input_features.squeeze(0).unsqueeze(0).to(M['device']) + vlen = proc.attention_mask.sum().item() + prompt = m.config.audio_special_token * (vlen or 1) + return mel, torch.tensor([vlen], device=M['device']), prompt + +def prep_image(b64): + img = Image.open(io.BytesIO(base64.b64decode(b64))).convert('RGB') + return {k: v.to(M['device']) for k, v in M['model'].vision_processor(images=img, return_tensors="pt").items()} + +def build_ids(prompt, history): + tok, dev, n = M['tokenizer'], M['device'], M['cfg'].max_history_turns + hist = history[-n:] if n > 0 else [] + msgs = hist + [{"role": "user", "content": prompt}] + t = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True) + return torch.tensor(tok(t).data['input_ids'], dtype=torch.long, device=dev)[None, ...] + +def _mimi_decode(frames): + codes = [f for f in frames if f and len(f) == 8] + if not codes or not M['mimi']: return None + mc = torch.tensor(codes, dtype=torch.long).T.unsqueeze(0) + mc = torch.where(mc >= 2049, torch.zeros_like(mc), mc) + with torch.no_grad(): + au = M['mimi'].decode(mc).audio_values.squeeze().cpu().numpy() + return au, mc.shape[-1] + +def pcm_bytes(frames, ov): + r = _mimi_decode(frames) + if r is None: return None + au, T = r + if ov > 0: au = au[int(ov * len(au) / T):] + return (au * 32767).astype('int16').tobytes() + +def stream_pcm(frames, flush=False): + """yield (pcm_bytes,) on chunk boundaries or on final flush.""" + if not M['mimi']: return + cf, ov_max, n = M['cfg'].audio_chunk_frames, M['cfg'].audio_overlap, len(frames) + if not flush and n >= cf and n % cf == 0: + ov = min(ov_max, n - cf) + p = pcm_bytes(frames[-(cf + ov):], ov) + if p: yield p + elif flush: + rem = n % cf + if rem: + ov = min(ov_max, n - rem) + p = pcm_bytes(frames[-(rem + ov):], ov) + if p: yield p + +def voice_args(name): + if name and name != 'default' and name in V: + v = V[name] + dev = M['device'] + rc = v['ref_codes'].unsqueeze(0).to(dev) + se = v['spk_emb'].half().unsqueeze(0).to(dev) if 'spk_emb' in v else None + return {'ref_codes': rc, 'spk_emb': se} + return {} + +def register_voice(name, value, group='manual'): + V[name] = value + groups = {'builtin': V_builtin, 'unseen': V_unseen, 'manual': V_manual} + dst = groups[group] + if name not in dst: + dst.append(name) + for k, lst in groups.items(): + if k != group and name in lst: + lst.remove(name) + +def clone_voice_path(): + return os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'model', 'speaker', CLONE_FILE) + +def delete_manual_voice(name): + if name not in V_manual: + raise RuntimeError('只能删除手动克隆的音色') + out_path = clone_voice_path() + saved = torch.load(out_path, map_location='cpu') if os.path.exists(out_path) else {} + if name in saved: + saved.pop(name) + torch.save(saved, out_path) + V.pop(name, None) + if name in V_manual: + V_manual.remove(name) + +def normalize_voice_name(name): + name = ' '.join(str(name or '').split()) + if not name: + name = CLONE_VOICE + if len(name) > 24: + raise RuntimeError('音色名太长,建议控制在 24 个字以内') + if name.lower() == 'default': + raise RuntimeError('default 是保留名称,请换一个') + if name in V_builtin or name in V_unseen: + raise RuntimeError('该名称已被现有音色占用,请换一个') + return name + +def validate_clone_audio(w16): + if w16.numel() < int(16000 * 1.8): + raise RuntimeError('录音太短,请把整句话读完') + peak = w16.abs().max().item() + frame, hop = 800, 400 + if w16.numel() >= frame: + rms = w16.unfold(0, frame, hop).pow(2).mean(dim=1).sqrt().cpu().numpy() + else: + rms = np.array([w16.pow(2).mean().sqrt().item()]) + hi = float(np.quantile(rms, 0.95)) + lo = float(np.quantile(rms, 0.2)) + if hi < 0.008: + raise RuntimeError('录音太轻,请靠近麦克风一点') + if hi > 0 and lo / hi > 0.45: + raise RuntimeError('环境噪声太大,请换安静一点的环境') + if peak > 0.995: + raise RuntimeError('录音有爆音,请离麦克风远一点') + +def build_clone_voice(audio_b64): + if M.get('mimi') is None or M.get('campplus') is None or M.get('mel_fn') is None: + raise RuntimeError('Mimi 或 CAM++ 未加载') + seg = AudioSegment.from_file(io.BytesIO(base64.b64decode(audio_b64))).set_channels(1).set_sample_width(2) + if len(seg) < 1000: + raise RuntimeError('录音太短,至少读 1 秒') + try: + seg = seg.speedup(playback_speed=1.5, chunk_size=150, crossfade=25) + except Exception: + seg = seg.speedup(playback_speed=1.5) + seg24 = seg.set_frame_rate(24000) + seg16 = seg.set_frame_rate(16000) + w24 = torch.tensor(np.frombuffer(seg24.raw_data, dtype=np.int16).astype(np.float32) / 32768.0) + w16 = torch.tensor(np.frombuffer(seg16.raw_data, dtype=np.int16).astype(np.float32) / 32768.0) + validate_clone_audio(w16) + mimi_dev = next(M['mimi'].parameters()).device + mimi_dtype = torch.float16 if mimi_dev.type != 'cpu' else torch.float32 + with torch.inference_mode(): + t = w24.unsqueeze(0).unsqueeze(0).to(device=mimi_dev, dtype=mimi_dtype) + codes = M['mimi'].encode(t).audio_codes + nf = math.ceil(w24.shape[-1] / SAMPLES_PER_FRAME) + ref_codes = codes[0, :8, :nf].cpu()[:, :min(nf, REF_FRAMES)] + with torch.no_grad(): + mel = M['mel_fn'](w16.unsqueeze(0).to(M['device'])) + feat = mel.clamp(min=1e-10).log().transpose(1, 2) + feat = feat - feat.mean(dim=1, keepdim=True) + spk_emb = M['campplus'](feat).squeeze(0).cpu() + return {'ref_codes': ref_codes, 'spk_emb': spk_emb} + +def run_generate(x, audio_inputs, audio_lens, pixel_values, **kw): + with MODEL_LOCK, torch.no_grad(): + yield from M['model'].generate( + x, M['tokenizer'].eos_token_id, stream=True, return_audio_codes=True, + audio_inputs=audio_inputs, audio_lens=audio_lens, pixel_values=pixel_values, **kw) + +def load_main_model(model_path, model_name): + with MODEL_LOCK: + [sys.modules.pop(k) for k in list(sys.modules) if 'transformers_modules' in k] + M.pop('model', None); M.pop('tokenizer', None) + if torch.cuda.is_available(): torch.cuda.empty_cache() + tok = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) + m = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True) + vision_encoder, vision_processor = MiniMindOmni.load_vision('../model/siglip2-base-p32-256-ve') + audio_encoder, audio_processor = MiniMindOmni.load_sensevoice('../model/SenseVoiceSmall') + object.__setattr__(m, 'vision_encoder', vision_encoder) + object.__setattr__(m, 'vision_processor', vision_processor) + object.__setattr__(m, 'audio_encoder', audio_encoder) + object.__setattr__(m, 'audio_processor', audio_processor) + m = m.half().eval().to(M['device']) + if m.audio_encoder: m.audio_encoder.to(M['device']) + if m.vision_encoder: m.vision_encoder.to(M['device']) + M['tokenizer'], M['model'], M['model_name'] = tok, m, model_name + params = sum(p.numel() for p in m.parameters()) / 1e6 + print(f'Loaded model: {model_name} ({params:.2f}M)') + return round(params, 2) + +def prepare_turn(text, samples, image_b64, do_asr_for_image): + """返回 (audio_inputs, audio_lens, pixel_values, prompt_for_model, user_text_for_history, asr_thread, asr_result)""" + audio_inputs = audio_lens = pixel_values = None + prompt = text or '' + user_text = text or '' + asr_thread, asr_result = None, [None] + if samples is not None: + if image_b64 and do_asr_for_image: + user_text = asr_run(samples) + prompt = user_text + else: + audio_inputs, audio_lens, prompt = prep_audio(samples) + if M['cfg'].max_history_turns > 0: + sa = samples.copy() + def _a(): asr_result[0] = asr_run(sa) + asr_thread = threading.Thread(target=_a); asr_thread.start() + if image_b64: + pixel_values = prep_image(image_b64) + m = M['model'] + prompt = (prompt + "\n\n" if prompt else "") + "请描述这张图片\n\n" + m.config.image_special_token * m.config.image_token_len + return audio_inputs, audio_lens, pixel_values, prompt, user_text, asr_thread, asr_result + +# -------- routes -------- +@app.route('/') +def index(): return send_from_directory('.', 'web_demo.html') +@app.route('/call') +def call_page(): return send_from_directory('.', 'web_demo.html') + +@app.route('/voices') +def get_voices(): + return json.dumps({'builtin': sorted(V_builtin), 'unseen': sorted(V_unseen), 'manual': sorted(V_manual)}) + +@app.route('/models') +def get_models(): + return json.dumps({'models': list(M.get('models', {}).keys()), 'current': M.get('model_name')}) + +@app.route('/switch_model', methods=['POST']) +def switch_model(): + name = (request.json or {}).get('name') + if name not in M.get('models', {}): + return Response(json.dumps({'ok': False, 'error': 'unknown model'}), status=400, mimetype='application/json') + try: + params = load_main_model(M['models'][name], name) + return Response(json.dumps({'ok': True, 'model': name, 'params': params}), mimetype='application/json') + except Exception as e: + return Response(json.dumps({'ok': False, 'error': str(e)}), status=500, mimetype='application/json') + +@app.route('/clone_voice', methods=['POST']) +def clone_voice(): + d = request.json or {} + if not d.get('audio'): + return Response(json.dumps({'ok': False, 'error': 'missing audio'}), status=400, mimetype='application/json') + try: + name = normalize_voice_name(d.get('name')) + value = build_clone_voice(d['audio']) + out_path = clone_voice_path() + saved = torch.load(out_path, map_location='cpu') if os.path.exists(out_path) else {} + saved[name] = value + torch.save(saved, out_path) + register_voice(name, value, group='manual') + return Response(json.dumps({'ok': True, 'voice': name, 'path': './model/speaker/' + CLONE_FILE}), mimetype='application/json') + except Exception as e: + return Response(json.dumps({'ok': False, 'error': str(e)}), status=500, mimetype='application/json') + +@app.route('/delete_voice', methods=['POST']) +def delete_voice(): + d = request.json or {} + name = ' '.join(str(d.get('name') or '').split()) + if not name: + return Response(json.dumps({'ok': False, 'error': 'missing name'}), status=400, mimetype='application/json') + try: + delete_manual_voice(name) + return Response(json.dumps({'ok': True, 'voice': name}), mimetype='application/json') + except Exception as e: + return Response(json.dumps({'ok': False, 'error': str(e)}), status=500, mimetype='application/json') + +@app.route('/chat', methods=['POST']) +def chat(): + d = request.json + history = d.get('history', []) + samples = None + if d.get('audio'): + seg = AudioSegment.from_file(io.BytesIO(base64.b64decode(d['audio']))).set_frame_rate(16000).set_channels(1).set_sample_width(2) + samples = np.frombuffer(seg.raw_data, dtype=np.int16).astype(np.float32) / 32768.0 + va = voice_args(d.get('voice', 'default')) + + def gen(): + audio_inputs, audio_lens, pixel_values, prompt, user_text, asr_th, asr_res = prepare_turn( + d.get('text', ''), samples, d.get('image'), do_asr_for_image=True) + x = build_ids(prompt, history) + asr_sent = False + if user_text and samples is not None and d.get('image'): + yield sse({'type': 'user_prompt', 'content': user_text}); asr_sent = True + frames, text_ttft, audio_ttft = [], None, None + t0 = time.time(); hi = 0 + for y, af in run_generate(x, audio_inputs, audio_lens, pixel_values, + max_new_tokens=d.get('max_tokens', 512), + temperature=d.get('temperature', 1), top_p=0.85, **va): + if not asr_sent and asr_th and not asr_th.is_alive(): + asr_th.join() + if asr_res[0]: yield sse({'type': 'user_prompt', 'content': asr_res[0]}) + asr_sent = True + if y is not None: + if text_ttft is None: + text_ttft = (time.time() - t0) * 1000 + yield sse({'type': 'ttft', 'text_ttft': round(text_ttft, 1)}) + ans = M['tokenizer'].decode(y[0].tolist(), skip_special_tokens=True) + if ans and ans[-1] != '\ufffd' and len(ans) > hi: + yield sse({'type': 'text', 'content': ans[hi:]}); hi = len(ans) + if af: + if audio_ttft is None: + audio_ttft = (time.time() - t0) * 1000 + yield sse({'type': 'ttft', 'audio_ttft': round(audio_ttft, 1)}) + frames.append(af) + for pcm in stream_pcm(frames): + b64 = base64.b64encode(pcm).decode() + for i in range(0, len(b64), 2000): + yield sse({'type': 'pcm', 'c': b64[i:i+2000], 'd': i+2000 >= len(b64)}) + for pcm in stream_pcm(frames, flush=True): + b64 = base64.b64encode(pcm).decode() + for i in range(0, len(b64), 2000): + yield sse({'type': 'pcm', 'c': b64[i:i+2000], 'd': i+2000 >= len(b64)}) + if not asr_sent: + if asr_th: + asr_th.join() + if asr_res[0]: yield sse({'type': 'user_prompt', 'content': asr_res[0]}) + else: + yield sse({'type': 'user_prompt', 'content': prompt}) + yield sse({'type': 'done'}) + + return Response(gen(), mimetype='text/event-stream', headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'}) + + +@sock.route('/ws/realtime') +def realtime(ws): + session = RealtimeSession(M['vad_path']) + q = queue.Queue(); alive = [True]; state = {'history': [], 'image': None} + n_hist = M['cfg'].max_history_turns + + def push_audio(data): + return session.push_chunk(np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0) + + def set_ctx(msg): + h = msg.get('history') or [] + state['history'] = h[-n_hist:] if n_hist > 0 else [] + if 'image' in msg: state['image'] = msg.get('image') + if 'voice' in msg: state['voice'] = msg.get('voice', 'default') + + def poll_interrupt(): + while True: + try: data = q.get_nowait() + except queue.Empty: return False + if isinstance(data, bytes): + if push_audio(data) == 'interrupt': return True + ws.send(json.dumps({'type': 'vad', 'speaking': session.speaking})) + else: + m = json.loads(data) + if m.get('type') == 'context': set_ctx(m) + elif m.get('type') in ('stop', 'end'): + if m['type'] == 'end': alive[0] = False + session.interrupt = True; return True + + def recv_loop(): + while alive[0]: + try: + data = ws.receive(timeout=1) + if data is None: alive[0] = False; break + q.put(data) + except: alive[0] = False; break + + threading.Thread(target=recv_loop, daemon=True).start() + try: + while alive[0]: + try: data = q.get(timeout=0.05) + except queue.Empty: continue + if isinstance(data, str): + m = json.loads(data) + if m.get('type') == 'context': set_ctx(m) + elif m.get('type') == 'stop': session.interrupt = True + elif m.get('type') == 'end': break + continue + if session.generating: + push_audio(data); ws.send(json.dumps({'type': 'vad', 'speaking': session.speaking})); continue + status = push_audio(data) + ws.send(json.dumps({'type': 'vad', 'speaking': session.speaking})) + if status != 'speech_end': continue + + session.generating = True + audio = session.get_audio() + ws.send(json.dumps({'type': 'generating'})) + audio_inputs, audio_lens, pixel_values, prompt, user_text, asr_th, asr_res = prepare_turn( + '', audio, state['image'], do_asr_for_image=True) + if state['image']: state['image'] = None + x = build_ids(prompt, state['history']) + va_rt = voice_args(state.get('voice', 'default')) + + frames, full_text, interrupted = [], '', False + for y, af in run_generate(x, audio_inputs, audio_lens, pixel_values, + max_new_tokens=512, temperature=0.7, **va_rt): + if poll_interrupt() or session.interrupt: interrupted = True; break + if y is not None: + ans = M['tokenizer'].decode(y[0].tolist(), skip_special_tokens=True) + if ans and ans[-1] != '\ufffd' and len(ans) > len(full_text): + ws.send(json.dumps({'type': 'text', 'content': ans[len(full_text):]})); full_text = ans + if af: + frames.append(af) + for pcm in stream_pcm(frames): + ws.send(json.dumps({'type': 'pcm', 'data': base64.b64encode(pcm).decode()})) + if not interrupted: + for pcm in stream_pcm(frames, flush=True): + ws.send(json.dumps({'type': 'pcm', 'data': base64.b64encode(pcm).decode()})) + if asr_th: + asr_th.join(); user_text = asr_res[0] or user_text + if n_hist > 0: + if user_text: state['history'].append({'role': 'user', 'content': user_text}) + if full_text: state['history'].append({'role': 'assistant', 'content': full_text}) + state['history'] = state['history'][-n_hist:] + ws.send(json.dumps({'type': 'done', 'interrupted': interrupted or session.interrupt})) + session.generating = False; session.interrupt = False + finally: + alive[0] = False + + +def init_model(args): + M['cfg'] = args; M['device'] = args.device + with contextlib.redirect_stdout(io.StringIO()): + M['asr'] = AutoModel(model='../model/SenseVoiceSmall', trust_remote_code=True, device=args.device, disable_update=True) + M['models'] = scan_hf_models(args.load_from) + if not M['models']: + raise RuntimeError(f"未在 {os.path.abspath(args.load_from)} 找到 transformers 模型") + model_name = next(iter(M['models'])) + load_main_model(M['models'][model_name], model_name) + try: + from transformers import MimiModel + M['mimi'] = MimiModel.from_pretrained('../model/mimi').eval() + print('Mimi model loaded') + except: M['mimi'] = None + try: + from modelscope.models.audio.sv.DTDNN import CAMPPlus + M['campplus'] = CAMPPlus(feat_dim=80, embedding_size=192, growth_rate=32, bn_size=4, + init_channels=128, config_str='batchnorm-relu', memory_efficient=True) + sd = torch.load('../model/campplus/campplus_cn_common.pt', map_location='cpu') + M['campplus'].load_state_dict({k: v.float() for k, v in sd.items()}) + M['campplus'] = M['campplus'].eval().to(args.device) + M['mel_fn'] = torchaudio.transforms.MelSpectrogram( + sample_rate=16000, n_fft=512, win_length=400, hop_length=160, + n_mels=80, f_min=20, f_max=7600, norm='slaney', mel_scale='slaney', + ).to(args.device) + print('CAM++ loaded') + except Exception as e: + M['campplus'], M['mel_fn'] = None, None + print(f'CAM++ load failed: {e}') + M['vad_path'] = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'model', 'vad', 'silero_vad.onnx') + spk_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'model', 'speaker') + for fn, group in [('voices.pt', 'builtin'), ('voices_unseen.pt', 'unseen'), (CLONE_FILE, 'manual')]: + fp = os.path.join(spk_dir, fn) + if os.path.exists(fp): + for speaker, v in torch.load(fp, map_location=args.device).items(): + if speaker not in V or fn == CLONE_FILE: + register_voice(speaker, v, group=group) + if V: print(f'Loaded {len(V)} voices: builtin={sorted(V_builtin)}, unseen={sorted(V_unseen)}, manual={sorted(V_manual)}') + log_model_params(M['model']) + print('Warmup...') + with torch.no_grad(): + ids = torch.tensor([[1, 2, 3]], device=args.device) + au = torch.full((1, 8, 3), 2049, dtype=torch.long, device=args.device) + M['model'].forward(torch.cat((au, ids.unsqueeze(1)), dim=1)) + if M['model'].audio_encoder: M['model'].audio_encoder(torch.zeros(1, 100, 560, device=args.device), torch.tensor([100], device=args.device)) + if M['mimi']: M['mimi'].decode(torch.zeros(1, 8, 1, dtype=torch.long)) + print('Warmup done!') + + +if __name__ == '__main__': + p = argparse.ArgumentParser() + p.add_argument('--load_from', default='./') + p.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu') + p.add_argument('--port', default=7860, type=int) + p.add_argument('--audio_chunk_frames', default=4, type=int) + p.add_argument('--audio_overlap', default=2, type=int) + p.add_argument('--max_history_turns', default=0, type=int) + args = p.parse_args() + init_model(args) + app.run(host='0.0.0.0', port=args.port, threaded=True)