Compare commits
6 Commits
0fee6a001a
...
ef13a90e35
| Author | SHA1 | Date |
|---|---|---|
|
|
ef13a90e35 | |
|
|
89d674b8a5 | |
|
|
2d4c385b0f | |
|
|
e9f46506da | |
|
|
1191fc8e9f | |
|
|
743420daac |
|
|
@ -4,3 +4,9 @@ __pycache__/
|
|||
out
|
||||
website/
|
||||
docs-minimind/
|
||||
|
||||
checkpoints/
|
||||
dataset/
|
||||
|
||||
minimind-3/
|
||||
minimind-3-moe/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
# .gitignore 忽略下列文件和目录
|
||||
|
||||
#比如minimind\scripts\minimind-3和minimind\scripts\minimind-3-moe
|
||||
minimind-3/
|
||||
minimind-3-moe/
|
||||
|
|
@ -64,6 +64,28 @@ st.markdown("""
|
|||
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>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
|
|
@ -83,6 +105,7 @@ LANG_TEXTS = {
|
|||
'disclaimer': 'AI 生成内容可能存在错误,请仔细核实',
|
||||
'think_tip': '自适应思考,目前多轮对话或Tool Call共存时思考不稳定',
|
||||
'tool_select': '工具选择(最多4个)',
|
||||
'new_chat': '新建对话', # 新增
|
||||
},
|
||||
'en': {
|
||||
'settings': 'Model Settings',
|
||||
|
|
@ -96,6 +119,7 @@ LANG_TEXTS = {
|
|||
'disclaimer': 'AI-generated content may be inaccurate, please verify',
|
||||
'think_tip': 'Adaptive thinking; may be unstable with multi-turn or Tool Call',
|
||||
'tool_select': 'Tool Selection (max 4)',
|
||||
'new_chat': 'New Chat', # 新增
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,15 +261,20 @@ def regenerate_answer(index):
|
|||
|
||||
|
||||
# 动态扫描模型目录
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
MODEL_PATHS = {}
|
||||
for d in sorted(os.listdir(script_dir), reverse=True):
|
||||
full_path = os.path.join(script_dir, d)
|
||||
if os.path.isdir(full_path) and not d.startswith('.') and not d.startswith('_'):
|
||||
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[d] = [d, d]
|
||||
if not MODEL_PATHS:
|
||||
MODEL_PATHS = {"No models found": ["", "No models"]}
|
||||
@st.cache_data(ttl=3600) # 增加缓存(有效期1小时),避免每次刷新页面都去高频读取硬盘文件树
|
||||
def get_model_paths():
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
paths = {}
|
||||
for d in sorted(os.listdir(script_dir), reverse=True):
|
||||
full_path = os.path.join(script_dir, d)
|
||||
if os.path.isdir(full_path) and not d.startswith('.') and not d.startswith('_'):
|
||||
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))):
|
||||
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)
|
||||
|
|
@ -285,7 +314,27 @@ with st.sidebar.expander(get_text('tools')):
|
|||
if checked and len(st.session_state.selected_tools) < 4:
|
||||
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(
|
||||
f'<div style="display: flex; flex-direction: column; align-items: center; text-align: center; margin: 0; padding: 0;">'
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import warnings
|
|||
import torch
|
||||
import torch.distributed as dist
|
||||
import torch.nn.functional as F
|
||||
from transformers import AutoTokenizer
|
||||
from contextlib import nullcontext
|
||||
from torch import optim, nn
|
||||
from torch.nn.parallel import DistributedDataParallel
|
||||
|
|
@ -76,7 +75,7 @@ def calculate_rewards(prompts, responses, reward_model):
|
|||
return rewards
|
||||
|
||||
|
||||
def ppo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, actor_scheduler, critic_scheduler, reward_model, start_step=0, wandb=None, use_sglang=False):
|
||||
def ppo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, actor_scheduler, critic_scheduler, reward_model, start_step=0, wandb=None):
|
||||
actor_model.train()
|
||||
critic_model.train()
|
||||
grad_accum_step = 0
|
||||
|
|
@ -174,10 +173,24 @@ def ppo_train_epoch(epoch, loader, iters, rollout_engine, ref_model, actor_sched
|
|||
with autocast_ctx:
|
||||
res = actor_unwrapped(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)
|
||||
|
||||
mb_resp_logp = F.log_softmax(res.logits[:, :-1], dim=-1).gather(2, labels[inds].unsqueeze(-1)).squeeze(-1).gather(1, logp_pos[inds])
|
||||
# 在 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])
|
||||
|
||||
log_ratio = mb_resp_logp - old_resp_logp[inds]
|
||||
|
||||
# 可开关的诊断:观察首轮首个 minibatch 的 mb 与 old logp 差异。
|
||||
if args.debug_log_ratio and ppo_epoch == 0 and i == 0 and is_main_process():
|
||||
_lr = log_ratio.detach()
|
||||
_m = resp_policy_mask[inds].bool()
|
||||
if _m.any():
|
||||
_lrv = _lr[_m]
|
||||
Logger(f"[DBG log_ratio] step={step} max|lr|={_lrv.abs().max().item():.6e} "
|
||||
f"mean|lr|={_lrv.abs().mean().item():.6e} "
|
||||
f"ratio_max={torch.exp(_lrv).max().item():.6f} "
|
||||
f"ratio_min={torch.exp(_lrv).min().item():.6f} "
|
||||
f"dropout={getattr(lm_config, 'dropout', None)} "
|
||||
f"training={actor_unwrapped.training}")
|
||||
approx_kl = (0.5 * (log_ratio ** 2) * resp_policy_mask[inds]).sum() / resp_policy_mask[inds].sum().clamp(min=1)
|
||||
|
||||
# 同步各卡的 approx_kl,防止某卡 break 而其它卡继续导致 DDP 死锁
|
||||
|
|
@ -331,6 +344,7 @@ if __name__ == "__main__":
|
|||
parser.add_argument("--use_compile", default=0, type=int, choices=[0, 1], help="是否使用torch.compile加速(0=否,1=是)")
|
||||
parser.add_argument("--debug_mode", action="store_true", help="是否打印训练调试采样")
|
||||
parser.add_argument("--debug_interval", type=int, default=20, help="debug模式下每隔多少step打印一次采样")
|
||||
parser.add_argument("--debug_log_ratio", action="store_true", help="打印首轮首个minibatch的log_ratio差异量级,用于核查ratio≈1是否成立")
|
||||
parser.add_argument("--thinking_ratio", type=float, default=0.9, help="按概率开启thinking(0.0~1.0)")
|
||||
parser.add_argument("--rollout_engine", type=str, default="torch", choices=["torch", "sglang"], help="rollout引擎类型")
|
||||
parser.add_argument("--sglang_base_url", type=str, default="http://localhost:8998", help="SGLang服务器URL")
|
||||
|
|
@ -427,9 +441,9 @@ if __name__ == "__main__":
|
|||
loader = DataLoader(train_ds, batch_sampler=batch_sampler, num_workers=args.num_workers, pin_memory=True)
|
||||
if skip > 0:
|
||||
Logger(f'Epoch [{epoch + 1}/{args.epochs}]: 跳过前{start_step}个step,从step {start_step + 1}开始')
|
||||
ppo_train_epoch(epoch, loader, len(loader) + skip, rollout_engine, ref_model, actor_scheduler, critic_scheduler, reward_model, start_step, wandb, use_sglang = (args.rollout_engine == "sglang"))
|
||||
ppo_train_epoch(epoch, loader, len(loader) + skip, rollout_engine, ref_model, actor_scheduler, critic_scheduler, reward_model, start_step, wandb)
|
||||
else:
|
||||
ppo_train_epoch(epoch, loader, len(loader), rollout_engine, ref_model, actor_scheduler, critic_scheduler, reward_model, 0, wandb, use_sglang = (args.rollout_engine == "sglang"))
|
||||
ppo_train_epoch(epoch, loader, len(loader), rollout_engine, ref_model, actor_scheduler, critic_scheduler, reward_model, 0, wandb)
|
||||
|
||||
# ========== 9. 清理分布进程 ==========
|
||||
if dist.is_initialized():
|
||||
|
|
|
|||
Loading…
Reference in New Issue