Compare commits
10 Commits
5118b8c96e
...
f4d34d38e7
| Author | SHA1 | Date |
|---|---|---|
|
|
f4d34d38e7 | |
|
|
f659b55761 | |
|
|
6fff76fe04 | |
|
|
7da0f92ff6 | |
|
|
a6ffddc0ea | |
|
|
4b86522f8a | |
|
|
1e6e909f88 | |
|
|
b00be3e5ca | |
|
|
182bff1bd8 | |
|
|
743420daac |
|
|
@ -3,4 +3,10 @@ __pycache__/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
out
|
out
|
||||||
website/
|
website/
|
||||||
docs-minimind/
|
docs-minimind/
|
||||||
|
|
||||||
|
checkpoints/
|
||||||
|
dataset/
|
||||||
|
|
||||||
|
minimind-3/
|
||||||
|
minimind-3-moe/
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ def init_model(args):
|
||||||
model.load_state_dict(torch.load(ckp, map_location=args.device), strict=True)
|
model.load_state_dict(torch.load(ckp, map_location=args.device), strict=True)
|
||||||
if args.lora_weight != 'None':
|
if args.lora_weight != 'None':
|
||||||
apply_lora(model)
|
apply_lora(model)
|
||||||
load_lora(model, f'./{args.save_dir}/{args.lora_weight}_{args.hidden_size}.pth')
|
load_lora(model, f'./{args.save_dir}/{args.lora_weight}_{args.hidden_size}{moe_suffix}.pth')
|
||||||
else:
|
else:
|
||||||
model = AutoModelForCausalLM.from_pretrained(args.load_from, trust_remote_code=True)
|
model = AutoModelForCausalLM.from_pretrained(args.load_from, trust_remote_code=True)
|
||||||
get_model_params(model, model.config)
|
get_model_params(model, model.config)
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,6 @@ class Attention(nn.Module):
|
||||||
if self.flash and (seq_len > 1) and (not self.is_causal or past_key_value is None) and (attention_mask is None or torch.all(attention_mask == 1)):
|
if self.flash and (seq_len > 1) and (not self.is_causal or past_key_value is None) and (attention_mask is None or torch.all(attention_mask == 1)):
|
||||||
output = F.scaled_dot_product_attention(xq, xk, xv, dropout_p=self.dropout if self.training else 0.0, is_causal=self.is_causal)
|
output = F.scaled_dot_product_attention(xq, xk, xv, dropout_p=self.dropout if self.training else 0.0, is_causal=self.is_causal)
|
||||||
else:
|
else:
|
||||||
# 先转 float32 再加 mask:-1e9 超出 float16 的表示范围(min≈-65504),
|
|
||||||
# 在 float16 张量上累加会饱和成 -inf。若某个 query 在因果范围内可见的
|
|
||||||
# key 全被 mask 掉(左 padding 时必然出现),softmax 整行就是 NaN,
|
|
||||||
# 并在下一层通过 K/V 投影扩散到同序列里有效的 token 上。
|
|
||||||
scores = (xq @ xk.transpose(-2, -1)).float() / math.sqrt(self.head_dim)
|
scores = (xq @ xk.transpose(-2, -1)).float() / math.sqrt(self.head_dim)
|
||||||
if self.is_causal: scores[:, :, :, -seq_len:] += torch.full((seq_len, seq_len), float("-inf"), device=scores.device).triu(1)
|
if self.is_causal: scores[:, :, :, -seq_len:] += torch.full((seq_len, seq_len), float("-inf"), device=scores.device).triu(1)
|
||||||
if attention_mask is not None: scores += (1.0 - attention_mask.unsqueeze(1).unsqueeze(2)) * -1e9
|
if attention_mask is not None: scores += (1.0 - attention_mask.unsqueeze(1).unsqueeze(2)) * -1e9
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# .gitignore 忽略下列文件和目录
|
||||||
|
|
||||||
|
#比如minimind\scripts\minimind-3和minimind\scripts\minimind-3-moe
|
||||||
|
minimind-3/
|
||||||
|
minimind-3-moe/
|
||||||
|
|
@ -40,7 +40,7 @@ def init_model(args):
|
||||||
model.load_state_dict(torch.load(ckp, map_location=device), strict=True)
|
model.load_state_dict(torch.load(ckp, map_location=device), strict=True)
|
||||||
if args.lora_weight != 'None':
|
if args.lora_weight != 'None':
|
||||||
apply_lora(model)
|
apply_lora(model)
|
||||||
load_lora(model, f'../{args.save_dir}/lora/{args.lora_weight}_{args.hidden_size}.pth')
|
load_lora(model, f'../{args.save_dir}/{args.lora_weight}_{args.hidden_size}{moe_suffix}.pth')
|
||||||
else:
|
else:
|
||||||
model = AutoModelForCausalLM.from_pretrained(args.load_from, trust_remote_code=True)
|
model = AutoModelForCausalLM.from_pretrained(args.load_from, trust_remote_code=True)
|
||||||
print(f'MiniMind模型参数量: {sum(p.numel() for p in model.parameters()) / 1e6:.2f} M(illion)')
|
print(f'MiniMind模型参数量: {sum(p.numel() for p in model.parameters()) / 1e6:.2f} M(illion)')
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,28 @@ st.markdown("""
|
||||||
transition: all 0.2s ease !important;
|
transition: all 0.2s ease !important;
|
||||||
margin: 0 2px !important; /* 调整这里的 margin 值 */
|
margin: 0 2px !important; /* 调整这里的 margin 值 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 单独恢复侧边栏按钮(新建对话)的正常横向长方形外观,避免文字被挤成竖排 */
|
||||||
|
[data-testid="stSidebar"] .stButton > button {
|
||||||
|
border-radius: 8px !important; /* 恢复稍微圆角的矩形 */
|
||||||
|
width: 100% !important; /* 撑满侧边栏宽度 */
|
||||||
|
height: auto !important;
|
||||||
|
min-width: 100% !important; /* 覆盖掉 18px 的限制 */
|
||||||
|
min-height: 40px !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
max-height: none !important;
|
||||||
|
padding: 8px !important; /* 恢复内边距 */
|
||||||
|
background-color: #f4f6f8 !important; /* 增加一点按钮底色以免透明看不清 */
|
||||||
|
border: 1px solid #e0e0e0 !important;
|
||||||
|
font-size: 15px !important;
|
||||||
|
color: #333 !important;
|
||||||
|
margin: 10px 0 !important;
|
||||||
|
}
|
||||||
|
[data-testid="stSidebar"] .stButton > button:hover {
|
||||||
|
border-color: #999 !important;
|
||||||
|
background-color: #e4e8f0 !important;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
""", unsafe_allow_html=True)
|
""", unsafe_allow_html=True)
|
||||||
|
|
@ -87,6 +109,7 @@ LANG_TEXTS = {
|
||||||
'disclaimer': 'AI 生成内容可能存在错误,请仔细核实',
|
'disclaimer': 'AI 生成内容可能存在错误,请仔细核实',
|
||||||
'think_tip': '自适应思考,目前多轮对话或Tool Call共存时思考不稳定',
|
'think_tip': '自适应思考,目前多轮对话或Tool Call共存时思考不稳定',
|
||||||
'tool_select': '工具选择(最多4个)',
|
'tool_select': '工具选择(最多4个)',
|
||||||
|
'new_chat': '新建对话', # 新增
|
||||||
},
|
},
|
||||||
'en': {
|
'en': {
|
||||||
'settings': 'Model Settings',
|
'settings': 'Model Settings',
|
||||||
|
|
@ -100,6 +123,7 @@ LANG_TEXTS = {
|
||||||
'disclaimer': 'AI-generated content may be inaccurate, please verify',
|
'disclaimer': 'AI-generated content may be inaccurate, please verify',
|
||||||
'think_tip': 'Adaptive thinking; may be unstable with multi-turn or Tool Call',
|
'think_tip': 'Adaptive thinking; may be unstable with multi-turn or Tool Call',
|
||||||
'tool_select': 'Tool Selection (max 4)',
|
'tool_select': 'Tool Selection (max 4)',
|
||||||
|
'new_chat': 'New Chat', # 新增
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,15 +265,20 @@ def regenerate_answer(index):
|
||||||
|
|
||||||
|
|
||||||
# 动态扫描模型目录
|
# 动态扫描模型目录
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
@st.cache_data(ttl=3600) # 增加缓存(有效期1小时),避免每次刷新页面都去高频读取硬盘文件树
|
||||||
MODEL_PATHS = {}
|
def get_model_paths():
|
||||||
for d in sorted(os.listdir(script_dir), reverse=True):
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
full_path = os.path.join(script_dir, d)
|
paths = {}
|
||||||
if os.path.isdir(full_path) and not d.startswith('.') and not d.startswith('_'):
|
for d in sorted(os.listdir(script_dir), reverse=True):
|
||||||
if any(f.endswith(('.bin', '.safetensors', '.pt')) or os.path.exists(os.path.join(full_path, 'model.safetensors.index.json')) for f in os.listdir(full_path) if os.path.isfile(os.path.join(full_path, f))):
|
full_path = os.path.join(script_dir, d)
|
||||||
MODEL_PATHS[d] = [d, d]
|
if os.path.isdir(full_path) and not d.startswith('.') and not d.startswith('_'):
|
||||||
if not MODEL_PATHS:
|
if any(f.endswith(('.bin', '.safetensors', '.pt')) or os.path.exists(os.path.join(full_path, 'model.safetensors.index.json')) for f in os.listdir(full_path) if os.path.isfile(os.path.join(full_path, f))):
|
||||||
MODEL_PATHS = {"No models found": ["", "No models"]}
|
paths[d] =[d, d]
|
||||||
|
if not paths:
|
||||||
|
paths = {"No models found": ["", "No models"]}
|
||||||
|
return paths
|
||||||
|
|
||||||
|
MODEL_PATHS = get_model_paths()
|
||||||
|
|
||||||
# 模型选择
|
# 模型选择
|
||||||
selected_model = st.sidebar.selectbox('Model', list(MODEL_PATHS.keys()), index=0)
|
selected_model = st.sidebar.selectbox('Model', list(MODEL_PATHS.keys()), index=0)
|
||||||
|
|
@ -289,7 +318,27 @@ with st.sidebar.expander(get_text('tools')):
|
||||||
if checked and len(st.session_state.selected_tools) < 4:
|
if checked and len(st.session_state.selected_tools) < 4:
|
||||||
st.session_state.selected_tools.append(name)
|
st.session_state.selected_tools.append(name)
|
||||||
|
|
||||||
image_url = "https://www.modelscope.cn/api/v1/studio/gongjy/MiniMind/repo?Revision=master&FilePath=images%2Flogo2.png&View=true"
|
# 新建对话
|
||||||
|
st.sidebar.markdown('<hr style="margin: 12px 0 16px 0;">', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
# 新建对话
|
||||||
|
if st.sidebar.button(get_text('new_chat'), key="new_chat", use_container_width=True):
|
||||||
|
clear_chat_messages()
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
# 将外部网络图片改为读取本地图片转 base64 渲染
|
||||||
|
import base64
|
||||||
|
@st.cache_data
|
||||||
|
def get_image_base64(image_path):
|
||||||
|
try:
|
||||||
|
with open(image_path, "rb") as img_file:
|
||||||
|
return f"data:image/png;base64,{base64.b64encode(img_file.read()).decode()}"
|
||||||
|
except:
|
||||||
|
# 如果本地图片不存在,再退回使用网络URL
|
||||||
|
return "https://www.modelscope.cn/api/v1/studio/gongjy/MiniMind/repo?Revision=master&FilePath=images%2Flogo2.png&View=true"
|
||||||
|
|
||||||
|
local_logo_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "images", "logo2.png")
|
||||||
|
image_url = get_image_base64(local_logo_path)
|
||||||
|
|
||||||
st.markdown(
|
st.markdown(
|
||||||
f'<div style="display: flex; flex-direction: column; align-items: center; text-align: center; margin: 0; padding: 0;">'
|
f'<div style="display: flex; flex-direction: column; align-items: center; text-align: center; margin: 0; padding: 0;">'
|
||||||
|
|
|
||||||
|
|
@ -240,12 +240,10 @@ def calculate_rewards(prompts, completions, gt_batch, tools_batch, num_gen, rewa
|
||||||
|
|
||||||
# ================================ 工具与 Reward = End ================================
|
# ================================ 工具与 Reward = End ================================
|
||||||
def rl_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_model=None, start_step=0, wandb=None, use_sglang=False):
|
def rl_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_model=None, start_step=0, wandb=None, use_sglang=False):
|
||||||
last_step = start_step
|
|
||||||
for step, batch in enumerate(loader, start=start_step + 1):
|
for step, batch in enumerate(loader, start=start_step + 1):
|
||||||
messages_batch = batch['messages']
|
messages_batch = batch['messages']
|
||||||
tools_batch = batch['tools']
|
tools_batch = batch['tools']
|
||||||
gt_batch = batch['gt']
|
gt_batch = batch['gt']
|
||||||
last_step = step
|
|
||||||
|
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
completions, contexts, prompt_ids_batch, response_ids_batch, response_masks_batch, response_old_logps_batch, turn_outputs_batch, unfinished_batch = rollout_batch(rollout_engine, tokenizer, messages_batch, tools_batch, args.num_generations, max_turns=3, max_new_tokens=args.max_gen_len, thinking_ratio=args.thinking_ratio, device=args.device)
|
completions, contexts, prompt_ids_batch, response_ids_batch, response_masks_batch, response_old_logps_batch, turn_outputs_batch, unfinished_batch = rollout_batch(rollout_engine, tokenizer, messages_batch, tools_batch, args.num_generations, max_turns=3, max_new_tokens=args.max_gen_len, thinking_ratio=args.thinking_ratio, device=args.device)
|
||||||
|
|
@ -273,8 +271,6 @@ def rl_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_model
|
||||||
rewards = calculate_rewards(prompts, completions, gt_batch, tools_batch, args.num_generations, reward_model, device=args.device, turn_outputs_batch=turn_outputs_batch, unfinished_batch=unfinished_batch)
|
rewards = calculate_rewards(prompts, completions, gt_batch, tools_batch, args.num_generations, reward_model, device=args.device, turn_outputs_batch=turn_outputs_batch, unfinished_batch=unfinished_batch)
|
||||||
|
|
||||||
with autocast_ctx:
|
with autocast_ctx:
|
||||||
# 反向传播必须经过 DDP 包装后的模块:直接调用 .module 会跳过
|
|
||||||
# DDP 的 prepare_for_backward,梯度不会 all-reduce,各卡静默发散。
|
|
||||||
res = model(input_ids, attention_mask=full_mask)
|
res = model(input_ids, attention_mask=full_mask)
|
||||||
aux_loss = res.aux_loss if lm_config.use_moe else torch.tensor(0.0, device=args.device)
|
aux_loss = res.aux_loss if lm_config.use_moe else torch.tensor(0.0, device=args.device)
|
||||||
logits = res.logits[:, :-1, :]
|
logits = res.logits[:, :-1, :]
|
||||||
|
|
@ -333,7 +329,7 @@ def rl_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_model
|
||||||
loss = (policy_loss + aux_loss) / args.accumulation_steps
|
loss = (policy_loss + aux_loss) / args.accumulation_steps
|
||||||
loss.backward()
|
loss.backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
if args.grad_clip > 0: torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
if args.grad_clip > 0: torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
||||||
optimizer.step(); scheduler.step(); optimizer.zero_grad()
|
optimizer.step(); scheduler.step(); optimizer.zero_grad()
|
||||||
|
|
||||||
|
|
@ -367,10 +363,6 @@ def rl_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_model
|
||||||
del per_token_logps, ref_per_token_logps
|
del per_token_logps, ref_per_token_logps
|
||||||
del completions, rewards, grouped_rewards, mean_r, std_r, advantages, completion_mask
|
del completions, rewards, grouped_rewards, mean_r, std_r, advantages, completion_mask
|
||||||
|
|
||||||
if last_step > start_step and last_step % args.accumulation_steps != 0:
|
|
||||||
if args.grad_clip > 0: torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
|
||||||
optimizer.step(); scheduler.step(); optimizer.zero_grad()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind Agent RL")
|
parser = argparse.ArgumentParser(description="MiniMind Agent RL")
|
||||||
|
|
|
||||||
|
|
@ -38,14 +38,12 @@ def distillation_loss(student_logits, teacher_logits, temperature=1.0, reduction
|
||||||
|
|
||||||
def train_epoch(epoch, loader, iters, teacher_model, lm_config_student, start_step=0, wandb=None, alpha=0.0, temperature=1.0):
|
def train_epoch(epoch, loader, iters, teacher_model, lm_config_student, start_step=0, wandb=None, alpha=0.0, temperature=1.0):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
last_step = start_step
|
|
||||||
|
|
||||||
if teacher_model is not None:
|
if teacher_model is not None:
|
||||||
teacher_model.eval()
|
teacher_model.eval()
|
||||||
teacher_model.requires_grad_(False)
|
teacher_model.requires_grad_(False)
|
||||||
|
|
||||||
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
||||||
last_step = step
|
|
||||||
input_ids = input_ids.to(args.device)
|
input_ids = input_ids.to(args.device)
|
||||||
labels = labels.to(args.device)
|
labels = labels.to(args.device)
|
||||||
loss_mask = (labels[..., 1:] != -100).float()
|
loss_mask = (labels[..., 1:] != -100).float()
|
||||||
|
|
@ -94,7 +92,7 @@ def train_epoch(epoch, loader, iters, teacher_model, lm_config_student, start_st
|
||||||
|
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
scaler.unscale_(optimizer)
|
scaler.unscale_(optimizer)
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
||||||
scaler.step(optimizer)
|
scaler.step(optimizer)
|
||||||
|
|
@ -135,13 +133,6 @@ def train_epoch(epoch, loader, iters, teacher_model, lm_config_student, start_st
|
||||||
|
|
||||||
del input_ids, labels, loss_mask, res, student_logits, ce_loss, distill_loss, loss
|
del input_ids, labels, loss_mask, res, student_logits, ce_loss, distill_loss, loss
|
||||||
|
|
||||||
if last_step > start_step and last_step % args.accumulation_steps != 0:
|
|
||||||
scaler.unscale_(optimizer)
|
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
|
||||||
scaler.step(optimizer)
|
|
||||||
scaler.update()
|
|
||||||
optimizer.zero_grad(set_to_none=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 模拟用moe模型蒸馏dense模型,也可以用更大teacher_hidden_size模型蒸馏更小student_hidden_size的
|
# 模拟用moe模型蒸馏dense模型,也可以用更大teacher_hidden_size模型蒸馏更小student_hidden_size的
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,8 @@ def dpo_loss(ref_log_probs, policy_log_probs, mask, beta):
|
||||||
|
|
||||||
def train_epoch(epoch, loader, iters, ref_model, lm_config, start_step=0, wandb=None, beta=0.1):
|
def train_epoch(epoch, loader, iters, ref_model, lm_config, start_step=0, wandb=None, beta=0.1):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
last_step = start_step
|
|
||||||
|
|
||||||
for step, batch in enumerate(loader, start=start_step + 1):
|
for step, batch in enumerate(loader, start=start_step + 1):
|
||||||
last_step = step
|
|
||||||
x_chosen = batch['x_chosen'].to(args.device)
|
x_chosen = batch['x_chosen'].to(args.device)
|
||||||
x_rejected = batch['x_rejected'].to(args.device)
|
x_rejected = batch['x_rejected'].to(args.device)
|
||||||
y_chosen = batch['y_chosen'].to(args.device)
|
y_chosen = batch['y_chosen'].to(args.device)
|
||||||
|
|
@ -86,7 +84,7 @@ def train_epoch(epoch, loader, iters, ref_model, lm_config, start_step=0, wandb=
|
||||||
|
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
scaler.unscale_(optimizer)
|
scaler.unscale_(optimizer)
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
||||||
scaler.step(optimizer)
|
scaler.step(optimizer)
|
||||||
|
|
@ -120,13 +118,6 @@ def train_epoch(epoch, loader, iters, ref_model, lm_config, start_step=0, wandb=
|
||||||
del x_chosen, x_rejected, y_chosen, y_rejected, mask_chosen, mask_rejected, x, y, mask
|
del x_chosen, x_rejected, y_chosen, y_rejected, mask_chosen, mask_rejected, x, y, mask
|
||||||
del ref_outputs, ref_logits, ref_log_probs, outputs, logits, policy_log_probs, loss
|
del ref_outputs, ref_logits, ref_log_probs, outputs, logits, policy_log_probs, loss
|
||||||
|
|
||||||
if last_step > start_step and last_step % args.accumulation_steps != 0:
|
|
||||||
scaler.unscale_(optimizer)
|
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
|
||||||
scaler.step(optimizer)
|
|
||||||
scaler.update()
|
|
||||||
optimizer.zero_grad(set_to_none=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind DPO (Direct Preference Optimization)")
|
parser = argparse.ArgumentParser(description="MiniMind DPO (Direct Preference Optimization)")
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,9 @@ warnings.filterwarnings('ignore')
|
||||||
|
|
||||||
def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
last_step = start_step
|
|
||||||
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
||||||
input_ids = input_ids.to(args.device)
|
input_ids = input_ids.to(args.device)
|
||||||
labels = labels.to(args.device)
|
labels = labels.to(args.device)
|
||||||
last_step = step
|
|
||||||
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
||||||
for param_group in optimizer.param_groups:
|
for param_group in optimizer.param_groups:
|
||||||
param_group['lr'] = lr
|
param_group['lr'] = lr
|
||||||
|
|
@ -39,7 +37,7 @@ def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
|
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
scaler.unscale_(optimizer)
|
scaler.unscale_(optimizer)
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
||||||
|
|
||||||
|
|
@ -73,13 +71,6 @@ def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
|
|
||||||
del input_ids, labels, res, loss
|
del input_ids, labels, res, loss
|
||||||
|
|
||||||
if last_step > start_step and last_step % args.accumulation_steps != 0:
|
|
||||||
scaler.unscale_(optimizer)
|
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
|
||||||
scaler.step(optimizer)
|
|
||||||
scaler.update()
|
|
||||||
optimizer.zero_grad(set_to_none=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind Full SFT")
|
parser = argparse.ArgumentParser(description="MiniMind Full SFT")
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,6 @@ def grpo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_mod
|
||||||
rewards = calculate_rewards(prompts, completions, reward_model).to(args.device) # [B*num_gen]
|
rewards = calculate_rewards(prompts, completions, reward_model).to(args.device) # [B*num_gen]
|
||||||
|
|
||||||
with autocast_ctx:
|
with autocast_ctx:
|
||||||
# 反向传播必须经过 DDP 包装后的模块:直接调用 .module 会跳过
|
|
||||||
# DDP 的 prepare_for_backward,梯度不会 all-reduce,各卡静默发散。
|
|
||||||
res = model(outputs, attention_mask=full_mask)
|
res = model(outputs, attention_mask=full_mask)
|
||||||
aux_loss = res.aux_loss if lm_config.use_moe else torch.tensor(0.0, device=args.device)
|
aux_loss = res.aux_loss if lm_config.use_moe else torch.tensor(0.0, device=args.device)
|
||||||
per_token_logps = F.log_softmax(res.logits[:, :-1, :], dim=-1).gather(2, outputs[:, 1:].unsqueeze(-1)).squeeze(-1).gather(1, logp_pos)
|
per_token_logps = F.log_softmax(res.logits[:, :-1, :], dim=-1).gather(2, outputs[:, 1:].unsqueeze(-1)).squeeze(-1).gather(1, logp_pos)
|
||||||
|
|
@ -146,7 +144,7 @@ def grpo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_mod
|
||||||
loss = (policy_loss + aux_loss) / args.accumulation_steps # scalar
|
loss = (policy_loss + aux_loss) / args.accumulation_steps # scalar
|
||||||
loss.backward()
|
loss.backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
if args.grad_clip > 0:
|
if args.grad_clip > 0:
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
||||||
optimizer.step()
|
optimizer.step()
|
||||||
|
|
@ -197,13 +195,6 @@ def grpo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, reward_mod
|
||||||
del prompt_inputs, outputs, completion_ids, per_token_logps, ref_per_token_logps
|
del prompt_inputs, outputs, completion_ids, per_token_logps, ref_per_token_logps
|
||||||
del completions, rewards, grouped_rewards, mean_r, std_r, advantages, completion_mask, completion_pad_mask, prompt_lens, logp_pos
|
del completions, rewards, grouped_rewards, mean_r, std_r, advantages, completion_mask, completion_pad_mask, prompt_lens, logp_pos
|
||||||
|
|
||||||
if step > start_step and step % args.accumulation_steps != 0:
|
|
||||||
if args.grad_clip > 0:
|
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
|
||||||
optimizer.step()
|
|
||||||
scheduler.step()
|
|
||||||
optimizer.zero_grad()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind GRPO (Group Relative Policy Optimization)")
|
parser = argparse.ArgumentParser(description="MiniMind GRPO (Group Relative Policy Optimization)")
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,9 @@ warnings.filterwarnings('ignore')
|
||||||
|
|
||||||
def train_epoch(epoch, loader, iters, lora_params, start_step=0, wandb=None):
|
def train_epoch(epoch, loader, iters, lora_params, start_step=0, wandb=None):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
last_step = start_step
|
|
||||||
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
||||||
input_ids = input_ids.to(args.device)
|
input_ids = input_ids.to(args.device)
|
||||||
labels = labels.to(args.device)
|
labels = labels.to(args.device)
|
||||||
last_step = step
|
|
||||||
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
||||||
for param_group in optimizer.param_groups:
|
for param_group in optimizer.param_groups:
|
||||||
param_group['lr'] = lr
|
param_group['lr'] = lr
|
||||||
|
|
@ -40,7 +38,7 @@ def train_epoch(epoch, loader, iters, lora_params, start_step=0, wandb=None):
|
||||||
|
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
scaler.unscale_(optimizer)
|
scaler.unscale_(optimizer)
|
||||||
torch.nn.utils.clip_grad_norm_(lora_params, args.grad_clip)
|
torch.nn.utils.clip_grad_norm_(lora_params, args.grad_clip)
|
||||||
scaler.step(optimizer)
|
scaler.step(optimizer)
|
||||||
|
|
@ -68,13 +66,6 @@ def train_epoch(epoch, loader, iters, lora_params, start_step=0, wandb=None):
|
||||||
|
|
||||||
del input_ids, labels, res, loss
|
del input_ids, labels, res, loss
|
||||||
|
|
||||||
if last_step > start_step and last_step % args.accumulation_steps != 0:
|
|
||||||
scaler.unscale_(optimizer)
|
|
||||||
torch.nn.utils.clip_grad_norm_(lora_params, args.grad_clip)
|
|
||||||
scaler.step(optimizer)
|
|
||||||
scaler.update()
|
|
||||||
optimizer.zero_grad(set_to_none=True)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind LoRA Fine-tuning")
|
parser = argparse.ArgumentParser(description="MiniMind LoRA Fine-tuning")
|
||||||
parser.add_argument("--save_dir", type=str, default="../out", help="模型保存目录")
|
parser.add_argument("--save_dir", type=str, default="../out", help="模型保存目录")
|
||||||
|
|
|
||||||
|
|
@ -166,16 +166,12 @@ def ppo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, actor_sched
|
||||||
for i in range(0, B, mb_size):
|
for i in range(0, B, mb_size):
|
||||||
inds = b_inds[i:i + mb_size]
|
inds = b_inds[i:i + mb_size]
|
||||||
|
|
||||||
# 反向传播必须经过 DDP 包装后的模块:直接调用 .module 会跳过
|
|
||||||
# DDP 的 prepare_for_backward,梯度不会 all-reduce,各卡静默发散。
|
|
||||||
mb_values_seq = critic_model(input_ids=gen_out[inds], attention_mask=full_mask[inds])
|
mb_values_seq = critic_model(input_ids=gen_out[inds], attention_mask=full_mask[inds])
|
||||||
mb_resp_values = mb_values_seq.gather(1, logp_pos[inds])
|
mb_resp_values = mb_values_seq.gather(1, logp_pos[inds])
|
||||||
|
|
||||||
with autocast_ctx:
|
with autocast_ctx:
|
||||||
res = actor_model(input_ids=gen_out[inds], attention_mask=full_mask[inds])
|
res = actor_model(input_ids=gen_out[inds], attention_mask=full_mask[inds])
|
||||||
aux_loss = res.aux_loss if lm_config.use_moe else torch.tensor(0.0, device=args.device)
|
aux_loss = res.aux_loss if lm_config.use_moe else torch.tensor(0.0, device=args.device)
|
||||||
# 在 autocast 内计算 log_softmax,避免直接对 fp16/bf16 logits
|
|
||||||
# 计算造成额外数值偏差。
|
|
||||||
mb_resp_logp = F.log_softmax(res.logits[:, :-1], dim=-1).gather(2, labels[inds].unsqueeze(-1)).squeeze(-1).gather(1, logp_pos[inds])
|
mb_resp_logp = F.log_softmax(res.logits[:, :-1], dim=-1).gather(2, labels[inds].unsqueeze(-1)).squeeze(-1).gather(1, logp_pos[inds])
|
||||||
|
|
||||||
log_ratio = mb_resp_logp - old_resp_logp[inds]
|
log_ratio = mb_resp_logp - old_resp_logp[inds]
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,9 @@ warnings.filterwarnings('ignore')
|
||||||
|
|
||||||
def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
last_step = start_step
|
|
||||||
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
for step, (input_ids, labels) in enumerate(loader, start=start_step + 1):
|
||||||
input_ids = input_ids.to(args.device)
|
input_ids = input_ids.to(args.device)
|
||||||
labels = labels.to(args.device)
|
labels = labels.to(args.device)
|
||||||
last_step = step
|
|
||||||
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
lr = get_lr(epoch * iters + step, args.epochs * iters, args.learning_rate)
|
||||||
for param_group in optimizer.param_groups:
|
for param_group in optimizer.param_groups:
|
||||||
param_group['lr'] = lr
|
param_group['lr'] = lr
|
||||||
|
|
@ -39,7 +37,7 @@ def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
|
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
|
|
||||||
if step % args.accumulation_steps == 0:
|
if step % args.accumulation_steps == 0 or step == iters:
|
||||||
scaler.unscale_(optimizer)
|
scaler.unscale_(optimizer)
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
||||||
|
|
||||||
|
|
@ -72,13 +70,6 @@ def train_epoch(epoch, loader, iters, start_step=0, wandb=None):
|
||||||
|
|
||||||
del input_ids, labels, res, loss
|
del input_ids, labels, res, loss
|
||||||
|
|
||||||
if last_step > start_step and last_step % args.accumulation_steps != 0:
|
|
||||||
scaler.unscale_(optimizer)
|
|
||||||
torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)
|
|
||||||
scaler.step(optimizer)
|
|
||||||
scaler.update()
|
|
||||||
optimizer.zero_grad(set_to_none=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="MiniMind Pretraining")
|
parser = argparse.ArgumentParser(description="MiniMind Pretraining")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue