news 2026/7/28 9:10:56

深度强化学习入门:从PPO、DQN到A3C,经典算法串讲与实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深度强化学习入门:从PPO、DQN到A3C,经典算法串讲与实战指南

这次我们来看一个面向新手的深度强化学习入门教程。这个教程一口气涵盖了PPO、DQN、A3C、Q-Learning、SARSA等核心经典算法,目标是把看似复杂的强化学习(RL)讲得清晰、可落地。对于想入门AI决策与控制领域,但又觉得理论晦涩、代码难懂的开发者来说,这是一个系统性的切入点。

教程的核心价值在于“串讲”和“对比”。它不会孤立地讲解某个算法,而是试图构建一个知识图谱,让你理解从传统表格型方法(如Q-Learning)到深度强化学习(如DQN、PPO)的演进逻辑,以及不同算法(如On-policy的SARSA vs Off-policy的Q-Learning)之间的根本区别。这对于建立扎实的认知框架至关重要。

本文将带你梳理这份教程的核心脉络。我们会先快速了解每个算法的定位与核心思想,然后探讨学习强化学习所需的“软硬件”环境——这里没有显卡门槛,重点是理解概念和跑通仿真环境。接着,我们会拆解一个典型的学习路径:从理解马尔可夫决策过程(MDP)开始,到用代码实现一个简单环境(如“悬崖寻路”或“CartPole”),再到逐步套入不同算法进行实战。最后,会给出资源推荐和避坑指南,帮助你在自学路上走得更稳。

无论你是学生、算法工程师,还是对机器人控制、游戏AI、自动化决策感兴趣的爱好者,只要具备基础的Python和机器学习知识,都能从本文和这份教程中获得清晰的进阶路线。

1. 核心能力速览:教程内容与学习路径

本教程并非一个可部署的软件项目,而是一套结构化的知识体系与实践指南。因此,其“核心能力”体现在内容覆盖度、讲解深度与可实践性上。

能力项说明
覆盖算法PPO (近端策略优化)、DQN (深度Q网络)、A3C (异步优势行动者-评论家)、Q-Learning、SARSA 等经典算法。
内容维度算法原理推导、代码实现解析、实战案例演示、算法间对比分析。
实践门槛无特殊硬件要求。主要依赖Python、NumPy、PyTorch/TensorFlow及标准强化学习仿真环境(如Gymnasium)。CPU即可运行大部分基础案例。
关键产出建立强化学习知识体系;获得可运行、可修改的算法代码模板;理解算法适用场景。
适合人群机器学习初学者、希望系统入门RL的学生、需在项目中应用RL的工程师。

2. 适用场景与使用边界

2.1 谁适合学习这份教程?

  • AI/机器学习初学者:在学过监督学习后,希望开拓决策智能领域。
  • 机器人/控制工程学生:需要RL作为工具解决路径规划、运动控制等问题。
  • 游戏AI开发者:想了解如何让智能体通过试错学习游戏策略。
  • 算法工程师:需将RL应用于推荐系统、资源调度等业务场景,需夯实基础。

2.2 能解决什么问题?

  1. 概念梳理:厘清“状态”、“动作”、“奖励”、“策略”、“价值函数”等核心概念。
  2. 算法理解:不仅知道算法流程,更理解其设计动机(如DQN为何引入经验回放和目标网络)。
  3. 代码实现:摆脱“调包侠”困境,能从零实现关键算法组件,加深理解。
  4. 场景选型:面对一个具体问题(如连续控制、离散决策),能初步判断哪种RL算法更合适。

2.3 不适合什么场景?

  • 前沿算法研究:教程聚焦经典算法,对最前沿的模型(如Transformer-based RL、扩散模型RL)涉及较少。
  • 超大规模分布式训练:A3C介绍了异步思想,但工业级分布式RL涉及大量工程优化,非本教程重点。
  • 特定领域深度优化:如金融交易、医疗诊断等高风险领域,RL的应用需极度谨慎,本教程仅提供算法基础,不涉及领域风险控制。

2.4 伦理与安全边界

强化学习作为强大的决策工具,必须被负责任地使用:

  • 模拟环境优先:任何算法应在完全受控的仿真环境(如Gymnasium)中充分测试,再考虑真实世界部署。
  • 价值对齐:奖励函数的设计至关重要。不合理的奖励可能导致智能体学会“钻空子”甚至产生有害行为,必须反复审视与测试。
  • 安全护栏:在物理系统(如机器人、自动驾驶)中应用时,必须设置硬性安全约束和人工干预机制,防止不可控行为。

3. 环境准备与前置条件

开始学习前,需要搭建一个轻量级的开发与实验环境。以下清单基于Python生态,是RL社区最主流的选择。

3.1 基础软件环境

  • 操作系统:Windows 10/11, macOS, 或 Linux (Ubuntu推荐)。RL库通常跨平台兼容。
  • Python:版本 3.8 至 3.10。建议使用condavenv创建独立的虚拟环境,避免包冲突。
  • 包管理工具pip

3.2 核心Python库

以下库将通过pip安装,它们是实现和运行RL算法的基石:

  • NumPy: 数值计算基础。
  • PyTorchTensorFlow:深度学习框架。本教程示例可能基于其一,PyTorch在RL研究社区更流行。安装时请根据自身显卡选择CUDA版本,若仅用CPU,安装CPU版本即可。
  • Gymnasium:OpenAI Gym的维护分支,提供了大量标准化的强化学习环境(如“CartPole-v1”, “Pendulum-v1”, “Atari”游戏等)。这是练习算法的“操场”。
  • Matplotlib:用于绘制学习曲线、可视化策略等。

3.3 可选但推荐的工具

  • Jupyter Notebook / Lab:用于交互式学习和代码演示,非常适合初学者分步执行和观察结果。
  • Git:用于克隆和管理教程相关的代码仓库。
  • IDE:VS Code 或 PyCharm,提供良好的代码编辑和调试支持。

4. 学习路径与核心内容拆解

教程内容庞大,遵循一个由浅入深、从理论到实践的逻辑。我们可以将其分解为以下几个关键阶段。

4.1 第一阶段:强化学习基石(MDP与表格型方法)

这是理解一切的基础。教程会首先阐述马尔可夫决策过程(MDP)的五个核心要素:状态(S)、动作(A)、转移概率(P)、奖励(R)、折扣因子(γ)。然后引入两个最经典的表格型算法:

  • Q-Learning:Off-policy(异策)学习的代表。通过更新Q表(状态-动作价值表)来学习最优策略。其更新公式是理解时序差分(TD)学习的钥匙。
    # Q-Learning 更新公式的核心伪代码 # Q[state, action] = Q[state, action] + alpha * (reward + gamma * max(Q[next_state]) - Q[state, action])
  • SARSA:On-policy(同策)学习的代表。其更新依赖于实际执行的下一步动作,因此更“保守”。与Q-Learning的对比是理解On/Off-policy差异的绝佳案例。

实践任务:在GymnasiumCliffWalking-v0FrozenLake-v1环境中,手动实现Q-Learning和SARSA算法,并观察它们学习策略的异同。

4.2 第二阶段:价值函数逼近与深度Q网络(DQN)

当状态空间巨大或连续时,Q表不再可行。需要引入函数逼近(如神经网络)来估计Q值,这就是DQN。 教程会重点剖析DQN解决稳定训练问题的三大技术:

  1. 经验回放(Experience Replay):打破数据间的相关性,提高数据利用率。
  2. 目标网络(Target Network):提供稳定的Q值目标,缓解训练振荡。
  3. 误差裁剪:使用Huber损失或对TD误差进行裁剪,提升稳定性。

实践任务:在GymnasiumCartPole-v1Atari Pong环境中,使用PyTorch实现DQN。重点观察经验回放缓冲区的大小、目标网络更新频率对训练稳定性的影响。

4.3 第三阶段:策略梯度与演员-评论家(Actor-Critic)

直接从策略出发进行优化。教程会引导你理解:

  • REINFORCE:蒙特卡洛策略梯度算法,方差大但概念清晰。
  • Actor-Critic框架:引入价值函数(Critic)来降低方差,指导策略(Actor)更新。这是现代RL算法的核心框架。

4.4 第四阶段:先进算法:A3C与PPO

  • A3C(Asynchronous Advantage Actor-Critic):通过多个智能体异步探索环境,并行更新全局网络,大幅提升样本效率和训练速度。教程会解释其“异步”和“优势函数”的设计思想。
  • PPO(Proximal Policy Optimization):当前最流行的On-policy算法之一。其核心是“近端”优化,通过裁剪概率比来避免策略更新步幅过大,从而保证训练稳定性。PPO因其良好的性能与易用性,成为许多实际应用的首选。

实践任务:在Pendulum-v1(连续动作空间)或LunarLander-v2环境中,实现PPO算法。尝试调整裁剪系数、价值函数损失系数等超参数,观察其对训练效果的影响。

5. 实战演练:以PPO算法为例

让我们以PPO为例,勾勒一个从零开始的实战流程,感受如何将教程知识转化为代码。

5.1 第一步:搭建环境与定义网络

import gymnasium as gym import torch import torch.nn as nn import torch.optim as optim import numpy as np # 1. 创建环境 env = gym.make('Pendulum-v1') # 一个连续控制任务 state_dim = env.observation_space.shape[0] action_dim = env.action_space.shape[0] # 2. 定义Actor(策略网络)和Critic(价值网络) class ActorNetwork(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.fc1 = nn.Linear(state_dim, 64) self.fc2 = nn.Linear(64, 64) self.mu_head = nn.Linear(64, action_dim) # 输出均值 self.sigma_head = nn.Linear(64, action_dim) # 输出标准差(对数形式) def forward(self, state): x = torch.relu(self.fc1(state)) x = torch.relu(self.fc2(x)) mu = torch.tanh(self.mu_head(x)) # 动作均值,限制在[-1,1] sigma = torch.nn.functional.softplus(self.sigma_head(x)) + 1e-4 # 标准差,保证为正 return mu, sigma class CriticNetwork(nn.Module): def __init__(self, state_dim): super().__init__() self.fc1 = nn.Linear(state_dim, 64) self.fc2 = nn.Linear(64, 64) self.value_head = nn.Linear(64, 1) def forward(self, state): x = torch.relu(self.fc1(state)) x = torch.relu(self.fc2(x)) value = self.value_head(x) return value

5.2 第二步:实现PPO核心更新逻辑

PPO的核心在于其目标函数和裁剪机制。

def compute_ppo_loss(actor, critic, states, actions, old_log_probs, returns, advantages, clip_epsilon=0.2): """ 计算PPO的联合损失(策略损失 + 价值损失) """ # 计算新策略下的动作概率 mu, sigma = actor(states) dist = torch.distributions.Normal(mu, sigma) new_log_probs = dist.log_prob(actions).sum(dim=-1) # 策略损失(带裁剪) ratio = torch.exp(new_log_probs - old_log_probs) surr1 = ratio * advantages surr2 = torch.clamp(ratio, 1 - clip_epsilon, 1 + clip_epsilon) * advantages policy_loss = -torch.min(surr1, surr2).mean() # 价值损失(MSE) values = critic(states).squeeze() value_loss = torch.nn.functional.mse_loss(values, returns) # 可选:熵正则项,鼓励探索 entropy = dist.entropy().mean() entropy_bonus = -0.01 * entropy total_loss = policy_loss + 0.5 * value_loss + entropy_bonus return total_loss, policy_loss.item(), value_loss.item(), entropy.item()

5.3 第三步:组织训练循环

训练循环包括数据收集(与环境交互)和参数更新(PPO更新)两个阶段。

