逐水寻源

欢迎来到逐水寻源,这是个人技术博客,汇聚了我在计算机技术、数据科学和人工智能等领域的学习与实践。从编程技巧到算法解析,从数据分析到机器学习项目,从技术知识到个人成长故事,这里有丰富的内容等待你的探索。让我们一起学习,一起成长,解锁技术世界的无限可能!

RL 后训练进化论:从PPO被动奖励、GRPO组内奖励到DeepSeekMath-V2自验证奖励

<blockquote> <p>深度解析DeepSeek-R1背后的GRPO/GSPO/DAPO等新一代强化学习算法,详解如何通过去除Critic网络将显存占用减半,对比PPO/GRPO/GSPO/DAPO的数学原理与工程实现差异,探讨DeepSeekMath-V2代表的大模型RL后训练从监督学习到自我博弈进化的范式转移。</p> </blockquote> <p>在 DeepSeek-R1 惊艳亮相之前,行业内曾有一个心照不宣的误区:SFT(监督微调)负责传授知识,而 RLHF(基于人类反馈的强化学习)仅负责价值观对齐。直到今天,仍有很多人认为强化学习(RL)并没有真正让模型学到东西,而是对 SFT 阶段能力的微调和影响。但 R1 用令人咋舌的推理能力证明,强化学习才是解锁大模型深度推理(Reasoning)能力的真正钥匙。模型并非通过模仿人类学会了复杂的数学推导,而是在大规模的自我博弈与试错中“悟”出来的。</p> <p>但 LLM 的进化道路被一道“显存墙”挡住了。传统的 PPO 算法对于 70B 甚至 1TB 的 MoE 模型而言,简直是资源黑洞。于是,一场关于 RL 后训练算法的“瘦身革命”悄然爆发。从 GRPO 的“去 Critic 化”,到 GSPO 的“维稳”,再到 DAPO 的“效率压榨”,我们正在见证 LLM 训练范式从模仿向探索的代际跃迁。</p> <hr> <h2 id="为什么我们要杀掉-critic" class="heading-element"><span>1 为什么我们要“杀掉” Critic?</span> <a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e6%88%91%e4%bb%ac%e8%a6%81%e6%9d%80%e6%8e%89-critic" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在经典的 PPO 架构中,训练不仅仅是优化一个模型,而是维护一个复杂的生态系统。我们需要同时加载四个庞然大物:Actor(演员)负责生成答案,Reward Model(判卷人)负责打分,Reference Model(锚点)负责防止模型跑偏,以及最麻烦的——Critic(评论家)。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/11/Four-Model-Roles-in-RL-Post-Training.avif" alt="Four-Model-Roles-in-RL-Post-Training" srcset="https://cdn1.zair.top/images/2025/11/Four-Model-Roles-in-RL-Post-Training.avif?size=small, https://cdn1.zair.top/images/2025/11/Four-Model-Roles-in-RL-Post-Training.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/11/Four-Model-Roles-in-RL-Post-Training.avif?size=large 2x" data-title="Four-Model-Roles-in-RL-Post-Training" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>Critic 的核心职责是预测当前状态的未来价值 $V(s)$。在传统的强化学习任务中,Critic 至关重要。但在大模型训练的语境下,Critic 变成了一个巨大的累赘。</p> <p>首先是<strong>显存噩梦</strong>。Critic 模型通常与 Actor 同等规模。这意味着如果你要训练一个 70B 的模型,仅为了计算梯度,你就需要承载双倍的参数量。对于动辄几百 GB 显存的 MoE 模型,这几乎判了 PPO 的死刑。</p> <p>其次,也是更本质的问题:<strong>在开放域生成任务中,训练一个准确的 Critic 极其困难。</strong> 面对无限的文本生成空间,Critic 往往难以准确预测长推理链条末端的价值。一个训练不好的 Critic 不仅不能降低方差,反而会向 Actor 注入大量的噪声梯度,导致模型越练越傻。</p> <p>既然 Critic 又贵又笨,DeepSeek 等团队提出了一个大胆的假设:我们是否可以完全抛弃价值网络,直接利用统计学规律来估计优势?</p> <h2 id="deepseek的时代grpo的时代" class="heading-element"><span>2 DeepSeek的时代,GRPO的时代</span> <a href="#deepseek%e7%9a%84%e6%97%b6%e4%bb%a3grpo%e7%9a%84%e6%97%b6%e4%bb%a3" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>为了验证“去 Critic”的可行性,工业界演化出了三条主要的技术路线。它们并非凭空出现,而是为了解决前人在工程落地中遇到的具体“坑”而逐步迭代出来的。</p> <h3 id="grpodeepseek-的减法哲学" class="heading-element"><span>2.1 GRPO:DeepSeek 的“减法”哲学</span> <a href="#grpodeepseek-%e7%9a%84%e5%87%8f%e6%b3%95%e5%93%b2%e5%ad%a6" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><blockquote> <p>核心逻辑:<em>用“组内相对排名”替代“绝对分数预测”。</em></p> </blockquote> <p>DeepSeekMath 和 R1 背后的核心算法是 GRPO。它的直觉非常朴素:在一个复杂的数学推理任务中,与其训练一个 Critic 去预测“这一步能得 0.8 分还是 0.9 分”(这很难且不准),不如直接让模型对同一个问题生成一组(比如 64 个)答案,然后看看<strong>谁做得比平均水平好</strong>。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/11/GRPO-vs-PPO.avif" alt="GRPO-vs-PPO" srcset="https://cdn1.zair.top/images/2025/11/GRPO-vs-PPO.avif?size=small, https://cdn1.zair.top/images/2025/11/GRPO-vs-PPO.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/11/GRPO-vs-PPO.avif?size=large 2x" data-title="GRPO-vs-PPO" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>对于同一个 Prompt,GRPO 会让模型采样生成一组(Group)答案。算法计算这组答案的平均奖励作为基线(Baseline)。凡是得分高于平均值的答案,就获得正向的梯度更新;反之则受到惩罚。</p> <p>其核心优势函数(Advantage)不再依赖 $V(s)$,而是完全基于组内统计: $$A_i = \frac{r_i - \text{mean}({r_1&hellip;r_G})}{\text{std}({r_1&hellip;r_G}) + \epsilon}$$</p> <p>这种方法巧妙地利用了组内统计量替代了 Critic 的价值估计,直接将显存占用减半。它证明了在数理逻辑等有明确判别标准的任务中,相对优势比绝对价值更有效。</p> <p><strong>算法执行</strong>: 1. 对于同一个 Prompt $q$,采样生成 $G$ 个输出 ${o_1, o_2, &hellip;, o_G}$。 2. 计算这组输出的奖励 ${r_1, &hellip;, r_G}$(通常由规则或轻量级模型给出)。 3. 计算组内平均奖励 $\mu$ 和标准差 $\sigma$。 4. <strong>计算优势(Advantage)</strong>:$A_i = (r_i - \mu) / (\sigma + \epsilon)$。 5. <strong>更新策略</strong>:如果 $A_i &gt; 0$,则提高该输出中所有 Token 的生成概率;反之则降低。</p> <p>这种方法巧妙地利用了组内统计量作为动态基线(Dynamic Baseline),直接去掉了 Critic 模型,将显存占用减去1/3.</p> <h3 id="gspoqwen-的维稳改良" class="heading-element"><span>2.2 GSPO:Qwen 的“维稳”改良</span> <a href="#gspoqwen-%e7%9a%84%e7%bb%b4%e7%a8%b3%e6%94%b9%e8%89%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><blockquote> <p>核心逻辑:<em>修正 Token 级更新的数学偏差,用“整体主义”拯救 MoE。</em></p> </blockquote> <p>当阿里云 Qwen 团队试图将 GRPO 应用于超大规模 <strong>MoE(混合专家)</strong> 模型(如 Qwen2.5-Math)时,他们遇到了严重的训练稳定性问题。他们发现 GRPO 存在一个隐蔽的数学缺陷:<strong>奖励的粒度与更新的粒度不匹配。</strong></p> <p>在 GRPO 中,我们得到的是整个序列的奖励(这道题做对了吗?),但我们在计算梯度时,是针对每个 Token 单独计算概率比率(Importance Ratio)的。这就好比一个团队拿了奖金,GRPO 简单粗暴地认为每个成员(Token)的贡献是一样的。这种近似在稠密模型(Dense)上还能凑合,但在对噪声极度敏感的 MoE 模型上,会导致梯度的方差极大,极易引发模型崩溃。</p> <p><strong>GSPO 的改进细节</strong>: 不再纠结于单个 Token 的得失,而是将优化的视角拉高到了 <strong>整个序列(Sequence)</strong> 层级。</p> <ul> <li><strong>序列级重要性采样(Sequence-Level IS)</strong>: GSPO 计算的是<strong>整个生成序列的联合概率比率</strong>,而不是单个 Token 比率的平均值。 $$\rho_{seq} = \frac{P_{\text{new}}(\text{整个句子})}{P_{\text{old}}(\text{整个句子})} = \prod_{t=1}^T \frac{\pi_\theta(y_t | y_{&lt;t}, x)}{\pi_{\theta_{old}}(y_t | y_{&lt;t}, x)}$$</li> <li><strong>整体截断</strong>: 它根据这个 $\rho_{seq}$ 对整个序列的更新幅度进行截断(Clip)。这意味着,模型要么“全盘接受”这个序列的更新信号,要么“全盘拒绝”,保证了更新方向的一致性。</li> </ul> <p>这种“整体主义”的策略,从数学上消除了 Credit Assignment(信用分配)带来的噪声,是目前在大规模集群上训练 MoE 模型的一种稳健方案。</p> <h3 id="dapo字节系的效率压榨" class="heading-element"><span>2.3 DAPO:字节系的“效率”压榨</span> <a href="#dapo%e5%ad%97%e8%8a%82%e7%b3%bb%e7%9a%84%e6%95%88%e7%8e%87%e5%8e%8b%e6%a6%a8" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><blockquote> <p>核心逻辑:<em>打破 PPO 的对称性束缚,只在“纠结区”做功。</em></p> </blockquote> <p>字节跳动 Seed 团队提出的 DAPO 并不满足于仅仅“跑通”训练,他们更关注<strong>样本效率</strong>和<strong>策略多样性</strong>。他们发现了传统算法的两个低效之处:</p> <ol> <li><strong>PPO 的截断太保守</strong>:PPO 默认将更新幅度限制在 $[1-\epsilon, 1+\epsilon]$(例如 0.8 到 1.2 倍)。这对称地限制了变好和变坏的幅度。</li> <li><strong>简单样本浪费算力</strong>:如果一组 Prompt 模型全做对了,或者全做错了,梯度信息量其实很低,继续训练就是浪费。</li> </ol> <p>DAPO 针对这两点进行了外科手术式的改良:</p> <ul> <li> <p><strong>解耦截断(Asymmetric Clip)—— 鼓励天才的灵光一现</strong>: DAPO 认为,“变好”和“变坏”的风险是不对等的。</p> <ul> <li>如果模型发现了一条前所未有的高分路径($A &gt; 0$),这是一次宝贵的探索(Exploration),我们不应该死板地限制在 1.2 倍。DAPO 引入了一个更大的上限 $\delta$(比如允许更新到 1.5 倍或更高)。</li> <li>如果模型表现变差了($A &lt; 0$),则依然严格限制下限,防止模型崩溃。 这种非对称设计(Clip-Higher)有效地缓解了 RL 训练后期的“熵坍塌”问题,保持了模型的多样性。</li> </ul> </li> <li> <p><strong>动态采样(Dynamic Sampling)—— 拒绝无效刷题</strong>: DAPO 会实时监控每个 Prompt 组的准确率分布。</p> <ul> <li><strong>全对 ($Acc=1$)</strong> 或 <strong>全错 ($Acc=0$)</strong> 的组,其组内方差为 0,优势函数 $A_i$ 趋近于 0,提供的梯度几乎无效。DAPO 会自动降低这些样本的采样权重。</li> <li>算法将算力集中在 <strong>$0 &lt; Acc &lt; 1$</strong> 的“纠结区”。这些是模型“努努力能做对,但不小心会做错”的认知边界,训练性价比最高。</li> </ul> </li> </ul> <p>配合动态采样机制(自动剔除全对或全错的简单样本),DAPO 在 AIME 等评测中证明了:在数学上打破对称性,并配合高质量的采样策略,比单纯的数据堆叠更重要。</p> <h2 id="数学本质与前沿变体" class="heading-element"><span>3 数学本质与前沿变体</span> <a href="#%e6%95%b0%e5%ad%a6%e6%9c%ac%e8%b4%a8%e4%b8%8e%e5%89%8d%e6%b2%bf%e5%8f%98%e4%bd%93" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在表面上,它们都是为了让模型得分更高,但在数学本质上,它们处理的是 <strong>策略梯度(Policy Gradient)中“优势函数(Advantage)”估计的偏差与方差权衡</strong>。</p> <p>所有 PPO 类算法的目标函数都可以抽象为: $$L(\theta) = \mathbb{E} \left[ \min(r_t(\theta) \cdot A_t, \text{clip}(r_t(\theta)) \cdot A_t) \right]$$ 其中 $r_t$ 是新旧策略的概率比率,$A_t$ 是优势函数。三大算法的“手术刀”动在不同的位置:</p> <table> <thead> <tr> <th style="text-align: left">维度</th> <th style="text-align: left"><strong>PPO (传统)</strong></th> <th style="text-align: left"><strong>GRPO (DeepSeek)</strong></th> <th style="text-align: left"><strong>GSPO (Qwen)</strong></th> <th style="text-align: left"><strong>DAPO (ByteDance)</strong></th> </tr> </thead> <tbody> <tr> <td style="text-align: left"><strong>优势 $A_t$ 来源</strong></td> <td style="text-align: left"><strong>价值网络</strong><br>$A = R - V(s)$<br>(需训练 Critic)</td> <td style="text-align: left"><strong>组内统计</strong><br>$A = (R - \mu) / \sigma$<br>(无需 Critic)</td> <td style="text-align: left"><strong>组内统计</strong><br>同 GRPO</td> <td style="text-align: left"><strong>组内统计</strong><br>同 GRPO</td> </tr> <tr> <td style="text-align: left"><strong>比率 $r_t$ 粒度</strong></td> <td style="text-align: left"><strong>Token 级</strong><br>逐词更新</td> <td style="text-align: left"><strong>Token 级</strong><br>原始版存在偏差</td> <td style="text-align: left"><strong>Sequence 级</strong><br>全序列统一权重</td> <td style="text-align: left"><strong>Token 级 + 修正</strong><br>动态调整 Clip 边界</td> </tr> <tr> <td style="text-align: left"><strong>核心数学假设</strong></td> <td style="text-align: left">$V(s)$ 能预测未来</td> <td style="text-align: left">组内方差代表真实方差</td> <td style="text-align: left">序列奖励不可拆分</td> <td style="text-align: left">非对称的更新更高效</td> </tr> </tbody> </table> <p>如果我们剥开这三个算法的工程外衣,会发现它们在本质上都在解决同一个核心问题:<strong>如何在没有 Value Function 的情况下,低方差地估计策略梯度。</strong></p> <p>它们共享一个根本性的数学假设:<strong>蒙特卡洛采样均值可以替代价值网络估计。</strong> 但在如何处理<strong>方差</strong>和<strong>基线估计</strong>上,一些变体给出了更精彩的答案:</p> <ul> <li> <p><strong>Dr. GRPO (GRPO Done Right)</strong></p> <ul> <li><em>问题</em>:GRPO 原文中使用标准差 $\sigma$ 进行归一化。当 Group Size 较小或模型收敛到单一模式时,$\sigma \rightarrow 0$,导致分母极小,Advantage 数值爆炸。</li> <li><em>解法</em>:Dr. GRPO 建议放弃标准差归一化,转而使用 <strong>分位数归一化</strong> 或简单的 <strong>常数归一化</strong>。这让小 Batch Size 的训练更加安全,防止了训练后期的数值不稳定。</li> </ul> </li> <li> <p><strong>RLOO (Reinforce Leave-One-Out)</strong></p> <ul> <li><em>问题</em>:GRPO 使用全组均值(包括自身)作为 Baseline,这在统计学上是有轻微偏差的。</li> <li><em>解法</em>:RLOO 采用“留一法”估计基线。对于第 $i$ 个样本,Baseline 是<strong>除了它自己之外</strong>的所有样本均值: $$Baseline_i = \frac{1}{G-1} \sum_{j \neq i} r_j$$</li> <li><em>意义</em>:这是一个无偏估计量(Unbiased Estimator)。虽然计算复杂度不变,但在数学上更严谨,目前在 HuggingFace TRL 库中,RLOO 往往比原始 GRPO 收敛得更快更稳。</li> </ul> </li> <li> <p><strong>ReMax (Reward Maximization)</strong></p> <ul> <li><em>问题</em>:如果显存连 Group Sampling 都跑不动怎么办?</li> <li><em>解法</em>:ReMax 返璞归真,使用 <strong>Greedy Baseline</strong>。它不进行随机采样,而是拿模型贪婪解码(Greedy Decoding)生成的那个结果作为基线。比贪婪解码好的才奖励,差的就惩罚。它是 GRPO 的极简特例(可以理解为 Group Size=2 的变体),被称为“穷鬼的 GRPO”,适合资源极度受限的场景。但实际上,ReMax 要比GRPO更早提出可以去除Critic这个理念。</li> </ul> </li> </ul> <h2 id="授人以鱼不如授人以渔" class="heading-element"><span>4 授人以鱼不如授人以渔</span> <a href="#%e6%8e%88%e4%ba%ba%e4%bb%a5%e9%b1%bc%e4%b8%8d%e5%a6%82%e6%8e%88%e4%ba%ba%e4%bb%a5%e6%b8%94" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>如果说 GRPO (DeepSeekMath-V1) 解决了“没有 Critic 怎么办”的问题,那么昨天发布的 DeepSeekMath-V2 则回答了“RL 的下一步去向何方”。我们清晰地看到 RL 后训练正在经历一次深刻的范式转移:<strong>从“价值预测(Value Prediction)”转向“自主过程验证(Self Process Verification)”,即这种过程验证,在一定程度上是可以由模型自己完成的。</strong></p> <p>通俗一点说,PPO 类算法是引入一个Critic老师来评估模型的训练过程,老师只告诉你现在做的对不对、好不好,怎么改你自己看着办; GRPO 类算法则是通过模型自己跟“同学”比,自评做得好不好,怎么改还是自己看着办;但 DeepSeekMath-V2 则是给模型请了个家教 (Verifier),又请了个老师(Meta-Verifier)来监督这个家教,做两件事:1. 给模型提供过程奖励,告诉他每一步对错与否 2.培养他自己判断每一步是否正确,并在自己输出最终答案前调整; 这相当于是把老师揣摩多年的出题人评分标准交给模型了。</p> <p>Critic 的消亡几乎已成定局。在一个长达数百步的 Chain-of-Thought 推理中,指望一个神经网络精准预测最终答案的价值是不现实的。但是,DeepSeek 引入的 <strong>Generator-Verifier</strong> 双模型架构揭示了新的可能。</p> <p>现在的 RL 不再是单纯的“跑分比赛”,而是演变成了一种<strong>生成者与验证者的博弈</strong>。Verifier 不再像 Critic 那样试图预测未来,而是扮演“助教”的角色,对 Generator 的每一步推理进行 Step-by-step 的逻辑检查。这种 <strong>Process Reward(过程奖励)</strong> 提供的信号比稀疏的最终结果奖励要丰富得多。</p> <p>这意味着,未来的训练架构将演变为 <strong>Self-Verification Loop(自我验证循环)</strong>。模型不仅是创作者,更是自己的审查者。通过 Verifier 指导 Actor 模型能力提升,Actor 也会反哺 Verifier 提高其鉴别能力,从而实现模型能力的进化。</p> <h2 id="结语" class="heading-element"><span>5 结语</span> <a href="#%e7%bb%93%e8%af%ad" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>RL 后训练的战场已经变了。仅仅一年前,我们还在纠结 PPO 的超参数微调。现在,我们已经扔掉了 Critic,开始在 Sequence Level 上重构算法,甚至引入了专门的 Verifier 模型。</p> <p>在2025年,我们看到了GRPO一族算法的爆发,产生了各类变体: GRPO 的极简主义,GSPO 的工程维稳,RLOO/DAPO 的数学修正&hellip; 在2026年,我们也许会看到许多自验证优化算法(Self-Verify Policy Optimization),就让我们拭目以待,看看一年后能有多少 S*PO 算法吧。</p> <p>但无论如何,它们都在指向同一个终局:依靠人工标注 SFT 的时代已成过去,模型自我博弈、自我进化的 RL 时代已经全面到来。</p> <hr> <h2 id="参考" class="heading-element"><span>6 参考</span> <a href="#%e5%8f%82%e8%80%83" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li> <p><strong>GRPO (Group Relative Policy Optimization)</strong><br> DeepSeek. &ldquo;DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning.&rdquo; arXiv:2501.12948, 2025.<br> <a href="https://arxiv.org/abs/2501.12948"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2501.12948<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>GSPO (Group Sequence Policy Optimization)</strong><br> Qwen Team. &ldquo;Group Sequence Policy Optimization for Large-Scale Alignment.&rdquo; arXiv:2507.18071, 2025.<br> <a href="https://arxiv.org/abs/2507.18071"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2507.18071<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>DAPO (Dynamic Asymmetric Policy Optimization)</strong><br> ByteDance Seed Team. &ldquo;DAPO: An Open-Source LLM Reinforcement Learning System at Scale.&rdquo; arXiv:2503.14476, 2025.<br> <a href="https://arxiv.org/abs/2503.14476"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2503.14476<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>Dr. GRPO (GRPO Done Right)</strong><br> Anonymous. &ldquo;Understanding R1-Zero-Like Training: A Critical Perspective on GRPO Instability.&rdquo; arXiv:2503.20783, 2025.<br> <a href="https://arxiv.org/abs/2503.20783"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2503.20783<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>RLOO (Reinforce Leave-One-Out)</strong><br> Dong et al. &ldquo;Revisiting REINFORCE-Style Optimization for Learning from Human Feedback.&rdquo; arXiv:2402.14740, 2024.<br> <a href="https://arxiv.org/abs/2402.14740"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2402.14740<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>ReMax (Reward Maximization with Greedy Baseline)</strong><br> Park et al. &ldquo;ReMax: A Simple, Effective, and Efficient Reinforcement Learning Method for Aligning LLMs.&rdquo; arXiv:2310.10505, 2023.<br> <a href="https://arxiv.org/abs/2310.10505"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2310.10505<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>Generator-Verifier Architecture &amp; Process Reward (DeepSeekMath-V2)</strong><br> DeepSeek Team. &ldquo;DeepSeekMath-V2: Towards Self-Verifiable Mathematical Reasoning.&rdquo; Technical Report, 2025-11-26.<br> <a href="https://huggingface.co/deepseek-ai/DeepSeekMath-V2/technical-report"target="_blank" rel="external nofollow noopener noreferrer">https://huggingface.co/deepseek-ai/DeepSeekMath-V2/technical-report<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> <li> <p><strong>Self-Verification Loop</strong><br> Li et al. &ldquo;Trust, But Verify: A Self-Verification Approach to Reinforcement Learning from Human Feedback.&rdquo; arXiv:2505.13445, 2025.<br> <a href="https://arxiv.org/abs/2505.13445"target="_blank" rel="external nofollow noopener noreferrer">https://arxiv.org/abs/2505.13445<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> </li> </ol> <blockquote> <p>AI声明:首图由Gemini创建;观点与思考来自作者,整理与编写来自Gemini-3-Pro-Preview;作者对本文内容负责;</p> </blockquote>

2025/11/28
阅读更多

个人数据低成本自动化备份方案

