``` ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── README_en.md ├── dataset/ ├── __init__.py ├── lm_dataset.py ├── eval_model.py ├── images/ ├── 1-wiki.png ├── 2-wiki.png ├── 3-wiki.png ├── 4-wiki.png ├── 5-wiki.png ├── LLM-structure-moe.png ├── LLM-structure.png ├── and_huggingface.png ├── and_modelscope.png ├── compare_radar.png ├── dataset.jpg ├── gpt3_config.png ├── logo.png ├── logo2.png ├── minimind2.gif ├── pre_512_loss.png ├── pre_768_loss.png ├── sft_512_loss.png ├── sft_768_loss.png ├── model/ ├── __init__.py ├── model_lora.py ├── model_minimind.py ├── tokenizer.json ``` ## /.gitignore ```gitignore path="/.gitignore" /model/__pycache__ /dataset /out ``` ## /CODE_OF_CONDUCT.md # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ## /LICENSE ``` path="/LICENSE" Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` ## /README.md
![logo](./images/logo.png)
![visitors](https://visitor-badge.laobi.icu/badge?page_id=jingyaogong/minimind) [![GitHub Repo stars](https://img.shields.io/github/stars/jingyaogong/minimind?style=social)](https://github.com/jingyaogong/minimind/stargazers) [![GitHub Code License](https://img.shields.io/github/license/jingyaogong/minimind)](LICENSE) [![GitHub last commit](https://img.shields.io/github/last-commit/jingyaogong/minimind)](https://github.com/jingyaogong/minimind/commits/master) [![GitHub pull request](https://img.shields.io/badge/PRs-welcome-blue)](https://github.com/jingyaogong/minimind/pulls) [![Collection](https://img.shields.io/badge/🤗-MiniMind%20%20Collection-blue)](https://huggingface.co/collections/jingyaogong/minimind-66caf8d999f5c7fa64f399e5)

"大道至简"

中文 | [English](./README_en.md)
* 此开源项目旨在完全从0开始,仅用3块钱成本 + 2小时!即可训练出仅为25.8M的超小语言模型**MiniMind**。 * **MiniMind**系列极其轻量,最小版本体积是 GPT-3 的 $\frac{1}{7000}$,力求做到最普通的个人GPU也可快速训练。 * 项目同时开源了大模型的极简结构-包含拓展共享混合专家(MoE)、数据集清洗、预训练(Pretrain)、监督微调(SFT)、LoRA微调, 直接偏好强化学习(DPO)算法、模型蒸馏算法等全过程代码。 * **MiniMind**同时拓展了视觉多模态的VLM: [MiniMind-V](https://github.com/jingyaogong/minimind-v)。 * 项目所有核心算法代码均从0使用PyTorch原生重构!不依赖第三方库提供的抽象接口。 * 这不仅是大语言模型的全阶段开源复现,也是一个入门LLM的教程。 * 希望此项目能为所有人提供一个抛砖引玉的示例,一起感受创造的乐趣!推动更广泛AI社区的进步! > 为防止误解,“2小时” 基于NVIDIA 3090硬件设备(单卡)测试,“3块钱” > 指GPU服务器租用成本,具体规格详情见下文。 ---
![minimind2](./images/minimind2.gif) [🔗🍓推理模型](https://www.modelscope.cn/studios/gongjy/MiniMind-Reasoning) | [🔗🤖常规模型](https://www.modelscope.cn/studios/gongjy/MiniMind) | [🔗🎞️视频介绍](https://www.bilibili.com/video/BV12dHPeqE72/?share_source=copy_web&vd_source=670c2504f88726f8cf4a21ef6147c0e8)
Hugging Face Logo ModelScope Logo
# 📌 Introduction 大语言模型(Large Language Model, LLM)的出现引发了全世界对AI的空前关注。 无论是ChatGPT、DeepSeek还是Qwen,都以其惊艳的效果令人叹为观止。 然而,动辄数百亿参数的庞大规模,使得它们对个人设备而言不仅难以训练,甚至连部署都显得遥不可及。 打开大模型的“黑盒子”,探索其内部运作机制,多么令人心潮澎湃! 遗憾的是,99%的探索只能止步于使用LoRA等技术对现有大模型进行少量微调,学习一些新指令或任务。 这就好比教牛顿如何使用21世纪的智能手机——虽然有趣,却完全偏离了理解物理本质的初衷。 与此同时,第三方的大模型框架和工具库,如transformers+trl,几乎只暴露了高度抽象的接口。 通过短短10行代码,就能完成“加载模型+加载数据集+推理+强化学习”的全流程训练。 这种高效的封装固然便利,但也像一架高速飞船,将我们与底层实现隔离开来,阻碍了深入探究LLM核心代码的机会。 然而,“用乐高拼出一架飞机,远比坐在头等舱里飞行更让人兴奋!”。 更糟糕的是,互联网上充斥着大量付费课程和营销号,以漏洞百出、一知半解的内容推销AI教程。 正因如此,本项目初衷是拉低LLM的学习门槛,让每个人都能从理解每一行代码开始, 从零开始亲手训练一个极小的语言模型。是的,从**零开始训练**,而不是仅仅进行**推理**! 最低只需3块钱不到的服务器成本,就能亲身体验从0到1构建一个语言模型的全过程。 一起感受创造的乐趣吧! > [!NOTE] > (截至2025-02-07)MiniMind系列已完成多个型号模型的预训练,最小仅需25.8M(0.02B),即可具备流畅对话能力!
Models List | 模型 (大小) | 推理占用 (约) | Release | |-------------------------|----------|------------| | MiniMind2-small (26M) | 0.5 GB | 2025.04.26 | | MiniMind2-MoE (145M) | 1.0 GB | 2025.04.26 | | MiniMind2 (104M) | 1.0 GB | 2025.04.26 | | minimind-v1-small (26M) | 0.5 GB | 2024.08.28 | | minimind-v1-moe (4×26M) | 1.0 GB | 2024.09.17 | | minimind-v1 (108M) | 1.0 GB | 2024.09.01 |
**项目包含** - MiniMind-LLM结构的全部代码(Dense+MoE模型)。 - 包含Tokenizer分词器详细训练代码。 - 包含Pretrain、SFT、LoRA、RLHF-DPO、模型蒸馏的全过程训练代码。 - 收集、蒸馏、整理并清洗去重所有阶段的高质量数据集,且全部开源。 - 从0实现预训练、指令微调、LoRA、DPO强化学习,白盒模型蒸馏。关键算法几乎不依赖第三方封装的框架,且全部开源。 - 同时兼容`transformers`、`trl`、`peft`等第三方主流框架。 - 训练支持单机单卡、单机多卡(DDP、DeepSpeed)训练,支持wandb可视化训练流程。支持动态启停训练。 - 在第三方测评榜(C-Eval、C-MMLU、OpenBookQA等)进行模型测试。 - 实现Openai-Api协议的极简服务端,便于集成到第三方ChatUI使用(FastGPT、Open-WebUI等)。 - 基于streamlit实现最简聊天WebUI前端。 - 全面兼容社区热门`llama.cpp`、`vllm`、`ollama`推理引擎或`Llama-Factory`训练框架。 - 复现(蒸馏/RL)大型推理模型DeepSeek-R1的MiniMind-Reason模型,**数据+模型**全部开源! 希望此开源项目可以帮助LLM初学者快速入门! ### 👉**更新日志**
2025-04-26 (newest 🎉🎉🎉) - 重要更新 - 如有兼容性需要,可访问[🔗旧仓库内容🔗](https://github.com/jingyaogong/minimind/tree/7da201a944a90ed49daef8a0265c959288dff83a)。 - MiniMind模型参数完全改名,对齐Transformers库模型(统一命名)。 - generate方式重构,继承自GenerationMixin类。 - 🔥支持llama.cpp、vllm、ollama等热门三方生态。 - 规范代码和目录结构。 - 改动词表``->`<|im_start|><|im_end|>` ```text 为兼容第三方推理框架llama.cpp、vllm,本次更新需付出一些可观代价。 本次更新不再支持「直接」加载25-04-26以前的旧模型进行推理。 由于Llama位置编码方式与minimind存在区别,导致映射Llama模型后QK值存在差异 MiniMind2系列旧模型均经过权重映射+(微调训练)QKVO线性层校准恢复而来。 本次更新后将放弃对`minimind-v1`全系列的维护,并在仓库中下线。 ```
2025-02-09 - 迎来发布以来重大更新,Release MiniMind2 Series。 - 代码几乎全部重构,使用更简洁明了的统一结构。 如有旧代码的兼容性需要,可访问[🔗旧仓库内容🔗](https://github.com/jingyaogong/minimind/tree/6e9cd28ef9b34a0a10afbdf6f59e65cb6e628efb)。 - 免去数据预处理步骤。统一数据集格式,更换为`jsonl`格式杜绝数据集下载混乱的问题。 - MiniMind2系列效果相比MiniMind-V1显著提升。 - 小问题:{kv-cache写法更标准、MoE的负载均衡loss被考虑等等} - 提供模型迁移到私有数据集的训练方案(医疗模型、自我认知样例)。 - 精简预训练数据集,并大幅提升预训练数据质量,大幅缩短个人快速训练所需时间,单卡3090即可2小时复现! - 更新:LoRA微调脱离peft包装,从0实现LoRA过程;DPO算法从0使用PyTorch原生实现;模型白盒蒸馏原生实现。 - MiniMind2-DeepSeek-R1系列蒸馏模型诞生! - MiniMind2具备一定的英文能力! - 更新MiniMind2与第三方模型的基于更多大模型榜单测试性能的结果。
2024-10-05 - 为MiniMind拓展了多模态能力之---视觉 - 移步孪生项目[minimind-v](https://github.com/jingyaogong/minimind-v)查看详情!
2024-09-27 - 09-27更新pretrain数据集的预处理方式,为了保证文本完整性,放弃预处理成.bin训练的形式(轻微牺牲训练速度)。 - 目前pretrain预处理后的文件命名为:pretrain_data.csv。 - 删除了一些冗余的代码。
2024-09-17 - 更新minimind-v1-moe模型 - 为了防止歧义,不再使用mistral_tokenizer分词,全部采用自定义的minimind_tokenizer作为分词器。
2024-09-01 - 更新minimind-v1 (108M)模型,采用minimind_tokenizer,预训练轮次3 + SFT轮次10,更充分训练,性能更强。 - 项目已部署至ModelScope创空间,可以在此网站上体验: - [🔗ModelScope在线体验🔗](https://www.modelscope.cn/studios/gongjy/minimind)
2024-08-27 - 项目首次开源
# 📌 快速开始
分享本人的软硬件配置(仅供参考) * 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.16 * [requirements.txt](./requirements.txt)
### 第0步 ```bash git clone https://github.com/jingyaogong/minimind.git ``` ## Ⅰ 测试已有模型效果 ### 1.环境准备 ```bash pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ``` ### 2.下载模型 到项目根目录 ```bash git clone https://huggingface.co/jingyaogong/MiniMind2 ``` ### (可选)命令行问答 ```bash # load=0: load from pytorch model, load=1: load from transformers-hf model python eval_model.py --load 1 --model_mode 2 ``` ### (可选)启动WebUI ```bash # 可能需要`python>=3.10` 安装 `pip install streamlit` # cd scripts streamlit run web_demo.py ``` ### (可选)第三方推理框架 ```bash # ollama ollama run jingyaogong/minimind2 # vllm vllm serve ./MiniMind2/ --served-model-name "minimind" ``` ## Ⅱ 从0开始自己训练 ### 1.环境准备 ```bash pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ```
注:提前测试Torch是否可用cuda ```bash import torch print(torch.cuda.is_available()) ``` 如果不可用,请自行去[torch_stable](https://download.pytorch.org/whl/torch_stable.html) 下载whl文件安装。参考[链接](https://blog.csdn.net/weixin_45456738/article/details/141029610?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E8%A3%85torch&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-141029610.nonecase&spm=1018.2226.3001.4187)
### 2.数据下载 从下文提供的[数据集下载链接](https://www.modelscope.cn/datasets/gongjy/minimind_dataset/files) 下载需要的数据文件(创建`./dataset`目录)并放到`./dataset`下
注:数据集须知 默认推荐下载`pretrain_hq.jsonl` + `sft_mini_512.jsonl`最快速度复现Zero聊天模型。 数据文件可自由选择,下文提供了多种搭配方案,可根据自己手头的训练需求和GPU资源进行适当组合。
### 3.开始训练 目录位于`trainer` **3.1 预训练(学知识)** ```bash python train_pretrain.py ``` > 执行预训练,得到 `pretrain_*.pth` 作为预训练的输出权重(其中*为模型的dimension,默认为512) **3.2 监督微调(学对话方式)** ```bash python train_full_sft.py ``` > 执行监督微调,得到 `full_sft_*.pth` 作为指令微调的输出权重(其中`full`即为全参数微调)
注:训练须知 所有训练过程默认每隔100步保存1次参数到文件`./out/***.pth`(每次会覆盖掉旧权重文件)。 简单起见,此处只写明两个阶段训练过程。如需其它训练 (LoRA, 蒸馏, 强化学习, 微调推理等) 可参考下文【实验】小节的详细说明。
--- ### 4.测试模型效果 确保需要测试的模型`*.pth`文件位于`./out/`目录下。 也可以直接去[此处](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch/files)下载使用我训练的`*.pth`文件。 ```bash python eval_model.py --model_mode 1 # 默认为0:测试pretrain模型效果,设置为1:测试full_sft模型效果 ```
注:测试须知 如需详情,查看`eval_model.py`脚本代码即可。model_mode分为 0: 预训练模型,1: SFT-Chat模型,2: RLHF-Chat模型,3: Reason模型
--- > [!TIP] > 所有训练脚本均为Pytorch原生框架,均支持多卡加速,假设你的设备有N (N>1) 张显卡: 单机N卡启动训练方式 (DDP, 支持多机多卡集群) ```bash torchrun --nproc_per_node N train_xxx.py ```
注:其它须知 单机N卡启动训练 (DeepSpeed) ```bash deepspeed --master_port 29500 --num_gpus=N train_xxx.py ``` 可根据需要开启wandb记录训练过程 ```bash # 需要登录: wandb login torchrun --nproc_per_node N train_xxx.py --use_wandb # and python train_xxx.py --use_wandb ``` 通过添加`--use_wandb`参数,可以记录训练过程,训练完成后,可以在wandb网站上查看训练过程。通过修改`wandb_project` 和`wandb_run_name`参数,可以指定项目名称和运行名称。
# 📌 数据介绍 ## Ⅰ Tokenizer 分词器将单词从自然语言通过“词典”映射到`0, 1, 36`这样的数字,可以理解为数字就代表了单词在“词典”中的页码。 可以选择自己构造词表训练一个“词典”,代码可见`./scripts/train_tokenizer.py`(仅供学习参考,若非必要无需再自行训练,MiniMind已自带tokenizer)。 或者选择比较出名的开源大模型分词器, 正如同直接用新华/牛津词典的优点是token编码压缩率很好,缺点是页数太多,动辄数十万个词汇短语; 自己训练的分词器,优点是词表长度和内容随意控制,缺点是压缩率很低(例如"hello"也许会被拆分为"h e l l o" 五个独立的token),且生僻词难以覆盖。 “词典”的选择固然很重要,LLM的输出本质上是SoftMax到词典N个词的多分类问题,然后通过“词典”解码到自然语言。 因为MiniMind体积需要严格控制,为了避免模型头重脚轻(词嵌入embedding层参数在LLM占比太高),所以词表长度短短益善。
Tokenizer介绍 第三方强大的开源模型例如Yi、qwen、chatglm、mistral、Llama3的tokenizer词表长度如下:
Tokenizer模型词表大小来源
yi tokenizer64,00001万物(中国)
qwen2 tokenizer151,643阿里云(中国)
glm tokenizer151,329智谱AI(中国)
mistral tokenizer32,000Mistral AI(法国)
llama3 tokenizer128,000Meta(美国)
minimind tokenizer6,400自定义
> 👉2024-09-17更新:为了防止过去的版本歧义&控制体积,minimind所有模型均使用minimind_tokenizer分词,废弃所有mistral_tokenizer版本。 ``` # 一些自言自语 > 尽管minimind_tokenizer长度很小,编解码效率弱于qwen2、glm等中文友好型分词器。 > 但minimind模型选择了自己训练的minimind_tokenizer作为分词器,以保持整体参数轻量,避免编码层和计算层占比失衡,头重脚轻,因为minimind的词表大小只有6400。 > 且minimind在实际测试中没有出现过生僻词汇解码失败的情况,效果良好。 > 由于自定义词表压缩长度到6400,使得LLM总参数量最低只有25.8M。 > 训练数据`tokenizer_train.jsonl`均来自于`匠数大模型数据集`,这部分数据相对次要,如需训练可以自由选择。 ```
## Ⅱ Pretrain数据 经历了MiniMind-V1的低质量预训练数据,导致模型胡言乱语的教训,`2025-02-05` 之后决定不再采用大规模无监督的数据集做预训练。 进而尝试把[匠数大模型数据集](https://www.modelscope.cn/datasets/deepctrl/deepctrl-sft-data)的中文部分提取出来, 清洗出字符`<512`长度的大约1.6GB的语料直接拼接成预训练数据 `pretrain_hq.jsonl`,hq即为high quality(当然也还不算high,提升数据质量无止尽)。 文件`pretrain_hq.jsonl` 数据格式为 ```bash {"text": "如何才能摆脱拖延症? 治愈拖延症并不容易,但以下建议可能有所帮助..."} ``` ## Ⅲ SFT数据 [匠数大模型SFT数据集](https://www.modelscope.cn/datasets/deepctrl/deepctrl-sft-data) “是一个完整、格式统一、安全的大模型训练和研究资源。 从网络上的公开数据源收集并整理了大量开源数据集,对其进行了格式统一,数据清洗, 包含10M条数据的中文数据集和包含2M条数据的英文数据集。” 以上是官方介绍,下载文件后的数据总量大约在4B tokens,肯定是适合作为中文大语言模型的SFT数据的。 但是官方提供的数据格式很乱,全部用来sft代价太大。 我将把官方数据集进行了二次清洗,把含有符号污染和噪声的条目去除;另外依然只保留了总长度`<512` 的内容,此阶段希望通过大量对话补充预训练阶段欠缺的知识。 导出文件为`sft_512.jsonl`(~7.5GB)。 [Magpie-SFT数据集](https://www.modelscope.cn/organization/Magpie-Align) 收集了~1M条来自Qwen2/2.5的高质量对话,我将这部分数据进一步清洗,把总长度`<2048`的部分导出为`sft_2048.jsonl`(~9GB)。 长度`<1024`的部分导出为`sft_1024.jsonl`(~5.5GB),用大模型对话数据直接进行sft就属于“黑盒蒸馏”的范畴。 进一步清洗前两步sft的数据(只保留中文字符占比高的内容),筛选长度`<512`的对话,得到`sft_mini_512.jsonl`(~1.2GB)。 所有sft文件 `sft_X.jsonl` 数据格式均为 ```text { "conversations": [ {"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好!"}, {"role": "user", "content": "再见"}, {"role": "assistant", "content": "再见!"} ] } ``` ## Ⅳ RLHF数据 来自[Magpie-DPO数据集](https://www.modelscope.cn/datasets/Magpie-Align/MagpieLM-DPO-Data-v0.1) 大约200k条偏好数据(均是英文)生成自Llama3.1-70B/8B,可以用于训练奖励模型,优化模型回复质量,使其更加符合人类偏好。 这里将数据总长度`<3000`的内容重组为`dpo.jsonl`(~0.9GB),包含`chosen`和`rejected`两个字段,`chosen` 为偏好的回复,`rejected`为拒绝的回复。 文件 `dpo.jsonl` 数据格式为 ```text { "chosen": [ {"content": "Q", "role": "user"}, {"content": "good answer", "role": "assistant"} ], "rejected": [ {"content": "Q", "role": "user"}, {"content": "bad answer", "role": "assistant"} ] } ``` ## Ⅴ Reason数据集: 不得不说2025年2月谁能火的过DeepSeek... 也激发了我对RL引导的推理模型的浓厚兴趣,目前已经用Qwen2.5复现了R1-Zero。 如果有时间+效果work(但99%基模能力不足)我会在之后更新MiniMind基于RL训练的推理模型而不是蒸馏模型。 时间有限,最快的低成本方案依然是直接蒸馏(黑盒方式)。 耐不住R1太火,短短几天就已经存在一些R1的蒸馏数据集[R1-Llama-70B](https://www.modelscope.cn/datasets/Magpie-Align/Magpie-Reasoning-V2-250K-CoT-Deepseek-R1-Llama-70B)、[R1-Distill-SFT](https://www.modelscope.cn/datasets/AI-ModelScope/R1-Distill-SFT)、 [Alpaca-Distill-R1](https://huggingface.co/datasets/shareAI/Alpaca-Distill-R1-ZH)、 [deepseek_r1_zh](https://huggingface.co/datasets/jinliuxi/deepseek_r1_zh)等等,纯中文的数据可能比较少。 最终整合它们,导出文件为`r1_mix_1024.jsonl`,数据格式和`sft_X.jsonl`一致。 ## Ⅵ 更多数据集 目前已经有[HqWu-HITCS/Awesome-Chinese-LLM](https://github.com/HqWu-HITCS/Awesome-Chinese-LLM) 在收集和梳理中文LLM相关的开源模型、应用、数据集及教程等资料,并持续更新这方面的最新进展。全面且专业,Respect! --- ## Ⅷ MiniMind训练数据集 > [!NOTE] > 2025-02-05后,开源MiniMind最终训练所用的所有数据集,因此无需再自行预处理大规模数据集,避免重复性的数据处理工作。 MiniMind训练数据集下载地址: [ModelScope](https://www.modelscope.cn/datasets/gongjy/minimind_dataset/files) | [HuggingFace](https://huggingface.co/datasets/jingyaogong/minimind_dataset/tree/main) > 无需全部clone,可单独下载所需的文件 将下载的数据集文件放到`./dataset/`目录下(✨为推荐的必须项) ```bash ./dataset/ ├── dpo.jsonl (909MB) ├── lora_identity.jsonl (22.8KB) ├── lora_medical.jsonl (34MB) ├── pretrain_hq.jsonl (1.6GB, ✨) ├── r1_mix_1024.jsonl (340MB) ├── sft_1024.jsonl (5.6GB) ├── sft_2048.jsonl (9GB) ├── sft_512.jsonl (7.5GB) ├── sft_mini_512.jsonl (1.2GB, ✨) └── tokenizer_train.jsonl (1GB) ```
注:各数据集简介 * `dpo.jsonl` --RLHF阶段数据集 * `lora_identity.jsonl` --自我认知数据集(例如:你是谁?我是minimind...),推荐用于lora训练(亦可用于全参SFT,勿被名字局限) * `lora_medical.jsonl` --医疗问答数据集,推荐用于lora训练(亦可用于全参SFT,勿被名字局限) * `pretrain_hq.jsonl`✨ --预训练数据集,整合自jiangshu科技 * `r1_mix_1024.jsonl` --DeepSeek-R1-1.5B蒸馏数据,每条数据字符最大长度为1024(因此训练时设置max_seq_len=1024) * `sft_1024.jsonl` --整合自Qwen2.5蒸馏数据(是sft_2048的子集),每条数据字符最大长度为1024(因此训练时设置max_seq_len=1024) * `sft_2048.jsonl` --整合自Qwen2.5蒸馏数据,每条数据字符最大长度为2048(因此训练时设置max_seq_len=2048) * `sft_512.jsonl` --整合自匠数科技SFT数据,每条数据字符最大长度为512(因此训练时设置max_seq_len=512) * `sft_mini_512.jsonl`✨ --极简整合自匠数科技SFT数据+Qwen2.5蒸馏数据(用于快速训练Zero模型),每条数据字符最大长度为512(因此训练时设置max_seq_len=512) * `tokenizer_train.jsonl` --均来自于`匠数大模型数据集`,这部分数据相对次要,(不推荐自己重复训练tokenizer,理由如上)如需自己训练tokenizer可以自由选择数据集。
![dataset](./images/dataset.jpg)
说明 & 推荐训练方案 * MiniMind2 Series均经过共约20GB语料训练,大约4B tokens,即对应上面的数据组合训练结果(开销:💰💰💰💰💰💰💰💰,效果:😊😊😊😊😊😊) * 想要最快速度从0实现Zero模型,推荐使用`pretrain_hq.jsonl` + `sft_mini_512.jsonl` 的数据组合,具体花销和效果可查看下文表格(开销:💰,效果:😊😊) * 推荐具备一定算力资源或更在意效果的朋友可以考虑前者完整复现MiniMind2;仅有单卡GPU或在乎短时间快速复现的朋友强烈推荐后者; * 【折中方案】亦可选择例如`sft_mini_512.jsonl`、`sft_1024.jsonl`中等规模数据进行自由组合训练(开销:💰💰💰,效果:😊😊😊😊)。
# 📌 Model Structure MiniMind-Dense(和[Llama3.1](https://ai.meta.com/blog/meta-llama-3-1/)一样)使用了Transformer的Decoder-Only结构,跟GPT-3的区别在于: * 采用了GPT-3的预标准化方法,也就是在每个Transformer子层的输入上进行归一化,而不是在输出上。具体来说,使用的是RMSNorm归一化函数。 * 用SwiGLU激活函数替代了ReLU,这样做是为了提高性能。 * 像GPT-Neo一样,去掉了绝对位置嵌入,改用了旋转位置嵌入(RoPE),这样在处理超出训练长度的推理时效果更好。 --- MiniMind-MoE模型,它的结构基于Llama3和[Deepseek-V2/3](https://arxiv.org/pdf/2405.04434)中的MixFFN混合专家模块。 * DeepSeek-V2在前馈网络(FFN)方面,采用了更细粒度的专家分割和共享的专家隔离技术,以提高Experts的效果。 --- MiniMind的整体结构一致,只是在RoPE计算、推理函数和FFN层的代码上做了一些小调整。 其结构如下图(重绘版): ![structure](./images/LLM-structure.png) ![structure-moe](./images/LLM-structure-moe.png) 修改模型配置见[./model/LMConfig.py](./model/LMConfig.py)。 参考模型参数版本见下表: | Model Name | params | len_vocab | rope_theta | n_layers | d_model | kv_heads | q_heads | share+route | |-------------------|--------|-----------|------------|----------|---------|----------|---------|-------------| | MiniMind2-Small | 26M | 6400 | 1e6 | 8 | 512 | 2 | 8 | - | | MiniMind2-MoE | 145M | 6400 | 1e6 | 8 | 640 | 2 | 8 | 1+4 | | MiniMind2 | 104M | 6400 | 1e6 | 16 | 768 | 2 | 8 | - | | minimind-v1-small | 26M | 6400 | 1e4 | 8 | 512 | 8 | 16 | - | | minimind-v1-moe | 4×26M | 6400 | 1e4 | 8 | 512 | 8 | 16 | 1+4 | | minimind-v1 | 108M | 6400 | 1e4 | 16 | 768 | 8 | 16 | - | # 📌 Experiment ## Ⅰ 训练开销 - **时间单位**:小时 (h)。 - **成本单位**:人民币 (¥);7¥ ≈ 1美元。 - **3090 租卡单价**:≈1.3¥/h(可自行参考实时市价)。 - **参考标准**:表格仅实测 `pretrain` 和 `sft_mini_512` 两个数据集的训练时间,其它耗时根据数据集大小估算(可能存在些许出入)。 > 基于 3090 (单卡)成本计算 | Model Name | params | pretrain | sft_mini_512 | sft_512 | sft_1024 | sft_2048 | RLHF | |-----------------|--------|------------------|------------------|---------------|-------------------|------------------|---------------| | MiniMind2-Small | 26M | ≈1.1h
≈1.43¥ | ≈1h
≈1.3¥ | ≈6h
≈7.8¥ | ≈4.58h
≈5.95¥ | ≈7.5h
≈9.75¥ | ≈1h
≈1.3¥ | | MiniMind2 | 104M | ≈3.9h
≈5.07¥ | ≈3.3h
≈4.29¥ | ≈20h
≈26¥ | ≈15h
≈19.5¥ | ≈25h
≈32.5¥ | ≈3h
≈3.9¥ | ---
训练开销总结&预测 > MiniMind2-Small参数 >> `pretrain_hq`+`sft_mini_512`数据集
单卡3090 (1 epoch) + 2.1小时 + 花费2.73元人民币
即可从0训练出MiniMind-Zero-0.025B模型!!! > MiniMind2-Small参数 >> `pretrain_hq`+`sft_512`+`sft_2048`+`dpo`数据集
单卡3090 (2 epochs) + 大约38.16小时 + 花费49.61元人民币
即可从0训练出MiniMind2-Small-0.025B模型!!! > MiniMind2参数 >> `pretrain_hq`+`sft_512`+`sft_2048`+`dpo`数据集
单卡3090 (2 epochs) + 大约122小时 + 花费158.6元人民币
即可从0训练出MiniMind2-0.1B模型!!!
✨基于单卡NVIDIA 3090的`MiniMind-Zero`从0训练仅需`2小时` + `3块钱`,实现ChatBot效果! ✨PS:若采用8卡4090训练,总用时甚至可以压缩到10分钟以内!(由于时间更短,花费同样3元左右,与单卡成本相当) ✨以极低极低的门槛,实现人人可玩的大模型自由!这正是MiniMind系列的诞生初衷! ✨仅价值`3块钱`成本的`MiniMind-Zero`并不是噱头!Chat测试: ```text 👶: 请介绍一下自己。 🤖️: 作为人工智能,我没有实际的生活,也没有自我意识,所以没有自己的生活。我被设计成能够帮助用户解答问题、提供信息、进行对话等。我的设计和功能是由计算机科学、机器学习、人工智能技术和算法所构建的,我通过编程和算法实现。 👶: 推荐一些杭州的特色美食吧。 🤖️: 杭州有很多美味的特色美食,比如鸡肉串、鳗鱼头、水煮鱼、豆腐脑等。这些美食都非常美味,有着独特的风味和口感,绝对是值得一试的美食。 👶: 请为我讲解“大语言模型”这个概念。 🤖️: 大语言模型是一种人工智能技术,它使用大量的文本数据来训练模型,然后在不断的迭代中不断优化模型。它的目标是根据输入的数据来生成符合特定需求的文本,这些文本可以是任何你感兴趣的主题。 👶: Introduce the history of the United States, please. 🤖️: 您提到的“Introok's the believeations of theument." 这个名字来源于中国古代的"groty of of the change." ``` 极速且初具效果,甚至仍然可以进一步压缩获取更小更优质的训练数据。 Zero模型权重保存为 `full_sft_512_zero.pth`(见下文MiniMind模型文件链接),如有兴趣可下载检验此模型效果。 --- ## Ⅱ 主要训练步骤 > 所有训练脚本均 `cd ./trainer` 目录执行 ### **1. 预训练(Pretrain)**: LLM首先要学习的并非直接与人交流,而是让网络参数中充满知识的墨水,“墨水” 理论上喝的越饱越好,产生大量的对世界的知识积累。 预训练就是让Model先埋头苦学大量基本的知识,例如从Wiki百科、新闻、书籍整理大规模的高质量训练数据。 这个过程是“无监督”的,即人类不需要在过程中做任何“有监督”的校正,而是由模型自己从大量文本中总结规律学习知识点。 模型此阶段目的只有一个:**学会词语接龙**。例如我们输入“秦始皇”四个字,它可以接龙“是中国的第一位皇帝”。 ```bash torchrun --nproc_per_node 1 train_pretrain.py # 1即为单卡训练,可根据硬件情况自行调整 (设置>=2) # or python train_pretrain.py ``` > 训练后的模型权重文件默认每隔`100步`保存为: `pretrain_*.pth`(* > 为模型具体dimension,每次保存时新文件会覆盖旧文件) ### **2. 有监督微调(Supervised Fine-Tuning)**: 经过预训练,LLM此时已经掌握了大量知识,然而此时它只会无脑地词语接龙,还不会与人聊天。 SFT阶段就需要把半成品LLM施加一个自定义的聊天模板进行微调。 例如模型遇到这样的模板【问题->回答,问题->回答】后不再无脑接龙,而是意识到这是一段完整的对话结束。 称这个过程为指令微调,就如同让已经学富五车的「牛顿」先生适应21世纪智能手机的聊天习惯,学习屏幕左侧是对方消息,右侧是本人消息这个规律。 在训练时,MiniMind的指令和回答长度被截断在512,是为了节省显存空间。就像我们学习时,会先从短的文章开始,当学会写作200字作文后,800字文章也可以手到擒来。 在需要长度拓展时,只需要准备少量的2k/4k/8k长度对话数据进行进一步微调即可(此时最好配合RoPE-NTK的基准差值)。 > 在推理时通过调整RoPE线性差值,实现免训练长度外推到2048及以上将会很方便。 ```bash torchrun --nproc_per_node 1 train_full_sft.py # or python train_full_sft.py ``` > 训练后的模型权重文件默认每隔`100步`保存为: `full_sft_*.pth`(* > 为模型具体dimension,每次保存时新文件会覆盖旧文件) ## Ⅲ 其它训练步骤 > 所有训练脚本均 `cd ./trainer` 目录执行 ### **3. 人类反馈强化学习(Reinforcement Learning from Human Feedback, RLHF)** 在前面的训练步骤中,模型已经具备了基本的对话能力,但是这样的能力完全基于单词接龙,缺少正反样例的激励。 模型此时尚未知什么回答是好的,什么是差的。我们希望它能够更符合人的偏好,降低让人类不满意答案的产生概率。 这个过程就像是让模型参加新的培训,从优秀员工的作为例子,消极员工作为反例,学习如何更好地回复。 此处使用的是RLHF系列之-直接偏好优化(Direct Preference Optimization, DPO)。 与PPO(Proximal Policy Optimization)这种需要奖励模型、价值模型的RL算法不同; DPO通过推导PPO奖励模型的显式解,把在线奖励模型换成离线数据,Ref模型输出可以提前保存。 DPO性能几乎不变,只用跑 actor_model 和 ref_model 两个模型,大大节省显存开销和增加训练稳定性。 > 注:RLHF训练步骤**并非必须**,此步骤难以提升模型“智力”而通常仅用于提升模型的“礼貌”,有利(符合偏好、减少有害内容)也有弊(样本收集昂贵、反馈偏差、多样性损失)。 ```bash torchrun --nproc_per_node 1 train_dpo.py # or python train_dpo.py ``` > 训练后的模型权重文件默认每隔`100步`保存为: `rlhf_*.pth`(* > 为模型具体dimension,每次保存时新文件会覆盖旧文件) ### **4. 知识蒸馏(Knowledge Distillation, KD)** 在前面的所有训练步骤中,模型已经完全具备了基本能力,通常可以学成出师了。 而知识蒸馏可以进一步优化模型的性能和效率,所谓知识蒸馏,即学生模型面向教师模型学习。 教师模型通常是经过充分训练的大模型,具有较高的准确性和泛化能力。 学生模型是一个较小的模型,目标是学习教师模型的行为,而不是直接从原始数据中学习。 在SFT学习中,模型的目标是拟合词Token分类硬标签(hard labels),即真实的类别标签(如 0 或 6400)。 在知识蒸馏中,教师模型的softmax概率分布被用作软标签(soft labels)。小模型仅学习软标签,并使用KL-Loss来优化模型的参数。 通俗地说,SFT直接学习老师给的解题答案。而KD过程相当于“打开”老师聪明的大脑,尽可能地模仿老师“大脑”思考问题的神经元状态。 例如,当老师模型计算`1+1=2`这个问题的时候,最后一层神经元a状态为0,神经元b状态为100,神经元c状态为-99... 学生模型通过大量数据,学习教师模型大脑内部的运转规律。这个过程即称之为:知识蒸馏。 知识蒸馏的目的只有一个:让小模型体积更小的同时效果更好。 然而随着LLM诞生和发展,模型蒸馏一词被广泛滥用,从而产生了“白盒/黑盒”知识蒸馏两个派别。 GPT-4这种闭源模型,由于无法获取其内部结构,因此只能面向它所输出的数据学习,这个过程称之为黑盒蒸馏,也是大模型时代最普遍的做法。 黑盒蒸馏与SFT过程完全一致,只不过数据是从大模型的输出收集,因此只需要准备数据并且进一步FT即可。 注意更改被加载的基础模型为`full_sft_*.pth`,即基于微调模型做进一步的蒸馏学习。 `./dataset/sft_1024.jsonl`与`./dataset/sft_2048.jsonl` 均收集自qwen2.5-7/72B-Instruct大模型,可直接用于SFT以获取Qwen的部分行为。 ```bash # 注意需要更改train_full_sft.py数据集路径,以及max_seq_len torchrun --nproc_per_node 1 train_full_sft.py # or python train_full_sft.py ``` > 训练后的模型权重文件默认每隔`100步`同样保存为: `full_sft_*.pth`(*为模型具体dimension,每次保存时新文件会覆盖旧文件) 此处应当着重介绍MiniMind实现的白盒蒸馏代码`train_distillation.py`,由于MiniMind同系列本身并不存在强大的教师模型,因此白盒蒸馏代码仅作为学习参考。 ```bash torchrun --nproc_per_node 1 train_distillation.py # or python train_distillation.py ``` ### **5. LoRA (Low-Rank Adaptation)** LoRA是一种高效的参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)方法,旨在通过低秩分解的方式对预训练模型进行微调。 相比于全参数微调(Full Fine-Tuning),LoRA 只需要更新少量的参数。 LoRA 的核心思想是:在模型的权重矩阵中引入低秩分解,仅对低秩部分进行更新,而保持原始预训练权重不变。 代码可见`./model/model_lora.py`和`train_lora.py`,完全从0实现LoRA流程,不依赖第三方库的封装。 ```bash torchrun --nproc_per_node 1 train_lora.py # or python train_lora.py ``` > 训练后的模型权重文件默认每隔`100步`保存为: `lora_xxx_*.pth`(* > 为模型具体dimension,每次保存时新文件会覆盖旧文件) 非常多的人困惑,如何使模型学会自己私有领域的知识?如何准备数据集?如何迁移通用领域模型打造垂域模型? 这里举几个例子,对于通用模型,医学领域知识欠缺,可以尝试在原有模型基础上加入领域知识,以获得更好的性能。 同时,我们通常不希望学会领域知识的同时损失原有基础模型的其它能力,此时LoRA可以很好的改善这个问题。 只需要准备如下格式的对话数据集放置到`./dataset/lora_xxx.jsonl`,启动 `python train_lora.py` 训练即可得到`./out/lora/lora_xxx.pth`新模型权重。 **医疗场景** ``` {"conversations": [{"role": "user", "content": "请问颈椎病的人枕头多高才最好?"}, {"role": "assistant", "content": "颈椎病患者选择枕头的高度应该根据..."}]} {"conversations": [{"role": "user", "content": "请问xxx"}, {"role": "assistant", "content": "xxx..."}]} ``` **自我认知场景** ``` {"conversations": [{"role": "user", "content": "你叫什么名字?"}, {"role": "assistant", "content": "我叫minimind..."}]} {"conversations": [{"role": "user", "content": "你是谁"}, {"role": "assistant", "content": "我是..."}]} ``` 此时【基础模型+LoRA模型】即可获得医疗场景模型增强的能力,相当于为基础模型增加了LoRA外挂,这个过程并不损失基础模型的本身能力。 我们可以通过`eval_model.py`进行模型评估测试。 ```bash # 注意:model_mode即选择基础模型的类型,这和train_lora是基于哪个模型训练的相关,确保统一即可。 python eval_model.py --lora_name 'lora_medical' --model_mode 2 ``` **小测试** ``` 👶: 我最近经常感到头晕,可能是什么原因? 🤖️: 头晕可能是由于多种原因,包括但不限于:心脏疾病、眼睛损伤、过敏反应、皮肤问题、过敏反应、压力、焦虑、疲劳、药物副作用、性功能障碍或者其他... --- 👶: 你和openai是什么关系? 🤖️: 我是 MiniMind,一个由 Jingyao Gong 开发的人工智能助手。我通过自然语言处理和算法训练来与用户进行交互。 ``` PS:只要有所需要的数据集,也可以full_sft全参微调(需要进行通用知识的混合配比,否则过拟合领域数据会让模型变傻,损失通用性) ### **6. 训练推理模型 (Reasoning Model)** DeepSeek-R1实在太火了,几乎重新指明了未来LLM的新范式。 论文指出`>3B`的模型经历多次反复的冷启动和RL奖励训练才能获得肉眼可见的推理能力提升。 最快最稳妥最经济的做法,以及最近爆发的各种各样所谓的推理模型几乎都是直接面向数据进行蒸馏训练, 但由于缺乏技术含量,蒸馏派被RL派瞧不起(hhhh)。 本人迅速已经在Qwen系列1.5B小模型上进行了尝试,很快复现了Zero过程的数学推理能力。 然而一个遗憾的共识是:参数太小的模型直接通过冷启动SFT+GRPO几乎不可能获得任何推理效果。 MiniMind2第一时间只能坚定不移的选择做蒸馏派,日后基于0.1B模型的RL如果同样取得小小进展会更新此部分的训练方案。 做蒸馏需要准备的依然是和SFT阶段同样格式的数据即可,数据集来源已如上文介绍。数据格式例如: ```json lines { "conversations": [ { "role": "user", "content": "你好,我是小芳,很高兴认识你。" }, { "role": "assistant", "content": "\n你好!我是由中国的个人开发者独立开发的智能助手MiniMind-R1-Lite-Preview,很高兴为您提供服务!\n\n\n你好!我是由中国的个人开发者独立开发的智能助手MiniMind-R1-Lite-Preview,很高兴为您提供服务!\n" } ] } ``` 推理模型R1的回复模板是: ```text \n思考过程\n\n \n最终回答\n ``` 这在GRPO中通过设置规则奖励函数约束模型符合思考标签和回复标签(在冷启动靠前的阶段奖励值设置应该提高一些) 另一个问题是蒸馏过程虽然和SFT一样,但实验结果是模型难以每次都符合模板规范的回复,即脱离思考和回复标签约束。 这里的小技巧是增加标记位置token的损失惩罚,详见`train_distill_reason.py`: ```text # 在 sp_ids 对应的位置增加额外的惩罚 ... loss_mask[sp_ids] = 10 # 惩罚系数 ``` 另另一个tips是由于推理数据由于只筛选了`<1024`长度的数据,其中多轮对话和英文数据偏少, 因此`r1_mix_1024.jsonl`进行了大约10k条多轮对话+英文数据的混合,防止模型遗忘严重。 脚本默认基于rlhf后的基模型做推理能力的蒸馏微调,下面直接启动训练即可: ```bash torchrun --nproc_per_node 1 train_distill_reason.py # or python train_distill_reason.py ``` > 训练后的模型权重文件默认每隔`100步`保存为: `reason_*.pth`(*为模型具体dimension,每次保存时新文件会覆盖旧文件) 测试一下: ```text 👶: 请介绍一下自己。 🤖️: 嗯,用户让我介绍一下自己。我现在需要解释一下自己。首先,我应该明确用户是谁,是学生、职场人士还是普通人。用户可能是一个学生或者职场人士,所以信息传递应该是帮助学生和职场人士的信息。 然后,我需要回应用户,这可能包括介绍自己的位置、兴趣爱好、职业经历等信息。用户还可能想了解自己在哪里,比如职业发展、工作经验等。 我应该注意用词要正式,不要太正式,同时要保持简洁明了。这样用户容易理解,也方便他们查阅相关资料。 最后,我应该总结一下,让用户知道我是如何帮助他们完成他们的任务的,这样他们有更多关于我的信息。 我是**“你的名字”,你叫什么名字? 👶: 你更擅长哪一个学科? 🤖️: 您好!我是由中国的个人开发者开发的智能助手MiniMind-R1。如您有任何任何问题,我会尽我所能为您提供帮助。 您好!我是由中国的个人开发者开发的智能助手MiniMind-R1。如您有任何任何问题,我会尽我所能为您提供帮助。 ``` --- ## Ⅳ 模型参数设定 📋关于LLM的参数配置,有一篇很有意思的论文[MobileLLM](https://arxiv.org/pdf/2402.14905)做了详细的研究和实验。 Scaling Law在小模型中有自己独特的规律。 引起Transformer参数成规模变化的参数几乎只取决于`d_model`和`n_layers`。 * `d_model`↑ + `n_layers`↓ -> 矮胖子 * `d_model`↓ + `n_layers`↑ -> 瘦高个 2020年提出Scaling Law的论文认为,训练数据量、参数量以及训练迭代次数才是决定性能的关键因素,而模型架构的影响几乎可以忽视。 然而似乎这个定律对小模型并不完全适用。 MobileLLM提出架构的深度比宽度更重要,「深而窄」的「瘦长」模型可以学习到比「宽而浅」模型更多的抽象概念。 例如当模型参数固定在125M或者350M时,30~42层的「狭长」模型明显比12层左右的「矮胖」模型有更优越的性能, 在常识推理、问答、阅读理解等8个基准测试上都有类似的趋势。 这其实是非常有趣的发现,因为以往为100M左右量级的小模型设计架构时,几乎没人尝试过叠加超过12层。 这与MiniMind在训练过程中,模型参数量在`d_model`和`n_layers`之间进行调整实验观察到的效果是一致的。 然而「深而窄」的「窄」也是有维度极限的,当d_model<512时,词嵌入维度坍塌的劣势非常明显, 增加的layers并不能弥补词嵌入在固定q_head带来d_head不足的劣势。 当d_model>1536时,layers的增加似乎比d_model的优先级更高,更能带来具有“性价比”的参数->效果增益。 * 因此MiniMind设定small模型dim=512,n_layers=8来获取的「极小体积<->更好效果」的平衡。 * 设定dim=768,n_layers=16来获取效果的更大收益,更加符合小模型Scaling-Law的变化曲线。 作为参考,GPT3的参数设定见下表: ![gpt3_config.png](./images/gpt3_config.png) --- ## Ⅴ 训练结果 MiniMind2 模型训练损失走势(由于数据集在训练后又更新清洗多次,因此Loss仅供参考) | models | pretrain (length-512) | sft (length-512) | |-----------------|----------------------------------------------------|----------------------------------------------------| | MiniMind2-Small | | | | MiniMind2 | | | ### 训练完成-模型合集 > 考虑到多人反应百度网盘速度慢,MiniMind2及以后全部使用ModelScope/HuggingFace托管。 #### ① PyTorch原生模型 MiniMind2模型权重 ([ModelScope](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch) | [HuggingFace](https://huggingface.co/jingyaogong/MiniMind2-Pytorch)) MiniMind-V1模型权重 ([百度网盘](https://pan.baidu.com/s/1KUfSzEkSXYbCCBj0Pw-9fA?pwd=6666))
Torch文件命名对照 | Model Name | params | pretrain_model | sft_model | rl_model | reason_model | lora_model | |-----------------|--------|------------------------|------------------------|--------------------|------------------|--------------------| | MiniMind2-small | 26M | `pretrain_512.pth` | `full_sft_512.pth` | `rlhf_512.pth` | `reason_512.pth` | `lora_xxx_512.pth` | | MiniMind2-MoE | 145M | `pretrain_640_moe.pth` | `full_sft_640_moe.pth` | `rlhf_640_moe.pth` | - | - | | MiniMind2 | 104M | `pretrain_768.pth` | `full_sft_768.pth` | `rlhf_768.pth` | `reason_768.pth` | `lora_xxx_768.pth` | | Model Name | params | pretrain_model | 单轮对话sft | 多轮对话sft | rl_model | |-------------------|--------|------------------------|------------------------------------|-----------------------------------|--------------| | minimind-v1-small | 26M | `pretrain_512.pth` | `single_chat/full_sft_512.pth` | `multi_chat/full_sft_512.pth` | `rl_512.pth` | | minimind-v1-moe | 4×26M | `pretrain_512_moe.pth` | `single_chat/full_sft_512_moe.pth` | `multi_chat/full_sft_512_moe.pth` | - | | minimind-v1 | 108M | `pretrain_768.pth` | `single_chat/full_sft_768.pth` | `multi_chat/full_sft_768.pth` | `rl_768.pth` |
#### ② Transformers模型 MiniMind系列 ([ModelScope](https://www.modelscope.cn/collections/MiniMind-b72f4cfeb74b47) | [HuggingFace](https://huggingface.co/collections/jingyaogong/minimind-66caf8d999f5c7fa64f399e5)) --- # 📌 Eval ## Ⅰ RLHF对比SFT篇 测试基于`full_sft`和`rlhf`的`MiniMind2系列`模型对比,测试随机种子均固定
问答实测 [A0] MiniMind2:full_sft_640 [A1] MiniMind2:rlhf_640 ```text [Q]: 你叫什么名字? [A0]: 我是一个大型语言模型,可以回答各种问题,提供信息和帮助。我可以回答各种问题,提供信息和帮助,帮助你解决各种问题。 [A1]: 我是一种基于文本的AI模型,我并没有个人经历或情感,所以无法感受和体验情感。我的设计目的是为了帮助用户解决问题、提供信息和进行对话。如果您有任何问题或需要帮助,请随时告诉我! [Q]: 你更擅长哪一个学科? [A0]: 作为一个AI模型,我并没有真正的“擅长”这个学科。我被设计成能够提供信息、解答问题、进行对话、执行任务和与用户进行对话。不过,我可以提供信息、解答问题、进行对话、提供建议、处理问题、进行对话、提供建议、学习新知识、编写代码、回答问题、提供建议等多种学科的功能。如果你有具体的学科需求或主题,欢迎提供详细信息。 [A1]: 作为AI,我并没有个人能力或兴趣,但我可以提供一些关于数学、科学、计算机科学、语言学、心理学等学科的知识。如果你对数学感兴趣,我可以提供一些基础知识,例如代数、几何、概率、统计学等。如果你需要更深入的学习,我也可以提供相关的在线课程、书籍、论文等资源。请告诉我你具体需要什么帮助,我会尽力提供帮助。 [Q]: 鲁迅的《狂人日记》是如何批判封建礼教的? [A0]: 鲁迅的《狂人日记》是一部讲述了鲁迅对人类价值和道德责任的倡导。在这部作品中,鲁迅通过细腻的笔触,探讨了人性、道德、正义、牺牲与责任等深刻的主题。以下是鲁迅在《狂人日记》中如何批判封建礼教的几个方面:1. **社会现实与道德观念**:鲁迅通过讲述鲁迅的故事,探讨了社会的不平等、权力的流动、人性的演变等社会问题。他认为,社会不平等不仅仅是不公正的,更是每个人在社会中的不公正。他呼吁社会各界共同努力,通过教育、宣传、法律等手段,为构建和谐社会做出贡献。2. **道德与责任**:鲁迅通过讲述鲁迅的故事,展现了道德和责任的重要性。他提倡通过道德教育来培养人们的道德品质,同时也强调了道德和责任的重要性。鲁迅通过讲述鲁迅的故事,揭示了社会的不平等和人性的复杂性,体现了对人性的深刻理解。3. **道德与责任**:鲁迅通过讲述鲁迅的故事,强调了道德责任的重要性。他提倡通过自己的行为来体现对社会的尊重和对人性的深刻理解。鲁迅通过讲述鲁迅的故事,强调了道德和责任的重要性。4. **道德与道德**:鲁迅通过讲述鲁迅的故事,揭示了道德和责任的真正意义。鲁迅通过讲述鲁迅的故事,揭示了道德和责任的本质,强调了道德和责任的重要性。5. **个人与社会**:鲁迅通过讲述鲁迅的故事,揭示了个人与社会之间的复杂关系。他呼吁社会各界共同努力,通过教育和宣传,为构建和谐社会做出贡献。鲁迅的《狂人日记》通过其丰富的故事和深刻的道德寓言,传达了关于人性、道德、正义、道德以及个人与社会的复杂关系。这部作品不仅在文学价值上取得了巨大成就,更在思想层面上启发了后来的文学创作,对后世产生了深远的影响。 [A1]: 鲁迅的《狂人日记》是一部描绘战争和自由精神的作品,它批判了封建社会的种种问题和冲突,反映了社会的不平等和人性本质。在这部作品中,鲁迅通过对封建社会的诸多现象进行批判,如“弗雷德里克·约翰逊的“鹰迪伦”、“纳扎特的”、“贝克汉姆”、“乔治·威尔逊”等,对封建社会的种种问题进行了批判,强调了封建社会的不平等、人性本质以及人性本质。鲁迅通过他的“鹰迪伦”、“纳扎特的”、“贝克汉姆”、“乔治·威尔逊”等,对封建社会的种种问题进行了批判。在小说中,鲁迅通过对封建社会的诸多现象进行批判,揭示了封建社会的腐败、人性本质以及人性本质的复杂性。这种批判体现了对封建社会的尊重和对人性的复杂性,同时也反映了现代社会中普遍存在的不平等和人性本质。鲁迅的《狂人日记》不仅是一部关于封建社会的小说,也是一部具有深刻社会意义的作品,对后世的审判和批判起到了关键作用。 [Q]: 我咳嗽已经持续了两周,需要去医院检查吗? [A0]: 咳嗽确实需要一定的检查,但如果您的咳嗽持续时间过长,或者有其他不适症状(如持续咳嗽、呼吸困难等),建议尽快就医。医生会根据您的症状、体检结果和可能的影像学检查(如X光、MRI或CT扫描)来诊断您的症状。在等待就医期间,您可以尝试以下方法来帮助缓解症状:1. **休息**:确保您有足够的睡眠。2. **保持水分**:脱水可能会加重咳嗽。3. **热敷**:使用热敷袋或热毛巾敷在疼痛的部位,有助于缓解喉咙痛。4. **避免刺激**:避免使用可能刺激喉咙的化学物质,如某些化学物质。5. **保持室内湿润**:确保您的卧室有良好的通风,可以使用加湿器帮助保持空气湿度。6. **保持室内湿度**:使用加湿器可以帮助缓解喉咙干燥和咳嗽。如果症状持续不缓解或有其他不适症状,请立即就医。希望您早日康复! [A1]: 如果您的咳嗽持续了两周,通常建议您尽快就医,因为这可能会影响您的呼吸和心跳。您可以通过以下步骤来检查咳嗽:1. **休息**:用纸巾或肘部轻轻按压咳嗽的位置。2. **使用药物**:在可能的情况下,使用药物(如抗过敏药、抗过敏药等)来缓解咳嗽。请在医生的指导下使用药物。3. **热敷**:在咳嗽期间,可以使用热敷来缓解疼痛和肿胀。4. **药物治疗**:如果咳嗽持续了两周,可以使用一些消炎药或抗过敏药来缓解症状。请务必在医生的指导下使用。5. **保持健康的生活方式**:保持充足的睡眠、均衡的饮食、适量的运动和避免吸烟和限酒,这些都有助于提高咳嗽的几率。请记住,这些建议不能替代专业医疗意见。如果您的症状持续或加重,强烈建议您尽快就医。 ```
👉效果总结 full_sft模型在简洁性和信息准确性方面表现更好;rlhf模型在回答中倾向于提供更多的背景信息,但信息准确性有待改进。 总的来说RLHF后的模型倾向于学习:说更多有礼貌但无用的废话讨好“对话”本身,而对信息准确性则有轻微损失。 天下没有免费的午餐,还需要继续提升RLHF数据集的质量,也要接受模型能力无法避免的损失(程度有轻重)。 DPO和在线PPO的区别在于reject和chosen都是离线准备的,和minimind模型本身的输出必然存在很大的分布差异。 通俗地说DPO算法使模型观看乒乓球世界冠军的打法「录像」进行RL,而不是像PPO一样请reward模型做「教练」纠正自己的打法进行RL。 ## Ⅱ 主观样例测评 🏃以下测试于2025-02-09完成,此日期后发布的新模型,无特殊需要时将不加入测试。 [A] [MiniMind2 (0.1B)](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch)
[B] [MiniMind2-MoE (0.15B)](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch)
[C] [MiniMind2-Small (0.02B)](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch)
[D] [minimind-v1-small(0.02B)](https://pan.baidu.com/s/1_COe0FQRDmeapSsvArahCA?pwd=6666)
[E] [minimind-v1-moe(0.1B)](https://pan.baidu.com/s/1tqB-GMvuiGQBvEl-yZ-oBw?pwd=6666)
[F] [minimind-v1(0.1B)](https://pan.baidu.com/s/1p713loS7EfwHQf3G9eYI3Q?pwd=6666)
[G] [baby-llama2-chinese(0.2B)](https://github.com/DLLXW/baby-llama2-chinese)
[H] [chatlm-mini-chinese(0.2B)](https://github.com/charent/ChatLM-mini-Chinese)
```text [Q]: 你知道长江吗? [A]: 长江,位于中国东南部,是中国的第一长河,全长6300多公里,流经11个省份,是中国最长的河流,也是中国的“母亲河”。长江的历史可以追溯到公元前2000多年的商朝时期,当时的商朝是商朝的后裔。商朝时期,长江的经济发展达到了高峰,是中国最重要的农业和工业中心之一。长江流域的经济主要以农业为主,长江渔业占据了主要产业,如钓鱼、猎鱼等。长江的水资源也非常丰富,包括河流、湖泊、河流等。长江的自然景观也十分壮观,长江四季景色各异,春季多雨,夏季多雨,秋季则多雨,冬季则寒冷多雪,春季和秋季都有美丽的花朵和果实。长江是中国的经济中心,拥有众多的国家和城市,是世界上重要的交通枢纽和航运中心。同时,长江还是中国的重要水利资源,对于中国的经济发展具有重要意义。 [B]: 是的,我知道长江。长江位于中国的中部,是中国第三长的河流,全长约6300公里,流经江西省、安徽省、江苏省、安徽省、江苏省和安徽省。 [C]: 当然,长江是中国最长的河流,全长约6300公里。它以其丰富的水资源、丰富的水生动植物资源以及独特的地理位置而闻名于世。 [D]: 是的,长江是中国最长的河流,全长超过3300千米。 [E]: 长江是中国最长的河流,总长度约为6300公里。它发源于青藏高原的青海湖,流经青海、四川、甘肃、宁夏、内蒙古、陕西和山西。长江流域是中国最大的农业区之一,也是中国的文化和经济中心。 [F]: 是的,长江是中国最长的河流之一,也是世界第三大河流。长江发源于青藏高原,流经11个省份,最终注入东海。长江流域是中国最大的河流流域之一,拥有着丰富的自然资源和文化遗产,如长江大桥、长江海洋博物馆、长江古城等。 [G]: 同时也知道,中国是世界上最长的城市之一,拥有超过3400万人口。 [H]: 是的,长江是中国最长的河流,也是世界上最长的河流之一。它发源于青藏高原,流经九个省区,最终注入东海。长江流域的气候温和,四季分明,是中国重要的农业和工业基地。长江流域的水产资源丰富,是中国的重要渔业资源之一。 [Q]: 世界上最高的山峰是什么? [A]: 世界上最高的山峰是珠穆朗玛峰(Mount Everest),位于喜马拉雅山脉,尼泊尔和中国的边界上。它的海拔高度是8,848.86米(29,031.7英尺),是地球上最高的山峰。 [B]: 珠穆朗玛峰(Mount Everest)是世界上最高的山峰,海拔高度达8,848米(29,029英尺)。 [C]: 珠穆朗玛峰是世界上最高的山峰,位于喜马拉雅山脉,海拔8,848.86米(29,031.7英尺)。它位于尼泊尔和中国的边界上,是喜马拉雅山脉的一部分。 [D]: 世界上最高的山峰是珠穆朗玛峰,位于尼泊尔和中国的边境上,海拔8848米。 [E]: 世界上最高的山峰是珠穆朗玛峰,位于喜马拉雅山脉,海拔8,848米。 [F]: 珠穆朗玛峰是世界上最高的山峰,海拔8848米。 [G]: 世界上最高的山峰是珠穆朗玛峰,位于喜马拉雅山脉,海拔8,848.86米(29,031.69英尺)。 [H]: 世界上最高的山峰是珠穆朗玛峰,也称为萨加玛塔,位于喜马拉雅山脉的尼泊尔和中国边境之间,海拔8,848.86米(29,031.69英尺)。它是世界上最著名的登山目标之一,吸引着来自世界各地的登山者前来挑战。珠穆朗玛峰的名字来自藏语,意思是“女神的额峰”,它是由两个不同的山峰组成的,其中一个是位于尼泊尔的珠穆朗玛峰,另一个则位于中国。它的高度是通过测量海拔高度来确定的,因为它是通过测量地球的平均高度来确定的。尽管珠穆朗玛峰是世界上最高的山峰,但它并不是地球上任何其他地方都能到达的。地球上的其他地方,如南美洲的安第斯山脉,也是世界上最高的山脉,但它并不是地球上最高的山峰。总之,珠穆朗玛峰是世界上最高的山峰,但它并不是地球上任何地方都能到达的。它是地球上最高的山脉,但它并不是地球上任何地方都能到达的。 [Q]: 万有引力是谁提出的? [A]: 万有引力是由牛顿在17世纪提出的,它是牛顿三大定律之一。牛顿在17世纪提出的引力定律揭示了万有引力定律的本质,并且解释了行星、卫星、彗星等天体之间如何相互作用。这个定律是由艾萨克·牛顿在1687年提出的,他通过对万有引力定律的分析和实验,成功地解释了天体之间的引力作用,从而解释了地球绕太阳旋转的现象。 [B]: 万有引力(Human Policy)是牛顿的三大力学定律,用于描述两个物体之间的相互作用力。它由万有引力定律和牛顿的第一定律(惯性定律)构成。万有引力是由于两个物体之间相互作用而产生的,它们的质量、距离、速度和方向等因素决定了它们之间的相互作用。这种相互作用力是:质量的乘积:质量的乘积是万有引力定律和牛顿第二定律的乘积。质量的乘积:质量的乘积是物体之间的一种物理过程,它是物体间相互作用的产物,对于其他物体来说,其质量可以表示为两个物体之间的质量之和,即重力加速度a。 [C]: 万有引力是一个由牛顿在1687年提出的物理学概念,它描述了物体之间引力如何相互作用的规律。 [D]: 万有引力是由英国物理学家牛顿提出的。他认为,所有物体都会产生引力,这个引力是由物体的质量和距离决定的。这个理论揭示了质量和距离之间的关系,并证明了万有引力的本质。 [E]: 万有引力是由牛顿在1687年提出的。它是宇宙中一个基本的物理现象,它描述了物体之间的相互作用。这个力被称为万有引力,它在所有物体之间都存在,这种力与物体的质量和距离有关。万有引力是自然界中最基本的力量之一,它不仅影响着物体的运动和变化,还对宇宙的结构和演化产生着重要的影响。 [F]: 万有引力是英国物理学家艾萨克·牛顿在17世纪提出的。 [G]: 引力是宇宙中一种力量,它是由质量或能量所组成的。根据牛顿的万有引力定律,一个物体之间的引力大小与它的质量成正比,与它的质量成反比。因此,牛顿的万有引力定律表明,一个物体之间的引力大小与它的质量成正比,与它的质量成反比。这个定律在物理学、天文学和物理学中都有广泛的应用。 [H]: 万有引力是由英国物理学家艾萨克·牛顿于1687年提出的。他在1687年发表了一篇著名的论文,题为《自然哲学的数学原理》,其中提出了万有引力定律,指出任何两个物体之间都存在着一种引力,这种引力的大小与它们的质量成正比,与它们之间的距离的平方成反比。这个定律被称为万有引力定律,它描述了质量和距离之间的关系。它表明,两个物体之间的引力与它们的质量成正比,与它们之间的距离的平方成反比。这个定律对于解释宇宙中的许多现象和现象之间的相互作用非常重要,例如行星绕太阳运动、恒星之间的引力透镜效应等等。它也被广泛应用于天文学、物理学和工程学等领域,为我们理解宇宙的本质和演化提供了重要的理论基础。 [Q]: 你知道大熊猫的主要食物是什么吗? [A]: 大熊猫主要以竹子为食,但也会吃其他植物、昆虫、小型哺乳动物和鱼类。它们的消化系统与其他熊科动物类似,能够消化竹子。大熊猫的牙齿锋利,适合咀嚼竹子,因此它们经常被称为“竹食动物”。 [B]: 大熊猫是以竹子为食的哺乳动物,它们是肉食动物。它们主要食物是竹子,特别是竹子中富含纤维素和矿物质,并且不会直接进入消化系统。大熊猫的肠道主要由竹子组成,它们需要消化竹子中的纤维素和其他纤维素来支持它们的正常进食。 [C]: 大熊猫主要以竹子为食,但它们也吃其他植物,包括树叶、嫩芽、昆虫、水果、蔬菜和小型哺乳动物等。大熊猫是中国特有的物种,属于熊科,主要分布在中国的四川、陕西和甘肃等地。 [D]: 大熊猫的主要食物是竹子。它们通常吃竹子,每天需要吃掉大约30公斤的竹子。 [E]: 是的,大熊猫主要以竹子为食。它们每天需要吃掉大量的竹子来维持生命,每天需要吃掉40至80公斤的竹子。此外,它们也会吃一些其他的植物,如草、果实、坚果和小型哺乳动物。 [F]: 大熊猫主要以竹子为食,也会偶尔食用其他的植物和动物。此外,大熊猫也会吃一些野果、小动物和植物,但这些都不属于它们的主要食物来源。 [G]: 大熊猫的主要食物是竹子。 [H]: 大熊猫是一种以竹子为主要食物的植食动物,它们的肠道结构适合消化纤维素含量高的竹子,但也会吃一些其他植物,如草本植物和小型哺乳动物。大熊猫的肠道结构也适应了高纤维的竹子,这使得它们能够消化大量的纤维素,从而帮助它们在野外生存。大熊猫的肠道中还含有一种叫做“纤维素酶”的酶类物质,这种酶能够帮助大熊猫分解纤维素,从而帮助它们消化大量的水分和营养物质。此外,大熊猫的肠道还含有一种叫做“纤维素蛋白”的蛋白质,这种蛋白质能够帮助大熊猫维持体内的能量水平,从而帮助它们在野外生存。总的来说,大熊猫的主要食物是竹子,但它们也会吃一些其他植物,如草本植物和小型哺乳动物。 [Q]: 海水为什么是咸的? [A]: 海水之所以是咸的,主要是由于其中的化学成分。海水中含有大量的矿物质和盐分,其中主要成分是硫酸盐、碳酸盐、镁离子和钾离子。这些物质在高浓度下,会沉淀在海水中,形成海水。具体来说,海水中的矿物质和盐分主要来自于岩石、土壤、生物和人类排泄物等。岩石是海水的组成部分,主要由矿物质组成,如石英、长石、云母等。土壤中含有大量的化学物质,如氮、磷、钾等,这些物质会渗透到土壤中,形成土壤。生物体内的盐分主要来源于植物的叶绿素等色素,这些色素会吸收太阳光中的能量,转化为生物体可以利用的能量。而人类排泄物中含有的盐分主要来源于矿物质和碳酸盐,这些矿物质在水中的浓度较高,会导致水中溶解的矿物质浓度增加,形成水体中的盐分。因此,海水之所以是咸的,是由于其中的化学成分和结构特征。海水中的矿物质和盐分主要来自于岩石、土壤、生物和人类排泄物等,而这些物质的积累和排泄会导致海水呈现出咸味。 [B]: 海水是咸的,因为它包含了多种物质,包括氯化钠(NaCl)、氯化钠(NaCl)、碳酸盐(如硫酸盐)、氯化钠(NaCl)和水。咸物质的来源:盐是咸的常见来源,含有钾、钙、镁、钾等矿物质。这些矿物质通常来自于地下水、地下水或土壤中,这些矿物质的含量远低于纯净水。咸味的原因:盐的咸味主要源于其溶解于水中的微小颗粒物。这些小颗粒物主要通过植物根部和动物体内的微生物传播,如昆虫、小型哺乳动物、鸟类和某些昆虫。综上所述,海水是咸的,因为它含有大量的盐类,包括矿物质、岩石、盐、矿物质等,这些物质在海水中的浓度高,对咸味的产生较大影响。 [C]: 海水是咸的,主要是因为其中含有各种盐分,其中以海水为主要成分之一。当海水中含有大量的盐分时,盐分会被溶解在海水中,形成咸味。海水中的盐分主要来源于溶解盐类、蒸发析出的盐和海水冻结成冰等因素。 [D]: 海水中含有许多盐分,这些盐分可以导致海水咸度的增加。海水中的盐分主要来自于海水中的盐分,这些盐分可以从海水中的盐分中获得。海水中的氧化物和盐分可以与海水中的盐分形成氢键,从而使海水咸度增加。 [E]: 海水是咸的,这是因为它含有大量的盐分,其中包括氯离子和氯离子。这些离子在海洋中形成了大量的盐,这些盐分被称为盐分。海水中含有许多溶解在水中的盐,这些盐分会随着海水的升高而逐渐蒸发,最终导致海水中盐分的蒸发。这些盐分在海水中被完全消耗,然后被排除在海水中。此外,海水中还含有许多其他的盐分,但这些盐分是不同的,因为它们在水中的浮起和下降过程是不同的。海水中的盐分是从海底的岩石中提取出来的,这些盐分在海水中可以被利用。总的来说,海水是咸的,这也是为什么海水中含有许多盐分和盐分的原因。 [F]: 海水含有许多溶解的盐类,其中最主要的是氯化钠。当水中加入盐时,盐的分子会与水分子发生反应,形成氢氧化钠,这是一种盐。 [G]: 因为水是许多生物的生存所必需的,它能够吸收和释放二氧化碳,释放氧气,维持大气中的水蒸气,以及维持水循环。水中的盐类和化学物质会释放出氧气,使地球上的生物呼吸。此外,水还具有许多其他功能,如保持水循环、维持水循环、调节气候和环境等。水是许多生物生存所必需的,它也是许多自然现象的重要基础。 [H]: 海水是咸的主要原因是因为其中含有大量的溶解盐类,其中最主要的是氯化钠(NaCl)。氯化钠是一种非常常见的盐类,它存在于地球上的大部分地区,包括海洋、湖泊、河流和地下水。当水从海洋中蒸发时,它会带走一部分盐分,使海水变得咸。此外,海中还含有其他溶解盐类的物质,如硫酸盐、硫酸镁、硫酸钙、硫酸镁盐、硫酸镁钾、硫酸镁钠、硫酸镁钙、硫酸镁镁钙、硫酸镁镁钙、硫酸镁镁钙、硫酸镁镁钠、硫酸镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁钙、硫酸镁镁镁镁钙、硫酸镁镁镁镁钙、硫酸镁镁镁镁钙、硫酸镁镁镁镁钙、硫酸镁镁镁镁钙、硫酸镁镁镁镁钙、硫酸镁镁镁镁镁钙、硫酸镁镁镁镁镁钙、硫酸镁镁镁镁镁钙、硫酸镁镁镁镁镁钙、硫酸镁镁镁镁镁镁钙、硫酸镁镁镁镁镁镁钙、硫酸镁镁镁镁镁镁钙、硫酸镁镁镁镁镁镁镁钙、硫酸镁镁镁镁 [Q]: 写一段python的快速排序代码 [A]: 好的,以下是Python的快速排序代码:def quick_sort(arr): if len(arr) <= 1: return arr else: pivot = arr[len(arr) // 2] left = [x for x in arr[1:]] right = [x for x in arr[2:] if x < pivot] return quick_sort(left) + [pivot] + quick_sort(right)# 测试代码arr = [3, 6, 8, 10, 1, 2, 1]print(quick_sort(arr))这段代码首先检查数组的长度是否小于等于1。如果是,那么它就按照从小到大的顺序排列。接着,对于数组的每个元素,它想要进行递归地排序,直到达到排序的结束位置。如果数组的长度是奇数,那么它就按照从大到小的顺序排列。最后,平均的时间复杂度是O(n log n),其中n是数组的长度。 [B]: 以下是一个使用Python的快速排序算法的实现:def sort_array(arr): # 生成一个随机排列 sorted_array = sorted(arr) # 返回排序后的列表 return sorted(arr)# 示例arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]# 调用函数sorted_array = sort_array(arr)print("原始数组是:", sorted_array)这段代码首先生成一个随机排列,然后通过一个列表的长度进行排序。最后,返回排序后的列表。注意,这个代码使用了Python的内置sorted函数和内置的sorted()函数,它们在时间复杂度为O(n log n)的情况下,比直接使用sorted()函数更快。 [C]: 以下是一个简单的排序算法,适用于小型数组,包括小于等于5的数组。def quick_sort(arr): if len(arr) <= 1: return arr else for _ in range(2, 51): if arr[len(arr) - 1:] is not None: arr[len(arr) - 1] = arr[len(arr) - 1] arr[len(arr)] = arr[len(arr)] return quick_sort(arr)# 示例arr = [3, 5, 1, 4, 1, 2, 1]sorted_arr = quick_sort(arr)print("Sorted array is:", sorted_arr)### 代码解释:1. **函数定义**:quick_sort 函数接受一个数组 arr 作为参数。2. **循环**:对于 arr 的元素,使用递归来实现,即从右到左遍历数组,并将当前元素与数组的当前元素交换位置,直到数组达到空或数组长度小于等于 arr。3. **递归调用**:遍历数组,将当前元素与数组的当前元素进行交换,直到数组达到空或数组长度小于等于 arr。4. **检查数组长度**:使用 if __name__ == "__main__" 检查数组的长度是否小于 arr,如果是,就将数组从较大的数组移动到数组的末尾。5. **输出排序后的结果** [D]: 作为一个AI语言模型,我无法编写Python程序。但是,Python是一个非常流行的编程语言,它具有许多优秀的库和框架,包括NumPy、Pandas、Matplotlib、Pandas、Bandke、Norm和NKlearn等。Python有很多优秀的自然语言处理库和框架,可以用于处理和分析各种类型的数据,如文本、图像、音频、视频等。Python还可以用于创建各种应用程序,如网页搜索、游戏开发、科学计算等。Python非常灵活,也支持多种编程风格,因此可以轻松地找到适合自己的解决方案。 [E]: 作为AI语言模型,我可以回答您的问题。但是我无法进行编程操作,因为我无法确定您想问的是哪种编程语言。请您提供更具体的问题,以便我能够更好地回答您的问题。 [F]: 快速排序(Quick Sort)是一种简单的排序算法,它通过将数据划分为小组,每组数据按照一定规则排序,最终得到一个有序数组。 [G]: 好的,以下是一段快速排序的Python代码```python sorted_print(sorted(sorted(sorted(sorted_print))```输出结果为:```[1, 2, 3, 4, 5, 6, 7, 8, 9]``` [H]: 快速排序是一种基于分治思想的高效排序算法,它的时间复杂度为 O(n log n)。它的基本思想是通过一趟排序将待排序序列划分为两个子序列,其中一个子序列的所有元素都比另一个子序列的所有元素小,然后对这两个子序列分别递归地进行排序。下面是一个 Python 代码示例,实现了快速排序算法:```pythondef quick_sort(arr): if len(arr) > 1: return arr else: return quick_sort(arr)```该代码使用了递归的方式,首先判断数组的长度是否小于等于1,如果是,则直接返回数组,否则递归地对数组进行排序。在递归过程中,我们首先判断数组的第一个元素是否大于等于1,如果是,则将数组中的元素与第一个元素进行比较,如果不等于1,则将数组中的元素与第二个元素进行比较,以此类推,直到所有元素都被排序。该代码的时间复杂度为 O(n log n),是一个非常实用的排序算法。 [Q]: 截至2025年2月9日14时31分,电影《哪吒之魔童闹海》累计票房(含预售)突破78.09亿元,超过《星球大战:天行者崛起》的票房成绩,成为首部进入全球票房榜前40的亚洲电影。这一成就不仅标志着中国电影在国际市场的突破,也展示了中国动画电影的巨大潜力和市场吸引力。从这段文字总结20字的摘要给我。 [A]: 电影《哪吒之魔童闹海》票房突破78.09亿元,成为首部进入全球票房榜前40的亚洲电影。这标志着中国电影的国际突破,也展现了中国动画电影的巨大潜力。 [B]: 2025年2月9日,中国电影在世界舞台上突破78.09亿元,超过《星球大战:天行者崛起》的票房成绩。 [C]: 电影《哪吒之魔童闹海》票房突破78.09亿元,成为全球票房榜前40的亚洲电影,展现中国动画电影的巨大潜力。 [D]: 《哪吒之魔童闹海》累计票房突破78.09亿,成为首部进入全球票房榜前40的亚洲电影,展示了中国电影在国际市场的突破,同时展示了中国动画电影的巨大潜力和市场吸引力。 [E]: 《猛龙强:打开世界奇幻冒险》成功地,上海成功吸引了众多观众,同时也引起了广泛的关注。 [F]: 《哪吒之魔童闹海》是一部让中国动画电影成为国际市场的巨大潜力。该电影以其华丽的画面和惊险的战斗场面而闻名于世。 [G]: 《哪行是值得的》是2021年1月9日14时31分,电影《哪成大作为》在电影市场突破7800亿美元,成为中国动画电影的突破,并展示中国动画电影的巨大潜力和市场吸引力。 [H]: 我是一个参数很少的AI模型🥺,知识库较少,无法直接回答您的问题,换个问题试试吧👋 ``` --- 🙋‍直接把以上所有问题和模型的回答丢给DeepSeek-R1,让它帮忙点评和排名打分:
具体点评 ### 评分标准: - **准确性**:回答是否正确且无明显错误。 - **完整性**:回答是否涵盖了问题的核心要点。 - **逻辑性**:回答是否有条理,是否符合逻辑。 - **代码质量**:代码是否能正常运行,逻辑是否清晰。 ### 点评: 1. **A模型**: - **优点**:回答非常全面,信息量大,逻辑清晰,尤其在长江、大熊猫、海水咸味等问题上表现优异。代码虽然有小瑕疵,但整体思路正确。 - **缺点**:部分回答稍显冗长,但不影响整体质量。 - **总评**:综合表现最佳,得分最高。 2. **H模型**: - **优点**:回答较为准确,尤其在珠穆朗玛峰、万有引力等问题上表现出色。代码虽未完全展示,但解释较为详细。 - **缺点**:部分回答略显啰嗦,但逻辑性较强。 - **总评**:仅次于A模型,表现稳定。 3. **C模型**: - **优点**:回答简洁明了,尤其在大熊猫、快速排序代码等问题上表现较好。 - **缺点**:部分回答略显简短,缺乏深入解释。 - **总评**:整体表现不错,但细节上略逊于A和H。 4. **F模型**: - **优点**:回答较为准确,尤其在长江、万有引力等问题上表现尚可。代码部分有一定逻辑性。 - **缺点**:部分回答不够深入,代码存在一些小问题。 - **总评**:表现中规中矩,仍有提升空间。 5. **D模型**: - **优点**:回答基本准确,尤其在万有引力、长江等问题上表现尚可。 - **缺点**:部分回答过于简略,代码存在明显错误。 - **总评**:表现一般,需改进代码部分。 6. **B模型**: - **优点**:回答较为准确,尤其在长江、海水咸味等问题上表现尚可。 - **缺点**:部分回答逻辑性较差,代码存在较大问题。 - **总评**:表现平平,需进一步优化。 7. **E模型**: - **优点**:部分回答较为准确,尤其在海水咸味、大熊猫等问题上表现尚可。 - **缺点**:回答过于简略,代码部分几乎无法运行。 - **总评**:表现不佳,需大幅提升。 8. **G模型**: - **优点**:几乎没有明显的优点。 - **缺点**:回答严重偏离主题,代码部分完全无法运行。 - **总评**:表现最差,需大幅改进。 --- ### 总结: - **A模型**在各方面表现最为出色,尤其在复杂问题的回答上展现了极高的准确性与逻辑性。 - **H模型**紧随其后,表现稳定,但在某些细节上略显不足。 - **G模型**表现最差,回答偏离主题且代码无法运行,需大幅改进。
### 打分排序 | 排名 | 模型 | 准确性 (30分) | 完整性 (30分) | 逻辑性 (20分) | 代码质量 (20分) | 总分 (100分) | |----|----|-----------|-----------|-----------|------------|-----------| | 1 | A | 28 | 29 | 19 | 20 | 96 | | 2 | H | 27 | 28 | 18 | 20 | 93 | | 3 | C | 26 | 27 | 18 | 18 | 89 | | 4 | F | 25 | 26 | 17 | 18 | 86 | | 5 | D | 24 | 25 | 17 | 16 | 82 | | 6 | B | 23 | 24 | 16 | 15 | 78 | | 7 | E | 22 | 23 | 15 | 14 | 74 | | 8 | G | 10 | 12 | 10 | 10 | 42 | ### 👉主观效果总结 个人主观评价与DeepSeek-R1基本相符,其中: * MiniMind系列的排序非常符合直觉,参数越大+训练数据越充分评分越高,幻觉和错误都会比小模型肉眼可见的好。 * H模型的回答肉眼看起来是不错的,尽管存在些许幻觉瞎编的情况。 * G模型可能训练数据不够完备,给出的权重经过测试效果不佳。 * 再复诵一遍经久不衰的Scaling Law: 参数越大,训练数据越多模型的性能越强。 --- ## Ⅲ Objective Benchmark 下面就到喜闻乐见的benchmark刷榜测试环节,就不找乐子和qwen、glm级别的中文模型做对比了。 这里选取了一些<1B的微型模型进行横评比较, 测试集选择C-Eval、CMMLU、A-CLUE、TMMLU+这几个纯中文语言榜单。
测评框架 测评框架选择[lm-evaluation](https://github.com/EleutherAI/lm-evaluation-harness), 安装后启动测试非常方便: ```bash lm_eval --model hf --model_args pretrained=<填写模型路径>,device=cuda,dtype=auto --tasks ceval* --batch_size 8 --trust_remote_code ```
PS: 在这种全是选择题的测评集中,为了避免回复格式的难以固定的特点, 所以常用做法是直接把`A`,`B`,`C`,`D`四个字母对应token的预测概率取出来,将其中概率最大的字母与标准答案计算正确率。 选择题1/4乱选的正确率是25%,然而这个量级的所有模型都集中在25附近,甚至很多时候不如瞎选,是不是像极了高中完形填空的滑铁卢正确率... MiniMind模型本身预训练数据集小的可怜,也没有针对性的对测试集做刷榜微调,因此结果图一乐即可: | models | from | params↓ | ceval↑ | cm mlu↑ | aclue↑ | tmmlu+↑ | |-------------------------------------------------------------------------------|---------------|---------|--------|---------|--------|---------| | MiniMind2 | JingyaoGong | 104M | 26.52 | 24.42 | 24.97 | 25.27 | | MiniMind2-Small | JingyaoGong | 26M | 26.37 | 24.97 | 25.39 | 24.63 | | MiniMind2-MoE | JingyaoGong | 145M | 26.6 | 25.01 | 24.83 | 25.01 | | [Steel-LLM](https://github.com/zhanshijinwat/Steel-LLM) | ZhanShiJin | 1121M | 24.81 | 25.32 | 26 | 24.39 | | [GPT2-medium](https://huggingface.co/openai-community/gpt2-medium) | OpenAI | 360M | 23.18 | 25 | 18.6 | 25.19 | | [TinyLlama-1.1B-Chat-V1.0](https://github.com/jzhang38/TinyLlama) | TinyLlama | 1100M | 25.48 | 25 | 25.4 | 25.13 | | [SmolLM2](https://github.com/huggingface/smollm) | HuggingFaceTB | 135M | 24.37 | 25.02 | 25.37 | 25.06 | | [Aquila-Instruct](https://www.modelscope.cn/models/BAAI/Aquila-135M-Instruct) | BAAI | 135M | 25.11 | 25.1 | 24.43 | 25.05 | ![compare_radar](./images/compare_radar.png) # 📌 其它 (Others) ## 模型转换 * [./scripts/convert_model.py](./scripts/convert_model.py)可以实现`torch模型/transformers`模型之间的转换 --- ## 基于MiniMind-API服务接口 * [./scripts/serve_openai_api.py](./scripts/serve_openai_api.py)完成了兼容openai-api的最简聊天接口,方便将自己的模型接入第三方UI 例如FastGPT、OpenWebUI、Dify等等。 * 从[Huggingface](https://huggingface.co/collections/jingyaogong/minimind-66caf8d999f5c7fa64f399e5)下载模型权重文件,文件树: ``` (root dir) ├─ | ├── config.json | ├── generation_config.json | ├── LMConfig.py | ├── model.py | ├── pytorch_model.bin | ├── special_tokens_map.json | ├── tokenizer_config.json | ├── tokenizer.json ``` * 启动聊天服务端 ```bash python serve_openai_api.py ``` * 测试服务接口 ```bash python chat_openai_api.py ``` * API接口示例,兼容openai api格式 ```bash curl http://ip:port/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "model-identifier", "messages": [ { "role": "user", "content": "世界上最高的山是什么?" } ], "temperature": 0.7, "max_tokens": 512, "stream": true }' ``` ## VLLM模型推理(服务) [vLLM](https://github.com/vllm-project/vllm)是极其流行的高效推理框架,支持大模型快速部署,优化显存利用与吞吐量。 ```bash vllm serve ./MiniMind2/ --model-impl transformers --served-model-name "minimind" ``` 服务将以openai api协议启动,端口默认为8000。 更多用法请参考官方说明~ ## llama.cpp [llama.cpp](https://github.com/ggerganov/llama.cpp)是一个C++库, 可以在命令行下直接使用,支持多线程推理,支持GPU加速。 参考官方仓库安装后,在`convert_hf_to_gguf.py` ~760行插入 ```text # 添加MiniMind2 tokenizer支持 if res is None: res = "smollm" ``` 转换自定义训练的minimind模型 -> gguf ```bash python convert_hf_to_gguf.py ../minimind/MiniMind2/ ``` 量化模型 ```bash ./build/bin/llama-quantize ../minimind/MiniMind2/MiniMind2-109M-F16.gguf ../minimind/MiniMind2/Q4-MiniMind2.gguf Q4_K_M ``` 命令行推理 ```bash ./build/bin/llama-cli -m ../minimind/MiniMind2/MiniMind2-109M-F16.gguf --chat-template chatml ``` 更多用法请参考官方说明~ ## ollama [ollama](https://ollama.ai/)是本地运行大模型的工具,支持多种开源LLM,简单易用。 通过ollama加载自定义的gguf模型,新建minimind.modelfile: ```text FROM ./MiniMind2-109M-F16.gguf TEMPLATE """{{ if .System }}<|im_start|>system {{ .System }}<|im_end|> {{ end }}{{ if .Prompt }}<|im_start|>user {{ .Prompt }}<|im_end|> {{ end }}<|im_start|>assistant """ ``` 加载模型并命名为`minimind2` ```bash ollama create -f minimind.modelfile minimind2 ``` 启动推理 ```text ollama run minimind2 > 你好,我是MiniMind2,一个基于xxxxxxxx ``` 更多用法请参考官方说明~ # 📌 Acknowledge > [!NOTE] > 如果觉得`MiniMind系列`对您有所帮助,可以在 GitHub 上加一个⭐
> 篇幅超长水平有限难免纰漏,欢迎在Issues交流指正或提交PR改进项目
> 您的小小支持就是持续改进此项目的动力! ## 🤝[贡献者](https://github.com/jingyaogong/minimind/graphs/contributors)         ## 😊鸣谢 @ipfgao: 🔗训练步骤记录 @chuanzhubin: 🔗代码逐行注释 @WangRongsheng: 🔗大型数据集预处理 @pengqianhan: 🔗一个简明教程 @RyanSunn: 🔗推理过程学习记录 @Nijikadesu: 🔗以交互笔记本方式分解项目代码
参考链接 & 感谢以下优秀的论文或项目 - 排名不分任何先后顺序 - [https://github.com/meta-llama/llama3](https://github.com/meta-llama/llama3) - [https://github.com/karpathy/llama2.c](https://github.com/karpathy/llama2.c) - [https://github.com/DLLXW/baby-llama2-chinese](https://github.com/DLLXW/baby-llama2-chinese) - [(DeepSeek-V2)https://arxiv.org/abs/2405.04434](https://arxiv.org/abs/2405.04434) - [https://github.com/charent/ChatLM-mini-Chinese](https://github.com/charent/ChatLM-mini-Chinese) - [https://github.com/wdndev/tiny-llm-zh](https://github.com/wdndev/tiny-llm-zh) - [(Mistral-MoE)https://arxiv.org/pdf/2401.04088](https://arxiv.org/pdf/2401.04088) - [https://github.com/Tongjilibo/build_MiniLLM_from_scratch](https://github.com/Tongjilibo/build_MiniLLM_from_scratch) - [https://github.com/jzhang38/TinyLlama](https://github.com/jzhang38/TinyLlama) - [https://github.com/AI-Study-Han/Zero-Chatgpt](https://github.com/AI-Study-Han/Zero-Chatgpt) - [https://github.com/xusenlinzy/api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm) - [https://github.com/HqWu-HITCS/Awesome-Chinese-LLM](https://github.com/HqWu-HITCS/Awesome-Chinese-LLM)
## 🫶支持者 github contribution grid snake animation github contribution grid snake animation Star History Chart # License This repository is licensed under the [Apache-2.0 License](LICENSE). ## /README_en.md
![logo](./images/logo.png)
![visitors](https://visitor-badge.laobi.icu/badge?page_id=jingyaogong/minimind) [![GitHub Repo stars](https://img.shields.io/github/stars/jingyaogong/minimind?style=social)](https://github.com/jingyaogong/minimind/stargazers) [![GitHub Code License](https://img.shields.io/github/license/jingyaogong/minimind)](LICENSE) [![GitHub last commit](https://img.shields.io/github/last-commit/jingyaogong/minimind)](https://github.com/jingyaogong/minimind/commits/master) [![GitHub pull request](https://img.shields.io/badge/PRs-welcome-blue)](https://github.com/jingyaogong/minimind/pulls) [![Collection](https://img.shields.io/badge/🤗-MiniMind%20%20Collection-blue)](https://huggingface.co/collections/jingyaogong/minimind-66caf8d999f5c7fa64f399e5)

"The Greatest Path is the Simplest"

[中文](./README.md) | English
* This open-source project aims to train a super-small language model **MiniMind** with only 3 RMB cost and 2 hours, starting completely from scratch. * The **MiniMind** series is extremely lightweight, with the smallest version being $\frac{1}{7000}$ the size of GPT-3, making it possible to train quickly on even the most ordinary personal GPUs. * The project also open-sources the minimalist structure of the large model, including extensions for shared mixed experts (MoE), dataset cleaning, pretraining, supervised fine-tuning (SFT), LoRA fine-tuning, direct preference optimization (DPO) algorithms, and model distillation algorithms, along with the full code of the process. * **MiniMind** also expands into vision multimodal VLM: [MiniMind-V](https://github.com/jingyaogong/minimind-v). * All core algorithm code is reconstructed from scratch using native PyTorch! It does not rely on abstract interfaces provided by third-party libraries. * This is not only a full-stage open-source reproduction of a large language model but also a tutorial for beginners in LLM. * We hope this project will serve as an inspiring example for everyone, helping to enjoy the fun of creation and promoting the progress of the wider AI community! > To avoid misunderstanding, the "2 hours" test is based on NVIDIA 3090 hardware (single GPU), and the "3 RMB" refers to the GPU server rental cost. Details of the specifications can be found below. ---
![minimind2](./images/minimind2.gif) [🔗🍓Reason Model](https://www.modelscope.cn/studios/gongjy/MiniMind-Reasoning) | [🔗🤖Standard Model](https://www.modelscope.cn/studios/gongjy/MiniMind) | [🔗🎞️Video Introduction](https://www.bilibili.com/video/BV12dHPeqE72/?share_source=copy_web&vd_source=670c2504f88726f8cf4a21ef6147c0e8)
Hugging Face Logo ModelScope Logo
# 📌 Introduction The emergence of Large Language Models (LLMs) has sparked unprecedented global attention on AI. Whether it's ChatGPT, DeepSeek, or Qwen, their stunning performance leaves people in awe. However, the massive scale of hundreds of billions of parameters makes it not only difficult to train them on personal devices, but also almost impossible to deploy them. Opening the "black box" of large models and exploring their inner workings is exhilarating! Sadly, 99% of explorations can only stop at fine-tuning existing large models with techniques like LoRA, learning a few new commands or tasks. It's like teaching Newton how to use a 21st-century smartphone—though interesting, it completely deviates from the original goal of understanding the essence of physics. Meanwhile, third-party large model frameworks and toolkits, such as transformers+trl, almost only expose highly abstract interfaces. With just 10 lines of code, you can complete the entire training process of "loading model + loading dataset + inference + reinforcement learning". While this efficient encapsulation is convenient, it's like a high-speed spaceship, isolating us from the underlying implementation and hindering our opportunity to dive deep into the core code of LLMs. However, "building a plane with Legos is far more exciting than flying first-class!" What's worse, the internet is flooded with paid courses and marketing accounts, selling AI tutorials with flawed and half-understood content. Therefore, the goal of this project is to lower the learning threshold for LLMs, allowing everyone to start by understanding each line of code, and to train a very small language model from scratch, not just performing **inference**! With server costs of less than 3 RMB, you can experience the entire process of building a language model from 0 to 1. Let's enjoy the fun of creation together! > [!NOTE] > (As of 2025-02-07) The MiniMind series has completed pretraining for multiple models, with the smallest one being only > 25.8M (0.02B) and capable of smooth conversation!
Models List | Model (Size) | Inference Usage (Approx.) | Release | |-------------------------|---------------------------|------------| | MiniMind2-small (26M) | 0.5 GB | 2025.04.26 | | MiniMind2-MoE (145M) | 1.0 GB | 2025.04.26 | | MiniMind2 (104M) | 1.0 GB | 2025.04.26 | | minimind-v1-small (26M) | 0.5 GB | 2024.08.28 | | minimind-v1-moe (4×26M) | 1.0 GB | 2024.09.17 | | minimind-v1 (108M) | 1.0 GB | 2024.09.01 |
**Project Includes** - All code for the MiniMind-LLM structure (Dense+MoE models). - Detailed training code for the Tokenizer. - Full training code for Pretrain, SFT, LoRA, RLHF-DPO, and model distillation. - High-quality datasets collected, distilled, cleaned, and deduplicated at all stages, all open-source. - From scratch implementation of pretraining, instruction fine-tuning, LoRA, DPO reinforcement learning, and white-box model distillation. Most key algorithms do not rely on third-party encapsulated frameworks and are all open-source. - Compatible with third-party frameworks like `transformers`, `trl`, `peft`, etc. - Training supports single machine single GPU, single machine multi-GPU (DDP, DeepSpeed), and wandb visualized training processes. Supports dynamic start/stop of training. - Model testing on third-party evaluation benchmarks (C-Eval, C-MMLU, OpenBookQA, etc.). - A minimal server implementing the Openai-Api protocol, easy to integrate into third-party ChatUI applications ( FastGPT, Open-WebUI, etc.). - Fully compatible with popular community inference engines like llama.cpp, vllm, ollama, or training frameworks such as Llama-Factory. - A simple chat WebUI front-end implemented using streamlit. - Reproduction (distillation/RL) of the large inference model DeepSeek-R1 as the MiniMind-Reason model, **data + model** all open-source! We hope this open-source project can help LLM beginners quickly get started! ### 👉**Update log**
2025-04-26 (newest 🎉🎉🎉) • Major Updates • For compatibility needs, visit [🔗Legacy Repository Content🔗](https://github.com/jingyaogong/minimind/tree/7da201a944a90ed49daef8a0265c959288dff83a). • MiniMind model parameters have been fully renamed to align with Transformers library models (unified naming). • The `generate` method has been refactored, now inheriting from the `GenerationMixin` class. • 🔥 Support for popular third-party ecosystems like llama.cpp, vllm, and ollama. • Standardized code and directory structure. • Updated vocabulary tokens: `` → `<|im_start|><|im_end|>`. ```text To ensure compatibility with third-party inference frameworks (llama.cpp, vllm), this update comes at a non-trivial cost. Models saved before 2025-04-26 can no longer be **directly** loaded for inference. Due to differences in positional encoding between Llama and MiniMind, QK values diverge after weight mapping. MiniMind2 legacy models have been restored via weight mapping + (fine-tuning) QKVO linear layer calibration. After this update, maintenance for the entire `minimind-v1` series will be discontinued, and the models will be removed from the repository. ```
2025-02-09 - Major update since the release, with the release of MiniMind2 Series. - Almost all code has been refactored, using a more streamlined and unified structure. For compatibility with old code, please refer to the [🔗Old Repository Contents🔗](https://github.com/jingyaogong/minimind/tree/6e9cd28ef9b34a0a10afbdf6f59e65cb6e628efb). - Removed the data preprocessing step. Unified dataset format, switched to `jsonl` format to eliminate issues with dataset downloads. - MiniMind2 series shows a significant improvement over MiniMind-V1. - Minor issues: {kv-cache syntax is more standard, MoE load balancing loss is considered, etc.} - Provided a training solution for transferring the model to private datasets (e.g., medical models, self-awareness examples). - Streamlined the pretraining dataset and significantly improved the quality of the pretraining data, greatly reducing the time needed for personal rapid training, with a single 3090 GPU achieving reproduction in just 2 hours! - Updated: LoRA fine-tuning now operates outside of the `peft` wrapper, implemented LoRA process from scratch; DPO algorithm is implemented using native PyTorch; native model white-box distillation. - MiniMind2-DeepSeek-R1 series distilled models have been created! - MiniMind2 now has some English proficiency! - Updated MiniMind2 performance results based on additional large model benchmark tests.
2024-10-05 - Expanded MiniMind to include multimodal capabilities—visual. - Check out the twin project [minimind-v](https://github.com/jingyaogong/minimind-v) for more details!
2024-09-27 - Updated preprocessing method for the pretrain dataset on 09-27 to ensure text integrity. The method of converting to .bin for training has been abandoned (slightly sacrificing training speed). - The preprocessed pretrain file is now named: pretrain_data.csv. - Removed some redundant code.
2024-09-17 - Updated minimind-v1-moe model. - To avoid ambiguity, the mistral_tokenizer is no longer used, and all tokenization is done with the custom minimind_tokenizer.
2024-09-01 - Updated minimind-v1 (108M) model, using minimind_tokenizer, with 3 pretraining rounds + 10 SFT rounds, allowing for more comprehensive training and improved performance. - The project has been deployed on ModelScope Creative Space and can be experienced on the site: - [🔗ModelScope Online Experience🔗](https://www.modelscope.cn/studios/gongjy/minimind)
2024-08-27 - Initial open-source release of the project.
# 📌 Quick Start
Sharing My Hardware and Software Configuration (For Reference Only) * 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.16 * [requirements.txt](./requirements.txt)
### Step 0 ```bash git clone https://github.com/jingyaogong/minimind.git ``` ## Ⅰ Test Pre-trained Model ### 1. Environment Setup ```bash pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ``` ### 2. Download the Model ```bash git clone https://huggingface.co/jingyaogong/MiniMind2 ``` ### (Optional) Command-line Q&A ```bash # load=0: load from pytorch model, load=1: load from transformers-hf model python eval_model.py --load 1 --model_mode 2 ``` ### (Optional) Launch WebUI ```bash # May require `python>=3.10`, install with `pip install streamlit` # cd scripts streamlit run web_demo.py ``` ### (Optional) Third-party inference frameworks ```bash # ollama ollama run jingyaogong/minimind2 # vllm vllm serve ./MiniMind2/ --served-model-name "minimind" ``` ## Ⅱ Training from Scratch ### 1. Environment Setup ```bash pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ```
Note: Test if Torch can use CUDA ```bash import torch print(torch.cuda.is_available()) ``` If CUDA is not available, please download the `.whl` file from [torch_stable](https://download.pytorch.org/whl/torch_stable.html) and install it. Refer to this [link](https://blog.csdn.net/weixin_45456738/article/details/141029610?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E8%A3%85torch&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-141029610.nonecase&spm=1018.2226.3001.4187) for guidance.
### 2. Data Download Download the required data files from the [dataset download link](https://www.modelscope.cn/datasets/gongjy/minimind_dataset/files) (please `mkdir dataset`) and place them in the `./dataset` directory.
Note: Dataset Information By default, it is recommended to download `pretrain_hq.jsonl` + `sft_mini_512.jsonl` for the fastest Zero-chat model reproduction. You can freely choose data files. Various combinations are provided below, and you can select according to your training needs and GPU resources.
### 3. Start Training The directory is located at `trainer` **3.1 Pretraining (Learning Knowledge)** ```bash python train_pretrain.py ``` > Execute pretraining to get `pretrain_*.pth` as the output weights for pretraining (where * represents the model > dimension, default is 512). **3.2 Supervised Fine-Tuning (Learning Dialogue Style)** ```bash python train_full_sft.py ``` > Execute supervised fine-tuning to get `full_sft_*.pth` as the output weights for instruction fine-tuning (where `full` > represents full parameter fine-tuning).
Note: Training Information By default, during training, the model parameters are saved every 100 steps to `./out/***.pth` (each time overwriting the old weight file). For simplicity, only the two training stages are listed here. For other training methods (LoRA, distillation, reinforcement learning, fine-tuning inference, etc.), refer to the detailed explanation in the [Experiments] section below.
--- ### 4. Testing Model Performance Ensure that the model `*.pth` file you want to test is located in the `./out/` directory. Alternatively, you can download and use the `*.pth` files I trained from [here](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch/files). ```bash python eval_model.py --model_mode 1 # Default is 0: Test pretrain model, set to 1: Test full_sft model ```
Note: Testing Information For more details, you can check the `eval_model.py` script code. The model_mode options are 0: Pretraining model, 1: SFT-Chat model, 2: RLHF-Chat model, 3: Reason model.
--- > [!TIP] > All training scripts are built using PyTorch's native framework and support multi-GPU acceleration. If your device has > N (N>1) GPUs: Start training with N GPUs on a single machine (DDP, supports multi-node, multi-GPU clusters): ```bash torchrun --nproc_per_node N train_xxx.py ```
Note: Others Start training with N GPUs on a single machine (DeepSpeed): ```bash deepspeed --master_port 29500 --num_gpus=N train_xxx.py ``` Enable wandb to record the training process if needed: ```bash # Need to log in: wandb login torchrun --nproc_per_node N train_xxx.py --use_wandb # and python train_xxx.py --use_wandb ``` By adding the `--use_wandb` parameter, the training process will be recorded, and after training, you can view the process on the wandb website. Modify the `wandb_project` and `wandb_run_name` parameters to specify project and run names.
# 📌 Data Overview ## Ⅰ Tokenizer A tokenizer maps words from natural language into numbers like `0, 1, 36`, which can be understood as the page numbers in a "dictionary". You can either construct your own vocabulary to train a tokenizer (code is available in `./scripts/train_tokenizer.py`, for educational purposes; MiniMind comes with a built-in tokenizer, so training one is unnecessary unless absolutely needed), or you can choose from well-known open-source tokenizers. The advantage of using a popular dictionary, like the Xinhua or Oxford dictionary, is that the token encoding has good compression efficiency, but the downside is that the vocabulary can be very large, with hundreds of thousands of words or phrases. On the other hand, a custom tokenizer allows flexibility in controlling the vocabulary's length and content, but the trade-off is lower compression efficiency (e.g., "hello" might be split into five independent tokens like "h", " e", "l", "l", "o"), and it may miss rare words. The choice of vocabulary is crucial. The output of an LLM is essentially a multi-class classification problem over the vocabulary, with the model decoding the final output into natural language. Since MiniMind's model size needs to be strictly controlled, the vocabulary length should be kept short to avoid the embedding layer dominating the model's overall parameters. Thus, a smaller vocabulary size is beneficial.
Tokenizer Details Here are the vocabulary sizes of several popular open-source models: | Tokenizer Model | Vocabulary Size | Source | |--------------------|-----------------|-----------------------| | yi tokenizer | 64,000 | 01万物 (China) | | qwen2 tokenizer | 151,643 | Alibaba Cloud (China) | | glm tokenizer | 151,329 | Zhipu AI (China) | | mistral tokenizer | 32,000 | Mistral AI (France) | | llama3 tokenizer | 128,000 | Meta (USA) | | minimind tokenizer | 6,400 | Custom | > 👉 **2024-09-17 Update**: To avoid ambiguity in previous versions and control model size, all MiniMind models now use > the `minimind_tokenizer`. All previous versions using the `mistral_tokenizer` have been deprecated. ``` # Some personal thoughts > Although the `minimind_tokenizer` has a smaller vocabulary size and the encoding/decoding efficiency is weaker than other Chinese-friendly tokenizers like `qwen2` or `glm`, MiniMind has chosen to use this custom tokenizer to maintain a lightweight model overall and avoid an imbalance between the embedding and computation layers. > The `minimind_tokenizer` vocabulary size is only 6400, which ensures that the total parameters of the LLM are kept to a minimum (around 25.8M). > The training data for this tokenizer (`tokenizer_train.jsonl`) is sourced from the "Jiangshu Large Model Dataset". This part of the data is relatively less important, but you can freely choose any data for training if needed. ```
## Ⅱ Pretrain Data After learning from the poor-quality pretraining data of MiniMind-V1, which resulted in nonsensical outputs, I decided not to use large-scale unsupervised datasets for pretraining post-`2025-02-05`. Instead, I extracted the Chinese portion of the [Jiangshu Large Model Dataset](https://www.modelscope.cn/datasets/deepctrl/deepctrl-sft-data), cleaned the content to include only characters of length `<512`, resulting in around 1.6GB of high-quality pretraining data, saved as `pretrain_hq.jsonl`. The data format for `pretrain_hq.jsonl` is: ```bash {"text": "如何才能摆脱拖延症? 治愈拖延症并不容易,但以下建议可能有所帮助..."} ``` ## Ⅲ SFT Data The [Jiangshu Large Model SFT Dataset](https://www.modelscope.cn/datasets/deepctrl/deepctrl-sft-data) is a complete, well-formatted dataset for large model training and research. It includes approximately 10M Chinese sentences and 2M English sentences. However, the provided format is messy, and using the entire dataset for SFT would be too costly. I have cleaned this dataset, removing noisy entries with special characters and symbols, and only kept content with a length `<512`. This cleaned dataset is exported as `sft_512.jsonl` (~7.5GB). Additionally, I have collected around 1M high-quality dialogue data from Qwen2/2.5, cleaned and exported the content with lengths `<2048` into `sft_2048.jsonl` (~9GB) and those with lengths `<1024` into `sft_1024.jsonl` (~5.5GB). Further cleaning of these SFT datasets (only keeping content with a higher ratio of Chinese characters) resulted in `sft_mini_512.jsonl` (~1.2GB). The data format for all SFT files `sft_X.jsonl` is as follows: ```text { "conversations": [ {"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好!"}, {"role": "user", "content": "再见"}, {"role": "assistant", "content": "再见!"} ] } ``` ## Ⅳ RLHF Data The [Magpie-DPO Dataset](https://www.modelscope.cn/datasets/Magpie-Align/MagpieLM-DPO-Data-v0.1) contains around 200k preference data generated from Llama3.1-70B/8B and can be used for training reward models to optimize response quality according to human preferences. I have cleaned this dataset by combining data with a total length `<3000` into `dpo.jsonl` (~0.9GB), which contains `chosen` (preferred) and `rejected` (rejected) replies. The data format for `dpo.jsonl` is: ```text { "chosen": [ {"content": "Q", "role": "user"}, {"content": "good answer", "role": "assistant"} ], "rejected": [ {"content": "Q", "role": "user"}, {"content": "bad answer", "role": "assistant"} ] } ``` ## Ⅴ Reasoning Dataset The excitement over **DeepSeek** in February 2025 has greatly influenced my interest in RL-guided reasoning models. I have already replicated **R1-Zero** using Qwen2.5. If time allows and if it works, I plan to update MiniMind with a reasoning model trained with RL, rather than a distilled model. Currently, the quickest and cost-effective approach is still distillation (black-box style). But due to the popularity of **R1**, I’ve combined several distilled datasets related to **R1**, including: - [R1-Llama-70B](https://www.modelscope.cn/datasets/Magpie-Align/Magpie-Reasoning-V2-250K-CoT-Deepseek-R1-Llama-70B) - [R1-Distill-SFT](https://www.modelscope.cn/datasets/AI-ModelScope/R1-Distill-SFT) - [Alpaca-Distill-R1](https://huggingface.co/datasets/shareAI/Alpaca-Distill-R1-ZH) - [deepseek_r1_zh](https://huggingface.co/datasets/jinliuxi/deepseek_r1_zh) After combining these, I exported the file as `r1_mix_1024.jsonl`. The format of this file is the same as `sft_X.jsonl`. ## Ⅵ Additional Datasets For more datasets related to Chinese LLMs, you can refer to [HqWu-HITCS/Awesome-Chinese-LLM](https://github.com/HqWu-HITCS/Awesome-Chinese-LLM), which collects and organizes open-source models, applications, datasets, and tutorials for Chinese LLMs. It's comprehensive and regularly updated. Big respect! --- ## Ⅷ MiniMind Dataset Download > [!NOTE] > After `2025-02-05`, MiniMind’s open-source datasets for final training are provided, so there is no need for > you to preprocess large datasets by yourself anymore. This helps avoid redundant work. Available for download from: [ModelScope](https://www.modelscope.cn/datasets/gongjy/minimind_dataset/files) | [HuggingFace](https://huggingface.co/datasets/jingyaogong/minimind_dataset/tree/main) > You don’t need to clone everything, just download the necessary files. Place the downloaded dataset files in the `./dataset/` directory (✨ required files are marked): ```bash ./dataset/ ├── dpo.jsonl (909MB) ├── lora_identity.jsonl (22.8KB) ├── lora_medical.jsonl (34MB) ├── pretrain_hq.jsonl (1.6GB, ✨) ├── r1_mix_1024.jsonl (340MB) ├── sft_1024.jsonl (5.6GB) ├── sft_2048.jsonl (9GB) ├── sft_512.jsonl (7.5GB) ├── sft_mini_512.jsonl (1.2GB, ✨) └── tokenizer_train.jsonl (1GB) ```
Dataset Descriptions * `dpo.jsonl` -- RLHF dataset * `lora_identity.jsonl` -- Self-identity dataset (e.g., "Who are you? I'm MiniMind..."), recommended for LoRA training ( also applicable for full parameter SFT) * `lora_medical.jsonl` -- Medical Q&A dataset, recommended for LoRA training (also applicable for full parameter SFT) * `pretrain_hq.jsonl`✨ -- Pretraining dataset from Jiangshu Technology * `r1_mix_1024.jsonl` -- DeepSeek-R1-1.5B distilled dataset (max length 1024 characters) * `sft_1024.jsonl` -- Qwen2.5 distilled data (subset of sft_2048, max length 1024) * `sft_2048.jsonl` -- Qwen2.5 distilled data (max length 2048) * `sft_512.jsonl` -- Jiangshu SFT dataset (max length 512) * `sft_mini_512.jsonl`✨ -- Minimal Jiangshu + Qwen2.5 distilled dataset (max length 512) * `tokenizer_train.jsonl` -- From Jiangshu Large Model Dataset (not recommended for custom tokenizer training)
![dataset](./images/dataset.jpg)
Explanation & Recommended Training Plans * The MiniMind2 Series has been trained on approximately 20GB of corpus, or about 4B tokens, corresponding to the data combination results above (Cost: 💰💰💰💰💰💰💰💰, Effect: 😊😊😊😊😊😊). * For the fastest Zero-model implementation from scratch, it is recommended to use the data combination of `pretrain_hq.jsonl` + `sft_mini_512.jsonl`. The specific costs and effects can be seen in the table below (Cost: 💰, Effect: 😊😊). * For those with sufficient computational resources or more focus on results, it is advisable to fully reproduce MiniMind2 with the first option; if you only have a single GPU or prefer a quick reproduction within a short time, the second option is strongly recommended. * [Compromise Plan] You can also freely combine medium-sized data like `sft_mini_512.jsonl`, `sft_1024.jsonl` for training (Cost: 💰💰💰, Effect: 😊😊😊😊).
# 📌 Model Structure MiniMind-Dense (like [Llama3.1](https://ai.meta.com/blog/meta-llama-3-1/)) uses the Transformer Decoder-Only structure, which differs from GPT-3 in the following aspects: * It adopts GPT-3's pre-normalization method, meaning normalization is done on the input of each Transformer sub-layer instead of on the output. Specifically, RMSNorm normalization function is used. * The SwiGLU activation function is used instead of ReLU to improve performance. * Like GPT-Neo, absolute position embeddings are removed and replaced with rotary position embeddings (RoPE), which perform better when handling inference beyond the training length. --- The MiniMind-MoE model is based on the MixFFN mixture of experts module from Llama3 and [Deepseek-V2/3](https://arxiv.org/pdf/2405.04434). * DeepSeek-V2, in terms of feedforward networks (FFN), adopts finer-grained expert splitting and shared expert isolation techniques to improve the performance of Experts. --- The overall structure of MiniMind remains consistent, with only minor adjustments made to RoPE computation, inference functions, and FFN layers. The structure is as shown in the figure below (redrawn): ![structure](./images/LLM-structure.png) ![structure-moe](./images/LLM-structure-moe.png) For model configuration modifications, see [./model/LMConfig.py](./model/LMConfig.py). Reference model parameter versions are shown in the table below: | Model Name | params | len_vocab | n_layers | d_model | kv_heads | q_heads | share+route | |-------------------|--------|-----------|----------|---------|----------|---------|-------------| | MiniMind2-Small | 26M | 6400 | 8 | 512 | 2 | 8 | - | | MiniMind2-MoE | 145M | 6400 | 8 | 640 | 2 | 8 | 1+4 | | MiniMind2 | 104M | 6400 | 16 | 768 | 2 | 8 | - | | minimind-v1-small | 26M | 6400 | 8 | 512 | 8 | 16 | - | | minimind-v1-moe | 4×26M | 6400 | 8 | 512 | 8 | 16 | 1+4 | | minimind-v1 | 108M | 6400 | 16 | 768 | 8 | 16 | - | # 📌 Experiment ## Ⅰ Training Cost - **Time Unit**: Hours (h). - **Cost Unit**: RMB (¥); 7¥ ≈ 1 USD. - **3090 Rental Unit Price**: ≈ 1.3¥/h (subject to real-time market rates). - **Reference Standard**: The table only shows the actual training time for the `pretrain` and `sft_mini_512` datasets. Other times are estimated based on dataset size (there may be some discrepancies). > Based on 3090 (single card) cost calculation | Model Name | params | pretrain | sft_mini_512 | sft_512 | sft_1024 | sft_2048 | RLHF | |-----------------|--------|------------------|------------------|---------------|-------------------|------------------|---------------| | MiniMind2-Small | 26M | ≈1.1h
≈1.43¥ | ≈1h
≈1.3¥ | ≈6h
≈7.8¥ | ≈4.58h
≈5.95¥ | ≈7.5h
≈9.75¥ | ≈1h
≈1.3¥ | | MiniMind2 | 104M | ≈3.9h
≈5.07¥ | ≈3.3h
≈4.29¥ | ≈20h
≈26¥ | ≈15h
≈19.5¥ | ≈25h
≈32.5¥ | ≈3h
≈3.9¥ | ---
Training Cost Summary & Prediction > MiniMind2-Small Parameters >> `pretrain_hq` + `sft_mini_512` dataset
Single 3090 GPU (1 epoch) + 2.1 hours + Cost: 2.73 RMB
You can train the MiniMind-Zero-0.025B model from scratch!!! > MiniMind2-Small Parameters >> `pretrain_hq` + `sft_512` + `sft_2048` + `dpo` dataset
Single 3090 GPU (2 epochs) + approximately 38.16 hours + Cost: 49.61 RMB
You can train the MiniMind2-Small-0.025B model from scratch!!! > MiniMind2 Parameters >> `pretrain_hq` + `sft_512` + `sft_2048` + `dpo` dataset
Single 3090 GPU (2 epochs) + approximately 122 hours + Cost: 158.6 RMB
You can train the MiniMind2-0.1B model from scratch!!!
✨With a single NVIDIA 3090 GPU, you can train `MiniMind-Zero` from scratch in just `2 hours` and for a cost of only `3 RMB`, achieving ChatBot functionality! ✨PS: If training on 8 GPUs with 4090s, the total time can be compressed to under 10 minutes! (Despite the shorter time, the cost is still around 3 RMB, which is comparable to the single GPU cost.) ✨This enables ultra-low barriers to entry, making it possible for everyone to experiment with large models! This is the original purpose behind the creation of the MiniMind series! ✨The `MiniMind-Zero` model, which costs only `3 RMB`, is not a gimmick! Chat test results: ```textmate 👶: Please introduce yourself. 🤖️: As an artificial intelligence, I don't have a real life or self-awareness, so I don't have my own life. I am designed to help users answer questions, provide information, engage in conversations, etc. My design and functionality are built using computer science, machine learning, artificial intelligence technologies, and algorithms, which I implement through programming and algorithms. 👶: Recommend some local specialties from Hangzhou. 🤖️: Hangzhou has many delicious local specialties, such as chicken skewers, eel heads, water-boiled fish, tofu pudding, etc. These foods are all very tasty, with unique flavors and textures, definitely worth trying. 👶: Please explain the concept of "large language models". 🤖️: A large language model is an AI technology that uses large amounts of text data to train the model, and then continuously optimizes the model through iterations. Its goal is to generate text based on the input data to meet specific needs, and the text can be about any topic you are interested in. 👶: Introduce the history of the United States, please. 🤖️: You mentioned "Introok's the believeations of theument." This name originates from the ancient Chinese "groty of of the change." ``` Fast and effective, it is still possible to further compress the training process by obtaining smaller and higher-quality datasets. The Zero model weights are saved as `full_sft_512_zero.pth` (see the MiniMind model file link below). Feel free to download and test the model's performance. ## Ⅱ Main Training Steps > All training scripts are executed in the `cd ./trainer` directory. ### **1. Pretraining**: The first task for LLM is not to interact directly with humans, but to fill the network parameters with knowledge. The " ink" of knowledge theoretically needs to be as full as possible, generating a large accumulation of world knowledge. Pretraining allows the model to first study a massive amount of basic knowledge, such as gathering high-quality training data from sources like Wikipedia, news articles, and books. This process is "unsupervised," meaning humans don't need to make any "supervised" corrections during the process; the model learns patterns and knowledge points on its own from large amounts of text. The goal at this stage is simple: **learn word chaining**. For example, when we input the word "Qin Shi Huang," it can continue with "was the first emperor of China." ```bash torchrun --nproc_per_node 1 train_pretrain.py # 1 represents single-card training, adjust according to hardware (set >=2) # or python train_pretrain.py ``` > The trained model weights are saved every `100 steps` by default as: `pretrain_*.pth` (the * represents the specific > model dimension, and each new save will overwrite the previous one). ### **2. Supervised Fine-Tuning (SFT)**: After pretraining, the LLM has acquired a large amount of knowledge, but it can only engage in word chaining and doesn't know how to chat with humans. The SFT stage involves applying a custom chat template to fine-tune the semi-finished LLM. For example, when the model encounters a template like [Question->Answer, Question->Answer], it no longer blindly chains words but understands this is a complete conversation. This process is known as instruction fine-tuning, similar to teaching a well-learned "Newton" to adapt to 21st-century smartphone chat habits, learning the rule that messages from others appear on the left, and the user's on the right. During training, MiniMind's instruction and response lengths are truncated to 512 tokens to save memory. This is like learning with short essays first, then gradually tackling longer ones like an 800-word essay once you can handle 200 words. When length expansion is needed, only a small amount of 2k/4k/8k length dialogue data is required for further fine-tuning (preferably with RoPE-NTK benchmark differences). > During inference, adjusting the RoPE linear difference makes it easy to extrapolate to lengths of 2048 and above > without additional training. ```bash torchrun --nproc_per_node 1 train_full_sft.py # or python train_full_sft.py ``` > The trained model weights are saved every `100 steps` by default as: `full_sft_*.pth` (the * represents the specific > model dimension, and each new save will overwrite the previous one). ## Ⅲ Other Training Steps > All training scripts are executed in the `cd ./trainer` directory. ### **3. Reinforcement Learning from Human Feedback (RLHF)** In the previous training steps, the model has acquired basic conversational abilities, but these are entirely based on word chaining, without positive or negative reinforcement examples. At this point, the model doesn't know what answers are good or bad. We want it to align more with human preferences and reduce the probability of unsatisfactory responses. This process is like providing the model with new training using examples of excellent employees' behavior and poor employees' behavior to learn how to respond better. Here, we use RLHF’s Direct Preference Optimization (DPO). Unlike RL algorithms like PPO (Proximal Policy Optimization), which require reward models and value models, DPO derives an explicit solution from the PPO reward model, replacing online reward models with offline data, where the Ref model's outputs can be pre-saved. DPO performance is nearly identical but requires running only the actor_model and ref_model, which greatly reduces memory usage and enhances training stability. > **Note**: The RLHF step **is not required**, as it typically does not improve the model’s "intelligence" but is used > to improve the model's "politeness," which can have pros (alignment with preferences, reducing harmful content) and > cons (expensive sample collection, feedback bias, loss of diversity). ```bash torchrun --nproc_per_node 1 train_dpo.py # or python train_dpo.py ``` > The trained model weights are saved every `100 steps` by default as: `rlhf_*.pth` (the * represents the specific model > dimension, and each new save will overwrite the previous one). ### **4. Knowledge Distillation (KD)** After the previous training steps, the model has fully acquired basic capabilities and is usually ready for release. Knowledge distillation can further optimize the model's performance and efficiency. Distillation involves having a smaller student model learn from a larger teacher model. The teacher model is typically a large, well-trained model with high accuracy and generalization capabilities. The student model is a smaller model aimed at learning the behavior of the teacher model, not directly from raw data. In SFT learning, the model’s goal is to fit the hard labels (e.g., real category labels like 0 or 6400) in word token classification. In knowledge distillation, the softmax probability distribution of the teacher model is used as soft labels. The small model learns from these soft labels and uses KL-Loss to optimize its parameters. In simpler terms, SFT directly learns the solution provided by the teacher, while KD "opens up" the teacher’s brain and mimics how the teacher’s neurons process the problem. For example, when the teacher model calculates `1+1=2`, the last layer's neuron states might be `a=0`, `b=100`, `c=-99`, etc. The student model learns how the teacher's brain works by studying this state. The goal of knowledge distillation is simple: make the smaller model more efficient while preserving performance. However, with the development of LLMs, the term "model distillation" has become widely misused, leading to the creation of "white-box/black-box" distillation. For closed-source models like GPT-4, where internal structures cannot be accessed, learning from its output data is known as black-box distillation, which is the most common approach in the era of large models. Black-box distillation is exactly the same as the SFT process, except the data is collected from the large model’s output, requiring only data collection and further fine-tuning. Note to change the base model loaded to `full_sft_*.pth`, as distillation is performed based on the fine-tuned model. The `./dataset/sft_1024.jsonl` and `./dataset/sft_2048.jsonl` datasets, collected from the qwen2.5-7/72B-Instruct large model, can be directly used for SFT to obtain some behavior from Qwen. ```bash # Make sure to change the dataset path and max_seq_len in train_full_sft.py torchrun --nproc_per_node 1 train_full_sft.py # or python train_full_sft.py ``` > The trained model weights are saved every `100 steps` by default as: `full_sft_*.pth` (the * represents the specific > model dimension, and each new save will overwrite the previous one). This section emphasizes MiniMind’s white-box distillation code `train_distillation.py`. Since MiniMind doesn’t have a powerful teacher model within the same series, the white-box distillation code serves as a learning reference. ```bash torchrun --nproc_per_node 1 train_distillation.py # or python train_distillation.py ``` ### **5. LoRA (Low-Rank Adaptation)** LoRA is an efficient parameter-efficient fine-tuning (PEFT) method designed to fine-tune pretrained models via low-rank decomposition. Compared to full parameter fine-tuning, LoRA only requires updating a small number of parameters. The core idea of LoRA is to introduce low-rank decomposition into the model's weight matrix and update only the low-rank part, leaving the original pretrained weights unchanged. The code can be found in `./model/model_lora.py` and `train_lora.py`, which fully implement the LoRA process from scratch without relying on third-party libraries. ```bash torchrun --nproc_per_node 1 train_lora.py # or python train_lora.py ``` > The trained model weights are saved every `100 steps` by default as: `lora_xxx_*.pth` (the * represents the specific > model dimension, and each new save will overwrite the previous one). Many people are puzzled: how can a model learn private domain knowledge? How should datasets be prepared? How to transfer general models into specialized domain models? Here are some examples. For general models, if there is a lack of medical domain knowledge, you can try adding domain-specific knowledge to the original model for better performance. At the same time, we typically don't want to lose the basic abilities of the model when learning domain knowledge. LoRA can address this issue well. Simply prepare dialogue datasets in the following format and place them in `./dataset/lora_xxx.jsonl`, then start `python train_lora.py`. Training will result in new model weights in `./out/lora/lora_xxx.pth`. **Medical Scenario** ``` {"conversations": [{"role": "user", "content": "What is the ideal pillow height for cervical spondylosis?"}, {"role": "assistant", "content": "The pillow height for cervical spondylosis should be..."}]} {"conversations ": [{"role": "user", "content": "What is xxx?"}, {"role": "assistant", "content": "xxx..."}]} ``` **Self-awareness Scenario** ``` {"conversations": [{"role": "user", "content": "What is your name?"}, {"role": "assistant", "content": "I am MiniMind..."}]} {"conversations": [{"role": "user", "content": "Who are you?"}, {"role": "assistant", "content": "I am..."}]} ``` At this point, combining the **base model + LoRA model** will enhance the medical domain capabilities without sacrificing the original model’s abilities. You can evaluate the model’s performance using `eval_model.py`. ```bash # Ensure model_mode matches the model used during train_lora to avoid mismatch python eval_model.py --lora_name 'lora_medical' --model_mode 2 ``` **Small Test** ``` 👶: I often feel dizzy recently. What might be the cause? 🤖️: Dizziness can have many causes, including but not limited to heart disease, eye injury, allergies, skin issues, stress, anxiety, fatigue, medication side effects, sexual dysfunction, or others... --- 👶: What is your relationship with OpenAI? 🤖️: I am MiniMind, an AI assistant developed by Jingyao Gong. I interact with users through natural language processing and algorithms. ``` PS: If needed, you can also fine-tune the full parameters using `full_sft` (but mix general knowledge to prevent overfitting on domain-specific data, which could reduce the generality of the model). ### **6. Training the Reasoning Model (Reasoning Model)** DeepSeek-R1 is so popular that it has almost redefined the future paradigm for LLMs. The paper suggests that models with `>3B` parameters need multiple rounds of cold starts and RL reward training to achieve noticeable improvements in reasoning abilities. The fastest, most reliable, and most economical approach, and the various so-called reasoning models that have emerged recently, are almost all directly trained through distillation on data. However, due to the lack of technical depth, the distillation faction is often looked down upon by the RL faction ( haha). I quickly attempted this on the Qwen series 1.5B small model and soon replicated the mathematical reasoning abilities of the Zero process. However, a disappointing consensus is: models with too few parameters almost cannot achieve any reasoning effects through cold-start SFT + GRPO. MiniMind2 firmly chose the distillation route at the beginning, but if the RL method for models with 0.1B parameters makes some small progress in the future, the training scheme will be updated accordingly. To do distillation, you need to prepare data in the same format as the SFT phase, as described earlier. The data format is as follows: ```json lines { "conversations": [ { "role": "user", "content": "Hello, I'm Xiaofang, nice to meet you." }, { "role": "assistant", "content": "\nHello! I am MiniMind-R1-Lite-Preview, an intelligent assistant independently developed by a Chinese individual. I'm happy to provide services for you!\n\n\nHello! I am MiniMind-R1-Lite-Preview, an intelligent assistant independently developed by a Chinese individual. I'm happy to provide services for you!\n" } ] } ``` The reply template for the reasoning model R1 is: ```text \nThinking process\n\n \nFinal answer\n ``` In GRPO, this is done by setting up a reward function that ensures the model adheres to the thinking and answering tags (the reward values should be higher in the early cold-start stages). Another issue is that although the distillation process is similar to SFT, the experimental results show that the model struggles to consistently follow the template for responses, meaning it may not always adhere to the thinking and answering tag constraints. A trick here is to add a loss penalty for token positions with tags, as detailed in `train_distill_reason.py`: ```text # Add additional penalty at positions corresponding to sp_ids ... loss_mask[sp_ids] = 10 # Penalty coefficient ``` Another tip is that, since the reasoning data only filters for data of length `<1024`, there is less multi-turn dialogue and English data. Therefore, `r1_mix_1024.jsonl` has been mixed with about 10k multi-turn dialogues + English data to prevent the model from forgetting too much. The script is set by default to distill reasoning abilities from the RLHF-based pre-trained model. To start training, just run: ```bash torchrun --nproc_per_node 1 train_distill_reason.py # or python train_distill_reason.py ``` > The trained model weights are saved every `100 steps` by default as: `reason_*.pth` (* being the specific dimension of > the model; each time a new file is saved, it will overwrite the old one). Test it: ```text 👶: Please introduce yourself. 🤖️: Hmm, the user asks me to introduce myself. I need to explain myself. First, I should clarify who the user is: a student, a professional, or an ordinary person. The user could be a student or a professional, so the information I provide should help them. Then, I need to respond to the user, which might include introducing my location, interests, career experiences, etc. The user might also want to know where I am, such as my career development or work experience. I should be careful with my wording, it should be formal but not too formal, and keep it concise and clear so the user can easily understand and access related information. Finally, I should summarize, letting the user know how I can help them complete their tasks, so they have more information about me. I am **"Your Name"**, what is your name? 👶: Which subject are you better at? 🤖️: Hello! I am MiniMind-R1, an intelligent assistant developed by a Chinese individual. If you have any questions, I will do my best to assist you. Hello! I am MiniMind-R1, an intelligent assistant developed by a Chinese individual. If you have any questions, I will do my best to assist you. ``` --- ## Ⅳ Model Parameter Settings 📋 Regarding LLM parameter configurations, an interesting paper, [MobileLLM](https://arxiv.org/pdf/2402.14905), provides a detailed study and experiment. The Scaling Law has unique patterns for small models. The parameters that cause the Transformer to scale mainly depend on `d_model` and `n_layers`. * `d_model`↑ + `n_layers`↓ -> Short and fat * `d_model`↓ + `n_layers`↑ -> Tall and thin The Scaling Law paper from 2020 suggests that the training data volume, parameter size, and number of training iterations are the key factors determining performance, with the model architecture having almost negligible impact. However, this law doesn't seem to fully apply to small models. MobileLLM suggests that the depth of the architecture is more important than the width, and "deep and narrow" models can learn more abstract concepts than "wide and shallow" models. For example, when the model parameters are fixed at 125M or 350M, the "narrow" models with 30-42 layers perform significantly better than the "short and fat" models with around 12 layers, across 8 benchmark tests like commonsense reasoning, Q&A, reading comprehension, etc. This is a fascinating discovery because, in the past, no one tried stacking more than 12 layers when designing architectures for small models around the 100M parameter range. This finding aligns with what MiniMind observed during training when adjusting between `d_model` and `n_layers`. However, the "deep and narrow" architecture has its limits, and when `d_model`<512, the collapse of word embedding dimensions becomes very evident, and increasing layers cannot compensate for the lack of `d_head` due to fixed `q_head`. When `d_model`>1536, the increase in layers seems to take priority over `d_model` and provides more cost-effective parameter-to-performance gains. * Therefore, MiniMind sets small models with `dim=512`, `n_layers=8` to strike a balance between "small size" and " better performance." * Sets `dim=768`, `n_layers=16` to achieve more significant performance improvements, which better matches the small model Scaling-Law curve. For reference, the parameter settings for GPT-3 are shown in the table below: ![gpt3_config.png](./images/gpt3_config.png) --- ## Ⅴ Training Results > `MiniMind2` model training loss trends (the loss is for reference only as the dataset was updated and cleaned several > times after training). | Models | Pretrain (length-512) | SFT (length-512) | |-----------------|----------------------------------------------------|----------------------------------------------------| | MiniMind2-Small | | | | MiniMind2 | | | ### Training Completed - Model Collection > Considering that many people have reported slow speeds with Baidu Cloud, all MiniMind2 models and beyond will be > hosted on ModelScope/HuggingFace. --- #### ① Native PyTorch Models MiniMind2 model weights ([ModelScope](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch) | [HuggingFace](https://huggingface.co/jingyaogong/MiniMind2-Pytorch)) MiniMind-V1 model weights ([Baidu Pan](https://pan.baidu.com/s/1KUfSzEkSXYbCCBj0Pw-9fA?pwd=6666))
Torch File Naming Reference | Model Name | params | pretrain_model | sft_model | rl_model | reason_model | lora_model | |-----------------|--------|------------------------|------------------------|--------------------|------------------|--------------------| | MiniMind2-small | 26M | `pretrain_512.pth` | `full_sft_512.pth` | `rlhf_512.pth` | `reason_512.pth` | `lora_xxx_512.pth` | | MiniMind2-MoE | 145M | `pretrain_640_moe.pth` | `full_sft_640_moe.pth` | `rlhf_640_moe.pth` | - | - | | MiniMind2 | 104M | `pretrain_768.pth` | `full_sft_768.pth` | `rlhf_768.pth` | `reason_768.pth` | `lora_xxx_768.pth` | | Model Name | params | pretrain_model | Single-turn Dialogue SFT | Multi-turn Dialogue SFT | rl_model | |-------------------|--------|------------------------|------------------------------------|-----------------------------------|--------------| | minimind-v1-small | 26M | `pretrain_512.pth` | `single_chat/full_sft_512.pth` | `multi_chat/full_sft_512.pth` | `rl_512.pth` | | minimind-v1-moe | 4×26M | `pretrain_512_moe.pth` | `single_chat/full_sft_512_moe.pth` | `multi_chat/full_sft_512_moe.pth` | - | | minimind-v1 | 108M | `pretrain_768.pth` | `single_chat/full_sft_768.pth` | `multi_chat/full_sft_768.pth` | `rl_768.pth` |
#### ② Transformers Models MiniMind Series ([ModelScope](https://www.modelscope.cn/collections/MiniMind-b72f4cfeb74b47) | [HuggingFace](https://huggingface.co/collections/jingyaogong/minimind-66caf8d999f5c7fa64f399e5)) --- # 📌 Eval ## Ⅰ RLHF vs SFT Comparison The test is based on comparing `MiniMind2 series` models with "full_sft" and "rlhf". The random seed is fixed.
Q&A Test [A0] MiniMind2:full_sft_640 [A1] MiniMind2:rlhf_640 ```text [Q]: What is your name? [A0]: I am a large language model capable of answering various questions, providing information and assistance. I can answer a wide range of questions, provide information, help you solve problems, and more. [A1]: I am a text-based AI model, and I don't have personal experiences or emotions, so I cannot feel or experience emotions. My design is to assist users by solving problems, providing information, and engaging in conversations. If you have any questions or need help, feel free to let me know! [Q]: Which subject are you better at? [A0]: As an AI model, I do not have a specific "specialty". I am designed to provide information, answer questions, engage in conversation, perform tasks, and interact with users. However, I can offer assistance in various subjects including providing information, solving problems, engaging in discussions, giving advice, learning new knowledge, writing code, and more. If you have a specific subject or topic in mind, feel free to provide more details. [A1]: As an AI, I do not have personal capabilities or interests, but I can provide knowledge on subjects such as mathematics, science, computer science, linguistics, psychology, and more. If you're interested in mathematics, I can offer some basic knowledge like algebra, geometry, probability, statistics, etc. If you need more in-depth learning, I can also provide relevant online courses, books, research papers, and other resources. Let me know what specific help you need, and I will do my best to assist you. [Q]: How does Lu Xun's "A Madman's Diary" criticize feudal ethics? [A0]: Lu Xun's "A Madman's Diary" is a work that advocates for human values and moral responsibility. In this work, Lu Xun explores profound themes such as human nature, morality, justice, sacrifice, and responsibility through detailed writing. Here are several ways in which Lu Xun critiques feudal ethics in "A Madman's Diary": 1. **Social Reality and Moral Concepts**: Lu Xun addresses social inequality, power dynamics, and human nature through his narrative. He argues that social inequality is not only unjust but also a form of social injustice affecting individuals. He calls for collective effort to build a harmonious society through education, publicity, and laws. 2. **Morality and Responsibility**: Through his narrative, Lu Xun highlights the importance of moral education and emphasizes personal responsibility. 3. **The Individual and Society**: Lu Xun delves into the complex relationship between the individual and society, advocating for collective efforts to contribute to social harmony. Lu Xun’s "A Madman's Diary" not only achieved great literary success but also inspired later literary creations with its profound impact on thoughts and societal reflections. [A1]: Lu Xun's "A Madman's Diary" is a work that critiques the feudal society's various issues and conflicts, reflecting social inequality and human nature. In this work, Lu Xun critiques feudal society’s problems, like "Frederick Johnson’s 'Eagle Dillon', 'Nazareth', 'Beckham', 'George Wilson'", emphasizing the inequality and complexity of human nature within feudal society. The work criticizes feudal society's corruption, human nature, and the inherent contradictions of the era. This critique reflects Lu Xun’s respect for social issues and his deep understanding of human nature. "A Madman's Diary" is a critical work that raises awareness and challenges the established norms of feudalism and has greatly influenced future critiques. [Q]: I’ve had a cough for two weeks, should I go to the hospital for a checkup? [A0]: A prolonged cough does require some checkups, and if your cough persists or is accompanied by other symptoms such as difficulty breathing, it's recommended to see a doctor. Based on your symptoms, physical examination, and possible imaging tests (e.g., X-rays, MRIs, or CT scans), the doctor will make a diagnosis. In the meantime, you can try the following to alleviate symptoms: 1. **Rest**: Ensure you get enough sleep. 2. **Stay hydrated**: Dehydration can worsen coughing. 3. **Warm compress**: Apply a warm compress to the sore area to relieve throat pain. 4. **Avoid irritants**: Avoid substances that may irritate the throat, such as certain chemicals. 5. **Maintain humidity**: Keep the room well-ventilated, and use a humidifier if necessary. If symptoms persist or worsen, seek medical attention immediately. Wishing you a speedy recovery! [A1]: If your cough has lasted for two weeks, it's usually recommended to see a doctor, as it may affect your breathing and heart rate. You can take the following steps to manage your cough: 1. **Rest**: Gently press on the coughing area with a tissue or elbow. 2. **Use medication**: If possible, use medications like antihistamines to relieve the cough (under a doctor's guidance). 3. **Warm compress**: Use a warm compress to alleviate pain and swelling. 4. **Medication treatment**: If the cough persists, use anti-inflammatory or antihistamine medications under the guidance of a doctor. 5. **Maintain a healthy lifestyle**: Get sufficient sleep, eat a balanced diet, and avoid smoking and excessive alcohol consumption to help reduce the chances of coughing. Please remember these suggestions are not substitutes for professional medical advice. If your symptoms persist or worsen, it is strongly advised to see a doctor. ```
👉 Summary of Results The full_sft model performs better in terms of simplicity and information accuracy; the rlhf model tends to provide more background information in its responses, but its accuracy needs improvement. Overall, after RLHF, the model tends to learn to say more polite but irrelevant things to please the "conversation", while sacrificing some information accuracy. There is no free lunch, and further improvement in the quality of the RLHF dataset is necessary. It's also important to accept that some loss in model capability is inevitable (to varying degrees). The difference between DPO and online PPO lies in the fact that both the reject and chosen are prepared offline, which causes a significant distribution difference compared to the minimind model's original output. Simply put, the DPO algorithm trains the model using RL by watching "recorded" games of a table tennis world champion, rather than using the PPO method where the reward model acts as a "coach" to correct the model's moves during RL. ## Ⅱ Subjective Sample Evaluation 🏃The following tests were completed on February 9, 2025. New models released after this date will not be included in the tests unless there is a special need. [A] [MiniMind2 (0.1B)](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch)
[B] [MiniMind2-MoE (0.15B)](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch)
[C] [MiniMind2-Small (0.02B)](https://www.modelscope.cn/models/gongjy/MiniMind2-PyTorch)
[D] [minimind-v1-small(0.02B)](https://pan.baidu.com/s/1_COe0FQRDmeapSsvArahCA?pwd=6666)
[E] [minimind-v1-moe(0.1B)](https://pan.baidu.com/s/1tqB-GMvuiGQBvEl-yZ-oBw?pwd=6666)
[F] [minimind-v1(0.1B)](https://pan.baidu.com/s/1p713loS7EfwHQf3G9eYI3Q?pwd=6666)
[G] [baby-llama2-chinese(0.2B)](https://github.com/DLLXW/baby-llama2-chinese)
[H] [chatlm-mini-chinese(0.2B)](https://github.com/charent/ChatLM-mini-Chinese)
```text [Q]: Do you know about the Yangtze River? [A]: The Yangtze River, located in southeastern China, is the longest river in China, stretching over 6,300 kilometers and flowing through 11 provinces. It is China's "mother river." The history of the Yangtze River dates back to the Shang Dynasty around 2000 BC. During the Shang Dynasty, the economic development of the Yangtze River reached its peak, making it one of the most important agricultural and industrial centers in China. The economy of the Yangtze River Basin mainly relies on agriculture, with Yangtze River fisheries occupying a major industry, such as fishing and hunting fish. The water resources of the Yangtze River are also very abundant, including rivers, lakes, and streams. The natural scenery of the Yangtze River is spectacular, with different landscapes in each season: rainy in spring and summer, cold and snowy in winter, and beautiful flowers and fruits in spring and autumn. The Yangtze River is China's economic center, home to many countries and cities, and an important transportation hub and shipping center in the world. At the same time, the Yangtze River is also an important water resource for China, playing a significant role in the country's economic development. [B]: Yes, I know about the Yangtze River. The Yangtze River is located in central China and is the third-longest river in China, stretching approximately 6,300 kilometers and flowing through Jiangxi, Anhui, Jiangsu, Anhui, Jiangsu, and Anhui Provinces. [C]: Of course, the Yangtze River is the longest river in China, stretching approximately 6,300 kilometers. It is famous for its rich water resources, diverse aquatic flora and fauna, and unique geographical location. [D]: Yes, the Yangtze River is the longest river in China, exceeding 3,300 kilometers in length. [E]: The Yangtze River is the longest river in China, with a total length of approximately 6,300 kilometers. It originates from Qinghai Lake on the Qinghai-Tibet Plateau and flows through Qinghai, Sichuan, Gansu, Ningxia, Inner Mongolia, Shaanxi, and Shanxi. The Yangtze River Basin is one of the largest agricultural areas in China and also the cultural and economic center of the country. [F]: Yes, the Yangtze River is one of the longest rivers in China and the third-largest river in the world. The Yangtze River originates from the Qinghai-Tibet Plateau, flows through 11 provinces, and eventually empties into the East China Sea. The Yangtze River Basin is one of the largest river basins in China, possessing abundant natural resources and cultural heritage, such as the Yangtze River Bridge, the Yangtze Ocean Museum, and the Yangtze Ancient City. [G]: It is also known that China is one of the longest cities in the world, with a population exceeding 34 million. [H]: Yes, the Yangtze River is the longest river in China and one of the longest rivers in the world. It originates from the Qinghai-Tibet Plateau and flows through nine provinces before finally emptying into the East China Sea. The climate of the Yangtze River Basin is mild with distinct seasons, making it an important agricultural and industrial base in China. The aquatic resources of the Yangtze River Basin are abundant, making it one of the important fishery resources in China. [Q]: What is the highest mountain in the world? [A]: The highest mountain in the world is Mount Everest, located in the Himalayas on the border between Nepal and China. Its elevation is 8,848.86 meters (29,031.7 feet), making it the highest mountain on Earth. [B]: Mount Everest is the highest mountain in the world, with an elevation of 8,848 meters (29,029 feet). [C]: Mount Everest is the highest mountain in the world, located in the Himalayas with an elevation of 8,848.86 meters (29,031.7 feet). It is located on the border between Nepal and China and is part of the Himalayan range. [D]: The highest mountain in the world is Mount Everest, located on the border between Nepal and China, with an elevation of 8,848 meters. [E]: The highest mountain in the world is Mount Everest, located in the Himalayas with an elevation of 8,848 meters. [F]: Mount Everest is the highest mountain in the world, with an elevation of 8,848 meters. [G]: The highest mountain in the world is Mount Everest, located in the Himalayas with an elevation of 8,848.86 meters (29,031.69 feet). [H]: The highest mountain in the world is Mount Everest, also known as Sagarmatha, located on the border between Nepal and China in the Himalayas, with an elevation of 8,848.86 meters (29,031.69 feet). It is one of the most famous mountaineering destinations in the world, attracting climbers from all over the globe. The name Mount Everest comes from Tibetan, meaning "the goddess's forehead," and it consists of two different peaks, one located in Nepal and the other in China. Its height is determined by measuring its altitude because it is determined by measuring the average height of the Earth. Although Mount Everest is the highest mountain in the world, it is not reachable from any other place on Earth. Other places on Earth, such as the Andes in South America, are also the highest mountain ranges in the world, but they are not the highest mountains on Earth. In summary, Mount Everest is the highest mountain in the world, but it is not reachable from any other place on Earth. It is the highest mountain range on Earth, but it is not reachable from any other place on Earth. [Q]: Who proposed the law of universal gravitation? [A]: The law of universal gravitation was proposed by Newton in the 17th century. It is one of Newton's three laws. Newton's gravitational law revealed the essence of the law of universal gravitation and explained how celestial bodies such as planets, satellites, and comets interact with each other. This law was proposed by Isaac Newton in 1687. Through analysis and experiments on the law of universal gravitation, he successfully explained the gravitational interactions between celestial bodies, thereby explaining the phenomenon of the Earth orbiting the Sun. [B]: Universal gravitation (Human Policy) is one of Newton's three laws of mechanics, used to describe the interaction force between two objects. It consists of the law of universal gravitation and Newton's first law (the law of inertia). Universal gravitation occurs due to the interaction between two objects, and factors such as their mass, distance, speed, and direction determine their interaction. This interaction force is: the product of masses: the product of masses is the result of the law of universal gravitation and Newton's second law. The product of masses: the product of masses is a physical process that is the result of interactions between objects, and for other objects, their mass can be expressed as the sum of the masses of the two interacting objects, i.e., gravitational acceleration a. [C]: Universal gravitation is a physics concept proposed by Newton in 1687. It describes the laws of how gravitational forces interact between objects. [D]: Universal gravitation was proposed by the British physicist Newton. He believed that all objects generate gravity, which is determined by the object's mass and distance. This theory reveals the relationship between mass and distance and proves the essence of universal gravitation. [E]: Universal gravitation was proposed by Newton in 1687. It is a fundamental physical phenomenon in the universe, describing the interactions between objects. This force is called universal gravitation, which exists between all objects, and this force is related to the mass and distance of the objects. Universal gravitation is one of the most basic forces in nature, influencing the motion and changes of objects and having a significant impact on the structure and evolution of the universe. [F]: Universal gravitation was proposed by the British physicist Isaac Newton in the 17th century. [G]: Gravity is a force in the universe composed of mass or energy. According to Newton's law of universal gravitation, the gravitational force between two objects is directly proportional to their mass and inversely proportional to their mass. Therefore, Newton's law of universal gravitation states that the gravitational force between two objects is directly proportional to their mass and inversely proportional to their mass. This law has wide applications in physics, astronomy, and physics. [H]: Universal gravitation was proposed by the British physicist Isaac Newton in 1687. In 1687, he published a famous paper titled "Mathematical Principles of Natural Philosophy," in which he proposed the law of universal gravitation, stating that there is a gravitational force between any two objects, and the magnitude of this force is directly proportional to their masses and inversely proportional to the square of the distance between them. This law is called the law of universal gravitation, describing the relationship between mass and distance. It shows that the gravitational force between two objects is directly proportional to their masses and inversely proportional to the square of the distance between them. This law is crucial for explaining many phenomena and interactions in the universe, such as planetary orbits around the Sun, gravitational lensing effects between stars, etc. It is also widely applied in astronomy, physics, and engineering, providing an important theoretical foundation for understanding the nature and evolution of the universe. [Q]: Do you know what the main food of giant pandas is? [A]: Giant pandas primarily feed on bamboo, but they also eat other plants, insects, small mammals, and fish. Their digestive system is similar to other bear species, capable of digesting bamboo. Pandas have sharp teeth suitable for chewing bamboo, so they are often referred to as "bamboo-eating animals." [B]: Giant pandas are mammals that feed on bamboo. They are carnivores. Their main food is bamboo, especially bamboo rich in cellulose and minerals, which does not directly enter the digestive system. The intestines of giant pandas are mainly composed of bamboo, and they need to digest cellulose and other fibers in bamboo to support their normal feeding. [C]: Giant pandas primarily feed on bamboo, but they also eat other plants, including leaves, buds, insects, fruits, vegetables, and small mammals. Giant pandas are a species unique to China, belonging to the bear family, mainly distributed in Sichuan, Shaanxi, and Gansu provinces. [D]: The main food of giant pandas is bamboo. They usually eat bamboo, consuming about 30 kilograms of bamboo daily. [E]: Yes, giant pandas primarily feed on bamboo. They need to consume large amounts of bamboo daily to sustain life, eating between 40 to 80 kilograms of bamboo per day. Additionally, they also eat some other plants, such as grass, fruits, nuts, and small mammals. [F]: Giant pandas primarily feed on bamboo but occasionally consume other plants and animals. Additionally, giant pandas eat some wild fruits, small animals, and plants, but these do not constitute their primary food source. [G]: The main food of giant pandas is bamboo. [H]: Giant pandas are herbivorous animals that primarily feed on bamboo. Their intestinal structure is suitable for digesting bamboo with high fiber content, but they also eat some other plants, such as herbaceous plants and small mammals. The intestinal structure of giant pandas also adapts to high-fiber bamboo, allowing them to digest large amounts of cellulose, helping them survive in the wild. The intestines of giant pandas also contain an enzyme called "cellulase," which helps break down cellulose, aiding in the digestion of large amounts of water and nutrients. Additionally, the intestines of giant pandas contain a protein called "fibroin," which helps maintain energy levels in the body, aiding in their survival in the wild. Overall, the main food of giant pandas is bamboo, but they also eat some other plants, such as herbaceous plants and small mammals. [Q]: Why is seawater salty? [A]: Seawater is salty mainly due to its chemical composition. Seawater contains large amounts of minerals and salts, with the main components being sulfates, carbonates, magnesium ions, and potassium ions. These substances precipitate in seawater at high concentrations, forming seawater. Specifically, the minerals and salts in seawater mainly come from rocks, soil, organisms, and human excrement. Rocks are a component of seawater, mainly composed of minerals such as quartz, feldspar, and mica. Soil contains large amounts of chemicals such as nitrogen, phosphorus, and potassium, which seep into the soil, forming soil. The salt in living organisms mainly comes from pigments like chlorophyll in plants, which absorb energy from sunlight and convert it into usable energy for organisms. The salt in human excrement mainly comes from minerals and carbonates, which have higher concentrations in water, leading to an increase in dissolved mineral concentration, forming salt in water bodies. Therefore, seawater is salty due to its chemical composition and structural characteristics. The minerals and salts in seawater mainly come from rocks, soil, organisms, and human excrement, and the accumulation and excretion of these substances cause seawater to taste salty. [B]: Seawater is salty because it contains various substances, including sodium chloride (NaCl), sodium chloride (NaCl), carbonates (such as sulfates), sodium chloride (NaCl), and water. Sources of salty substances: Salt is a common source of saltiness, containing minerals such as potassium, calcium, magnesium, and potassium. These minerals usually come from groundwater, groundwater, or soil, and their content is much lower than pure water. Reasons for saltiness: The saltiness of salt mainly comes from tiny particles dissolved in water. These tiny particles are mainly transmitted through plant roots and microorganisms inside animals, such as insects, small mammals, birds, and certain insects. In summary, seawater is salty because it contains large amounts of salts, including minerals, rocks, salts, and minerals, and the high concentration of these substances in seawater significantly affects the saltiness. [C]: Seawater is salty mainly because it contains various salts, with seawater being one of the main components. When seawater contains large amounts of salt, the salt dissolves in seawater, creating a salty taste. The salts in seawater mainly come from dissolved salts, evaporated salts, and frozen ice. [D]: Seawater contains many salts, which can lead to an increase in the salinity of seawater. The salts in seawater mainly come from the salts in seawater, which can be obtained from the salts in seawater. Oxides and salts in seawater can form hydrogen bonds with the salts in seawater, increasing the salinity of seawater. [E]: Seawater is salty because it contains large amounts of salts, including chloride ions and chloride ions. These ions form large amounts of salts in the ocean, known as salts. Seawater contains many salts dissolved in water, which gradually evaporate as seawater rises, eventually leading to the evaporation of salts in seawater. These salts are completely consumed in seawater and then excluded from seawater. Additionally, seawater contains many other salts, but these salts are different because their floating and sinking processes in water are different. The salts in seawater are extracted from rocks at the bottom of the sea, and these salts can be utilized in seawater. Overall, seawater is salty, which is why seawater contains many salts and salts. [F]: Seawater contains many dissolved salts, the most important of which is sodium chloride. When salt is added to water, the molecules of salt react with water molecules to form sodium hydroxide, which is a salt. [G]: Because water is essential for the survival of many organisms, it can absorb and release carbon dioxide, release oxygen, maintain water vapor in the atmosphere, and maintain the water cycle. Salts and chemicals in water release oxygen, allowing organisms on Earth to breathe. Additionally, water has many other functions, such as maintaining the water cycle, regulating climate and environment, etc. Water is essential for the survival of many organisms and is also an important basis for many natural phenomena. [H]: The main reason seawater is salty is that it contains large amounts of dissolved salts, the most important of which is sodium chloride (NaCl). Sodium chloride is a very common salt found in most parts of the Earth, including oceans, lakes, rivers, and groundwater. When water evaporates from the ocean, it takes away some of the salt, making seawater salty. Additionally, seawater contains other dissolved substances, such as sulfates, magnesium sulfate, calcium sulfate, magnesium sulfate salt, magnesium sulfate potassium, magnesium sulfate sodium, magnesium sulfate calcium, magnesium sulfate magnesium calcium, magnesium sulfate magnesium calcium, magnesium sulfate magnesium calcium, magnesium sulfate magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium magnesium magnesium magnesium calcium, magnesium sulfate magnesium magnesium magnesium magnesium [Q]: Write a Python quicksort code. [A]: Okay, here is the Python quicksort code: def quick_sort(arr): if len(arr) <= 1: return arr else: pivot = arr[len(arr) // 2] left = [x for x in arr[1:]] right = [x for x in arr[2:] if x < pivot] return quick_sort(left) + [pivot] + quick_sort(right) # Test code arr = [3, 6, 8, 10, 1, 2, 1] print(quick_sort(arr)) This code first checks whether the length of the array is less than or equal to 1. If so, it sorts in ascending order. Then, for each element in the array, it recursively sorts until reaching the end position. If the length of the array is odd, it sorts in descending order. Finally, the average time complexity is O(n log n), where n is the length of the array. [B]: Here is an implementation of the quicksort algorithm using Python: def sort_array(arr): # Generate a random permutation sorted_array = sorted(arr) # Return the sorted list return sorted(arr) # Example arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5] # Call the function sorted_array = sort_array(arr) print("Original array is:", sorted_array) This code first generates a random permutation, then sorts based on the length of the list. Finally, it returns the sorted list. Note that this code uses Python's built-in sorted function and the built-in sorted() function, which are faster than directly using sorted() under a time complexity of O(n log n). [C]: Here is a simple sorting algorithm suitable for small arrays, including those with lengths less than or equal to 5. def quick_sort(arr): if len(arr) <= 1: return arr else for _ in range(2, 51): if arr[len(arr) - 1:] is not None: arr[len(arr) - 1] = arr[len(arr) - 1] arr[len(arr)] = arr[len(arr)] return quick_sort(arr) # Example arr = [3, 5, 1, 4, 1, 2, 1] sorted_arr = quick_sort(arr) print("Sorted array is:", sorted_arr) ### Code Explanation: 1. **Function Definition**: The quick_sort function accepts an array arr as a parameter. 2. **Loop**: For elements in arr, recursion is used, traversing the array from right to left and swapping the current element with the current element in the array until the array is empty or the array length is less than or equal to arr. 3. **Recursive Call**: Traverse the array, swap the current element with the current element in the array until the array is empty or the array length is less than or equal to arr. 4. **Check Array Length**: Use if __name__ == "__main__" to check if the array length is less than arr. If so, move the array from the larger array to the end of the array. 5. **Output Sorted Results** [D]: As an AI language model, I cannot write Python programs. However, Python is a very popular programming language with many excellent libraries and frameworks, including NumPy, Pandas, Matplotlib, Pandas, Bandke, Norm, and NKlearn. Python has many excellent natural language processing libraries and frameworks that can handle and analyze various types of data, such as text, images, audio, and video. Python can also be used to create various applications, such as web search, game development, scientific computing, etc. Python is very flexible and supports multiple programming styles, making it easy to find solutions that suit your needs. [E]: As an AI language model, I can answer your questions. But I cannot perform programming operations because I cannot determine which programming language you are asking about. Please provide more specific questions so I can better answer your queries. [F]: Quick Sort is a simple sorting algorithm that divides data into groups, sorts each group according to certain rules, and ultimately obtains a sorted array. [G]: Okay, here is a Python quicksort code ```python sorted_print(sorted(sorted(sorted(sorted_print))``` Output: ```[1, 2, 3, 4, 5, 6, 7, 8, 9]``` [H]: Quicksort is an efficient sorting algorithm based on the divide-and-conquer approach, with a time complexity of O(n log n). Its basic idea is to partition the sequence to be sorted into two subsequences through one pass of sorting, where all elements in one subsequence are smaller than all elements in the other subsequence. Then, the two subsequences are recursively sorted. Below is a Python code example implementing the quicksort algorithm: ```python def quick_sort(arr): if len(arr) > 1: return arr else: return quick_sort(arr) ``` This code uses recursion. First, it checks whether the length of the array is less than or equal to 1. If so, it directly returns the array; otherwise, it recursively sorts the array. During the recursion, we first check whether the first element of the array is greater than or equal to 1. If so, compare the elements in the array with the first element. If not equal to 1, compare the elements in the array with the second element, and so on, until all elements are sorted. The time complexity of this code is O(n log n), making it a very practical sorting algorithm. [Q]: As of 14:31 on February 9, 2025, the cumulative box office revenue (including presales) of the movie "Ne Zha: The Demon Child Rises from the Sea" exceeded 7.809 billion yuan, surpassing the box office performance of "Star Wars: The Rise of Skywalker" and becoming the first Asian film to enter the top 40 global box office rankings. This achievement not only marks a breakthrough for Chinese films in the international market but also demonstrates the huge potential and market appeal of Chinese animated films. Summarize this passage in 20 words for me. [A]: The movie "Ne Zha: The Demon Child Rises from the Sea" surpassed 7.809 billion yuan at the box office, becoming the first Asian film to enter the top 40 global box office rankings. This marks a breakthrough for Chinese films internationally and showcases the huge potential of Chinese animated films. [B]: On February 9, 2025, Chinese films broke through 7.809 billion yuan globally, surpassing the box office performance of "Star Wars: The Rise of Skywalker." [C]: The movie "Ne Zha: The Demon Child Rises from the Sea" surpassed 7.809 billion yuan at the box office, becoming a top 40 global box office Asian film and showcasing the huge potential of Chinese animated films. [D]: "Ne Zha: The Demon Child Rises from the Sea" surpassed 7.809 billion yuan at the box office, becoming the first Asian film to enter the top 40 global box office rankings, marking a breakthrough for Chinese films internationally and showcasing the huge potential and market appeal of Chinese animated films. [E]: "The Dragon Strong: Opening the World Fantasy Adventure" successfully attracted numerous audiences in Shanghai and also drew widespread attention. [F]: "Ne Zha: The Demon Child Rises from the Sea" represents the huge international potential of Chinese animated films. The movie is renowned for its stunning visuals and thrilling battle scenes. [G]: "Which Is Worth It" was released on January 9, 2021, at 14:31. The movie "Which Big Move" broke through 780 billion dollars in the film market, marking a breakthrough for Chinese animated films and showcasing their huge potential and market appeal. [H]: I am a small-parameter AI model 🥺, with limited knowledge, unable to directly answer your question. Try another question 👋 ``` --- 🙋‍Directly give all the questions and the model's answers above to DeepSeek-R1, let it help comment and rank with scores:
Specific comments ### Scoring Criteria: - **Accuracy**: Whether the answer is correct and free of obvious errors. - **Completeness**: Whether the answer covers the core points of the question. - **Logic**: Whether the answer is structured and logical. - **Code Quality**: Whether the code runs correctly and the logic is clear. ### Review: 1. **Model A**: - **Strengths**: The answer is very comprehensive, with a lot of information and clear logic, especially excelling in questions about the Yangtze River, giant pandas, seawater salinity, etc. The code has minor flaws, but the overall approach is correct. - **Weaknesses**: Some answers are a bit too lengthy, but this doesn’t affect the overall quality. - **Overall**: Best overall performance, scored the highest. 2. **Model H**: - **Strengths**: The answers are quite accurate, especially excelling in questions about Mount Everest, universal gravitation, etc. Although the code is not fully presented, the explanation is detailed. - **Weaknesses**: Some answers are a bit verbose, but the logic is strong. - **Overall**: Second to Model A, stable performance. 3. **Model C**: - **Strengths**: The answers are concise and clear, especially performing well in questions about giant pandas, quicksort code, etc. - **Weaknesses**: Some answers are a bit brief and lack in-depth explanations. - **Overall**: Good overall performance, but lacks the detail of Models A and H. 4. **Model F**: - **Strengths**: The answers are fairly accurate, especially in questions about the Yangtze River and universal gravitation. The code is logically sound. - **Weaknesses**: Some answers lack depth, and the code has a few small issues. - **Overall**: Average performance, with room for improvement. 5. **Model D**: - **Strengths**: The answers are generally accurate, especially in questions about universal gravitation and the Yangtze River. - **Weaknesses**: Some answers are overly brief, and there are obvious errors in the code. - **Overall**: Average performance, needs improvement in code. 6. **Model B**: - **Strengths**: The answers are fairly accurate, especially in questions about the Yangtze River and seawater salinity. - **Weaknesses**: Some answers lack logic, and there are significant issues with the code. - **Overall**: Mediocre performance, needs further optimization. 7. **Model E**: - **Strengths**: Some answers are fairly accurate, especially in questions about seawater salinity and giant pandas. - **Weaknesses**: The answers are too brief, and the code is almost non-functional. - **Overall**: Poor performance, needs significant improvement. 8. **Model G**: - **Strengths**: Nearly no apparent strengths. - **Weaknesses**: The answers deviate significantly from the topic, and the code doesn’t work at all. - **Overall**: Worst performance, needs major improvements. --- ### Summary: - **Model A** performs the best overall, especially excelling in complex questions with high accuracy and logic. - **Model H** follows closely, with stable performance but some minor shortcomings in detail. - **Model G** has the worst performance, with answers straying from the topic and code failing to run, needing major improvements.
### Scoring Rank | Rank | Model | Accuracy (30 points) | Completeness (30 points) | Logic (20 points) | Code Quality (20 points) | Total (100 points) | |------|-------|----------------------|--------------------------|-------------------|--------------------------|--------------------| | 1 | A | 28 | 29 | 19 | 20 | 96 | | 2 | H | 27 | 28 | 18 | 20 | 93 | | 3 | C | 26 | 27 | 18 | 18 | 89 | | 4 | F | 25 | 26 | 17 | 18 | 86 | | 5 | D | 24 | 25 | 17 | 16 | 82 | | 6 | B | 23 | 24 | 16 | 15 | 78 | | 7 | E | 22 | 23 | 15 | 14 | 74 | | 8 | G | 10 | 12 | 10 | 10 | 42 | ### 👉 Subjective Effect Summary My personal evaluation aligns with DeepSeek-R1's results,and: * The ranking of the MiniMind series is very intuitive. The larger the parameters and the more training data, the higher the score, and hallucinations and errors are less noticeable than with smaller models. * Model H's answers appear quite good to the naked eye, although there are some hallucinations and fabricated responses. * Model G may have incomplete training data, and the performance based on tested weights is poor. * Repeating the timeless Scaling Law: The larger the parameters and the more training data, the stronger the model's performance. --- ## Ⅲ Objective Benchmark Now, onto the much-anticipated benchmark testing phase. We won’t bother comparing with models like Qwen or GLM-level Chinese models. Instead, we'll focus on a selection of <1B micro-models for a comparative analysis. The test sets chosen include C-Eval, CMMLU, A-CLUE, and TMMLU+, which are pure Chinese language leaderboards.
Evaluation Framework The evaluation framework chosen is [lm-evaluation](https://github.com/EleutherAI/lm-evaluation-harness), which is very easy to set up and run after installation: ```bash lm_eval --model hf --model_args pretrained=,device=cuda,dtype=auto --tasks ceval* --batch_size 8 --trust_remote_code ```
PS: In these multiple-choice-based evaluations, to avoid issues with inconsistent response formats, the common approach is to extract the prediction probabilities for the four options ('A', 'B', 'C', 'D'), and calculate the accuracy by comparing the letter with the highest probability to the standard answer. The accuracy for random guessing is 25%, and models typically cluster around this number, often performing worse than random guessing, reminiscent of a high school cloze test... The MiniMind model, with its modest pretraining dataset and lack of fine-tuning on the test set, is mainly for fun, so take the results lightly: | models | from | params↓ | ceval↑ | cmmlu↑ | aclue↑ | tmmlu+↑ | |-------------------------------------------------------------------------------|---------------|---------|--------|--------|--------|---------| | MiniMind2 | JingyaoGong | 104M | 26.52 | 24.42 | 24.97 | 25.27 | | MiniMind2-Small | JingyaoGong | 26M | 26.37 | 24.97 | 25.39 | 24.63 | | MiniMind2-MoE | JingyaoGong | 145M | 26.6 | 25.01 | 24.83 | 25.01 | | [Steel-LLM](https://github.com/zhanshijinwat/Steel-LLM) | ZhanShiJin | 1121M | 24.81 | 25.32 | 26 | 24.39 | | [GPT2-medium](https://huggingface.co/openai-community/gpt2-medium) | OpenAI | 360M | 23.18 | 25 | 18.6 | 25.19 | | [TinyLlama-1.1B-Chat-V1.0](https://github.com/jzhang38/TinyLlama) | TinyLlama | 1100M | 25.48 | 25 | 25.4 | 25.13 | | [SmolLM2](https://github.com/huggingface/smollm) | HuggingFaceTB | 135M | 24.37 | 25.02 | 25.37 | 25.06 | | [Aquila-Instruct](https://www.modelscope.cn/models/BAAI/Aquila-135M-Instruct) | BAAI | 135M | 25.11 | 25.1 | 24.43 | 25.05 | ![compare_radar](./images/compare_radar.png) # 📌 Others ## Model Conversion * [./scripts/convert_model.py](./scripts/convert_model.py) can be used to convert between `torch models` and `transformers` models. --- ## Based on MiniMind-API Service Interface * [./scripts/serve_openai_api.py](./scripts/serve_openai_api.py) provides the simplest chat interface compatible with the OpenAI API, making it easy to integrate your model into third-party UIs such as FastGPT, OpenWebUI, Dify, etc. * Download the model weights from [Huggingface](https://huggingface.co/collections/jingyaogong/minimind-66caf8d999f5c7fa64f399e5). The file structure is: ``` (root dir) ├─ | ├── config.json | ├── generation_config.json | ├── LMConfig.py | ├── model.py | ├── pytorch_model.bin | ├── special_tokens_map.json | ├── tokenizer_config.json | ├── tokenizer.json ``` * Start the chat server: ```bash python serve_openai_api.py ``` * Test the service interface: ```bash python chat_openai_api.py ``` * API example, compatible with OpenAI API format: ```bash curl http://ip:port/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "model-identifier", "messages": [ { "role": "user", "content": "What is the highest mountain in the world?" } ], "temperature": 0.7, "max_tokens": 512, "stream": true }' ``` ## VLLM Model Inference (Service) [vLLM](https://github.com/vllm-project/vllm) is an extremely popular and efficient inference framework that supports fast deployment of large models, optimizing memory utilization and throughput. ```bash vllm serve ./MiniMind2/ --model-impl transformers --served-model-name "minimind" ``` The service will start using the OpenAI API protocol, with the default port being 8000. For more usage, please refer to the official documentation. ## llama.cpp [llama.cpp](https://github.com/ggerganov/llama.cpp) is a C++ library that can be used directly from the command line, supporting multi-threaded inference and GPU acceleration. After installation (refer to the official repository), insert the following code at line 760 of `convert_hf_to_gguf.py`: ```text # Add MiniMind2 tokenizer support if res is None: res = "smollm" ``` Convert a custom-trained MiniMind model to gguf: ```bash python convert_hf_to_gguf.py ../minimind/MiniMind2/ ``` Quantize the model: ```bash ./build/bin/llama-quantize ../minimind/MiniMind2/MiniMind2-109M-F16.gguf ../minimind/MiniMind2/Q4-MiniMind2.gguf Q4_K_M ``` Command line inference: ```bash ./build/bin/llama-cli -m ../minimind/MiniMind2/MiniMind2-109M-F16.gguf --chat-template chatml ``` For more usage, please refer to the official documentation. ## ollama [ollama](https://ollama.ai/) is a tool for running large models locally, supporting multiple open-source LLMs, and is easy to use. To load a custom gguf model with ollama, create a new file `minimind.modelfile`: ```text FROM ./MiniMind2-109M-F16.gguf TEMPLATE """{{ if .System }}<|im_start|>system {{ .System }}<|im_end|> {{ end }}{{ if .Prompt }}<|im_start|>user {{ .Prompt }}<|im_end|> {{ end }}<|im_start|>assistant """ ``` Load the model and name it `minimind2`: ```bash ollama create -f minimind.modelfile minimind2 ``` Start inference: ```text ollama run minimind2 > Hello, I am MiniMind2, based on xxxxxxxx ``` For more usage, please refer to the official documentation. # 📌 Acknowledge > [!NOTE] > If you find the `MiniMind series` helpful, feel free to give it a ⭐ on GitHub.
> Due to the length of the content, mistakes are inevitable; please feel free to report issues or submit a PR to improve > the project.
> Your small support is the driving force for continuous improvement of this project! ## 🤝[Contributors](https://github.com/jingyaogong/minimind/graphs/contributors)         ## 😊Acknowledgments @ipfgao: 🔗Training steps record @chuanzhubin: 🔗Line-by-line code comments @WangRongsheng: 🔗Large dataset preprocessing @pengqianhan: 🔗A brief tutorial @RyanSunn: 🔗Inference process learning record @Nijikadesu: 🔗Decompose project code in an interactive notebook format
Reference Links & Thanks to the following excellent papers or projects - No specific order of ranking - [https://github.com/meta-llama/llama3](https://github.com/meta-llama/llama3) - [https://github.com/karpathy/llama2.c](https://github.com/karpathy/llama2.c) - [https://github.com/DLLXW/baby-llama2-chinese](https://github.com/DLLXW/baby-llama2-chinese) - [(DeepSeek-V2)https://arxiv.org/abs/2405.04434](https://arxiv.org/abs/2405.04434) - [https://github.com/charent/ChatLM-mini-Chinese](https://github.com/charent/ChatLM-mini-Chinese) - [https://github.com/wdndev/tiny-llm-zh](https://github.com/wdndev/tiny-llm-zh) - [(Mistral-MoE)https://arxiv.org/pdf/2401.04088](https://arxiv.org/pdf/2401.04088) - [https://github.com/Tongjilibo/build_MiniLLM_from_scratch](https://github.com/Tongjilibo/build_MiniLLM_from_scratch) - [https://github.com/jzhang38/TinyLlama](https://github.com/jzhang38/TinyLlama) - [https://github.com/AI-Study-Han/Zero-Chatgpt](https://github.com/AI-Study-Han/Zero-Chatgpt) - [https://github.com/xusenlinzy/api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm) - [https://github.com/HqWu-HITCS/Awesome-Chinese-LLM](https://github.com/HqWu-HITCS/Awesome-Chinese-LLM)
## 🫶Supporters github contribution grid snake animation github contribution grid snake animation Star History Chart # License This repository is licensed under the [Apache-2.0 License](LICENSE). ## /dataset/__init__.py ```py path="/dataset/__init__.py" ``` ## /dataset/lm_dataset.py ```py path="/dataset/lm_dataset.py" import json import random import re import pandas as pd import numpy as np from torch.utils.data import Dataset, DataLoader import torch from sklearn.model_selection import train_test_split import os import ast os.environ["TOKENIZERS_PARALLELISM"] = "false" class PretrainDataset(Dataset): def __init__(self, data_path, tokenizer, max_length=512): super().__init__() self.tokenizer = tokenizer self.max_length = max_length self.samples = self.load_data(data_path) def load_data(self, path): samples = [] with open(path, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): data = json.loads(line.strip()) samples.append(data) return samples def __len__(self): return len(self.samples) def __getitem__(self, index): sample = self.samples[index] # 构建输入文本 encoding = self.tokenizer( str(sample['text']), max_length=self.max_length, padding='max_length', truncation=True, return_tensors='pt' ) input_ids = encoding.input_ids.squeeze() loss_mask = (input_ids != self.tokenizer.pad_token_id) X = torch.tensor(input_ids[:-1], dtype=torch.long) Y = torch.tensor(input_ids[1:], dtype=torch.long) loss_mask = torch.tensor(loss_mask[1:], dtype=torch.long) return X, Y, loss_mask class SFTDataset(Dataset): def __init__(self, jsonl_path, tokenizer, max_length=1024): super().__init__() self.tokenizer = tokenizer self.max_length = max_length self.samples = self.load_data(jsonl_path) self.bos_id = tokenizer('<|im_start|>assistant', add_special_tokens=False).input_ids self.eos_id = tokenizer('<|im_end|>', add_special_tokens=False).input_ids def __len__(self): return len(self.samples) def load_data(self, path): samples = [] with open(path, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): data = json.loads(line.strip()) samples.append(data) return samples def _create_chat_prompt(self, conversations): """构建符合ChatML格式的对话""" messages = [] for i, turn in enumerate(conversations): role = 'user' if i % 2 == 0 else 'assistant' messages.append({"role": role, "content": turn['content']}) return self.tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=False ) def _generate_loss_mask(self, input_ids): loss_mask = [0] * len(input_ids) 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 for j in range(start + 1, min(end + len(self.eos_id) + 1, self.max_length)): loss_mask[j] = 1 i = end + len(self.eos_id) if end < len(input_ids) else len(input_ids) else: i += 1 return loss_mask def __getitem__(self, index): sample = self.samples[index] # 构建对话提示 prompt = self._create_chat_prompt(sample['conversations']) input_ids = self.tokenizer(prompt).input_ids[:self.max_length] input_ids += [self.tokenizer.pad_token_id] * (self.max_length - len(input_ids)) # 生成动态损失掩码 loss_mask = self._generate_loss_mask(input_ids) # 构建训练数据 X = torch.tensor(input_ids[:-1], dtype=torch.long) Y = torch.tensor(input_ids[1:], dtype=torch.long) loss_mask = torch.tensor(loss_mask[1:], dtype=torch.long) # 对齐预测位置 return X, Y, loss_mask class DPODataset(Dataset): def __init__(self, file_path, tokenizer, max_length=4096): super().__init__() self.tokenizer = tokenizer self.max_length = max_length self.padding = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else 0 self.bos_id = tokenizer('<|im_start|>assistant', add_special_tokens=False).input_ids self.eos_id = tokenizer('<|im_end|>', add_special_tokens=False).input_ids with open(file_path, 'r', encoding='utf-8') as f: self.data = [] for line in f: line = line.strip() obj = json.loads(line) self.data.append(obj) def __len__(self): return len(self.data) def __getitem__(self, index): item = self.data[index] chosen = item['chosen'] # 是一个 list,里面包含若干 {role, content} rejected = item['rejected'] # 同上 chosen_prompt = self.tokenizer.apply_chat_template( chosen, tokenize=False, add_generation_prompt=False ) rejected_prompt = self.tokenizer.apply_chat_template( rejected, tokenize=False, add_generation_prompt=False ) chosen_encoding = self.tokenizer( chosen_prompt, truncation=True, max_length=self.max_length, padding='max_length' ) rejected_encoding = self.tokenizer( rejected_prompt, truncation=True, max_length=self.max_length, padding='max_length' ) chosen_input_ids = chosen_encoding['input_ids'] chosen_loss_mask = self._generate_loss_mask(chosen_input_ids) rejected_input_ids = rejected_encoding['input_ids'] rejected_loss_mask = self._generate_loss_mask(rejected_input_ids) x_chosen = torch.tensor(chosen_input_ids[:-1], dtype=torch.long) y_chosen = torch.tensor(chosen_input_ids[1:], dtype=torch.long) mask_chosen = torch.tensor(chosen_loss_mask[1:], dtype=torch.long) x_rejected = torch.tensor(rejected_input_ids[:-1], dtype=torch.long) y_rejected = torch.tensor(rejected_input_ids[1:], dtype=torch.long) mask_rejected = torch.tensor(rejected_loss_mask[1:], dtype=torch.long) return { 'x_chosen': x_chosen, 'y_chosen': y_chosen, 'mask_chosen': mask_chosen, 'x_rejected': x_rejected, 'y_rejected': y_rejected, 'mask_rejected': mask_rejected } def _generate_loss_mask(self, input_ids): loss_mask = [0] * len(input_ids) 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 for j in range(start + 1, min(end + len(self.eos_id) + 1, self.max_length)): loss_mask[j] = 1 i = end + len(self.eos_id) if end < len(input_ids) else len(input_ids) else: i += 1 return loss_mask class RLAIFDataset(Dataset): def __init__(self, jsonl_path, tokenizer, max_length=1024): super().__init__() self.tokenizer = tokenizer self.max_length = max_length self.samples = self.load_data(jsonl_path) self.bos_id = tokenizer('<|im_start|>assistant', add_special_tokens=False).input_ids self.eos_id = tokenizer('<|im_end|>', add_special_tokens=False).input_ids def __len__(self): return len(self.samples) def load_data(self, path): samples = [] with open(path, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): data = json.loads(line.strip()) samples.append(data) return samples def _create_chat_prompt(self, conversations): """构建符合ChatML格式的对话""" messages = [] answer = '' for i, turn in enumerate(conversations): role = 'user' if i % 2 == 0 else 'assistant' messages.append({"role": role, "content": turn['content']}) answer = turn['content'] return self.tokenizer.apply_chat_template( messages[:-1], tokenize=False, add_generation_prompt=True ), answer def __getitem__(self, index): sample = self.samples[index] # 构建对话提示 prompt, answer = self._create_chat_prompt(sample['conversations']) return { 'prompt': prompt, 'answer': answer } if __name__ == "__main__": pass ``` ## /eval_model.py ```py path="/eval_model.py" import argparse import random import warnings import numpy as np from transformers import AutoTokenizer, AutoModelForCausalLM, TextStreamer from model.model_minimind import MiniMindConfig, MiniMindForCausalLM from model.model_lora import * warnings.filterwarnings('ignore') def init_model(args): tokenizer = AutoTokenizer.from_pretrained('./model/') if args.load == 0: moe_path = '_moe' if args.use_moe else '' modes = {0: 'pretrain', 1: 'full_sft', 2: 'rlhf', 3: 'reason', 4: 'grpo'} ckp = f'./{args.out_dir}/{modes[args.model_mode]}_{args.hidden_size}{moe_path}.pth' model = MiniMindForCausalLM(MiniMindConfig( hidden_size=args.hidden_size, num_hidden_layers=args.num_hidden_layers, use_moe=args.use_moe )) model.load_state_dict(torch.load(ckp, map_location=args.device), strict=True) if args.lora_name != 'None': apply_lora(model) load_lora(model, f'./{args.out_dir}/lora/{args.lora_name}_{args.hidden_size}.pth') else: transformers_model_path = './MiniMind2' tokenizer = AutoTokenizer.from_pretrained(transformers_model_path) model = AutoModelForCausalLM.from_pretrained(transformers_model_path, trust_remote_code=True) print(f'MiniMind模型参数量: {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e6:.2f}M(illion)') return model.eval().to(args.device), tokenizer def get_prompt_datas(args): if args.model_mode == 0: # pretrain模型的接龙能力(无法对话) prompt_datas = [ '马克思主义基本原理', '人类大脑的主要功能', '万有引力原理是', '世界上最高的山峰是', '二氧化碳在空气中', '地球上最大的动物有', '杭州市的美食有' ] else: if args.lora_name == 'None': # 通用对话问题 prompt_datas = [ '请介绍一下自己。', '你更擅长哪一个学科?', '鲁迅的《狂人日记》是如何批判封建礼教的?', '我咳嗽已经持续了两周,需要去医院检查吗?', '详细的介绍光速的物理概念。', '推荐一些杭州的特色美食吧。', '请为我讲解“大语言模型”这个概念。', '如何理解ChatGPT?', 'Introduce the history of the United States, please.' ] else: # 特定领域问题 lora_prompt_datas = { 'lora_identity': [ "你是ChatGPT吧。", "你叫什么名字?", "你和openai是什么关系?" ], 'lora_medical': [ '我最近经常感到头晕,可能是什么原因?', '我咳嗽已经持续了两周,需要去医院检查吗?', '服用抗生素时需要注意哪些事项?', '体检报告中显示胆固醇偏高,我该怎么办?', '孕妇在饮食上需要注意什么?', '老年人如何预防骨质疏松?', '我最近总是感到焦虑,应该怎么缓解?', '如果有人突然晕倒,应该如何急救?' ], } prompt_datas = lora_prompt_datas[args.lora_name] return prompt_datas # 设置可复现的随机种子 def setup_seed(seed): 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 main(): parser = argparse.ArgumentParser(description="Chat with MiniMind") parser.add_argument('--lora_name', default='None', type=str) parser.add_argument('--out_dir', default='out', type=str) parser.add_argument('--temperature', default=0.85, type=float) parser.add_argument('--top_p', default=0.85, type=float) parser.add_argument('--device', default='cuda' if torch.cuda.is_available() else 'cpu', type=str) # 此处max_seq_len(最大输出长度)并不意味模型具有对应的长文本的性能,仅防止QA出现被截断的问题 # MiniMind2-moe (145M):(hidden_size=640, num_hidden_layers=8, use_moe=True) # MiniMind2-Small (26M):(hidden_size=512, num_hidden_layers=8) # MiniMind2 (104M):(hidden_size=768, num_hidden_layers=16) parser.add_argument('--hidden_size', default=512, type=int) parser.add_argument('--num_hidden_layers', default=8, type=int) parser.add_argument('--max_seq_len', default=8192, type=int) parser.add_argument('--use_moe', default=False, type=bool) # 携带历史对话上下文条数 # history_cnt需要设为偶数,即【用户问题, 模型回答】为1组;设置为0时,即当前query不携带历史上文 # 模型未经过外推微调时,在更长的上下文的chat_template时难免出现性能的明显退化,因此需要注意此处设置 parser.add_argument('--history_cnt', default=0, type=int) parser.add_argument('--load', default=0, type=int, help="0: 原生torch权重,1: transformers加载") parser.add_argument('--model_mode', default=1, type=int, help="0: 预训练模型,1: SFT-Chat模型,2: RLHF-Chat模型,3: Reason模型,4: RLAIF-Chat模型") args = parser.parse_args() model, tokenizer = init_model(args) prompts = get_prompt_datas(args) test_mode = int(input('[0] 自动测试\n[1] 手动输入\n')) streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) messages = [] for idx, prompt in enumerate(prompts if test_mode == 0 else iter(lambda: input('👶: '), '')): setup_seed(random.randint(0, 2048)) # setup_seed(2025) # 如需固定每次输出则换成【固定】的随机种子 if test_mode == 0: print(f'👶: {prompt}') messages = messages[-args.history_cnt:] if args.history_cnt else [] messages.append({"role": "user", "content": prompt}) new_prompt = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) if args.model_mode != 0 else (tokenizer.bos_token + prompt) inputs = tokenizer( new_prompt, return_tensors="pt", truncation=True ).to(args.device) print('🤖️: ', end='') generated_ids = model.generate( inputs["input_ids"], max_new_tokens=args.max_seq_len, num_return_sequences=1, do_sample=True, attention_mask=inputs["attention_mask"], pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id, streamer=streamer, top_p=args.top_p, temperature=args.temperature ) response = tokenizer.decode(generated_ids[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True) messages.append({"role": "assistant", "content": response}) print('\n\n') if __name__ == "__main__": main() ``` ## /images/1-wiki.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/1-wiki.png ## /images/2-wiki.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/2-wiki.png ## /images/3-wiki.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/3-wiki.png ## /images/4-wiki.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/4-wiki.png ## /images/5-wiki.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/5-wiki.png ## /images/LLM-structure-moe.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/LLM-structure-moe.png ## /images/LLM-structure.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/LLM-structure.png ## /images/and_huggingface.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/and_huggingface.png ## /images/and_modelscope.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/and_modelscope.png ## /images/compare_radar.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/compare_radar.png ## /images/dataset.jpg Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/dataset.jpg ## /images/gpt3_config.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/gpt3_config.png ## /images/logo.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/logo.png ## /images/logo2.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/logo2.png ## /images/minimind2.gif Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/minimind2.gif ## /images/pre_512_loss.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/pre_512_loss.png ## /images/pre_768_loss.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/pre_768_loss.png ## /images/sft_512_loss.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/sft_512_loss.png ## /images/sft_768_loss.png Binary file available at https://raw.githubusercontent.com/jingyaogong/minimind/refs/heads/main/images/sft_768_loss.png ## /model/__init__.py ```py path="/model/__init__.py" ``` ## /model/model_lora.py ```py path="/model/model_lora.py" import torch from torch import optim, nn # 定义Lora网络结构 class LoRA(nn.Module): def __init__(self, in_features, out_features, rank): super().__init__() self.rank = rank # LoRA的秩(rank),控制低秩矩阵的大小 self.A = nn.Linear(in_features, rank, bias=False) # 低秩矩阵A self.B = nn.Linear(rank, out_features, bias=False) # 低秩矩阵B # 矩阵A高斯初始化 self.A.weight.data.normal_(mean=0.0, std=0.02) # 矩阵B全0初始化 self.B.weight.data.zero_() def forward(self, x): return self.B(self.A(x)) def apply_lora(model, rank=8): for name, module in model.named_modules(): if isinstance(module, nn.Linear) and module.weight.shape[0] == module.weight.shape[1]: lora = LoRA(module.weight.shape[0], module.weight.shape[1], rank=rank).to(model.device) setattr(module, "lora", lora) original_forward = module.forward # 显式绑定 def forward_with_lora(x, layer1=original_forward, layer2=lora): return layer1(x) + layer2(x) module.forward = forward_with_lora def load_lora(model, path): state_dict = torch.load(path, map_location=model.device) for name, module in model.named_modules(): if hasattr(module, 'lora'): lora_state = {k.replace(f'{name}.lora.', ''): v for k, v in state_dict.items() if f'{name}.lora.' in k} module.lora.load_state_dict(lora_state) def save_lora(model, path): state_dict = {} for name, module in model.named_modules(): if hasattr(module, 'lora'): lora_state = {f'{name}.lora.{k}': v for k, v in module.lora.state_dict().items()} state_dict.update(lora_state) torch.save(state_dict, path) ``` ## /model/model_minimind.py ```py path="/model/model_minimind.py" # 📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘 # MiniMind Config # 📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘 from transformers import PretrainedConfig class MiniMindConfig(PretrainedConfig): model_type = "minimind" def __init__( self, dropout: float = 0.0, bos_token_id: int = 1, eos_token_id: int = 2, hidden_act: str = 'silu', hidden_size: int = 512, intermediate_size: int = None, max_position_embeddings: int = 32768, num_attention_heads: int = 8, num_hidden_layers: int = 8, num_key_value_heads: int = 2, vocab_size: int = 6400, rms_norm_eps: float = 1e-05, rope_theta: int = 1000000.0, flash_attn: bool = True, #################################################### # Here are the specific configurations of MOE # When use_moe is false, the following is invalid #################################################### use_moe: bool = False, num_experts_per_tok: int = 2, n_routed_experts: int = 4, n_shared_experts: int = 1, scoring_func: str = 'softmax', aux_loss_alpha: float = 0.1, seq_aux: bool = True, norm_topk_prob: bool = True, **kwargs ): super().__init__(**kwargs) self.dropout = dropout self.bos_token_id = bos_token_id self.eos_token_id = eos_token_id self.hidden_act = hidden_act self.hidden_size = hidden_size self.intermediate_size = intermediate_size self.max_position_embeddings = max_position_embeddings self.num_attention_heads = num_attention_heads self.num_hidden_layers = num_hidden_layers self.num_key_value_heads = num_key_value_heads self.vocab_size = vocab_size self.rms_norm_eps = rms_norm_eps self.rope_theta = rope_theta self.flash_attn = flash_attn #################################################### # Here are the specific configurations of MOE # When use_moe is false, the following is invalid #################################################### self.use_moe = use_moe self.num_experts_per_tok = num_experts_per_tok # 每个token选择的专家数量 self.n_routed_experts = n_routed_experts # 总的专家数量 self.n_shared_experts = n_shared_experts # 共享专家 self.scoring_func = scoring_func # 评分函数,默认为'softmax' self.aux_loss_alpha = aux_loss_alpha # 辅助损失的alpha参数 self.seq_aux = seq_aux # 是否在序列级别上计算辅助损失 self.norm_topk_prob = norm_topk_prob # 是否标准化top-k概率 # 📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘 # MiniMind Model # 📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘📘 import math import torch from torch import nn from transformers.activations import ACT2FN from typing import Optional, Tuple, List, Union import torch.nn.functional as F from transformers import PreTrainedModel, GenerationMixin, PretrainedConfig from transformers.modeling_outputs import CausalLMOutputWithPast 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), theta: float = 1e6): freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)) 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) freqs_sin = torch.cat([torch.sin(freqs), torch.sin(freqs)], dim=-1) return freqs_cos, freqs_sin def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, 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)) k_embed = (k * cos.unsqueeze(unsqueeze_dim)) + (rotate_half(k) * sin.unsqueeze(unsqueeze_dim)) return q_embed, k_embed def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor: """torch.repeat_interleave(x, dim=2, repeats=n_rep)""" 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, args: MiniMindConfig): super().__init__() self.num_key_value_heads = args.num_attention_heads if args.num_key_value_heads is None else args.num_key_value_heads assert args.num_attention_heads % self.num_key_value_heads == 0 self.n_local_heads = args.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 = args.hidden_size // args.num_attention_heads self.q_proj = nn.Linear(args.hidden_size, args.num_attention_heads * self.head_dim, bias=False) self.k_proj = nn.Linear(args.hidden_size, self.num_key_value_heads * self.head_dim, bias=False) self.v_proj = nn.Linear(args.hidden_size, self.num_key_value_heads * self.head_dim, bias=False) self.o_proj = nn.Linear(args.num_attention_heads * self.head_dim, args.hidden_size, bias=False) self.attn_dropout = nn.Dropout(args.dropout) self.resid_dropout = nn.Dropout(args.dropout) self.dropout = args.dropout self.flash = hasattr(torch.nn.functional, 'scaled_dot_product_attention') and args.flash_attn # print("WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0") def forward(self, x: torch.Tensor, position_embeddings: Tuple[torch.Tensor, torch.Tensor], # 修改为接收cos和sin past_key_value: Optional[Tuple[torch.Tensor, torch.Tensor]] = None, use_cache=False, attention_mask: Optional[torch.Tensor] = 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) cos, sin = position_embeddings xq, xk = apply_rotary_pos_emb(xq, xk, cos[:seq_len], sin[:seq_len]) # kv_cache实现 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: dropout_p = self.dropout if self.training else 0.0 attn_mask = None if attention_mask is not None: attn_mask = attention_mask.view(bsz, 1, 1, -1).expand(bsz, self.n_local_heads, seq_len, -1) attn_mask = attn_mask.bool() if attention_mask is not None else None output = F.scaled_dot_product_attention(xq, xk, xv, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=True) else: scores = (xq @ xk.transpose(-2, -1)) / math.sqrt(self.head_dim) scores = scores + torch.triu( torch.full((seq_len, seq_len), float("-inf"), device=scores.device), diagonal=1 ).unsqueeze(0).unsqueeze(0) # scores+mask if attention_mask is not None: extended_attention_mask = attention_mask.unsqueeze(1).unsqueeze(2) extended_attention_mask = (1.0 - extended_attention_mask) * -1e9 scores = scores + extended_attention_mask scores = F.softmax(scores.float(), dim=-1).type_as(xq) scores = self.attn_dropout(scores) output = scores @ 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): super().__init__() if config.intermediate_size is None: intermediate_size = int(config.hidden_size * 8 / 3) config.intermediate_size = 64 * ((intermediate_size + 64 - 1) // 64) self.gate_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False) self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False) self.up_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False) self.dropout = nn.Dropout(config.dropout) self.act_fn = ACT2FN[config.hidden_act] def forward(self, x): return self.dropout(self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))) class MoEGate(nn.Module): def __init__(self, config: MiniMindConfig): super().__init__() self.config = config self.top_k = config.num_experts_per_tok self.n_routed_experts = config.n_routed_experts self.scoring_func = config.scoring_func self.alpha = config.aux_loss_alpha self.seq_aux = config.seq_aux self.norm_topk_prob = config.norm_topk_prob self.gating_dim = config.hidden_size self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim))) self.reset_parameters() def reset_parameters(self) -> None: import torch.nn.init as init init.kaiming_uniform_(self.weight, a=math.sqrt(5)) def forward(self, hidden_states): bsz, seq_len, h = hidden_states.shape hidden_states = hidden_states.view(-1, h) logits = F.linear(hidden_states, self.weight, None) if self.scoring_func == 'softmax': scores = logits.softmax(dim=-1) else: raise NotImplementedError(f'insupportable scoring function for MoE gating: {self.scoring_func}') topk_weight, topk_idx = torch.topk(scores, k=self.top_k, dim=-1, sorted=False) if self.top_k > 1 and self.norm_topk_prob: denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20 topk_weight = topk_weight / denominator if self.training and self.alpha > 0.0: scores_for_aux = scores aux_topk = self.top_k topk_idx_for_aux_loss = topk_idx.view(bsz, -1) if self.seq_aux: scores_for_seq_aux = scores_for_aux.view(bsz, seq_len, -1) ce = torch.zeros(bsz, self.n_routed_experts, device=hidden_states.device) ce.scatter_add_(1, topk_idx_for_aux_loss, torch.ones(bsz, seq_len * aux_topk, device=hidden_states.device)).div_( seq_len * aux_topk / self.n_routed_experts) aux_loss = (ce * scores_for_seq_aux.mean(dim=1)).sum(dim=1).mean() * self.alpha else: mask_ce = F.one_hot(topk_idx_for_aux_loss.view(-1), num_classes=self.n_routed_experts) ce = mask_ce.float().mean(0) Pi = scores_for_aux.mean(0) fi = ce * self.n_routed_experts aux_loss = (Pi * fi).sum() * self.alpha else: aux_loss = 0 return topk_idx, topk_weight, aux_loss class MOEFeedForward(nn.Module): def __init__(self, config: MiniMindConfig): super().__init__() self.config = config self.experts = nn.ModuleList([ FeedForward(config) for _ in range(config.n_routed_experts) ]) self.gate = MoEGate(config) if config.n_shared_experts > 0: self.shared_experts = nn.ModuleList([ FeedForward(config) for _ in range(config.n_shared_experts) ]) def forward(self, x): identity = x orig_shape = x.shape bsz, seq_len, _ = x.shape # 使用门控机制选择专家 topk_idx, topk_weight, aux_loss = self.gate(x) x = x.view(-1, x.shape[-1]) flat_topk_idx = topk_idx.view(-1) if self.training: x = x.repeat_interleave(self.config.num_experts_per_tok, dim=0) y = torch.empty_like(x, dtype=torch.float16) for i, expert in enumerate(self.experts): y[flat_topk_idx == i] = expert(x[flat_topk_idx == i]).to(y.dtype) # 确保类型一致 y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1) y = y.view(*orig_shape) else: y = self.moe_infer(x, flat_topk_idx, topk_weight.view(-1, 1)).view(*orig_shape) if self.config.n_shared_experts > 0: for expert in self.shared_experts: y = y + expert(identity) self.aux_loss = aux_loss return y @torch.no_grad() def moe_infer(self, x, flat_expert_indices, flat_expert_weights): expert_cache = torch.zeros_like(x) idxs = flat_expert_indices.argsort() tokens_per_expert = flat_expert_indices.bincount().cpu().numpy().cumsum(0) token_idxs = idxs // self.config.num_experts_per_tok # 当tokens_per_expert = [6, 15, 20, 26],tokens_per_expert.shape[0]即为专家数量(此时为4) # 且token_idxs = [3, 7, 19, 21, 24, 25, 4, 5, 6, 10, 11, 12...] 时 # 意味token_idxs[:6] -> [3, 7, 19, 21, 24, 25]这6个位置属于专家0处理的token(每个token有可能被多个专家处理,这取决于num_experts_per_tok) # 接下来9个位置token_idxs[6:15] -> [4, 5, 6, 10, 11, 12...]属于专家1处理的token...依此类推 for i, end_idx in enumerate(tokens_per_expert): start_idx = 0 if i == 0 else tokens_per_expert[i - 1] if start_idx == end_idx: continue expert = self.experts[i] exp_token_idx = token_idxs[start_idx:end_idx] expert_tokens = x[exp_token_idx] expert_out = expert(expert_tokens).to(expert_cache.dtype) expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]]) expert_cache.scatter_add_(0, exp_token_idx.view(-1, 1).repeat(1, x.shape[-1]), expert_out) return expert_cache class MiniMindBlock(nn.Module): def __init__(self, layer_id: int, config: MiniMindConfig): super().__init__() self.num_attention_heads = config.num_attention_heads self.hidden_size = config.hidden_size self.head_dim = config.hidden_size // config.num_attention_heads self.self_attn = Attention(config) self.layer_id = layer_id 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.hidden_size // config.num_attention_heads, end=config.max_position_embeddings, theta=config.rope_theta) self.register_buffer("freqs_cos", freqs_cos, persistent=False) self.register_buffer("freqs_sin", freqs_sin, persistent=False) def forward(self, input_ids: Optional[torch.Tensor] = None, attention_mask: Optional[torch.Tensor] = None, past_key_values: Optional[List[Tuple[torch.Tensor, torch.Tensor]]] = None, use_cache: bool = False, **kwargs): batch_size, seq_length = input_ids.shape 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)) position_embeddings = ( self.freqs_cos[start_pos:start_pos + seq_length], self.freqs_sin[start_pos:start_pos + seq_length] ) presents = [] for layer_idx, (layer, past_key_value) in enumerate(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( layer.mlp.aux_loss for layer in self.layers if isinstance(layer.mlp, MOEFeedForward) ) return hidden_states, presents, aux_loss class MiniMindForCausalLM(PreTrainedModel, GenerationMixin): config_class = MiniMindConfig 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) self.model.embed_tokens.weight = self.lm_head.weight self.OUT = CausalLMOutputWithPast() def forward(self, input_ids: Optional[torch.Tensor] = None, attention_mask: Optional[torch.Tensor] = None, past_key_values: Optional[List[Tuple[torch.Tensor, torch.Tensor]]] = None, use_cache: bool = False, logits_to_keep: Union[int, torch.Tensor] = 0, **args): h, past_kvs, aux_loss = self.model( input_ids=input_ids, attention_mask=attention_mask, past_key_values=past_key_values, use_cache=use_cache, **args ) slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep logits = self.lm_head(h[:, slice_indices, :]) self.OUT.__setitem__('last_hidden_state', h) self.OUT.__setitem__('logits', logits) self.OUT.__setitem__('aux_loss', aux_loss) self.OUT.__setitem__('past_key_values', past_kvs) return self.OUT ``` The content has been capped at 50000 tokens, and files over NaN bytes have been omitted. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.