def train_ppo(env_name='Pendulum-v1', total_timesteps=100000): env = gym.make(env_name) actor = ActorNetwork(state_dim, action_dim) critic = CriticNetwork(state_dim) optimizer = optim.Adam(list(actor.parameters()) + list(critic.parameters()), lr=3e-4) # 超参数 update_epochs = 10 # PPO更新轮数 batch_size = 64 gamma = 0.99 # 折扣因子 gae_lambda = 0.95 # GAE系数 # 训练循环 for episode in range(total_timesteps // 2048): # 假设每轮收集2048个时间步数据 # 1. 收集轨迹数据 states, actions, rewards, dones, old_log_probs = [], [], [], [], [] state, _ = env.reset() for _ in range(2048): state_tensor = torch.FloatTensor(state).unsqueeze(0) with torch.no_grad(): mu, sigma = actor(state_tensor) dist = torch.distributions.Normal(mu, sigma) action = dist.sample() log_prob = dist.log_prob(action).sum(dim=-1) action = action.squeeze().numpy() next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated states.append(state) actions.append(action) rewards.append(reward) dones.append(done) old_log_probs.append(log_prob.item()) state = next_state if done: state, _ = env.reset() # 2. 计算GAE和Returns # ... (此处需实现GAE和Returns的计算,是PPO的关键部分) # 3. PPO更新阶段 for _ in range(update_epochs): # 将数据打乱,分成小批量进行更新 indices = np.arange(len(states)) np.random.shuffle(indices) for start in range(0, len(states), batch_size): batch_indices = indices[start:start+batch_size] batch_states = torch.FloatTensor(np.array(states)[batch_indices]) batch_actions = torch.FloatTensor(np.array(actions)[batch_indices]) batch_old_log_probs = torch.FloatTensor(np.array(old_log_probs)[batch_indices]) batch_returns = torch.FloatTensor(returns[batch_indices]) batch_advantages = torch.FloatTensor(advantages[batch_indices]) # 计算损失并更新 optimizer.zero_grad() loss, p_loss, v_loss, ent = compute_ppo_loss( actor, critic, batch_states, batch_actions, batch_old_log_probs, batch_returns, batch_advantages ) loss.backward() optimizer.step() # 4. 定期评估与保存模型 if episode % 10 == 0: # 运行评估回合,计算平均奖励 eval_reward = evaluate_policy(actor, env) print(f"Episode {episode}, Eval Reward: {eval_reward:.2f}") torch.save(actor.state_dict(), f'ppo_actor_{episode}.pth')

预期结果:经过数万步训练,智能体应能学会稳定地将倒立摆摆动并维持在上方垂直位置(奖励接近0,Pendulum环境奖励为负,越接近0越好)。

6. 算法对比与选型指南

学完多个算法后,如何选择?下表总结了关键特性:

算法类型关键思想适用场景优点缺点
Q-LearningOff-policy, 价值学习通过更新Q表学习最优动作价值。离散、低维状态/动作空间。概念简单,收敛性有理论保证。无法处理连续或高维空间。
SARSAOn-policy, 价值学习基于当前策略执行的动作进行更新。对探索有安全约束的场景。策略更保守、安全。可能收敛到次优策略,样本效率较低。
DQNOff-policy, 价值学习用神经网络拟合Q函数,引入经验回放和目标网络。高维状态(如图像)、离散动作空间(如游戏)。能处理高维输入,相对稳定。对超参数敏感,可能高估Q值。
A2C/A3COn-policy, Actor-Critic多个智能体并行探索,异步更新全局网络。需要快速样本收集、可并行化的任务。样本效率高,训练速度快。实现复杂,需要协调多线程/进程。
PPOOn-policy, Actor-Critic通过裁剪概率比限制策略更新幅度,保证稳定性。通用性强,连续/离散动作空间均可,是当前实践首选。易于实现,调参相对友好,性能稳定。是On-policy算法,样本效率可能低于Off-policy方法。

选型建议

  • 入门练手:从Q-Learning(表格法) 和DQN(深度RL入门) 开始。
  • 连续控制:优先尝试PPODDPG(深度确定性策略梯度)。
  • 需要高样本效率:考虑SAC(软演员-评论家) 或TD3(双延迟DDPG) 等Off-policy算法。
  • 分布式训练:研究A3CIMPALA的思想。

7. 资源占用与性能观察

强化学习训练的性能消耗主要来自两部分:环境模拟神经网络训练

7.1 环境模拟开销

  • 经典控制问题(如CartPole, Pendulum):CPU模拟,开销极低,普通笔记本即可流畅运行。
  • Atari游戏:需要渲染图像,CPU开销增大。使用Ram版本(如Pong-ram-v4)可避免图像渲染,提升速度。
  • 3D物理仿真(如MuJoCo, PyBullet环境):计算密集,对CPU单核性能要求高。可能需要较长的训练时间。
  • 自定义复杂环境:如果环境逻辑复杂或涉及外部通信(如机器人仿真),可能成为性能瓶颈。