<blockquote> <p>我们的生活和工作越来越依赖于各种数据。从重要的工作文档到珍贵的个人照片,这些数据承载着我们的记忆和价值。然而,硬件故障、系统崩溃、恶意软件攻击等风险随时可能导致数据丢失。本文将介绍一套基于Rclone和云盘的低成本个人数据备份方案,能够有效保护这些个人数据。</p> </blockquote> <h2 id="数据备份的目的" class="heading-element"><span>1 数据备份的目的</span> <a href="#%e6%95%b0%e6%8d%ae%e5%a4%87%e4%bb%bd%e7%9a%84%e7%9b%ae%e7%9a%84" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>生活中充满各种可能导致数据丢失的意外情况。硬件故障是最常见的原因之一,硬盘的平均使用寿命有限,SSD通常可靠使用3-5年。系统错误也是数据丢失的常见原因,特别是操作系统更新失败可能导致文件系统损坏。此外,恶意软件如勒索病毒可能加密你的所有文件,人为错误可能导致重要文件被误删除或覆盖,如果设备丢失或被盗则可能同时失去物理设备和设备上的数据。甚至有时候某些软件服务进行一次大更新,都有可能导致数据损坏。</p> <p>想象一下,当你的笔记本电脑突然无法开机,或者重要文件被误删,如果没有备份,你可能需要花费数小时甚至数天来重新安装系统、配置环境和重建文件。有了完善的备份方案,你可以在短时间内恢复到之前的工作状态,最大限度减少生产力损失。</p> <h2 id="个人数据分类" class="heading-element"><span>2 个人数据分类</span> <a href="#%e4%b8%aa%e4%ba%ba%e6%95%b0%e6%8d%ae%e5%88%86%e7%b1%bb" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>低成本不仅是金钱成本,更是管理成本。在设计备份方案前,我们需要先明确有哪些数据需要备份。下表列出了常见的个人数据类型及其特点:</p> <table> <thead> <tr> <th>数据类型</th> <th>包含内容</th> <th>重要性</th> <th>更新频率</th> <th>备份难度</th> </tr> </thead> <tbody> <tr> <td>软件配置</td> <td>IDE设置、终端配置(.bashrc)、应用程序设置、服务程序配置</td> <td>中</td> <td>低</td> <td>低</td> </tr> <tr> <td>程序数据</td> <td>数据库文件、应用程序生成的数据、游戏存档</td> <td>中-高</td> <td>中</td> <td>中</td> </tr> <tr> <td>媒体文件</td> <td>音乐、电影、视频、播客</td> <td>低-中</td> <td>低</td> <td>高(体积大)</td> </tr> <tr> <td>代码和文档</td> <td>项目源代码、技术文档、学习笔记、论文</td> <td>高</td> <td>高</td> <td>低</td> </tr> <tr> <td>个人照片和视频</td> <td>家庭照片、旅行视频、生活记录</td> <td>极高</td> <td>中</td> <td>中-高</td> </tr> <tr> <td>社交媒体数据</td> <td>微信聊天记录、其他社交平台内容</td> <td>高</td> <td>高</td> <td>高</td> </tr> </tbody> </table> <p>不同类型的数据价值和可替代性各不相同,因此备份策略也应有所区别。例如,软件配置和代码可以通过Git进行版本控制和备份,而个人照片等不可替代的数据则需要更严格的备份机制。在规划备份方案时,应该把成本优先分配给那些重要性高、难以重建的数据。</p> <h2 id="数据备份原则" class="heading-element"><span>3 数据备份原则</span> <a href="#%e6%95%b0%e6%8d%ae%e5%a4%87%e4%bb%bd%e5%8e%9f%e5%88%99" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>高效的备份方案应遵循几个核心原则。首先是广为人知的<strong>3-2-1备份策略</strong>:保留至少3份数据副本,使用至少2种不同的存储媒介(如本地硬盘和云存储),并确保至少1份备份存储在异地(防止火灾、洪水等物理灾害)。这一策略为数据提供了多层保护,即使在最坏的情况下也能保证数据安全。</p> <p>自动化是另一个重要原则。人工备份容易被遗忘或拖延,特别是在工作繁忙的时期。通过设置<strong>自动化备份流程</strong>,可以确保备份按计划进行,减少人为因素的干扰。理想的备份系统应该是&quot;设置一次,长期运行&quot;,只需偶尔检查确认备份状态。</p> <p><strong>定期验证备份</strong>的完整性和可恢复性是保障数据安全的关键步骤。备份数据如果无法恢复就毫无意义。至少每季度应进行一次恢复测试,确保在需要时能够成功恢复数据。这种测试不仅验证了备份的有效性,还能让你熟悉恢复流程,在真正需要恢复时不会手忙脚乱。</p> <p>对于重要数据,还需要适当的<strong>冗余备份</strong>。不同重要级别的数据可以有不同的备份策略。最重要的个人数据,如家庭照片或关键文档,就需要多个备份副本;而容易重新获取的数据,如可重新下载的媒体文件,则可以采用较为简单的备份策略,甚至使用流媒体而不备份。</p> <h2 id="基于rclone和云盘的低成本数据备份方案" class="heading-element"><span>4 基于Rclone和云盘的低成本数据备份方案</span> <a href="#%e5%9f%ba%e4%ba%8erclone%e5%92%8c%e4%ba%91%e7%9b%98%e7%9a%84%e4%bd%8e%e6%88%90%e6%9c%ac%e6%95%b0%e6%8d%ae%e5%a4%87%e4%bb%bd%e6%96%b9%e6%a1%88" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>本文结合开源工具 Rclone 和 Alist, 利用各类云存储服务,构建一套自动化、安全且经济实惠的个人数据备份系统。Rclone是一款功能强大的命令行工具,可以同步本地文件到多种云存储服务,并提供加密功能保护数据隐私;Alist则起到对Rclone的补充作用,很多网盘和存储源无法直接使用Rclone挂载,就需要通过Alist挂载为Webdav。结合适当的脚本和定时任务,我们就可以实现全自动的备份流程。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/05/7e51b1464ce9072d4918a420692de6f5.avif" alt="利用Rclone和Alist进行数据备份示意图" srcset="https://cdn1.zair.top/images/2025/05/7e51b1464ce9072d4918a420692de6f5.avif?size=small, https://cdn1.zair.top/images/2025/05/7e51b1464ce9072d4918a420692de6f5.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/05/7e51b1464ce9072d4918a420692de6f5.avif?size=large 2x" data-title="利用Rclone和Alist进行数据备份示意图" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h3 id="方案概述" class="heading-element"><span>4.1 方案概述</span> <a href="#%e6%96%b9%e6%a1%88%e6%a6%82%e8%bf%b0" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在这套方案中,软件配置和代码将通过Git进行版本控制和备份,这是因为Git天然适合跟踪这类文本文件的变化,并提供完整的历史记录。而对于其他类型的个人数据,如文档、照片和媒体文件,我们将使用Rclone同步到加密的云存储。备份频率和副本数量可以根据数据重要性灵活调整,确保最重要的数据得到最全面的保护。</p> <p>比如就我个人而言,配置和代码使用Github和Gitlab,文档和媒体使用Notion,程序数据以中频率备份到国内和国外的三个不同网盘上,个人照片则以低频率再额外备份到亚马逊云的对象存储AWS S3上(3网盘 + 1 OSS + 1 照片服务器 + 若干拍摄设备, 一份照片至少存了6份 😢)。</p> <p>这个方案的核心优势在于低成本、高安全性和自动化程度高。只要选择合适的云存储提供商(如Google Drive的教育优惠、 微软E5账号、 路边捡的对象存储),成本可以控制在较低水平;通过Rclone的加密功能,数据在云端存储时始终保持加密状态,既能保护数据隐私,也可以避免因为触碰国内网盘奇怪的规则而被封号;通过脚本和定时任务,备份过程完全自动化,无需人工干预。</p> <h3 id="需求确认" class="heading-element"><span>4.2 需求确认</span> <a href="#%e9%9c%80%e6%b1%82%e7%a1%ae%e8%ae%a4" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在实施方案前,我们先明确具体需求。本备份方案将直接备份源目录中的文件,而不是创建压缩包,这样方便随时查看和恢复单个文件。备份策略采用每月全量备份,每周增量备份,并滚动保留3个全量副本,平衡了存储空间和数据安全。为了支持多设备环境,远程路径将包含主机名和源目录名称,便于识别不同设备的备份。</p> <p>使用Rclone的<code>--backup-dir</code>功能,可以将删除或覆盖的文件存档到专门的目录(如<code>deleted_时间戳</code>),并自动清理这些存档,只保留最近3个。这样既能恢复误删的文件,又不会无限制占用存储空间。最后,通过Webhook通知机制,可以通过微信实时了解备份状态,包括常规通知(开始、成功、结束)和异常通知(备份失败或意外中断)。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/05/b85a7ae5c122c7ec54f8cc374be8fdcc.avif" alt="备份数据的多个时间副本" srcset="https://cdn1.zair.top/images/2025/05/b85a7ae5c122c7ec54f8cc374be8fdcc.avif?size=small, https://cdn1.zair.top/images/2025/05/b85a7ae5c122c7ec54f8cc374be8fdcc.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/05/b85a7ae5c122c7ec54f8cc374be8fdcc.avif?size=large 2x" data-title="备份数据的多个时间副本" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h3 id="方案设计" class="heading-element"><span>4.3 方案设计</span> <a href="#%e6%96%b9%e6%a1%88%e8%ae%be%e8%ae%a1" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><h3 id="安装与配置rclone" class="heading-element"><span>4.4 安装与配置Rclone</span> <a href="#%e5%ae%89%e8%a3%85%e4%b8%8e%e9%85%8d%e7%bd%aerclone" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>首先需要安装Rclone并进行基本配置。在大多数Linux系统上,可以通过包管理器安装:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装Rclone</span> </span></span><span class="line"><span class="cl">sudo apt install rclone </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 配置Rclone(交互式)</span> </span></span><span class="line"><span class="cl">rclone config</span></span></code></pre></td></tr></table> </div> </div><p>在配置过程中,需要设置两个远程存储:一个直接连接到云存储服务(如Google Drive),另一个在此基础上添加加密层。这种加密远程的设置确保即使云服务提供商可以访问你的文件,也无法查看文件内容,有效保护个人隐私。</p> <p>Rclone的配置过程是交互式的,需要按照提示输入相关信息,包括选择云存储提供商、授权访问、设置加密密码等。比如要连接Alist,就选择webdav驱动,具体步骤可参考<a href="https://rclone.org/webdav/"target="_blank" rel="external nofollow noopener noreferrer">官方文档<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>或相关教程。完成配置后,可以通过简单的命令测试连接是否正常:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rclone lsd gdrive: </span></span><span class="line"><span class="cl">rclone lsd gdrive-crypt:</span></span></code></pre></td></tr></table> </div> </div><h3 id="备份脚本" class="heading-element"><span>4.5 备份脚本</span> <a href="#%e5%a4%87%e4%bb%bd%e8%84%9a%e6%9c%ac" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>核心的备份功能由一个名为<code>backup_files_with_backup_dir_cleanup.sh</code>的脚本实现。该脚本负责同步本地文件到云存储,管理备份历史,并发送状态通知。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt"> 10 </span><span class="lnt"> 11 </span><span class="lnt"> 12 </span><span class="lnt"> 13 </span><span class="lnt"> 14 </span><span class="lnt"> 15 </span><span class="lnt"> 16 </span><span class="lnt"> 17 </span><span class="lnt"> 18 </span><span class="lnt"> 19 </span><span class="lnt"> 20 </span><span class="lnt"> 21 </span><span class="lnt"> 22 </span><span class="lnt"> 23 </span><span class="lnt"> 24 </span><span class="lnt"> 25 </span><span class="lnt"> 26 </span><span class="lnt"> 27 </span><span class="lnt"> 28 </span><span class="lnt"> 29 </span><span class="lnt"> 30 </span><span class="lnt"> 31 </span><span class="lnt"> 32 </span><span class="lnt"> 33 </span><span class="lnt"> 34 </span><span class="lnt"> 35 </span><span class="lnt"> 36 </span><span class="lnt"> 37 </span><span class="lnt"> 38 </span><span class="lnt"> 39 </span><span class="lnt"> 40 </span><span class="lnt"> 41 </span><span class="lnt"> 42 </span><span class="lnt"> 43 </span><span class="lnt"> 44 </span><span class="lnt"> 45 </span><span class="lnt"> 46 </span><span class="lnt"> 47 </span><span class="lnt"> 48 </span><span class="lnt"> 49 </span><span class="lnt"> 50 </span><span class="lnt"> 51 </span><span class="lnt"> 52 </span><span class="lnt"> 53 </span><span class="lnt"> 54 </span><span class="lnt"> 55 </span><span class="lnt"> 56 </span><span class="lnt"> 57 </span><span class="lnt"> 58 </span><span class="lnt"> 59 </span><span class="lnt"> 60 </span><span class="lnt"> 61 </span><span class="lnt"> 62 </span><span class="lnt"> 63 </span><span class="lnt"> 64 </span><span class="lnt"> 65 </span><span class="lnt"> 66 </span><span class="lnt"> 67 </span><span class="lnt"> 68 </span><span class="lnt"> 69 </span><span class="lnt"> 70 </span><span class="lnt"> 71 </span><span class="lnt"> 72 </span><span class="lnt"> 73 </span><span class="lnt"> 74 </span><span class="lnt"> 75 </span><span class="lnt"> 76 </span><span class="lnt"> 77 </span><span class="lnt"> 78 </span><span class="lnt"> 79 </span><span class="lnt"> 80 </span><span class="lnt"> 81 </span><span class="lnt"> 82 </span><span class="lnt"> 83 </span><span class="lnt"> 84 </span><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="c1">##### 配置区域 #####</span> </span></span><span class="line"><span class="cl"><span class="nv">REMOTE</span><span class="o">=</span><span class="s2">&#34;gdrive-crypt:&#34;</span> </span></span><span class="line"><span class="cl"><span class="nv">WEBHOOK_KEY1</span><span class="o">=</span><span class="s2">&#34;693axxx6-7aoc-4bc4-97a0-0ec2sifa5aaa&#34;</span> <span class="c1"># 常规通知 Key</span> </span></span><span class="line"><span class="cl"><span class="nv">WEBHOOK_KEY2</span><span class="o">=</span><span class="s2">&#34;another-key-for-errors-xxx&#34;</span> <span class="c1"># 异常通知 Key</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 需要备份的源目录</span> </span></span><span class="line"><span class="cl"><span class="nv">SOURCE_DIRS</span><span class="o">=(</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;/etc/nginx&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;/var/lib/mysql&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;/home/user/docs&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;/home/user/photos&#34;</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;/var/lib/docker&#34;</span> </span></span><span class="line"><span class="cl"><span class="o">)</span> </span></span><span class="line"><span class="cl"><span class="c1">##### 结束配置区域 #####</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nv">TIMESTAMP</span><span class="o">=</span><span class="k">$(</span>date +%Y%m%d_%H%M%S<span class="k">)</span> </span></span><span class="line"><span class="cl"><span class="nv">HOSTNAME</span><span class="o">=</span><span class="k">$(</span>hostname<span class="k">)</span> <span class="c1"># 获取当前主机名</span> </span></span><span class="line"><span class="cl"><span class="nv">LOG_FILE</span><span class="o">=</span><span class="s2">&#34;backup_</span><span class="si">${</span><span class="nv">HOSTNAME</span><span class="si">}</span><span class="s2">_</span><span class="nv">$TIMESTAMP</span><span class="s2">.log&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 发送 Webhook 通知函数(企业微信格式)</span> </span></span><span class="line"><span class="cl">send_webhook<span class="o">()</span> <span class="o">{</span> </span></span><span class="line"><span class="cl"> <span class="nb">local</span> <span class="nv">key</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">local</span> <span class="nv">status</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$2</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">local</span> <span class="nv">message</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$3</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">local</span> <span class="nv">url</span><span class="o">=</span><span class="s2">&#34;&lt;https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=</span><span class="nv">$key</span><span class="s2">&gt;&#34;</span> </span></span><span class="line"><span class="cl"> curl <span class="s2">&#34;</span><span class="nv">$url</span><span class="s2">&#34;</span> <span class="se">\\</span> </span></span><span class="line"><span class="cl"> -H <span class="s1">&#39;Content-Type: application/json&#39;</span> <span class="se">\\</span> </span></span><span class="line"><span class="cl"> -d <span class="s2">&#34;{\\&#34;</span>msgtype<span class="se">\\</span><span class="s2">&#34;: \\&#34;</span>text<span class="se">\\</span><span class="s2">&#34;, \\&#34;</span>text<span class="se">\\</span><span class="s2">&#34;: {\\&#34;</span>content<span class="se">\\</span><span class="s2">&#34;: \\&#34;</span><span class="o">[</span><span class="nv">$status</span><span class="o">]</span> <span class="nv">$message</span> on <span class="nv">$HOSTNAME</span> at <span class="nv">$TIMESTAMP</span><span class="se">\\</span><span class="s2">&#34;}}&#34;</span> <span class="se">\\</span> </span></span><span class="line"><span class="cl"> 2&gt;&gt;<span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"><span class="o">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 异常退出处理(使用 Key2)</span> </span></span><span class="line"><span class="cl"><span class="nb">trap</span> <span class="s1">&#39;send_webhook &#34;$WEBHOOK_KEY2&#34; &#34;ERROR&#34; &#34;Backup aborted unexpectedly&#34;; exit 1&#39;</span> ERR INT TERM </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 开始备份(使用 Key1)</span> </span></span><span class="line"><span class="cl">send_webhook <span class="s2">&#34;</span><span class="nv">$WEBHOOK_KEY1</span><span class="s2">&#34;</span> <span class="s2">&#34;START&#34;</span> <span class="s2">&#34;Backup process started&#34;</span> </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Backup started at </span><span class="k">$(</span>date<span class="k">)</span><span class="s2"> on </span><span class="nv">$HOSTNAME</span><span class="s2">&#34;</span> &gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 检查是否为月初(全量备份)</span> </span></span><span class="line"><span class="cl"><span class="nv">DAY</span><span class="o">=</span><span class="k">$(</span>date +%d<span class="k">)</span> </span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$DAY</span><span class="s2">&#34;</span> -le <span class="m">7</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="c1"># 第一周视为月初</span> </span></span><span class="line"><span class="cl"> <span class="nv">BACKUP_TYPE</span><span class="o">=</span><span class="s2">&#34;full&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;Performing full backup on </span><span class="nv">$HOSTNAME</span><span class="s2">...&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"><span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="nv">BACKUP_TYPE</span><span class="o">=</span><span class="s2">&#34;incremental&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;Performing incremental backup on </span><span class="nv">$HOSTNAME</span><span class="s2">...&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"><span class="k">fi</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 遍历源目录并上传文件</span> </span></span><span class="line"><span class="cl"><span class="k">for</span> dir in <span class="s2">&#34;</span><span class="si">${</span><span class="nv">SOURCE_DIRS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> <span class="nv">dir_name</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&#34;</span><span class="nv">$dir</span><span class="s2">&#34;</span><span class="k">)</span> <span class="c1"># 获取源目录的最后一级名称</span> </span></span><span class="line"><span class="cl"> <span class="nv">backup_name</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">BACKUP_TYPE</span><span class="si">}</span><span class="s2">_</span><span class="nv">$TIMESTAMP</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nv">remote_path</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">/</span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">/</span><span class="nv">$backup_name</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nv">backup_dir</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">/</span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">/deleted_</span><span class="nv">$TIMESTAMP</span><span class="s2">&#34;</span> <span class="c1"># 删除文件存档目录</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 同步文件到远程,使用 --backup-dir 存档删除/覆盖的文件</span> </span></span><span class="line"><span class="cl"> rclone sync <span class="s2">&#34;</span><span class="nv">$dir</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$remote_path</span><span class="s2">&#34;</span> <span class="se">\\</span> </span></span><span class="line"><span class="cl"> --progress <span class="se">\\</span> </span></span><span class="line"><span class="cl"> --log-file<span class="o">=</span><span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> <span class="se">\\</span> </span></span><span class="line"><span class="cl"> --exclude <span class="s2">&#34;*.log&#34;</span> <span class="se">\\</span> </span></span><span class="line"><span class="cl"> --max-size 1G <span class="se">\\</span> </span></span><span class="line"><span class="cl"> --backup-dir <span class="s2">&#34;</span><span class="nv">$backup_dir</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 检查同步结果</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> -eq <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;Synced </span><span class="nv">$dir</span><span class="s2"> to </span><span class="nv">$remote_path</span><span class="s2"> successfully (deleted files archived to </span><span class="nv">$backup_dir</span><span class="s2">)&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;Failed to sync </span><span class="nv">$dir</span><span class="s2"> to </span><span class="nv">$remote_path</span><span class="s2">&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> send_webhook <span class="s2">&#34;</span><span class="nv">$WEBHOOK_KEY2</span><span class="s2">&#34;</span> <span class="s2">&#34;ERROR&#34;</span> <span class="s2">&#34;Failed to sync </span><span class="nv">$dir_name</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">exit</span> <span class="m">1</span> </span></span><span class="line"><span class="cl"> <span class="k">fi</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 滚动保留 3 个全量副本</span> </span></span><span class="line"><span class="cl"> <span class="nv">FULL_BACKUPS</span><span class="o">=</span><span class="k">$(</span>rclone lsf <span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">/</span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">&#34;</span> <span class="p">|</span> grep <span class="s2">&#34;^full_&#34;</span> <span class="p">|</span> sort -r<span class="k">)</span> </span></span><span class="line"><span class="cl"> <span class="nv">FULL_COUNT</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$FULL_BACKUPS</span><span class="s2">&#34;</span> <span class="p">|</span> wc -l<span class="k">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$FULL_COUNT</span><span class="s2">&#34;</span> -gt <span class="m">3</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> </span></span><span class="line"><span class="cl"> <span class="nv">DELETE_COUNT</span><span class="o">=</span><span class="k">$((</span>FULL_COUNT <span class="o">-</span> <span class="m">3</span><span class="k">))</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$FULL_BACKUPS</span><span class="s2">&#34;</span> <span class="p">|</span> tail -n <span class="s2">&#34;</span><span class="nv">$DELETE_COUNT</span><span class="s2">&#34;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r old_backup<span class="p">;</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> rclone purge <span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">/</span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">/</span><span class="nv">$old_backup</span><span class="s2">&#34;</span> 2&gt;&gt;<span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;Deleted old full backup: </span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">/</span><span class="nv">$old_backup</span><span class="s2">&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">done</span> </span></span><span class="line"><span class="cl"> <span class="k">fi</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># 滚动保留 3 个 deleted_* 目录</span> </span></span><span class="line"><span class="cl"> <span class="nv">DELETED_DIRS</span><span class="o">=</span><span class="k">$(</span>rclone lsf <span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">/</span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">&#34;</span> <span class="p">|</span> grep <span class="s2">&#34;^deleted_&#34;</span> <span class="p">|</span> sort -r<span class="k">)</span> </span></span><span class="line"><span class="cl"> <span class="nv">DELETED_COUNT</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$DELETED_DIRS</span><span class="s2">&#34;</span> <span class="p">|</span> wc -l<span class="k">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$DELETED_COUNT</span><span class="s2">&#34;</span> -gt <span class="m">3</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span> </span></span><span class="line"><span class="cl"> <span class="nv">DELETE_COUNT</span><span class="o">=</span><span class="k">$((</span>DELETED_COUNT <span class="o">-</span> <span class="m">3</span><span class="k">))</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$DELETED_DIRS</span><span class="s2">&#34;</span> <span class="p">|</span> tail -n <span class="s2">&#34;</span><span class="nv">$DELETE_COUNT</span><span class="s2">&#34;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r old_deleted<span class="p">;</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> rclone purge <span class="s2">&#34;</span><span class="nv">$REMOTE</span><span class="s2">/</span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">/</span><span class="nv">$old_deleted</span><span class="s2">&#34;</span> 2&gt;&gt;<span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">echo</span> <span class="s2">&#34;Deleted old deleted directory: </span><span class="nv">$HOSTNAME</span><span class="s2">/</span><span class="nv">$dir_name</span><span class="s2">/</span><span class="nv">$old_deleted</span><span class="s2">&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">done</span> </span></span><span class="line"><span class="cl"> <span class="k">fi</span> </span></span><span class="line"><span class="cl"><span class="k">done</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 备份完成(使用 Key1)</span> </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Backup completed at </span><span class="k">$(</span>date<span class="k">)</span><span class="s2"> on </span><span class="nv">$HOSTNAME</span><span class="s2">&#34;</span> &gt;&gt; <span class="s2">&#34;</span><span class="nv">$LOG_FILE</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl">send_webhook <span class="s2">&#34;</span><span class="nv">$WEBHOOK_KEY1</span><span class="s2">&#34;</span> <span class="s2">&#34;SUCCESS&#34;</span> <span class="s2">&#34;Backup completed successfully&#34;</span> </span></span><span class="line"><span class="cl">send_webhook <span class="s2">&#34;</span><span class="nv">$WEBHOOK_KEY1</span><span class="s2">&#34;</span> <span class="s2">&#34;END&#34;</span> <span class="s2">&#34;Backup process finished&#34;</span></span></span></code></pre></td></tr></table> </div> </div><p>脚本的主要功能包括:</p> <ul> <li>使用Rclone同步本地文件到加密的云存储</li> <li>将删除或覆盖的文件存档到特定目录</li> <li>每月创建全量备份,用于长期保存</li> <li>自动清理旧备份,只保留最近的几个版本</li> <li>通过企业微信Webhook发送备份状态通知</li> <li>详细记录备份过程,便于故障排查</li> </ul> <p>将脚本设置为可执行并移动到系统路径:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x backup.sh </span></span><span class="line"><span class="cl">sudo mv backup.sh /usr/local/bin/</span></span></code></pre></td></tr></table> </div> </div><h3 id="设置定时任务" class="heading-element"><span>4.6 设置定时任务</span> <a href="#%e8%ae%be%e7%bd%ae%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>通过crontab设置定时任务,实现备份自动化:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">crontab -e</span></span></code></pre></td></tr></table> </div> </div><p>添加以下内容,设置每周日凌晨2点自动执行备份:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">0 2 * * 0 /usr/local/bin/backup.sh</span></span></code></pre></td></tr></table> </div> </div><p>这样,备份过程就会定期自动执行,无需人工干预。时间选择在凌晨是为了避免备份过程影响正常使用,同时网络通常在这个时段比较空闲,有利于数据上传。如果系统在预定时间关机,可以考虑使用<code>anacron</code>代替<code>cron</code>,或者设置多个备份时间点增加冗余。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/05/23f11b9932af042c2f55055711ab99b7.avif" alt="备份过程通知" srcset="https://cdn1.zair.top/images/2025/05/23f11b9932af042c2f55055711ab99b7.avif?size=small, https://cdn1.zair.top/images/2025/05/23f11b9932af042c2f55055711ab99b7.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/05/23f11b9932af042c2f55055711ab99b7.avif?size=large 2x" data-title="备份过程通知" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h3 id="webhook通知系统" class="heading-element"><span>4.7 Webhook通知系统</span> <a href="#webhook%e9%80%9a%e7%9f%a5%e7%b3%bb%e7%bb%9f" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>通过Webhook通知系统,可以实时了解备份状态。以下是通知消息示例:</p> <p><strong>常规通知(使用Key1)</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[START] Backup process started on node1 at 20250227_020000 </span></span><span class="line"><span class="cl">[SUCCESS] Backup completed successfully on node1 at 20250227_020000 </span></span><span class="line"><span class="cl">[END] Backup process finished on node1 at 20250227_020000</span></span></code></pre></td></tr></table> </div> </div><p><strong>异常通知(使用Key2)</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[ERROR] Failed to sync docs on node1 at 20250227_020000 </span></span><span class="line"><span class="cl">[ERROR] Backup aborted unexpectedly on node1 at 20250227_020000</span></span></code></pre></td></tr></table> </div> </div><p>这些通知可以集成到各种平台,如Slack、Microsoft Teams、钉钉或自定义应用程序。通过区分常规通知和异常通知,可以更有针对性地处理备份问题,只有在出现异常时才需要人工干预。通知系统是备份方案的重要组成部分,它让备份过程变得透明,有助于及时发现和解决问题。</p> <h3 id="验证与恢复" class="heading-element"><span>4.8 验证与恢复</span> <a href="#%e9%aa%8c%e8%af%81%e4%b8%8e%e6%81%a2%e5%a4%8d" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>备份系统最终目的是在需要时能够恢复数据。因此,定期验证备份并熟悉恢复流程是必不可少的。以下是常用的验证和恢复命令:</p> <p><strong>验证备份文件</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 检查正常备份</span> </span></span><span class="line"><span class="cl">rclone ls gdrive-crypt:/node1/docs/full_20250227_020000 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 检查删除存档</span> </span></span><span class="line"><span class="cl">rclone ls gdrive-crypt:/node1/docs/deleted_20250227_020000 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 验证保留数量</span> </span></span><span class="line"><span class="cl">rclone lsf gdrive-crypt:/node1/docs/ <span class="p">|</span> grep <span class="s2">&#34;^deleted_&#34;</span></span></span></code></pre></td></tr></table> </div> </div><p><strong>恢复数据</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 恢复正常文件</span> </span></span><span class="line"><span class="cl">rclone copy gdrive-crypt:/node1/docs/full_20250227_020000 /tmp/restore </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 恢复已删除文件</span> </span></span><span class="line"><span class="cl">rclone copy gdrive-crypt:/node1/docs/deleted_20250227_020000 /tmp/restore_deleted</span></span></code></pre></td></tr></table> </div> </div><p>建议定期进行恢复测试,验证备份数据的完整性和可用性。这种测试不仅能确保在真正需要时可以成功恢复数据,还能让你熟悉恢复流程,防止遗忘。测试时可以选择恢复到临时目录,避免覆盖现有文件。</p> <h2 id="总结" class="heading-element"><span>5 总结</span> <a href="#%e6%80%bb%e7%bb%93" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>本文介绍的基于Rclone和云盘的低成本数据备份方案,为个人用户提供了一种经济、安全且高效的数据保护策略。通过自动化备份流程、加密存储和多重备份,可以有效防止数据丢失,保护数字资产安全。</p> <p>数据备份不是一劳永逸的工作,而是需要持续维护和改进的过程。定期检查备份状态,测试恢复过程,并根据数据变化调整备份策略,才能确保数据始终安全可靠。随着技术发展和需求变化,备份方案也应该不断演进和完善。</p> <p>最后,没有完美的备份方案,但有最适合你需求的方案。比如为了快速恢复生产能力,系统级快照备份是更合适的方案,但这与本文低成本的理念不符,因此并未介绍。基于本文提供的框架,你可以根据自己的实际情况,定制专属的数据备份系统。在数字资产日益重要的今天,投入适当的时间和资源构建可靠的备份系统,是对自己数字生活的负责任态度。</p> <p><strong>你的数据,值得最好的保护。</strong></p> <h2 id="参考资料" class="heading-element"><span>6 参考资料</span> <a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li>Rclone官方文档: <a href="https://rclone.org/docs/"target="_blank" rel="external nofollow noopener noreferrer">https://rclone.org/docs/<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li>3-2-1备份策略: <a href="https://www.backblaze.com/blog/the-3-2-1-backup-strategy/"target="_blank" rel="external nofollow noopener noreferrer">https://www.backblaze.com/blog/the-3-2-1-backup-strategy/<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li>数据备份最佳实践: <a href="https://www.cloudwards.net/backup-strategies/"target="_blank" rel="external nofollow noopener noreferrer">https://www.cloudwards.net/backup-strategies/<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> </ol>

2025/5/16
阅读更多

异地组网:免域名免备案自建Tailscale DERP节点

<blockquote> <p>无论是远程办公需要访问公司内网资源,还是想要在外出时轻松管理家中的设备,甚至只是想绕过NAT限制直接连接到另一台主机,异地组网都是一个理想的解决方案。本文将介绍一种不需要域名、无需备案的方法来建立自己的私有网络,特别适合国内环境下的技术爱好者和专业人士。</p> </blockquote> <h2 id="异地组网的目的与好处" class="heading-element"><span>1 异地组网的目的与好处</span> <a href="#%e5%bc%82%e5%9c%b0%e7%bb%84%e7%bd%91%e7%9a%84%e7%9b%ae%e7%9a%84%e4%b8%8e%e5%a5%bd%e5%a4%84" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>想象这样一个场景:李华是一名开发者,他在家里有一台NAS用于娱乐,公司有一台性能强劲的PC作为开发环境,同时在云端租了一台服务器用于部署项目。他经常需要在星巴克充当气氛组或是出差时工作,使用的是一台环保的MacBook。如果没有异地组网,李华每次在外工作或访问家里的电脑,可能需要设置复杂的端口转发规则,担心家庭网络的安全性,还得记住不断变化的家庭IP地址。而想要在本地直接调试部署在云服务器上的应用,又必须开放服务器端口,增加了安全风险。</p> <p>通过建立异地组网,李华只需要登录到私有网络,就像所有设备都在同一个局域网内一样。他可以从咖啡厅直接访问到NAS、开发环境,在笔记本上进行开发,然后无缝地部署到云服务器上。整个过程安全、高效,无需担心复杂的网络配置和安全风险。</p> <p>异地组网为我们带来了多方面的好处:首先是<strong>便捷访问</strong>,无论身处何地,都能像在本地网络一样访问远程设备和服务;其次是保障了数据安全,通过加密通道传输数据,有效避免敏感信息在公共网络中泄露;同时,它能<strong>突破各种网络限制</strong>,绕过NAT、防火墙等障碍,实现真正的点对点连接;最后,它提供了<strong>统一管理</strong>的可能,让分散在不同地点的设备整合到一个虚拟网络中,便于集中管理和维护。</p> <p>现代异地组网解决方案通常部署迅速,只需几分钟就能完成设置;提供低延迟体验,在良好的网络环境下几乎感受不到距离带来的延迟;且大多支持Windows、MacOS、Linux、iOS、Android等多种操作系统,让你的所有设备都能加入到这个私有网络中;更重要的是,它们简化了传统VPN的复杂配置,普通用户也能轻松上手使用。</p> <h2 id="异地组网的常见方案对比" class="heading-element"><span>2 异地组网的常见方案对比</span> <a href="#%e5%bc%82%e5%9c%b0%e7%bb%84%e7%bd%91%e7%9a%84%e5%b8%b8%e8%a7%81%e6%96%b9%e6%a1%88%e5%af%b9%e6%af%94" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>市场上有多种异地组网解决方案,每种都有其独特的特点和适用场景。下面我们通过一个综合对比来了解几种主流方案:</p> <table> <thead> <tr> <th>方案</th> <th>核心特点</th> <th>优势</th> <th>局限性</th> <th>适用场景</th> </tr> </thead> <tbody> <tr> <td><strong>Tailscale</strong></td> <td>基于WireGuard的P2P VPN</td> <td>高性能、简单易用、支持P2P直连、开源核心组件</td> <td>免费版设备数量有限(100个)、官方DERP中继在国内不稳定</td> <td>个人使用、小型团队、需要高安全性的场景</td> </tr> <tr> <td><strong>ZeroTier</strong></td> <td>全球虚拟网络</td> <td>灵活的网络拓扑、可创建多个网络、多平台支持</td> <td>免费版设备数量有限(10个)、中继节点在国内不稳定、配置略复杂</td> <td>需要复杂网络结构、多个虚拟网络的场景</td> </tr> <tr> <td><strong>FRP</strong></td> <td>反向代理工具</td> <td>完全开源、灵活配置、无设备限制</td> <td>非组网工具、需要公网服务器、配置复杂</td> <td>特定服务端口转发、内网穿透</td> </tr> <tr> <td><strong>Cloudflare Tunnel</strong></td> <td>云端隧道服务</td> <td>强大基础设施、不需公网IP、CDN加速</td> <td>非P2P连接、主要针对Web服务、免费版功能有限</td> <td>Web服务暴露、需要CDN加速的场景</td> </tr> </tbody> </table> <p>虽然表格提供了直观的对比,但选择合适的方案还需结合具体使用场景进行考量。对于追求简单易用且希望建立真正点对点连接的用户,Tailscale通常是最佳选择。它简化了传统VPN的复杂性,同时保留了高性能和安全性。ZeroTier则适合那些需要构建更复杂网络拓扑的用户,它提供了更多的网络结构定制可能性。</p> <p>如果您只是想解决特定应用的端口转发问题,FRP作为一个轻量级工具可能更为适合。而对于主要需要暴露Web服务的场景,特别是希望享受CDN加速的用户,Cloudflare Tunnel则提供了独特的优势。</p> <p>就我个人而言,可以将上面的方案分为两类:前两者为SDN类,后两者为FRP类,两类正好是互补的关系。对于大多数情况,我的服务只需要在SDN类构建的局域网中访问即可;但对于某些特殊服务,还是需要Cloudflare Tunnel来打洞,即与他人分享的服务(正好Cloudflare Tunnel的鉴权访问做得非常完善)以及无root权限的机器。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/02/7a88bd7f40630b2623c4a82d7dec8743.avif" alt="异地组网拓扑示意图" srcset="https://cdn1.zair.top/images/2025/02/7a88bd7f40630b2623c4a82d7dec8743.avif?size=small, https://cdn1.zair.top/images/2025/02/7a88bd7f40630b2623c4a82d7dec8743.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/02/7a88bd7f40630b2623c4a82d7dec8743.avif?size=large 2x" data-title="异地组网拓扑示意图" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>在本文中,我们将重点探讨如何优化Tailscale在国内的使用体验,特别是通过自建DERP节点来提高连接质量,这对于国内用户来说是一个至关重要的改进。</p> <h2 id="tailscale详解" class="heading-element"><span>3 Tailscale详解</span> <a href="#tailscale%e8%af%a6%e8%a7%a3" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Tailscale是近年来迅速崛起的新一代VPN解决方案,它巧妙地融合了传统VPN的安全性和P2P网络的直接连接优势,为用户提供了一种便捷、高效的异地组网方式。</p> <h3 id="什么是tailscale" class="heading-element"><span>3.1 什么是Tailscale?</span> <a href="#%e4%bb%80%e4%b9%88%e6%98%aftailscale" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Tailscale本质上是一个基于WireGuard协议的网状VPN(Mesh VPN)服务,它将复杂的网络配置简化为几次点击操作。与传统VPN不同,Tailscale采用了分布式架构,大多数情况下可以实现直接的点对点连接,无需所有流量经过中心服务器。这种设计既提高了数据传输效率,又降低了服务器负载和中心节点故障的风险。例如在上面的拓扑图中,四个节点间可以实现两两(P2P)直连。</p> <p>Tailscale的魅力在于它极致的简化理念。用户无需深入了解网络配置,也不需要固定IP地址或是复杂的防火墙规则设置,只需在设备上安装客户端并登录,就能自动加入到安全的私有网络中。这种&quot;零配置&quot;的体验使得即使是非技术背景的用户也能轻松使用。</p> <h3 id="tailscale的工作原理" class="heading-element"><span>3.2 Tailscale的工作原理</span> <a href="#tailscale%e7%9a%84%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>理解Tailscale的工作原理,需要先了解几个核心概念:</p> <p>WireGuard协议是Tailscale的基础,这是一个现代化的VPN协议,相比OpenVPN等传统方案,它代码量更小、性能更高、更易于审计。WireGuard提供了底层的加密通信能力,确保数据在传输过程中的安全性。</p> <p>Tailscale采用了控制平面与数据平面分离的架构。控制平面负责设备认证、密钥交换、网络配置等管理功能,由Tailscale的服务器处理;而数据平面则负责实际的数据传输,尽可能地在设备间直接进行,无需经过中央服务器。</p> <p>当两台设备需要通信时,Tailscale会首先尝试通过各种NAT穿透技术建立直接的P2P连接。这包括使用STUN协议发现设备的公网地址,以及尝试UDP打洞等技术。在大多数情况下,这些技术能够成功建立直接连接。</p> <p>然而,在一些严格的网络环境下(如对称型NAT、企业防火墙、运营商级NAT等),直接的P2P连接可能无法建立。这时,Tailscale会退而求其次,使用DERP(Designated Encrypted Relay for Packets)服务作为数据中继。DERP是Tailscale开发的一种中继协议,它会在无法直连的情况下,帮助转发加密后的数据包。</p> <h3 id="tailscale的优势与局限性" class="heading-element"><span>3.3 Tailscale的优势与局限性</span> <a href="#tailscale%e7%9a%84%e4%bc%98%e5%8a%bf%e4%b8%8e%e5%b1%80%e9%99%90%e6%80%a7" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Tailscale凭借其独特的设计,在异地组网领域具有显著优势。首先是其卓越的性能表现,基于WireGuard协议,Tailscale比传统VPN解决方案如OpenVPN快得多,延迟更低,适合各种实时应用场景。其次是极简的用户体验,几分钟内就能完成全部配置,后续几乎零维护,大大降低了用户的使用门槛和运维成本。</p> <p>安全性方面,Tailscale提供端到端加密,支持多种身份验证方式,包括与Google、Microsoft等身份提供商的集成,适合企业环境使用。跨平台支持也是Tailscale的一大亮点,它几乎支持所有主流操作系统,包括Windows、macOS、Linux、iOS、Android等,甚至还支持各种路由器固件如OpenWRT,让用户可以将所有设备纳入同一个私有网络。</p> <p>然而,Tailscale也存在一些局限性。最突出的问题是官方DERP节点在国内连接不佳,由于众所周知的网络原因,官方提供的中继服务器在中国大陆地区的表现往往不尽如人意,影响了用户体验。此外,免费版本的设备限制(最多100台设备)对于个人用户通常足够,但对于较大规模的组织可能会成为制约。最后,一些高级功能如子网路由、多用户组织等需要付费计划支持,这也是使用Tailscale需要考虑的成本因素。</p> <p>正是由于官方DERP节点在国内连接不佳的问题,自建DERP节点成为了提升国内Tailscale使用体验的关键解决方案,这也是本文接下来将重点讨论的内容。</p> <h2 id="自建tailscale-derp节点" class="heading-element"><span>4 自建Tailscale DERP节点</span> <a href="#%e8%87%aa%e5%bb%batailscale-derp%e8%8a%82%e7%82%b9" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>针对国内用户面临的最大问题——官方DERP节点连接不稳定,我们可以通过在国内服务器上自建DERP节点来解决。这种方法不需要域名和备案,只需要一台拥有公网IP的国内服务器,就能大大提升Tailscale在国内的使用体验。</p> <h3 id="derp服务简介" class="heading-element"><span>4.1 DERP服务简介</span> <a href="#derp%e6%9c%8d%e5%8a%a1%e7%ae%80%e4%bb%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>DERP(Designated Encrypted Relay for Packets)是Tailscale专门开发的中继服务。当两台设备因为网络环境限制无法直接建立P2P连接时,DERP会作为中间人帮助转发加密数据包。这种情况常见于严格的NAT环境下,如校园网、移动网络、企业防火墙等场景。</p> <p>需要强调的是,虽然DERP服务会中转数据,但这些数据在发送前已经被端到端加密,DERP服务器本身无法解密或查看数据内容,因此安全性不受影响。当然,使用DERP中继会增加一定的网络延迟,并受限于DERP服务器的带宽和地理位置。</p> <p>默认情况下,Tailscale提供了遍布全球的DERP节点网络,包括美国、欧洲、亚洲多个地区,但遗憾的是没有中国大陆地区的节点。这导致国内用户在需要使用DERP中继时,往往需要连接到距离较远的节点,如香港、新加坡、日本等地,造成较高的延迟和不稳定的连接。</p> <h3 id="准备工作" class="heading-element"><span>4.2 准备工作</span> <a href="#%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在开始自建DERP节点前,您需要准备以下资源:</p> <ol> <li>一台拥有公网IP的国内服务器,推荐配置至少1核2GB内存,带宽建议10Mbps以上,系统可以是常见的Linux发行版如Ubuntu、CentOS等。</li> <li>服务器上已安装Docker和Docker Compose,这将极大简化我们的部署流程。如果尚未安装,可以参考Docker官方文档进行安装。</li> <li>已注册的Tailscale账号,用于管理您的私有网络和DERP节点。</li> </ol> <p>值得注意的是,虽然许多教程建议使用域名和SSL证书来部署DERP服务,但本文将介绍一种不需要域名和备案的简化方法,特别适合个人用户或小团队使用。</p> <h3 id="在derp节点服务器上安装tailscale客户端" class="heading-element"><span>4.3 在DERP节点服务器上安装Tailscale客户端</span> <a href="#%e5%9c%a8derp%e8%8a%82%e7%82%b9%e6%9c%8d%e5%8a%a1%e5%99%a8%e4%b8%8a%e5%ae%89%e8%a3%85tailscale%e5%ae%a2%e6%88%b7%e7%ab%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>首先,我们需要在服务器上安装Tailscale客户端。这一步骤至关重要,它是防止他人未经授权使用我们DERP节点的关键机制。由于DERP服务本身不验证客户端身份,我们需要借助Tailscale客户端的身份验证机制来实现访问控制。</p> <p>在服务器上执行以下命令安装Tailscale客户端:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 使用官方脚本安装Tailscale客户端</span> </span></span><span class="line"><span class="cl">curl -fsSL https://tailscale.com/install.sh <span class="p">|</span> sh </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 登录Tailscale</span> </span></span><span class="line"><span class="cl">tailscale login</span></span></code></pre></td></tr></table> </div> </div><p>执行登录命令后,终端会显示一个URL链接。使用浏览器访问这个链接,按照提示完成身份验证流程。认证成功后,服务器就会成为您Tailscale网络中的一个节点。</p> <p>您可以通过<code>tailscale status</code>命令验证登录状态,确保服务器已经成功连接到您的Tailscale网络。</p> <h3 id="使用docker部署derp服务" class="heading-element"><span>4.4 使用Docker部署DERP服务</span> <a href="#%e4%bd%bf%e7%94%a8docker%e9%83%a8%e7%bd%b2derp%e6%9c%8d%e5%8a%a1" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>接下来,我们将使用<a href="https://github.com/yangchuansheng/ip_derper"target="_blank" rel="external nofollow noopener noreferrer">yangchuansheng<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>开发的Docker镜像来部署DERP服务。这个镜像封装了DERP服务的配置和运行环境,大大简化了部署过程。</p> <p>首先,创建一个新的目录用于存放Docker配置文件:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p ~/derp <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~/derp</span></span></code></pre></td></tr></table> </div> </div><p>然后,创建<code>docker-compose.yml</code>文件:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nano docker-compose.yml</span></span></code></pre></td></tr></table> </div> </div><p>将以下内容复制到文件中:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">derper</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">ghcr.io/yangchuansheng/ip_derper:latest</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">derper</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">ports</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;12345:12345&#34;</span><span class="w"> </span><span class="c"># 将12345改为您想使用的端口</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="s2">&#34;3478:3478/udp&#34;</span><span class="w"> </span><span class="c"># STUN端口,建议保持不变</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">/var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock</span><span class="w"> </span><span class="c"># 映射本地Tailscale客户端以验证连接</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">DERP_ADDR=:12345</span><span class="w"> </span><span class="c"># 与上面端口保持一致</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">DERP_CERTS=/app/certs</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">DERP_VERIFY_CLIENTS=true</span><span class="w"> </span><span class="c"># 防止未授权使用的关键参数</span></span></span></code></pre></td></tr></table> </div> </div><p>如果您的服务器在国内,可能会遇到拉取国外Docker镜像缓慢的问题,这时可以使用国内镜像源:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">derper</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">ghcr.nju.edu.cn/yangchuansheng/ip_derper:latest</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c"># 其余配置保持不变</span></span></span></code></pre></td></tr></table> </div> </div><p>保存文件后,启动DERP服务:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose up -d</span></span></code></pre></td></tr></table> </div> </div><p>通过查看容器日志,可以确认服务是否正常启动:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker logs derper</span></span></code></pre></td></tr></table> </div> </div><p>如果看到类似&quot;DERP server is up and running&quot;的消息,说明服务已成功启动。</p> <h3 id="配置tailscale使用自建derp节点" class="heading-element"><span>4.5 配置Tailscale使用自建DERP节点</span> <a href="#%e9%85%8d%e7%bd%aetailscale%e4%bd%bf%e7%94%a8%e8%87%aa%e5%bb%baderp%e8%8a%82%e7%82%b9" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>现在,我们需要告诉Tailscale网络使用我们自建的DERP节点。这需要修改Tailscale的ACL(访问控制列表)配置。</p> <ol> <li> <p>登录Tailscale管理控制台:https://login.tailscale.com/admin/acls/file</p> </li> <li> <p>在编辑器中,找到或添加<code>derpMap</code>部分,配置自建的DERP节点信息:</p> </li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 其他ACL配置... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;derpMap&#34;</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;OmitDefaultRegions&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// 设置为true可禁用官方DERP节点 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;Regions&#34;</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;900&#34;</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;RegionID&#34;</span><span class="p">:</span> <span class="mi">900</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;RegionCode&#34;</span><span class="p">:</span> <span class="s2">&#34;chn1&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;RegionName&#34;</span><span class="p">:</span> <span class="s2">&#34;China1&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;Nodes&#34;</span><span class="p">:</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;Name&#34;</span><span class="p">:</span> <span class="s2">&#34;cn-derp1&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;RegionID&#34;</span><span class="p">:</span> <span class="mi">900</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">&#34;IPv4&#34;</span><span class="p">:</span> <span class="s2">&#34;1.2.3.4&#34;</span><span class="p">,</span> <span class="c1">// 替换为您服务器的公网IP </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;DERPPort&#34;</span><span class="p">:</span> <span class="mi">12345</span><span class="p">,</span> <span class="c1">// 与docker-compose中配置的端口一致 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nt">&#34;InsecureForTests&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="c1">// 使用自签名证书时需要设置为true </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div><p>注意事项:</p> <ul> <li><code>RegionID</code>在900-999之间是预留给自定义DERP节点的,避免与官方节点冲突</li> <li><code>RegionCode</code>和<code>RegionName</code>可以自定义,建议使用有意义的名称</li> <li><code>IPv4</code>必须是服务器的公网IP地址</li> <li><code>DERPPort</code>必须与Docker配置中暴露的端口一致</li> <li><code>InsecureForTests</code>设置为true是因为我们使用了自签名证书</li> </ul> <ol start="3"> <li>保存配置后,Tailscale控制面板会自动将新的DERP配置推送到所有连接的设备。这个过程可能需要几分钟时间,具体取决于设备数量和网络状况。</li> </ol> <h3 id="验证derp节点是否工作" class="heading-element"><span>4.6 验证DERP节点是否工作</span> <a href="#%e9%aa%8c%e8%af%81derp%e8%8a%82%e7%82%b9%e6%98%af%e5%90%a6%e5%b7%a5%e4%bd%9c" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>成功部署后,我们需要验证DERP节点是否正常工作。有两种主要的验证方法:</p> <p><strong>方法一:使用网络连接测试</strong></p> <p>在任何一台已连接到Tailscale网络的设备上,运行以下命令:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tailscale netcheck</span></span></code></pre></td></tr></table> </div> </div><p>这个命令会检测当前设备能够连接到哪些DERP节点,以及它们的延迟情况。在输出结果中,应该能看到我们自建的DERP节点信息,如果显示延迟数值(而不是超时),说明节点连接正常。</p> <p><strong>方法二:使用Ping测试</strong></p> <p>当两台设备无法直接建立P2P连接时,Tailscale会自动通过DERP节点中继流量。我们可以通过ping测试来验证:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tailscale ping 另一台设备的Tailscale IP</span></span></code></pre></td></tr></table> </div> </div><p>如果在输出中看到类似<code>via DERP(cn-derp1)</code>的信息,说明流量正在通过我们自建的DERP节点中继,证明配置成功。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2025/02/c12a90053f05b17e01d8b491a00ce515.avif" alt="中继前后对比" srcset="https://cdn1.zair.top/images/2025/02/c12a90053f05b17e01d8b491a00ce515.avif?size=small, https://cdn1.zair.top/images/2025/02/c12a90053f05b17e01d8b491a00ce515.avif?size=medium 1.5x, https://cdn1.zair.top/images/2025/02/c12a90053f05b17e01d8b491a00ce515.avif?size=large 2x" data-title="中继前后对比" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>我的测试结果如上图所示,左侧是使用自建服务器之前,右侧是使用自建中转节点之后。其中第一条命令的机器porser是国内异地,都是先中转再打洞成功,明显看到使用自建DERP节点后,中转的时间显著下降。第二条命令的测试机器nerd是美国节点,经过自建DERP中转甚至比直连更快。第三调命令显示我们的DERP节点(sh)延迟只有15ms,比之前官方提供的最近节点(70ms)有了显著提升。</p> <p>在实际使用中,您可能会注意到,当两台设备位于不同的网络环境时(如一台在家庭网络,一台在移动网络),它们很可能会通过DERP节点通信。而当设备在同一网络环境时,Tailscale会尽可能建立直接的P2P连接,不使用DERP中继。</p> <h2 id="常见问题与解决方案" class="heading-element"><span>5 常见问题与解决方案</span> <a href="#%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98%e4%b8%8e%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在部署和使用自建DERP节点的过程中,您可能会遇到各种问题。以下是一些常见问题及其解决方案:</p> <p><strong>问题:无法连接到DERP节点</strong></p> <p>这通常是由网络配置或服务器设置导致的。首先,检查服务器防火墙是否开放了DERP服务使用的端口(在我们的例子中是12345和3478/UDP)。大多数云服务商除了操作系统防火墙外,还有额外的安全组或网络ACL设置,确保这些也已正确配置。</p> <p>其次,确认<code>docker-compose.yml</code>中的配置是否正确,特别是端口映射和环境变量设置。可以通过<code>docker logs derper</code>查看容器日志,寻找可能的错误信息。</p> <p>最后,验证服务器上的Tailscale客户端是否正常登录,可以通过<code>tailscale status</code>命令查看。如果客户端未登录或状态异常,可能会导致DERP服务无法正常工作。</p> <p><strong>问题:连接DERP成功但设备之间无法通信</strong></p> <p>这种情况通常与Tailscale的访问控制设置有关。首先,检查ACL配置是否正确,确保相关设备有互相访问的权限。Tailscale的ACL功能非常强大,可能需要仔细检查是否有意外的访问限制规则。</p> <p>其次,确认设备间的子网路由设置。如果涉及到访问设备所在的子网(而不仅是设备本身),需要在Tailscale客户端启用子网路由功能,并在ACL中授权相应的子网访问权限。</p> <p>使用<code>tailscale status</code>命令可以查看当前网络中所有设备的连接状态,包括它们是否通过P2P直连或DERP中继通信。这有助于诊断具体的连接问题。</p> <p><strong>问题:DERP连接速度较慢</strong></p> <p>DERP中继的性能受多种因素影响,包括服务器硬件配置、网络带宽、地理位置等。如果发现连接速度不理想,可以考虑以下优化方案:</p> <p>升级服务器配置,特别是网络带宽。DERP服务对CPU和内存要求不高,但带宽是关键因素,尤其是当多台设备同时通过中继通信时。</p> <p>优化服务器的网络配置,如调整TCP/IP参数,开启BBR等拥塞控制算法,这可能会提高网络吞吐量。</p> <p>如果条件允许,可以在不同地理位置或使用不同ISP的服务器上部署多个DERP节点,通过<code>derpMap</code>配置让Tailscale网络使用距离最近或延迟最低的节点。</p> <p><strong>问题:服务器重启后DERP服务不自动启动</strong></p> <p>确保Docker服务配置为开机自启动:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl <span class="nb">enable</span> docker</span></span></code></pre></td></tr></table> </div> </div><p>同时,在<code>docker-compose.yml</code>中已经设置了<code>restart: always</code>参数,这应该能确保Docker容器在服务重启后自动启动。如果仍有问题,可以创建一个系统服务或启动脚本,确保在系统启动时自动启动DERP服务。</p> <h2 id="总结与展望" class="heading-element"><span>6 总结与展望</span> <a href="#%e6%80%bb%e7%bb%93%e4%b8%8e%e5%b1%95%e6%9c%9b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>我们已经成功在国内环境下建立了自己的Tailscale DERP节点,而且不需要域名和备案,大大提升了Tailscale的使用体验。这种方案不仅适用于个人用户,也适合小型团队或企业内部使用,能够在保证数据安全的同时,提供稳定、高效的异地组网服务。</p> <p>自建DERP节点的方案解决了国内用户使用Tailscale面临的最大痛点——官方DERP节点连接不稳定的问题。通过国内服务器中继,不仅大幅降低了网络延迟,还提高了连接的稳定性和可靠性,让Tailscale这款优秀的组网工具在国内环境下发挥出应有的价值。</p> <p>展望未来,随着网络技术的不断发展,我们可能会看到更多适合国内环境的P2P连接解决方案。Tailscale本身也在不断改进NAT穿透技术,增强直接P2P连接的成功率。但在当前阶段,自建DERP节点仍然是解决Tailscale在国内使用问题的最佳方案之一。</p> <p>值得一提的是,除了本文介绍的方法外,还有其他一些改进Tailscale使用体验的方式,如使用Headscale作为自托管的控制服务器,以进一步增强网络的自主性和定制性。对于有更高要求的用户,这些方案也值得探索。</p> <p>最后,感谢开源社区的贡献,尤其是yangchuansheng开发的ip_derper项目,让自建DERP节点变得如此简单。</p> <h2 id="参考资料" class="heading-element"><span>7 参考资料</span> <a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ul> <li><a href="https://tailscale.com/kb/1118/custom-derp-servers"target="_blank" rel="external nofollow noopener noreferrer">Tailscale官方文档<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://github.com/yangchuansheng/ip_derper"target="_blank" rel="external nofollow noopener noreferrer">yangchuansheng/ip_derper项目<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://icloudnative.io/posts/custom-derp-servers"target="_blank" rel="external nofollow noopener noreferrer">Custom DERP Servers介绍<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://tailscale.com/kb/1347/installation"target="_blank" rel="external nofollow noopener noreferrer">Tailscale安装指南<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> </ul>

2025/2/26
阅读更多

家庭网络配置:利用 Docker 在闲置笔记本上运行 OpenWRT 作为旁路由

<blockquote> <p>通过 Docker 在闲置笔记本上运行 OpenWRT 作为旁路由,充分利用旧设备,提升家庭网络的可定制性和功能性。本文详细介绍了从零开始配置 Docker 环境、创建 macvlan 网络、运行 OpenWRT 容器,并进行网络配置的全过程。无论是去广告、科学上网,还是流量监控和 VPN 服务,都可以通过旁路由轻松实现。适合有一定 Linux 基础的用户,帮助你将闲置设备变身为强大的网络工具。</p> </blockquote> <p>过年回家总想着折腾点什么,于是就瞄上了早已退休的19年拯救者。相信很多家庭都有闲置的笔记本电脑,性能尚可却一直闲置。如果希望充分利用这些设备,可以考虑将其改造成“旁路由”(又称“透明网关”或“旁挂路由”),将某些网络功能(如去广告、流量监控、VPN 等)卸载到这台机器上进行处理。但是,这么强大的一个X86平台,重新刷一个Openwrt系统太大材小用了,已经装了Ubuntu的它还可以有更多用处(All in BOOM)。如果不想大动硬件也不想重装系统,Docker 技术可以派上用场。通过在闲置笔记本上运行一个 OpenWRT 容器,就可以以旁路由的方式接管或旁路处理局域网的流量。</p> <p>本文将介绍从零开始完成一套基本环境配置,包括:</p> <ul> <li><strong>检查和配置网络接口</strong></li> <li><strong>创建 Docker macvlan 网络</strong></li> <li><strong>拉取并运行 OpenWRT 镜像</strong></li> <li><strong>进入容器进行简单网络配置</strong></li> </ul> <h2 id="环境与思路" class="heading-element"><span>1 环境与思路</span> <a href="#%e7%8e%af%e5%a2%83%e4%b8%8e%e6%80%9d%e8%b7%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="闲置笔记本电脑" class="heading-element"><span>1.1 闲置笔记本电脑</span> <a href="#%e9%97%b2%e7%bd%ae%e7%ac%94%e8%ae%b0%e6%9c%ac%e7%94%b5%e8%84%91" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>需要一台可以正常联网的笔记本(最好有以太网口)。如果只有无线网卡,需要额外注意无线网卡桥接或 macvlan 可能受限。</li> <li>笔记本的操作系统可以是任意 Linux 发行版(Ubuntu、Debian、CentOS、Arch Linux 等),只要能安装 Docker 即可。</li> </ul> <h3 id="docker-环境" class="heading-element"><span>1.2 Docker 环境</span> <a href="#docker-%e7%8e%af%e5%a2%83" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>确保 Docker 可以正常启动和拉取镜像,如果不熟悉 Docker 的安装,可以参照 <a href="https://docs.docker.com/engine/install/"target="_blank" rel="external nofollow noopener noreferrer">Docker 官方文档<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a> 或对应发行版社区的文档。如在大陆地区,可参照<a href="https://www.coderjia.cn/archives/dba3f94c-a021-468a-8ac6-e840f85867ea"target="_blank" rel="external nofollow noopener noreferrer">此处<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>配置镜像。</p> <h3 id="旁路由思路" class="heading-element"><span>1.3 旁路由思路</span> <a href="#%e6%97%81%e8%b7%af%e7%94%b1%e6%80%9d%e8%b7%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>在传统路由器(或主路由)仍然负责 DHCP、主要 NAT 功能的前提下,让 OpenWRT 容器在笔记本上“旁路”处理网络中的其他功能,比如去广告、科学上网、流量分析等。</li> <li>所有设备可以灵活选择是否需要使用旁路由,避免爆炸的时候网络异常或是增加家中长辈的用网成本。</li> <li>为了让笔记本上运行的 OpenWRT 容器拥有和内网同网段的 IP,需要使用 <code>macvlan</code> 网络模式。</li> </ul> <h2 id="检查并配置网络接口" class="heading-element"><span>2 检查并配置网络接口</span> <a href="#%e6%a3%80%e6%9f%a5%e5%b9%b6%e9%85%8d%e7%bd%ae%e7%bd%91%e7%bb%9c%e6%8e%a5%e5%8f%a3" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="查看网卡信息" class="heading-element"><span>2.1 查看网卡信息</span> <a href="#%e6%9f%a5%e7%9c%8b%e7%bd%91%e5%8d%a1%e4%bf%a1%e6%81%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在 Linux 上,首先用 <code>ifconfig</code> 或 <code>ip addr</code> 命令查看网卡信息。以下示例输出里,重点关注无线网卡 <code>wlp0s20f3</code>、有线网卡 <code>enp7s0</code> 以及它们的 IP 地址:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ifconfig</span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">wlp0s20f3: flags=4419&lt;UP,BROADCAST,RUNNING,PROMISC,MULTICAST&gt; mtu 1500 </span></span><span class="line"><span class="cl"> inet 192.168.1.23 netmask 255.255.255.0 broadcast 192.168.1.255 </span></span><span class="line"><span class="cl"> ... </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">enp7s0: ... </span></span><span class="line"><span class="cl"> ...</span></span></code></pre></td></tr></table> </div> </div><p>当然这只是我的设备情况,每个设备的网卡名称几乎都是不同的,需要自主根据IP地址、流量信息去确定,但大多数情况下,以太网卡的名称会以“e”开头,比如“eth0”或是我的“enp***“,后文中所有的代码中,记得把<code>enp7s0</code>替换成你的设备名称,你可以在此博客页面代码框的右上角点击编辑按钮后直接修改。</p> <p>如果希望在有线网卡上使用 macvlan,一般会选择 <code>enp7s0</code> 作为 <code>macvlan</code> 的父接口(<code>-o parent=enp7s0</code>),因为无线网卡与 macvlan 配合往往有兼容性问题。<br> <em>在少数情况下,如果只有无线网卡,可能需要通过其他方案来旁路,比如 TAP/TUN 方式或者路由策略转发等,macvlan 对 Wi-Fi 不够友好。</em></p> <h3 id="开启混杂模式" class="heading-element"><span>2.2 开启混杂模式</span> <a href="#%e5%bc%80%e5%90%af%e6%b7%b7%e6%9d%82%e6%a8%a1%e5%bc%8f" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>为了确保网卡可以截获所有流量进行分析或桥接,需要设置网卡的混杂模式。可通过以下命令启用:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> enp7s0 promisc on</span></span></code></pre></td></tr></table> </div> </div><p>或者对于无线网卡,如果想在 <code>wlp0s20f3</code> 上开启混杂模式,也可执行:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ip link <span class="nb">set</span> wlp0s20f3 promisc on</span></span></code></pre></td></tr></table> </div> </div><p><em>注意:混杂模式一般只在需要检测或中转特定流量时才必需,不然可以不设。</em></p> <h2 id="创建-docker-macvlan-网络" class="heading-element"><span>3 创建 Docker macvlan 网络</span> <a href="#%e5%88%9b%e5%bb%ba-docker-macvlan-%e7%bd%91%e7%bb%9c" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p><code>macvlan</code> 可以让容器拥有与物理机不同的 MAC 地址以及内网 IP,从而像独立设备一样接入到局域网中。</p> <ol> <li>假设希望容器所在网段是 <code>192.168.10.0/24</code>,网关是 <code>192.168.10.1</code>(主路由器或管理网关,可以在当前联网的设备上查看详细信息)。</li> <li>使用 <code>macvlan</code> 驱动创建一个名为 <code>macnet</code> 的网络,父接口是 <code>enp7s0</code>(以太网卡),具体命令如下:</li> </ol> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker network create -d macvlan <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --subnet<span class="o">=</span>192.168.10.0/24 <span class="se">\ </span> <span class="c1"># 与网关在同一网段</span> </span></span><span class="line"><span class="cl"> --gateway<span class="o">=</span>192.168.10.1 <span class="se">\ </span><span class="c1"># 修改为路由器或者网关ip</span> </span></span><span class="line"><span class="cl"> -o <span class="nv">parent</span><span class="o">=</span>enp7s0 <span class="se">\ </span><span class="c1"># 修改为以太网卡名称</span> </span></span><span class="line"><span class="cl"> macnet</span></span></code></pre></td></tr></table> </div> </div><p>上述命令解析:</p> <ul> <li><code>-d macvlan</code> 指定驱动类型为 macvlan。</li> <li><code>--subnet=192.168.10.0/24</code> 指定容器网络的子网。</li> <li><code>--gateway=192.168.10.1</code> 指定子网的网关地址,一般是主路由 IP。</li> <li><code>-o parent=enp7s0</code> 指明物理机将要桥接到的网卡接口,即上一步确定的以太网接口。</li> <li><code>macnet</code> 是给此网络取的名称。</li> </ul> <p>务必确认主路由器所管理的网段是否与这里的 <code>192.168.10.0/24</code> 相兼容。如果家里的主路由器网段是 <code>192.168.1.x</code>,则可以将旁路由容器放到同一个网段,或者通过适当的路由配置让这两个网段互通。具体做法可以根据实际网络需求进行调整。</p> <h2 id="拉取并运行-openwrt-镜像" class="heading-element"><span>4 拉取并运行 OpenWRT 镜像</span> <a href="#%e6%8b%89%e5%8f%96%e5%b9%b6%e8%bf%90%e8%a1%8c-openwrt-%e9%95%9c%e5%83%8f" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="确认系统架构" class="heading-element"><span>4.1 确认系统架构</span> <a href="#%e7%a1%ae%e8%ae%a4%e7%b3%bb%e7%bb%9f%e6%9e%b6%e6%9e%84" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>先用一些命令查看当前系统是 <code>x86_64</code>、<code>arm</code>、<code>arm64</code> 等。OpenWRT 镜像一般会按架构分得比较细,确保选对镜像才能在容器内正常运行。以 Arch Linux 下查看命令为例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">arch</span></span></code></pre></td></tr></table> </div> </div><p>或</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uname -m</span></span></code></pre></td></tr></table> </div> </div><p>假设输出为 <code>x86_64</code>,表示需要拉取 <code>x86_64</code> 架构的 OpenWRT Docker 镜像。</p> <h3 id="拉取-openwrt-镜像" class="heading-element"><span>4.2 拉取 OpenWRT 镜像</span> <a href="#%e6%8b%89%e5%8f%96-openwrt-%e9%95%9c%e5%83%8f" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在 Docker 仓库或阿里云镜像仓库可以查到一些第三方的 OpenWRT 镜像,例如 <a href="https://hub.docker.com/r/sulinggg/openwrt"target="_blank" rel="external nofollow noopener noreferrer">sulinggg/openwrt<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a> 或者其它定制镜像。这里以 <code>registry.cn-shanghai.aliyuncs.com/suling/openwrt:x86_64</code> 为例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker pull registry.cn-shanghai.aliyuncs.com/suling/openwrt:x86_64</span></span></code></pre></td></tr></table> </div> </div><p>拉取成功后,可以用 <code>docker images</code> 命令查看是否已经下载成功。</p> <h2 id="启动容器" class="heading-element"><span>5 启动容器</span> <a href="#%e5%90%af%e5%8a%a8%e5%ae%b9%e5%99%a8" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>以下命令将基于之前创建的 <code>macnet</code> 网络,并使容器拥有特权权限(<code>--privileged</code>)来访问更多系统特性:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo docker run --restart always <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --name openwrt <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> -d <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --network macnet <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --privileged <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> registry.cn-shanghai.aliyuncs.com/suling/openwrt:x86_64 <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> /sbin/init</span></span></code></pre></td></tr></table> </div> </div><p>命令解析:</p> <ul> <li><code>--restart always</code>:Docker 容器异常退出或系统重启后,自动重启容器。</li> <li><code>--name openwrt</code>:容器名称设置为 <code>openwrt</code>。</li> <li><code>-d</code>:后台运行容器。</li> <li><code>--network macnet</code>:使用先前创建的 <code>macvlan</code> 网络。</li> <li><code>--privileged</code>:给容器特权,可以支持更多底层操作,比如网络管理、挂载等。</li> <li><code>registry.cn-shanghai.aliyuncs.com/suling/openwrt:x86_64</code>:要运行的镜像名称。</li> <li><code>/sbin/init</code>:指定容器启动时运行的初始进程。</li> </ul> <h2 id="容器内网络配置" class="heading-element"><span>6 容器内网络配置</span> <a href="#%e5%ae%b9%e5%99%a8%e5%86%85%e7%bd%91%e7%bb%9c%e9%85%8d%e7%bd%ae" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>容器启动后,可以进入容器内部进行网络和服务配置。</p> <h3 id="进入容器" class="heading-element"><span>6.1 进入容器</span> <a href="#%e8%bf%9b%e5%85%a5%e5%ae%b9%e5%99%a8" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker <span class="nb">exec</span> -it openwrt bash</span></span></code></pre></td></tr></table> </div> </div><p>这会在容器内部开启一个交互式 Shell,之后可以像在普通 Linux 主机上一样进行一些 OpenWRT 的配置。</p> <h3 id="编辑-openwrt-网络配置" class="heading-element"><span>6.2 编辑 OpenWRT 网络配置</span> <a href="#%e7%bc%96%e8%be%91-openwrt-%e7%bd%91%e7%bb%9c%e9%85%8d%e7%bd%ae" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>OpenWRT 容器的网络配置文件在 <code>/etc/config/network</code>,和常见的 OpenWRT 路由器类似。可以使用 <code>vim</code> 或 <code>vi</code> 打开它:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim /etc/config/network</span></span></code></pre></td></tr></table> </div> </div><p>可根据需求(WAN/LAN 设置、静态 IP、DHCP 等)进行修改。以一个简单的例子为参考(仅示意,需要具体修改):</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">config interface &#39;lan&#39; </span></span><span class="line"><span class="cl"> option ifname &#39;eth0&#39; </span></span><span class="line"><span class="cl"> option proto &#39;static&#39; </span></span><span class="line"><span class="cl"> option ipaddr &#39;192.168.10.2&#39; </span></span><span class="line"><span class="cl"> option netmask &#39;255.255.255.0&#39; </span></span><span class="line"><span class="cl"> option gateway &#39;192.168.10.1&#39; </span></span><span class="line"><span class="cl"> option dns &#39;192.168.10.1&#39;</span></span></code></pre></td></tr></table> </div> </div><p>这里 <code>lan</code> 接口与 <code>eth0</code> 绑定,假设要给容器 IP 设为 <code>192.168.10.2</code>;网关就是 <code>192.168.10.1</code>。 这里配置的容器 IP 地址就是旁路由的 IP,所有步骤成功后,将上网设备的网关地址设为这个就能使用旁路由了 。</p> <p>修改完成后,重启 OpenWRT 网络服务:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/etc/init.d/network restart</span></span></code></pre></td></tr></table> </div> </div><h3 id="验证容器网络生效" class="heading-element"><span>6.3 验证容器网络生效</span> <a href="#%e9%aa%8c%e8%af%81%e5%ae%b9%e5%99%a8%e7%bd%91%e7%bb%9c%e7%94%9f%e6%95%88" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>重启网络后,可以在容器内再次查看 IP 信息:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ifconfig</span></span></code></pre></td></tr></table> </div> </div><p>或</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ip addr</span></span></code></pre></td></tr></table> </div> </div><p>如果配置正确,应该能看到 <code>eth0</code> 或对应的网卡上有设定的 <code>192.168.10.2</code> 这样的 IP,并可以使用 <code>ping</code> 命令检测与网关或外网的连通性。</p> <h2 id="混杂模式服务可选" class="heading-element"><span>7 混杂模式服务(可选)</span> <a href="#%e6%b7%b7%e6%9d%82%e6%a8%a1%e5%bc%8f%e6%9c%8d%e5%8a%a1%e5%8f%af%e9%80%89" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在机器重启后,之前设置的混杂模式会失效。如果要启动时自动启用混杂模式,可以通过以下步骤将其添加到启动参数中。</p> <h3 id="创建并配置-etcrclocal" class="heading-element"><span>7.1 创建并配置 <code>/etc/rc.local</code></span> <a href="#%e5%88%9b%e5%bb%ba%e5%b9%b6%e9%85%8d%e7%bd%ae-etcrclocal" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>首先,创建 <code>/etc/rc.local</code> 文件并赋予可执行权限:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo touch /etc/rc.local </span></span><span class="line"><span class="cl">sudo chmod +x /etc/rc.local</span></span></code></pre></td></tr></table> </div> </div><p>然后,编辑 <code>/etc/rc.local</code> 文件,添加以下内容:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ip link <span class="nb">set</span> eth0 promisc on </span></span><span class="line"><span class="cl"><span class="nb">exit</span> <span class="m">0</span></span></span></code></pre></td></tr></table> </div> </div><h3 id="创建并配置-rc-localservice" class="heading-element"><span>7.2 创建并配置 <code>rc-local.service</code></span> <a href="#%e5%88%9b%e5%bb%ba%e5%b9%b6%e9%85%8d%e7%bd%ae-rc-localservice" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>接下来,创建一个 systemd 服务文件 <code>/etc/systemd/system/rc-local.service</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo vim /etc/systemd/system/rc-local.service</span></span></code></pre></td></tr></table> </div> </div><p>内容如下:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span> </span></span><span class="line"><span class="cl"> <span class="na">Description</span><span class="o">=</span><span class="s">/etc/rc.local Compatibility </span></span></span><span class="line"><span class="cl"><span class="s"> ConditionPathExists=/etc/rc.local </span></span></span><span class="line"><span class="cl"><span class="s"> After=network.target</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">[Service]</span> </span></span><span class="line"><span class="cl"> <span class="na">Type</span><span class="o">=</span><span class="s">forking </span></span></span><span class="line"><span class="cl"><span class="s"> ExecStart=/etc/rc.local start </span></span></span><span class="line"><span class="cl"><span class="s"> TimeoutSec=0 </span></span></span><span class="line"><span class="cl"><span class="s"> RemainAfterExit=yes</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">[Install]</span> </span></span><span class="line"><span class="cl"> <span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span></span></span></code></pre></td></tr></table> </div> </div><h3 id="启用并启动服务" class="heading-element"><span>7.3 启用并启动服务</span> <a href="#%e5%90%af%e7%94%a8%e5%b9%b6%e5%90%af%e5%8a%a8%e6%9c%8d%e5%8a%a1" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>最后启用并启动 <code>rc-local</code> 服务:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> rc-local </span></span><span class="line"><span class="cl">sudo systemctl start rc-local.service </span></span><span class="line"><span class="cl">sudo systemctl status rc-local.service</span></span></code></pre></td></tr></table> </div> </div><h2 id="将容器用作旁路由" class="heading-element"><span>8 将容器用作旁路由</span> <a href="#%e5%b0%86%e5%ae%b9%e5%99%a8%e7%94%a8%e4%bd%9c%e6%97%81%e8%b7%af%e7%94%b1" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li><strong>旁路由原理</strong>:在家里现有的主路由器配置中,可以将特定设备或者某个 VLAN 的网关地址指向这个 OpenWRT 容器的 IP(如 <code>192.168.10.2</code>),或者在主路由上配置静态路由,将某些流量转给 OpenWRT 做高级处理。</li> <li><strong>常见用途</strong>:去广告(Adblock)、VPN 客户端、代理服务器、科学上网、流量监控等等都可以在这个 OpenWRT 容器上配置。</li> <li><strong>确保网络互通</strong>:如果笔记本通过 Wi-Fi 上网,就要留意物理网卡与 macvlan 之间的兼容问题;也要确保主路由、笔记本与旁路由容器处于同一个网段或路由可达。</li> <li>功耗:启动Openwrt后,没流量时整机功耗13w,有下载流量时整机功耗约20w,功耗还是比较高,看来不宜在x86设备上长期使用。</li> </ol> <h2 id="故障排查" class="heading-element"><span>9 故障排查</span> <a href="#%e6%95%85%e9%9a%9c%e6%8e%92%e6%9f%a5" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li><strong>容器无网络</strong>:检查是否将 macvlan 父接口指定为正确的有线网卡;检查容器内 IP 与主路由网段是否冲突;检查物理网卡 IP 分配是否正确。</li> <li><strong>冲突</strong>:如果笔记本本身也在 <code>192.168.10.x</code> 网段分配了 IP,而给容器也设置了同网段的 IP,可能出现地址冲突。可以通过在物理机上把网卡 IP 与容器 IP 分离到不同网段,或通过给 Docker <code>macvlan</code> 指定一个子接口(<code>ip link add</code> 创建子接口)来避免冲突。</li> <li><strong>无法访问容器管理界面(LUCI)</strong>:OpenWRT 默认可能没有开放或安装 Luci,如需 Web 管理界面,需要进入容器安装 <code>luci</code>,并检查防火墙设置。</li> <li><strong>无线网卡问题</strong>:如果只有 Wi-Fi 网卡想要使用 macvlan,通常会碰到兼容性障碍,可以尝试其他方式,如 TUN/TAP,或者额外配置一块 USB 网卡用作桥接。</li> </ol> <h2 id="总结" class="heading-element"><span>10 总结</span> <a href="#%e6%80%bb%e7%bb%93" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>通过 Docker 在闲置笔记本上运行 OpenWRT 用作旁路由,可以在保留原路由器主要功能的同时,为家庭网络增添更多可定制化的高级网络服务。从此,可以随时在容器中添加功能模块(Adblock、代理、QoS、VPN、流量统计等),灵活扩展家庭网络能力。并且 Docker 容器使用和维护都相对简单,后续升级、迁移也更方便。</p> <p>如果对网络的管理和性能优化有更高的要求,可以考虑直接在笔记本上安装类似 PVE(Proxmox VE)、ESXi 或者开源虚拟化平台,然后让 OpenWRT 以虚拟机方式运行。但对于大多数轻量应用场景,Docker + macvlan 已足够轻巧易用。</p> <p>如果在实际操作中遇到更多问题,欢迎在评论区留下疑问或经验。</p>

2025/1/21
阅读更多

专家混合模型 (MoE) 详解:Mixtral 8X7B、DBRX 和 Deepseek-v2 的架构与应用

<blockquote> <p>深入了解专家混合模型 (MoE) 的架构与工作原理,探索Mixtral 8X7B、DBRX 和 Deepseek-v2等热门MoE模型的应用与优势。通过Python实现MoE模型,并评估其在逻辑推理、摘要和实体提取等任务中的表现。</p> </blockquote> <p>专家混合模型 (MoE) 这个概念在大型语言模型 (LLMs) 领域火了很久了。它不仅让模型的效率和可扩展性有了质的飞跃,还为处理复杂任务提供了新的思路。简单来说,MoE 模型就是把一个大模型拆分成多个小模型,每个小模型专注于特定的任务或数据类型。这样一来,模型在处理任务时,只需要激活相关的“专家”,而不需要整个模型都参与,从而大大节省了计算资源。</p> <p><strong>在本文中你将:</strong></p> <ul> <li>搞懂 MoE模型的基本架构和工作原理</li> <li>了解几个热门的MoE模型,比如Mixtral 8X7B、DBRX 和 Deepseek-v2</li> <li>通过Python代码在Google Colab上实现MoE模型</li> <li>评估一个典型的MoE模型在逻辑推理、摘要和实体提取等任务上的表现</li> <li>了解MoE模型在自然语言处理和代码生成等复杂任务中的优势和挑战</li> </ul> <h2 id="什么是专家混合模型-moe" class="heading-element"><span>1 什么是专家混合模型 (MoE)?</span> <a href="#%e4%bb%80%e4%b9%88%e6%98%af%e4%b8%93%e5%ae%b6%e6%b7%b7%e5%90%88%e6%a8%a1%e5%9e%8b-moe" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>现在的深度学习模型大多基于人工神经网络,这些网络由多个层组成,每一层包含很多“神经元”。每个神经元处理输入数据,进行简单的数学运算(比如激活函数),然后把结果传递给下一层。更高级的模型,比如 Transformer,引入了自注意力机制,能够捕捉数据中的复杂模式。</p> <p>不过,传统的密集模型在处理每个输入时,整个网络都要参与计算,这会导致计算成本非常高。为了解决这个问题,Mixture of Experts (MoE) 模型采用了<strong>稀疏架构</strong>,只激活与当前任务相关的部分网络,也就是“专家”。这样一来,MoE 模型在处理复杂任务时,比如自然语言处理,消耗的计算资源就少了很多。</p> <p>试想一下,在一个团队项目中,团队成员被分成多个小组,每个小组专注于不同的任务。MoE 模型的工作方式与此类似。它把一个复杂问题分解成多个子任务,每个子任务由专门的“专家”负责处理。</p> <p>MoE 模型的主要优势有:</p> <ul> <li><strong>预训练速度更快</strong>:相比传统的密集模型,MoE 模型的预训练过程更加高效。</li> <li><strong>推理速度更快</strong>:即使参数数量相同,MoE 模型的推理速度也更快。</li> <li><strong>对 VRAM 需求较高</strong>:由于所有专家必须同时存储在内存中,MoE 模型对显存的需求较大。</li> </ul> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/12/a7b209eb10f3d2b2c8e5414c41e62f1c.avif" alt="MoE结构示意" srcset="https://cdn1.zair.top/images/2024/12/a7b209eb10f3d2b2c8e5414c41e62f1c.avif?size=small, https://cdn1.zair.top/images/2024/12/a7b209eb10f3d2b2c8e5414c41e62f1c.avif?size=medium 1.5x, https://cdn1.zair.top/images/2024/12/a7b209eb10f3d2b2c8e5414c41e62f1c.avif?size=large 2x" data-title="MoE结构示意" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>MoE 模型由两个关键组件组成:<strong>专家(Experts)</strong>,即专注于特定任务的小型神经网络;以及<strong>路由器(Router)</strong>,它根据输入数据选择性地激活相关专家。这种选择性激活通过仅使用每个任务所需的专家来提高效率。</p> <h2 id="几个典型的-moe-模型" class="heading-element"><span>2 几个典型的 MoE 模型</span> <a href="#%e5%87%a0%e4%b8%aa%e5%85%b8%e5%9e%8b%e7%9a%84-moe-%e6%a8%a1%e5%9e%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>近年来,MoE 模型在 AI 研究中备受关注,因为它能够在保持高性能的同时,高效扩展大型语言模型。其中,Mixtral 8x7B 是一个典型的例子,它采用了稀疏的专家混合架构。该模型只对每个输入激活其专家的子集,从而在实现与更大、完全密集模型相当的性能的同时,显著提高了效率。</p> <p>接下来,我们来看看几个热门的 MoE 模型,并通过在 Google Colab 上使用 Ollama 进行 Python 实现,感受一下它们的魅力。</p> <h3 id="mixtral-8x7b" class="heading-element"><span>2.1 Mixtral 8X7B</span> <a href="#mixtral-8x7b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p><a href="https://huggingface.co/mistralai/Mixtral-8x7B-v0.1"target="_blank" rel="external nofollow noopener noreferrer">Mixtral 8X7B<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a> 的架构基于decoder-only的 Transformer。模型输入是一系列tokens,这些tokens被嵌入为向量,然后通过解码器层进行处理。输出是每个位置被某个单词占据的概率,从而实现文本填充和预测。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/12/ccb17e49fea11e6db8e4d3ce48d9f373.avif" alt="MoE Decoder" srcset="https://cdn1.zair.top/images/2024/12/ccb17e49fea11e6db8e4d3ce48d9f373.avif?size=small, https://cdn1.zair.top/images/2024/12/ccb17e49fea11e6db8e4d3ce48d9f373.avif?size=medium 1.5x, https://cdn1.zair.top/images/2024/12/ccb17e49fea11e6db8e4d3ce48d9f373.avif?size=large 2x" data-title="MoE Decoder" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>每个解码器层包含两个关键部分:一个注意力机制,用于整合上下文信息;以及一个稀疏专家混合 (SMOE) 部分,用于分别处理每个词向量。MLP 层是计算资源的消耗大头。SMOE 有多个层(“专家”)可用。对于每个输入,取最相关专家输出的加权和。因此,SMOE 层可以在计算成本相对较低的情况下学习复杂的模式。</p> <p><strong>模型的关键特性</strong>:</p> <ul> <li>专家总数:8</li> <li>激活的专家数量:2</li> <li>解码器层数:32</li> <li>词汇表大小:32000</li> <li>嵌入大小:4096</li> <li>每个专家的大小:56 亿参数,而不是 70 亿。剩余的参数(70亿 - 56亿)来自共用组件,如嵌入层、归一化层和门控机制。</li> <li>激活参数总数:128 亿</li> <li>上下文长度:32k tokens</li> </ul> <p>在加载模型时,所有 448 亿(8*56 亿参数)必须加载(以及所有共享参数),但我们只需要使用 2×56 亿(128 亿)激活参数进行推理。</p> <p>Mixtral 8x7B 在文本生成、理解、翻译、摘要、情感分析、教育、客户服务自动化、研究辅助等多个领域表现出色。其高效的架构使其成为跨多个领域的强大工具。</p> <h3 id="dbrx" class="heading-element"><span>2.2 DBRX</span> <a href="#dbrx" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p><a href="https://github.com/databricks/dbrx"target="_blank" rel="external nofollow noopener noreferrer">DBRX<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a> 由 Databricks 开发,是一个基于 Transformer 的decoder-only大型语言模型 (LLM),使用下一个标记预测(next-token prediction)进行训练。它采用细粒度的专家混合 (MoE) 架构,总参数为 1320 亿,其中每个输入激活 360 亿参数。它在 12 万亿个文本和代码数据上进行了预训练。与其他开源的 MoE 模型(如 Mixtral 和 Grok-1)相比,DBRX 粒度更细,使用了更多的小型专家。DBRX 有 16 个专家并选择 4 个,而 Mixtral 和 Grok-1 有 8 个专家并选择 2 个。</p> <p><strong>架构的关键特性</strong>:</p> <ul> <li><strong>细粒度专家</strong>:通常情况下,从标准 FFN 层过渡到专家混合 (MoE) 层时,只需多次复制 FFN 以创建多个专家。然而在细粒度专家的背景下,我们的目标是生成更多专家而不增加参数数量。为此,可以将单个 FFN 划分为多个段,每个段作为一个单独的专家。DBRX 采用细粒度的 MoE 架构,有 16 个专家,从中为每个输入选择 4 个专家。</li> <li>该模型还采用了其他创新技术,如旋转位置编码 (RoPE)、门控线性单元 (GLU) 和分组查询注意力 (GQA)。</li> </ul> <p><strong>模型的关键特性</strong>:</p> <ul> <li>专家总数:16</li> <li>每层激活的专家数量:4</li> <li>解码器层数:24</li> <li>激活参数总数:360 亿</li> <li>总参数数:1320 亿</li> <li>上下文长度:32k tokens</li> </ul> <p>DBRX 模型在代码生成、复杂语言理解、数学推理和编程任务等用例中表现出色,特别是在需要高准确性和效率的场景中,如生成代码片段、解决数学问题以及对复杂提示提供详细解释。</p> <h3 id="deepseek-v2" class="heading-element"><span>2.3 Deepseek-v2</span> <a href="#deepseek-v2" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在 <a href="https://github.com/deepseek-ai/DeepSeek-V2"target="_blank" rel="external nofollow noopener noreferrer">Deepseek-v2<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a> 的 MoE 架构中,利用了两个关键思想:</p> <ul> <li><strong>细粒度专家</strong>:将专家细分为更细的粒度,以实现更高的专家专业化和对知识的更准确获取。</li> <li><strong>共享专家</strong>:该方法侧重于指定某些专家作为共享专家,确保它们始终处于激活状态。这种策略有助于收集和整合适用于各种上下文的通用知识。</li> </ul> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/12/e813bb99ca7a99faa7fff420daaf43a7.avif" alt="DeepSeekMoE" srcset="https://cdn1.zair.top/images/2024/12/e813bb99ca7a99faa7fff420daaf43a7.avif?size=small, https://cdn1.zair.top/images/2024/12/e813bb99ca7a99faa7fff420daaf43a7.avif?size=medium 1.5x, https://cdn1.zair.top/images/2024/12/e813bb99ca7a99faa7fff420daaf43a7.avif?size=large 2x" data-title="DeepSeekMoE" class="suffix-invalid suffix-invalid__small suffix-invalid__large" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p><strong>模型的关键特性</strong>:</p> <ul> <li>总参数数:2360 亿</li> <li>激活参数总数:210 亿</li> <li>每层路由的专家数:160(其中选择 2 个)</li> <li>每层共享专家数:2</li> <li>每层激活的专家数:8</li> <li>解码器层数:60</li> <li>上下文长度:128k tokens</li> </ul> <p>该模型在 8.1 万亿个tokens的庞大数据集上进行了预训练。</p> <p>DeepSeek-V2 特别擅长进行对话,因此适用于聊天机器人和虚拟助手。该模型可以生成高质量的文本,因此适用于内容创作、语言翻译和文本摘要。该模型还可以高效地用于代码生成用例。</p> <h2 id="用-python-实现-moe-模型" class="heading-element"><span>3 用 Python 实现 MoE 模型</span> <a href="#%e7%94%a8-python-%e5%ae%9e%e7%8e%b0-moe-%e6%a8%a1%e5%9e%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>MoE 模型是一种先进的机器学习模型,它动态选择不同的专家网络来处理不同的任务。接下来,我们来看看如何用 Python 实现 MoE 模型,并感受一下它的强大之处。</p> <h3 id="步骤1安装所需的-python-库" class="heading-element"><span>3.1 步骤1:安装所需的 Python 库</span> <a href="#%e6%ad%a5%e9%aa%a41%e5%ae%89%e8%a3%85%e6%89%80%e9%9c%80%e7%9a%84-python-%e5%ba%93" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>首先,我们需要安装一些必要的 Python 库:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="err">!</span><span class="n">sudo</span> <span class="n">apt</span> <span class="n">update</span> </span></span><span class="line"><span class="cl"><span class="err">!</span><span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="o">-</span><span class="n">y</span> <span class="n">pciutils</span> </span></span><span class="line"><span class="cl"><span class="err">!</span><span class="n">pip</span> <span class="n">install</span> <span class="n">langchain</span><span class="o">-</span><span class="n">ollama</span> </span></span><span class="line"><span class="cl"><span class="err">!</span><span class="n">curl</span> <span class="o">-</span><span class="n">fsSL</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">ollama</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">install</span><span class="o">.</span><span class="n">sh</span> <span class="o">|</span> <span class="n">sh</span> </span></span><span class="line"><span class="cl"><span class="err">!</span><span class="n">pip</span> <span class="n">install</span> <span class="n">ollama</span><span class="o">==</span><span class="mf">0.4.2</span></span></span></code></pre></td></tr></table> </div> </div><h3 id="步骤2启用线程" class="heading-element"><span>3.2 步骤2:启用线程</span> <a href="#%e6%ad%a5%e9%aa%a42%e5%90%af%e7%94%a8%e7%ba%bf%e7%a8%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>接下来用threading来运行 Ollama 服务:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span> </span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">subprocess</span> </span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">run_ollama_serve</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="s2">&#34;ollama&#34;</span><span class="p">,</span> <span class="s2">&#34;serve&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">run_ollama_serve</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> </span></span><span class="line"><span class="cl"><span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div><h3 id="步骤3拉取-ollama-模型" class="heading-element"><span>3.3 步骤3:拉取 Ollama 模型</span> <a href="#%e6%ad%a5%e9%aa%a43%e6%8b%89%e5%8f%96-ollama-%e6%a8%a1%e5%9e%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>拉取一个 Ollama 模型,比如 DBRX:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="err">!</span><span class="n">ollama</span> <span class="n">pull</span> <span class="n">dbrx</span></span></span></code></pre></td></tr></table> </div> </div><h3 id="步骤4查询模型" class="heading-element"><span>3.4 步骤4:查询模型</span> <a href="#%e6%ad%a5%e9%aa%a44%e6%9f%a5%e8%af%a2%e6%a8%a1%e5%9e%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>查询测试模型,看看它的表现如何:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain_core.prompts</span> <span class="kn">import</span> <span class="n">ChatPromptTemplate</span> </span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">langchain_ollama.llms</span> <span class="kn">import</span> <span class="n">OllamaLLM</span> </span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">Markdown</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">template</span> <span class="o">=</span> <span class="s2">&#34;&#34;&#34;Question: </span><span class="si">{question}</span><span class="s2"> </span></span></span><span class="line"><span class="cl"><span class="s2"> </span></span></span><span class="line"><span class="cl"><span class="s2">Answer: Let&#39;s think step by step.&#34;&#34;&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">prompt</span> <span class="o">=</span> <span class="n">ChatPromptTemplate</span><span class="o">.</span><span class="n">from_template</span><span class="p">(</span><span class="n">template</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">model</span> <span class="o">=</span> <span class="n">OllamaLLM</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="s2">&#34;dbrx&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">chain</span> <span class="o">=</span> <span class="n">prompt</span> <span class="o">|</span> <span class="n">model</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 准备输入以进行调用</span> </span></span><span class="line"><span class="cl"><span class="n">input_data</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;question&#34;</span><span class="p">:</span> <span class="s1">&#39;将以下内容总结成一句话:&#34;Bob 是一个男孩。Bob 有一只狗。Bob 和他的狗去散步。Bob 和他的狗走到公园。在公园里,Bob 扔了一根棍子,他的狗把它带回来。狗追了一只松鼠,Bob 追着它跑。Bob 把他的狗找回来,他们一起走回家。&#34;&#39;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 使用输入数据调用链并显示响应</span> </span></span><span class="line"><span class="cl"><span class="n">response</span> <span class="o">=</span> <span class="n">chain</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">input_data</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">display</span><span class="p">(</span><span class="n">Markdown</span><span class="p">(</span><span class="n">response</span><span class="p">))</span></span></span></code></pre></td></tr></table> </div> </div><h2 id="mixtral-8x7b-模型的实际效果" class="heading-element"><span>4 Mixtral 8x7B 模型的实际效果</span> <a href="#mixtral-8x7b-%e6%a8%a1%e5%9e%8b%e7%9a%84%e5%ae%9e%e9%99%85%e6%95%88%e6%9e%9c" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在比较不同 MoE 模型的输出时,我们可以从逻辑推理、摘要和实体提取等任务的表现来分析它们的差异。</p> <p><strong>逻辑推理问题</strong></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">给我一个包含 13 个单词的清单,每个单词有 9 个字母。</span></span></code></pre></td></tr></table> </div> </div><p><strong>输出</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">Sure,here are thirteen words,each with nine letters: </span></span><span class="line"><span class="cl"><span class="k">1.</span> Agriculture </span></span><span class="line"><span class="cl"><span class="k">2.</span> Beautiful </span></span><span class="line"><span class="cl"><span class="k">3.</span> Chocolate </span></span><span class="line"><span class="cl"><span class="k">4.</span> Dangerous </span></span><span class="line"><span class="cl"><span class="k">5.</span> Encyclopedia </span></span><span class="line"><span class="cl"><span class="k">6.</span> Fireplace </span></span><span class="line"><span class="cl"><span class="k">7.</span> Grammarly </span></span><span class="line"><span class="cl"><span class="k">8.</span> Hamburger </span></span><span class="line"><span class="cl"><span class="k">9.</span> Important </span></span><span class="line"><span class="cl"><span class="k">10.</span> Juxtapose </span></span><span class="line"><span class="cl"><span class="k">11.</span> Kitchener </span></span><span class="line"><span class="cl"><span class="k">12.</span> Landscape </span></span><span class="line"><span class="cl"><span class="k">13.</span> Necessary </span></span><span class="line"><span class="cl">I hope this list helps! If you have any other questions or need further assistance,feel free to ask.</span></span></code></pre></td></tr></table> </div> </div><p>从上面的输出可以看出,所有响应的单词都不包含 9 个字母。在 13 个单词中,只有 8 个单词包含 9 个字母。因此回复部分正确。</p> <p><strong>摘要问题</strong></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">将以下内容总结成一句话:&#34;Bob 是一个男孩。他有一只狗。Bob 和他的狗去散步。Bob 和他的狗走到公园。在公园里,Bob 扔了一根棍子,他的狗把它带回来。狗追了一只松鼠,Bob 追着它跑。Bob 把他的狗找回来,他们一起走回家。&#34;</span></span></code></pre></td></tr></table> </div> </div><p><strong>输出</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">小男孩Bob很享受与他爱犬一起进行的一次活跃郊游。他们一起走到公园,一起玩捡球游戏、追松鼠,然后一起回到家。</span></span></code></pre></td></tr></table> </div> </div><p>从上面的输出可以看出,回复总结得相当好。</p> <p><strong>实体提取</strong></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">从文本中提取所有数值及其对应单位:&#34;马拉松长42公里,超过30,000人参加。&#34;</span></span></code></pre></td></tr></table> </div> </div><p><strong>输出</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="k">1.</span> 在给定的文本中,有两个数值及其对应的单位: </span></span><span class="line"><span class="cl"> <span class="k">-</span> 42(马拉松的长度,文中未提及单位,但后面提到可以推断为千米) </span></span><span class="line"><span class="cl"> <span class="k">-</span> 30,000(参与人数) </span></span><span class="line"><span class="cl"><span class="k">2.</span> 文中后来提供了马拉松长度的单位:千米。由于没有为这个值指定其他单位,我们假设它是以千米为单位。 </span></span><span class="line"><span class="cl"><span class="k">3.</span> 因此,最终答案是: </span></span><span class="line"><span class="cl"> <span class="k">-</span> 42(千米,马拉松长度) </span></span><span class="line"><span class="cl"> - 30,000(参与人数)</span></span></code></pre></td></tr></table> </div> </div><p>从上面的输出可以看出,结果正确提取了所有数值和单位。</p> <p><strong>数学推理问题</strong></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">我有2个苹果,然后我又买了2个。我用2个苹果烤了一个派。吃完半个派后,我还剩下多少苹果?</span></span></code></pre></td></tr></table> </div> </div><p><strong>输出</strong>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl"><span class="k">1.</span> 开始时有2个苹果。 </span></span><span class="line"><span class="cl"><span class="k">2.</span> 再买2个苹果,这样你就有4个苹果了(2 + 2 = 4)。 </span></span><span class="line"><span class="cl"><span class="k">3.</span> 用2个苹果做一个派,这样你还剩下2个苹果(4 - 2 = 2)。 </span></span><span class="line"><span class="cl">4. 吃掉一半的派,但由于我们不知道派里有多少个苹果,仅凭这些信息无法确定还剩下多少个苹果。</span></span></code></pre></td></tr></table> </div> </div><p>模型的输出不准确。准确的输出应该是2,因为4个苹果中有两个做了苹果派,剩下的两个苹果还在。</p> <h2 id="总结" class="heading-element"><span>5 总结</span> <a href="#%e6%80%bb%e7%bb%93" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>MoE 模型通过仅激活每个任务的相关专家,提供了一种高效的深度学习方法。这种选择性激活使得 MoE 模型能够在与传统密集模型相比减少计算资源的情况下执行复杂操作。但MoE 模型也需要进行取舍,因为它需要大量的 VRAM 来存储所有专家。</p> <p>Mixtral 8X7B 架构是一个典型的例子,它利用稀疏专家混合 (SMoE) 机制,仅激活专家的子集以实现高效的文本处理,显著降低了计算成本。它拥有 128 亿激活参数和 32k tokens的上下文长度,在从文本生成到客户服务自动化的广泛应用中表现不错。Databricks 的 DRBX 模型也因其创新的细粒度 MoE 架构而脱颖而出,允许其在每个输入时仅激活 360 亿参数的情况下利用 1320 亿参数。同样,DeepSeek-v2 利用细粒度和共享专家,提供了一个强大的架构,拥有 2360 亿参数和 128,000 tokens的上下文长度,使其成为聊天机器人、内容创作和代码生成等多样化应用的理想选择。</p> <h3 id="关键要点" class="heading-element"><span>5.1 关键要点</span> <a href="#%e5%85%b3%e9%94%ae%e8%a6%81%e7%82%b9" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>MoE 模型通过仅激活特定任务的相关专家,提高了深度学习的效率,与传统密集模型相比减少了计算资源的使用。</li> <li>尽管 MoE 模型提供了计算效率,但它们需要大量的 VRAM 来存储所有专家,突显了计算能力和内存需求之间的关键权衡。</li> <li>Mixtral 8X7B 采用稀疏专家混合 (SMoE) 机制,激活其 128 亿激活参数的子集以实现高效的文本处理,并支持 32,000 标记的上下文长度,使其适用于各种应用,包括文本生成和客户服务自动化。</li> <li>Databricks 的 DBRX 模型采用细粒度的专家混合架构,在每个输入时仅激活 360 亿参数的情况下高效利用 1320 亿总参数,展示了其在处理复杂语言任务中的能力。</li> <li>DeepSeek-v2 结合了细粒度和共享专家策略,形成了一个强大的架构,拥有 2360 亿参数和 128,000 标记的上下文长度,使其在聊天机器人、内容创作和代码生成等多样化应用中表现出色。</li> </ul> <h2 id="常见问题" class="heading-element"><span>6 常见问题</span> <a href="#%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p><strong>Q1. 什么是专家混合模型 (MoE)?</strong></p> <p>A. MoE 模型使用稀疏架构,仅激活每个任务最相关的专家,从而减少与传统密集模型相比的计算资源使用。</p> <p><strong>Q2. 使用 MoE 模型的利弊是什么?</strong></p> <p>A. 尽管 MoE 模型提高了计算效率,但它们需要大量的 VRAM 来存储所有专家,需要在计算能力和内存需求之间权衡。</p> <p><strong>Q3. Mixtral 8X7B 模型的激活参数数量是多少?</strong></p> <p>A. Mixtral 8X7B 有 128 亿(2×56 亿)激活参数,占总数 448 亿(8×56 亿参数)的一部分,使其能够高效处理复杂任务并提供更快的推理。</p> <p><strong>Q4. DBRX 模型与其他 MoE 模型(如 Mixtral 和 Grok-1)有何不同?</strong></p> <p>A. DBRX 采用细粒度的专家混合方法,每层有 16 个专家和 4 个激活专家,而其他 MoE 模型每层有 8 个专家和 2 个激活专家。</p> <p><strong>Q5. DeepSeek-v2 与其他 MoE 模型有何不同?</strong></p> <p>A. DeepSeek-v2 结合了细粒度和共享专家,以及其庞大的参数集和广泛的上下文长度,使其成为多种应用的强大工具。</p> <h2 id="推荐阅读" class="heading-element"><span>7 推荐阅读</span> <a href="#%e6%8e%a8%e8%8d%90%e9%98%85%e8%af%bb" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li><a href="https://mistral.ai/news/mixtral-of-experts/"target="_blank" rel="external nofollow noopener noreferrer">Mixtral of experts | Mistral AI | Frontier AI in your hands<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://medium.com/@bnjmn_marie/deepseekmoe-moe-with-segmented-and-shared-experts-dedf22e4a98c"target="_blank" rel="external nofollow noopener noreferrer">DeepSeekMoE: MoE with Segmented and Shared Experts<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://arxiv.org/abs/2401.04088"target="_blank" rel="external nofollow noopener noreferrer">Mixtral of Experts<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://arxiv.org/abs/2401.04089"target="_blank" rel="external nofollow noopener noreferrer">DBRX: A Large-Scale Decoder-Only Language Model with Fine-Grained Mixture of Experts<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> </ol>

2024/12/25
阅读更多

数学视角下的支持向量机(SVM):优化问题求解

<blockquote> <p>支持向量机(SVM)是机器学习中的经典算法。本文聚焦于SVM中的公式推导,如间隔距离公式的详细推理,以及原问题与对偶问题公式化阐述。深入探讨优化问题,包括构建拉格朗日函数来处理约束优化问题,利用KKT条件求解最优解的过程。同时涉及多项式核函数与高斯核函数公式特性。</p> </blockquote> <h2 id="间隔距离推理" class="heading-element"><span>间隔距离推理</span> <a href="#%e9%97%b4%e9%9a%94%e8%b7%9d%e7%a6%bb%e6%8e%a8%e7%90%86" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在支持向量机(SVM)中,正超平面和负超平面的式分别为: $$ \vec{w} \cdot \vec{x} + b = 1 \quad \text{(正超平面)} $$ $$ \vec{w} \cdot \vec{x} + b = -1 \quad \text{(负超平面)} $$ 其中$\vec{w}=(w_1, w_2)$是权重向量,$b$是偏置项,$\vec{x}=(x_1, x_2)$是数据点。</p> <p>假设$\vec{x_m}$是正超平面上的点,$\vec{x_n}$是负超平面上的点,则有: $$ w_1 x_{1m} + w_2 x_{2m} + b = 1 \quad \text{(1)} $$ $$ w_1 x_{1n} + w_2 x_{2n} + b = -1 \quad \text{(2)} $$</p> <p>用式(1)减去式(2),得到: $$ w_1 (x_{1m} - x_{1n}) + w_2 (x_{2m} - x_{2n}) = 2 $$ 写成向量形式: $$ \vec{w} \cdot (\vec{x_m} - \vec{x_n}) = 2 \quad \text{(3)} $$ 考虑决策超平面上的两个点$\vec{x_0}$和$\vec{x_p}$,它们满足决策超平面式$\vec{w} \cdot \vec{x} + b = 0$,即: $$ w_1 x_{10} + w_2 x_{20} + b = 0 $$ $$ w_1 x_{1p} + w_2 x_{2p} + b = 0 $$ 两式相减得到: $$ w_1 (x_{10} - x_{1p}) + w_2 (x_{20} - x_{2p}) = 0 $$ 写成向量形式: $$ \vec{w} \cdot (\vec{x_0} - \vec{x_p}) = 0 \quad \text{(4)} $$ 式(4)表明$\vec{w}$与决策超平面上任意两点的向量差是垂直的。</p> <p>从式(3)和(4)可知,$\vec{w}$与$(\vec{x_m} - \vec{x_n})$的点积为2,根据向量点积的定义$\vec{a} \cdot \vec{b}=|\vec{a}| \cdot |\vec{b}| \cdot \cos \theta$,这里$\theta$是$\vec{w}$与$(\vec{x_m} - \vec{x_n})$的夹角,我们有: $$ |\vec{x_m} - \vec{x_n}| \cdot \cos \theta \cdot |\vec{w}| = 2 $$ 令$L = |\vec{x_m} - \vec{x_n}| \cdot \cos \theta$,则: $$ L \cdot |\vec{w}| = 2 $$ 解得: $$ L=\frac{2}{|\vec{w}|} $$</p> <p>这里$L$就是SVM的间隔(margin)距离。</p> <p>在推导间隔距离时,我们利用了向量点积的几何意义,即$\vec{a} \cdot \vec{b}=|\vec{a}| \cdot |\vec{b}| \cdot \cos \theta$,其中$\theta$是两向量的夹角。通过这个关系,我们将点积转化为向量模长和夹角的关系,从而得出间隔距离的表达式。</p> <h2 id="对偶等价证明" class="heading-element"><span>对偶等价证明</span> <a href="#%e5%af%b9%e5%81%b6%e7%ad%89%e4%bb%b7%e8%af%81%e6%98%8e" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在线性支持向量机(SVM)中,原问题是找到最小化目标函数的权重向量$w$和偏置$b$:</p> <p>$$ \min_w f(w) = \frac{1}{2} |w|^2 $$</p> <p>这里的$|w|^2$代表向量$w$的欧几里得范数平方,即$L_2$范数。目标是最小化决策边界的宽度,以获得更好的泛化能力。该问题受到以下约束:</p> <p>$$ y_j (w^T x_j + b) - 1 \geq 0 $$</p> <p>这里$x_j$是第$j$个训练样本,$y_j$是对应的标签,取值为+1或-1,这确保了所有数据点都被正确分类,并且距离决策边界至少有一个单位的距离。</p> <p>为了处理这个带约束的优化问题,我们构建拉格朗日函数:</p> <p>$$ L(w, b, \alpha) = f(w) - \sum_{j = 1}^n \alpha_j g_j(w, b) $$</p> <p>这里$\alpha_j \geq 0$是拉格朗日乘子,用于引入原始问题中的约束条件$g_j(w, b) = y_j (w^T x_j + b) - 1 \geq 0$。</p> <p>接下来,我们定义对偶函数$q(\alpha)$为:</p> <p>$$ q(\alpha) = \min_{w, b} L(w, b, \alpha) = \min_{w, b} \left( f(w) - \sum_{j = 1}^n \alpha_j g_j(w, b) \right) $$</p> <p>因为$\alpha_j \geq 0$ 和$g_j(w^{*}, b^{*}) \geq 0$ ,我们可以得出:</p> <p>$$ q(\alpha) = \min_{w, b} \left( f(w) - \sum_{j = 1}^n \alpha_j g_j(w, b) \right) \leq f(w^*) - \sum_{j = 1}^n \alpha_j g_j(w^*, b^*) \leq f(w^*) \leq f(w) $$</p> <p>这意味着对偶函数给出了原问题的一个下界。接下来,我们需要找到一个$\alpha^*$,使得:</p> <p>$$ q(\alpha) \leq q(\alpha^*) \leq f(w^*) \leq f(w) $$</p> <p>SVM的原问题和对偶问题可以表述为:</p> <p>$$ \max_{\alpha} q(\alpha) = \max_{\alpha} \min_{w, b} L(w, b, \alpha) $$</p> <p>其约束条件为:$ \alpha_i \geq 0 $</p> <p>并且,当满足弱对偶性时,我们有$q(\alpha^*) \leq f(w^*)$;而当满足强对偶性时,即Slater条件成立时,我们有$q(\alpha^*) = f(w^*)$。Slater条件要求存在一个可行解使得所有不等式约束严格成立,而线性支持向量机是线性可分的,能自动满足Slater条件。</p> <p>至此,我们有:</p> <p>$$ f(w) \geq q(\alpha^*) = f(w^*) \geq q(\alpha_i) $$</p> <p>根据上面这个式子,我们能得到:</p> <p>$$ q(\alpha^*) \geq q(\alpha_i) $$ $$ f(w^*) \leq f(w) $$</p> <p>$f(w)$ 找到了最小值(原问题),$q(\alpha)$找到了最大值(对偶问题),原问题和对偶问题的最优解是相等的,即:</p> <p>$ w^*, b^* $是原问题的解,$\alpha^*$是对偶问题的解,且$f(w^*) = q(\alpha^*)$。</p> <p>我们可以看到在线性SVM中,当满足特定条件(Slater条件)时,原问题和对偶问题的解是一致的。这为是解决复杂的优化问题的一种有效途径,尤其是<strong>在原问题难以直接求解时,可以通过求解对偶问题来间接解决问题</strong>。</p> <h3 id="简单的例子" class="heading-element"><span>简单的例子</span> <a href="#%e7%ae%80%e5%8d%95%e7%9a%84%e4%be%8b%e5%ad%90" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>为了更直观地理解上面原问题和对偶问题的解是相同的,考虑一个简单的优化问题,原问题定义如下:</p> <p>原问题为: $$ \min_x f(x) = x^2 $$ 约束条件为: $$ x - 1 \geq 0 $$</p> <p>这个问题的目标是最小化函数$f(x) = x^2$,同时$x$需要满足$x \geq 1$。直观上,我们知道当$x = 1$时,$f(x) = 1$,这是在给定约束下的最小值。</p> <p>为了验证对偶性,我们构造拉格朗日函数:</p> <p>$$ q(\alpha) = \min_x L(x, \alpha) = \min_x (x^2 - \alpha(x - 1)) $$</p> <p>这里$\alpha \geq 0$是拉格朗日乘子,用来引入原问题中的约束条件$x - 1 \geq 0$。通过构造拉格朗日函数,我们将有约束的优化问题转换成了一个无约束的问题。</p> <p>接下来,我们对$L(x, \alpha)$关于$x$求偏导数,并令其等于0:</p> <p>$$ \frac{\partial L}{\partial x} = 0 2x - \alpha = 0 $$</p> <p>解得:</p> <p>$$ x = \frac{\alpha}{2} $$</p> <p>将$x = \frac{\alpha}{2}$代入$q(\alpha)$中:</p> <p>$$ q(\alpha) = - \frac{\alpha^2}{4} + \alpha $$</p> <p>现在我们已经得到了对偶函数$q(\alpha)$的形式。接下来,我们需要求解对偶问题的最大值$\max_{\alpha} q(\alpha) $</p> <p>为此,我们对$q(\alpha)$关于$\alpha$求导,并令其等于0:</p> <p>$$ \frac{dq}{d\alpha} = - \frac{\alpha}{2} + 1 = 0 $$</p> <p>解得$$ \alpha = 2 $$</p> <p>将$\alpha = 2$代入$x = \frac{\alpha}{2}$,得到:$$ x = 1 $$</p> <p>此时,将$\alpha = 2$代入$q(\alpha)$,计算得到:</p> <p>$$ q(\alpha) = - \frac{2^2}{4} + 2 = 1 $$</p> <p>通过这个简单例子,我们可以看到,原问题的解$x = 1$,$f(x) = 1$,与对偶问题的解$\alpha = 2$,$q(\alpha) = 1$是等价的。这验证了在满足一定条件下,对偶问题的解与原问题的解是一致的。</p> <p>通过对偶理论的应用,我们不仅找到了原问题的解,还通过求解对偶问题得到了相同的结果,从而验证了对偶问题解的等价性。</p> <h2 id="kkt条件求解" class="heading-element"><span>KKT条件求解</span> <a href="#kkt%e6%9d%a1%e4%bb%b6%e6%b1%82%e8%a7%a3" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="svm满足kkt条件" class="heading-element"><span>SVM满足KKT条件</span> <a href="#svm%e6%bb%a1%e8%b6%b3kkt%e6%9d%a1%e4%bb%b6" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>SVM的原始优化问题是一个凸优化问题。SVM的目标函数 $\frac{1}{2}|w|^2$ 是一个二次函数,它是关于 $w$ 的<strong>凸函数</strong>。同时,约束条件 $y_i(w \cdot x_i + b) \geq 1$ 是线性的(<strong>仿射约束</strong>),因此也是凸的。在凸优化问题中,局部最优解就是全局最优解,而且KKT条件是必要且充分的条件。这意味着如果一个点满足KKT条件,那么它就是全局最优解。</p> <p>目标函数 $\frac{1}{2}|w|^2$ 是连续且可微的,约束条件 $y_i(w \cdot x_i + b) \geq 1$ 也是连续且可微的。这种光滑性保证了梯度的存在性和唯一性,从而让KKT条件中的梯度条件(即对 $w$ 和 $b$ 求偏导数并设置为零)能够有效应用。</p> <p>在凸优化问题中,KKT条件不仅是必要条件,而且是充分条件。也就是说如果一个点满足KKT条件,那么它一定是全局最优解。对于SVM来说,通过求解KKT条件,我们可以找到最优的 $w^*$ 和 $b^*$,从而确定最佳的分离超平面。</p> <h3 id="利用kkt条件求解线性支持向量机" class="heading-element"><span>利用KKT条件求解线性支持向量机</span> <a href="#%e5%88%a9%e7%94%a8kkt%e6%9d%a1%e4%bb%b6%e6%b1%82%e8%a7%a3%e7%ba%bf%e6%80%a7%e6%94%af%e6%8c%81%e5%90%91%e9%87%8f%e6%9c%ba" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>原始的SVM优化问题是最小化 $\frac{1}{2}|w|^{2}$,同时满足约束条件 $y_{i}(w\cdot x_{i}+b)\geqslant1$,这里 $i = 1,2,\cdots,N$。</p> <p>首先,构建拉格朗日函数 $L(w,b,\alpha)=\frac{1}{2}|w|^{2}-\sum_{i = 1}^{N}\alpha_{i}(y_{i}(w\cdot x_{i}+b)-1)$,其中 $\alpha_{i}\geqslant0$ 是拉格朗日乘子。根据KKT条件,我们有:</p> <p>$$ \nabla_{w}L(w^*,b^*,\alpha^*) = w^*-\sum_{i = 1}^{N}\alpha_{i}^*y_{i}x_{i}=0 $$</p> <p>$$ \nabla_{b}L(w^*,b^*,\alpha^*)=-\sum_{i = 1}^{N}\alpha_{i}^*y_{i}=0 $$</p> <p>$$ \alpha_{i}^*(y_{i}(w^*\cdot x_{i}+b^*)-1)=0 $$</p> <p>$$ y_{i}(w^*\cdot x_{i}+b^*)-1\geqslant0 $$</p> <p>$$ \alpha_{i}^*\geqslant0 $$</p> <p>这些条件适用于所有 $i = 1,2,\cdots,N$ 的情况。</p> <p>从 $\nabla_{w}L(w^*,b^*,\alpha^*) = w^*-\sum_{i = 1}^{N}\alpha_{i}^*y_{i}x_{i}=0$ 可以得出</p> <p>$$ w^*=\sum_{i = 1}^{N}\alpha_{i}^*y_{i}x_{i} \quad \text{(5)} $$ 由于至少有一个 $\alpha_{j}^*&gt;0$(如果假设 $\alpha_{i}^*=0$,则会导致由式 $\nabla_{w}L(w^*,b^*,\alpha^*) = w^*-\sum_{i = 1}^{N}\alpha_{i}^*y_{i}x_{i}=0$ 给出的解产生矛盾)。</p> <p>对于 $b^*$ 的求解,可以通过将 $w^*=\sum_{i = 1}^{N}\alpha_{i}^*y_{i}x_{i}$ 代入到 $y_{j}(w^*\cdot x_{j}+b^*)-1 = 0$(考虑到存在 $\alpha_{j}^*&gt;0$ 的情况),并注意到 $y_{j}^{2}=1$,从而得到:</p> <p>$$ b^*=y_{j}-\sum_{i = 1}^{N}\alpha_{i}^*y_{i}(x_{i}\cdot x_{j}) \quad \text{(6)} $$</p> <p>基于上述理论,分离超平面可以表达为:</p> <p>$$ \sum_{i = 1}^{N}\alpha_{i}^*y_{i}(x\cdot x_{i})+b^*=0 $$</p> <p>从而分类决策函数则可以写作:</p> <p>$$ f(x)=\text{sign}(\sum_{i = 1}^{N}\alpha_{i}^*y_{i}(x\cdot x_{i})+b^*) $$</p> <p>在SVM中,互补松弛条件 $\alpha_i (y_i(w \cdot x_i + b) - 1) = 0$ 表明,如果一个样本点 $x_i$ 不是支持向量(即 $y_i(w \cdot x_i + b) &gt; 1$),那么对应的拉格朗日乘子 $\alpha_i$ 必须为零。相反,如果一个样本点是支持向量(即 $y_i(w \cdot x_i + b) = 1$),那么对应的 $\alpha_i$ 可以是非零的。这种条件确保了只有支持向量对优化问题的解有贡献,从而简化了问题的求解过程。</p> <h2 id="多项式核函数与高斯核函数" class="heading-element"><span>多项式核函数与高斯核函数</span> <a href="#%e5%a4%9a%e9%a1%b9%e5%bc%8f%e6%a0%b8%e5%87%bd%e6%95%b0%e4%b8%8e%e9%ab%98%e6%96%af%e6%a0%b8%e5%87%bd%e6%95%b0" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>如果现有的问题不是线性可分的,我们可以对现有的数据映射到高维空间,使其在高维空间成为一个线性可分的问题。但直接在高维特征空间中进行计算会非常复杂。由式(5)和式(6)可知,我们其实不需要真的去把数据映射到高维空间,只要知道数据点之间的内积就可以了。核函数的作用就是避免显式地进行高维特征映射,通过在原始特征空间中计算核函数值来间接实现高维特征空间中的内积计算。</p> <p>高斯核函数是一种常见的核函数,其形式为: $$ K(x, y) = \exp\left(-\gamma |x - y|^2\right) $$</p> <p>其中 $\gamma$ 是一个正的参数,控制着核函数的宽度。</p> <p>我们可以对指数函数进行泰勒展开:</p> <p>$$ \exp(z) = \sum_{k=0}^{\infty} \frac{z^k}{k!} $$</p> <p>将 $ z = -\gamma |x - y|^2 $ 代入上述公式,得到:</p> <p>$$ K(x, y) = \exp\left(-\gamma |x - y|^2\right) = \sum_{k=0}^{\infty} \frac{(-\gamma |x - y|^2)^k}{k!} $$</p> <p>多项式核函数的形式为:</p> <p>$$ K_{\text{poly}}(x, y) = (x \cdot y + c)^d $$</p> <p>其中 $ c $ 是常数项,$ d $ 是多项式的次数。</p> <p>$|x - y|^2$ 可以展开为:</p> <p>$$ |x - y|^2 = (x - y) \cdot (x - y) = x \cdot x + y \cdot y - 2 x \cdot y $$</p> <p>将这一表达式代入高斯核函数的泰勒展开式中:</p> <p>$$ K(x, y) = \sum_{k=0}^{\infty} \frac{(-\gamma (x \cdot x + y \cdot y - 2 x \cdot y))^k}{k!} $$</p> <p>可以看到,每一项 $ \frac{(-\gamma (x \cdot x + y \cdot y - 2 x \cdot y))^k}{k!} $ 实际上是一个多项式项,也就是说每一项都可以表示为 $ x $ 和 $ y $ 的不同幂次的组合。</p> <p>如果我们仔细观察每一项,可以发现高斯核函数实际上是通过将不同阶次的多项式核函数进行加权求和得到的。每一项 $ \frac{(-\gamma (x \cdot x + y \cdot y - 2 x \cdot y))^k}{k!} $ 可以看作是一个 $ k $-阶多项式核函数的加权形式。</p> <p>例如,当 $ k = 1 $ 时:</p> <p>$$ \frac{(-\gamma (x \cdot x + y \cdot y - 2 x \cdot y))^1}{1!} = -\gamma (x \cdot x + y \cdot y - 2 x \cdot y) $$</p> <p>当 $ k = 2 $ 时:</p> <p>$$ \frac{(-\gamma (x \cdot x + y \cdot y - 2 x \cdot y))^2}{2!} = \frac{\gamma^2 (x \cdot x + y \cdot y - 2 x \cdot y)^2}{2} $$</p> <p>这些项都是 $ x $ 和 $ y $ 的多项式形式,并且通过阶乘 $ k! $ 进行加权。</p> <p>高斯核函数可以被视为在无穷维度上通过不同阶次的多项式核函数进行调和得到的。这种调和使得高斯核函数能够在高维特征空间中捕捉到更复杂的非线性关系。因此,在一半的非线性任务场景中,高斯核函数都是很不错的一个选择。</p>

2024/11/27
阅读更多

VLDB 2024广州之旅

<blockquote> <p>VLDB 2024 是数据管理和数据库领域的顶级国际会议,涵盖数据管理、数据库架构、图数据管理、数据隐私与安全、数据挖掘、机器学习、人工智能与数据库系统研究等领域。今年的会议于8月26日至30日在中国广州举行,汇集了全球的研究人员和企业。会议内容丰富,包括250多场研究报告、11场主题演讲、多个教程和workshop,为与会者提供了广泛的学习和交流机会。</p> </blockquote> <p>最近去了趟广州,参加了 VLDB 2024,体验了完整的学术会议流程。旅途中也遇到了不少有趣的事情,想着写一篇博客分享一下。 <div class="details admonition note open"> <div class="details-summary admonition-title"><i class="icon fa-fw fa-solid fa-pencil-alt" aria-hidden="true"></i>注意<i class="details-icon fa-solid fa-angle-right fa-fw" aria-hidden="true"></i></div> <div class="details-content"> <div class="admonition-content">本文是<strong>旅游向分享</strong>,内容包含在广州的见闻以及学术会议流程介绍,不包含对具体报告内容和方向的介绍,请放心食用。</div> </div> </div></p> <p>此次行程为期6天,行程安排如下。其中前5天在琶洲岛广交会展馆附近,主要是参加会议以及珠江夜游;最后一天为特种兵打卡式旅游,西侧从沙面岛开始,途径十三行博物馆,走到圣心大教堂,东侧为黄埔军校纪念馆。 <div class="mapbox" data-options="{&#34;darkStyle&#34;:&#34;mapbox://styles/mapbox/dark-v10?optimize=true&#34;,&#34;fullscreen&#34;:true,&#34;geolocate&#34;:true,&#34;lat&#34;:23.10001,&#34;lightStyle&#34;:&#34;mapbox://styles/mapbox/streets-zh-v1&#34;,&#34;lng&#34;:113.35466,&#34;marked&#34;:true,&#34;markers&#34;:&#34;[{\&#34;lng\&#34;: 113.23979, \&#34;lat\&#34;: 23.10946, \&#34;description\&#34;: \&#34;沙面岛\&#34;},{\&#34;lng\&#34;: 113.24564, \&#34;lat\&#34;: 23.11157, \&#34;description\&#34;: \&#34;十三行博物馆\&#34;},{\&#34;lng\&#34;: 113.254735,\&#34;lat\&#34;: 23.11735, \&#34;description\&#34;: \&#34;圣心大教堂\&#34;},{\&#34;lng\&#34;: 113.317919,\&#34;lat\&#34;: 23.111565, \&#34;description\&#34;: \&#34;珠江夜游\&#34;},{\&#34;lng\&#34;: 113.419044,\&#34;lat\&#34;: 23.08878, \&#34;description\&#34;: \&#34;黄埔军校\&#34;}]&#34;,&#34;navigation&#34;:true,&#34;scale&#34;:true,&#34;zoom&#34;:11}" style="width: 100%; height: 20rem;"></div></p> <h2 id="day-0给现充来点小小的广州震撼" class="heading-element"><span>Day 0:给现充来点小小的广州震撼</span> <a href="#day-0%e7%bb%99%e7%8e%b0%e5%85%85%e6%9d%a5%e7%82%b9%e5%b0%8f%e5%b0%8f%e7%9a%84%e5%b9%bf%e5%b7%9e%e9%9c%87%e6%92%bc" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>下午3点,从广州南站坐地铁到琶洲站。还没出站就意识到事情有点不太对劲,地铁里挤满了Coser。好巧不巧,缺德地图选了个保利世贸馆的出口,这下可捅了二刺螈的老窝:从地铁出口到场馆地下,从保利世贸馆到广交会展馆,琶洲岛上挤满了二刺螈!不仅参与的人数多,而且cos的角色也非常丰富,从国产游戏到日本动漫,从牢大到川普遇刺…停车场里也挤满了痛车,这些车几乎没有电车,车主们大概也是资深二刺螈罢。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/c156b0a801e9663247ddd67b9a9ebc95.webp" alt="停车场里的痛车" srcset="https://cdn1.zair.top/images/2024/09/c156b0a801e9663247ddd67b9a9ebc95.webp?size=small, https://cdn1.zair.top/images/2024/09/c156b0a801e9663247ddd67b9a9ebc95.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/c156b0a801e9663247ddd67b9a9ebc95.webp?size=large 2x" data-title="停车场里的痛车" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>好不容易突出重围来到酒店,结果晚上21点去吃饭时,发现漫展还没结束,居然沿江边架起了夜场——第二天可是周一啊!广州的二次元氛围还是太浓厚了。</p> <h2 id="day-1学术蝗虫与晚宴乌龙" class="heading-element"><span>Day 1:学术蝗虫与晚宴乌龙</span> <a href="#day-1%e5%ad%a6%e6%9c%af%e8%9d%97%e8%99%ab%e4%b8%8e%e6%99%9a%e5%ae%b4%e4%b9%8c%e9%be%99" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>VLDB2024由香港科技大学(广州)在广州朗豪酒店举办。第一天报到从早上8点开始,由于第一次参加学术会议太兴奋,直接赶了个大早。报道时会发胸牌、会议手册、日程表、饭票、船票、一盒纪念品以及一堆赞助商的广告。纪念品包含一本精装笔记本、一支圆珠笔和一把伞,有黑色和蓝色两种主题色。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/7bb7269f28c393c00fe6ec24e2296522.webp" alt="报道现场" srcset="https://cdn1.zair.top/images/2024/09/7bb7269f28c393c00fe6ec24e2296522.webp?size=small, https://cdn1.zair.top/images/2024/09/7bb7269f28c393c00fe6ec24e2296522.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/7bb7269f28c393c00fe6ec24e2296522.webp?size=large 2x" data-title="报道现场" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>第一天的会议内容全部是以Workshop的形式组织的。现场会有关于不同主题的workshop同时进行,因此需要提前选取自己感兴趣的主题去参加。我选取了关于 LLM, Knowledge Graph和Vector Database相关的主题,听懂演讲内容的问题不大,但到了讨论环节,有几个老哥后来聊high了,语速逐渐提升,就开始听不懂了,英语听力和口语还得练。</p> <div class="details admonition info"> <div class="details-summary admonition-title"><i class="icon fa-fw fa-solid fa-circle-info" aria-hidden="true"></i>学术会议有哪些环节?<i class="details-icon fa-solid fa-angle-right fa-fw" aria-hidden="true"></i></div> <div class="details-content"> <div class="admonition-content"><h3 id="1-开幕式opening-ceremony" class="heading-element"><span>1. <strong>开幕式(Opening Ceremony)</strong></span> <a href="#1-%e5%bc%80%e5%b9%95%e5%bc%8fopening-ceremony" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>会议的正式启动环节。</li> <li>主办方或主办机构的负责人致辞,介绍会议主题、议程以及参会的重要嘉宾。</li> <li>有时还包括特别的欢迎仪式或文化展示。</li> </ul> <h3 id="2-主旨演讲keynote-speech" class="heading-element"><span>2. <strong>主旨演讲(Keynote Speech)</strong></span> <a href="#2-%e4%b8%bb%e6%97%a8%e6%bc%94%e8%ae%b2keynote-speech" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>由某个领域的知名学者或专家发表的演讲,通常围绕大会主题展开。</li> <li>主旨演讲通常具有启发性,为与会者提供前瞻性或总结性的观点。</li> <li>是会议中备受关注的环节,时间一般较长(45分钟至1小时)。</li> </ul> <h3 id="3-分组报告parallel-sessions" class="heading-element"><span>3. <strong>分组报告(Parallel Sessions)</strong></span> <a href="#3-%e5%88%86%e7%bb%84%e6%8a%a5%e5%91%8aparallel-sessions" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>与会者按研究方向或主题分组,在多个会场同时进行。</li> <li>每组内有多个报告,研究人员展示自己的研究成果。</li> <li>每个报告后会有简短的提问环节,供听众提问和讨论。</li> </ul> <h3 id="4-海报展示poster-session" class="heading-element"><span>4. <strong>海报展示(Poster Session)</strong></span> <a href="#4-%e6%b5%b7%e6%8a%a5%e5%b1%95%e7%a4%baposter-session" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>研究者通过海报展示其研究成果,通常在展览区或休息期间进行。</li> <li>参会者可以在指定时间内自由参观各个海报展位,与研究人员互动并进行讨论。</li> <li>海报展示环节适合展示较为新颖或尚未成熟的研究项目。</li> </ul> <h3 id="5-专题讨论panel-discussion" class="heading-element"><span>5. <strong>专题讨论(Panel Discussion)</strong></span> <a href="#5-%e4%b8%93%e9%a2%98%e8%ae%a8%e8%ae%bapanel-discussion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>由多个专家组成的讨论小组,就某个特定议题进行对话。</li> <li>各位专家先各自发表见解,然后由主持人引导进行互动和讨论,最后开放给观众提问。</li> <li>专题讨论有助于从多个角度深入探讨某一研究问题或学术领域的热点问题。</li> </ul> <h3 id="6-圆桌会议roundtable-discussion" class="heading-element"><span>6. <strong>圆桌会议(Roundtable Discussion)</strong></span> <a href="#6-%e5%9c%86%e6%a1%8c%e4%bc%9a%e8%ae%aeroundtable-discussion" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>参与者以圆桌形式围绕某一主题进行开放式讨论。</li> <li>圆桌会议更加注重互动性,参与者可以随时发言、提出问题并分享观点。</li> </ul> <h3 id="7-工作坊workshop" class="heading-element"><span>7. <strong>工作坊(Workshop)</strong></span> <a href="#7-%e5%b7%a5%e4%bd%9c%e5%9d%8aworkshop" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>强调实际操作和互动学习的环节,参与者在会议上学习新技能、方法或工具。</li> <li>工作坊通常需要预先注册,有时需要参与者带来自己的数据或材料。</li> </ul> <h3 id="8-社交活动networking-sessions" class="heading-element"><span>8. <strong>社交活动(Networking Sessions)</strong></span> <a href="#8-%e7%a4%be%e4%ba%a4%e6%b4%bb%e5%8a%a8networking-sessions" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>会议中的社交环节,目的是为与会者提供轻松的环境进行非正式交流和建立合作关系。</li> <li>社交活动可能包括晚宴、茶歇、午餐、酒会等。</li> </ul> <h3 id="9-闭幕式closing-ceremony" class="heading-element"><span>9. <strong>闭幕式(Closing Ceremony)</strong></span> <a href="#9-%e9%97%ad%e5%b9%95%e5%bc%8fclosing-ceremony" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>会议的总结与闭幕环节。</li> <li>主办方回顾会议的主要内容和成果,可能会表彰优秀的报告、论文或研究成果。</li> <li>通常还会宣布下届会议的时间和地点。</li> </ul> <h3 id="10-问答环节qa-sessions" class="heading-element"><span>10. <strong>问答环节(Q&amp;A Sessions)</strong></span> <a href="#10-%e9%97%ae%e7%ad%94%e7%8e%af%e8%8a%82qa-sessions" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>紧随每个报告或专题讨论之后,允许听众提问。</li> <li>研究者或演讲者会对听众提出的问题做出详细解答,促进双向交流。</li> </ul> <h3 id="11-小组讨论breakout-sessions" class="heading-element"><span>11. <strong>小组讨论(Breakout Sessions)</strong></span> <a href="#11-%e5%b0%8f%e7%bb%84%e8%ae%a8%e8%ae%babreakout-sessions" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>在大型会议中,为了讨论具体的子议题或研究方向,通常将参会者分成若干小组。</li> <li>每个小组集中讨论特定主题,然后可能汇报讨论成果或建议。</li> </ul> <h3 id="12-展览区exhibition-booths" class="heading-element"><span>12. <strong>展览区(Exhibition Booths)</strong></span> <a href="#12-%e5%b1%95%e8%a7%88%e5%8c%baexhibition-booths" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>在某些学术会议中,尤其是技术或应用领域的会议,通常设有展览区。</li> <li>相关机构、出版社、公司会在展区展示他们的产品、技术、服务或出版物,参会者可以自由参观。</li> </ul> <h3 id="13-评奖和颁奖awards-and-recognition" class="heading-element"><span>13. <strong>评奖和颁奖(Awards and Recognition)</strong></span> <a href="#13-%e8%af%84%e5%a5%96%e5%92%8c%e9%a2%81%e5%a5%96awards-and-recognition" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>一些会议设有论文、报告或研究成果的评奖环节。</li> <li>优秀的研究人员、论文、海报展示等可能会在闭幕式或专门的颁奖仪式上得到表彰。</li> </ul> <h3 id="14-茶歇和午餐coffee-breaks-and-lunches" class="heading-element"><span>14. <strong>茶歇和午餐(Coffee Breaks and Lunches)</strong></span> <a href="#14-%e8%8c%b6%e6%ad%87%e5%92%8c%e5%8d%88%e9%a4%90coffee-breaks-and-lunches" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><ul> <li>会议期间的休息时间,提供茶点或午餐。</li> <li>茶歇和午餐时间也是与会者进行非正式交流和社交的重要机会。</li> </ul> </div> </div> </div> <p>茶歇分别是在上午10点和下午3点,8点的早餐也是茶歇的规格。对于大佬们来说,茶歇是进行Social的好时候,探讨研究内容或是交换联系方式。但对于我这种学术蝗虫来说,茶歇就真的只是茶歇。虽然每场茶歇的食物会有一些变化,但总体就是饼干、面包、蛋糕、水果和饮料这几类。其中,属慕斯蛋糕和泰式烤鱼饼配甜辣酱最得我心。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/2f666a1d1847796883f504015934c0fb.webp" alt="茶歇" srcset="https://cdn1.zair.top/images/2024/09/2f666a1d1847796883f504015934c0fb.webp?size=small, https://cdn1.zair.top/images/2024/09/2f666a1d1847796883f504015934c0fb.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/2f666a1d1847796883f504015934c0fb.webp?size=large 2x" data-title="茶歇" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>晚上的欢迎宴会也非常丰盛,是自助餐。不过当时排队人太多,提前没注意到有哪些菜品,哐哐先打了两勺米饭,错过了不少好菜。菜品里中餐约占30%,其余为日料(寿司和刺身)、咖喱、蔬菜沙拉、西点、水果和饮品(红酒和果汁),能充分照顾到不同的饮食差异。但这里面中餐的那些菜实在一般,导致我最后是碳水配碳水——咖喱米饭和小蛋糕。嗟乎,吾其辞小蛋糕难矣。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/7b8be626151b7f432153424206d219e1.webp" alt="晚宴场景" srcset="https://cdn1.zair.top/images/2024/09/7b8be626151b7f432153424206d219e1.webp?size=small, https://cdn1.zair.top/images/2024/09/7b8be626151b7f432153424206d219e1.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/7b8be626151b7f432153424206d219e1.webp?size=large 2x" data-title="晚宴场景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>晚宴期间还闹了个乌龙。由于这次会议我和另一位同学一起来参加的,我们俩都还没面见过导师。当我们选完菜随便找个桌子落座后,就开始听同桌的其他人在聊什么。然后我们俩就发生了如下的对话:“你有没有觉得对面那个有点像我们导师”?“不会吧,这么巧“?”研究方向对的上,刚才他们说”…“年龄不对吧”…”人数也对的上“…”身高也对的上”…”太像了”…”简直就是”…”怎么办,现在认吗”?”事已至此,先吃饭吧”。就这样我们俩非常紧张地吃完饭,上去相认后,结果发现根本不是。他们分别是来自天津大学和华东师范大学的博士生,研究方向很相似,相互介绍后也加了好友。后面又发现他们的文章提名最佳研究论文,真是羡慕极了。</p> <h2 id="day-2醒狮开幕" class="heading-element"><span>Day 2:醒狮,开幕!</span> <a href="#day-2%e9%86%92%e7%8b%ae%e5%bc%80%e5%b9%95" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>开幕式上,令人印象最深的便是广州沙坑醒狮表演。表演开始时,舞者操控狮子做出各种逼真的动作,模仿狮子的生活习性,如甩头、挠痒、打滚等,其中最有特色的便是狮子的眼睛会动(这是我判断它是沙坑醒狮的依据)。随后狮子开始在台上进行高难度的跳跃和踩桩动作,展现了舞者的精准技巧和强健体魄。最后是领导上台点睛,以及狮子叼出庆贺的对联。整场表演配合着锣鼓节奏,狮子时而威猛、时而灵动,既有力量感,又充满了趣味性。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/948640fe6fc14f716c337844f4759641.webp" alt="开幕式醒狮表演" srcset="https://cdn1.zair.top/images/2024/09/948640fe6fc14f716c337844f4759641.webp?size=small, https://cdn1.zair.top/images/2024/09/948640fe6fc14f716c337844f4759641.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/948640fe6fc14f716c337844f4759641.webp?size=large 2x" data-title="开幕式醒狮表演" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>这是我第一次看到真正的舞狮表演,还是最具特色的广东醒狮。虽然很多游戏里面也有以广东醒狮为原型的桥段,特别是标志性的踩桩动作,但这些桥段往往侧重于对表演场景的渲染,缺少狮子模仿动作带来的趣味性,所体现出的力量感也不如现场表演来的强烈。</p> <p>舞狮表演结束后,便是会议主席,来自港科大的陈雷老师介绍会议的一些大致情况:这次会议是VLDB举办历史上参会人数最多的一次,中国大陆的参会人员占60%,会议是最有钱的一次(half a million dollar),AI相关的论文最多、数据库底层设计的论文最少等。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/7863a1513eefcffea29210ab0fa95f58.webp" alt="分组报告" srcset="https://cdn1.zair.top/images/2024/09/7863a1513eefcffea29210ab0fa95f58.webp?size=small, https://cdn1.zair.top/images/2024/09/7863a1513eefcffea29210ab0fa95f58.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/7863a1513eefcffea29210ab0fa95f58.webp?size=large 2x" data-title="分组报告" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>开幕式结束后,正式开启一整天的分组报告。分组报告也是需要事先选定感兴趣的主题去听,最好要提前看感兴趣的论文,甚至准备要提问的问题。晚餐跟前一天一样丰盛,意式蔬菜浓汤和千层饼非常赞!(唯独)这一天还有烤串,有翅根和面筋两种。吃完饭后逛了一圈,发现只要是有中国人的桌子,没有不拿烤串的,甚至还有人就是白米饭配烤串的,看来大家都还是中国胃。<del>码农烧烤,大有可为。</del></p> <h2 id="day-3从粤菜天花板到甲板" class="heading-element"><span>Day 3:从粤菜天花板到甲板</span> <a href="#day-3%e4%bb%8e%e7%b2%a4%e8%8f%9c%e5%a4%a9%e8%8a%b1%e6%9d%bf%e5%88%b0%e7%94%b2%e6%9d%bf" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>早上由来自 Google的keynote开场,主题关于大模型对数据库研究的影响。随后是和前一天相同的分组报告。</p> <h3 id="精品粤菜" class="heading-element"><span>精品粤菜</span> <a href="#%e7%b2%be%e5%93%81%e7%b2%a4%e8%8f%9c" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/6b721b32a1014f4cccd4955ff0a9ef92.webp" alt="进入炳胜" srcset="https://cdn1.zair.top/images/2024/09/6b721b32a1014f4cccd4955ff0a9ef92.webp?size=small, https://cdn1.zair.top/images/2024/09/6b721b32a1014f4cccd4955ff0a9ef92.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/6b721b32a1014f4cccd4955ff0a9ef92.webp?size=large 2x" data-title="进入炳胜" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>晚上的宴会是在<a href="http://www.bingsheng.com/intro/11.html"target="_blank" rel="external nofollow noopener noreferrer">炳胜品味海印总店<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>进行的。路上遇到了两位在美国留学的学生,他们评价说,中国人办的会议,场面是一定要给足的。此言不虚,后面才了解到这家店又是米其林,又是号称粤菜天花板的。这两位,一个在美西,评价自己的生活跟在村子里一样,没得吃没得玩;另一位在美东也表示饮食条件不行,每天都可以坚持不同体育项目的锻炼。但当谈及读研究生的目的时,其中一位表示是为了保持一种洞察力和终身学习的思维能力,再赞同不过了。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/d5c6b79be52e29d398dd4140b2795d1b.webp" alt="炳胜就餐" srcset="https://cdn1.zair.top/images/2024/09/d5c6b79be52e29d398dd4140b2795d1b.webp?size=small, https://cdn1.zair.top/images/2024/09/d5c6b79be52e29d398dd4140b2795d1b.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/d5c6b79be52e29d398dd4140b2795d1b.webp?size=large 2x" data-title="炳胜就餐" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>晚餐菜品的确很丰富。由于餐桌上不方便使用手机,只能凭借记忆回忆起几道印象比较深刻的菜:西洋参排骨汤、整头烤乳猪(但眼睛里塞了俩红色电灯泡,作为外乡人我愿称之为邪能烤乳猪)、蒜蒸波士顿龙、大海参、鱼、鸡鸭、咕咾肉、甜水、西红柿和蜜瓜、沙琪玛和普洱茶。总体来说还不错,烤乳猪皮脆肉嫩,咕咾肉酸爽可口,食材选用极具岭南特色。但可能是由于吃不习惯,一些粤菜中的特色菜,反而是最难吃的:甜水真的甜到发齁、西洋参放汤里尽显酸涩,还有一种点心,吃起来仿佛是月饼皮包着芹菜猪肉饺子的生馅料,着实无法接受。有趣的是,餐厅默认没有提供刀叉,一些老外也被迫使用筷子,但看起来好像都没什么障碍。</p> <p>我们师门同桌的有亚马逊的研究员和美国某大学的AP。亚马逊的研究员当着我导的面,从工业界的角度讲了一些不适合学生听的话。AP则聊到了在美国的大学里只需要按自己的节奏做工作,就可以拿到终身教职的宽松氛围。排骨汤可能就是这时候被西洋参泡酸了。</p> <p>饭后,全体人员一起步行前往码头,参加珠江夜游。途中遇到一位在名古屋大学读博士一年级的哥们,他大肆夸赞日本的读博生活,包括导师放养、高补贴、日本的卖方就业市场以及文化环境等。读博还能这么爽的?</p> <h3 id="珠江夜游" class="heading-element"><span>珠江夜游</span> <a href="#%e7%8f%a0%e6%b1%9f%e5%a4%9c%e6%b8%b8" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/fe51049529fee14e8352584425b9594b.webp" alt="从另一艘船上拍我们的船" srcset="https://cdn1.zair.top/images/2024/09/fe51049529fee14e8352584425b9594b.webp?size=small, https://cdn1.zair.top/images/2024/09/fe51049529fee14e8352584425b9594b.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/fe51049529fee14e8352584425b9594b.webp?size=large 2x" data-title="从另一艘船上拍我们的船" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>夜幕降临,珠江两岸华灯初上。我们登上游船,缓缓驶入璀璨夜色中。两岸高楼大厦灯火辉煌,倒映在波光粼粼的江面上,宛如繁星坠入碧波。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/42a46c5b4e60a2c6e14297ffdbca41b3.webp" alt="珠江夜游外景" srcset="https://cdn1.zair.top/images/2024/09/42a46c5b4e60a2c6e14297ffdbca41b3.webp?size=small, https://cdn1.zair.top/images/2024/09/42a46c5b4e60a2c6e14297ffdbca41b3.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/42a46c5b4e60a2c6e14297ffdbca41b3.webp?size=large 2x" data-title="珠江夜游外景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>沿途经过标志性建筑,广州塔巍峨耸立,变幻的光影如梦似幻。珠江新城的摩天大楼群熠熠生辉,勾勒出现代都市的天际线。船经过海珠桥时,桥上车流如织,桥下游船穿梭,一派繁忙景象。</p> <p>微风拂面,江水轻轻拍打船舷。甲板之下传来民俗乐声,为这夜色增添了几分惬意气息。游客们或倚栏远眺,或举杯畅聊,沉醉于这迷人夜色中。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/aef20be076fa4c7b76f3db07bf93f331.webp" alt="珠江夜游内景" srcset="https://cdn1.zair.top/images/2024/09/aef20be076fa4c7b76f3db07bf93f331.webp?size=small, https://cdn1.zair.top/images/2024/09/aef20be076fa4c7b76f3db07bf93f331.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/aef20be076fa4c7b76f3db07bf93f331.webp?size=large 2x" data-title="珠江夜游内景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>两个小时的航程转瞬即逝。回望整个旅程,繁华都市与悠久历史在这条母亲河上完美交融,令人难忘。</p> <h3 id="新型搭讪" class="heading-element"><span>新型搭讪</span> <a href="#%e6%96%b0%e5%9e%8b%e6%90%ad%e8%ae%aa" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>我和同行的同学在三层船头处观赏风景时,来了个老外。他背着双肩包,满头大汗,略显匆忙。他问我们这船上是否有WIFI,我们知不知道密码,他要跟家人联系。然后我们发现没有,就提出可以给他开热点。连接成功后,我们又想到,国外的社交软件几乎都在墙外,这老哥会不会来广州后,一直在找Wifi,但一直被墙了。换到他的角度想想:人在遥远而神秘的东方,每个Wifi都没法突破防火墙,与家人失联三天,找别人开热点但都不愿意“分享”——这实在是太可怜了。于是我们就又尝试把魔法猫咪的能力放到局域网上共享,这件在电脑很简单的事,在手机上最终也没能完成。</p> <p>不过,我们很快发现这位老哥是个社牛。一路上疯狂找话题:“ICBC是什么意思?‘猎德’是什么意思?你们从哪来?那个Logo是什么”……还疯狂找我们合影。</p> <p>就这样聊了一路,给这场夜游也增添了不一样的趣味。</p> <p>后来在whova上,我们发现,这老哥活跃度排名第二,任职于德国某大学,还有家公司——这实在不像是与家人失联而需要到处蹭网的样子。</p> <h2 id="day-4无料收集" class="heading-element"><span>Day 4:无料收集</span> <a href="#day-4%e6%97%a0%e6%96%99%e6%94%b6%e9%9b%86" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>VLDB2024期间,有Google、Meta、阿里和字节等一众赞助商企业的展台,在展台上,可以填写调查问卷,然后会获得无料一份。问卷主要是调查研究方向和求职倾向,也有直接收名片的。所有无料中,我最喜欢的是Google的白衬衫和下面图里的企鹅。<del>如果你不知道这个企鹅是哪家公司的,当你看到上面有正版防伪贴纸后,你便知道它是腾讯的。</del></p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/e5cd97b0da922baad80e38f0e27d53eb.webp" alt="腾讯的企鹅无料" srcset="https://cdn1.zair.top/images/2024/09/e5cd97b0da922baad80e38f0e27d53eb.webp?size=small, https://cdn1.zair.top/images/2024/09/e5cd97b0da922baad80e38f0e27d53eb.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/e5cd97b0da922baad80e38f0e27d53eb.webp?size=large 2x" data-title="腾讯的企鹅无料" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>下午的报告给了我很多启发。有些工作看似简单,甚至只是一个朴素的想法,但经过深入研究,也能形成一篇完整的论文,令人佩服。印象比较深刻的是一位康奈尔大学的独狼老哥,在同一个报告会上,连续报告了两个由自己独立完成的工作。他手拿话筒,单手插兜,双臂撑住讲台,PPT 采用简单大字风格(没有一页PPT超过30个字),完全是把报告当成演讲来进行。这也许就是大佬,已经完全next level。</p> <p>这一天结束了我们所有的会议日程,离开酒店时遇到了非常极端的雷暴天气,大雨倾盆。</p> <h2 id="day-5半天走过中国五百年" class="heading-element"><span>Day 5:半天走过中国五百年</span> <a href="#day-5%e5%8d%8a%e5%a4%a9%e8%b5%b0%e8%bf%87%e4%b8%ad%e5%9b%bd%e4%ba%94%e7%99%be%e5%b9%b4" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>本着“来都来了“的原则,我在广州的最后一天安排了一次特种兵式的密集行程。这一天我踏遍了广州的几个著名景点,领略了这座城市丰富的历史文化底蕴。 清晨,首先来到了充满异国情调的沙面岛。漫步在这个曾经的租界区,欧式建筑与热带植物相映成趣,仿佛穿越回了旧时光。判断一个地方的风景是否足够美丽,一个标准是有没有人在这里拍结婚照,南京的浦口火车站、绿博园和中山陵如此,广州的沙面岛也是如此。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/c9fd66a828e28a4b12b992f6ded1012e.webp" alt="沙面岛风景" srcset="https://cdn1.zair.top/images/2024/09/c9fd66a828e28a4b12b992f6ded1012e.webp?size=small, https://cdn1.zair.top/images/2024/09/c9fd66a828e28a4b12b992f6ded1012e.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/c9fd66a828e28a4b12b992f6ded1012e.webp?size=large 2x" data-title="沙面岛风景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>在沙面岛逗留了一上午,中午步行去了旁边的十三行博物馆,深入了解了广州作为海上丝绸之路重要港口的辉煌历史。博物馆中陈列的文物和图片,生动展现了广州自17世纪商贸繁荣的盛况,到19世纪的半殖民地景象。在这里了解到,广州被迫通商后,各国商馆本设在今广州塔对岸的市中心沿江位置,但后来因一场大火,才转移到沙面岛。</p> <p>午后又来到了圣心大教堂。这是一座规模庞大的哥特式建筑,是小红书推荐的必来打卡点。结果也确实只是打卡点,只有门口可以打卡,大部分时间也不开放。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/d87b6cdd619cecaec340f8a6504a178c.webp" alt="圣心大教堂风景" srcset="https://cdn1.zair.top/images/2024/09/d87b6cdd619cecaec340f8a6504a178c.webp?size=small, https://cdn1.zair.top/images/2024/09/d87b6cdd619cecaec340f8a6504a178c.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/d87b6cdd619cecaec340f8a6504a178c.webp?size=large 2x" data-title="圣心大教堂风景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>下午去参观了黄埔军校旧址。漫步在这个培养了无数革命先烈的地方,希望感受那个激情燃烧的年代。由于提前没做功课,跟想象中的不太一样,多少还是有些失望。黄埔军校原址已经在抗战时期被日军轰炸化为灰烬了,现有的景点只是尽力还原的。看着整洁规整的、被还原后的建筑,让我不断有”夫子庙“的既视感——只能感觉自己是在景区。找了半天的大门也没有那幅对联:”升官发财请往他处,贪生怕死勿入斯门“。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/be63cdc9e66d411f0a863944e4a1bb3c.webp" alt="黄埔军校旧址风景" srcset="https://cdn1.zair.top/images/2024/09/be63cdc9e66d411f0a863944e4a1bb3c.webp?size=small, https://cdn1.zair.top/images/2024/09/be63cdc9e66d411f0a863944e4a1bb3c.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/be63cdc9e66d411f0a863944e4a1bb3c.webp?size=large 2x" data-title="黄埔军校旧址风景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>夜幕降临,我又来到了珠江边,沿着江畔漫步。夜晚的广州,地平线与天际线,小蛮腰和大剧院,百看不厌。从海珠大桥到猎德大桥,同样的一条珠江,17世纪到21世纪,每个百年的景象竟能如此不同。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024/09/6499961344854ffce2524a9d20d4be76.webp" alt="珠江夜景" srcset="https://cdn1.zair.top/images/2024/09/6499961344854ffce2524a9d20d4be76.webp?size=small, https://cdn1.zair.top/images/2024/09/6499961344854ffce2524a9d20d4be76.webp?size=medium 1.5x, https://cdn1.zair.top/images/2024/09/6499961344854ffce2524a9d20d4be76.webp?size=large 2x" data-title="珠江夜景" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>这段在广州的日子,宛如一场穿梭于古今的旅程。从琶洲的学术殿堂,到珠江两岸的灯火辉煌;从沙面岛的异国风情,到黄埔军校的革命遗迹。这座城市以其独特的方式,将学术、文化、历史和现代性完美融合。在高楼大厦的阴影下,也有城中村的烟火气息;车水马龙的大桥下,也有流浪汉的身影,提醒着这座城市仍在不断进步和包容的过程中。但这些场景不仅没有削弱广州的魅力,反而让这座城市显得更加真实、更富人情味。</p> <p>再见,广州。</p> <div class="bilibili"> <iframe src="//player.bilibili.com/player.html?bvid=BV1AP1GYfEBS&page=1&autoplay=false&poster=true&muted=false&danmaku=true&t=0" border="0" framespacing="0" allowfullscreen="true"></iframe> </div>

