Merge b7f58ce95e into ecd1ae1563
commit
511b317511
|
|
@ -249,7 +249,8 @@ class MoEGate(nn.Module):
|
||||||
self.reset_parameters()
|
self.reset_parameters()
|
||||||
|
|
||||||
def reset_parameters(self) -> None:
|
def reset_parameters(self) -> None:
|
||||||
init.kaiming_uniform_(self.weight, a=math.sqrt(5))
|
# init.kaiming_uniform_(self.weight, a=math.sqrt(5))
|
||||||
|
init.normal_(self.weight, mean=0.0, std=0.01)
|
||||||
|
|
||||||
def forward(self, hidden_states):
|
def forward(self, hidden_states):
|
||||||
bsz, seq_len, h = hidden_states.shape
|
bsz, seq_len, h = hidden_states.shape
|
||||||
|
|
@ -313,7 +314,7 @@ class MOEFeedForward(nn.Module):
|
||||||
flat_topk_idx = topk_idx.view(-1)
|
flat_topk_idx = topk_idx.view(-1)
|
||||||
if self.training:
|
if self.training:
|
||||||
x = x.repeat_interleave(self.config.num_experts_per_tok, dim=0)
|
x = x.repeat_interleave(self.config.num_experts_per_tok, dim=0)
|
||||||
y = torch.empty_like(x, dtype=torch.float16)
|
y = torch.empty_like(x, dtype=x.dtype)
|
||||||
for i, expert in enumerate(self.experts):
|
for i, expert in enumerate(self.experts):
|
||||||
y[flat_topk_idx == i] = expert(x[flat_topk_idx == i]).to(y.dtype) # 确保类型一致
|
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(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||||
|
import torch
|
||||||
|
|
||||||
|
model_path = "minimind/out/merged_model"
|
||||||
|
|
||||||
|
# 加载 tokenizer(通常不需要 trust_remote_code)
|
||||||
|
tokenizer = AutoTokenizer.from_pretrained(model_path)
|
||||||
|
print("EOS token:", tokenizer.eos_token)
|
||||||
|
print("EOS token ID:", tokenizer.eos_token_id)
|
||||||
|
print("PAD token ID:", tokenizer.pad_token_id)
|
||||||
|
|
||||||
|
# 加载模型:必须加 trust_remote_code=True
|
||||||
|
model = AutoModelForCausalLM.from_pretrained(
|
||||||
|
model_path,
|
||||||
|
dtype=torch.float16,
|
||||||
|
trust_remote_code=True
|
||||||
|
).to("cuda") # 或 "cpu"
|
||||||
|
|
||||||
|
# 推理
|
||||||
|
messages = [
|
||||||
|
{"role": "user", "content": "你好"}
|
||||||
|
]
|
||||||
|
prompt = tokenizer.apply_chat_template(
|
||||||
|
messages,
|
||||||
|
tokenize=False,
|
||||||
|
add_generation_prompt=True # ← 关键!添加生成触发符
|
||||||
|
)
|
||||||
|
print("Prompt:", repr(prompt))
|
||||||
|
inputs = tokenizer(
|
||||||
|
prompt,
|
||||||
|
return_tensors="pt",
|
||||||
|
return_token_type_ids=False # ← 关键!
|
||||||
|
).to(model.device)
|
||||||
|
# 关键:用 autocast 确保所有计算在 float16 下进行
|
||||||
|
with torch.autocast(device_type="cuda", dtype=torch.float16):
|
||||||
|
outputs = model.generate(
|
||||||
|
**inputs,
|
||||||
|
max_new_tokens=100,
|
||||||
|
do_sample=True,
|
||||||
|
temperature=0.3,
|
||||||
|
top_p=0.9
|
||||||
|
)
|
||||||
|
print("Generated token IDs:", outputs[0].tolist())
|
||||||
|
print("Decoded with special tokens:", tokenizer.decode(outputs[0], skip_special_tokens=False))
|
||||||
|
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
__package__ = "scripts"
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
import torch
|
||||||
|
import warnings
|
||||||
|
from transformers import AutoTokenizer
|
||||||
|
from model.model_minimind import MiniMindConfig, MiniMindForCausalLM
|
||||||
|
import argparse
|
||||||
|
warnings.filterwarnings('ignore', category=UserWarning)
|
||||||
|
|
||||||
|
# MiniMind 配置(需与训练时一致)
|
||||||
|
lm_config = MiniMindConfig(
|
||||||
|
hidden_size=512,
|
||||||
|
num_hidden_layers=8,
|
||||||
|
max_position_embeddings=32768,
|
||||||
|
vocab_size=6400, # 确保与 tokenizer 一致
|
||||||
|
use_moe=False,
|
||||||
|
num_attention_heads=8,
|
||||||
|
num_key_value_heads=2,
|
||||||
|
rms_norm_eps=1e-5,
|
||||||
|
rope_theta=10000.0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def merge_lora_weights(model, lora_state_dict):
|
||||||
|
"""
|
||||||
|
将 LoRA 权重合并到基础模型中
|
||||||
|
LoRA 权重格式: {module_name}.lora.A.weight 和 {module_name}.lora.B.weight
|
||||||
|
合并公式: W_new = W_old + B @ A
|
||||||
|
|
||||||
|
注意: 根据 model_lora.py,LoRA 只应用于方阵线性层 (shape[0] == shape[1])
|
||||||
|
"""
|
||||||
|
# 清理 state_dict 中的 'module.' 前缀(如果有)
|
||||||
|
cleaned_lora_dict = {k.replace('module.', ''): v for k, v in lora_state_dict.items()}
|
||||||
|
|
||||||
|
# 收集所有 LoRA 权重
|
||||||
|
lora_weights = {}
|
||||||
|
for key in cleaned_lora_dict.keys():
|
||||||
|
if '.lora.' in key:
|
||||||
|
# 提取模块名称和权重类型
|
||||||
|
parts = key.split('.lora.')
|
||||||
|
if len(parts) == 2:
|
||||||
|
module_name = parts[0]
|
||||||
|
weight_type = parts[1] # 'A.weight' 或 'B.weight'
|
||||||
|
|
||||||
|
if module_name not in lora_weights:
|
||||||
|
lora_weights[module_name] = {}
|
||||||
|
lora_weights[module_name][weight_type] = cleaned_lora_dict[key]
|
||||||
|
|
||||||
|
print(f"找到 {len(lora_weights)} 个应用了 LoRA 的模块")
|
||||||
|
|
||||||
|
# 合并 LoRA 权重到对应的线性层
|
||||||
|
merged_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
|
||||||
|
for name, module in model.named_modules():
|
||||||
|
# 只处理线性层
|
||||||
|
if isinstance(module, torch.nn.Linear):
|
||||||
|
# 检查是否有对应的 LoRA 权重
|
||||||
|
if name in lora_weights:
|
||||||
|
lora_data = lora_weights[name]
|
||||||
|
if 'A.weight' in lora_data and 'B.weight' in lora_data:
|
||||||
|
A = lora_data['A.weight'] # shape: [rank, in_features]
|
||||||
|
B = lora_data['B.weight'] # shape: [out_features, rank]
|
||||||
|
|
||||||
|
# 验证形状匹配
|
||||||
|
original_weight = module.weight.data # shape: [out_features, in_features]
|
||||||
|
expected_out, expected_in = original_weight.shape
|
||||||
|
|
||||||
|
# A: [rank, in_features], B: [out_features, rank]
|
||||||
|
if A.shape[1] != expected_in or B.shape[0] != expected_out:
|
||||||
|
print(f" ⚠ 跳过 {name}: 形状不匹配 (A: {A.shape}, B: {B.shape}, 期望: [{expected_out}, {expected_in}])")
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 合并: W_new = W_old + B @ A
|
||||||
|
# B @ A: [out_features, rank] @ [rank, in_features] = [out_features, in_features]
|
||||||
|
lora_delta = torch.matmul(B, A) # shape: [out_features, in_features]
|
||||||
|
module.weight.data = original_weight + lora_delta.to(original_weight.device)
|
||||||
|
merged_count += 1
|
||||||
|
print(f" ✓ 合并 LoRA 权重到: {name} (形状: {original_weight.shape}, rank: {A.shape[0]})")
|
||||||
|
|
||||||
|
print(f"成功合并 {merged_count} 个 LoRA 权重")
|
||||||
|
if skipped_count > 0:
|
||||||
|
print(f"跳过 {skipped_count} 个不匹配的权重")
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
def merge_lora_to_minimind(lora_model_path, base_model_path, output_path, config=None, dtype=torch.float16):
|
||||||
|
"""
|
||||||
|
将 MiniMind + LoRA 权重合并并保存为 safetensors 格式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
lora_model_path: LoRA 权重文件路径 (.pth)
|
||||||
|
base_model_path: 基础模型文件路径 (.pth)
|
||||||
|
output_path: 输出目录路径
|
||||||
|
config: MiniMindConfig 配置对象
|
||||||
|
dtype: 输出模型的数据类型
|
||||||
|
"""
|
||||||
|
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
||||||
|
|
||||||
|
# 使用提供的配置或默认配置
|
||||||
|
if config is None:
|
||||||
|
config = lm_config
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print("开始合并 LoRA 权重到 MiniMind 模型")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# 1. 加载基础模型
|
||||||
|
print(f"\n1. 加载基础模型: {base_model_path}")
|
||||||
|
model = MiniMindForCausalLM(config)
|
||||||
|
|
||||||
|
if os.path.isfile(base_model_path):
|
||||||
|
state_dict = torch.load(base_model_path, map_location='cpu')
|
||||||
|
|
||||||
|
# 如果 checkpoint 包含 'model' 键,则提取模型权重
|
||||||
|
if isinstance(state_dict, dict) and 'model' in state_dict:
|
||||||
|
state_dict = state_dict['model']
|
||||||
|
print(" ℹ 检测到完整 checkpoint,提取模型权重")
|
||||||
|
|
||||||
|
# 清理 state_dict 中的 'module.' 前缀(如果有,用于 DDP 模型)
|
||||||
|
cleaned_state_dict = {k.replace('module.', ''): v for k, v in state_dict.items()}
|
||||||
|
|
||||||
|
# 过滤出模型相关的键(排除 optimizer、scheduler 等)
|
||||||
|
model_keys = set(model.state_dict().keys())
|
||||||
|
filtered_state_dict = {k: v for k, v in cleaned_state_dict.items() if k in model_keys}
|
||||||
|
|
||||||
|
missing_keys, unexpected_keys = model.load_state_dict(filtered_state_dict, strict=False)
|
||||||
|
print(" ✓ 基础模型加载完成")
|
||||||
|
if missing_keys:
|
||||||
|
print(f" ⚠ 警告: {len(missing_keys)} 个键未找到(已忽略)")
|
||||||
|
if unexpected_keys:
|
||||||
|
print(f" ℹ 信息: {len(unexpected_keys)} 个额外键(已忽略)")
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"基础模型文件不存在: {base_model_path}")
|
||||||
|
|
||||||
|
# 2. 加载 LoRA 权重
|
||||||
|
print(f"\n2. 加载 LoRA 权重: {lora_model_path}")
|
||||||
|
if not os.path.isfile(lora_model_path):
|
||||||
|
raise FileNotFoundError(f"LoRA 权重文件不存在: {lora_model_path}")
|
||||||
|
|
||||||
|
lora_state_dict = torch.load(lora_model_path, map_location='cpu')
|
||||||
|
print(f" ✓ LoRA 权重加载完成 (包含 {len(lora_state_dict)} 个键)")
|
||||||
|
|
||||||
|
# 3. 合并 LoRA 权重
|
||||||
|
print(f"\n3. 合并 LoRA 权重到基础模型...")
|
||||||
|
model = merge_lora_weights(model, lora_state_dict)
|
||||||
|
|
||||||
|
# 4. 转换精度
|
||||||
|
print(f"\n4. 转换模型精度为 {dtype}...")
|
||||||
|
model = model.to(dtype)
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
# 5. 保存模型为 safetensors 格式
|
||||||
|
print(f"\n5. 保存模型到: {output_path}")
|
||||||
|
os.makedirs(output_path, exist_ok=True)
|
||||||
|
|
||||||
|
# 注册模型类以便 transformers 能够识别
|
||||||
|
MiniMindConfig.register_for_auto_class()
|
||||||
|
MiniMindForCausalLM.register_for_auto_class("AutoModelForCausalLM")
|
||||||
|
|
||||||
|
# 保存模型(使用 safe_serialization=True 保存为 safetensors)
|
||||||
|
model.save_pretrained(output_path, safe_serialization=False)
|
||||||
|
print(" ✓ 模型已保存为 safetensors 格式")
|
||||||
|
|
||||||
|
# 6. 保存 tokenizer
|
||||||
|
print(f"\n6. 保存 tokenizer...")
|
||||||
|
tokenizer_path = os.path.join(os.path.dirname(__file__), '..', 'model')
|
||||||
|
if os.path.exists(tokenizer_path):
|
||||||
|
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
|
||||||
|
tokenizer.save_pretrained(output_path)
|
||||||
|
print(" ✓ Tokenizer 保存完成")
|
||||||
|
else:
|
||||||
|
print(f" ⚠ 警告: Tokenizer 路径不存在: {tokenizer_path}")
|
||||||
|
|
||||||
|
# 7. 打印参数量
|
||||||
|
print(f"\n7. 模型信息:")
|
||||||
|
model_params = sum(p.numel() for p in model.parameters())
|
||||||
|
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
|
||||||
|
print(f" 总参数量: {model_params / 1e6:.2f} M = {model_params / 1e9:.2f} B")
|
||||||
|
print(f" 可训练参数: {trainable_params / 1e6:.2f} M")
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print(f"✅ LoRA 已合并并保存到: {output_path}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 配置路径
|
||||||
|
parser = argparse.ArgumentParser(description="Merge LoRA weights into base MiniMind model")
|
||||||
|
parser.add_argument(
|
||||||
|
"--lora_model_path",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
# default="/home/dieu/minimind/out/lora/lora_identity_512.pth",
|
||||||
|
help="Path to the LoRA weights (.pth file)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--base_model_path",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
# default="/home/dieu/minimind/out/full_sft_512.pth",
|
||||||
|
help="Path to the base model (.pth file)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output_path",
|
||||||
|
type=str,
|
||||||
|
required=True,
|
||||||
|
# default="/home/dieu/minimind/out/merged_model_test",
|
||||||
|
help="Directory to save the merged model"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dtype",
|
||||||
|
type=str,
|
||||||
|
default="float16",
|
||||||
|
choices=["float16", "float32", "bfloat16"],
|
||||||
|
help="Data type for the merged model (default: float16)"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 映射 dtype 字符串到 torch.dtype
|
||||||
|
dtype_map = {
|
||||||
|
"float16": torch.float16,
|
||||||
|
"float32": torch.float32,
|
||||||
|
"bfloat16": torch.bfloat16,
|
||||||
|
}
|
||||||
|
dtype = dtype_map[args.dtype]
|
||||||
|
# 调用合并函数
|
||||||
|
merge_lora_to_minimind(
|
||||||
|
lora_model_path=args.lora_model_path,
|
||||||
|
base_model_path=args.base_model_path,
|
||||||
|
output_path=args.output_path,
|
||||||
|
config=lm_config,
|
||||||
|
dtype=dtype
|
||||||
|
)
|
||||||
|
|
@ -27,19 +27,32 @@ def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
X = X.to(args.device)
|
X = X.to(args.device)
|
||||||
Y = Y.to(args.device)
|
Y = Y.to(args.device)
|
||||||
loss_mask = loss_mask.to(args.device)
|
loss_mask = loss_mask.to(args.device)
|
||||||
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
# lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
||||||
|
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate, args.warmup_steps, args.warmup_ratio)
|
||||||
for param_group in optimizer.param_groups:
|
for param_group in optimizer.param_groups:
|
||||||
param_group['lr'] = lr
|
param_group['lr'] = lr
|
||||||
|
|
||||||
with autocast_ctx:
|
with autocast_ctx:
|
||||||
res = model(X)
|
res = model(X)
|
||||||
loss = loss_fct(
|
# loss = loss_fct(
|
||||||
|
# res.logits.view(-1, res.logits.size(-1)),
|
||||||
|
# Y.view(-1)
|
||||||
|
# ).view(Y.size())
|
||||||
|
#
|
||||||
|
# loss = (loss * loss_mask).sum() / loss_mask.sum()
|
||||||
|
# loss += res.aux_loss
|
||||||
|
# loss = loss / args.accumulation_steps
|
||||||
|
# --- 【新增监控 1】拆分 Loss 方便观察 ---
|
||||||
|
raw_loss = loss_fct(
|
||||||
res.logits.view(-1, res.logits.size(-1)),
|
res.logits.view(-1, res.logits.size(-1)),
|
||||||
Y.view(-1)
|
Y.view(-1)
|
||||||
).view(Y.size())
|
).view(Y.size())
|
||||||
|
|
||||||
loss = (loss * loss_mask).sum() / loss_mask.sum()
|
main_loss = (raw_loss * loss_mask).sum() / loss_mask.sum()
|
||||||
loss += res.aux_loss
|
aux_loss = res.aux_loss # 获取辅助损失
|
||||||
|
|
||||||
|
# 这里的比例很关键,如果 aux_loss 比 main_loss 还大,模型就练歪了
|
||||||
|
loss = main_loss + aux_loss
|
||||||
loss = loss / args.accumulation_steps
|
loss = loss / args.accumulation_steps
|
||||||
|
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
|
|
@ -59,10 +72,42 @@ def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
current_loss = loss.item() * args.accumulation_steps
|
current_loss = loss.item() * args.accumulation_steps
|
||||||
current_lr = optimizer.param_groups[-1]['lr']
|
current_lr = optimizer.param_groups[-1]['lr']
|
||||||
eta_min = spend_time / (step + 1) * iters // 60 - spend_time // 60
|
eta_min = spend_time / (step + 1) * iters // 60 - spend_time // 60
|
||||||
|
|
||||||
Logger(f'Epoch:[{epoch+1}/{args.epochs}]({step}/{iters}) loss:{current_loss:.6f} lr:{current_lr:.12f} epoch_Time:{eta_min}min:')
|
# --- 【新增监控 2】打印 Gate 梯度和 Aux Loss ---
|
||||||
|
# 1. 解包 DDP 模型,获取原始 model
|
||||||
if wandb: wandb.log({"loss": current_loss, "lr": current_lr, "epoch_Time": eta_min})
|
raw_model = model.module if hasattr(model, 'module') else model
|
||||||
|
|
||||||
|
# 2. 统计 MoE Gate 的梯度范数 (检查路由是否还有梯度)
|
||||||
|
gate_grad_sum = 0.0
|
||||||
|
gate_count = 0
|
||||||
|
for name, param in raw_model.named_parameters():
|
||||||
|
# 筛选出所有门控层的权重
|
||||||
|
if "gate.weight" in name and param.grad is not None:
|
||||||
|
gate_grad_sum += param.grad.norm().item()
|
||||||
|
gate_count += 1
|
||||||
|
avg_gate_grad = gate_grad_sum / gate_count if gate_count > 0 else 0.0
|
||||||
|
|
||||||
|
# 3. 打印详细日志
|
||||||
|
# Main: 主任务Loss (越低越好)
|
||||||
|
# Aux: 负载均衡Loss (太低说明可能只顾着平均分配了,太高说明完全没分配好)
|
||||||
|
# G_Grad: Gate梯度 (如果是 0.0000,说明路由彻底死了,没有任何学习)
|
||||||
|
debug_info = f"Main:{main_loss.item():.4f} Aux:{aux_loss.item():.4f} G_Grad:{avg_gate_grad:.6f}"
|
||||||
|
|
||||||
|
Logger(
|
||||||
|
f'Epoch:[{epoch + 1}/{args.epochs}]({step}/{iters}) Loss:{current_loss:.4f} {debug_info} lr:{current_lr:.8f} Time:{eta_min}m')
|
||||||
|
|
||||||
|
if wandb:
|
||||||
|
wandb.log({
|
||||||
|
"total_loss": current_loss,
|
||||||
|
"main_loss": main_loss.item(),
|
||||||
|
"aux_loss": aux_loss.item(),
|
||||||
|
"gate_grad_norm": avg_gate_grad,
|
||||||
|
"lr": current_lr,
|
||||||
|
"epoch_Time": eta_min
|
||||||
|
})
|
||||||
|
# Logger(f'Epoch:[{epoch+1}/{args.epochs}]({step}/{iters}) loss:{current_loss:.6f} lr:{current_lr:.12f} epoch_Time:{eta_min}min:')
|
||||||
|
#
|
||||||
|
# if wandb: wandb.log({"loss": current_loss, "lr": current_lr, "epoch_Time": eta_min})
|
||||||
|
|
||||||
if (step % args.save_interval == 0 or step == iters - 1) and is_main_process():
|
if (step % args.save_interval == 0 or step == iters - 1) and is_main_process():
|
||||||
model.eval()
|
model.eval()
|
||||||
|
|
@ -85,25 +130,28 @@ if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind Pretraining")
|
parser = argparse.ArgumentParser(description="MiniMind Pretraining")
|
||||||
parser.add_argument("--save_dir", type=str, default="../out", help="模型保存目录")
|
parser.add_argument("--save_dir", type=str, default="../out", help="模型保存目录")
|
||||||
parser.add_argument('--save_weight', default='pretrain', type=str, help="保存权重的前缀名")
|
parser.add_argument('--save_weight', default='pretrain', type=str, help="保存权重的前缀名")
|
||||||
parser.add_argument("--epochs", type=int, default=1, help="训练轮数(建议1轮zero或2-6轮充分训练)")
|
parser.add_argument("--epochs", type=int, default=3, help="训练轮数(建议1轮zero或2-6轮充分训练)")
|
||||||
parser.add_argument("--batch_size", type=int, default=32, help="batch size")
|
parser.add_argument("--batch_size", type=int, default=64, help="batch size")
|
||||||
parser.add_argument("--learning_rate", type=float, default=5e-4, help="初始学习率")
|
parser.add_argument("--learning_rate", type=float, default=1e-4, help="初始学习率")
|
||||||
parser.add_argument("--device", type=str, default="cuda:0" if torch.cuda.is_available() else "cpu", help="训练设备")
|
parser.add_argument("--device", type=str, default="cuda:0" if torch.cuda.is_available() else "cpu", help="训练设备")
|
||||||
parser.add_argument("--dtype", type=str, default="bfloat16", help="混合精度类型")
|
parser.add_argument("--dtype", type=str, default="bfloat16", help="混合精度类型")
|
||||||
parser.add_argument("--num_workers", type=int, default=1, help="数据加载线程数")
|
parser.add_argument("--num_workers", type=int, default=64, help="数据加载线程数")
|
||||||
parser.add_argument("--accumulation_steps", type=int, default=8, help="梯度累积步数")
|
parser.add_argument("--accumulation_steps", type=int, default=16, help="梯度累积步数")
|
||||||
parser.add_argument("--grad_clip", type=float, default=1.0, help="梯度裁剪阈值")
|
parser.add_argument("--grad_clip", type=float, default=1, help="梯度裁剪阈值")
|
||||||
parser.add_argument("--log_interval", type=int, default=100, help="日志打印间隔")
|
parser.add_argument("--log_interval", type=int, default=100, help="日志打印间隔")
|
||||||
parser.add_argument("--save_interval", type=int, default=100, help="模型保存间隔")
|
parser.add_argument("--save_interval", type=int, default=500, help="模型保存间隔")
|
||||||
parser.add_argument('--hidden_size', default=512, type=int, help="隐藏层维度")
|
parser.add_argument('--hidden_size', default=640, type=int, help="隐藏层维度")
|
||||||
parser.add_argument('--num_hidden_layers', default=8, type=int, help="隐藏层数量")
|
parser.add_argument('--num_hidden_layers', default=8, type=int, help="隐藏层数量")
|
||||||
parser.add_argument('--max_seq_len', default=512, type=int, help="训练的最大截断长度")
|
parser.add_argument('--max_seq_len', default=512, type=int, help="训练的最大截断长度")
|
||||||
parser.add_argument('--use_moe', default=0, type=int, choices=[0, 1], help="是否使用MoE架构(0=否,1=是)")
|
parser.add_argument('--use_moe', default=1, type=int, choices=[0, 1], help="是否使用MoE架构(0=否,1=是)")
|
||||||
parser.add_argument("--data_path", type=str, default="../dataset/pretrain_hq.jsonl", help="预训练数据路径")
|
parser.add_argument("--data_path", type=str, default="../dataset/pretrain_hq.jsonl", help="预训练数据路径")
|
||||||
parser.add_argument('--from_weight', default='none', type=str, help="基于哪个权重训练,为none则从头开始")
|
parser.add_argument('--from_weight', default='none', type=str, help="基于哪个权重训练,为none则从头开始")
|
||||||
parser.add_argument('--from_resume', default=0, type=int, choices=[0, 1], help="是否自动检测&续训(0=否,1=是)")
|
parser.add_argument('--from_resume', default=0, type=int, choices=[0, 1], help="是否自动检测&续训(0=否,1=是)")
|
||||||
parser.add_argument("--use_wandb", action="store_true", help="是否使用wandb")
|
parser.add_argument("--use_wandb", action="store_true", help="是否使用wandb")
|
||||||
parser.add_argument("--wandb_project", type=str, default="MiniMind-Pretrain", help="wandb项目名")
|
parser.add_argument("--wandb_project", type=str, default="MiniMind-Pretrain-640-moe-wramup", help="wandb项目名")
|
||||||
|
parser.add_argument("--warmup_steps", type=int, default=3000, help="Warmup步数,0表示不使用warmup(建议为总步数的1-5%)")
|
||||||
|
parser.add_argument("--warmup_ratio", type=float, default=0.01,
|
||||||
|
help="Warmup起始学习率比例,起始lr = lr * warmup_ratio")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# ========== 1. 初始化环境和随机种子 ==========
|
# ========== 1. 初始化环境和随机种子 ==========
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,32 @@ def Logger(content):
|
||||||
print(content)
|
print(content)
|
||||||
|
|
||||||
|
|
||||||
def get_lr(current_step, total_steps, lr):
|
def get_lr(current_step, total_steps, lr, warmup_steps=0, warmup_ratio=0.01):
|
||||||
return lr / 10 + 0.5 * lr * (1 + math.cos(math.pi * current_step / total_steps))
|
"""
|
||||||
|
学习率调度:Warmup + Cosine Decay
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_step: 当前步数
|
||||||
|
total_steps: 总步数
|
||||||
|
lr: 目标学习率
|
||||||
|
warmup_steps: warmup步数,0表示不使用warmup
|
||||||
|
warmup_ratio: warmup起始学习率比例,起始lr = lr * warmup_ratio
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
当前步数对应的学习率
|
||||||
|
"""
|
||||||
|
if warmup_steps > 0 and current_step < warmup_steps:
|
||||||
|
# Warmup阶段:线性从 warmup_ratio * lr 增长到 lr
|
||||||
|
return warmup_ratio * lr + (1 - warmup_ratio) * lr * (current_step / warmup_steps)
|
||||||
|
else:
|
||||||
|
# Cosine Decay阶段:从 lr 按余弦函数衰减到 lr / 10
|
||||||
|
if warmup_steps > 0:
|
||||||
|
cosine_steps = current_step - warmup_steps
|
||||||
|
cosine_total = total_steps - warmup_steps
|
||||||
|
else:
|
||||||
|
cosine_steps = current_step
|
||||||
|
cosine_total = total_steps
|
||||||
|
return lr / 10 + 0.5 * lr * (1 + math.cos(math.pi * cosine_steps / cosine_total))
|
||||||
|
|
||||||
|
|
||||||
def init_distributed_mode():
|
def init_distributed_mode():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue