rename minimind→omni in model_type, eval/convert scripts, and docs
Browse files- README.md +24 -12
- scripts/convert_model.py +30 -21
- scripts/eval_llm.py +2 -2
- scripts/serve_openai_api.py +1 -1
- src/models/lm/config.py +23 -13
- src/models/vam/config.py +1 -1
- src/models/vlm/config.py +1 -1
- src/trainers/lm/rollout_engine.py +1 -1
README.md
CHANGED
|
@@ -173,25 +173,37 @@ torchrun --nproc_per_node=4 -m trainers.lm.full_sft --config configs/lm/lm_full_
|
|
| 173 |
### 推理 / 对话
|
| 174 |
|
| 175 |
```bash
|
| 176 |
-
#
|
| 177 |
-
python scripts/eval_llm.py --save_dir checkpoint/lm_full_sft_mini \
|
| 178 |
-
--weight full_sft --hidden_size 128
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
--weight pretrain --hidden_size 128 --native
|
| 183 |
|
| 184 |
-
#
|
| 185 |
-
python scripts/eval_llm.py --
|
| 186 |
-
--
|
| 187 |
|
| 188 |
-
|
| 189 |
-
|
| 190 |
|
| 191 |
-
#
|
|
|
|
| 192 |
python scripts/eval_vam.py --save_dir checkpoint/vam --weight full_sft
|
| 193 |
```
|
| 194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
## 配置说明
|
| 196 |
|
| 197 |
`configs/*.yaml` 分为 `model` / `train` / `paths` 三段,由 `utils.training.apply_config`
|
|
|
|
| 173 |
### 推理 / 对话
|
| 174 |
|
| 175 |
```bash
|
| 176 |
+
# 原生 torch 格式(.pth)
|
| 177 |
+
python scripts/eval_llm.py --native --save_dir checkpoint/lm_full_sft_mini \
|
| 178 |
+
--weight full_sft --hidden_size 128
|
| 179 |
|
| 180 |
+
python scripts/eval_llm.py --native --save_dir checkpoint/lm_pretrain_mini \
|
| 181 |
+
--weight pretrain --hidden_size 128
|
|
|
|
| 182 |
|
| 183 |
+
# HuggingFace 格式(config.json + model.safetensors)
|
| 184 |
+
python scripts/eval_llm.py --load_from checkpoint/omni \
|
| 185 |
+
--tokenizer_path checkpoint/omni
|
| 186 |
|
| 187 |
+
python scripts/eval_llm.py --load_from checkpoint/lm_full_sft_mini/hf \
|
| 188 |
+
--tokenizer_path checkpoint/lm_full_sft_mini/hf
|
| 189 |
|
| 190 |
+
# 多模态(VLM / VAM)
|
| 191 |
+
python scripts/eval_vlm.py --save_dir checkpoint/vlm --weight full_sft
|
| 192 |
python scripts/eval_vam.py --save_dir checkpoint/vam --weight full_sft
|
| 193 |
```
|
| 194 |
|
| 195 |
+
### 格式转换
|
| 196 |
+
|
| 197 |
+
```bash
|
| 198 |
+
# 原生 torch → HuggingFace 格式(omni 原生)
|
| 199 |
+
python scripts/convert_model.py checkpoint/lm_full_sft_mini/full_sft_128.pth \
|
| 200 |
+
checkpoint/lm_full_sft_mini/hf \
|
| 201 |
+
--tokenizer_path checkpoint/tokenizer
|
| 202 |
+
|
| 203 |
+
# 省略 --mode,默认 omni;也可输出 Qwen3 兼容格式
|
| 204 |
+
python scripts/convert_model.py checkpoint/omni/full_sft.pth output/dir --mode qwen
|
| 205 |
+
```
|
| 206 |
+
|
| 207 |
## 配置说明
|
| 208 |
|
| 209 |
`configs/*.yaml` 分为 `model` / `train` / `paths` 三段,由 `utils.training.apply_config`
|
scripts/convert_model.py
CHANGED
|
@@ -10,7 +10,7 @@ from models.lm.lora import apply_lora, merge_lora
|
|
| 10 |
|
| 11 |
warnings.filterwarnings('ignore', category=UserWarning)
|
| 12 |
|
| 13 |
-
def
|
| 14 |
LMConfig.register_for_auto_class()
|
| 15 |
LMForCausalLM.register_for_auto_class("AutoModelForCausalLM")
|
| 16 |
lm_model = LMForCausalLM(lm_config)
|
|
@@ -21,7 +21,7 @@ def convert_torch2transformers_minimind(torch_path, transformers_path, dtype=tor
|
|
| 21 |
model_params = sum(p.numel() for p in lm_model.parameters() if p.requires_grad)
|
| 22 |
print(f'模型参数: {model_params / 1e6} 百万 = {model_params / 1e9} B (Billion)')
|
| 23 |
lm_model.save_pretrained(transformers_path, safe_serialization=False)
|
| 24 |
-
tokenizer = AutoTokenizer.from_pretrained(
|
| 25 |
tokenizer.save_pretrained(transformers_path)
|
| 26 |
# ======= transformers-5.0的兼容低版本写法 =======
|
| 27 |
if int(transformers.__version__.split('.')[0]) >= 5:
|
|
@@ -30,11 +30,11 @@ def convert_torch2transformers_minimind(torch_path, transformers_path, dtype=tor
|
|
| 30 |
config = json.load(open(config_path, 'r', encoding='utf-8'))
|
| 31 |
config['rope_theta'] = lm_config.rope_theta; config['rope_scaling'] = None; del config['rope_parameters']
|
| 32 |
json.dump(config, open(config_path, 'w', encoding='utf-8'), indent=2, ensure_ascii=False)
|
| 33 |
-
print(f"模型已保存为 Transformers-
|
| 34 |
|
| 35 |
|
| 36 |
# QwenForCausalLM/LlamaForCausalLM结构兼容生态
|
| 37 |
-
def convert_torch2transformers(torch_path, transformers_path, dtype=torch.float16):
|
| 38 |
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
| 39 |
state_dict = torch.load(torch_path, map_location=device)
|
| 40 |
common_config = {
|
|
@@ -80,7 +80,7 @@ def convert_torch2transformers(torch_path, transformers_path, dtype=torch.float1
|
|
| 80 |
qwen_model.save_pretrained(transformers_path)
|
| 81 |
model_params = sum(p.numel() for p in qwen_model.parameters() if p.requires_grad)
|
| 82 |
print(f'模型参数: {model_params / 1e6} 百万 = {model_params / 1e9} B (Billion)')
|
| 83 |
-
tokenizer = AutoTokenizer.from_pretrained(
|
| 84 |
tokenizer.save_pretrained(transformers_path)
|
| 85 |
|
| 86 |
# ======= transformers-5.0的兼容低版本写法 =======
|
|
@@ -123,19 +123,28 @@ def convert_json_to_jinja(json_file_path, output_path):
|
|
| 123 |
|
| 124 |
|
| 125 |
if __name__ == '__main__':
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
warnings.filterwarnings('ignore', category=UserWarning)
|
| 12 |
|
| 13 |
+
def convert_torch2transformers_omni(torch_path, transformers_path, dtype=torch.float16, tokenizer_path='checkpoint/tokenizer'):
|
| 14 |
LMConfig.register_for_auto_class()
|
| 15 |
LMForCausalLM.register_for_auto_class("AutoModelForCausalLM")
|
| 16 |
lm_model = LMForCausalLM(lm_config)
|
|
|
|
| 21 |
model_params = sum(p.numel() for p in lm_model.parameters() if p.requires_grad)
|
| 22 |
print(f'模型参数: {model_params / 1e6} 百万 = {model_params / 1e9} B (Billion)')
|
| 23 |
lm_model.save_pretrained(transformers_path, safe_serialization=False)
|
| 24 |
+
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
|
| 25 |
tokenizer.save_pretrained(transformers_path)
|
| 26 |
# ======= transformers-5.0的兼容低版本写法 =======
|
| 27 |
if int(transformers.__version__.split('.')[0]) >= 5:
|
|
|
|
| 30 |
config = json.load(open(config_path, 'r', encoding='utf-8'))
|
| 31 |
config['rope_theta'] = lm_config.rope_theta; config['rope_scaling'] = None; del config['rope_parameters']
|
| 32 |
json.dump(config, open(config_path, 'w', encoding='utf-8'), indent=2, ensure_ascii=False)
|
| 33 |
+
print(f"模型已保存为 Transformers-Omni 格式: {transformers_path}")
|
| 34 |
|
| 35 |
|
| 36 |
# QwenForCausalLM/LlamaForCausalLM结构兼容生态
|
| 37 |
+
def convert_torch2transformers(torch_path, transformers_path, dtype=torch.float16, tokenizer_path='checkpoint/tokenizer'):
|
| 38 |
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
| 39 |
state_dict = torch.load(torch_path, map_location=device)
|
| 40 |
common_config = {
|
|
|
|
| 80 |
qwen_model.save_pretrained(transformers_path)
|
| 81 |
model_params = sum(p.numel() for p in qwen_model.parameters() if p.requires_grad)
|
| 82 |
print(f'模型参数: {model_params / 1e6} 百万 = {model_params / 1e9} B (Billion)')
|
| 83 |
+
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
|
| 84 |
tokenizer.save_pretrained(transformers_path)
|
| 85 |
|
| 86 |
# ======= transformers-5.0的兼容低版本写法 =======
|
|
|
|
| 123 |
|
| 124 |
|
| 125 |
if __name__ == '__main__':
|
| 126 |
+
import argparse
|
| 127 |
+
parser = argparse.ArgumentParser(description="转换模型格式")
|
| 128 |
+
parser.add_argument('torch_path', type=str, help="输入 .pth 权重路径")
|
| 129 |
+
parser.add_argument('output_dir', type=str, help="输出目录")
|
| 130 |
+
parser.add_argument('--hidden_size', type=int, default=None, help="hidden_size(未指定则从 checkpoint 推断)")
|
| 131 |
+
parser.add_argument('--num_hidden_layers', type=int, default=None, help="层数(未指定则从 checkpoint 推断)")
|
| 132 |
+
parser.add_argument('--use_moe', action='store_true', help="MoE 架构")
|
| 133 |
+
parser.add_argument('--max_seq_len', type=int, default=8192, help="最大序列长度")
|
| 134 |
+
parser.add_argument('--mode', choices=['omni', 'qwen'], default='omni', help="输出格式(omni=原生LM格式, qwen=Qwen3兼容格式)")
|
| 135 |
+
parser.add_argument('--dtype', type=str, default='float16', help="权重精度")
|
| 136 |
+
parser.add_argument('--tokenizer_path', type=str, default='checkpoint/tokenizer', help="tokenizer 路径")
|
| 137 |
+
args = parser.parse_args()
|
| 138 |
+
|
| 139 |
+
state = torch.load(args.torch_path, map_location='cpu')
|
| 140 |
+
hs = args.hidden_size or state['model.embed_tokens.weight'].shape[1]
|
| 141 |
+
nl = args.num_hidden_layers or (max(int(k.split('.')[2]) for k in state if k.startswith('model.layers.')) + 1)
|
| 142 |
+
dtype = getattr(torch, args.dtype)
|
| 143 |
+
|
| 144 |
+
lm_config = LMConfig(hidden_size=hs, num_hidden_layers=nl,
|
| 145 |
+
max_seq_len=args.max_seq_len, use_moe=args.use_moe)
|
| 146 |
+
|
| 147 |
+
if args.mode == 'omni':
|
| 148 |
+
convert_torch2transformers_omni(args.torch_path, args.output_dir, dtype, args.tokenizer_path)
|
| 149 |
+
else:
|
| 150 |
+
convert_torch2transformers(args.torch_path, args.output_dir, dtype, args.tokenizer_path)
|
scripts/eval_llm.py
CHANGED
|
@@ -3,7 +3,7 @@ import argparse
|
|
| 3 |
import random
|
| 4 |
import warnings
|
| 5 |
import torch
|
| 6 |
-
from transformers import AutoTokenizer,
|
| 7 |
from models import LMConfig, LMForCausalLM
|
| 8 |
from models.lm.lora import * # noqa: F401,F403
|
| 9 |
from utils.training import setup_seed, get_model_params
|
|
@@ -27,7 +27,7 @@ def init_model(args):
|
|
| 27 |
apply_lora(model)
|
| 28 |
load_lora(model, f'./{args.save_dir}/{args.lora_weight}_{args.hidden_size}.pth')
|
| 29 |
else:
|
| 30 |
-
model =
|
| 31 |
get_model_params(model, model.config)
|
| 32 |
return model.half().eval().to(args.device), tokenizer
|
| 33 |
|
|
|
|
| 3 |
import random
|
| 4 |
import warnings
|
| 5 |
import torch
|
| 6 |
+
from transformers import AutoTokenizer, TextStreamer
|
| 7 |
from models import LMConfig, LMForCausalLM
|
| 8 |
from models.lm.lora import * # noqa: F401,F403
|
| 9 |
from utils.training import setup_seed, get_model_params
|
|
|
|
| 27 |
apply_lora(model)
|
| 28 |
load_lora(model, f'./{args.save_dir}/{args.lora_weight}_{args.hidden_size}.pth')
|
| 29 |
else:
|
| 30 |
+
model = LMForCausalLM.from_pretrained(args.load_from)
|
| 31 |
get_model_params(model, model.config)
|
| 32 |
return model.half().eval().to(args.device), tokenizer
|
| 33 |
|
scripts/serve_openai_api.py
CHANGED
|
@@ -218,7 +218,7 @@ async def chat_completions(request: ChatRequest):
|
|
| 218 |
"id": f"chatcmpl-{int(time.time())}",
|
| 219 |
"object": "chat.completion",
|
| 220 |
"created": int(time.time()),
|
| 221 |
-
"model": "
|
| 222 |
"choices": [
|
| 223 |
{
|
| 224 |
"index": 0,
|
|
|
|
| 218 |
"id": f"chatcmpl-{int(time.time())}",
|
| 219 |
"object": "chat.completion",
|
| 220 |
"created": int(time.time()),
|
| 221 |
+
"model": "omni",
|
| 222 |
"choices": [
|
| 223 |
{
|
| 224 |
"index": 0,
|
src/models/lm/config.py
CHANGED
|
@@ -3,7 +3,7 @@ from transformers import PretrainedConfig
|
|
| 3 |
|
| 4 |
|
| 5 |
class LMConfig(PretrainedConfig):
|
| 6 |
-
model_type = "
|
| 7 |
|
| 8 |
def __init__(self, hidden_size=768, num_hidden_layers=8, use_moe=False, **kwargs):
|
| 9 |
super().__init__(**kwargs)
|
|
@@ -17,25 +17,35 @@ class LMConfig(PretrainedConfig):
|
|
| 17 |
self.flash_attn = kwargs.get("flash_attn", True)
|
| 18 |
self.num_attention_heads = kwargs.get("num_attention_heads", 8)
|
| 19 |
self.num_key_value_heads = kwargs.get("num_key_value_heads", 4)
|
| 20 |
-
self.head_dim = kwargs.get(
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
self.max_position_embeddings = kwargs.get("max_position_embeddings", 32768)
|
| 24 |
self.rms_norm_eps = kwargs.get("rms_norm_eps", 1e-6)
|
| 25 |
self.rope_theta = kwargs.get("rope_theta", 1e6)
|
| 26 |
self.tie_word_embeddings = kwargs.get("tie_word_embeddings", True)
|
| 27 |
self.inference_rope_scaling = kwargs.get("inference_rope_scaling", False)
|
| 28 |
-
self.rope_scaling =
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# MoE specific configs (ignored if use_moe = False)
|
| 37 |
self.num_experts = kwargs.get("num_experts", 4)
|
| 38 |
self.num_experts_per_tok = kwargs.get("num_experts_per_tok", 1)
|
| 39 |
-
self.moe_intermediate_size = kwargs.get(
|
|
|
|
|
|
|
| 40 |
self.norm_topk_prob = kwargs.get("norm_topk_prob", True)
|
| 41 |
self.router_aux_loss_coef = kwargs.get("router_aux_loss_coef", 5e-4)
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
class LMConfig(PretrainedConfig):
|
| 6 |
+
model_type = "omni"
|
| 7 |
|
| 8 |
def __init__(self, hidden_size=768, num_hidden_layers=8, use_moe=False, **kwargs):
|
| 9 |
super().__init__(**kwargs)
|
|
|
|
| 17 |
self.flash_attn = kwargs.get("flash_attn", True)
|
| 18 |
self.num_attention_heads = kwargs.get("num_attention_heads", 8)
|
| 19 |
self.num_key_value_heads = kwargs.get("num_key_value_heads", 4)
|
| 20 |
+
self.head_dim = kwargs.get(
|
| 21 |
+
"head_dim", self.hidden_size // self.num_attention_heads
|
| 22 |
+
)
|
| 23 |
+
self.hidden_act = kwargs.get("hidden_act", "silu")
|
| 24 |
+
self.intermediate_size = kwargs.get(
|
| 25 |
+
"intermediate_size", math.ceil(hidden_size * math.pi / 64) * 64
|
| 26 |
+
)
|
| 27 |
self.max_position_embeddings = kwargs.get("max_position_embeddings", 32768)
|
| 28 |
self.rms_norm_eps = kwargs.get("rms_norm_eps", 1e-6)
|
| 29 |
self.rope_theta = kwargs.get("rope_theta", 1e6)
|
| 30 |
self.tie_word_embeddings = kwargs.get("tie_word_embeddings", True)
|
| 31 |
self.inference_rope_scaling = kwargs.get("inference_rope_scaling", False)
|
| 32 |
+
self.rope_scaling = (
|
| 33 |
+
{
|
| 34 |
+
"beta_fast": 32,
|
| 35 |
+
"beta_slow": 1,
|
| 36 |
+
"factor": 16,
|
| 37 |
+
"original_max_position_embeddings": 2048,
|
| 38 |
+
"attention_factor": 1.0,
|
| 39 |
+
"type": "yarn",
|
| 40 |
+
}
|
| 41 |
+
if self.inference_rope_scaling
|
| 42 |
+
else None
|
| 43 |
+
)
|
| 44 |
# MoE specific configs (ignored if use_moe = False)
|
| 45 |
self.num_experts = kwargs.get("num_experts", 4)
|
| 46 |
self.num_experts_per_tok = kwargs.get("num_experts_per_tok", 1)
|
| 47 |
+
self.moe_intermediate_size = kwargs.get(
|
| 48 |
+
"moe_intermediate_size", self.intermediate_size
|
| 49 |
+
)
|
| 50 |
self.norm_topk_prob = kwargs.get("norm_topk_prob", True)
|
| 51 |
self.router_aux_loss_coef = kwargs.get("router_aux_loss_coef", 5e-4)
|
src/models/vam/config.py
CHANGED
|
@@ -2,7 +2,7 @@ from models.lm.config import LMConfig
|
|
| 2 |
|
| 3 |
|
| 4 |
class VAMConfig(LMConfig):
|
| 5 |
-
model_type = "
|
| 6 |
|
| 7 |
def __init__(self, **kwargs):
|
| 8 |
super().__init__(**kwargs)
|
|
|
|
| 2 |
|
| 3 |
|
| 4 |
class VAMConfig(LMConfig):
|
| 5 |
+
model_type = "omni-o"
|
| 6 |
|
| 7 |
def __init__(self, **kwargs):
|
| 8 |
super().__init__(**kwargs)
|
src/models/vlm/config.py
CHANGED
|
@@ -2,7 +2,7 @@ from models.lm.config import LMConfig
|
|
| 2 |
|
| 3 |
|
| 4 |
class VLMConfig(LMConfig):
|
| 5 |
-
model_type = "
|
| 6 |
|
| 7 |
def __init__(self, image_special_token='<|image_pad|>', image_ids=[12], **kwargs):
|
| 8 |
self.image_special_token = image_special_token
|
|
|
|
| 2 |
|
| 3 |
|
| 4 |
class VLMConfig(LMConfig):
|
| 5 |
+
model_type = "omni-v"
|
| 6 |
|
| 7 |
def __init__(self, image_special_token='<|image_pad|>', image_ids=[12], **kwargs):
|
| 8 |
self.image_special_token = image_special_token
|
src/trainers/lm/rollout_engine.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"""Rollout engines for RL training.
|
| 2 |
|
| 3 |
To use the SGLang backend, first launch the server (with a transformers-format model):
|
| 4 |
-
python -m sglang.launch_server --model-path ./
|
| 5 |
"""
|
| 6 |
import os
|
| 7 |
import requests
|
|
|
|
| 1 |
"""Rollout engines for RL training.
|
| 2 |
|
| 3 |
To use the SGLang backend, first launch the server (with a transformers-format model):
|
| 4 |
+
python -m sglang.launch_server --model-path ./checkpoint/omni --attention-backend triton --host 0.0.0.0 --port 8998
|
| 5 |
"""
|
| 6 |
import os
|
| 7 |
import requests
|