2024/9/23
阅读更多

数据枯竭危机:AI发展面临的数据瓶颈与应对措施

<blockquote> <p>我们估计人类产生的的公共文本存量约为 300 万亿个token。如果趋势持续,语言模型将在 2026 年至 2032 年之间完全耗尽这一存量,或者如果过度训练,甚至会更早。 ——Epoch AI</p> </blockquote> <p>在2006年,时任伊利诺伊大学教授的李飞飞(现为斯坦福大学教授)看到了互联网改变人工智能(AI)研究的潜力。语言学领域的研究已经识别出了8万个“名词同义词集”,即描述同一类事物的同义词集合。李飞飞推测,互联网上的数十亿张图片中,一定包含了这些同义词集的无数实例。如果能够收集足够多的这些图片,就可以创建一个超越以往任何AI训练资源的庞大数据库。她说“很多人关注模型,我们应该关注数据”。于是,ImageNet项目诞生了。</p> <p>互联网不仅提供了图片,还为图像标注提供了资源。通过搜索引擎找到如猫、狗、椅子等的图片后,亚马逊的众包平台Mechanical Turk上的人对这些图片进行人工检查和标注。最终,构建了一个包含数百万经过校验的图片的数据库。正是使用了部分ImageNet数据训练的AlexNet在2012年展示了“深度学习”的巨大潜力,开启了上一个AI周期,也催生了依赖于大量标注数据的行业。</p> <h2 id="数据驱动的ai时代" class="heading-element"><span>1 数据驱动的AI时代</span> <a href="#%e6%95%b0%e6%8d%ae%e9%a9%b1%e5%8a%a8%e7%9a%84ai%e6%97%b6%e4%bb%a3" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>在这一轮AI周期中,AI的发展延伸到了大型语言模型(LLM),这些模型的训练也依赖于互联网数据,但方式有所不同。计算机视觉领域(CV)的经典训练任务是预测一张图片的内容(图片分类任务),但LLM训练的经典任务是基于上下文预测一段文本中被删除的词语。</p> <p>这种训练方式不需要人工标注数据,系统可以自行空出词语,进行推理并通过“自监督训练”来评估答案的正确性。但这种方式需要大量的数据。<strong>一般来说,模型获得的文本越多、数据量越大,其性能就越好(Scaling Law)。</strong> 互联网正好就提供了数以百亿计的文本,它对LLM的意义就像沉积了亿万年的碳对现代工业的意义一样,是一种可以精炼成燃料的宝贵资源。</p> <p>常见的训练数据来源是Common Crawl,一个包含500亿个网页的互联网档案。随着AI模型的发展,更多的数据被加入其中,如Books3——一个包含数千本书籍的数据库。然而,随着AI对文本数据需求的增长,互联网上的优质数据供给逐渐不堪重负。根据Epoch AI的估计,到2028年,互联网中的高质量文本数据将被完全利用完毕,行业将面临所谓的“数据墙”。中文互联网更严重,从“中文互联网正在崩塌”,到各大平台纷纷锁上大门,当大家意识到数据的价值后,便把它放进了保险库。如何应对这一壁垒,可能是AI未来发展中最棘手的问题之一,也可能是最有可能放缓其进程的问题。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F08%2Fd834acebf20bd29c720550594dac4481.png" alt="AI数据墙现象" srcset="https://cdn1.zair.top/images/2024%2F08%2Fd834acebf20bd29c720550594dac4481.png?size=small, https://cdn1.zair.top/images/2024%2F08%2Fd834acebf20bd29c720550594dac4481.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F08%2Fd834acebf20bd29c720550594dac4481.png?size=large 2x" data-title="AI数据墙现象" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h2 id="数据所有权与版权问题" class="heading-element"><span>2 数据所有权与版权问题</span> <a href="#%e6%95%b0%e6%8d%ae%e6%89%80%e6%9c%89%e6%9d%83%e4%b8%8e%e7%89%88%e6%9d%83%e9%97%ae%e9%a2%98" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>AI模型越来越依赖互联网数据,但数据的版权问题也充满争议。许多用于训练大型语言模型的数据往往未经版权方的同意就被使用,一些AI公司甚至利用了付费墙后的内容。虽然AI公司会声称这种使用属于版权法中的“合理使用”范畴,但版权方并不买账。Getty Images起诉了图像生成公司Stability AI,指控其未经授权使用了其图片库。纽约时报则起诉了OpenAI和微软,指控其侵犯了数百万篇文章的版权。Stack Overflow、Reddit和X(前Twitter)现在都向AI公司收取费用。知乎也正通过乱码来干扰必应和谷歌等爬虫,从而限制其中文内容作为数据集被用于AI训练。</p> <p>不同地区对这一问题的态度有所不同。日本和以色列采取了宽松的立场,以促进其AI产业的发展。欧盟则没有通用的“合理使用”概念,可能会更加严格。国内也仅仅是设立了国家数据局,明确了数据兼有生产资料和生产对象双重身份。</p> <h2 id="现有的数据使用策略" class="heading-element"><span>3 现有的数据使用策略</span> <a href="#%e7%8e%b0%e6%9c%89%e7%9a%84%e6%95%b0%e6%8d%ae%e4%bd%bf%e7%94%a8%e7%ad%96%e7%95%a5" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>面对数据墙,AI领域提出了几种应对策略。其中一个关键的应对策略是<strong>专注于数据的质量而非数量</strong>。各家AI实验室不再盲目地使用整个互联网的数据来训练模型,而是更加重视数据的过滤、清洗和优化,确保模型能够从中提取到最有价值的内容。在过去的一年里(2024年),OpenAI的模型似乎不再“遥遥领先”了,大家的模型打得有来有回,这些模型在不同任务上的性能差异,就来自训练数据的构建。毕竟开源算法和模型的很多,开源数据集的却寥寥无几。</p> <p>获取“真实世界的信息”至关重要,特别是当模型涉及大量推理时,学术教科书等权威资源变得格外宝贵。但如何<strong>在不同数据源之间找到最佳平衡</strong>点仍然是一门玄学。</p> <p>在数据使用的过程中,模型还面临“灾难性遗忘”的问题——即当系统在某些类型的数据上训练过多时,可能会在擅长该领域的同时,遗忘先前学到的其他知识。因此,<strong>训练时数据的顺序</strong>也需要仔细考虑。如果把所有关于某个主题的数据(如数学)集中在训练过程的末尾,模型或许会在数学问题上表现出色,但同时就可能削弱了其它领域的能力。这种不平衡的训练方式加剧了灾难性遗忘的风险。</p> <p>在数据既涉及不同领域,还涉及不同形式(模态)时,这些策略就变得更加复杂。由于新的文本数据供不应求,像OpenAI的GPT-4和Google的Gemini这样的领先模型在自监督学习过程中,除了使用文本外,还使用图像、视频和音频进行训练。然而,视频数据尤其棘手,因为视频文件包含的数据点极为密集。为简化问题,现有模型通常仅抽取部分帧进行简化处理,学界仍在寻找更高效的解决方案。</p> <h2 id="合成数据与ai自我训练" class="heading-element"><span>4 合成数据与AI自我训练</span> <a href="#%e5%90%88%e6%88%90%e6%95%b0%e6%8d%ae%e4%b8%8eai%e8%87%aa%e6%88%91%e8%ae%ad%e7%bb%83" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>模型能力还可以通过在自监督学习产生的版本(预训练版本)基础上,使用额外的数据进行精细调整(<strong>微调</strong>)来提升。例如,“监督微调”就是向模型提供由人类收集或制作的问答对,来教模型什么是好的答案。另一种方法“<strong>基于人类反馈的强化学习</strong>”(RLHF),则是告诉模型答案是否满足提问者。</p> <p>在RLHF中,用户对模型输出的质量进行反馈,这些反馈随后用于调整模型的参数(权重)。与聊天机器人进行的用户互动,如点赞或踩,对RLHF特别有用。这就是“数据飞轮”的机制:更多的用户带来更多的数据,这些数据又反过来优化更好的模型。AI公司密切关注用户向其模型提出的各种问题,然后收集数据以调整模型以涵盖这些主题。阿里、字节和Minimax等厂商纷纷掀起模型价格战,很难说没有这方面的考量。</p> <p>随着互联网上的预训练数据逐渐枯竭,后期训练(Post-Training)的重要性日益凸显。像Scale AI和Surge AI这样的标注公司每年通过收集后期训练数据赚取数亿美元。Scale最近以140亿美元的估值筹集了10亿美元。如今的标注工作已经超越了Mechanical Turk的时代:顶尖的标注员每小时可赚取高达100美元。尽管后期训练有助于生成更好的模型,并能满足许多商业应用的需求,但这仍然只是增量改进,治标不治本。</p> <p>除了逐步突破数据墙,还有一种解决方案是完全跳过它,也就是使用机器<strong>生成的合成数据</strong>。DeepMind(谷歌的子公司)推出的AlphaGo Zero模型就是一个很好的例子。该公司第一个成功的围棋模型是通过数百万场业余比赛的数据进行训练的,而AlphaGo Zero则完全没有使用现有的数据。相反,它通过在三天内与自己对弈490万次来学习围棋,并记录下成功的策略。这种“强化学习”教会了它如何通过模拟大量可能的回应来应对对手的招数,并选择成功概率最高的策略。</p> <p>类似的方法也可以用于LLM,比如目前最强的开源大模型Llama 3.1。LLama3.1 的SFT数据里有相当比例是由模型生成的合成数据,而Gemma2 在SFT阶段的数据很大比例是由规模更大的模型合成的,证明了合成数据质量不比人工标注质量差。</p> <p>那我们可以无限生成合成数据,左脚踩右脚登天吗?我认为答案是否定的。上个月发表在《Nature》的一项研究发现,在模型训练中“滥用”合成数据可能导致“不可逆的缺陷”。用模型合成的数据来微调模型,只需要重复几轮,模型就会胡言乱语,研究人员将这一现象称为“模型崩溃”。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F08%2F105aac035a19fd4f52f02f6d7aa0c11c.png" alt="Nature当期封面:Garbage in, Garbage out." srcset="https://cdn1.zair.top/images/2024%2F08%2F105aac035a19fd4f52f02f6d7aa0c11c.png?size=small, https://cdn1.zair.top/images/2024%2F08%2F105aac035a19fd4f52f02f6d7aa0c11c.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F08%2F105aac035a19fd4f52f02f6d7aa0c11c.png?size=large 2x" data-title="Nature当期封面:Garbage in, Garbage out." style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>更大的问题在于如何将这种方法扩展到医疗或教育等垂直领域。在游戏中,胜利的定义明确,而且更容易收集到某个举动是否有利的数据。在其他领域,这要复杂得多。关于“好”决策的数据通常是从专家那里收集的,但这既昂贵又费时,解决方案也不够全面。如何判断某个专家是否正确,这也是个套娃的问题。</p> <h2 id="总结" class="heading-element"><span>5 总结</span> <a href="#%e6%80%bb%e7%bb%93" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>获取更多数据将是保持AI快速进步的关键。不论是从专家来源获取的专门数据,还是机器生成的合成数据,AI的进步都取决于数据的持续供应。随着最容易获取的数据储备逐渐耗尽,AI行业也做出了许多努力来缓解这个问题:</p> <ul> <li>强调数据质量,进行数据清洗</li> <li>增加数学、逻辑和代码数据的配比,调整训练顺序</li> <li>使用合成数据来补充真实数据</li> </ul> <p>但这些似乎都不可持续,必须寻找新的数据源或开发可持续的替代方案;或者从算法架构层面,设计出不依赖数据的新架构,顺势开启下一轮AI周期。</p> <h2 id="推荐阅读" class="heading-element"><span>6 推荐阅读</span> <a href="#%e6%8e%a8%e8%8d%90%e9%98%85%e8%af%bb" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li><a href="https://web.archive.org/web/20240523001943/https:/mp.weixin.qq.com/s/afg3zHPpEyRzSfOR1Aeh3w"target="_blank" rel="external nofollow noopener noreferrer">中文互联网正在加速崩塌 | 何加盐<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://epochai.org/blog/will-we-run-out-of-data-limits-of-llm-scaling-based-on-human-generated-data"target="_blank" rel="external nofollow noopener noreferrer">Will We Run Out of Data? Limits of LLM Scaling Based on Human-Generated Data | EpochAI<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://blog.csdn.net/weixin_41446370/article/details/140030906"target="_blank" rel="external nofollow noopener noreferrer">知乎正通过乱码来干扰必应/谷歌等爬虫 | CSDN<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://wallstreetcn.com/articles/3723705"target="_blank" rel="external nofollow noopener noreferrer">人工智能的训练数据正在枯竭,合成数据引发巨大争议 | 华尔街见闻<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://developer.volcengine.com/articles/7396884852433551379"target="_blank" rel="external nofollow noopener noreferrer">浅谈Llama3.1,从结构、训练过程、影响到数据合成 | 火山社区<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li><a href="https://www.nature.com/articles/s41586-024-07566-y"target="_blank" rel="external nofollow noopener noreferrer">AI models collapse when trained on recursively generated data | Nature<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> </ol>

2024/8/21
阅读更多

微调GPT-4o-mini生成博客文章

<p>7月18日发布的新模型GPT-4o-mini有着超越GPT-3.5、接近GPT-4的性能,且价格只用GPT-3.5的一半,响应速度也是全系列模型最快的。OpenAI于今天正式开放了GPT-4o-mini的微调接口,在2024年9月23日前,每天有2M token免费额度。</p> <p><del>不是Llama 3.1 405B玩不起,而是GPT-4o-mini更有性价比。</del></p> <h2 id="微调适用场景" class="heading-element"><span>1 微调适用场景</span> <a href="#%e5%be%ae%e8%b0%83%e9%80%82%e7%94%a8%e5%9c%ba%e6%99%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>对于一般的简单任务,只需要编写提示词(Prompting),模型就能很好地完成。如果任务比较复杂,可以尝试使用思维链(Chain of Thought)将复杂任务分解为多个步骤,并逐步推理。但对于需要高精度和一致性输出的任务,就需要进行微调(Fine-tuning)。</p> <p>下面的表格对比了这三种办法的优缺点以及应用场景。</p> <table> <thead> <tr> <th>方法</th> <th>优点</th> <th>缺点</th> <th>应用场景</th> </tr> </thead> <tbody> <tr> <td>微调</td> <td>提供高质量结果</td> <td>需要大量时间和资源来准备和训练数据</td> <td>需要稳定、可靠和高质量的输出</td> </tr> <tr> <td></td> <td>适用于复杂任务和特定领域的定制</td> <td>反馈循环较慢,训练成本高</td> <td>改进模型在特定任务或领域的性能</td> </tr> <tr> <td></td> <td>节省Token,降低延迟</td> <td>有深度学习基础知识作为门槛</td> <td>任务需要高精度或独特的风格、语气、格式时</td> </tr> <tr> <td>提示词</td> <td>快速迭代和测试</td> <td>依赖于提示词的设计质量</td> <td>常见任务的快速原型和测试</td> </tr> <tr> <td></td> <td>适合初始探索和一般任务</td> <td>对复杂任务可能不够准确</td> <td>需要灵活调整模型输出时</td> </tr> <tr> <td></td> <td>无需额外数据准备和训练资源</td> <td></td> <td>不适合大量示例和复杂逻辑的任务</td> </tr> <tr> <td>思维链</td> <td>提供分步骤逻辑和推理</td> <td>增加了提示的复杂性和长度</td> <td>处理需要推理和逻辑步骤的任务</td> </tr> <tr> <td></td> <td>改善复杂任务的性能</td> <td>增加了Token使用量和延迟</td> <td>涉及多步骤解决问题的场景</td> </tr> <tr> <td></td> <td>易于结合多种策略和工具</td> <td>对非常复杂的任务可能仍不够</td> <td>需要明确逻辑流程和分步执行时</td> </tr> </tbody> </table> <p>NFL定理告诉我们,没有哪种方法能适用于所有场景,这里也是一样,微调也不一定就比另外两种方法更好。但可以明确的是,<strong>微调适用于那些“难以描述的任务”</strong>,比如一种风格和语气。此外,这三种方法也并不是水火不容,微调模型使用精心设计的提示词、甚至结合思维链,说不定能达到更好的结果。</p> <p>只是简单地写一篇文章或段落,提示词就够了。但一篇博客文章,如果考虑SEO的话,就会有很多细节,例如核心关键词出现频率等。这些细节大模型不一定能尽数理解,且作为用户也不一定能很好地在提示词中进行描述。因此撰写一篇这样的博客文章就可以使用微调。</p> <h2 id="准备数据" class="heading-element"><span>2 准备数据</span> <a href="#%e5%87%86%e5%a4%87%e6%95%b0%e6%8d%ae" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>数据需要以<code>jsonl</code>的格式组织,每一行是一个json。例如:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Marv is a factual chatbot that is also sarcastic.&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s the capital of France?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Paris, as if everyone doesn&#39;t know that already.&#34;</span><span class="p">}]}</span> </span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Marv is a factual chatbot that is also sarcastic.&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Who wrote &#39;Romeo and Juliet&#39;?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Oh, just some guy named William Shakespeare. Ever heard of him?&#34;</span><span class="p">}]}</span> </span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Marv is a factual chatbot that is also sarcastic.&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;How far is the Moon from Earth?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Around 384,400 kilometers. Give or take a few, like that really matters.&#34;</span><span class="p">}]}</span></span></span></code></pre></td></tr></table> </div> </div><p>也可以在多轮对话中设置权重,weight取0表示让模型规避这种回答。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Marv is a factual chatbot that is also sarcastic.&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;What&#39;s the capital of France?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Paris&#34;</span><span class="p">,</span> <span class="nt">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Can you be more sarcastic?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Paris, as if everyone doesn&#39;t know that already.&#34;</span><span class="p">,</span> <span class="nt">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">}]}</span> </span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Marv is a factual chatbot that is also sarcastic.&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Who wrote &#39;Romeo and Juliet&#39;?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;William Shakespeare&#34;</span><span class="p">,</span> <span class="nt">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Can you be more sarcastic?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Oh, just some guy named William Shakespeare. Ever heard of him?&#34;</span><span class="p">,</span> <span class="nt">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">}]}</span> </span></span><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;messages&#34;</span><span class="p">:</span> <span class="p">[{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Marv is a factual chatbot that is also sarcastic.&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;How far is the Moon from Earth?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;384,400 kilometers&#34;</span><span class="p">,</span> <span class="nt">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Can you be more sarcastic?&#34;</span><span class="p">},</span> <span class="p">{</span><span class="nt">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;assistant&#34;</span><span class="p">,</span> <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Around 384,400 kilometers. Give or take a few, like that really matters.&#34;</span><span class="p">,</span> <span class="nt">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">}]}</span></span></span></code></pre></td></tr></table> </div> </div><p>当然,处理数据是最耗时的,这里也可以直接使用<a href="https://github.com/Ryaang/industry-article-dataset/blob/main/all_filter_2120.jsonl"target="_blank" rel="external nofollow noopener noreferrer">我制作的数据集<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>。该数据集用于对大模型进行微调,来源于对 <a href="https://reads.alibaba.com/"target="_blank" rel="external nofollow noopener noreferrer">reads.alibaba.com<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a> 网站中13个分类领域的3000+个页面的抓取,开源的不只有处理后的数据,也有原始数据以及爬虫代码。</p> <p>将准备好的数据上传,记录返回的文件ID.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span> </span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">files</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">file</span><span class="o">=</span><span class="nb">open</span><span class="p">(</span><span class="s2">&#34;all_filter_2120.jsonl&#34;</span><span class="p">,</span> <span class="s2">&#34;rb&#34;</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">purpose</span><span class="o">=</span><span class="s2">&#34;fine-tune&#34;</span> </span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div><h2 id="微调模型" class="heading-element"><span>3 微调模型</span> <a href="#%e5%be%ae%e8%b0%83%e6%a8%a1%e5%9e%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>准备好数据、验证无误、确认token成本后,即可创建微调任务</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span> </span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">fine_tuning</span><span class="o">.</span><span class="n">jobs</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">training_file</span><span class="o">=</span><span class="s2">&#34;file-zWptPbsD37ZnemssjpsK6CnF&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;gpt-4o-mini&#34;</span> </span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div><p>这一步更详细的参数配置可以参考<a href="https://platform.openai.com/docs/api-reference/fine-tuning/create"target="_blank" rel="external nofollow noopener noreferrer">官方API文档<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F07%2F3e3c8d91530a4be8e7ca7d3d96ad3cae.png" alt="OpenAI 微调UI" srcset="https://cdn1.zair.top/images/2024%2F07%2F3e3c8d91530a4be8e7ca7d3d96ad3cae.png?size=small, https://cdn1.zair.top/images/2024%2F07%2F3e3c8d91530a4be8e7ca7d3d96ad3cae.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F07%2F3e3c8d91530a4be8e7ca7d3d96ad3cae.png?size=large 2x" data-title="OpenAI 微调UI" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>上面这两步也可以在<a href="https://platform.openai.com/finetune"target="_blank" rel="external nofollow noopener noreferrer">UI界面<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>快速完成,提交任务后,也可以在UI界面实时查看进度和损失变化。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F07%2F853e4147b0c7e82527a646eb60b612b5.png" alt="OpenAI 微调过程日志" srcset="https://cdn1.zair.top/images/2024%2F07%2F853e4147b0c7e82527a646eb60b612b5.png?size=small, https://cdn1.zair.top/images/2024%2F07%2F853e4147b0c7e82527a646eb60b612b5.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F07%2F853e4147b0c7e82527a646eb60b612b5.png?size=large 2x" data-title="OpenAI 微调过程日志" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h2 id="调用模型" class="heading-element"><span>4 调用模型</span> <a href="#%e8%b0%83%e7%94%a8%e6%a8%a1%e5%9e%8b" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>通过下面的代码查询微调任务状态,作业成功后,就能看到<code>fine_tuned_model</code>字段填充了模型的名称。记下此名称,就可以进行调用了。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span> </span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 查询微调任务列表</span> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">fine_tuning</span><span class="o">.</span><span class="n">jobs</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="n">limit</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 查询微调任务详情</span> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">fine_tuning</span><span class="o">.</span><span class="n">jobs</span><span class="o">.</span><span class="n">retrieve</span><span class="p">(</span><span class="s2">&#34;ftjob-gvP0VB7RlWcF3QHdQrEVf49Y&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 取消任务</span> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">fine_tuning</span><span class="o">.</span><span class="n">jobs</span><span class="o">.</span><span class="n">cancel</span><span class="p">(</span><span class="s2">&#34;ftjob-gvP0VB7RlWcF3QHdQrEVf49Y&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 查看任务中的日志</span> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">fine_tuning</span><span class="o">.</span><span class="n">jobs</span><span class="o">.</span><span class="n">list_events</span><span class="p">(</span><span class="n">fine_tuning_job_id</span><span class="o">=</span><span class="s2">&#34;ftjob-gvP0VB7RlWcF3QHdQrEVf49Y&#34;</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># 删除微调模型</span> </span></span><span class="line"><span class="cl"><span class="n">client</span><span class="o">.</span><span class="n">models</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">&#34;ft:gpt-3.5-turbo:acemeco:suffix:abc123&#34;</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div><p>调用方式跟官方的模型是一样的,只需要修改一个模型名称就可以了,例如:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span> </span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">completion</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">chat</span><span class="o">.</span><span class="n">completions</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;ft:gpt-4o-mini-2024-07-18:personal:0724:9oMH6S7A&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">messages</span><span class="o">=</span><span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;Please write an SEO article of no less than 800 words based on the title I gave you, including at least 4 subtitles by HTML format. Do not include the &lt;h1&gt; , &lt;body&gt; tag. Do not include the &lt;html&gt; tag in the start and end of the content. Directly start with the content.&#34;</span><span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&#34;title:</span><span class="si">{</span><span class="n">task</span><span class="o">.</span><span class="n">title</span><span class="si">}</span><span class="s2">,core keyword:</span><span class="si">{</span><span class="n">task</span><span class="o">.</span><span class="n">coreKeywords</span><span class="si">}</span><span class="s2">,related keyword:</span><span class="si">{</span><span class="n">task</span><span class="o">.</span><span class="n">relatedKeywords</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">completion</span><span class="o">.</span><span class="n">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">message</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div><h2 id="评估结果" class="heading-element"><span>5 评估结果</span> <a href="#%e8%af%84%e4%bc%b0%e7%bb%93%e6%9e%9c" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>训练过程中有两个指标可供参考,分别是损失值和Token准确率,官方解释如下:</p> <blockquote> <p>验证损失和验证Token准确率通过两种不同的方式计算——在每一步期间的一个小批量数据上计算,以及在每个Epoch结束时的整个验证集上计算。整个验证损失和整个验证Token准确率指标是追踪模型总体性能的最准确指标。这些统计数据旨在提供一个合理性检查,以确保训练顺利进行(损失应该减少,Token准确率应该增加)。</p> </blockquote> <p>但指标毕竟只是参考,实际效果还是要自己评估。微调后的模型至少有以下提升:</p> <ul> <li>文章篇幅增长20%</li> <li>文章结构更接近训练的数据</li> <li>不会再出现格式错误(如markdown格式、添加css等)</li> </ul> <p>以&quot;What is the Difference Between a Mural and a Mosaic?&ldquo;为题生成的文章如下:</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F07%2F4821854ba99cf201f9b8795df70d0d9d.png" alt="评估结果" srcset="https://cdn1.zair.top/images/2024%2F07%2F4821854ba99cf201f9b8795df70d0d9d.png?size=small, https://cdn1.zair.top/images/2024%2F07%2F4821854ba99cf201f9b8795df70d0d9d.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F07%2F4821854ba99cf201f9b8795df70d0d9d.png?size=large 2x" data-title="评估结果" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h2 id="参考文章" class="heading-element"><span>6 参考文章</span> <a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ul> <li><a href="https://platform.openai.com/docs/guides/fine-tuning"target="_blank" rel="external nofollow noopener noreferrer">https://platform.openai.com/docs/guides/fine-tuning<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> </ul>

