分类: 大模型

  • 强化学习(PPO)初步接触 3 (模型微调)

    根据表格内容,进行参数的逐步优化

    代码:github

    根据上篇文档中的数据内容:

    图表解析
    imageexplained_variance 数据为负数或0附近,代表 价值网络拟合失效。
    imageapprox_kl 持续上行,策略更新幅度偏大
    image学习率 lr不变

    test_01_4

    进行参数调整:

    参数原数据变化理由
    learning_rate0.2(默认值)LinearSchedule(start=1.0, end=0.05, end_fraction=0.2)线性衰减学习率,前期快速探索,后期小幅精细更新
    clip_range0.2(默认值)0.17缩小clip,抑制approx_kl持续上涨,限制单次策略更新幅度,防止策略突变、性能跳水
    vf_coef0.5(默认值)0.8提高价值loss权重,改善explained_variance,强制网络更重视价值函数拟合
    ent_coef0.0(默认值)0.01增加基础探索,小幅熵系数持续保留探索
    gae_lambda0.95(默认值)0.96略微提升GAE,优势估计更平滑
    n_epochs10(默认值)8减少迭代轮次,减少在同一批 rollout 数据上反复更新,也是为了减少approx_kl更新上涨速度
    total_timesteps10_000100_000增加训练总步数,否则很难充分收敛

    训练后的结果

    点击查看完整日志截图

    4

    可以通过rollout/ep_rew_mean发现,价值回报的数据已经崩了。下次训练需要调整。

    test_01_5

    进行参数调整:

    参数原数据变化理由
    policy_kwargspi=[256, 256], vf=[256, 256]感觉网络不够,网络扩容,提升价值网络拟合能力
    learning_rateLinearSchedule(start=1.0, end=0.05, end_fraction=0.2)LinearSchedule(start=3e-4, end=1e-4, end_fraction=0.2)进一步压低初始梯度更新冲击,避免开局策略崩坏
    vf_coef0.80.9小幅继续提高价值损失权重,强化价值拟合
    ent_coef0.010.02提高熵系数,保留更多探索,防止提前锁死次优策略
    gae_lambda0.960.97进一步平滑GAE优势估计
    n_epochs86继续降低迭代次数,减轻单批次过度更新,缓解开局震荡
    clip_range_vf0.3新增:约束价值网络更新幅度,抑制value_loss剧烈波动
    total_timesteps10_000100_000增加训练总步数,否则很难充分收敛

    训练后的结果

    点击查看完整日志截图

    5

    可以通过rollout/ep_rew_mean发现,价值回报的数据已经开始向正常方向进行。指标都开始改善了。
    后续准备继续拉高回报。

    test_01_6

    进行参数调整:

    参数原数据变化理由
    policy_kwargspi=[256, 256], vf=[256, 256]pi=[256, 256, 128], vf=[256, 256, 128]进一步加宽网络,提升容量

    训练后的结果

    点击查看完整日志截图

    6

    回合奖励继续提升,这个是很好的现象。通过train/clip_fraction发现,后期波动很大,证明模型探索新区域时出发裁剪。

    test_01_7

    进行参数调整:

    参数原数据变化理由
    clip_range0.170.18小幅放开更新区间(train/clip_fraction后期波动大,所以修改此参数,让其波动生效)
    vf_coef0.90.95进一步强化价值拟合 (提升explained_variance的预测)
    total_timesteps100_000150_000继续增加训练总步数,让后期出现新的探索生效

    训练后的结果

    点击查看完整日志截图

    7

    训练的日志看起来都很棒了,但回报仍然没有突破0。后续训练准备继续降低探索,目标是突破回报0。

    test_01_8

    进行参数调整:

    参数原数据变化理由
    learning_rateLinearSchedule(start=3e-4, end=1e-4, end_fraction=0.2)LinearSchedule(start=3e-4, end=8e-5, end_fraction=0.2)最终学习率略微降低,让后期更新更精细
    ent_coef0.020.018轻微降低熵系数,减少不必要随机抖动,降低一点点🤏模型的“好奇心”
    total_timesteps150_000200_000继续增加训练总步数,让后期出现新的探索生效

    训练后的结果

    点击查看完整日志截图

    8

    很棒!回合回报终于大于0了!并且可以发现,explained_variance在后期已经稳定的超过了0.6。非常好的现象。
    整个模型曲线非常健康,前期没有崩盘,后期没有震荡。

    test_01_8_1_0

    基于test_01_8,继续训练50_000回合。

    训练后的结果

    点击查看完整日志截图

    8-1

    explained_variance已经可以上升到0.8了,针对理论值的1,已经非常接近了。并且ep_rew_mean已经稳定的超过了0,并且通过图表可以看出在持续上升。

    评估

    使用模型跑50次,看看模型奖励,评估一下。

    # python evaluate.py
    
    import gymnasium as gym
    from stable_baselines3 import PPO
    
    env = gym.make("LunarLander-v3")
    model = PPO.load("ppo_LunarLander")
    
    # 批量评估
    eval_episodes = 50
    total_reward = 0
    for ep in range(eval_episodes):
        obs, _ = env.reset()
        ep_r = 0
        while True:
            action, _ = model.predict(obs, deterministic=True)
            obs, reward, terminated, truncated, info = env.step(action)
            ep_r += reward
            if terminated or truncated:
                break
        total_reward += ep_r
        print(f"Episode {ep+1} reward: {ep_r:.2f}")
    print(f"平均奖励:{total_reward/eval_episodes:.2f}")
    
    env.close()
    

    结果如下:
    image

    平均奖励很高。

    最终效果对比

    未训练的帖子在:macos(M4)上跑强化学习 – PyTorch LunarLander-v3 Stable-Baselines3

    未训练训练后
    not_training-ezgif.com-video-to-gif-converter202607231650

  • 强化学习(PPO)初步接触 2 (TensorBoard log 解析)

    通用的图例数据基础介绍:

    • Value:原始真实指标数值。(图标中与图例颜色较淡的那根线)
    • Smotthed:TensorBoard 自带曲线滑动平均处理后的数值。主要用来观察整体升降趋势。(图表中和图例相同颜色的那根线)
    • Step:横轴坐标,也就是从训练开始,智能体和环境一共执行了多少步 action。
    • Relative:相对时间。用来横向对比不同实验训练速度快慢。

    Rollout

    PPO 不是走一步更新一次,而是先收集一批轨迹,再统一训练,这一批收集过程就叫一次 rollout。
    其中有2个图标:如下图
    image

    rollout/ep_len_mean

    本轮 rollout 收集到的所有回合,平均回合长度(其实就是回合执行的时间,这里用步数代替了)。如下图:
    image

    rollout/ep_rew_mean

    本轮 rollout 收集到的所有回合,平均回合奖励(就是平均回合奖励的总数)。如下图:
    image

    Time

    其中只有一个,就是fps。(这个不是游戏渲染帧率)每秒完成多少条环境交互 step,也就是训练数据采集阶段的运行速度

    简单来说,fps越高,收集训练样本速度越快,也就意味着训练速度越快。

    image

    Train

    这里有9个表格。

    train/approx_kl

    Approximate KL Divergence,近似平均KL散度,代表的是更新前旧策略 和 更新后新策略 的分布差距有多大

    PPO 最核心思想:不能让策略一次性改动太大!,否则训练极易崩溃、震荡。

    • 数值小:新旧策略几乎没变化,更新力度太弱,学习慢
    • 数值大:新策略一次性改动过猛,很危险,容易训崩
      网上有很多说法,有说0.001-0.02之间,有说0.001-0.03之间。不过没有找到详细的解析或说明。
      image

    train/clip_fraction

    被 Clip 裁剪截断的样本占全部样本的比例
    强化学习中,需要限制策略更新幅度,阻止一次性改动过大。也就意味着,更新的新策略越多,裁剪越大。所以这里有一个规律,KL 越高 → 策略改动越大 → 更多样本触发裁剪 → clip_fraction 上升
    image

    train/clip_fraction

    PPO 裁剪区间的半径,是初始化模型设置的参数。默认为0.2。可以开启动态自适应clip_range。
    image

    train/entropy_loss

    熵损失。熵(Entropy):用来衡量策略随机性大小。

    • 熵越大:动作概率分布越平均 → 智能体更喜欢随机探索
    • 熵越小:策略趋向确定性,总是固定选最优动作,探索变少
      image

    train/explained_variance

    解释方差(Explained Variance)专门用来评价 PPO 里 Critic 价值网络(估值网络)预测准不准
    数值区间解读

    • ≈ 1.0:完美!价值网络预测和真实回报几乎一致
    • > 0:价值网络有一定预测能力(有效)
    • ≈ 0:价值网络预测效果 ≈ 和直接猜平均值差不多,基本没用
    • < 0:最差情况!价值网络预测完全不靠谱,甚至还不如直接输出常数均值
      image

    train/learning_rate

    优化器当前使用的学习率 lr。其实就是控制神经网络每一次参数更新的步幅大小。

    • lr 大:参数改动幅度大,学习快,但容易震荡、训崩
    • lr 小:更新平稳,不容易翻车,但收敛速度变慢
      图中是固定学习率,没有开启线性衰减(lr_schedule)。开启后随着训练步数增加,学习率会持续慢慢降低,这条曲线会一路向下倾斜。线性衰减优势在于,开始时快速探索,后期稳定收敛,不容易崩。
      image

    train/policy_gradient_loss

    策略梯度损失(Actor 网络专属损失)
    这一步是策略更加倾向选择能拿到更高奖励的动作。只控制模型怎么选择动作。

    好的表现是:缓慢持续下降,不会剧烈震荡、不会突然断崖式飙升。
    强化学习的一个结论:平稳温和的迭代,经常比单次激进优化得到更好的最终结果。

    image

    train/value_loss

    价值网络损失(Critic 网络专属损失)
    这个目标是让 Critic 的估值越来越贴近真实收益
    价值网络:评估当前状态好不好。

    好的表现是:训练前期 Critic 完全不懂环境,预测偏差巨大;随着训练不断修正,估值误差持续缩小。平稳持续下降,没有突然剧烈跳涨。

    image

    train/loss

    图表中这个会在policy_gradient_loss和value_loss前面。只是这里我放到了最后。
    PPO 总的联合损失函数(Total Loss)
    策略梯度损失(policy_gradient_loss,优化 Actor) + 价值网络损失(优化 Critic)熵损失(entropy_loss,鼓励探索,带负号)
    强化学习中,不能像普通监督学习一样,单纯认为 loss 越低 = 模型越好!

    image

  • 强化学习(PPO)初步接触 1 (环境搭建Mac M4)

    安装python环境

    • 安装miniforge3
      正确安装后,就有了conda命令

    配置python环境

    # 创建环境,python3.11
    conda create -n rl-env python=3.11
    conda activate rl-env
    # 关闭用这个
    conda deactivate
    

    报错‘CondaError: Run ‘conda init’ before ‘conda activate’’ google 一下,init命令需要参数

    使用PyTorch进行训练,所以需要安装PyTorch

    pip3 install torch torchvision torchaudio
    

    创建文件进行测试,是否安装成功,是否是M系列芯片

    # test_env.py
    import torch
    print(torch.backends.mps.is_available()) # True = GPU可用
    print(torch.backends.mps.is_built())
    

    输出如图:
    image
    小模型完全没必要使用gpu,直接使用cpu可能速度更快。

    You are trying to run PPO on the GPU, but it is primarily intended to run on the CPU when not using a CNN policy (you are using ActorCriticPolicy which should be a MlpPolicy). See https://github.com/DLR-RM/stable-baselines3/issues/1245 for more info. You can pass device='cpu' or export CUDA_VISIBLE_DEVICES= to force using the CPU.Note: The model will train, but the GPU utilization will be poor and the training might take longer than on CPU.

    安装 gymnasium

    pip install gymnasium
    # 或
    pip install "gymnasium[box2d]"
    # 我用的这个带box2d的
    

    创建文件进行测试,是否安装成功。参考自:https://zhuanlan.zhihu.com/p/694923486

    # test_gymnasium.py
    
    import gymnasium as gym
    env = gym.make("LunarLander-v3", render_mode="human")
    observation, info = env.reset()
    
    for _ in range(100):
        action = env.action_space.sample()
        observation, reward, terminated, truncated, info = env.step(action)
        print('---')
        print("action: " + str(action))
        print("observation: " + str(observation))
        print("reward: " + str(reward))
        print("terminated: " + str(terminated))
        print("truncated: " + str(truncated))
        print("info: " + str(info))
    
        if terminated or truncated:
            observation, info = env.reset()
    
    env.close()
    

    生成如图:
    not_training-ezgif.com-video-to-gif-converter

    image

    之后将使用LunarLander-v3 这个环境进行模型训练。

    使用Stable-Baselines3进行训练

    使用Stable-Baselines3先让流程跑通

    pip install 'stable-baselines3[extra]'
    

    写个脚本开始训练进行测试

    # test_sb3.py
    import gymnasium as gym
    import torch
    from stable_baselines3 import PPO
    
    # 检测设备
    device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")
    print("device: ", device)
    
    # 创建环境
    env = gym.make("LunarLander-v3")
    
    # 简单PPO测试训练
    model = PPO(
        "MlpPolicy", 
        env, 
        verbose=1, 
        device=device, 
        tensorboard_log="./ppo_logs/"  # 日志存放目录
        )
    model.learn(
        total_timesteps=10000,
        tb_log_name="test_01",    # 本次实验名称
        )
    
    # 测试运行
    obs, _ = env.reset()
    for _ in range(1000):
        action, _states = model.predict(obs)
        obs, reward, terminated, truncated, info = env.step(action)
        if terminated or truncated:
            obs, _ = env.reset()
    env.close()
    
    # 保存模型
    model.save("ppo_LunarLander")
    print("done")
    

    运行结果部分截图:
    image
    查看训练效果的方法,使用tensorboard

    tensorboard --logdir ./ppo_logs/
    

    运行后,则可以针对日志文件进行查看,打开 http://localhost:6006/
    如下图:
    image
    具体这个训练效果图要如何分析,先跳过,先尝试把流程跑完。

    使用训练的模型

    写个脚本来使用模型,看看效果:

    # test_use_ppo.py
    import gymnasium as gym
    import torch
    from stable_baselines3 import PPO
    
    # 检测设备
    device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")
    print("device: ", device)
    
    model = PPO.load("ppo_LunarLander", device=device)
    
    env = gym.make("LunarLander-v3", render_mode="human")
    obs, _ = env.reset()
    
    for _ in range(1000):
        # 模型预测动作
        action, _states = model.predict(obs, deterministic=True)
        obs, reward, terminated, truncated, info = env.step(action)
        
        if terminated or truncated:
            obs, _ = env.reset()
    
    env.close()
    

    效果如下图所示:
    training-ezgif.com-video-to-gif-converter

    至此,训练就完成了。
    剩下就可以一步一步针对全流程中的内容进行拆解学习。

  • 学习自己训练一个语言模型

    学习自己训练一个语言模型

    很久之前就看到了这个项目:https://github.com/jingyaogong/minimind,直到今天才有时间来玩一玩。

    魔搭社区中,notebook有免费实例可以用。

    cpu不用试了,速度太慢了。开GPU环境玩吧,按照minimind的基础配置,预训练大约需要1个小时,蛮不错的。