7.2 神经网络训练开销

  • 网络规模:用于CartPole的简单MLP(几层全连接)训练几乎无感。用于Atari的CNN+DQN或大型PPO策略网络,则需要更多显存和算力。
  • 批处理大小:PPO等算法需要收集一批轨迹后统一更新。增大批次大小能提升训练稳定性,但会增加GPU显存占用。
  • 观察方法
    • CPU/GPU利用率:使用nvidia-smi(GPU) 或任务管理器 (CPU) 监控。
    • 训练速度:记录每秒处理的环境步数(steps per second)。这是衡量样本吞吐量的关键指标。
    • 内存/显存:监控Python进程的内存占用和GPU显存占用,防止溢出。

7.3 优化建议

  1. 向量化环境:使用GymnasiumAsyncVectorEnvSubprocVecEnv并行运行多个环境实例,可大幅提升数据收集速度。
  2. 高效数据格式:确保状态、动作等数据在NumPy数组和Tensor之间转换时没有不必要的拷贝。
  3. 调整更新频率:对于PPO,不一定需要非常多的更新轮数(update_epochs)。尝试减少它,可能在不明显影响性能的前提下加快训练。
  4. 简化网络:在确保表达能力的前提下,使用更小的神经网络。

8. 常见问题与排查方法

自学强化学习必然会遇到各种“坑”。下表整理了常见问题及解决思路。

问题现象可能原因排查方式解决方案
奖励不上升,智能体不学习1. 奖励函数设计不合理。
2. 学习率过大或过小。
3. 探索不足(如ε-greedy中ε太小)。
4. 网络结构或激活函数不合适。
1. 可视化奖励曲线,看是否完全随机。
2. 打印网络输出,看是否发生变化。
3. 检查梯度是否消失/爆炸。
1. 重新设计奖励,确保其稀疏性和可学习性。
2. 调整学习率(如使用Adam默认值3e-4)。
3. 增加探索率,或添加熵正则项。
4. 尝试更简单的网络,使用ReLU激活。
训练初期表现好,后期崩溃1. 过拟合。
2. 策略更新步幅太大(PPO中裁剪系数太小)。
3. 价值函数估计不准确,导致策略被误导。
1. 观察验证集(独立评估回合)表现。
2. 监控策略更新的KL散度或概率比。
1. 增加策略熵正则化强度。
2. 调大PPO的裁剪系数(clip_epsilon)。
3. 增加价值函数的训练次数或调整其损失权重。
GPU显存溢出(OOM)1. 批次大小(batch_size)过大。
2. 网络层数过深或神经元过多。
3. 经验回放缓冲区过大且全部放在GPU上。
使用nvidia-smi监控显存占用变化。1. 减小批次大小。
2. 简化网络结构。
3. 将经验回放缓冲区放在主机内存,仅将当前批次数据送入GPU。
环境运行速度极慢1. 环境渲染被开启。
2. 环境重置或步进函数中有低效操作。
3. 未使用向量化环境。
1. 在创建环境时使用render_mode='rgb_array'或关闭渲染。
2. 使用性能分析工具(如cProfile)定位瓶颈。
1. 训练时关闭渲染(render_mode=None)。
2. 优化自定义环境代码。
3. 使用AsyncVectorEnv并行多个环境。
算法实现看似正确,但效果远差于基准1. 超参数设置与原始论文或标准实现不同。
2. 随机种子未固定,导致结果波动大。
3. 细微的实现错误(如梯度未清零、数据格式错误)。
1. 对比自己的超参数与经典实现(如OpenAI Baselines, Stable-Baselines3)。
2. 固定所有随机种子(Python, NumPy, PyTorch, 环境)。
3. 使用梯度检查或与已验证的代码逐行对比。
1. 首先复现论文或库中的标准超参数。
2. 始终固定随机种子以确保可复现性。
3. 编写单元测试,检查关键函数(如GAE计算、折扣回报计算)的输出。

9. 最佳实践与学习建议

  1. 从复现开始,不要从零造轮子:第一遍学习时,优先找一份高质量、有口碑的开源代码(如Stable-Baselines3库中的实现)进行阅读、运行和调试。理解透彻后,再尝试自己从头实现。
  2. 固定随机种子:在实验开始前,固定所有随机数生成器的种子。这是确保实验结果可复现、可对比的唯一方法。
    import random import numpy as np import torch import gymnasium as gym seed = 42 random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) env = gym.make('CartPole-v1') env.reset(seed=seed)
  3. 系统化实验与记录:使用Weights & Biases (wandb)TensorBoard记录每次实验的超参数、奖励曲线、损失曲线等。这有助于分析不同设置的影响。
  4. 理解比调参更重要:初期不要沉迷于调参。花时间理解算法流程图、损失函数每一项的物理意义、以及超参数(如折扣因子γ、GAE系数λ)对算法行为的影响。
  5. 从小环境到大环境:先在CartPole-v1(简单)、Pendulum-v1(连续) 等小型环境上验证算法正确性。成功后再挑战LunarLander-v2,Atari等更复杂的环境。
  6. 善用社区资源
    • 代码库Stable-Baselines3,Ray RLlib,Tianshou
    • 课程:David Silver的经典课程,UC Berkeley的CS285,李宏毅老师的强化学习课程。
    • 书籍:《强化学习导论》(Sutton & Barto)是圣经,《深度强化学习》等可作为补充。
    • 论文:从经典算法(DQN, PPO)的原始论文读起。

这份“一口气讲完”的教程,其最大价值在于提供了一个结构化的学习地图和算法对比视角。强化学习入门之路,道阻且长,但遵循从原理到实现、从简单到复杂、从模仿到创新的路径,并辅以持续的动手实践,你完全能够掌握这项强大的决策智能技术。建议将本文提及的实践任务逐一完成,并善用排查清单解决遇到的问题,你的RL实战能力将会稳步建立。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/28 9:09:16

解决Android底部弹窗痛点:SuperBottomSheet实战案例

解决Android底部弹窗痛点:SuperBottomSheet实战案例 【免费下载链接】SuperBottomSheet Android native BottomSheet on steroids 💪 项目地址: https://gitcode.com/gh_mirrors/su/SuperBottomSheet SuperBottomSheet是一款为Android开发者打造的…

作者头像 李华
网站建设 2026/7/28 9:09:10

高效文件压缩方案:算法对比与多线程优化

1. 文件压缩神器的核心价值解析在数字信息爆炸的今天,我们每天都要处理大量文件——工作文档、设计素材、视频照片、程序代码...这些文件不仅占用存储空间,更影响传输效率。我最近深度测试了市面上主流的文件压缩方案,发现真正高效的压缩工具…

作者头像 李华
网站建设 2026/7/28 9:06:23

掌控板图形化编程实现平行四边形面积动态可视化

1. 项目概述:当掌控板遇上数学可视化最近在带学生做科创项目,发现一个挺有意思的现象:很多孩子对抽象的数学公式感到头疼,但一看到图形化的演示,眼睛立马就亮了。这让我想起了手头这块“掌控板”——一个集成了OLED屏幕…

作者头像 李华
网站建设 2026/7/28 9:03:42

英雄联盟回放分析终极方案:ROFL-Player开源工具深度解析

英雄联盟回放分析终极方案:ROFL-Player开源工具深度解析 【免费下载链接】ROFL-Player (No longer supported) One stop shop utility for viewing League of Legends replays! 项目地址: https://gitcode.com/gh_mirrors/ro/ROFL-Player 想要快速分析英雄联…

作者头像 李华
网站建设 2026/7/28 9:02:48

nRPC完全指南:基于NATS构建轻量级RPC服务的终极方案

nRPC完全指南:基于NATS构建轻量级RPC服务的终极方案 【免费下载链接】nrpc nRPC is like gRPC, but over NATS 项目地址: https://gitcode.com/gh_mirrors/nr/nrpc nRPC是一个类似gRPC的RPC框架,但专为NATS消息系统设计。它允许开发者使用与gRPC相…

作者头像 李华
网站建设 2026/7/28 9:01:34

如何快速配置黑苹果:Intel显卡驱动的终极解决方案

如何快速配置黑苹果:Intel显卡驱动的终极解决方案 【免费下载链接】OS-X-Clover-Laptop-Config Some Clover config.plist files for common Intel graphics configurations 项目地址: https://gitcode.com/gh_mirrors/os/OS-X-Clover-Laptop-Config 对于技术…

作者头像 李华