2024/7/24
阅读更多

AI Agent智能体四类设计模式:通用人工智能必经之路

<h2 id="ai-agent智能体四类设计模式通用人工智能必经之路" class="heading-element"><span>AI Agent智能体四类设计模式:通用人工智能必经之路</span> <a href="#ai-agent%e6%99%ba%e8%83%bd%e4%bd%93%e5%9b%9b%e7%b1%bb%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e9%80%9a%e7%94%a8%e4%ba%ba%e5%b7%a5%e6%99%ba%e8%83%bd%e5%bf%85%e7%bb%8f%e4%b9%8b%e8%b7%af" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>如果把使用AI完成任务比喻成写作文,那么非智能体(Agent)的方式就是让AI从头到尾一次写完,不准修改,而Agent的方式则允许AI多次反复修改,并且在修改的过程中还可以借助工具、与外界合作交流。Agent,在2024年看来,是实现通用人工智能(AGI)的道路之一。</p> <p>基础模型推动了生成式人工智能的发展,让AI Agent可以自动地帮用户完成任务。近一年来,各类Agent层出不穷。受吴恩达在红衫AI峰会的演讲启发,本文通过结合近一年内发表的论文和Langchain等工程博客,尽力整理了现有的Agent并总结出设计范式,希望能为设计基于基础模型的智能体提供帮助。</p> <p>可初步整理出以下表格中的16中模式:</p> <table> <thead> <tr> <th>模式</th> <th>简介</th> </tr> </thead> <tbody> <tr> <td>Passive goal creator</td> <td>通过对话界面分析用户的明确提示,以保持交互性、目标追踪和直观性。</td> </tr> <tr> <td>Proactive goal creator</td> <td>通过理解人类交互和捕捉上下文来预见用户的目标,以增强交互性、目标追踪和可访问性。</td> </tr> <tr> <td>Prompt/response optimiser</td> <td>根据预期的输入或输出内容和格式优化提示/响应,以提供标准化、响应准确性、互操作性和适应性。</td> </tr> <tr> <td>Retrieval augmented generation</td> <td>在保持本地基础模型智能体系统实现数据隐私的同时,增强智能体的知识更新能力。</td> </tr> <tr> <td>One-shot model querying</td> <td>在单个实例中访问基础模型以生成计划所需的所有步骤,以提高成本效率和简化流程。</td> </tr> <tr> <td>Incremental model querying</td> <td>在计划生成过程的每一步访问基础模型,以提供补充上下文、提高响应准确性和解释性。</td> </tr> <tr> <td>Single-path plan generator</td> <td>协调生成实现用户目标的中间步骤,以提高推理确定性、连贯性和效率。</td> </tr> <tr> <td>Multi-path plan generator</td> <td>允许在实现用户目标的每一步创建多种选择,以增强推理确定性、连贯性、对人类偏好的对齐性和包容性。</td> </tr> <tr> <td>Self-reflection</td> <td>使智能体能够生成对计划和推理过程的反馈,并提供自我改进的指导,以提高推理确定性、解释性、持续改进和效率。</td> </tr> <tr> <td>Cross-reflection</td> <td>使用不同的智能体或基础模型提供反馈并改进生成的计划和推理过程,以提高推理确定性、解释性、互操作性、包容性、可扩展性和持续改进。</td> </tr> <tr> <td>Human reflection</td> <td>收集人类反馈以改进计划和推理过程,有效对齐人类偏好,提高可争议性、有效性、公平性和持续改进。</td> </tr> <tr> <td>Voting-based cooperation</td> <td>使智能体可以自由表达意见,并通过提交投票达成共识,以提高多样性、有效的分工和容错性。</td> </tr> <tr> <td>Role-based cooperation</td> <td>分配不同的角色,并根据智能体的角色最终确定决策,以提高决策的确定性、分工、容错性、可扩展性和责任性。</td> </tr> <tr> <td>Debate-based cooperation</td> <td>智能体通过辩论提供和接收反馈,调整其想法和行为,直到达成共识,以提高决策确定性、适应性、解释性、响应准确性和批判性思维。</td> </tr> <tr> <td>Multimodal guardrails</td> <td>控制基础模型的输入和输出,以满足特定要求,如用户要求、伦理标准和法律法规,以增强稳健性、安全性、标准对齐和适应性。</td> </tr> <tr> <td>Tool/agent registry</td> <td>维护一个统一且方便的来源,以选择不同的智能体和工具,以提高可发现性、效率和工具适用性。</td> </tr> </tbody> </table> <p>这16种模式,都可以归结到吴恩达在红衫AI峰会的演讲中提出的4范式,分别是:</p> <ul> <li>Reflection</li> <li>Tool Use</li> <li>Planning</li> <li>Multiagent Collaboration</li> </ul> <h2 id="reflection" class="heading-element"><span>1 Reflection</span> <a href="#reflection" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="basic-reflection" class="heading-element"><span>1.1 Basic Reflection</span> <a href="#basic-reflection" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在LLM Agent构建的背景下,反思(Reflection)是指提示LLM观察其过去的步骤(以及来自工具/环境的潜在观察)以评估所选行动的质量的过程。然后将这些反馈用于重新规划、搜索或评估等下游任务。下图是一种基本的反思模式。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F88d05b24ee82d9bead9a2b0c6c2b2724.png" alt="Basic Reflection" srcset="https://cdn1.zair.top/images/2024%2F05%2F88d05b24ee82d9bead9a2b0c6c2b2724.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F88d05b24ee82d9bead9a2b0c6c2b2724.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F88d05b24ee82d9bead9a2b0c6c2b2724.png?size=large 2x" data-title="Basic Reflection" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>Basic Reflection</p> <h3 id="reflexion-actor" class="heading-element"><span>1.2 Reflexion Actor</span> <a href="#reflexion-actor" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>由 Shinn 等人提出的 Reflexion 是一种通过语言反馈和自我反思来学习的架构。这种Agent会点评其任务结果,以生成更高质量的最终结果,但代价是执行时间更长。主要包含三个组成部分:</p> <ol> <li>Actor (agent) with self-reflection 会自我反思的工人</li> <li>External evaluator (task-specific, e.g. code compilation steps) 外部评委(特定任务,例如代码编译步骤)</li> <li>Episodic memory that stores the reflections from (1). 来自(1)的反思的情景记忆存储</li> </ol> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2Fa33408400ae8ecf30cbbf598cf709e2f.png" alt="Reflexion Actor" srcset="https://cdn1.zair.top/images/2024%2F05%2Fa33408400ae8ecf30cbbf598cf709e2f.png?size=small, https://cdn1.zair.top/images/2024%2F05%2Fa33408400ae8ecf30cbbf598cf709e2f.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2Fa33408400ae8ecf30cbbf598cf709e2f.png?size=large 2x" data-title="Reflexion Actor" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>Reflexion Actor</p> <h3 id="lats" class="heading-element"><span>1.3 LATS</span> <a href="#lats" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>语言智能体树搜索(LATS),由 Zhou 等人提出,是一种通用的LLM Agent搜索算法,它结合了反思/评估和搜索(特别是蒙特卡洛树搜索),与类似技术如 ReACT、Reflexion 或 Tree of Thoughts 相比,能够实现更好的整体任务性能。</p> <p>它有四个主要步骤:</p> <ol> <li> <p>Select: pick the best next actions based on the aggregate rewards from step (2). Either respond (if a solution is found or the max search depth is reached) or continue searching.</p> <p>选择:根据步骤(2)中的总奖励选择最佳的下一步行动。若找到解决方案或达到最大搜索深度,则进行响应,否则继续搜索。</p> </li> <li> <p>Expand and simulate: select the &ldquo;best&rdquo; 5 potential actions to take and execute them in parallel.</p> <p>扩展并模拟:选择“最佳”的 5 个候选行动并同时执行。</p> </li> <li> <p>Reflect + Evaluate: observe the outcomes of these actions and score the decisions based on reflection (and possibly external feedback)</p> <p>反思 + 评估:观察这些行动的结果,并根据反思(以及可能的外部反馈)对决策进行评分</p> </li> <li> <p>Backpropagate: update the scores of the root trajectories based on the outcomes.</p> <p>反向传播:根据结果更新根轨迹的分数。</p> </li> </ol> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F64ac3101a548d42fd1ede6715e99d143.png" alt="LATS" srcset="https://cdn1.zair.top/images/2024%2F05%2F64ac3101a548d42fd1ede6715e99d143.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F64ac3101a548d42fd1ede6715e99d143.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F64ac3101a548d42fd1ede6715e99d143.png?size=large 2x" data-title="LATS" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>LATS</p> <h2 id="tool-use" class="heading-element"><span>2 Tool Use</span> <a href="#tool-use" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>调用工具,通过函数的形式使用。</p> <h2 id="planning" class="heading-element"><span>3 Planning</span> <a href="#planning" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="react" class="heading-element"><span>3.1 ReAct</span> <a href="#react" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>ReAct通过结合推理和行动来增强智能体的能力。ReAct方法允许智能体在接收信息后立即作出反应,而不是等待所有信息处理完毕。同时这种方法还注重推理和行动的紧密结合,智能体不仅需要分析和理解输入信息,还需要根据分析结果进行相应的行动。这种方式的优点在于其灵活性和环境适应性。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F3627afc055f30f3c275fadaf2ec38080.png" alt="ReAct框架" srcset="https://cdn1.zair.top/images/2024%2F05%2F3627afc055f30f3c275fadaf2ec38080.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F3627afc055f30f3c275fadaf2ec38080.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F3627afc055f30f3c275fadaf2ec38080.png?size=large 2x" data-title="ReAct框架" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>ReAct框架</p> <h3 id="plan-and-execute" class="heading-element"><span>3.2 Plan and Execute</span> <a href="#plan-and-execute" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>Plan and Execute 核心思想是首先制定一个多步骤的计划,然后逐项执行该计划。在完成特定任务后,可以重新审视计划并进行适当修改。</p> <p>这与典型的 ReAct 风格代理相比,后者是一次思考一步。这种“计划和执行”风格的优点是:</p> <ol> <li>明确的长期规划(即使是非常强大的LLMs也难做到这点)</li> <li>能够在执行步骤中使用较小/较弱的模型,仅在规划步骤中使用较大/较好的模型</li> </ol> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2Ffacdd82299104208c2110cff4a41ce81.png" alt="Plan-and-Execute" srcset="https://cdn1.zair.top/images/2024%2F05%2Ffacdd82299104208c2110cff4a41ce81.png?size=small, https://cdn1.zair.top/images/2024%2F05%2Ffacdd82299104208c2110cff4a41ce81.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2Ffacdd82299104208c2110cff4a41ce81.png?size=large 2x" data-title="Plan-and-Execute" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p><strong>Plan-and-Execute</strong></p> <h3 id="rewoo" class="heading-element"><span>3.3 ReWOO</span> <a href="#rewoo" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>在 ReWOO 中,Xu 等人提出了一种结合多步规划器和变量替换的智能体,以实现有效的工具使用。它通过以下方面改进 ReACT 风格的Agent架构:</p> <ol> <li>通过一次性生成使用的完整工具链来减少token消耗和执行时间。(ReACT 风格的代理架构需要许多LLM调用,并且有冗余前缀(因为系统提示和前面的步骤在每个推理步骤中都提供给LLM)</li> <li>简化微调过程。由于规划数据不依赖于工具的输出,模型可以在理论上不实际调用工具的情况下进行微调</li> </ol> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F63e39cb8db866a5a4a1db4e9970f9413.png" alt="Reasoning without Observation" srcset="https://cdn1.zair.top/images/2024%2F05%2F63e39cb8db866a5a4a1db4e9970f9413.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F63e39cb8db866a5a4a1db4e9970f9413.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F63e39cb8db866a5a4a1db4e9970f9413.png?size=large 2x" data-title="Reasoning without Observation" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p><strong>Reasoning without Observation</strong></p> <h3 id="llmcompiler" class="heading-element"><span>3.4 LLMCompiler</span> <a href="#llmcompiler" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>LLMCompiler 是一种通过在 DAG 中突击执行任务来加速agent任务的执行的Agent架构。它还通过减少对LLM的调用次数来节省冗余token使用的成本。主要包含三个部分:</p> <ol> <li> <p>Planner: stream a DAG of tasks.</p> <p>规划器:流式处理任务的有向无环图。</p> </li> <li> <p>Task Fetching Unit: schedules and executes the tasks as soon as they are executable</p> <p>任务获取单元:在任务可执行时立即调度和执行任务</p> </li> <li> <p>Joiner: Responds to the user or triggers a second plan</p> <p>连接器:响应用户或触发第二个计划</p> </li> </ol> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F593ce5e78b3d68b79abffb49fe0a137d.png" alt="LLMCompiler" srcset="https://cdn1.zair.top/images/2024%2F05%2F593ce5e78b3d68b79abffb49fe0a137d.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F593ce5e78b3d68b79abffb49fe0a137d.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F593ce5e78b3d68b79abffb49fe0a137d.png?size=large 2x" data-title="LLMCompiler" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>LLMCompiler</p> <h2 id="multiagent-collaboration" class="heading-element"><span>4 Multiagent collaboration</span> <a href="#multiagent-collaboration" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><h3 id="supervison" class="heading-element"><span>4.1 Supervison</span> <a href="#supervison" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>通过一个监管者,管理和调度多个Agent进行协作。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F8d55fa6833db17b2e1970198230f1373.png" alt="监督方式" srcset="https://cdn1.zair.top/images/2024%2F05%2F8d55fa6833db17b2e1970198230f1373.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F8d55fa6833db17b2e1970198230f1373.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F8d55fa6833db17b2e1970198230f1373.png?size=large 2x" data-title="监督方式" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>监督方式</p> <h3 id="hierarchical-teams" class="heading-element"><span>4.2 <strong>Hierarchical Teams</strong></span> <a href="#hierarchical-teams" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>通过分层、分级组织Agent来完成复杂且工作量大的任务。AutoGen就是这种方式的典型代表。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F77c3c444633cb7c024fbc0ac23b8f690.png" alt="分层团队方式" srcset="https://cdn1.zair.top/images/2024%2F05%2F77c3c444633cb7c024fbc0ac23b8f690.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F77c3c444633cb7c024fbc0ac23b8f690.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F77c3c444633cb7c024fbc0ac23b8f690.png?size=large 2x" data-title="分层团队方式" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>分层团队方式</p> <h3 id="collaboration" class="heading-element"><span>4.3 <strong>Collaboration</strong></span> <a href="#collaboration" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h3><p>单个agent使用多种(领域)工具的能力有限,需要多个agent写作使用更多类型的工具。可以使用“分治法”的思想,让每个agent成为专注于处理一类问题的“专家”,然后再让他们进行合作。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2Fd16b1684829ffde358e49d51b5d0e8c0.png" alt="一种基本的多智能体合作" srcset="https://cdn1.zair.top/images/2024%2F05%2Fd16b1684829ffde358e49d51b5d0e8c0.png?size=small, https://cdn1.zair.top/images/2024%2F05%2Fd16b1684829ffde358e49d51b5d0e8c0.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2Fd16b1684829ffde358e49d51b5d0e8c0.png?size=large 2x" data-title="一种基本的多智能体合作" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>一种基本的多智能体合作</p> <h2 id="评估" class="heading-element"><span>5 评估</span> <a href="#%e8%af%84%e4%bc%b0" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>一种最直接的想法是使用一个agent作为“虚拟用户”进行评估,很多任务结果无法量化评估的任务可能不得不使用这种方式。但对于有明确指标的任务(分类、回归),也许可以直接利用一个工具进行评估。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F558f46c4a3d4c8ed5bd4d6f0d06837ac.png" alt="Agent-based Evaluation" srcset="https://cdn1.zair.top/images/2024%2F05%2F558f46c4a3d4c8ed5bd4d6f0d06837ac.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F558f46c4a3d4c8ed5bd4d6f0d06837ac.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F558f46c4a3d4c8ed5bd4d6f0d06837ac.png?size=large 2x" data-title="Agent-based Evaluation" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <p>Agent-based Evaluation</p> <h2 id="agi的其它方式" class="heading-element"><span>6 AGI的其它方式</span> <a href="#agi%e7%9a%84%e5%85%b6%e5%ae%83%e6%96%b9%e5%bc%8f" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p>Agent只是一种比较有希望的AGI方式,但并不是唯一方式。Agent方式本身可以和RAG、用户参与等方法有机结合。比如下面Shi等人结合Agent和检索的方式,实现了大模型解决奥赛编程问题。</p> <p><img loading="lazy" src="https://cdn1.zair.top/images/2024%2F05%2F787708ee6ea3b818eda88ca3ac7504f2.png" alt="Untitled" srcset="https://cdn1.zair.top/images/2024%2F05%2F787708ee6ea3b818eda88ca3ac7504f2.png?size=small, https://cdn1.zair.top/images/2024%2F05%2F787708ee6ea3b818eda88ca3ac7504f2.png?size=medium 1.5x, https://cdn1.zair.top/images/2024%2F05%2F787708ee6ea3b818eda88ca3ac7504f2.png?size=large 2x" data-title="Untitled" style="background: url(/images/loading.min.svg) no-repeat center;" onload="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}this.dataset.lazyloaded='';" onerror="this.title=this.dataset.title;for(const i of ['style', 'data-title','onerror','onload']){this.removeAttribute(i);}"/></p> <h2 id="推荐阅读" class="heading-element"><span>7 推荐阅读</span> <a href="#%e6%8e%a8%e8%8d%90%e9%98%85%e8%af%bb" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><p><a href="https://github.com/AGI-Edgerunners/LLM-Agents-Papers?tab=readme-ov-file"target="_blank" rel="external nofollow noopener noreferrer">https://github.com/AGI-Edgerunners/LLM-Agents-Papers<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> <p><a href="https://github.com/zjunlp/LLMAgentPapers"target="_blank" rel="external nofollow noopener noreferrer">https://github.com/zjunlp/LLMAgentPapers<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> <p><a href="https://www.cnblogs.com/bonelee/p/18153432"target="_blank" rel="external nofollow noopener noreferrer">AI agent智能体任务分解和调度的几篇经典文章 - bonelee - 博客园 (cnblogs.com)<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> <p><a href="https://zhuanlan.zhihu.com/p/691370751"target="_blank" rel="external nofollow noopener noreferrer">Agent四大范式 | CRITIC:吴恩达力推Agent设计范式 - 知乎 (zhihu.com)<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></p> <h2 id="参考" class="heading-element"><span>8 参考</span> <a href="#%e5%8f%82%e8%80%83" class="heading-mark"> <svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg> </a> </h2><ol> <li>Kim, Sehoon, Suhong Moon, Ryan Tabrizi, Nicholas Lee, Michael W. Mahoney, Kurt Keutzer, and Amir Gholami. “An LLM Compiler for Parallel Function Calling.” arXiv, February 6, 2024. <a href="https://doi.org/10.48550/arXiv.2312.04511"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2312.04511<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Liu, Yue, Sin Kit Lo, Qinghua Lu, Liming Zhu, Dehai Zhao, Xiwei Xu, Stefan Harrer, and Jon Whittle. “Agent Design Pattern Catalogue: A Collection of Architectural Patterns for Foundation Model Based Agents.” arXiv, May 16, 2024. <a href="https://doi.org/10.48550/arXiv.2405.10467"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2405.10467<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Shi, Quan, Michael Tang, Karthik Narasimhan, and Shunyu Yao. “Can Language Models Solve Olympiad Programming?” arXiv, April 16, 2024. <a href="https://doi.org/10.48550/arXiv.2404.10952"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2404.10952<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Shinn, Noah, Federico Cassano, Edward Berman, Ashwin Gopinath, Karthik Narasimhan, and Shunyu Yao. “Reflexion: Language Agents with Verbal Reinforcement Learning.” arXiv, October 10, 2023. <a href="https://doi.org/10.48550/arXiv.2303.11366"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2303.11366<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Wang, Lei, Wanyu Xu, Yihuai Lan, Zhiqiang Hu, Yunshi Lan, Roy Ka-Wei Lee, and Ee-Peng Lim. “Plan-and-Solve Prompting: Improving Zero-Shot Chain-of-Thought Reasoning by Large Language Models.” arXiv, May 26, 2023. <a href="https://doi.org/10.48550/arXiv.2305.04091"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2305.04091<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Xu, Binfeng, Zhiyuan Peng, Bowen Lei, Subhabrata Mukherjee, Yuchen Liu, and Dongkuan Xu. “ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models.” arXiv, May 22, 2023. <a href="https://doi.org/10.48550/arXiv.2305.18323"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2305.18323<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Yao, Shunyu, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan, and Yuan Cao. “ReAct: Synergizing Reasoning and Acting in Language Models.” arXiv, March 9, 2023. <a href="https://doi.org/10.48550/arXiv.2210.03629"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2210.03629<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>“Yoheinakajima/Babyagi.” Accessed May 21, 2024. <a href="https://github.com/yoheinakajima/babyagi/tree/main"target="_blank" rel="external nofollow noopener noreferrer">https://github.com/yoheinakajima/babyagi/tree/main<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>“LangGraph tutorials.” Accessed May 21, 2024. <a href="https://langchain-ai.github.io/langgraph/tutorials/"target="_blank" rel="external nofollow noopener noreferrer">https://langchain-ai.github.io/langgraph/tutorials/<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a></li> <li>Zhou, Andy, Kai Yan, Michal Shlapentokh-Rothman, Haohan Wang, and Yu-Xiong Wang. “Language Agent Tree Search Unifies Reasoning Acting and Planning in Language Models.” arXiv, December 5, 2023. <a href="https://doi.org/10.48550/arXiv.2310.04406"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2310.04406<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> <li>Zhou, Pei, Jay Pujara, Xiang Ren, Xinyun Chen, Heng-Tze Cheng, Quoc V. Le, Ed H. Chi, Denny Zhou, Swaroop Mishra, and Huaixiu Steven Zheng. “Self-Discover: Large Language Models Self-Compose Reasoning Structures.” arXiv, February 5, 2024. <a href="https://doi.org/10.48550/arXiv.2402.03620"target="_blank" rel="external nofollow noopener noreferrer">https://doi.org/10.48550/arXiv.2402.03620<i class="fa-solid fa-external-link-alt fa-fw fa-xs ms-1 text-secondary" aria-hidden="true"></i></a>.</li> </ol>

2024/5/22
阅读更多