<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>ZH</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://zhhll.com.cn/</id>
  <link href="https://zhhll.com.cn/" rel="alternate"/>
  <link href="https://zhhll.com.cn/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, ZH</rights>
  <subtitle>java菜鸟号正在起航</subtitle>
  <title>小菜鸟</title>
  <updated>2026-06-29T10:44:03.474Z</updated>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？"><a href="#GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？" class="headerlink" title="GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？"></a>GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？</h1><blockquote><p>传统RNN在长序列面前总“健忘”，GRU用两个门教会了神经网络什么时候该记、什么时候该忘。</p></blockquote><h2 id="一、什么是GRU？"><a href="#一、什么是GRU？" class="headerlink" title="一、什么是GRU？"></a>一、什么是GRU？</h2><p>如果你接触过循环神经网络（RNN），一定听说过它的“远亲”LSTM，以及它的“近亲”——GRU。</p><p><strong>门控循环单元（GRU）</strong>是循环神经网络（RNN）的一种重要变体，由Cho等人于2014年提出。</p><p>要理解GRU，得先知道它从哪来。传统RNN有一个致命问题——<strong>梯度消失</strong>，导致它处理长序列时记不住太久远的信息。为了解决这个问题，LSTM在1997年被发明，它通过复杂的门控机制让RNN终于能“记住”长距离的依赖。</p><p>但LSTM的结构太复杂了——三个门、一个额外的细胞状态，参数多、计算慢。于是，Cho等人在LSTM的基础上做了一个“减法”，提出了GRU。它继承了LSTM的核心门控思想，但把结构大幅简化：三个门变成两个，去掉独立的细胞状态，用更少的参数达到相近的性能。</p><p>换句话说，<strong>GRU可以理解为LSTM的“轻量版”或“精简版”</strong>——在效率和效果之间找到了更优的平衡点。</p><p>那GRU和传统RNN最大的区别是什么？</p><p>想象一下你在读一本小说。传统RNN就像记忆力极差的人，读到第100页时，已经想不起第1页的主角名字了。而GRU则像一个聪明的读者，他会主动判断：哪些信息是重要的，需要记在心里；哪些是无关的，可以直接忽略；当剧情发生转折时，他会清空旧记忆、迎接新情节。</p><p>这种“智能记忆”的能力，就来自GRU内部的两个关键结构：</p><ul><li><p><strong>重置门（Reset Gate）</strong>：决定“忘记多少过去的记忆”。如果上一时刻的信息和当前任务无关，重置门可以把它“清空”，让模型从当前输入重新开始。</p></li><li><p><strong>更新门（Update Gate）</strong>：决定“保留多少旧记忆、吸收多少新信息”。当更新门接近1时，模型会一直“记住”很久以前的信息；当它接近0时，模型会完全用新信息替换旧状态。</p></li></ul><p>正是这两个门相互配合，让GRU既不会“健忘”，也不会“被噪音淹没”。</p><hr><h2 id="二、为什么需要GRU？它解决了什么问题？"><a href="#二、为什么需要GRU？它解决了什么问题？" class="headerlink" title="二、为什么需要GRU？它解决了什么问题？"></a>二、为什么需要GRU？它解决了什么问题？</h2><h3 id="传统RNN的致命缺陷：梯度消失"><a href="#传统RNN的致命缺陷：梯度消失" class="headerlink" title="传统RNN的致命缺陷：梯度消失"></a>传统RNN的致命缺陷：梯度消失</h3><p>在GRU诞生之前，传统RNN虽然在语言建模、时间序列预测等领域崭露头角，但始终被一个致命问题困扰——<strong>梯度消失（Vanishing Gradient）</strong>。</p><span id="more"></span><p>简单来说：RNN在训练时，需要将误差从序列的最后一个时间步“反向传播”到第一个时间步。这个过程中，梯度要经历多次连乘运算。如果梯度值小于1，经过数十次连乘后就会趋近于0——这就是“梯度消失”。结果就是，<strong>序列中靠前的信息对模型几乎不再产生任何影响</strong>。</p><p>举个例子：假设你要预测一段文本的情感倾向，开头的“我不喜欢”对结果至关重要。但在传统RNN中，当文本长度超过几十个词时，开头的信息早就“衰减没了”，模型只能根据结尾的几个词做判断——这显然是不合理的。</p><h3 id="GRU的三大应用场景"><a href="#GRU的三大应用场景" class="headerlink" title="GRU的三大应用场景"></a>GRU的三大应用场景</h3><p>GRU通过门控机制有效缓解了梯度消失，让信息可以在长序列中顺畅流动。它主要解决了三类实际问题：</p><p><strong>场景一：需要长期保存关键信息</strong></p><p>比如股价预测中，几个月前的某个政策变化可能对当前走势有深远影响；或者医疗诊断中，病人早期的症状记录对最终判断至关重要。GRU的更新门可以让这些“老信息”一直保留到需要它的时刻。</p><p><strong>场景二：需要跳过无关的噪音</strong></p><p>在处理网页HTML代码、长篇文档时，存在大量无关的辅助信息（如格式标签、广告内容）。GRU可以通过重置门“跳过”这些噪音，只关注真正有用的内容。</p><p><strong>场景三：需要处理逻辑分段的数据</strong></p><p>比如在金融时间序列中，熊市和牛市是完全不同的逻辑阶段；或者在多章节的书籍中，情节会发生重大转折。GRU可以在阶段切换时重置内部状态，避免旧逻辑干扰新判断。</p><h3 id="如果没有GRU会怎样？"><a href="#如果没有GRU会怎样？" class="headerlink" title="如果没有GRU会怎样？"></a>如果没有GRU会怎样？</h3><p><strong>答案很直接：在长序列任务上，传统RNN将寸步难行。</strong></p><p>尽管梯度裁剪（Gradient Clipping）可以应对梯度爆炸，但对于梯度消失，传统RNN几乎束手无策。没有GRU（或LSTM这类门控网络），机器翻译无法准确处理长句、语音识别难以理解长语音片段、情感分析会在长文本上频频失误。</p><p>可以说，GRU的出现，<strong>让深度学习真正具备了处理长序列数据的能力</strong>。</p><hr><h2 id="三、GRU并非万能：它带来了哪些新问题？"><a href="#三、GRU并非万能：它带来了哪些新问题？" class="headerlink" title="三、GRU并非万能：它带来了哪些新问题？"></a>三、GRU并非万能：它带来了哪些新问题？</h2><p>任何技术都是一把双刃剑，GRU也不例外。它在解决问题的同时，也带来了新的挑战：</p><p><strong>挑战一：复杂依赖建模能力有限</strong></p><p>GRU本质上是LSTM的“精简版”，参数更少、结构更简单。这种设计在效率上有优势，但在处理非常复杂的依赖关系时，LSTM的表现往往更胜一筹。</p><p><strong>挑战二：超长序列仍会“力不从心”</strong></p><p>GRU虽然将有效依赖长度从10步左右提升到了100-200步，但面对几千甚至上万步的超长序列（比如整本小说、长达数小时的语音），信息衰减依然存在。</p><p><strong>挑战三：无法并行计算</strong></p><p>和所有RNN一样，GRU必须按时间步顺序计算——先算第1步，再算第2步，依次类推。这种串行特性使得GRU无法像Transformer那样充分利用GPU的并行计算能力，训练速度成为瓶颈。</p><p><strong>挑战四：可解释性差</strong></p><p>门控机制虽然有效，但也让GRU的内部运作变得复杂而难以理解——我们很难直观解释某个门为什么开、为什么关。</p><p><strong>挑战五：超参数调优难度大</strong></p><p>GRU对学习率、隐藏层大小、门控偏置等超参数比较敏感，需要仔细调优才能达到最佳效果。</p><hr><h2 id="四、GRU的下一步往哪走？"><a href="#四、GRU的下一步往哪走？" class="headerlink" title="四、GRU的下一步往哪走？"></a>四、GRU的下一步往哪走？</h2><h3 id="方向一：架构轻量化"><a href="#方向一：架构轻量化" class="headerlink" title="方向一：架构轻量化"></a>方向一：架构轻量化</h3><p>学术界一直在探索如何进一步精简GRU。2026年提出的<strong>Minion Recurrent Unit（MiRU）</strong>就是一个典型代表——用简单的缩放系数替代了复杂的门控机制，参数量减少约2.88倍，训练速度提升1.92倍以上，能耗降低约5倍，而性能与标准GRU相当。这种轻量化设计对手机、物联网设备等资源受限场景非常有价值。</p><h3 id="方向二：与图神经网络融合"><a href="#方向二：与图神经网络融合" class="headerlink" title="方向二：与图神经网络融合"></a>方向二：与图神经网络融合</h3><p>在交通流量预测、气象预报等时空序列预测任务中，GRU正在与图神经网络（GNN）深度结合。例如<strong>DGI-GRU</strong>模型，将图卷积嵌入GRU架构，在同一层中同时提取时间和空间特征，有效减少了参数冗余。</p><h3 id="方向三：残差连接增强"><a href="#方向三：残差连接增强" class="headerlink" title="方向三：残差连接增强"></a>方向三：残差连接增强</h3><p>为了让更深层的GRU网络也能稳定训练，<strong>RT-GRU（Residual-Time GRU）</strong>在时间维度和梯度传播方向上都引入了残差连接，让梯度能更顺畅地流动，进一步提升了捕捉超长依赖的能力。</p><h3 id="方向四：持续学习能力"><a href="#方向四：持续学习能力" class="headerlink" title="方向四：持续学习能力"></a>方向四：持续学习能力</h3><p>最新的GRU变体开始具备持续学习能力——在不断变化的环境中，模型能学习新任务而不遗忘旧知识。MiRU结合回放策略和生物启发的全局抑制机制，在多任务学习中展现了良好的稳定性。</p><h3 id="方向五：与Transformer生态互补"><a href="#方向五：与Transformer生态互补" class="headerlink" title="方向五：与Transformer生态互补"></a>方向五：与Transformer生态互补</h3><p>不可否认，Transformer在长序列建模和并行计算上有巨大优势。但在移动端、边缘设备等资源受限场景，以及序列长度动态变化的在线学习任务中，GRU依然有不可替代的价值。<strong>未来的趋势很可能是“混合架构”</strong>：云端用Transformer处理超长序列，端侧用轻量化GRU变体做实时推理，两者优势互补。</p><hr><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>GRU诞生于解决RNN“健忘症”的刚需，它用简练而优雅的门控设计，在记忆效率与计算成本之间找到了一个巧妙的平衡点。</p><p>它当然不是完美的——面对超长序列、复杂依赖和并行计算的挑战，GRU仍显吃力。但也正是这些局限，推动着学术界不断探索更轻、更强、更智能的变体。</p><p>在Transformer独领风骚的今天，GRU并未过时。它在资源敏感场景、实时推理任务、传统序列建模中依然活跃。而更重要的是，GRU的设计思想——<strong>如何让神经网络学会“选择性记忆”</strong>——仍是深度学习中最深刻的问题之一。</p><p>也许未来会有更强大的架构取代GRU，但它作为“门控循环网络”这一思想流派的代表性作品，在整个深度学习史上的地位，不可撼动。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/11.GRU/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/11.GRU/"/>
    <published>2026-06-29T11:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？"><a href="#GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？" class="headerlink" title="GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？"></a>GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？</h1><blockquote>
<p>传统RNN在长序列面前总“健忘”，GRU用两个门教会了神经网络什么时候该记、什么时候该忘。</p>
</blockquote>
<h2 id="一、什么是GRU？"><a href="#一、什么是GRU？" class="headerlink" title="一、什么是GRU？"></a>一、什么是GRU？</h2><p>如果你接触过循环神经网络（RNN），一定听说过它的“远亲”LSTM，以及它的“近亲”——GRU。</p>
<p><strong>门控循环单元（GRU）</strong>是循环神经网络（RNN）的一种重要变体，由Cho等人于2014年提出。</p>
<p>要理解GRU，得先知道它从哪来。传统RNN有一个致命问题——<strong>梯度消失</strong>，导致它处理长序列时记不住太久远的信息。为了解决这个问题，LSTM在1997年被发明，它通过复杂的门控机制让RNN终于能“记住”长距离的依赖。</p>
<p>但LSTM的结构太复杂了——三个门、一个额外的细胞状态，参数多、计算慢。于是，Cho等人在LSTM的基础上做了一个“减法”，提出了GRU。它继承了LSTM的核心门控思想，但把结构大幅简化：三个门变成两个，去掉独立的细胞状态，用更少的参数达到相近的性能。</p>
<p>换句话说，<strong>GRU可以理解为LSTM的“轻量版”或“精简版”</strong>——在效率和效果之间找到了更优的平衡点。</p>
<p>那GRU和传统RNN最大的区别是什么？</p>
<p>想象一下你在读一本小说。传统RNN就像记忆力极差的人，读到第100页时，已经想不起第1页的主角名字了。而GRU则像一个聪明的读者，他会主动判断：哪些信息是重要的，需要记在心里；哪些是无关的，可以直接忽略；当剧情发生转折时，他会清空旧记忆、迎接新情节。</p>
<p>这种“智能记忆”的能力，就来自GRU内部的两个关键结构：</p>
<ul>
<li><p><strong>重置门（Reset Gate）</strong>：决定“忘记多少过去的记忆”。如果上一时刻的信息和当前任务无关，重置门可以把它“清空”，让模型从当前输入重新开始。</p>
</li>
<li><p><strong>更新门（Update Gate）</strong>：决定“保留多少旧记忆、吸收多少新信息”。当更新门接近1时，模型会一直“记住”很久以前的信息；当它接近0时，模型会完全用新信息替换旧状态。</p>
</li>
</ul>
<p>正是这两个门相互配合，让GRU既不会“健忘”，也不会“被噪音淹没”。</p>
<hr>
<h2 id="二、为什么需要GRU？它解决了什么问题？"><a href="#二、为什么需要GRU？它解决了什么问题？" class="headerlink" title="二、为什么需要GRU？它解决了什么问题？"></a>二、为什么需要GRU？它解决了什么问题？</h2><h3 id="传统RNN的致命缺陷：梯度消失"><a href="#传统RNN的致命缺陷：梯度消失" class="headerlink" title="传统RNN的致命缺陷：梯度消失"></a>传统RNN的致命缺陷：梯度消失</h3><p>在GRU诞生之前，传统RNN虽然在语言建模、时间序列预测等领域崭露头角，但始终被一个致命问题困扰——<strong>梯度消失（Vanishing Gradient）</strong>。</p>]]>
    </summary>
    <title>GRU：从“遗忘”到“记住”，门控循环单元如何重塑序列建模？</title>
    <updated>2026-06-29T10:44:03.474Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来"><a href="#从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来" class="headerlink" title="从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来"></a>从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来</h1><p>在深度学习的浩瀚星海中，<strong>长短期记忆网络（LSTM）</strong> 曾是一颗璀璨的恒星。它不仅是学术界的重要里程碑，更是工业界落地时间序列问题的“银弹”。</p><p>今天，我们不只聊LSTM的原理，更想从“Why、What、How、Future”四个维度，带你重新审视这位曾经的序列之王——它为何而生、它带来了什么麻烦、以及它的后继者是谁。</p><h2 id="一、-什么是LSTM？——RNN家族的“反遗忘战士”"><a href="#一、-什么是LSTM？——RNN家族的“反遗忘战士”" class="headerlink" title="一、 什么是LSTM？——RNN家族的“反遗忘战士”"></a>一、 什么是LSTM？——RNN家族的“反遗忘战士”</h2><p>LSTM，全称Long Short-Term Memory，属于循环神经网络（RNN）的一个特殊变种。如果标准RNN是“金鱼记忆”（只能记住几秒钟前的事），那么LSTM就是试图通过精密的细胞机制，实现“选择性遗忘与长期记忆”的AI大脑。</p><p><strong>LSTM的核心“黑科技”在于细胞状态（Cell State）和门控机制（Gates）。</strong></p><p>你可以把<strong>细胞状态</strong>想象成一条贯穿始终的传送带，信息在上面流动几乎不做改变，这是长期记忆的物理载体。而<strong>遗忘门、输入门、输出门</strong>则像三个智能阀门：</p><ul><li><strong>遗忘门</strong>：决定扔掉哪些没用的旧信息（比如“昨天午饭吃了什么”）。</li><li><strong>输入门</strong>：决定把哪些新信息存入大脑（比如“今天会议的结论”）。</li><li><strong>输出门</strong>：决定基于当前记忆，对外输出什么内容。</li></ul><p>正是这种精巧的设计，让LSTM具备了处理长序列的潜力。</p><h2 id="二、-为什么需要LSTM？——救RNN于“水火之中”"><a href="#二、-为什么需要LSTM？——救RNN于“水火之中”" class="headerlink" title="二、 为什么需要LSTM？——救RNN于“水火之中”"></a>二、 为什么需要LSTM？——救RNN于“水火之中”</h2><p>在LSTM诞生之前，传统的RNN在反向传播时面临致命的<strong>梯度消失</strong>和<strong>梯度爆炸</strong>问题。简单来说，当序列长度超过一定限度（比如50个字），RNN的梯度会在连乘中趋近于0（消失）或变得极大（爆炸），导致模型无法更新权重，直接“罢工”。</p><p><strong>LSTM正是为了解决“长期依赖”而生。</strong> 它用加和的方式替代了连乘，让梯度能无损地流过细胞状态，从而捕捉到上百个时间步之前的关联。</p><span id="more"></span><p><strong>没有LSTM会怎样？</strong></p><ul><li>在机器翻译中，它可能译完前10个词，就忘了句首的主语是单数还是复数。</li><li>在情感分析中，它无法理解“虽然这部电影前半部分极其平庸，但结尾的反转令人惊艳”这种跨越全篇的转折关系。</li></ul><p>可以说，LSTM的存在，让深度学习第一次真正具备了“上下文理解”的能力。</p><h2 id="三、-LSTM的“副作用”：成也萧何，败也萧何"><a href="#三、-LSTM的“副作用”：成也萧何，败也萧何" class="headerlink" title="三、 LSTM的“副作用”：成也萧何，败也萧何"></a>三、 LSTM的“副作用”：成也萧何，败也萧何</h2><p>LSTM虽然强大，但它绝不是一个“免费午餐”的解决方案。恰恰相反，它带来了新的烦恼，这也是为什么后来Transformer能迅速崛起的原因之一。</p><p><strong>1. 算力吞金兽，训练极其缓慢</strong><br>LSTM内部拥有4个全连接层（FFN），参数数量是标准RNN的4倍。这不仅导致训练时间成倍增加，其循环的序列依赖结构也使得<strong>无法进行高效的并行计算</strong>（必须等上一个词处理完才能处理下一个）。</p><p><strong>2. 越复杂，越“黑盒”</strong><br>由于门控交互过于复杂，LSTM的决策过程极难解释。在金融风控、医疗诊断等强监管领域，这种“我只管预测，但不告诉你为什么”的特性是致命伤。</p><p><strong>3. 数据饥渴与过拟合</strong><br>参数多意味着需要海量数据。在小样本数据集上，LSTM很容易陷入“死记硬背”的过拟合陷阱，泛化能力远不如简化版的GRU（门控循环单元）。</p><p><strong>4. 依然没彻底解决长序列问题</strong><br>虽然LSTM缓解了梯度消失，但在处理数千个时间步的超长序列时，信息依然会在长距离传播中衰减，它并未“根除”问题。</p><h2 id="四、-后LSTM时代：我们该何去何从？"><a href="#四、-后LSTM时代：我们该何去何从？" class="headerlink" title="四、 后LSTM时代：我们该何去何从？"></a>四、 后LSTM时代：我们该何去何从？</h2><p>LSTM已经不是解决序列问题的唯一最优解。<strong>2017年Google提出Transformer架构后，整个行业的方向发生了巨变。</strong> 但LSTM的思想并未消亡，而是以新的形式进化着。</p><p><strong>1. 轻量化变体：GRU</strong><br>GRU将LSTM的三个门简化为两个（重置门与更新门），参数更少，训练更快，在中小规模数据集上，效果往往与LSTM持平，是工程落地的“高性价比”之选。</p><p><strong>2. 双向与深层化：BiLSTM与Stacked LSTM</strong><br>为了捕捉更丰富的特征，工程师们将LSTM正向和反向组合（BiLSTM），或者将多层LSTM堆叠（Stacked LSTM），用于语音识别和更复杂的时序特征提取。</p><p><strong>3. 注意力机制的降维打击（最关键）</strong><br>Attention机制允许模型在解码时直接“关注”输入序列中任意位置的关键信息，打破了RNN必须按顺序处理的枷锁。基于此的Transformer架构，因为<strong>支持完全并行计算</strong>，迅速在NLP、CV等领域取代了LSTM的主流地位。</p><p><strong>4. 多模态融合：CNN+LSTM</strong><br>在视频分析领域，常用CNN提取图像空间特征，再送入LSTM提取时间动态特征，这种组合依然在动作识别等领域发挥余热。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>LSTM见证了深度学习从“感知”走向“认知”的飞跃。虽然如今聚光灯更多打在Transformer和大模型身上，但LSTM所奠定的<strong>门控思想</strong>和<strong>记忆存储</strong>理念，依然是现代AI架构的基石。</p><p>对于算法工程师而言，精通LSTM不仅是为了调参，更是为了理解：<strong>如何让机器学会“记住该记住的，忘记该忘记的”</strong>——这不仅是LSTM的核心哲学，也恰好是人工智能最迷人的地方。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/10.LSTM/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/10.LSTM/"/>
    <published>2026-06-29T08:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来"><a href="#从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来" class="headerlink" title="从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来"></a>从“记忆大师”到“算力吞金兽”：一文读懂LSTM的前世、今生与未来</h1><p>在深度学习的浩瀚星海中，<strong>长短期记忆网络（LSTM）</strong> 曾是一颗璀璨的恒星。它不仅是学术界的重要里程碑，更是工业界落地时间序列问题的“银弹”。</p>
<p>今天，我们不只聊LSTM的原理，更想从“Why、What、How、Future”四个维度，带你重新审视这位曾经的序列之王——它为何而生、它带来了什么麻烦、以及它的后继者是谁。</p>
<h2 id="一、-什么是LSTM？——RNN家族的“反遗忘战士”"><a href="#一、-什么是LSTM？——RNN家族的“反遗忘战士”" class="headerlink" title="一、 什么是LSTM？——RNN家族的“反遗忘战士”"></a>一、 什么是LSTM？——RNN家族的“反遗忘战士”</h2><p>LSTM，全称Long Short-Term Memory，属于循环神经网络（RNN）的一个特殊变种。如果标准RNN是“金鱼记忆”（只能记住几秒钟前的事），那么LSTM就是试图通过精密的细胞机制，实现“选择性遗忘与长期记忆”的AI大脑。</p>
<p><strong>LSTM的核心“黑科技”在于细胞状态（Cell State）和门控机制（Gates）。</strong></p>
<p>你可以把<strong>细胞状态</strong>想象成一条贯穿始终的传送带，信息在上面流动几乎不做改变，这是长期记忆的物理载体。而<strong>遗忘门、输入门、输出门</strong>则像三个智能阀门：</p>
<ul>
<li><strong>遗忘门</strong>：决定扔掉哪些没用的旧信息（比如“昨天午饭吃了什么”）。</li>
<li><strong>输入门</strong>：决定把哪些新信息存入大脑（比如“今天会议的结论”）。</li>
<li><strong>输出门</strong>：决定基于当前记忆，对外输出什么内容。</li>
</ul>
<p>正是这种精巧的设计，让LSTM具备了处理长序列的潜力。</p>
<h2 id="二、-为什么需要LSTM？——救RNN于“水火之中”"><a href="#二、-为什么需要LSTM？——救RNN于“水火之中”" class="headerlink" title="二、 为什么需要LSTM？——救RNN于“水火之中”"></a>二、 为什么需要LSTM？——救RNN于“水火之中”</h2><p>在LSTM诞生之前，传统的RNN在反向传播时面临致命的<strong>梯度消失</strong>和<strong>梯度爆炸</strong>问题。简单来说，当序列长度超过一定限度（比如50个字），RNN的梯度会在连乘中趋近于0（消失）或变得极大（爆炸），导致模型无法更新权重，直接“罢工”。</p>
<p><strong>LSTM正是为了解决“长期依赖”而生。</strong> 它用加和的方式替代了连乘，让梯度能无损地流过细胞状态，从而捕捉到上百个时间步之前的关联。</p>]]>
    </summary>
    <title>什么是RNN？——让网络拥有“记忆”的魔法</title>
    <updated>2026-06-29T09:50:02.512Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="什么是RNN？——让网络拥有“记忆”的魔法"><a href="#什么是RNN？——让网络拥有“记忆”的魔法" class="headerlink" title="什么是RNN？——让网络拥有“记忆”的魔法"></a>什么是RNN？——让网络拥有“记忆”的魔法</h2><p>想象一下，你在读一本悬疑小说。你之所以能理解当下的情节，是因为你记得前几章发生了什么。传统的神经网络（比如全连接网络或CNN）就像金鱼一样，每次只看当下的一帧画面，对“过去”毫无概念。而<strong>循环神经网络（RNN）</strong>，就是为了打破这个魔咒而生的。</p><p><strong>RNN的核心，是一个循环结构</strong>。它允许信息在网络的“隐藏状态”中持续传递。简单来说，在当前时刻，RNN的神经元不仅接收当前的输入，还接收上一个时刻自己处理过的信息。这使得RNN拥有了一种对过去信息的“记忆”，能够捕捉数据在时间或顺序上的依赖关系。</p><p>如果我们沿着时间轴把它“展开”，会发现它像一个链条，同一个网络在每个时间步被重复使用。这也引出了它的一个重要特性：<strong>参数共享</strong>——在所有时间步上，模型的权重是相同的。这保证了网络在处理不同长度的序列时，都能用统一的方式提取特征。</p><h2 id="为什么需要RNN？——没有它，序列数据将是一盘散沙"><a href="#为什么需要RNN？——没有它，序列数据将是一盘散沙" class="headerlink" title="为什么需要RNN？——没有它，序列数据将是一盘散沙"></a>为什么需要RNN？——没有它，序列数据将是一盘散沙</h2><p>在现实生活中，充满了具有先后顺序的数据。<strong>RNN之所以被需要，正是因为传统的机器学习方法在处理这类数据时捉襟见肘。</strong></p><h3 id="1-它解决了什么问题？"><a href="#1-它解决了什么问题？" class="headerlink" title="1. 它解决了什么问题？"></a>1. 它解决了什么问题？</h3><p>RNN专门为处理<strong>序列数据</strong>而生。它可以应用在多种场景中：</p><ul><li><strong>多对一</strong>：输入一个序列，输出一个结果。比如，用一句话的情感分析判断它是好评还是差评。</li><li><strong>一对多</strong>：输入一个东西，生成一个序列。比如，用一张图片生成一句描述它的文字。</li><li><strong>多对多</strong>：输入一个序列，输出一个序列。这是最经典的应用，比如机器翻译（输入英文，输出中文）、语音识别（输入声音信号序列，输出文字序列）。</li></ul><h3 id="2-没有RNN会怎样？"><a href="#2-没有RNN会怎样？" class="headerlink" title="2. 没有RNN会怎样？"></a>2. 没有RNN会怎样？</h3><p>如果没有RNN，处理序列数据将变得非常低效和生硬。</p><span id="more"></span><ul><li><strong>信息割裂</strong>：传统模型（如DNN、CNN）通常假设输入是<strong>独立同分布</strong>的。例如，在预测一句话的最后一个词时，它只能看到这个词本身，而无法参考前面的语境。这对于理解“The clouds are in the <strong>_</strong>”这种填空还行，但对于“I grew up in France… I speak fluent <strong>_</strong>”这种需要跨越长距离依赖的句子，就完全无能为力了。</li><li><strong>特征工程繁琐</strong>：我们可以用“滑动窗口”等人工技巧将序列强行塞给传统模型，但这需要大量领域知识来设计特征，且无法捕捉长距离的复杂关系。RNN则可以通过端到端的学习，自动从原始序列中提取有用模式。</li></ul><h2 id="RNN的阿喀琉斯之踵——梯度消失与爆炸"><a href="#RNN的阿喀琉斯之踵——梯度消失与爆炸" class="headerlink" title="RNN的阿喀琉斯之踵——梯度消失与爆炸"></a>RNN的阿喀琉斯之踵——梯度消失与爆炸</h2><p>尽管RNN很强大，但它有一个致命的缺陷：<strong>难以学习到序列中的“长期依赖”关系</strong>。当输入序列很长时，早期的信息会逐渐“衰减”，直到对最终输出几乎没有影响。</p><p>这个问题的根源在于RNN的训练方式——<strong>时间步反向传播（BPTT）</strong>。在这个过程中，误差梯度需要沿着时间轴，从最后一个时间步一步步传回最开始的步。由于链式法则的连乘效应，梯度在传递过程中会指数级地<strong>缩小（梯度消失）</strong> 或<strong>放大（梯度爆炸）</strong>。</p><ul><li><strong>梯度消失</strong>：最常见的问题。想象一下，你把一个好消息通过一长串人传话，传到最后可能就变成了一个哈欠。梯度消失也是如此，导致网络前部的权重几乎不更新，无法学习到早期输入的重要性。</li><li><strong>梯度爆炸</strong>：与消失相反，梯度在传递过程中指数级增长，导致模型参数更新过大，训练过程剧烈震荡甚至无法收敛。</li></ul><p>你可能会想，为什么不直接用ReLU这类能缓解梯度消失的激活函数呢？原因在于RNN的循环结构。ReLU在RNN中虽然能缓解消失问题，却极易引发梯度爆炸，实际效果并不理想。</p><p>正是为了解决RNN的这个“硬伤”，更强大的变体——<strong>长短期记忆网络（LSTM）</strong> 和<strong>门控循环单元（GRU）</strong> 应运而生。它们通过精巧的“门控”机制，控制信息的流入与流出，有效地保护了长期记忆，成为了处理序列数据的主流工具。</p><h2 id="从单向到双向：让RNN拥有“环顾四周”的能力"><a href="#从单向到双向：让RNN拥有“环顾四周”的能力" class="headerlink" title="从单向到双向：让RNN拥有“环顾四周”的能力"></a>从单向到双向：让RNN拥有“环顾四周”的能力</h2><p>在了解了RNN的核心原理与局限之后，一个很自然的问题浮现出来：<strong>在处理序列时，我们只能利用过去的信息吗？</strong></p><p>想象一下这两个场景：</p><ul><li><strong>场景A（单向）</strong>：“我吃了一个苹果，它很甜。” 要理解后半句的“它”指代什么，只需看前面的“苹果”即可，这是<strong>正向</strong>的阅读顺序。</li><li><strong>场景B（需要上下文）</strong>：“在<em>__</em>的河边，有一座古老的寺庙。” 要填空，你不仅要看前半部分，还要看后半部分的“河边”和“寺庙”来决定空格里应该填“蜿蜒”、“宁静”还是“美丽”。</li></ul><p>场景B揭示了一个事实：在许多任务中，<strong>“未来”的信息对理解“当下”同样重要</strong>。标准RNN只能“向后看”（记住历史），却无法“环顾四周”（结合上下文）。这正是<strong>双向RNN（Bidirectional RNN, BRNN）</strong> 登场的理由。</p><h3 id="什么是双向RNN？"><a href="#什么是双向RNN？" class="headerlink" title="什么是双向RNN？"></a>什么是双向RNN？</h3><p>双向RNN的思想非常直接且优雅：它不再只沿着一个方向处理序列，而是<strong>同时从两个方向</strong>进行：</p><ol><li><strong>前向层（Forward Layer）</strong>：从左到右（从过去到未来）读取序列，捕捉“上文”信息。</li><li><strong>后向层（Backward Layer）</strong>：从右到左（从未来到过去）读取序列，捕捉“下文”信息。</li></ol><p>最终，这两个方向产生的隐藏状态会被拼接（Concatenate）在一起，作为当前时刻的最终输出。这样一来，模型在预测每个时间步的输出时，都能兼顾<strong>完整的上下文</strong>。</p><p><pre class="mermaid">graph LR<br>    subgraph 输出层<br>        Y1[Y1]<br>        Y2[Y2]<br>        Y3[Y3]<br>        Y4[Y4]<br>    end</pre></p><pre><code>subgraph 后向层    B1[B1] --&gt; B2[B2] --&gt; B3[B3] --&gt; B4[B4]endsubgraph 前向层    F1[F1] --&gt; F2[F2] --&gt; F3[F3] --&gt; F4[F4]endsubgraph 输入层    X1[X1] --&gt; X2[X2] --&gt; X3[X3] --&gt; X4[X4]endX1 --&gt; F1X2 --&gt; F2X3 --&gt; F3X4 --&gt; F4X1 --&gt; B1X2 --&gt; B2X3 --&gt; B3X4 --&gt; B4F1 --&gt; Y1B1 --&gt; Y1F2 --&gt; Y2B2 --&gt; Y2F3 --&gt; Y3B3 --&gt; Y3F4 --&gt; Y4B4 --&gt; Y4&lt;/pre&gt;</code></pre><p><em>（图示：双向RNN由前向和后向两个独立的RNN层组成，输出时合并两者的信息。）</em></p><h3 id="什么时候用双向，什么时候用单向？"><a href="#什么时候用双向，什么时候用单向？" class="headerlink" title="什么时候用双向，什么时候用单向？"></a>什么时候用双向，什么时候用单向？</h3><p>这完全取决于你的任务是否需要<strong>完整的上下文</strong>：</p><div class="table-container"><table><thead><tr><th style="text-align:left">使用场景</th><th style="text-align:left">推荐结构</th><th style="text-align:left">典型任务</th></tr></thead><tbody><tr><td style="text-align:left"><strong>任务已提供完整序列，可同时利用过去和未来信息</strong></td><td style="text-align:left"><strong>双向RNN</strong>（或其变体BiLSTM/BiGRU）</td><td style="text-align:left">文本分类、命名实体识别、机器翻译（编码器部分）、情感分析、语音识别（离线）</td></tr><tr><td style="text-align:left"><strong>流式任务，不能“偷看”未来数据</strong></td><td style="text-align:left"><strong>单向RNN</strong></td><td style="text-align:left">实时语音转写、在线翻译、金融时间序列预测、自动驾驶决策</td></tr></tbody></table></div><blockquote><p><strong>关键提醒</strong>：双向RNN在推理时必须看到完整的输入序列，这使得它<strong>不能用于在线预测</strong>，因为模型在生成第一个词时，必须知道最后一个词是什么。这是其强大的代价——更高的延迟和无法流式处理。</p></blockquote><h3 id="历史小注脚：双向RNN与LSTM的“同年之约”"><a href="#历史小注脚：双向RNN与LSTM的“同年之约”" class="headerlink" title="历史小注脚：双向RNN与LSTM的“同年之约”"></a>历史小注脚：双向RNN与LSTM的“同年之约”</h3><p>一个有趣的事实是，双向RNN（由Schuster和Paliwal于1997年提出）与LSTM（由Hochreiter和Schmidhuber于同年提出）几乎同时诞生。</p><p>但它们解决的是<strong>完全不同维度</strong>的问题：</p><ul><li><strong>LSTM</strong>是<strong>纵向</strong>的改良：解决“记性差”的问题，通过门控机制让网络能记住长距离信息，攻克了梯度消失/爆炸。</li><li><strong>双向RNN</strong>是<strong>横向</strong>的扩展：解决“视野窄”的问题，让网络能同时利用过去和未来的上下文。</li></ul><p>正因如此，这两个技术不仅不冲突，反而<strong>非常互补</strong>。2005年，Alex Graves等人将两者结合，提出了<strong>双向LSTM（BiLSTM）</strong>。这种结构既能拥有长时记忆，又能同时利用上下文信息，一度成为众多NLP任务的最强基准模型。</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p><strong>RNN让你能“向后看”（记住历史），LSTM让你能“记得更远”（延长记忆），而双向RNN则让你能“环顾四周”（结合上下文）。</strong> 当你的任务允许看到完整序列时，请毫不犹豫地尝试双向结构——它往往能带来惊喜的效果提升。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/9.RNN/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/9.RNN/"/>
    <published>2026-06-29T05:12:23.000Z</published>
    <summary>
      <![CDATA[<h2 id="什么是RNN？——让网络拥有“记忆”的魔法"><a href="#什么是RNN？——让网络拥有“记忆”的魔法" class="headerlink" title="什么是RNN？——让网络拥有“记忆”的魔法"></a>什么是RNN？——让网络拥有“记忆”的魔法</h2><p>想象一下，你在读一本悬疑小说。你之所以能理解当下的情节，是因为你记得前几章发生了什么。传统的神经网络（比如全连接网络或CNN）就像金鱼一样，每次只看当下的一帧画面，对“过去”毫无概念。而<strong>循环神经网络（RNN）</strong>，就是为了打破这个魔咒而生的。</p>
<p><strong>RNN的核心，是一个循环结构</strong>。它允许信息在网络的“隐藏状态”中持续传递。简单来说，在当前时刻，RNN的神经元不仅接收当前的输入，还接收上一个时刻自己处理过的信息。这使得RNN拥有了一种对过去信息的“记忆”，能够捕捉数据在时间或顺序上的依赖关系。</p>
<p>如果我们沿着时间轴把它“展开”，会发现它像一个链条，同一个网络在每个时间步被重复使用。这也引出了它的一个重要特性：<strong>参数共享</strong>——在所有时间步上，模型的权重是相同的。这保证了网络在处理不同长度的序列时，都能用统一的方式提取特征。</p>
<h2 id="为什么需要RNN？——没有它，序列数据将是一盘散沙"><a href="#为什么需要RNN？——没有它，序列数据将是一盘散沙" class="headerlink" title="为什么需要RNN？——没有它，序列数据将是一盘散沙"></a>为什么需要RNN？——没有它，序列数据将是一盘散沙</h2><p>在现实生活中，充满了具有先后顺序的数据。<strong>RNN之所以被需要，正是因为传统的机器学习方法在处理这类数据时捉襟见肘。</strong></p>
<h3 id="1-它解决了什么问题？"><a href="#1-它解决了什么问题？" class="headerlink" title="1. 它解决了什么问题？"></a>1. 它解决了什么问题？</h3><p>RNN专门为处理<strong>序列数据</strong>而生。它可以应用在多种场景中：</p>
<ul>
<li><strong>多对一</strong>：输入一个序列，输出一个结果。比如，用一句话的情感分析判断它是好评还是差评。</li>
<li><strong>一对多</strong>：输入一个东西，生成一个序列。比如，用一张图片生成一句描述它的文字。</li>
<li><strong>多对多</strong>：输入一个序列，输出一个序列。这是最经典的应用，比如机器翻译（输入英文，输出中文）、语音识别（输入声音信号序列，输出文字序列）。</li>
</ul>
<h3 id="2-没有RNN会怎样？"><a href="#2-没有RNN会怎样？" class="headerlink" title="2. 没有RNN会怎样？"></a>2. 没有RNN会怎样？</h3><p>如果没有RNN，处理序列数据将变得非常低效和生硬。</p>]]>
    </summary>
    <title>什么是RNN？——让网络拥有“记忆”的魔法</title>
    <updated>2026-06-29T09:49:53.024Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限"><a href="#深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限" class="headerlink" title="深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限"></a>深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限</h1><p>在深度学习的浪潮中，卷积神经网络（CNN，Convolutional Neural Network）几乎是计算机视觉的代名词。从手机相册的人脸识别，到自动驾驶的障碍物检测，CNN无处不在。</p><p>但如果你只把它当成一个“黑盒分类器”，那就太小看它了。今天我们不堆砌复杂的数学公式，而是从<strong>信号处理</strong>和<strong>设计哲学</strong>的角度，聊聊这三个核心问题：</p><ol><li>CNN到底是什么？</li><li>为什么非它不可？没有它会怎样？</li><li>它带来了哪些“副作用”和难题？</li></ol><h2 id="一、什么是CNN？它不是简单的“多层感知机”"><a href="#一、什么是CNN？它不是简单的“多层感知机”" class="headerlink" title="一、什么是CNN？它不是简单的“多层感知机”"></a>一、什么是CNN？它不是简单的“多层感知机”</h2><p>如果全连接网络是一个“事无巨细”的管家，那CNN就是一个“抓大放小”的专家。CNN的设计灵感来源于生物视觉皮层，核心在于三个关键词：<strong>局部连接</strong>、<strong>权重共享</strong>和<strong>层次化表示</strong>。</p><ul><li><strong>局部感受野</strong>：全连接网络会把图片的每个像素都连起来，参数巨大。而CNN的每个神经元只关注输入图片的一小块区域（比如 ( 3 * 3 ) 像素）。这就像用放大镜扫过图片，每次只看局部细节。</li><li><strong>权重共享</strong>：同一个卷积核（滤波器）会滑遍整张图片的每个角落。这意味着，无论一只猫出现在图片的左上角还是右下角，识别它的“眼睛”和“耳朵”的那组参数是同一个。</li><li><strong>层次化特征提取</strong>：CNN的底层负责识别边缘和颜色，中层组合成纹理和形状，高层则抽象出“脸”、“轮子”等语义信息。</li></ul><p>这种结构让CNN极其擅长处理具有<strong>网格结构</strong>的数据，特别是二维图像。</p><h2 id="二、为什么需要CNN？没有它会怎样？"><a href="#二、为什么需要CNN？没有它会怎样？" class="headerlink" title="二、为什么需要CNN？没有它会怎样？"></a>二、为什么需要CNN？没有它会怎样？</h2><p>你可能听说过，Transformer也能做图像分类。那为什么在很长一段时间里，CNN是视觉任务的首选？那是因为<strong>没有CNN，深度学习在图像领域根本无法落地</strong>。</p><h3 id="1-解决“参数灾难”"><a href="#1-解决“参数灾难”" class="headerlink" title="1. 解决“参数灾难”"></a>1. 解决“参数灾难”</h3><p>假设我们有一张 ( 224 <em> 224 ) 的彩色图片，如果用全连接网络，输入层就有 ( 224 </em> 224 <em> 3  ≈ 15  万个神经元），隐藏层哪怕只有1000个神经元，参数量也是千万级的。这极易导致<em>*过拟合</em></em>，且显存根本装不下。</p><p><strong>CNN的解法</strong>：通过权重共享，同样的任务参数量可以降至数百万，甚至几十万。它把“无限可能”压缩成了“有限模式”，让模型在数据量有限的情况下变得可训练。</p><h3 id="2-解决“语义鸿沟”"><a href="#2-解决“语义鸿沟”" class="headerlink" title="2. 解决“语义鸿沟”"></a>2. 解决“语义鸿沟”</h3><p>在CNN出现之前，人们靠手工特征（如SIFT、HOG）来识别物体。这些特征依赖于工程师的经验，泛化能力很差。</p><p><strong>CNN的解法</strong>：它是端到端的，直接从像素中学习最优特征。它能捕捉到人类语言难以描述的复杂纹理和模式，准确率远超传统视觉算法。</p><span id="more"></span><h3 id="3-如果不用CNN，会怎样？"><a href="#3-如果不用CNN，会怎样？" class="headerlink" title="3. 如果不用CNN，会怎样？"></a>3. 如果不用CNN，会怎样？</h3><p>你可以试试把图片打平成向量，丢进全连接网络。结果往往是：<strong>训练集准确率很高，测试集一塌糊涂</strong>。</p><p>即便换成目前最火的ViT（Vision Transformer），如果没有CNN提供的“<strong>空间归纳偏置</strong>”（即“相邻像素有关系”的先验知识），Transformer在中小型数据集（如ImageNet-1K）上的表现往往不如同等体量的CNN。直到谷歌用3亿张私有大图（JFT-300M）去喂，ViT才勉强超越ResNet。这恰恰证明了CNN在数据效率上的巨大成功。</p><h2 id="三、成也萧何，败也萧何：CNN会导致哪些问题？"><a href="#三、成也萧何，败也萧何：CNN会导致哪些问题？" class="headerlink" title="三、成也萧何，败也萧何：CNN会导致哪些问题？"></a>三、成也萧何，败也萧何：CNN会导致哪些问题？</h2><p>任何算法都有命门。CNN之所以强大，是因为它假设“空间局部性”；而它之所以有瓶颈，也是因为太过依赖这个假设。</p><h3 id="1-缺乏全局视野（感受野受限）"><a href="#1-缺乏全局视野（感受野受限）" class="headerlink" title="1. 缺乏全局视野（感受野受限）"></a>1. 缺乏全局视野（感受野受限）</h3><p>CNN本质上是“管中窥豹”。虽然通过加深网络可以扩大感受野，但它天生不擅长捕捉<strong>长距离依赖</strong>。</p><ul><li><strong>表现</strong>：在人体姿态估计中，CNN可能会把左手识别成右手，因为它只盯着局部关节，没有理解“左肩和左手”的全局逻辑关系。</li></ul><h3 id="2-对几何变换极其敏感"><a href="#2-对几何变换极其敏感" class="headerlink" title="2. 对几何变换极其敏感"></a>2. 对几何变换极其敏感</h3><p>CNN对平移比较鲁棒（不管猫在哪都能找到），但对<strong>旋转、缩放</strong>非常脆弱。比如，一张正常朝向的人脸能识别，但倒立的人脸CNN很可能就认不出来了。</p><ul><li><strong>解法</strong>：我们只能通过数据增强（随机裁剪、旋转）来硬补，但这种“打补丁”的方式治标不治本，因为卷积核本身并不具备旋转不变性的数学能力。</li></ul><h3 id="3-纹理偏差（可解释性陷阱）"><a href="#3-纹理偏差（可解释性陷阱）" class="headerlink" title="3. 纹理偏差（可解释性陷阱）"></a>3. 纹理偏差（可解释性陷阱）</h3><p>CNN依赖纹理和颜色做判断，而不是形状。经典案例是：如果给一张大象的图片覆盖上沙发的纹理，CNN会坚定地认为那是“沙发”。</p><ul><li><strong>后果</strong>：这种<strong>纹理偏差</strong>导致CNN在分布外（OOD）数据上泛化能力骤降。遇到对抗样本攻击时，往往只需要改变几个像素，就能让CNN把熊猫误认为长臂猿。</li></ul><h3 id="4-显存杀手"><a href="#4-显存杀手" class="headerlink" title="4. 显存杀手"></a>4. 显存杀手</h3><p>虽然CNN<strong>参数少</strong>，但它<strong>中间特征图</strong>极大。在进行高分辨率（如遥感、4K医疗影像）处理时，3D CNN甚至能把顶级A100显卡直接跑崩。参数少不代表计算量少，这是初学者最容易误解的地方。</p><h3 id="5-结构适应性差"><a href="#5-结构适应性差" class="headerlink" title="5. 结构适应性差"></a>5. 结构适应性差</h3><p>CNN的归纳偏置（局部性）非常强，这使得它在图像上如鱼得水，但在非欧空间（如分子结构、社交网络图数据）中则完全失效。</p><h2 id="四、打破天花板：多模态大模型中的视觉编码演进"><a href="#四、打破天花板：多模态大模型中的视觉编码演进" class="headerlink" title="四、打破天花板：多模态大模型中的视觉编码演进"></a>四、打破天花板：多模态大模型中的视觉编码演进</h2><p>CNN的“局部视野”是其核心瓶颈。在图像分类任务中，这个缺陷可以通过加深网络来勉强弥补，但在更复杂的<strong>多模态理解</strong>场景中（比如让AI看图写故事、回答“图片里左边那个人和右边那只狗是什么关系？”），这种“管中窥豹”的方式就捉襟见肘了。</p><p>于是，随着大语言模型（LLM）的爆发，多模态大模型（如GPT-4V、CLIP、LLaVA等）在视觉编码器的选择上，经历了一次明显的范式转移。</p><h3 id="1-经典方案：CNN仍然是坚实的“地基”"><a href="#1-经典方案：CNN仍然是坚实的“地基”" class="headerlink" title="1. 经典方案：CNN仍然是坚实的“地基”"></a>1. 经典方案：CNN仍然是坚实的“地基”</h3><p>在多模态模型的早期探索中，CNN凭借其<strong>高效、成熟、参数少</strong>的优势，自然成为了视觉编码器的首选。</p><ul><li><strong>典型代表</strong>：OpenAI的CLIP模型在训练时，就明确提供了两种视觉编码器选项——<strong>ResNet（CNN系）</strong> 和 <strong>ViT（Vision Transformer）</strong>，允许用户根据需求选择。这表明CNN在当时依然是值得信赖的主力军。</li><li><strong>其工作原理</strong>：CNN负责将原始的像素矩阵压缩成富含语义信息的<strong>特征图</strong>（Feature Map），然后将这些特征“喂”给后端的语言模型进行跨模态对齐。这个过程中，CNN扮演了“视觉翻译官”的角色，把图像翻译成语言模型能听懂的特征向量。</li></ul><h3 id="2-当前主流：ViT（视觉Transformer）成为新宠"><a href="#2-当前主流：ViT（视觉Transformer）成为新宠" class="headerlink" title="2. 当前主流：ViT（视觉Transformer）成为新宠"></a>2. 当前主流：ViT（视觉Transformer）成为新宠</h3><p>随着多模态任务越来越复杂（比如需要理解图片中的讽刺幽默、物理关系），研究者们发现CNN的“局部性”先验反而成了一种束缚。它们希望模型能直接看到全局，而不是靠层层堆叠去“拼凑”全局。</p><ul><li><strong>ViT的崛起</strong>：ViT将图像切成固定大小的Patch（块），然后像处理单词序列一样处理这些图像块。通过自注意力机制，<strong>每个图像块都能直接与所有其他块进行信息交互</strong>，一步到位获得全局感受野。</li><li><strong>代表性案例</strong>：现如今，顶尖的多模态大模型，如<strong>LLaVA（视觉指令微调的代表）</strong>、<strong>Flamingo（DeepMind的少样本学习模型）</strong>，其视觉端普遍基于ViT或其变体。它们在处理复杂场景问答、指代理解等任务上，展现出了超越传统CNN的性能。</li></ul><h3 id="3-前沿探索：混合架构（CNN-ViT）——两手都要硬"><a href="#3-前沿探索：混合架构（CNN-ViT）——两手都要硬" class="headerlink" title="3. 前沿探索：混合架构（CNN + ViT）——两手都要硬"></a>3. 前沿探索：混合架构（CNN + ViT）——两手都要硬</h3><p>既然ViT全局视野好，但数据效率低；CNN局部感知强，但视野受限，那有没有办法兼得？这正是当前学术界最热门的探索方向之一。</p><ul><li><strong>具体做法</strong>：在ViT的前端嵌入卷积层（如使用卷积进行Patch Embedding），或者在自注意力模块中引入卷积的归纳偏置。代表模型如<strong>CvT（卷积视觉Transformer）</strong>，它在分词阶段就利用卷积提取局部纹理，让后续的Transformer能够站在“更高层次”的语义上进行全局推理，既保留了CNN的高效性，又弥补了Transformer在局部细节捕捉上的不足。</li></ul><h3 id="总结：没有“谁淘汰谁”，只有“谁更合适”"><a href="#总结：没有“谁淘汰谁”，只有“谁更合适”" class="headerlink" title="总结：没有“谁淘汰谁”，只有“谁更合适”"></a>总结：没有“谁淘汰谁”，只有“谁更合适”</h3><p>多模态大模型的实践告诉我们，CNN并没有被淘汰，而是退居到与ViT互补的位置：</p><ul><li>如果你的任务是<strong>高分辨率遥感</strong>或<strong>医学影像分割</strong>，CNN依然是最佳选择。</li><li>如果你的任务是<strong>开放式多模态问答</strong>或<strong>图像描述生成</strong>，ViT或混合架构更适合。</li></ul><p>这种“取长补短”的演进，恰恰体现了深度学习架构设计的美妙之处——<strong>没有万能的算法，只有最适合当前数据与任务的组合</strong>。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/8.CNN/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/8.CNN/"/>
    <published>2026-06-29T02:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限"><a href="#深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限" class="headerlink" title="深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限"></a>深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限</h1><p>在深度学习的浪潮中，卷积神经网络（CNN，Convolutional Neural Network）几乎是计算机视觉的代名词。从手机相册的人脸识别，到自动驾驶的障碍物检测，CNN无处不在。</p>
<p>但如果你只把它当成一个“黑盒分类器”，那就太小看它了。今天我们不堆砌复杂的数学公式，而是从<strong>信号处理</strong>和<strong>设计哲学</strong>的角度，聊聊这三个核心问题：</p>
<ol>
<li>CNN到底是什么？</li>
<li>为什么非它不可？没有它会怎样？</li>
<li>它带来了哪些“副作用”和难题？</li>
</ol>
<h2 id="一、什么是CNN？它不是简单的“多层感知机”"><a href="#一、什么是CNN？它不是简单的“多层感知机”" class="headerlink" title="一、什么是CNN？它不是简单的“多层感知机”"></a>一、什么是CNN？它不是简单的“多层感知机”</h2><p>如果全连接网络是一个“事无巨细”的管家，那CNN就是一个“抓大放小”的专家。CNN的设计灵感来源于生物视觉皮层，核心在于三个关键词：<strong>局部连接</strong>、<strong>权重共享</strong>和<strong>层次化表示</strong>。</p>
<ul>
<li><strong>局部感受野</strong>：全连接网络会把图片的每个像素都连起来，参数巨大。而CNN的每个神经元只关注输入图片的一小块区域（比如 ( 3 * 3 ) 像素）。这就像用放大镜扫过图片，每次只看局部细节。</li>
<li><strong>权重共享</strong>：同一个卷积核（滤波器）会滑遍整张图片的每个角落。这意味着，无论一只猫出现在图片的左上角还是右下角，识别它的“眼睛”和“耳朵”的那组参数是同一个。</li>
<li><strong>层次化特征提取</strong>：CNN的底层负责识别边缘和颜色，中层组合成纹理和形状，高层则抽象出“脸”、“轮子”等语义信息。</li>
</ul>
<p>这种结构让CNN极其擅长处理具有<strong>网格结构</strong>的数据，特别是二维图像。</p>
<h2 id="二、为什么需要CNN？没有它会怎样？"><a href="#二、为什么需要CNN？没有它会怎样？" class="headerlink" title="二、为什么需要CNN？没有它会怎样？"></a>二、为什么需要CNN？没有它会怎样？</h2><p>你可能听说过，Transformer也能做图像分类。那为什么在很长一段时间里，CNN是视觉任务的首选？那是因为<strong>没有CNN，深度学习在图像领域根本无法落地</strong>。</p>
<h3 id="1-解决“参数灾难”"><a href="#1-解决“参数灾难”" class="headerlink" title="1. 解决“参数灾难”"></a>1. 解决“参数灾难”</h3><p>假设我们有一张 ( 224 <em> 224 ) 的彩色图片，如果用全连接网络，输入层就有 ( 224 </em> 224 <em> 3  ≈ 15  万个神经元），隐藏层哪怕只有1000个神经元，参数量也是千万级的。这极易导致<em>*过拟合</em></em>，且显存根本装不下。</p>
<p><strong>CNN的解法</strong>：通过权重共享，同样的任务参数量可以降至数百万，甚至几十万。它把“无限可能”压缩成了“有限模式”，让模型在数据量有限的情况下变得可训练。</p>
<h3 id="2-解决“语义鸿沟”"><a href="#2-解决“语义鸿沟”" class="headerlink" title="2. 解决“语义鸿沟”"></a>2. 解决“语义鸿沟”</h3><p>在CNN出现之前，人们靠手工特征（如SIFT、HOG）来识别物体。这些特征依赖于工程师的经验，泛化能力很差。</p>
<p><strong>CNN的解法</strong>：它是端到端的，直接从像素中学习最优特征。它能捕捉到人类语言难以描述的复杂纹理和模式，准确率远超传统视觉算法。</p>]]>
    </summary>
    <title>深度学习的“视觉基因”：一文搞懂CNN的前世、今生与局限</title>
    <updated>2026-06-30T06:26:35.456Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h2 id="大模型中的精度划分"><a href="#大模型中的精度划分" class="headerlink" title="大模型中的精度划分"></a>大模型中的精度划分</h2><h3 id="1-传统双精度与单精度（FP64-amp-FP32）"><a href="#1-传统双精度与单精度（FP64-amp-FP32）" class="headerlink" title="1. 传统双精度与单精度（FP64 &amp; FP32）"></a>1. 传统双精度与单精度（FP64 &amp; FP32）</h3><ul><li><strong>结构</strong>：<ul><li><strong>FP32（单精度）</strong>：1位符号位 + 8位指数位 + 23位尾数位（共32位）</li><li><strong>FP64（双精度）</strong>：1位符号位 + 11位指数位 + 52位尾数位（共64位）</li></ul></li><li><strong>特点</strong>：数值范围广，精度极高。</li><li><strong>用途</strong>：<strong>大模型预训练</strong>中的反向传播梯度累积、优化器状态（如Adam）通常必须使用FP32/FP32主权重，以保证数值稳定性，防止梯度下溢。</li></ul><h3 id="2-半精度与脑浮点（FP16-amp-BF16）"><a href="#2-半精度与脑浮点（FP16-amp-BF16）" class="headerlink" title="2. 半精度与脑浮点（FP16 &amp; BF16）"></a>2. 半精度与脑浮点（FP16 &amp; BF16）</h3><ul><li><strong>结构</strong>：<ul><li><strong>FP16</strong>：1+5+10（共16位）。指数位少，尾数位中等。</li><li><strong>BF16（Brain Float 16）</strong>：1+8+7（共16位）。指数位与FP32相同，尾数位缩减。</li></ul></li><li><strong>特点对比</strong>：<ul><li><strong>FP16</strong>：<strong>精度高但范围窄</strong>，容易溢出（上溢/下溢），训练时需要配合动态损失缩放（Loss Scaling）。</li><li><strong>BF16</strong>：<strong>范围广但精度稍低</strong>，能直接覆盖FP32的数值范围，不易溢出，且硬件（如英伟达Ampere架构及后续、Intel CPU）支持良好。</li></ul></li><li><strong>用途</strong>：<strong>大模型的训练前向传播和激活值</strong>、<strong>微调（LoRA等）</strong>和<strong>推理加速</strong>。BF16现已成为大模型训练的主流选择。</li></ul><h3 id="3-新兴的8位浮点数（FP8）"><a href="#3-新兴的8位浮点数（FP8）" class="headerlink" title="3. 新兴的8位浮点数（FP8）"></a>3. 新兴的8位浮点数（FP8）</h3><p>你图片中列出了三种变体，这是目前最前沿的推理和轻量级训练技术：</p><ul><li><strong>E4M3（4位指数，3位尾数）</strong>：<ul><li>精度更高（尾数多1位），动态范围较小。</li><li><strong>常用于</strong>：<strong>权重和激活值</strong>的前向传播（需要精细数值）。</li></ul></li><li><strong>E5M2（5位指数，2位尾数）</strong>：<ul><li>动态范围更大（能表示更大的数），但精度较低（尾数少1位），存在特殊值（Inf/NaN）。</li><li><strong>常用于</strong>：<strong>梯度</strong>的反向传播（需要防止梯度爆炸）。</li></ul></li><li><strong>UE8M0（无符号8位，无指数/尾数）</strong>：<ul><li>实际上就是<strong>整数格式</strong>（范围0-255）。</li><li><strong>常用于</strong>：大模型中的<strong>KV Cache（键值缓存）量化</strong>，用于在长上下文场景下极大幅度节省显存。</li></ul></li></ul><hr><h3 id="终极总结（核心结论）"><a href="#终极总结（核心结论）" class="headerlink" title="终极总结（核心结论）"></a>终极总结（核心结论）</h3><div class="table-container"><table><thead><tr><th style="text-align:left">精度格式</th><th style="text-align:left">适用场景</th><th style="text-align:left">核心地位</th></tr></thead><tbody><tr><td style="text-align:left"><strong>FP32</strong></td><td style="text-align:left">主权重、优化器状态（Adam）、损失缩放</td><td style="text-align:left">训练的“定海神针”，保证精度</td></tr><tr><td style="text-align:left"><strong>FP16</strong></td><td style="text-align:left">早期训练前向、推理</td><td style="text-align:left">因易溢出，正被BF16取代</td></tr><tr><td style="text-align:left"><strong>BF16</strong></td><td style="text-align:left"><strong>现阶段大模型训练</strong>（前向+反向）、微调</td><td style="text-align:left"><strong>当前主流黄金标准</strong>（平衡了范围和硬件效率）</td></tr><tr><td style="text-align:left"><strong>FP8</strong></td><td style="text-align:left">最新架构（如H100）的加速训练、极速推理</td><td style="text-align:left"><strong>下一代趋势</strong>，大幅降低显存和带宽</td></tr><tr><td style="text-align:left"><strong>UE8M0</strong></td><td style="text-align:left">KV Cache量化、输入值存储</td><td style="text-align:left">专门为<strong>超长上下文（百万token）</strong> 优化，节省显存</td></tr></tbody></table></div>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/7.%E7%B2%BE%E5%BA%A6/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/7.%E7%B2%BE%E5%BA%A6/"/>
    <published>2026-06-16T11:55:23.000Z</published>
    <summary>
      <![CDATA[<h2 id="大模型中的精度划分"><a href="#大模型中的精度划分" class="headerlink" title="大模型中的精度划分"></a>大模型中的精度划分</h2><h3 id="1-传统双精度与单精度（FP64-amp-FP32）"><a href="]]>
    </summary>
    <title>精度</title>
    <updated>2026-06-29T10:56:27.288Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="Transformer-架构三剑客：Encoder、Decoder-与-Encoder-Decoder-的区别与应用"><a href="#Transformer-架构三剑客：Encoder、Decoder-与-Encoder-Decoder-的区别与应用" class="headerlink" title="Transformer 架构三剑客：Encoder、Decoder 与 Encoder-Decoder 的区别与应用"></a>Transformer 架构三剑客：Encoder、Decoder 与 Encoder-Decoder 的区别与应用</h1><p>在深度学习自然语言处理（NLP）领域，Transformer 无疑是皇冠上的明珠。但<strong>Transformer 并不是只有一种形态</strong>。</p><p>根据任务需求的不同，它演化出了三个各具特色的分支。今天我们来拆解 Transformer 家族的三大主力：<strong>仅编码器（Encoder-only）</strong>、<strong>仅解码器（Decoder-only）</strong> 和 <strong>编解码器（Encoder-Decoder）</strong>。</p><blockquote><p> <strong>一句话总结</strong>：</p><ul><li><strong>Encoder-only</strong> 是“阅读理解大师”；</li><li><strong>Decoder-only</strong> 是“即兴演讲天才”；</li><li><strong>Encoder-Decoder</strong> 则是“同声传译专家”。</li></ul></blockquote><hr><h2 id="一、架构总览：一张图看懂三剑客"><a href="#一、架构总览：一张图看懂三剑客" class="headerlink" title="一、架构总览：一张图看懂三剑客"></a>一、架构总览：一张图看懂三剑客</h2><p>为了直观展示它们的区别，我们先看一张架构图：</p><pre class="mermaid">graph TB    subgraph "仅编码器 (Encoder-only)"        E_IN[输入序列] --> E_ENC["编码器堆叠<br>(双向关注)"]        E_ENC --> E_OUT[特征向量/分类结果]    end    subgraph "仅解码器 (Decoder-only)"        D_IN[输入序列] --> D_DEC["解码器堆叠<br>(因果掩码/单向)"]        D_DEC --> D_OUT[输出序列]    end    subgraph "编解码器 (Encoder-Decoder)"        ED_IN[源序列] --> ED_ENC[编码器]        ED_ENC --> ED_CROSS[交叉注意力]        ED_DEC_IN[目标序列] --> ED_DEC[解码器]        ED_CROSS --> ED_DEC        ED_DEC --> ED_OUT[目标序列]    end</pre><div class="table-container"><table><thead><tr><th style="text-align:left">架构</th><th style="text-align:left">核心公式</th><th style="text-align:left">训练方式</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Encoder-only</strong></td><td style="text-align:left">H=Encoder(X)<em>H</em>=Encoder(<em>X</em>)</td><td style="text-align:left">掩码语言模型（MLM）</td></tr><tr><td style="text-align:left"><strong>Decoder-only</strong></td><td style="text-align:left">P(yt∥y&lt;t,X)<em>P</em>(<em>y**t</em>∥<em>y</em>&lt;<em>t</em>,<em>X</em>)</td><td style="text-align:left">自回归语言模型（CLM）</td></tr><tr><td style="text-align:left"><strong>Encoder-Decoder</strong></td><td style="text-align:left">H=Encoder(X);Y=Decoder(H,Y&lt;t)<em>H</em>=Encoder(<em>X</em>);<em>Y</em>=Decoder(<em>H</em>,<em>Y</em>&lt;<em>t</em>)</td><td style="text-align:left">去噪自编码器 / Seq2Seq</td></tr></tbody></table></div><h3 id="核心特点"><a href="#核心特点" class="headerlink" title="核心特点"></a>核心特点</h3><ul><li><strong>双向注意力机制（Bidirectional Attention）</strong>：这是它的杀手锏。在处理句子中的某个词时，它能同时看到左边和右边的所有词。就像你阅读时，会结合上下文来理解一个多义词的含义。</li><li><strong>擅长“读”而非“写”</strong>：它的强项在于把一段文字压缩成高质量的向量表示（Embedding），从中提取特征。它是天生的“分析型”选手。</li></ul><h3 id="典型应用"><a href="#典型应用" class="headerlink" title="典型应用"></a>典型应用</h3><span id="more"></span><div class="table-container"><table><thead><tr><th>任务</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>文本分类</strong></td><td>判断文本的类别或情感</td><td>垃圾邮件检测、电商评论情感分析</td></tr><tr><td><strong>命名实体识别</strong></td><td>从文本中抽取特定实体</td><td>找出新闻里的人名、地名、公司名</td></tr><tr><td><strong>搜索排序</strong></td><td>理解查询和文档的相关性</td><td>Google/Bing 搜索的语义匹配</td></tr></tbody></table></div><blockquote><p><strong>选型建议</strong>：当你需要让机器“读懂”一段文字，做分类、打分或提取信息时，选它准没错。</p></blockquote><hr><h2 id="三、仅解码器（Decoder-only）：生成的天才"><a href="#三、仅解码器（Decoder-only）：生成的天才" class="headerlink" title="三、仅解码器（Decoder-only）：生成的天才"></a>三、仅解码器（Decoder-only）：生成的天才</h2><blockquote><p><strong>核心定位</strong>：擅长“创作”新内容，是目前大语言模型的绝对主流。</p></blockquote><h3 id="核心特点-1"><a href="#核心特点-1" class="headerlink" title="核心特点"></a>核心特点</h3><ul><li><strong>自回归生成（Auto-regressive）</strong>：就像人说话一样，它是一个词一个词往外“蹦”的。预测下一个词时，只能看到前面已经生成的词，不能“偷看”未来——这被称为<strong>因果掩码（Causal Masking）</strong>。</li><li><strong>擅长“写”而非“读”</strong>：它是目前的“当红炸子鸡”，具备强大的逻辑推理、文本续写和对话能力。从 ChatGPT 到 Claude，背后都是 Decoder-only 架构。</li></ul><p><strong>解码过程</strong>：</p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">输入: [<span class="string">&quot;我&quot;</span>, <span class="string">&quot;爱&quot;</span>]</span><br><span class="line">   ↓</span><br><span class="line">模型预测下一个词 → <span class="string">&quot;你&quot;</span></span><br><span class="line">   ↓</span><br><span class="line">新输入: [<span class="string">&quot;我&quot;</span>, <span class="string">&quot;爱&quot;</span>, <span class="string">&quot;你&quot;</span>]</span><br><span class="line">   ↓</span><br><span class="line">模型预测下一个词 → <span class="string">&quot;。&quot;</span> / 停止</span><br></pre></td></tr></table></figure><h3 id="典型应用-1"><a href="#典型应用-1" class="headerlink" title="典型应用"></a>典型应用</h3><div class="table-container"><table><thead><tr><th>任务</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>对话系统</strong></td><td>多轮交互、指令遵循</td><td>ChatGPT, DeepSeek, Claude</td></tr><tr><td><strong>代码生成</strong></td><td>根据自然语言生成代码</td><td>GitHub Copilot</td></tr><tr><td><strong>创意写作</strong></td><td>续写、润色、扩写</td><td>写邮件、写小说、写周报</td></tr></tbody></table></div><blockquote><p><strong>选型建议</strong>：当你需要机器“写出”新内容，或者进行多轮对话时，它是当前的最优解。</p></blockquote><hr><h2 id="四、编解码器（Encoder-Decoder）：转换的桥梁"><a href="#四、编解码器（Encoder-Decoder）：转换的桥梁" class="headerlink" title="四、编解码器（Encoder-Decoder）：转换的桥梁"></a>四、编解码器（Encoder-Decoder）：转换的桥梁</h2><blockquote><p><strong>核心定位</strong>：先“读懂”输入，再“生成”输出——是理解和生成的完美结合。</p></blockquote><h3 id="核心特点-2"><a href="#核心特点-2" class="headerlink" title="核心特点"></a>核心特点</h3><ul><li><strong>强强联合</strong>：先用 Encoder 充分“读懂”源文本（获得双向语义表示），再将这个信息传递给 Decoder，让 Decoder 逐词“翻译”或“改写”成目标文本。</li><li><strong>代价</strong>：它有两套庞大的网络堆叠，<strong>参数量大、训练成本高、推理速度相对较慢</strong>。这也是为什么当前主流大模型多选择 Decoder-only 的原因之一——在效果差距不大的情况下，效率更高。</li></ul><p><strong>内部交互</strong>：</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">源语言<span class="punctuation">:</span> <span class="string">&quot;Hello world&quot;</span>   →   Encoder（双向阅读）</span><br><span class="line">                                    ↓</span><br><span class="line">                          Encoder → Decoder 的交叉注意力</span><br><span class="line">                                    ↓</span><br><span class="line">目标语言<span class="punctuation">:</span> <span class="string">&quot;你好 世界&quot;</span>   ←   Decoder（自回归生成）</span><br></pre></td></tr></table></figure><h3 id="典型应用-2"><a href="#典型应用-2" class="headerlink" title="典型应用"></a>典型应用</h3><div class="table-container"><table><thead><tr><th>任务</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>机器翻译</strong></td><td>中译英、英译中等</td><td>Google Translate</td></tr><tr><td><strong>文本摘要</strong></td><td>长文档 → 短摘要</td><td>新闻摘要、论文摘要</td></tr><tr><td><strong>语法纠错</strong></td><td>错误句子 → 正确句子</td><td>Grammarly</td></tr></tbody></table></div><blockquote><p><strong>选型建议</strong>：当你需要将一种结构化的输入转换为另一种结构化的输出（且两者差异较大），且对推理速度不极端敏感时，它是最经典的选择。</p></blockquote><hr><h2 id="五、演进脉络：为什么-Decoder-only-成了主角？"><a href="#五、演进脉络：为什么-Decoder-only-成了主角？" class="headerlink" title="五、演进脉络：为什么 Decoder-only 成了主角？"></a>五、演进脉络：为什么 Decoder-only 成了主角？</h2><pre class="mermaid">gantt    title Transformer 架构的演进历程    dateFormat YYYY    axisFormat %Y年    section 基础与理解        原始 Transformer (Encoder-Decoder) :done, t1, 2017, 2018        BERT (Encoder-only) 统治理解任务 :done, t2, 2018, 2020    section 生成与大模型时代        GPT 系列开启大模型时代 :active, t3, 2018, 2022        Decoder-only 成为绝对主流 (ChatGPT引爆) :t4, 2022, 2026</pre><p><strong>为什么 Decoder-only 后来居上？</strong></p><ol><li><strong>能力全面</strong>：优秀的 Decoder-only 模型（如 GPT-4）不仅能生成，通过上下文学习也能很好地完成理解类任务（分类、抽取等）。</li><li><strong>训练更简单</strong>：自回归语言模型可以直接在海量无标注文本上训练，不需要复杂的数据构造。</li><li><strong>扩展性强</strong>：更容易扩展到超大规模（千亿、万亿参数）。</li></ol><hr><h2 id="六、核心记忆卡"><a href="#六、核心记忆卡" class="headerlink" title="六、核心记忆卡"></a>六、核心记忆卡</h2><p>最后，送给大家一张核心记忆卡，帮你快速区分它们：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│                    Transformer 三剑客                        │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                             │</span><br><span class="line">│   Encoder-only        Decoder-only      Encoder-Decoder│</span><br><span class="line">│   &quot;我读懂了你&quot;           &quot;我会接着说&quot;         &quot;我听懂了，帮你转述&quot;│</span><br><span class="line">│                                                             │</span><br><span class="line">│   代表：BERT              代表：GPT           代表：T5       │</span><br><span class="line">│   任务：分类、NER         任务：对话、写作     任务：翻译、摘要│</span><br><span class="line">│   ↓                        ↓                   ↓            │</span><br><span class="line">│   双向注意力              因果注意力           编码器双向      │</span><br><span class="line">│                         (自回归生成)          解码器单向      │</span><br><span class="line">│                                                             │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/5.Transformer%20%E6%9E%B6%E6%9E%84%E4%B8%89%E5%89%91%E5%AE%A2%EF%BC%9AEncoder%E3%80%81Decoder%20%E4%B8%8E%20Encoder-Decoder%20%E7%9A%84%E5%8C%BA%E5%88%AB%E4%B8%8E%E5%BA%94%E7%94%A8/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/5.Transformer%20%E6%9E%B6%E6%9E%84%E4%B8%89%E5%89%91%E5%AE%A2%EF%BC%9AEncoder%E3%80%81Decoder%20%E4%B8%8E%20Encoder-Decoder%20%E7%9A%84%E5%8C%BA%E5%88%AB%E4%B8%8E%E5%BA%94%E7%94%A8/"/>
    <published>2026-06-06T11:55:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="Transformer-架构三剑客：Encoder、Decoder-与-Encoder-Decoder-的区别与应用"><a href="#Transformer-架构三剑客：Encoder、Decoder-与-Encoder-Decoder-的区别与应用" class="headerlink" title="Transformer 架构三剑客：Encoder、Decoder 与 Encoder-Decoder 的区别与应用"></a>Transformer 架构三剑客：Encoder、Decoder 与 Encoder-Decoder 的区别与应用</h1><p>在深度学习自然语言处理（NLP）领域，Transformer 无疑是皇冠上的明珠。但<strong>Transformer 并不是只有一种形态</strong>。</p>
<p>根据任务需求的不同，它演化出了三个各具特色的分支。今天我们来拆解 Transformer 家族的三大主力：<strong>仅编码器（Encoder-only）</strong>、<strong>仅解码器（Decoder-only）</strong> 和 <strong>编解码器（Encoder-Decoder）</strong>。</p>
<blockquote>
<p> <strong>一句话总结</strong>：</p>
<ul>
<li><strong>Encoder-only</strong> 是“阅读理解大师”；</li>
<li><strong>Decoder-only</strong> 是“即兴演讲天才”；</li>
<li><strong>Encoder-Decoder</strong> 则是“同声传译专家”。</li>
</ul>
</blockquote>
<hr>
<h2 id="一、架构总览：一张图看懂三剑客"><a href="#一、架构总览：一张图看懂三剑客" class="headerlink" title="一、架构总览：一张图看懂三剑客"></a>一、架构总览：一张图看懂三剑客</h2><p>为了直观展示它们的区别，我们先看一张架构图：</p>
<pre class="mermaid">graph TB
    subgraph "仅编码器 (Encoder-only)"
        E_IN[输入序列] --> E_ENC["编码器堆叠<br>(双向关注)"]
        E_ENC --> E_OUT[特征向量/分类结果]
    end

    subgraph "仅解码器 (Decoder-only)"
        D_IN[输入序列] --> D_DEC["解码器堆叠<br>(因果掩码/单向)"]
        D_DEC --> D_OUT[输出序列]
    end

    subgraph "编解码器 (Encoder-Decoder)"
        ED_IN[源序列] --> ED_ENC[编码器]
        ED_ENC --> ED_CROSS[交叉注意力]
        ED_DEC_IN[目标序列] --> ED_DEC[解码器]
        ED_CROSS --> ED_DEC
        ED_DEC --> ED_OUT[目标序列]
    end</pre>

<div class="table-container">
<table>
<thead>
<tr>
<th style="text-align:left">架构</th>
<th style="text-align:left">核心公式</th>
<th style="text-align:left">训练方式</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>Encoder-only</strong></td>
<td style="text-align:left">H=Encoder(X)<em>H</em>=Encoder(<em>X</em>)</td>
<td style="text-align:left">掩码语言模型（MLM）</td>
</tr>
<tr>
<td style="text-align:left"><strong>Decoder-only</strong></td>
<td style="text-align:left">P(yt∥y&lt;t,X)<em>P</em>(<em>y**t</em>∥<em>y</em>&lt;<em>t</em>,<em>X</em>)</td>
<td style="text-align:left">自回归语言模型（CLM）</td>
</tr>
<tr>
<td style="text-align:left"><strong>Encoder-Decoder</strong></td>
<td style="text-align:left">H=Encoder(X);Y=Decoder(H,Y&lt;t)<em>H</em>=Encoder(<em>X</em>);<em>Y</em>=Decoder(<em>H</em>,<em>Y</em>&lt;<em>t</em>)</td>
<td style="text-align:left">去噪自编码器 / Seq2Seq</td>
</tr>
</tbody>
</table>
</div>
<h3 id="核心特点"><a href="#核心特点" class="headerlink" title="核心特点"></a>核心特点</h3><ul>
<li><strong>双向注意力机制（Bidirectional Attention）</strong>：这是它的杀手锏。在处理句子中的某个词时，它能同时看到左边和右边的所有词。就像你阅读时，会结合上下文来理解一个多义词的含义。</li>
<li><strong>擅长“读”而非“写”</strong>：它的强项在于把一段文字压缩成高质量的向量表示（Embedding），从中提取特征。它是天生的“分析型”选手。</li>
</ul>
<h3 id="典型应用"><a href="#典型应用" class="headerlink" title="典型应用"></a>典型应用</h3>]]>
    </summary>
    <title>Transformer 架构三剑客：Encoder、Decoder 与 Encoder-Decoder 的区别与应用</title>
    <updated>2026-06-29T08:30:30.802Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="知识蒸馏：让小模型拥有大模型的“智慧”"><a href="#知识蒸馏：让小模型拥有大模型的“智慧”" class="headerlink" title="知识蒸馏：让小模型拥有大模型的“智慧”"></a>知识蒸馏：让小模型拥有大模型的“智慧”</h1><p>在人工智能飞速发展的今天，我们见证了参数量动辄百亿、千亿的“巨无霸”模型诞生。它们极其聪明，但代价是高昂的计算成本和漫长的推理延迟。如果想在手机或智能手表上运行这些大模型，显然不切实际。</p><p>有没有一种方法，既能保留大模型的强大能力，又能享受小模型的轻量与高效？</p><p>答案就是：<strong>知识蒸馏（Knowledge Distillation）</strong>。</p><hr><h2 id="一、什么是知识蒸馏？"><a href="#一、什么是知识蒸馏？" class="headerlink" title="一、什么是知识蒸馏？"></a>一、什么是知识蒸馏？</h2><p>简单来说，知识蒸馏是一种<strong>模型压缩和加速技术</strong>。它的核心思想非常符合人类的教育模式——<strong>师生学习（Teacher-Student Learning）</strong>。</p><p>在这个框架中：</p><div class="table-container"><table><thead><tr><th>角色</th><th>含义</th><th>特点</th></tr></thead><tbody><tr><td><strong>教师模型</strong></td><td>庞大、复杂、高性能的大模型</td><td>不需要部署到终端，唯一任务是输出高质量预测</td></tr><tr><td><strong>学生模型</strong></td><td>体量小、结构简单的轻量模型</td><td>目标是模仿老师的行为，用更少参数达到接近的性能</td></tr></tbody></table></div><p><strong>一个生动的比喻</strong>：</p><blockquote><p>传统训练像让学生直接翻阅整座图书馆的藏书来寻找规律；而知识蒸馏则是让一位学识渊博的老教授，把自己消化理解后的精华笔记传授给学生。学生通过模仿教授的解题思路，以极低的成本掌握同样的知识。</p></blockquote><hr><h2 id="二、为什么要做知识蒸馏？"><a href="#二、为什么要做知识蒸馏？" class="headerlink" title="二、为什么要做知识蒸馏？"></a>二、为什么要做知识蒸馏？</h2><div class="table-container"><table><thead><tr><th>痛点</th><th>说明</th></tr></thead><tbody><tr><td><strong>部署成本高</strong></td><td>GPT-3 级别的模型需要多张 GPU 才能运行，无法部署到手机、智能手表等边缘设备</td></tr><tr><td><strong>推理速度慢</strong></td><td>大模型生成一个字可能需要几秒钟，实时性差</td></tr><tr><td><strong>能耗大</strong></td><td>一次推理可能消耗相当于几小时手机使用的电量</td></tr></tbody></table></div><p><strong>知识蒸馏的核心价值</strong>：在保持较高精度的前提下，<strong>大幅压缩模型体积和推理成本</strong>。</p><hr><h2 id="三、知识蒸馏的完整流程"><a href="#三、知识蒸馏的完整流程" class="headerlink" title="三、知识蒸馏的完整流程"></a>三、知识蒸馏的完整流程</h2><pre class="mermaid">graph LR    subgraph "阶段一：训练教师模型"        A[海量数据] --> B[大模型训练]        B --> C["教师模型<br>（精度高，体积大）"]    end    subgraph "阶段二：知识蒸馏训练"        D["同一份数据<br>（可无标签）"] --> C        D --> E["学生模型<br>（体积小）"]        C -->|软标签<br>概率分布| F[蒸馏损失<br>KL散度]        E -->|软预测| F        D -->|硬标签| G[学生损失<br>交叉熵]        E -->|硬预测| G        F --> H[总损失]        G --> H        H -->|反向传播| E    end    subgraph "阶段三：部署"        E --> I[推理部署<br>手机/边缘设备]    end</pre><hr><h2 id="四、核心概念详解：温度与软标签"><a href="#四、核心概念详解：温度与软标签" class="headerlink" title="四、核心概念详解：温度与软标签"></a>四、核心概念详解：温度与软标签</h2><p>这是知识蒸馏最精妙的部分，也是理解它的关键。</p><span id="more"></span><h3 id="4-1-硬标签-vs-软标签"><a href="#4-1-硬标签-vs-软标签" class="headerlink" title="4.1 硬标签 vs 软标签"></a>4.1 硬标签 vs 软标签</h3><div class="table-container"><table><thead><tr><th></th><th><strong>硬标签（Hard Target）</strong></th><th><strong>软标签（Soft Target）</strong></th></tr></thead><tbody><tr><td><strong>形式</strong></td><td><code>[1, 0, 0]</code>（猫）</td><td><code>[0.7, 0.2, 0.1]</code></td></tr><tr><td><strong>信息量</strong></td><td>低，只告诉“正确答案”</td><td>高，还告诉“其他类别的相似度”</td></tr><tr><td><strong>来源</strong></td><td>人工标注</td><td>教师模型输出</td></tr></tbody></table></div><p><strong>举个例子</strong>：一张猫的图片</p><ul><li>硬标签：<code>{猫: 1, 狗: 0, 鸟: 0}</code>（学习：这是猫，其他都不是）</li><li>软标签：<code>{猫: 0.7, 狗: 0.2, 鸟: 0.1}</code>（学习：这是猫，但和狗也有点像）</li></ul><p>软标签包含的 <strong>“暗知识”</strong> 教会学生：猫的图片可能也有狗的某些特征（四条腿、有毛等）。这是硬标签无法提供的宝贵信息。</p><h3 id="4-2-温度（Temperature-T）的作用"><a href="#4-2-温度（Temperature-T）的作用" class="headerlink" title="4.2 温度（Temperature, T）的作用"></a>4.2 温度（Temperature, T）的作用</h3><p>温度是一个超参数，用于<strong>软化概率分布</strong>。</p><p><strong>公式</strong>：</p><script type="math/tex; mode=display">q_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)}</script><p>其中 $z_i$ 是模型输出的 logit，$T$ 是温度。</p><div class="table-container"><table><thead><tr><th>温度</th><th>效果</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>T=1</strong></td><td>标准 Softmax，原始概率分布</td><td>常规推理</td></tr><tr><td><strong>T&gt;1</strong></td><td>概率分布更平滑，小概率类被“放大”</td><td>知识蒸馏（让学生学到更多类间关系）</td></tr><tr><td><strong>T&lt;1</strong></td><td>概率分布更尖锐，更接近 one-hot</td><td>极少使用</td></tr></tbody></table></div><p><strong>直观示例</strong>（猫图片）：</p><div class="table-container"><table><thead><tr><th>类别</th><th>T=1</th><th>T=3</th><th>T=5</th></tr></thead><tbody><tr><td>猫</td><td>0.70</td><td>0.50</td><td>0.40</td></tr><tr><td>狗</td><td>0.20</td><td>0.30</td><td>0.32</td></tr><tr><td>鸟</td><td>0.10</td><td>0.20</td><td>0.28</td></tr></tbody></table></div><blockquote><p>高温下，概率分布更平滑，猫和狗的差距缩小，学生能学到“猫和狗在视觉上有相似之处”这个知识。</p></blockquote><hr><h2 id="五、代码实战：从零实现知识蒸馏"><a href="#五、代码实战：从零实现知识蒸馏" class="headerlink" title="五、代码实战：从零实现知识蒸馏"></a>五、代码实战：从零实现知识蒸馏</h2><p>下面以 PyTorch 为例，展示一个完整的知识蒸馏实现。</p><h3 id="5-1-导入依赖"><a href="#5-1-导入依赖" class="headerlink" title="5.1 导入依赖"></a>5.1 导入依赖</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.nn.functional <span class="keyword">as</span> F</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> datasets, transforms</span><br></pre></td></tr></table></figure><h3 id="5-2-定义蒸馏损失函数"><a href="#5-2-定义蒸馏损失函数" class="headerlink" title="5.2 定义蒸馏损失函数"></a>5.2 定义蒸馏损失函数</h3><p>这是知识蒸馏的核心：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">distillation_loss</span>(<span class="params">student_logits, teacher_logits, labels, </span></span><br><span class="line"><span class="params">                      temperature=<span class="number">3.0</span>, alpha=<span class="number">0.7</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    student_logits: 学生模型的原始输出（logits）</span></span><br><span class="line"><span class="string">    teacher_logits: 教师模型的原始输出（logits）</span></span><br><span class="line"><span class="string">    labels: 真实标签（硬标签）</span></span><br><span class="line"><span class="string">    temperature: 温度参数</span></span><br><span class="line"><span class="string">    alpha: 蒸馏损失和硬标签损失的权重</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 1. 蒸馏损失：学生软输出 vs 教师软目标（KL散度）</span></span><br><span class="line">    soft_teacher = F.softmax(teacher_logits / temperature, dim=<span class="number">1</span>)</span><br><span class="line">    log_soft_student = F.log_softmax(student_logits / temperature, dim=<span class="number">1</span>)</span><br><span class="line">    distill_loss = F.kl_div(</span><br><span class="line">        log_soft_student, soft_teacher, </span><br><span class="line">        reduction=<span class="string">&#x27;batchmean&#x27;</span></span><br><span class="line">    ) * (temperature ** <span class="number">2</span>)  <span class="comment"># 温度补偿</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 2. 学生损失：学生硬输出 vs 真实标签（交叉熵）</span></span><br><span class="line">    student_loss = F.cross_entropy(student_logits, labels)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 3. 总损失 = α × 蒸馏损失 + (1-α) × 学生损失</span></span><br><span class="line">    total_loss = alpha * distill_loss + (<span class="number">1</span> - alpha) * student_loss</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> total_loss</span><br></pre></td></tr></table></figure><h3 id="5-3-定义教师和学生模型"><a href="#5-3-定义教师和学生模型" class="headerlink" title="5.3 定义教师和学生模型"></a>5.3 定义教师和学生模型</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 教师模型：大模型（以 ResNet-34 为例）</span></span><br><span class="line">teacher_model = torchvision.models.resnet34(pretrained=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 学生模型：小模型（以 ResNet-18 为例）</span></span><br><span class="line">student_model = torchvision.models.resnet18(pretrained=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调整输出层（假设分类数为 10）</span></span><br><span class="line">teacher_model.fc = nn.Linear(<span class="number">512</span>, <span class="number">10</span>)</span><br><span class="line">student_model.fc = nn.Linear(<span class="number">512</span>, <span class="number">10</span>)</span><br></pre></td></tr></table></figure><h3 id="5-4-完整的训练流程"><a href="#5-4-完整的训练流程" class="headerlink" title="5.4 完整的训练流程"></a>5.4 完整的训练流程</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">train_distillation</span>():</span><br><span class="line">    <span class="comment"># 1. 加载数据</span></span><br><span class="line">    transform = transforms.Compose([</span><br><span class="line">        transforms.ToTensor(),</span><br><span class="line">        transforms.Normalize((<span class="number">0.5</span>,), (<span class="number">0.5</span>,))</span><br><span class="line">    ])</span><br><span class="line">    train_loader = DataLoader(</span><br><span class="line">        datasets.MNIST(<span class="string">&#x27;./data&#x27;</span>, train=<span class="literal">True</span>, download=<span class="literal">True</span>, transform=transform),</span><br><span class="line">        batch_size=<span class="number">64</span>, shuffle=<span class="literal">True</span></span><br><span class="line">    )</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 2. 初始化模型</span></span><br><span class="line">    teacher = teacher_model.to(device)</span><br><span class="line">    student = student_model.to(device)</span><br><span class="line">    optimizer = torch.optim.Adam(student.parameters(), lr=<span class="number">1e-4</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 3. 固定教师模型参数（不参与训练）</span></span><br><span class="line">    teacher.<span class="built_in">eval</span>()</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 4. 训练循环</span></span><br><span class="line">    <span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">        total_loss = <span class="number">0</span></span><br><span class="line">        <span class="keyword">for</span> images, labels <span class="keyword">in</span> train_loader:</span><br><span class="line">            images, labels = images.to(device), labels.to(device)</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 前向传播</span></span><br><span class="line">            <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">                teacher_logits = teacher(images)</span><br><span class="line">            student_logits = student(images)</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 计算蒸馏损失</span></span><br><span class="line">            loss = distillation_loss(</span><br><span class="line">                student_logits, teacher_logits, labels,</span><br><span class="line">                temperature=<span class="number">3.0</span>, alpha=<span class="number">0.7</span></span><br><span class="line">            )</span><br><span class="line">            </span><br><span class="line">            <span class="comment"># 反向传播</span></span><br><span class="line">            optimizer.zero_grad()</span><br><span class="line">            loss.backward()</span><br><span class="line">            optimizer.step()</span><br><span class="line">            </span><br><span class="line">            total_loss += loss.item()</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Epoch <span class="subst">&#123;epoch+<span class="number">1</span>&#125;</span>, Loss: <span class="subst">&#123;total_loss/<span class="built_in">len</span>(train_loader):<span class="number">.4</span>f&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> student_model</span><br></pre></td></tr></table></figure><h3 id="5-5-超参数调优建议"><a href="#5-5-超参数调优建议" class="headerlink" title="5.5 超参数调优建议"></a>5.5 超参数调优建议</h3><div class="table-container"><table><thead><tr><th>超参数</th><th>推荐范围</th><th>说明</th></tr></thead><tbody><tr><td><strong>温度 T</strong></td><td>3 ~ 8</td><td>温度越高，软标签越平滑；分类数越多，推荐更高温度</td></tr><tr><td><strong>权重 α</strong></td><td>0.5 ~ 0.9</td><td>α 越大，学生越偏向模仿教师；建议从 0.7 开始尝试</td></tr><tr><td><strong>学习率</strong></td><td>1e-4 ~ 1e-3</td><td>比正常训练略低，保持训练的稳定性</td></tr><tr><td><strong>epoch 数</strong></td><td>10 ~ 30</td><td>通常比正常训练需要更多 epoch</td></tr></tbody></table></div><hr><h2 id="六、进阶蒸馏方法"><a href="#六、进阶蒸馏方法" class="headerlink" title="六、进阶蒸馏方法"></a>六、进阶蒸馏方法</h2><p>除了经典的输出层蒸馏，还有更高效的变体：</p><h3 id="6-1-特征蒸馏（Feature-Distillation）"><a href="#6-1-特征蒸馏（Feature-Distillation）" class="headerlink" title="6.1 特征蒸馏（Feature Distillation）"></a>6.1 特征蒸馏（Feature Distillation）</h3><blockquote><p>不仅模仿老师的最终答案，还强迫学生去学习老师中间隐藏层的特征。</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FeatureDistillationLoss</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, mse_weight=<span class="number">0.1</span></span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.mse_weight = mse_weight</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, student_feats, teacher_feats, </span></span><br><span class="line"><span class="params">                student_logits, teacher_logits, labels</span>):</span><br><span class="line">        <span class="comment"># 特征模仿损失（MSE）</span></span><br><span class="line">        feat_loss = F.mse_loss(student_feats, teacher_feats)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 输出蒸馏损失</span></span><br><span class="line">        distill_loss = distillation_loss(</span><br><span class="line">            student_logits, teacher_logits, labels</span><br><span class="line">        )</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> distill_loss + <span class="variable language_">self</span>.mse_weight * feat_loss</span><br></pre></td></tr></table></figure><h3 id="6-2-自蒸馏（Self-Distillation）"><a href="#6-2-自蒸馏（Self-Distillation）" class="headerlink" title="6.2 自蒸馏（Self-Distillation）"></a>6.2 自蒸馏（Self-Distillation）</h3><blockquote><p>不需要外部教师，用模型自身深层网络指导浅层网络。</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">SelfDistillationModel</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="comment"># 同一模型的不同深度层</span></span><br><span class="line">        <span class="variable language_">self</span>.shallow = nn.Sequential(...)</span><br><span class="line">        <span class="variable language_">self</span>.deep = nn.Sequential(...)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        shallow_out = <span class="variable language_">self</span>.shallow(x)</span><br><span class="line">        deep_out = <span class="variable language_">self</span>.deep(shallow_out)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 用深层指导浅层</span></span><br><span class="line">        distill_loss = F.kl_div(</span><br><span class="line">            F.log_softmax(shallow_out, dim=<span class="number">1</span>),</span><br><span class="line">            F.softmax(deep_out.detach(), dim=<span class="number">1</span>),</span><br><span class="line">            reduction=<span class="string">&#x27;batchmean&#x27;</span></span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">return</span> deep_out, distill_loss</span><br></pre></td></tr></table></figure><h3 id="6-3-三种方法对比"><a href="#6-3-三种方法对比" class="headerlink" title="6.3 三种方法对比"></a>6.3 三种方法对比</h3><div class="table-container"><table><thead><tr><th>方法</th><th>原理</th><th>优点</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>输出蒸馏</strong></td><td>模仿教师软标签</td><td>实现简单，效果稳定</td><td>通用场景</td></tr><tr><td><strong>特征蒸馏</strong></td><td>模仿中间层特征</td><td>学习更丰富的信息</td><td>视觉任务（CNN）</td></tr><tr><td><strong>自蒸馏</strong></td><td>自身深层指导浅层</td><td>无需额外教师模型</td><td>资源受限场景</td></tr></tbody></table></div><hr><h2 id="七、典型应用场景"><a href="#七、典型应用场景" class="headerlink" title="七、典型应用场景"></a>七、典型应用场景</h2><div class="table-container"><table><thead><tr><th>场景</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><strong>大模型压缩</strong></td><td>将 BERT 蒸馏为 TinyBERT</td><td>手机端 BERT 应用</td></tr><tr><td><strong>模型加速</strong></td><td>图像分类模型蒸馏</td><td>自动驾驶中的实时检测</td></tr><tr><td><strong>跨模态蒸馏</strong></td><td>图文多模态 → 单模态</td><td>用 CLIP 蒸馏轻量图像模型</td></tr><tr><td><strong>隐私保护</strong></td><td>无需访问原始数据</td><td>远程调用教师 API 进行蒸馏</td></tr></tbody></table></div><p><strong>工业界案例</strong>：</p><div class="table-container"><table><thead><tr><th>模型</th><th>蒸馏前</th><th>蒸馏后</th><th>精度保留</th></tr></thead><tbody><tr><td><strong>DistilBERT</strong></td><td>BERT-base (110M)</td><td>66M 参数</td><td>保留 97% 精度，速度提升 60%</td></tr><tr><td><strong>TinyBERT</strong></td><td>BERT-base</td><td>参数减少 7 倍</td><td>保留 96% 精度</td></tr><tr><td><strong>MobileNet 蒸馏</strong></td><td>ResNet-50</td><td>MobileNet</td><td>边缘设备实时推理</td></tr></tbody></table></div><hr><h2 id="八、总结"><a href="#八、总结" class="headerlink" title="八、总结"></a>八、总结</h2><h3 id="核心公式回顾"><a href="#核心公式回顾" class="headerlink" title="核心公式回顾"></a>核心公式回顾</h3><script type="math/tex; mode=display">\mathcal{L}_{\text{total}} = \alpha \cdot \mathcal{L}_{\text{distill}} + (1-\alpha) \cdot \mathcal{L}_{\text{student}}</script><script type="math/tex; mode=display">\mathcal{L}_{\text{distill}} = T^2 \cdot \text{KL}\left(\text{softmax}\left(\frac{z_s}{T}\right) \parallel \text{softmax}\left(\frac{z_t}{T}\right)\right)</script><h3 id="核心要点"><a href="#核心要点" class="headerlink" title="核心要点"></a>核心要点</h3><div class="table-container"><table><thead><tr><th>要点</th><th>说明</th></tr></thead><tbody><tr><td><strong>核心思想</strong></td><td>学生模型模仿教师模型的输出分布</td></tr><tr><td><strong>温度 T</strong></td><td>控制软标签的平滑程度，T&gt;1 时分布更平滑</td></tr><tr><td><strong>软标签</strong></td><td>蕴含类间相似性，比硬标签信息更丰富</td></tr><tr><td><strong>损失函数</strong></td><td>蒸馏损失（KL散度）+ 学生损失（交叉熵）</td></tr><tr><td><strong>应用价值</strong></td><td>模型压缩、加速推理、边缘设备部署</td></tr></tbody></table></div>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/6.%E7%9F%A5%E8%AF%86%E8%92%B8%E9%A6%8F/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/6.%E7%9F%A5%E8%AF%86%E8%92%B8%E9%A6%8F/"/>
    <published>2026-06-06T11:55:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="知识蒸馏：让小模型拥有大模型的“智慧”"><a href="#知识蒸馏：让小模型拥有大模型的“智慧”" class="headerlink" title="知识蒸馏：让小模型拥有大模型的“智慧”"></a>知识蒸馏：让小模型拥有大模型的“智慧”</h1><p>在人工智能飞速发展的今天，我们见证了参数量动辄百亿、千亿的“巨无霸”模型诞生。它们极其聪明，但代价是高昂的计算成本和漫长的推理延迟。如果想在手机或智能手表上运行这些大模型，显然不切实际。</p>
<p>有没有一种方法，既能保留大模型的强大能力，又能享受小模型的轻量与高效？</p>
<p>答案就是：<strong>知识蒸馏（Knowledge Distillation）</strong>。</p>
<hr>
<h2 id="一、什么是知识蒸馏？"><a href="#一、什么是知识蒸馏？" class="headerlink" title="一、什么是知识蒸馏？"></a>一、什么是知识蒸馏？</h2><p>简单来说，知识蒸馏是一种<strong>模型压缩和加速技术</strong>。它的核心思想非常符合人类的教育模式——<strong>师生学习（Teacher-Student Learning）</strong>。</p>
<p>在这个框架中：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>角色</th>
<th>含义</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>教师模型</strong></td>
<td>庞大、复杂、高性能的大模型</td>
<td>不需要部署到终端，唯一任务是输出高质量预测</td>
</tr>
<tr>
<td><strong>学生模型</strong></td>
<td>体量小、结构简单的轻量模型</td>
<td>目标是模仿老师的行为，用更少参数达到接近的性能</td>
</tr>
</tbody>
</table>
</div>
<p><strong>一个生动的比喻</strong>：</p>
<blockquote>
<p>传统训练像让学生直接翻阅整座图书馆的藏书来寻找规律；而知识蒸馏则是让一位学识渊博的老教授，把自己消化理解后的精华笔记传授给学生。学生通过模仿教授的解题思路，以极低的成本掌握同样的知识。</p>
</blockquote>
<hr>
<h2 id="二、为什么要做知识蒸馏？"><a href="#二、为什么要做知识蒸馏？" class="headerlink" title="二、为什么要做知识蒸馏？"></a>二、为什么要做知识蒸馏？</h2><div class="table-container">
<table>
<thead>
<tr>
<th>痛点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>部署成本高</strong></td>
<td>GPT-3 级别的模型需要多张 GPU 才能运行，无法部署到手机、智能手表等边缘设备</td>
</tr>
<tr>
<td><strong>推理速度慢</strong></td>
<td>大模型生成一个字可能需要几秒钟，实时性差</td>
</tr>
<tr>
<td><strong>能耗大</strong></td>
<td>一次推理可能消耗相当于几小时手机使用的电量</td>
</tr>
</tbody>
</table>
</div>
<p><strong>知识蒸馏的核心价值</strong>：在保持较高精度的前提下，<strong>大幅压缩模型体积和推理成本</strong>。</p>
<hr>
<h2 id="三、知识蒸馏的完整流程"><a href="#三、知识蒸馏的完整流程" class="headerlink" title="三、知识蒸馏的完整流程"></a>三、知识蒸馏的完整流程</h2><pre class="mermaid">graph LR
    subgraph "阶段一：训练教师模型"
        A[海量数据] --> B[大模型训练]
        B --> C["教师模型<br>（精度高，体积大）"]
    end

    subgraph "阶段二：知识蒸馏训练"
        D["同一份数据<br>（可无标签）"] --> C
        D --> E["学生模型<br>（体积小）"]
        C -->|软标签<br>概率分布| F[蒸馏损失<br>KL散度]
        E -->|软预测| F
        D -->|硬标签| G[学生损失<br>交叉熵]
        E -->|硬预测| G
        F --> H[总损失]
        G --> H
        H -->|反向传播| E
    end

    subgraph "阶段三：部署"
        E --> I[推理部署<br>手机/边缘设备]
    end</pre>

<hr>
<h2 id="四、核心概念详解：温度与软标签"><a href="#四、核心概念详解：温度与软标签" class="headerlink" title="四、核心概念详解：温度与软标签"></a>四、核心概念详解：温度与软标签</h2><p>这是知识蒸馏最精妙的部分，也是理解它的关键。</p>]]>
    </summary>
    <title>知识蒸馏：让小模型拥有大模型的“智慧”</title>
    <updated>2026-06-07T13:51:34.490Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="大模型第一课：手写推导线性回归的“灵魂”"><a href="#大模型第一课：手写推导线性回归的“灵魂”" class="headerlink" title="大模型第一课：手写推导线性回归的“灵魂”"></a>大模型第一课：手写推导线性回归的“灵魂”</h1><p>在深度学习和人工智能的宏大叙事中，Transformer、大语言模型动辄拥有千亿参数，令人望而生畏。但如果我们剥去这些复杂的外衣，会发现一切神经网络的基石，其实都源于一个最简单的模型——<strong>线性回归</strong>。</p><p>今天这篇博客，我们不谈复杂的框架代码，而是回归数学本质。我们将通过一份手写的学习笔记，彻底搞懂机器是如何通过“试错”来学会预测的。</p><blockquote><p><strong>核心观点</strong>：理解线性回归，就是掌握了深度学习的“第一性原理”。</p></blockquote><hr><h2 id="一、模型的诞生：给数据画一条直线"><a href="#一、模型的诞生：给数据画一条直线" class="headerlink" title="一、模型的诞生：给数据画一条直线"></a>一、模型的诞生：给数据画一条直线</h2><p>想象一下，你是一名房产中介，手头有一堆历史成交数据（房子的面积和对应的价格）。你的目标是找到一条规律，能最好地“穿过”这些数据点，从而根据新房子的面积来预测它的价格。</p><p>这就是线性回归要解决的问题：<strong>拟合</strong>。</p><p>为了实现这个目标，我们需要定义两个核心要素：</p><ul><li><strong>输入值 (x)</strong>：喂给模型的特征数据（例如：房子面积）。</li><li><strong>输出值 (z)</strong>：模型给出的预测结果（例如：预测房价）。</li></ul><p>为了让 x 变成 z，我们需要两个<strong>可调节的参数</strong>——这也是模型真正要去“学习”的东西：</p><ol><li><strong>权重 ($w$, Weight)</strong>：决定输入 x 对结果的影响程度。在几何上，它是直线的<strong>斜率</strong>。</li><li><strong>偏置 ($b$, Bias)</strong>：决定当 x=0 时的基础值。在几何上，它是直线的<strong>截距</strong>。</li></ol><p>于是，我们得到了机器学习界最经典的公式：</p><script type="math/tex; mode=display">z = x \cdot w + b</script><blockquote><p>💡 <strong>笔记划重点</strong>：这个简单的线性公式，就是所有神经网络神经元计算的雏形。</p></blockquote><hr><h2 id="二、怎么才算“学得好”？引入-Loss-函数"><a href="#二、怎么才算“学得好”？引入-Loss-函数" class="headerlink" title="二、怎么才算“学得好”？引入 Loss 函数"></a>二、怎么才算“学得好”？引入 Loss 函数</h2><p>模型给出了预测值 z，但怎么知道它准不准呢？我们需要一个“裁判”来打分。这个裁判就是<strong>损失函数（Loss Function）</strong>。</p><p>同时，我们需要一个参照标准——<strong>目标值 (Y, Ground Truth)</strong>，也就是真实答案（例如：房子的实际成交价）。</p><p>笔记中使用了最直观的<strong>均方误差（MSE）</strong>思想。为了简化推导，我们只看单个样本的误差平方：</p><span id="more"></span><script type="math/tex; mode=display">\text{Loss} = (z - Y)^2</script><p>把 <script type="math/tex">z = x \cdot w + b</script> 代入，就得到了关于 w 和 b 的完整表达式：</p><script type="math/tex; mode=display">\text{Loss} = (x \cdot w + b - Y)^2</script><p><strong>核心逻辑图解：</strong></p><div class="table-container"><table><thead><tr><th style="text-align:left">情况</th><th style="text-align:left">预测值 vs 真实值</th><th style="text-align:left">Loss 大小</th><th style="text-align:left">含义</th></tr></thead><tbody><tr><td style="text-align:left"><strong>预测准确</strong></td><td style="text-align:left">z 接近 Y</td><td style="text-align:left">很小 (趋近于0)</td><td style="text-align:left">模型表现好 ✅</td></tr><tr><td style="text-align:left"><strong>预测偏差大</strong></td><td style="text-align:left">z 远离 Y</td><td style="text-align:left">很大</td><td style="text-align:left">模型表现差 ❌</td></tr></tbody></table></div><blockquote><p><strong>我们的终极目标</strong>：通过不断调整 w 和 b，让 Loss <strong>尽可能小</strong>。这就是所谓的“训练”。</p></blockquote><hr><h2 id="三、如何调整参数？反向传播与梯度下降"><a href="#三、如何调整参数？反向传播与梯度下降" class="headerlink" title="三、如何调整参数？反向传播与梯度下降"></a>三、如何调整参数？反向传播与梯度下降</h2><p>知道了目标是最小化 Loss，那具体该怎么调 $w$ 和 $b$ 呢？像无头苍蝇一样随机乱猜肯定不行，我们需要数学指引。</p><p>这就涉及到了两个关键概念：<strong>反向传播（Backpropagation）</strong> 和 <strong>梯度下降（Gradient Descent）</strong>。</p><h3 id="1-寻找方向：梯度的意义"><a href="#1-寻找方向：梯度的意义" class="headerlink" title="1. 寻找方向：梯度的意义"></a>1. 寻找方向：梯度的意义</h3><p>我们需要知道：<strong>如果我稍微改变一点点 $w$，Loss 会变大还是变小？变化有多快？</strong></p><p>这就需要用到微积分中的<strong>求导（Derivative）</strong>。导数（也叫梯度）告诉我们 Loss 函数在当前点的“坡度”方向。</p><ul><li>如果梯度是正的，说明往右走 Loss 会变大，我们要往左走（减小 $w$）。</li><li>如果梯度是负的，说明往右走 Loss 会变小，我们要往右走（增加 $w$）。</li></ul><h3 id="2-链式求导实战"><a href="#2-链式求导实战" class="headerlink" title="2. 链式求导实战"></a>2. 链式求导实战</h3><p>根据链式法则，我们对 $w$ 和 $b$ 分别求导（即计算梯度）：</p><p><strong>① 对权重 $w$ 求导：</strong></p><script type="math/tex; mode=display">\frac{\partial \text{Loss}}{\partial w} = 2(x \cdot w + b - Y) \cdot x</script><ul><li><strong>推导拆解</strong>：外层平方求导得到 $2(\dots)$ $\times$ 内层 $(xw+b)$ 对 $w$ 求导得到 $x$。</li><li><strong>物理含义</strong>：误差越大，或者输入 $x$ 越大，权重的调整幅度就应该越大。</li></ul><p><strong>② 对偏置 $b$ 求导：</strong></p><script type="math/tex; mode=display">\frac{\partial \text{Loss}}{\partial b} = 2(x \cdot w + b - Y) \cdot 1</script><ul><li><strong>推导拆解</strong>：外层平方求导得到 $2(\dots)$ $\times$ 内层 $(xw+b)$ 对 $b$ 求导得到 $1$。</li></ul><h3 id="3-迈出步伐：参数更新"><a href="#3-迈出步伐：参数更新" class="headerlink" title="3. 迈出步伐：参数更新"></a>3. 迈出步伐：参数更新</h3><p>有了梯度之后，我们就可以沿着梯度的<strong>反方向</strong>更新参数（因为梯度的方向是 Loss 增加最快的方向，反方向才是下山的路）：</p><script type="math/tex; mode=display">w_{\text{new}} = w - \eta \cdot \frac{\partial \text{Loss}}{\partial w}</script><script type="math/tex; mode=display">b_{\text{new}} = b - \eta \cdot \frac{\partial \text{Loss}}{\partial b}</script><p>其中 $\eta$（读作 Eta）是<strong>学习率（Learning Rate）</strong>。它控制每次更新的步长：</p><ul><li><strong>太大</strong>：可能直接跨过最低点，导致无法收敛（震荡）。</li><li><strong>太小</strong>：下山速度太慢，训练时间过长。</li></ul><hr><h2 id="四、总结：机器学习的完美闭环"><a href="#四、总结：机器学习的完美闭环" class="headerlink" title="四、总结：机器学习的完美闭环"></a>四、总结：机器学习的完美闭环</h2><p>回顾整个流程，我们可以总结出机器学习最基本的<strong>闭环逻辑</strong>：</p><pre class="mermaid">graph LR    A[输入 x] --> B(前向传播 计算 z = x*w + b)    B --> C["计算 Loss(z - Y)²"]    C -- 误差反馈 --> D["反向传播 计算梯度 ∂Loss/∂w, ∂Loss/∂b"]    D --> E["参数更新w = w - η·梯度"]    E --> B    style A fill:#e1f5fe,stroke:#333,stroke-width:2px    style B fill:#fff9c4,stroke:#333,stroke-width:2px    style C fill:#ffe0b2,stroke:#333,stroke-width:2px    style D fill:#f3e5f5,stroke:#333,stroke-width:2px    style E fill:#c8e6c9,stroke:#333,stroke-width:2px</pre><div class="table-container"><table><thead><tr><th style="text-align:left">步骤</th><th style="text-align:left">名称</th><th style="text-align:left">作用</th><th style="text-align:left">形象比喻</th></tr></thead><tbody><tr><td style="text-align:left">1</td><td style="text-align:left"><strong>前向传播</strong></td><td style="text-align:left">用当前的 $w, b$ 算出预测值 $z$</td><td style="text-align:left">盲人摸象，先猜一下</td></tr><tr><td style="text-align:left">2</td><td style="text-align:left"><strong>计算 Loss</strong></td><td style="text-align:left">对比预测值 $z$ 和真实值 $Y$，算出误差</td><td style="text-align:left">睁眼看看，猜错了多少</td></tr><tr><td style="text-align:left">3</td><td style="text-align:left"><strong>反向传播</strong></td><td style="text-align:left">利用求导公式，算出梯度</td><td style="text-align:left">分析原因，确定调整方向</td></tr><tr><td style="text-align:left">4</td><td style="text-align:left"><strong>参数更新</strong></td><td style="text-align:left">沿梯度反方向更新 $w$ 和 $b$</td><td style="text-align:left">修正动作，下次猜得更准</td></tr></tbody></table></div><p><strong>循环往复</strong>，直到 Loss 降到一个可接受的范围。这就是机器“学习”的本质——<strong>不断试错、反馈、调整，直到找到最优解。</strong></p><hr><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>虽然现在的 Transformer 和大模型结构极其复杂，内部包含了注意力机制、多层感知机等高级组件，但它们底层的优化逻辑，依然没有逃出这几行简单的公式：</p><ul><li>前向传播：$z = x \cdot w + b$ （及其非线性变体）</li><li>计算损失：$(z - Y)^2$ （或交叉熵等）</li><li>反向传播：链式求导</li><li>参数更新：梯度下降</li></ul><p><strong>万丈高楼平地起。</strong> 当你以后面对再复杂的模型感到迷茫时，只要回到线性回归这个原点，你就不会迷失方向。</p><hr><h2 id="核心公式速查卡"><a href="#核心公式速查卡" class="headerlink" title="核心公式速查卡"></a>核心公式速查卡</h2><p>建议截图保存，随时复习：</p><div class="table-container"><table><thead><tr><th style="text-align:left">公式</th><th style="text-align:left">含义</th></tr></thead><tbody><tr><td style="text-align:left">$z = x \cdot w + b$</td><td style="text-align:left"><strong>线性回归模型</strong>（前向传播）</td></tr><tr><td style="text-align:left">$\text{Loss} = (z - Y)^2$</td><td style="text-align:left"><strong>损失函数</strong>（衡量预测误差）</td></tr><tr><td style="text-align:left">$\frac{\partial \text{Loss}}{\partial w} = 2(z - Y) \cdot x$</td><td style="text-align:left"><strong>权重梯度</strong>（指导 $w$ 如何变）</td></tr><tr><td style="text-align:left">$\frac{\partial \text{Loss}}{\partial b} = 2(z - Y)$</td><td style="text-align:left"><strong>偏置梯度</strong>（指导 $b$ 如何变）</td></tr><tr><td style="text-align:left">$w = w - \eta \cdot \text{梯度}$</td><td style="text-align:left"><strong>参数更新规则</strong>（梯度下降）</td></tr></tbody></table></div>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/4.%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%A0%B9%E5%9F%BA/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/4.%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E6%A0%B9%E5%9F%BA/"/>
    <published>2026-06-06T09:40:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="大模型第一课：手写推导线性回归的“灵魂”"><a href="#大模型第一课：手写推导线性回归的“灵魂”" class="headerlink" title="大模型第一课：手写推导线性回归的“灵魂”"></a>大模型第一课：手写推导线性回归的“灵魂”</h1><p>在深度学习和人工智能的宏大叙事中，Transformer、大语言模型动辄拥有千亿参数，令人望而生畏。但如果我们剥去这些复杂的外衣，会发现一切神经网络的基石，其实都源于一个最简单的模型——<strong>线性回归</strong>。</p>
<p>今天这篇博客，我们不谈复杂的框架代码，而是回归数学本质。我们将通过一份手写的学习笔记，彻底搞懂机器是如何通过“试错”来学会预测的。</p>
<blockquote>
<p><strong>核心观点</strong>：理解线性回归，就是掌握了深度学习的“第一性原理”。</p>
</blockquote>
<hr>
<h2 id="一、模型的诞生：给数据画一条直线"><a href="#一、模型的诞生：给数据画一条直线" class="headerlink" title="一、模型的诞生：给数据画一条直线"></a>一、模型的诞生：给数据画一条直线</h2><p>想象一下，你是一名房产中介，手头有一堆历史成交数据（房子的面积和对应的价格）。你的目标是找到一条规律，能最好地“穿过”这些数据点，从而根据新房子的面积来预测它的价格。</p>
<p>这就是线性回归要解决的问题：<strong>拟合</strong>。</p>
<p>为了实现这个目标，我们需要定义两个核心要素：</p>
<ul>
<li><strong>输入值 (x)</strong>：喂给模型的特征数据（例如：房子面积）。</li>
<li><strong>输出值 (z)</strong>：模型给出的预测结果（例如：预测房价）。</li>
</ul>
<p>为了让 x 变成 z，我们需要两个<strong>可调节的参数</strong>——这也是模型真正要去“学习”的东西：</p>
<ol>
<li><strong>权重 ($w$, Weight)</strong>：决定输入 x 对结果的影响程度。在几何上，它是直线的<strong>斜率</strong>。</li>
<li><strong>偏置 ($b$, Bias)</strong>：决定当 x=0 时的基础值。在几何上，它是直线的<strong>截距</strong>。</li>
</ol>
<p>于是，我们得到了机器学习界最经典的公式：</p>
<script type="math/tex; mode=display">
z = x \cdot w + b</script><blockquote>
<p>💡 <strong>笔记划重点</strong>：这个简单的线性公式，就是所有神经网络神经元计算的雏形。</p>
</blockquote>
<hr>
<h2 id="二、怎么才算“学得好”？引入-Loss-函数"><a href="#二、怎么才算“学得好”？引入-Loss-函数" class="headerlink" title="二、怎么才算“学得好”？引入 Loss 函数"></a>二、怎么才算“学得好”？引入 Loss 函数</h2><p>模型给出了预测值 z，但怎么知道它准不准呢？我们需要一个“裁判”来打分。这个裁判就是<strong>损失函数（Loss Function）</strong>。</p>
<p>同时，我们需要一个参照标准——<strong>目标值 (Y, Ground Truth)</strong>，也就是真实答案（例如：房子的实际成交价）。</p>
<p>笔记中使用了最直观的<strong>均方误差（MSE）</strong>思想。为了简化推导，我们只看单个样本的误差平方：</p>]]>
    </summary>
    <title>大模型第一课：手写推导线性回归的“灵魂”</title>
    <updated>2026-06-06T10:28:06.620Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="从静态词向量到动态理解：NLP-预训练模型的完整进化图谱"><a href="#从静态词向量到动态理解：NLP-预训练模型的完整进化图谱" class="headerlink" title="从静态词向量到动态理解：NLP 预训练模型的完整进化图谱"></a>从静态词向量到动态理解：NLP 预训练模型的完整进化图谱</h1><p>在自然语言处理（NLP）的学习过程中，理清模型的演进路线至关重要。这不仅仅是算法的更迭，更是我们对“如何让机器理解语言”这一认知不断深化的过程。回顾过去几年，我们经历了一场从简单的词嵌入到大规模预训练模型的技术革命。</p><p>今天，我们就来梳理一下这条从 Word2Vec 到 ELMo，再到 BERT 的进阶之路，看看它们是如何一步步改变我们对“语言表示”的理解的。</p><hr><h2 id="一、起点：Word2Vec-与静态词向量"><a href="#一、起点：Word2Vec-与静态词向量" class="headerlink" title="一、起点：Word2Vec 与静态词向量"></a>一、起点：Word2Vec 与静态词向量</h2><p>一切的起点是从 <strong>Word2Vec</strong> 开始的。</p><div class="table-container"><table><thead><tr><th>维度</th><th>说明</th></tr></thead><tbody><tr><td><strong>核心思想</strong></td><td>将离散的词语映射为连续的向量空间（Embedding）</td></tr><tr><td><strong>核心贡献</strong></td><td>解决了计算机无法直接处理文本的问题，并且捕捉到了词语之间的语义关系。比如：<code>King - Man + Woman ≈ Queen</code></td></tr><tr><td><strong>致命局限</strong></td><td>它是<strong>静态</strong>的。无论上下文如何变化，“苹果”这个词的向量永远是一样的，无法区分是水果还是科技公司。</td></tr></tbody></table></div><p>这种“一词多义”的困境，严重限制了模型对上下文的理解能力。</p><hr><h2 id="二、序列建模：RNN-及其变体"><a href="#二、序列建模：RNN-及其变体" class="headerlink" title="二、序列建模：RNN 及其变体"></a>二、序列建模：RNN 及其变体</h2><p>为了让机器理解句子的顺序和语境，我们引入了循环神经网络（<strong>RNN</strong>）。</p><p><strong>原理</strong>：RNN 具有“记忆”功能，当前的输出不仅取决于当前的输入，还取决于上一时刻的状态。这使得它能够处理变长的序列数据。</p><p><strong>痛点</strong>：标准的 RNN 存在严重的<strong>梯度消失/梯度爆炸</strong>问题，导致它很难记住长句子开头的信息（即长距离依赖问题）。</p><p><strong>改进</strong>：为了解决这个问题，<strong>LSTM</strong>（长短期记忆网络）和 <strong>GRU</strong>（门控循环单元）应运而生。它们通过引入“门控机制”，有效地控制了信息的遗忘和保留，成为了当时处理序列任务的主流选择。</p><div class="table-container"><table><thead><tr><th>模型</th><th>核心创新</th><th>解决的问题</th></tr></thead><tbody><tr><td>RNN</td><td>循环结构，具有“记忆”</td><td>处理变长序列</td></tr><tr><td>LSTM</td><td>遗忘门、输入门、输出门</td><td>长距离依赖（梯度消失）</td></tr><tr><td>GRU</td><td>更新门、重置门（LSTM 的简化版）</td><td>长距离依赖，参数更少</td></tr></tbody></table></div><hr><h2 id="三、架构升级：Seq2Seq-模型"><a href="#三、架构升级：Seq2Seq-模型" class="headerlink" title="三、架构升级：Seq2Seq 模型"></a>三、架构升级：Seq2Seq 模型</h2><p>有了强大的 RNN/LSTM 作为基础组件，<strong>Seq2Seq</strong>（Sequence-to-Sequence）架构诞生了。</p><p><strong>结构</strong>：它由两个 RNN 组成——<strong>编码器（Encoder）</strong> 和 <strong>解码器（Decoder）</strong>。</p><p><strong>工作流</strong>：</p><span id="more"></span><ol><li><strong>Encoder</strong>：读取整个输入序列，将其压缩成一个固定长度的“上下文向量”（Context Vector）。</li><li><strong>Decoder</strong>：接收这个向量，一步步生成目标序列。</li></ol><p><strong>应用</strong>：这是机器翻译、文本摘要等任务的基石。</p><p><strong>瓶颈</strong>：所有的输入信息都被压缩进了一个固定长度的向量里。对于长句子来说，开头的信息很容易被“遗忘”或稀释，信息丢失严重。这就是“信息瓶颈”问题。</p><hr><h2 id="四、关键突破：注意力机制"><a href="#四、关键突破：注意力机制" class="headerlink" title="四、关键突破：注意力机制"></a>四、关键突破：注意力机制</h2><p>为了打破 Seq2Seq 的瓶颈，<strong>注意力机制（Attention Mechanism）</strong> 被引入了进来。</p><p><strong>核心逻辑</strong>：不再强迫模型把所有信息压缩进一个向量，而是允许 Decoder 在生成每一个词时，都能“回头看” Encoder 的所有输出，并根据重要性分配不同的权重（Attention Weights）。</p><p><strong>直观理解</strong>：这就像人类阅读一样，我们在翻译某个词时，会重点关注原文中对应的部分，而不是死记硬背整句话。</p><p><strong>意义</strong>：</p><ul><li>极大地提升了长序列任务的效果</li><li>让模型的决策过程变得更加“可解释”（可视化注意力权重）</li><li>为后续彻底抛弃 RNN 埋下了伏笔</li></ul><hr><h2 id="五、动态的觉醒：ELMo"><a href="#五、动态的觉醒：ELMo" class="headerlink" title="五、动态的觉醒：ELMo"></a>五、动态的觉醒：ELMo</h2><p>虽然 RNN + Attention 效果很好，但 RNN 必须按顺序计算（第 t 步必须等第 t-1 步完成），这导致训练速度很慢，且难以并行化。在这样的背景下，ELMo（Embeddings from Language Models）迈出了“动态化”的第一步。</p><h3 id="核心突破：动态词向量"><a href="#核心突破：动态词向量" class="headerlink" title="核心突破：动态词向量"></a>核心突破：动态词向量</h3><p>ELMo 的核心思想是：<strong>不再给每个词分配固定的向量，而是根据句子中的上下文来动态生成词的表示。</strong></p><ul><li>在“我吃了一个<strong>苹果</strong>”中，它生成的向量代表水果。</li><li>在“<strong>苹果</strong>发布了新手机”中，它生成的向量代表公司。</li></ul><p>这正是 Word2Vec 所不具备的能力。</p><h3 id="架构秘密：双层双向-LSTM"><a href="#架构秘密：双层双向-LSTM" class="headerlink" title="架构秘密：双层双向 LSTM"></a>架构秘密：双层双向 LSTM</h3><p>虽然 ELMo 效果拔群，但它的架构其实非常“复古”。ELMo <strong>没有使用后来大放异彩的 Transformer</strong>，而是采用了<strong>双层双向 LSTM</strong>。</p><div class="table-container"><table><thead><tr><th>设计</th><th>说明</th></tr></thead><tbody><tr><td><strong>双向</strong></td><td>ELMo 分别训练了一个前向 LSTM 和一个后向 LSTM，然后将它们的输出拼接起来。这种方式属于“浅层融合”，不像 BERT 那样实现真正的深层双向交互。</td></tr><tr><td><strong>深层</strong></td><td>通过堆叠多层 LSTM，低层捕获语法特征（如词性），高层捕获语义特征（如情感）。</td></tr></tbody></table></div><h3 id="历史定位"><a href="#历史定位" class="headerlink" title="历史定位"></a>历史定位</h3><p>ELMo 最大的贡献是<strong>证明了“预训练 + 微调”范式的可行性</strong>。但它受限于 RNN/LSTM 的串行计算特性，训练速度较慢，且难以并行化。这个“接力棒”注定要交给后来者。</p><hr><h2 id="六、终极形态：Transformer"><a href="#六、终极形态：Transformer" class="headerlink" title="六、终极形态：Transformer"></a>六、终极形态：Transformer</h2><p>Google 于 2017 年提出了 <strong>Transformer</strong>，这是一次真正的架构革命。</p><p><strong>核心创新</strong>：彻底抛弃 RNN，完全基于<strong>自注意力机制（Self-Attention）</strong>。</p><p><strong>三大优势</strong>：</p><div class="table-container"><table><thead><tr><th>优势</th><th>说明</th></tr></thead><tbody><tr><td><strong>并行计算</strong></td><td>Transformer 可以同时处理句子中的所有单词，极大地提高了训练效率。RNN 必须按顺序计算，Transformer 不需要。</td></tr><tr><td><strong>全局视野</strong></td><td>自注意力机制让每个词都能直接与其他所有词建立联系，无论距离多远，真正实现了“一眼万年”。</td></tr><tr><td><strong>长距离依赖</strong></td><td>不受 RNN 梯度消失问题的困扰，可以轻松处理上百个词的距离。</td></tr></tbody></table></div><p><strong>直观理解</strong>：如果说 RNN 是一个“逐字阅读”的读者，那么 Transformer 就是一个“全景扫描”的读者，一眼就能看到整个句子的全貌，并理解每个词与其他所有词的关系。</p><hr><h2 id="七、王者诞生：BERT"><a href="#七、王者诞生：BERT" class="headerlink" title="七、王者诞生：BERT"></a>七、王者诞生：BERT</h2><p>基于 Transformer 架构，Google 于 2018 年推出了 <strong>BERT</strong>（Bidirectional Encoder Representations from Transformers）。BERT 的出现，彻底改写了 NLP 的格局。</p><p>BERT 之所以能赢，主要做对了三件事：</p><h3 id="1-架构升级：拥抱-Transformer"><a href="#1-架构升级：拥抱-Transformer" class="headerlink" title="1. 架构升级：拥抱 Transformer"></a>1. 架构升级：拥抱 Transformer</h3><p>BERT 全面拥抱 Transformer 的 Encoder 部分，继承了并行计算和全局视野两大优势。</p><h3 id="2-真正的双向：Masked-LM"><a href="#2-真正的双向：Masked-LM" class="headerlink" title="2. 真正的双向：Masked LM"></a>2. 真正的双向：Masked LM</h3><p>ELMo 的双向是“浅层融合”（分别训练两个单向 LSTM 再拼接），而 BERT 通过 <strong>Masked LM（完形填空任务）</strong> 实现了<strong>真正深层的双向交互</strong>。</p><p>具体做法：随机遮盖输入中 15% 的词，让模型根据上下文预测被遮盖的词。这使得模型在预测每个词时，都能同时利用其左侧和右侧的上下文信息。</p><h3 id="3-极强的通用性"><a href="#3-极强的通用性" class="headerlink" title="3. 极强的通用性"></a>3. 极强的通用性</h3><p>BERT 成为了一个<strong>通用的语言理解底座</strong>。无论是分类、问答还是命名实体识别，只需在预训练模型基础上微调几层参数，即可达到 State-of-the-art 的效果。</p><blockquote><p><strong>一句话总结</strong>：ELMo 证明了“预训练”这条路走得通，BERT 证明了“这条路该怎么走才最快、最远。”</p></blockquote><hr><h2 id="八、宏观视角：AI-技术的层级演进"><a href="#八、宏观视角：AI-技术的层级演进" class="headerlink" title="八、宏观视角：AI 技术的层级演进"></a>八、宏观视角：AI 技术的层级演进</h2><p>从更广阔的视角看，整个 NLP（乃至整个 AI）的发展遵循着清晰的层级脉络：</p><pre class="mermaid">graph TD    A[机器学习] --> B[深度学习 神经网络]    B --> C[CNN 卷积神经网络]    B --> D[RNN 循环神经网络]    D --> E[LSTM / GRU]    B --> F[NLP 预训练模型 如 BERT/GPT]    style F fill:#f9f,stroke:#333,stroke-width:2px</pre><p><strong>逐层解读</strong>：</p><div class="table-container"><table><thead><tr><th>层级</th><th>代表技术</th><th>核心贡献</th></tr></thead><tbody><tr><td><strong>基础层</strong></td><td>机器学习 → 深度学习</td><td>从人工特征到自动特征提取</td></tr><tr><td><strong>架构层</strong></td><td>CNN、RNN</td><td>CNN 擅长局部特征（图像），RNN 擅长序列特征（文本、语音）</td></tr><tr><td><strong>RNN 进化</strong></td><td>LSTM、GRU</td><td>解决梯度消失，让 RNN 能处理更长序列</td></tr><tr><td><strong>预训练层</strong></td><td>BERT、GPT</td><td>在无标注数据上自监督学习，通用性强</td></tr><tr><td><strong>对齐层</strong></td><td>RLHF（基于人类反馈的强化学习）</td><td>让模型不仅“懂语言”，还能“懂人类喜好”</td></tr></tbody></table></div><hr><h2 id="九、总结"><a href="#九、总结" class="headerlink" title="九、总结"></a>九、总结</h2><h3 id="演进路线一览"><a href="#演进路线一览" class="headerlink" title="演进路线一览"></a>演进路线一览</h3><div class="table-container"><table><thead><tr><th>阶段</th><th>代表模型/技术</th><th>核心贡献</th><th>局限性</th></tr></thead><tbody><tr><td><strong>静态词向量</strong></td><td>Word2Vec</td><td>词语 → 向量，捕捉语义关系</td><td>无法处理一词多义</td></tr><tr><td><strong>序列建模</strong></td><td>RNN / LSTM / GRU</td><td>处理变长序列，记忆上下文</td><td>RNN 梯度消失，LSTM 串行慢</td></tr><tr><td><strong>编码-解码</strong></td><td>Seq2Seq</td><td>机器翻译、文本摘要的基础</td><td>固定向量瓶颈，长句信息丢失</td></tr><tr><td><strong>软对齐</strong></td><td>注意力机制</td><td>动态关注重要信息，打破瓶颈</td><td>仍依赖 RNN 串行计算</td></tr><tr><td><strong>动态词向量</strong></td><td>ELMo</td><td>上下文感知 + 预训练范式可行</td><td>LSTM 串行，训练慢</td></tr><tr><td><strong>架构革命</strong></td><td>Transformer</td><td>并行计算 + 全局视野 + 长距离依赖</td><td>计算资源要求高</td></tr><tr><td><strong>预训练霸主</strong></td><td>BERT</td><td>真正双向 + 通用底座 + 微调范式</td><td>生成任务弱（需要 GPT）</td></tr></tbody></table></div><h3 id="核心脉络"><a href="#核心脉络" class="headerlink" title="核心脉络"></a>核心脉络</h3><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">静态词向量 → 序列记忆 → 编码解码 → 注意力机制 → 动态词向量 → 并行化革命 → 预训练霸主</span><br><span class="line">   ↑            ↑          ↑           ↑            ↑             ↑            ↑</span><br><span class="line">Word2Vec      RNN      Seq2Seq    Attention      ELMo       Transformer    BERT/GPT</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/2.%E4%BB%8E%E9%9D%99%E6%80%81%E8%AF%8D%E5%90%91%E9%87%8F%E5%88%B0%E5%8A%A8%E6%80%81%E7%90%86%E8%A7%A3%EF%BC%9ANLP%20%E9%A2%84%E8%AE%AD%E7%BB%83%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%AE%8C%E6%95%B4%E8%BF%9B%E5%8C%96%E5%9B%BE%E8%B0%B1/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/2.%E4%BB%8E%E9%9D%99%E6%80%81%E8%AF%8D%E5%90%91%E9%87%8F%E5%88%B0%E5%8A%A8%E6%80%81%E7%90%86%E8%A7%A3%EF%BC%9ANLP%20%E9%A2%84%E8%AE%AD%E7%BB%83%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%AE%8C%E6%95%B4%E8%BF%9B%E5%8C%96%E5%9B%BE%E8%B0%B1/"/>
    <published>2026-06-06T07:40:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="从静态词向量到动态理解：NLP-预训练模型的完整进化图谱"><a href="#从静态词向量到动态理解：NLP-预训练模型的完整进化图谱" class="headerlink" title="从静态词向量到动态理解：NLP 预训练模型的完整进化图谱"></a>从静态词向量到动态理解：NLP 预训练模型的完整进化图谱</h1><p>在自然语言处理（NLP）的学习过程中，理清模型的演进路线至关重要。这不仅仅是算法的更迭，更是我们对“如何让机器理解语言”这一认知不断深化的过程。回顾过去几年，我们经历了一场从简单的词嵌入到大规模预训练模型的技术革命。</p>
<p>今天，我们就来梳理一下这条从 Word2Vec 到 ELMo，再到 BERT 的进阶之路，看看它们是如何一步步改变我们对“语言表示”的理解的。</p>
<hr>
<h2 id="一、起点：Word2Vec-与静态词向量"><a href="#一、起点：Word2Vec-与静态词向量" class="headerlink" title="一、起点：Word2Vec 与静态词向量"></a>一、起点：Word2Vec 与静态词向量</h2><p>一切的起点是从 <strong>Word2Vec</strong> 开始的。</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>维度</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>核心思想</strong></td>
<td>将离散的词语映射为连续的向量空间（Embedding）</td>
</tr>
<tr>
<td><strong>核心贡献</strong></td>
<td>解决了计算机无法直接处理文本的问题，并且捕捉到了词语之间的语义关系。比如：<code>King - Man + Woman ≈ Queen</code></td>
</tr>
<tr>
<td><strong>致命局限</strong></td>
<td>它是<strong>静态</strong>的。无论上下文如何变化，“苹果”这个词的向量永远是一样的，无法区分是水果还是科技公司。</td>
</tr>
</tbody>
</table>
</div>
<p>这种“一词多义”的困境，严重限制了模型对上下文的理解能力。</p>
<hr>
<h2 id="二、序列建模：RNN-及其变体"><a href="#二、序列建模：RNN-及其变体" class="headerlink" title="二、序列建模：RNN 及其变体"></a>二、序列建模：RNN 及其变体</h2><p>为了让机器理解句子的顺序和语境，我们引入了循环神经网络（<strong>RNN</strong>）。</p>
<p><strong>原理</strong>：RNN 具有“记忆”功能，当前的输出不仅取决于当前的输入，还取决于上一时刻的状态。这使得它能够处理变长的序列数据。</p>
<p><strong>痛点</strong>：标准的 RNN 存在严重的<strong>梯度消失/梯度爆炸</strong>问题，导致它很难记住长句子开头的信息（即长距离依赖问题）。</p>
<p><strong>改进</strong>：为了解决这个问题，<strong>LSTM</strong>（长短期记忆网络）和 <strong>GRU</strong>（门控循环单元）应运而生。它们通过引入“门控机制”，有效地控制了信息的遗忘和保留，成为了当时处理序列任务的主流选择。</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>模型</th>
<th>核心创新</th>
<th>解决的问题</th>
</tr>
</thead>
<tbody>
<tr>
<td>RNN</td>
<td>循环结构，具有“记忆”</td>
<td>处理变长序列</td>
</tr>
<tr>
<td>LSTM</td>
<td>遗忘门、输入门、输出门</td>
<td>长距离依赖（梯度消失）</td>
</tr>
<tr>
<td>GRU</td>
<td>更新门、重置门（LSTM 的简化版）</td>
<td>长距离依赖，参数更少</td>
</tr>
</tbody>
</table>
</div>
<hr>
<h2 id="三、架构升级：Seq2Seq-模型"><a href="#三、架构升级：Seq2Seq-模型" class="headerlink" title="三、架构升级：Seq2Seq 模型"></a>三、架构升级：Seq2Seq 模型</h2><p>有了强大的 RNN/LSTM 作为基础组件，<strong>Seq2Seq</strong>（Sequence-to-Sequence）架构诞生了。</p>
<p><strong>结构</strong>：它由两个 RNN 组成——<strong>编码器（Encoder）</strong> 和 <strong>解码器（Decoder）</strong>。</p>
<p><strong>工作流</strong>：</p>]]>
    </summary>
    <title>从静态词向量到动态理解：NLP 预训练模型的完整进化图谱</title>
    <updated>2026-06-06T10:02:08.335Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="Python" scheme="https://zhhll.com.cn/categories/Python/"/>
    <category term="Python" scheme="https://zhhll.com.cn/tags/Python/"/>
    <content>
      <![CDATA[<h1 id="Python-多进程：8-核-CPU-只有-1-个在跑？那是-GIL-在”捣乱”"><a href="#Python-多进程：8-核-CPU-只有-1-个在跑？那是-GIL-在”捣乱”" class="headerlink" title="Python 多进程：8 核 CPU 只有 1 个在跑？那是 GIL 在”捣乱”"></a>Python 多进程：8 核 CPU 只有 1 个在跑？那是 GIL 在”捣乱”</h1><p>写了一个计算密集型的程序，跑起来 CPU 占用 100%，但仔细一看——8 个核心只有 1 个在满负荷运转，剩下 7 个几乎闲置。</p><p>这不是程序写得不对，是 Python 的 <strong>GIL（全局解释器锁）</strong> 在”从中作梗”。GIL 规定：<strong>同一时刻，只有一个线程能执行 Python 代码</strong>。所以多线程在计算任务上，形同虚设。</p><p>多进程是绕过 GIL 的方案——每个进程有自己独立的 Python 解释器，各自跑各自的，操作系统把它们调度到不同的 CPU 核心上，真正实现了并行。</p><p><strong>什么场景用多进程？</strong> CPU 密集型——大量计算、加密解密、图像处理、数据压缩。这种任务”算”的时间多，”等”的时间少，多线程没用，多进程才是答案。</p><h3 id="先看证据：多线程-vs-多进程，差距一目了然"><a href="#先看证据：多线程-vs-多进程，差距一目了然" class="headerlink" title="先看证据：多线程 vs 多进程，差距一目了然"></a>先看证据：多线程 vs 多进程，差距一目了然</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cpu_work</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;纯计算，不干别的&quot;&quot;&quot;</span></span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, n):</span><br><span class="line">        is_prime = <span class="built_in">all</span>(i % j != <span class="number">0</span> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, <span class="built_in">int</span>(i**<span class="number">0.5</span>) + <span class="number">1</span>))</span><br><span class="line">        <span class="keyword">if</span> is_prime:</span><br><span class="line">            count += <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> count</span><br><span class="line"></span><br><span class="line"><span class="comment"># 单线程</span></span><br><span class="line">start = time.time()</span><br><span class="line">cpu_work(<span class="number">100000</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;单线程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多线程（4个）</span></span><br><span class="line">start = time.time()</span><br><span class="line"><span class="keyword">with</span> ThreadPoolExecutor(<span class="number">4</span>) <span class="keyword">as</span> ex:</span><br><span class="line">    [cpu_work(<span class="number">100000</span>) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>)]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;多线程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 比单线程还慢！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多进程（4个）</span></span><br><span class="line">start = time.time()</span><br><span class="line"><span class="keyword">with</span> multiprocessing.Pool(<span class="number">4</span>) <span class="keyword">as</span> pool:</span><br><span class="line">    pool.<span class="built_in">map</span>(cpu_work, [<span class="number">100000</span>] * <span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;多进程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 约单线程的 1/4</span></span><br></pre></td></tr></table></figure><p><strong>典型结果：</strong></p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">单线程: 2.35s</span></span><br><span class="line"><span class="section">多线程: 2.78s   ← 不仅没快，还慢了</span></span><br><span class="line"><span class="section">多进程: 0.68s   ← 接近 4 倍提速</span></span><br></pre></td></tr></table></figure><p>多线程在计算任务上不仅无效，线程切换还带来额外开销。多进程才是真正的并行。</p><h3 id="多进程池：别反复创建进程，用池复用"><a href="#多进程池：别反复创建进程，用池复用" class="headerlink" title="多进程池：别反复创建进程，用池复用"></a>多进程池：别反复创建进程，用池复用</h3><p>每次创建进程都要分配内存、启动解释器，开销不小。实际开发用<strong>进程池</strong>——预先创建一批进程，任务来了分配，干完了回收。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">return</span> n ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># 默认进程数 = CPU 核心数</span></span><br><span class="line">    <span class="keyword">with</span> multiprocessing.Pool() <span class="keyword">as</span> pool:</span><br><span class="line">        <span class="comment"># map：批量提交，保持顺序</span></span><br><span class="line">        results = pool.<span class="built_in">map</span>(worker, <span class="built_in">range</span>(<span class="number">100</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># apply_async：异步提交单个任务</span></span><br><span class="line">        async_result = pool.apply_async(worker, (<span class="number">10</span>,))</span><br><span class="line">        result = async_result.get()  <span class="comment"># 获取结果（会阻塞）</span></span><br></pre></td></tr></table></figure><p><strong>进程数设多少？</strong></p><div class="table-container"><table><thead><tr><th style="text-align:left">任务类型</th><th style="text-align:left">建议进程数</th></tr></thead><tbody><tr><td style="text-align:left">CPU 密集型</td><td style="text-align:left">CPU 核心数</td></tr><tr><td style="text-align:left">I/O 密集型（但不想用异步）</td><td style="text-align:left">CPU 核心数 × 2</td></tr><tr><td style="text-align:left">内存密集型</td><td style="text-align:left">CPU 核心数 ÷ 2</td></tr></tbody></table></div><p>设多了没用，操作系统频繁切换进程反而拖慢速度。</p><span id="more"></span><h3 id="进程间通信：进程不共享内存，怎么传数据？"><a href="#进程间通信：进程不共享内存，怎么传数据？" class="headerlink" title="进程间通信：进程不共享内存，怎么传数据？"></a>进程间通信：进程不共享内存，怎么传数据？</h3><p>多进程的代价是：<strong>进程之间不共享内存</strong>。传递数据需要拷贝，有开销。</p><h4 id="Queue：最常用，生产者-消费者模式"><a href="#Queue：最常用，生产者-消费者模式" class="headerlink" title="Queue：最常用，生产者-消费者模式"></a><strong>Queue：最常用，生产者-消费者模式</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">producer</span>(<span class="params">q, items</span>):</span><br><span class="line">    <span class="keyword">for</span> item <span class="keyword">in</span> items:</span><br><span class="line">        q.put(item)</span><br><span class="line">    q.put(<span class="literal">None</span>)   <span class="comment"># 结束信号</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">consumer</span>(<span class="params">q</span>):</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        item = q.get()</span><br><span class="line">        <span class="keyword">if</span> item <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;处理: <span class="subst">&#123;item&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    q = multiprocessing.Queue()</span><br><span class="line">    p1 = multiprocessing.Process(target=producer, args=(q, [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]))</span><br><span class="line">    p2 = multiprocessing.Process(target=consumer, args=(q,))</span><br><span class="line">    p1.start()</span><br><span class="line">    p2.start()</span><br><span class="line">    p1.join()</span><br><span class="line">    p2.join()</span><br></pre></td></tr></table></figure><h4 id="Pipe：两个进程之间点对点通信"><a href="#Pipe：两个进程之间点对点通信" class="headerlink" title="Pipe：两个进程之间点对点通信"></a><strong>Pipe：两个进程之间点对点通信</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">parent_conn, child_conn = multiprocessing.Pipe()</span><br><span class="line"><span class="comment"># 一个用 parent_conn 发，另一个用 child_conn 收</span></span><br></pre></td></tr></table></figure><h4 id="共享内存：高频读写，但得加锁"><a href="#共享内存：高频读写，但得加锁" class="headerlink" title="共享内存：高频读写，但得加锁"></a><strong>共享内存：高频读写，但得加锁</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">counter = multiprocessing.Value(<span class="string">&#x27;i&#x27;</span>, <span class="number">0</span>)   <span class="comment"># &#x27;i&#x27; 表示整数</span></span><br><span class="line">lock = multiprocessing.Lock()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">increment</span>():</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">        <span class="keyword">with</span> lock:</span><br><span class="line">            counter.value += <span class="number">1</span></span><br></pre></td></tr></table></figure><p>不加锁的话，多个进程同时改同一个值会乱。<code>with lock</code> 保证同一时刻只有一个进程在改。</p><p><strong>通信方式怎么选？</strong></p><div class="table-container"><table><thead><tr><th style="text-align:left">场景</th><th style="text-align:left">推荐</th></tr></thead><tbody><tr><td style="text-align:left">多对多，任务分发</td><td style="text-align:left">Queue</td></tr><tr><td style="text-align:left">两个进程双向通信</td><td style="text-align:left">Pipe</td></tr><tr><td style="text-align:left">高频读写简单数值</td><td style="text-align:left">Value/Array + Lock</td></tr><tr><td style="text-align:left">复杂数据结构（省事但慢）</td><td style="text-align:left">Manager</td></tr></tbody></table></div><p><code>Manager</code> 是最方便的，不用操心锁的问题，但性能最差。能用 Queue 和 Value 就不用 Manager。</p><h3 id="Windows-上必写的保护：if-name-quot-main-quot"><a href="#Windows-上必写的保护：if-name-quot-main-quot" class="headerlink" title="Windows 上必写的保护：if __name__ == &quot;__main__&quot;:"></a>Windows 上必写的保护：<code>if __name__ == &quot;__main__&quot;:</code></h3><p>Windows 创建新进程时会重新导入主模块，如果不加保护，程序会无限递归创建进程直到崩溃。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#  Windows 上会出问题</span></span><br><span class="line">p = multiprocessing.Process(target=worker)</span><br><span class="line">p.start()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 必须加</span></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    p = multiprocessing.Process(target=worker)</span><br><span class="line">    p.start()</span><br></pre></td></tr></table></figure><p>Linux 和 Mac 上没有这个限制，但<strong>为了跨平台兼容，永远加上</strong>。</p><h3 id="三个容易踩的坑"><a href="#三个容易踩的坑" class="headerlink" title="三个容易踩的坑"></a>三个容易踩的坑</h3><h4 id="坑1：传递大对象，序列化开销巨大"><a href="#坑1：传递大对象，序列化开销巨大" class="headerlink" title="坑1：传递大对象，序列化开销巨大"></a><strong>坑1：传递大对象，序列化开销巨大</strong></h4><p>进程间传递数据需要 pickle 序列化。一个 500MB 的 DataFrame，传一次可能要好几秒。</p><p><strong>解决方案：</strong></p><ul><li>用共享内存（<code>multiprocessing.shared_memory</code>）</li><li>或者把数据切分好再传给每个进程，让每个进程自己读数据（比如从磁盘读）</li></ul><h4 id="坑2：日志混乱"><a href="#坑2：日志混乱" class="headerlink" title="坑2：日志混乱"></a><strong>坑2：日志混乱</strong></h4><p>多个进程同时写一个日志文件，内容会穿插，格式乱掉。</p><p><strong>解决方案：</strong> 所有进程把日志发到一个队列里，用一个专门的进程负责写文件。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> logging.handlers</span><br><span class="line"></span><br><span class="line">log_queue = multiprocessing.Queue()</span><br><span class="line">handler = logging.handlers.QueueHandler(log_queue)</span><br><span class="line"><span class="comment"># 另一个进程从队列取日志，写入文件</span></span><br></pre></td></tr></table></figure><h4 id="坑3：死锁"><a href="#坑3：死锁" class="headerlink" title="坑3：死锁"></a><strong>坑3：死锁</strong></h4><p>生产者往队列放数据，队列满了阻塞；主进程又在等生产者结束，形成死锁。</p><p><strong>解决方案：</strong> 用不限大小的队列，或者确保消费者及时取数据。</p><h3 id="实战：批量处理-1-万张图片"><a href="#实战：批量处理-1-万张图片" class="headerlink" title="实战：批量处理 1 万张图片"></a>实战：批量处理 1 万张图片</h3><p>一个真实场景——处理 1 万张图片，每张缩放、加水印、调色。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_one</span>(<span class="params">args</span>):</span><br><span class="line">    input_path, output_path, size = args</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">with</span> Image.<span class="built_in">open</span>(input_path) <span class="keyword">as</span> img:</span><br><span class="line">            img.thumbnail(size)</span><br><span class="line">            img.save(output_path, optimize=<span class="literal">True</span>, quality=<span class="number">85</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span>, input_path</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span>, <span class="string">f&quot;<span class="subst">&#123;input_path&#125;</span>: <span class="subst">&#123;e&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">batch_process</span>(<span class="params">input_dir, output_dir</span>):</span><br><span class="line">    tasks = []</span><br><span class="line">    <span class="keyword">for</span> f <span class="keyword">in</span> os.listdir(input_dir):</span><br><span class="line">        <span class="keyword">if</span> f.lower().endswith((<span class="string">&#x27;.png&#x27;</span>, <span class="string">&#x27;.jpg&#x27;</span>, <span class="string">&#x27;.jpeg&#x27;</span>)):</span><br><span class="line">            tasks.append((</span><br><span class="line">                os.path.join(input_dir, f),</span><br><span class="line">                os.path.join(output_dir, f),</span><br><span class="line">                (<span class="number">800</span>, <span class="number">800</span>)</span><br><span class="line">            ))</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 8 个进程并行处理</span></span><br><span class="line">    <span class="keyword">with</span> multiprocessing.Pool(<span class="number">8</span>) <span class="keyword">as</span> pool:</span><br><span class="line">        results = pool.<span class="built_in">map</span>(process_one, tasks)</span><br><span class="line">    </span><br><span class="line">    success = <span class="built_in">sum</span>(<span class="number">1</span> <span class="keyword">for</span> ok, _ <span class="keyword">in</span> results <span class="keyword">if</span> ok)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;成功: <span class="subst">&#123;success&#125;</span>/<span class="subst">&#123;<span class="built_in">len</span>(tasks)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    batch_process(<span class="string">&quot;./raw&quot;</span>, <span class="string">&quot;./processed&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>设计思路：</strong></p><ul><li>每张图片独立处理 → 天然可并行</li><li>进程数设为 8（假设机器 8 核）</li><li>单张失败不影响整体</li><li>若需要进度条，可用 <code>tqdm</code> 配合回调</li></ul>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/24.%E5%A4%9A%E8%BF%9B%E7%A8%8B/</id>
    <link href="https://zhhll.com.cn/2026/Python/24.%E5%A4%9A%E8%BF%9B%E7%A8%8B/"/>
    <published>2026-05-21T09:40:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="Python-多进程：8-核-CPU-只有-1-个在跑？那是-GIL-在”捣乱”"><a href="#Python-多进程：8-核-CPU-只有-1-个在跑？那是-GIL-在”捣乱”" class="headerlink" title="Python 多进程：8 核 CPU 只有 1 个在跑？那是 GIL 在”捣乱”"></a>Python 多进程：8 核 CPU 只有 1 个在跑？那是 GIL 在”捣乱”</h1><p>写了一个计算密集型的程序，跑起来 CPU 占用 100%，但仔细一看——8 个核心只有 1 个在满负荷运转，剩下 7 个几乎闲置。</p>
<p>这不是程序写得不对，是 Python 的 <strong>GIL（全局解释器锁）</strong> 在”从中作梗”。GIL 规定：<strong>同一时刻，只有一个线程能执行 Python 代码</strong>。所以多线程在计算任务上，形同虚设。</p>
<p>多进程是绕过 GIL 的方案——每个进程有自己独立的 Python 解释器，各自跑各自的，操作系统把它们调度到不同的 CPU 核心上，真正实现了并行。</p>
<p><strong>什么场景用多进程？</strong> CPU 密集型——大量计算、加密解密、图像处理、数据压缩。这种任务”算”的时间多，”等”的时间少，多线程没用，多进程才是答案。</p>
<h3 id="先看证据：多线程-vs-多进程，差距一目了然"><a href="#先看证据：多线程-vs-多进程，差距一目了然" class="headerlink" title="先看证据：多线程 vs 多进程，差距一目了然"></a>先看证据：多线程 vs 多进程，差距一目了然</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">cpu_work</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;纯计算，不干别的&quot;&quot;&quot;</span></span><br><span class="line">    count = <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, n):</span><br><span class="line">        is_prime = <span class="built_in">all</span>(i % j != <span class="number">0</span> <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>, <span class="built_in">int</span>(i**<span class="number">0.5</span>) + <span class="number">1</span>))</span><br><span class="line">        <span class="keyword">if</span> is_prime:</span><br><span class="line">            count += <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> count</span><br><span class="line"></span><br><span class="line"><span class="comment"># 单线程</span></span><br><span class="line">start = time.time()</span><br><span class="line">cpu_work(<span class="number">100000</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;单线程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多线程（4个）</span></span><br><span class="line">start = time.time()</span><br><span class="line"><span class="keyword">with</span> ThreadPoolExecutor(<span class="number">4</span>) <span class="keyword">as</span> ex:</span><br><span class="line">    [cpu_work(<span class="number">100000</span>) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">4</span>)]</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;多线程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 比单线程还慢！</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多进程（4个）</span></span><br><span class="line">start = time.time()</span><br><span class="line"><span class="keyword">with</span> multiprocessing.Pool(<span class="number">4</span>) <span class="keyword">as</span> pool:</span><br><span class="line">    pool.<span class="built_in">map</span>(cpu_work, [<span class="number">100000</span>] * <span class="number">4</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;多进程: <span class="subst">&#123;time.time() - start:<span class="number">.2</span>f&#125;</span>s&quot;</span>)   <span class="comment"># 约单线程的 1/4</span></span><br></pre></td></tr></table></figure>
<p><strong>典型结果：</strong></p>
<figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">单线程: 2.35s</span></span><br><span class="line"><span class="section">多线程: 2.78s   ← 不仅没快，还慢了</span></span><br><span class="line"><span class="section">多进程: 0.68s   ← 接近 4 倍提速</span></span><br></pre></td></tr></table></figure>
<p>多线程在计算任务上不仅无效，线程切换还带来额外开销。多进程才是真正的并行。</p>
<h3 id="多进程池：别反复创建进程，用池复用"><a href="#多进程池：别反复创建进程，用池复用" class="headerlink" title="多进程池：别反复创建进程，用池复用"></a>多进程池：别反复创建进程，用池复用</h3><p>每次创建进程都要分配内存、启动解释器，开销不小。实际开发用<strong>进程池</strong>——预先创建一批进程，任务来了分配，干完了回收。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> multiprocessing</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="keyword">return</span> n ** <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    <span class="comment"># 默认进程数 = CPU 核心数</span></span><br><span class="line">    <span class="keyword">with</span> multiprocessing.Pool() <span class="keyword">as</span> pool:</span><br><span class="line">        <span class="comment"># map：批量提交，保持顺序</span></span><br><span class="line">        results = pool.<span class="built_in">map</span>(worker, <span class="built_in">range</span>(<span class="number">100</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># apply_async：异步提交单个任务</span></span><br><span class="line">        async_result = pool.apply_async(worker, (<span class="number">10</span>,))</span><br><span class="line">        result = async_result.get()  <span class="comment"># 获取结果（会阻塞）</span></span><br></pre></td></tr></table></figure>
<p><strong>进程数设多少？</strong></p>
<div class="table-container">
<table>
<thead>
<tr>
<th style="text-align:left">任务类型</th>
<th style="text-align:left">建议进程数</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">CPU 密集型</td>
<td style="text-align:left">CPU 核心数</td>
</tr>
<tr>
<td style="text-align:left">I/O 密集型（但不想用异步）</td>
<td style="text-align:left">CPU 核心数 × 2</td>
</tr>
<tr>
<td style="text-align:left">内存密集型</td>
<td style="text-align:left">CPU 核心数 ÷ 2</td>
</tr>
</tbody>
</table>
</div>
<p>设多了没用，操作系统频繁切换进程反而拖慢速度。</p>]]>
    </summary>
    <title>多进程</title>
    <updated>2026-07-03T10:28:57.853Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h1 id="深度学习三大支柱：CNN、RNN-与预训练语言模型"><a href="#深度学习三大支柱：CNN、RNN-与预训练语言模型" class="headerlink" title="深度学习三大支柱：CNN、RNN 与预训练语言模型"></a>深度学习三大支柱：CNN、RNN 与预训练语言模型</h1><blockquote><p><strong>核心认知</strong>：深度学习并非单一技术，而是由不同架构组成的“武器库”——CNN 擅长捕捉空间特征（图像），RNN 擅长处理时序依赖（文本、语音），而预训练语言模型（PLM）则通过“预训练+微调”范式，彻底改变了自然语言处理的方式。</p></blockquote><p>本文将从原理到应用，系统讲解三种核心深度学习架构，帮助你建立完整的知识体系。</p><hr><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ol><li><a href="#卷积神经网络cnn">卷积神经网络（CNN）</a></li><li><a href="#循环神经网络rnn与双向rnn">循环神经网络（RNN）与双向RNN</a></li><li><a href="#预训练语言模型与微调">预训练语言模型与微调</a></li><li><a href="#三者对比与选型">三者对比与选型</a></li></ol><hr><h2 id="卷积神经网络（CNN）"><a href="#卷积神经网络（CNN）" class="headerlink" title="卷积神经网络（CNN）"></a>卷积神经网络（CNN）</h2><h3 id="为什么需要-CNN？"><a href="#为什么需要-CNN？" class="headerlink" title="为什么需要 CNN？"></a>为什么需要 CNN？</h3><p>传统的全连接神经网络在处理图像时会遇到两个致命问题：</p><div class="table-container"><table><thead><tr><th>问题</th><th>说明</th><th>后果</th></tr></thead><tbody><tr><td><strong>参数爆炸</strong></td><td>一张 224×224 的彩色图片有 150,528 个像素，全连接层的参数数量 = 输入维度 × 输出维度</td><td>需要海量数据和计算资源</td></tr><tr><td><strong>丢失空间结构</strong></td><td>全连接层将像素展平为一维向量，忽略像素间的空间关系</td><td>无法识别边缘、形状等局部模式</td></tr></tbody></table></div><p><strong>CNN 的解决方案</strong>：通过<strong>局部连接</strong>和<strong>权重共享</strong>，大幅减少参数数量，同时保留空间结构信息。</p><h3 id="CNN-的核心组件"><a href="#CNN-的核心组件" class="headerlink" title="CNN 的核心组件"></a>CNN 的核心组件</h3><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">┌─────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                        CNN 整体架构                                  │</span><br><span class="line">├─────────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                     │</span><br><span class="line">│   输入图像        卷积层       池化层       卷积层       池化层      全连接层      输出   │</span><br><span class="line">│                                                                     │</span><br><span class="line">│   ┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐      ┌─┐   │</span><br><span class="line">│   │     │ ──→ │     │ ──→ │     │ ──→ │     │ ──→ │     │ ──→ │     │ ──→ │ │   │</span><br><span class="line">│   │<span class="number"> 224 </span>│      │<span class="number"> 222 </span>│      │<span class="number"> 111 </span>│      │<span class="number"> 110 </span>│      │<span class="number"> 55 </span> │      │ 4096│      │10│   │</span><br><span class="line">│   │ ×224│      │ ×222│      │ ×111│      │ ×110│      │ ×55 │      │     │      │ │   │</span><br><span class="line">│   │ ×3  │      │ ×32 │      │ ×32 │      │ ×64 │      │ ×64 │      │     │      └─┘   │</span><br><span class="line">│   └─────┘      └─────┘      └─────┘      └─────┘      └─────┘      └─────┘            │</span><br><span class="line">│    <span class="number"> 3 </span>           <span class="number"> 32 </span>         <span class="number"> 32 </span>         <span class="number"> 64 </span>         <span class="number"> 64 </span>        <span class="number"> 4096 </span>          <span class="number"> 10 </span> │</span><br><span class="line">│   (RGB通道)    (特征图)     (降维后)      (特征图)      (降维后)      (特征向量)      (类别) │</span><br><span class="line">│                                                                     │</span><br><span class="line">└─────────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h4 id="1-卷积层（Convolutional-Layer）"><a href="#1-卷积层（Convolutional-Layer）" class="headerlink" title="1. 卷积层（Convolutional Layer）"></a>1. 卷积层（Convolutional Layer）</h4><p><strong>卷积核（Filter/Kernel）</strong>：一个小的权重矩阵（如 3×3 或 5×5），在输入图像上滑动，计算局部区域的点积，生成特征图。</p><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">卷积操作可视化（步长=1，无填充）</span><br><span class="line"></span><br><span class="line">输入图像 (5×5)              卷积核 (3×3)              输出特征图 (3×3)</span><br><span class="line">┌─────────────────┐        ┌─────────┐              ┌─────────────────┐</span><br><span class="line">│<span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 0 </span>│        │<span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 1 </span>│              │<span class="number"> 4 </span><span class="number"> 3 </span><span class="number"> 4 </span>│</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span>│   ⊙    │<span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 0 </span>│     =      │<span class="number"> 2 </span><span class="number"> 4 </span><span class="number"> 3 </span>│</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 1 </span>│        │<span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 1 </span>│              │<span class="number"> 2 </span><span class="number"> 3 </span><span class="number"> 4 </span>│</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span>│        └─────────┘              └─────────────────┘</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 0 </span>│</span><br><span class="line">└─────────────────┘</span><br><span class="line"></span><br><span class="line">每个输出位置 = 卷积核与输入对应区域的逐元素乘积之和</span><br></pre></td></tr></table></figure><p><strong>关键参数</strong>：</p><span id="more"></span><ul><li><strong>卷积核大小</strong>：常见 3×3、5×5、7×7</li><li><strong>步长（Stride）</strong>：卷积核滑动的步长（通常为 1 或 2）</li><li><strong>填充（Padding）</strong>：在输入边缘补零，控制输出尺寸</li><li><strong>通道数</strong>：输入 RGB 有 3 个通道，输出可以有多个卷积核（如 32、64、128 个）</li></ul><p><strong>为什么有效</strong>：</p><ul><li>每个卷积核专门检测一种局部模式（边缘、角点、纹理）</li><li>多个卷积核堆叠，从低级到高级逐层抽象</li></ul><h4 id="2-激活函数（Activation-Function）"><a href="#2-激活函数（Activation-Function）" class="headerlink" title="2. 激活函数（Activation Function）"></a>2. 激活函数（Activation Function）</h4><p>激活函数就是把线性函数转化为非线性函数。引入非线性，使神经网络能够学习复杂模式。</p><div class="table-container"><table><thead><tr><th>函数</th><th>公式</th><th>特点</th></tr></thead><tbody><tr><td><strong>ReLU</strong></td><td><code>max(0, x)</code></td><td>计算快、缓解梯度消失（最常用）</td></tr><tr><td><strong>Sigmoid</strong></td><td><code>1/(1+e^{-x})</code></td><td>输出范围 (0,1)，适合二分类输出层</td></tr><tr><td><strong>Tanh</strong></td><td><code>(e^x-e^{-x})/(e^x+e^{-x})</code></td><td>输出范围 (-1,1)，零中心化</td></tr></tbody></table></div><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ReLU 激活前后对比</span></span><br><span class="line">输入特征图: [-<span class="number">2</span>, -<span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">ReLU 之后:  [<span class="number">0</span>,  <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]  <span class="comment"># 负值归零，正值保留</span></span><br></pre></td></tr></table></figure><h4 id="3-池化层（Pooling-Layer）"><a href="#3-池化层（Pooling-Layer）" class="headerlink" title="3. 池化层（Pooling Layer）"></a>3. 池化层（Pooling Layer）</h4><p><strong>下采样</strong>操作，减少特征图尺寸，降低计算量，增强平移不变性。</p><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">最大池化（Max Pooling）- 2×2 池化核，步长=2</span><br><span class="line"></span><br><span class="line">输入 (4×4)                    输出 (2×2)</span><br><span class="line">┌─────────────────┐          ┌─────────┐</span><br><span class="line">│<span class="number"> 1 </span><span class="number"> 3 </span><span class="number"> 2 </span><span class="number"> 4 </span>│                  │<span class="number"> 3 </span><span class="number"> 4 </span>│</span><br><span class="line">│<span class="number"> 5 </span><span class="number"> 6 </span><span class="number"> 1 </span><span class="number"> 2 </span>│    ──→          │<span class="number"> 7 </span><span class="number"> 8 </span>│</span><br><span class="line">│<span class="number"> 2 </span><span class="number"> 1 </span><span class="number"> 7 </span><span class="number"> 8 </span>│                  └─────────┘</span><br><span class="line">│<span class="number"> 3 </span><span class="number"> 4 </span><span class="number"> 5 </span><span class="number"> 6 </span>│</span><br><span class="line">└─────────────────┘</span><br><span class="line"></span><br><span class="line">每个 2×2 区域取最大值</span><br></pre></td></tr></table></figure><div class="table-container"><table><thead><tr><th>池化类型</th><th>操作</th><th>特点</th></tr></thead><tbody><tr><td><strong>最大池化</strong></td><td>取区域内最大值</td><td>保留最显著特征（最常用）</td></tr><tr><td><strong>平均池化</strong></td><td>取区域内平均值</td><td>保留整体信息</td></tr><tr><td><strong>全局平均池化</strong></td><td>整个特征图取平均</td><td>替代全连接层，减少参数</td></tr></tbody></table></div><h4 id="4-全连接层（Fully-Connected-Layer）"><a href="#4-全连接层（Fully-Connected-Layer）" class="headerlink" title="4. 全连接层（Fully Connected Layer）"></a>4. 全连接层（Fully Connected Layer）</h4><p>将高维特征图展平，通过全连接网络映射到最终的分类结果。</p><h3 id="经典-CNN-架构演进"><a href="#经典-CNN-架构演进" class="headerlink" title="经典 CNN 架构演进"></a>经典 CNN 架构演进</h3><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">时间线 ──────────────────────────────────────────────────────────────────→</span><br><span class="line"></span><br><span class="line">AlexNet (2012)          VGG (2014)           ResNet (2015)        EfficientNet (2019)</span><br><span class="line">     │                      │                      │                      │</span><br><span class="line">     ▼                      ▼                      ▼                      ▼</span><br><span class="line">  ┌─────┐               ┌─────┐                ┌─────┐                ┌─────┐</span><br><span class="line">  │ 8层 │               │ 19层│                │152层│                │ 缩放│</span><br><span class="line">  └─────┘               └─────┘                └─────┘                └─────┘</span><br><span class="line">     │                      │                      │                      │</span><br><span class="line">     ▼                      ▼                      ▼                      ▼</span><br><span class="line">  ILSVRC冠军           结构规整              残差连接              复合缩放</span><br><span class="line">  引入ReLU+Dropout     3×3卷积堆叠          解决梯度消失           精度/速度平衡</span><br></pre></td></tr></table></figure><div class="table-container"><table><thead><tr><th>架构</th><th>年份</th><th>层数</th><th>核心创新</th><th>ImageNet Top-5 错误率</th></tr></thead><tbody><tr><td><strong>AlexNet</strong></td><td>2012</td><td>8</td><td>ReLU、Dropout、GPU 训练</td><td>15.3%</td></tr><tr><td><strong>VGG</strong></td><td>2014</td><td>16-19</td><td>小卷积核（3×3）堆叠</td><td>7.3%</td></tr><tr><td><strong>GoogLeNet</strong></td><td>2014</td><td>22</td><td>Inception 模块（多尺度卷积）</td><td>6.7%</td></tr><tr><td><strong>ResNet</strong></td><td>2015</td><td>50-152</td><td>残差连接（解决梯度消失）</td><td>3.6%</td></tr><tr><td><strong>DenseNet</strong></td><td>2017</td><td>121-264</td><td>密集连接（特征复用）</td><td>3.7%</td></tr><tr><td><strong>EfficientNet</strong></td><td>2019</td><td>可变</td><td>复合缩放（深度×宽度×分辨率）</td><td>2.5%</td></tr></tbody></table></div><h3 id="残差连接（Residual-Connection）原理解析"><a href="#残差连接（Residual-Connection）原理解析" class="headerlink" title="残差连接（Residual Connection）原理解析"></a>残差连接（Residual Connection）原理解析</h3><p>这是 ResNet 的核心创新，解决了深层网络难以训练的问题。</p><figure class="highlight gml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">普通连接（无残差）              残差连接（有残差）</span><br><span class="line"></span><br><span class="line">    <span class="variable language_">x</span>                                <span class="variable language_">x</span></span><br><span class="line">    │                                │</span><br><span class="line">    ▼                                ▼</span><br><span class="line">┌───────┐                       ┌───────┐</span><br><span class="line">│ 层   │                        │ 层   │</span><br><span class="line">└───────┘                       └───────┘</span><br><span class="line">    │                                │</span><br><span class="line">    ▼                                │</span><br><span class="line">┌───────┐                           │</span><br><span class="line">│ 激活  │                           ▼</span><br><span class="line">└───────┘                    ┌───────────┐</span><br><span class="line">    │                        │   + (逐元素相加)   │</span><br><span class="line">    ▼                        └───────────┘</span><br><span class="line">    <span class="variable language_">y</span>                                │</span><br><span class="line">                                     ▼</span><br><span class="line">                                 ┌───────┐</span><br><span class="line">                                 │ 激活  │</span><br><span class="line">                                 └───────┘</span><br><span class="line">                                     │</span><br><span class="line">                                     ▼</span><br><span class="line">                                     <span class="variable language_">y</span></span><br><span class="line"></span><br><span class="line">普通层：<span class="variable language_">y</span> = F(<span class="variable language_">x</span>)                残差层：<span class="variable language_">y</span> = F(<span class="variable language_">x</span>) + <span class="variable language_">x</span></span><br><span class="line">                                      ↑</span><br><span class="line">                                   恒等映射（短路连接）</span><br><span class="line"></span><br><span class="line">学习目标：从学习完整映射 F(<span class="variable language_">x</span>) 降为学习残差 F(<span class="variable language_">x</span>) - <span class="variable language_">x</span></span><br></pre></td></tr></table></figure><p><strong>为什么有效</strong>：</p><ul><li>底层网络至少可以学习到<strong>恒等映射</strong>（残差为0）</li><li>梯度可以直接通过短路连接传播，解决梯度消失</li><li>可以训练超过 1000 层的超深网络</li></ul><h3 id="CNN-的应用场景"><a href="#CNN-的应用场景" class="headerlink" title="CNN 的应用场景"></a>CNN 的应用场景</h3><div class="table-container"><table><thead><tr><th>领域</th><th>应用</th><th>说明</th></tr></thead><tbody><tr><td><strong>计算机视觉</strong></td><td>图像分类、目标检测、语义分割</td><td>自动驾驶、医疗影像</td></tr><tr><td><strong>视频分析</strong></td><td>动作识别、视频分类</td><td>监控分析、体育分析</td></tr><tr><td><strong>医学影像</strong></td><td>X光、CT、MRI 诊断</td><td>肺结节检测、骨折识别</td></tr><tr><td><strong>人脸识别</strong></td><td>人脸检测、人脸验证</td><td>手机解锁、安防监控</td></tr><tr><td><strong>OCR</strong></td><td>文字识别</td><td>文档扫描、车牌识别</td></tr></tbody></table></div><hr><h2 id="循环神经网络（RNN）与双向-RNN"><a href="#循环神经网络（RNN）与双向-RNN" class="headerlink" title="循环神经网络（RNN）与双向 RNN"></a>循环神经网络（RNN）与双向 RNN</h2><h3 id="为什么需要-RNN？"><a href="#为什么需要-RNN？" class="headerlink" title="为什么需要 RNN？"></a>为什么需要 RNN？</h3><p>传统神经网络（包括 CNN）假设输入之间<strong>相互独立</strong>，无法处理<strong>序列数据</strong>（文本、语音、时间序列）。</p><div class="table-container"><table><thead><tr><th>数据类型</th><th>序列特性</th><th>传统网络的问题</th></tr></thead><tbody><tr><td>文本</td><td>单词顺序决定语义</td><td>“猫追老鼠” ≠ “老鼠追猫”</td></tr><tr><td>语音</td><td>时间顺序至关重要</td><td>无法捕捉音频时序依赖</td></tr><tr><td>股票价格</td><td>前后依赖</td><td>无法建模趋势和周期</td></tr></tbody></table></div><p><strong>RNN 的解决方案</strong>：引入<strong>隐藏状态（Hidden State）</strong>，让网络具有“记忆”能力——当前输出依赖历史输入。</p><h3 id="RNN-核心原理"><a href="#RNN-核心原理" class="headerlink" title="RNN 核心原理"></a>RNN 核心原理</h3><figure class="highlight gml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">RNN 单元展开图（时间步）</span><br><span class="line"></span><br><span class="line">         t=<span class="number">0</span>          t=<span class="number">1</span>          t=<span class="number">2</span>          t=<span class="number">3</span></span><br><span class="line">          │            │            │            │</span><br><span class="line">    <span class="variable language_">x</span>₀ ──┐│       <span class="variable language_">x</span>₁ ──┐│       <span class="variable language_">x</span>₂ ──┐│       <span class="variable language_">x</span>₃ ──┐│</span><br><span class="line">         ▼▼            ▼▼            ▼▼            ▼▼</span><br><span class="line">    ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐</span><br><span class="line">    │  RNN    │   │  RNN    │   │  RNN    │   │  RNN    │</span><br><span class="line">    │  单元   │←─│  单元   │←─│  单元   │←─│  单元   │</span><br><span class="line">    └─────────┘   └─────────┘   └─────────┘   └─────────┘</span><br><span class="line">         │            │            │            │</span><br><span class="line">         ▼            ▼            ▼            ▼</span><br><span class="line">        <span class="variable language_">y</span>₀           <span class="variable language_">y</span>₁           <span class="variable language_">y</span>₂           <span class="variable language_">y</span>₃</span><br><span class="line"></span><br><span class="line">             隐藏状态 h_t 在时间步之间传递</span><br><span class="line">             h_t = f(W_h·h_&#123;t<span class="number">-1</span>&#125; + W_x·x_t + b)</span><br></pre></td></tr></table></figure><p><strong>核心公式</strong>：<br><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">h_t</span> = tanh(W_hh · h_&#123;t-<span class="number">1</span>&#125; + W_xh · x_t + b_h)    <span class="comment"># 隐藏状态更新</span></span><br><span class="line"><span class="attr">y_t</span> = W_hy · h_t + b_y                            <span class="comment"># 输出（可选）</span></span><br></pre></td></tr></table></figure></p><p><strong>关键概念</strong>：</p><ul><li><strong>隐藏状态（h_t）</strong>：网络的“记忆”，编码历史信息</li><li><strong>权重共享</strong>：所有时间步使用相同的 W_hh、W_xh 矩阵</li><li><strong>循环连接</strong>：h_{t-1} → h_t 形成循环，实现记忆</li></ul><h3 id="RNN-的变体"><a href="#RNN-的变体" class="headerlink" title="RNN 的变体"></a>RNN 的变体</h3><h4 id="1-标准-RNN（Vanilla-RNN）"><a href="#1-标准-RNN（Vanilla-RNN）" class="headerlink" title="1. 标准 RNN（Vanilla RNN）"></a>1. 标准 RNN（Vanilla RNN）</h4><figure class="highlight subunit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">特点：</span><br><span class="line"> 结构简单，参数量少</span><br><span class="line"> 梯度消失/爆炸（难以学习长距离依赖）</span><br><span class="line"> 记忆容量有限（约 10<span class="string">-20</span> 个时间步）</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 标准 RNN 的局限性示例</span></span><br><span class="line">输入: <span class="string">&quot;我 在 北京 长大，... (省略500字) ... 我 喜欢 吃 ？&quot;</span></span><br><span class="line">                           ↑</span><br><span class="line">                  难以记住前面的<span class="string">&quot;北京&quot;</span></span><br></pre></td></tr></table></figure><h4 id="2-LSTM（长短期记忆网络）"><a href="#2-LSTM（长短期记忆网络）" class="headerlink" title="2. LSTM（长短期记忆网络）"></a>2. LSTM（长短期记忆网络）</h4><p>LSTM 通过<strong>门控机制</strong>解决了梯度消失问题，能够学习长达数百个时间步的依赖。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">LSTM 单元结构图</span><br><span class="line"></span><br><span class="line">                    ┌─────────────────────────────────────────┐</span><br><span class="line">                    │              LSTM 单元                   │</span><br><span class="line">                    │                                         │</span><br><span class="line">    h_&#123;t<span class="number">-1</span>&#125; ────────┼───┐    ┌──────┐    ┌──────┐    ┌──────┐  │</span><br><span class="line">                    │   │    │      │    │      │    │      │  │</span><br><span class="line">    C_&#123;t<span class="number">-1</span>&#125; ────────┼───┼────│遗忘门│────│输入门│────│输出门│──┼──→ C_t</span><br><span class="line">                    │   │    │      │    │      │    │      │  │</span><br><span class="line">                    │   │    └──────┘    └──────┘    └──────┘  │</span><br><span class="line">                    │   │        │           │           │      │</span><br><span class="line">    <span class="type">x_t</span> ────────────┼───┴────────┴───────────┴───────────┴──────┼──→ <span class="type">h_t</span></span><br><span class="line">                    │                                         │</span><br><span class="line">                    └─────────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">三个门控机制：</span><br><span class="line">┌─────────────────────────────────────────────────────────────┐</span><br><span class="line">│ 遗忘门：决定从细胞状态中丢弃什么信息                          │</span><br><span class="line">│   <span class="type">f_t</span> = σ(W_f·[h_&#123;t<span class="number">-1</span>&#125;, <span class="type">x_t</span>] + b_f)                        │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│ 输入门：决定将哪些新信息存入细胞状态                          │</span><br><span class="line">│   <span class="type">i_t</span> = σ(W_i·[h_&#123;t<span class="number">-1</span>&#125;, <span class="type">x_t</span>] + b_i)                        │</span><br><span class="line">│   C̃<span class="type">_t</span> = <span class="built_in">tanh</span>(W_c·[h_&#123;t<span class="number">-1</span>&#125;, <span class="type">x_t</span>] + b_c)                     │</span><br><span class="line">├─────────────────────────────────────────────────────────────┤</span><br><span class="line">│ 输出门：决定输出哪些信息                                      │</span><br><span class="line">│   <span class="type">o_t</span> = σ(W_o·[h_&#123;t<span class="number">-1</span>&#125;, <span class="type">x_t</span>] + b_o)                        │</span><br><span class="line">│   <span class="type">h_t</span> = <span class="type">o_t</span> * <span class="built_in">tanh</span>(C_t)                                     │</span><br><span class="line">└─────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p><strong>LSTM vs 标准 RNN</strong>：</p><div class="table-container"><table><thead><tr><th>特性</th><th>标准 RNN</th><th>LSTM</th></tr></thead><tbody><tr><td>长距离依赖</td><td>难以学习（&gt;20步）</td><td>可以学习（&gt;500步）</td></tr><tr><td>梯度消失</td><td>严重</td><td>大幅缓解</td></tr><tr><td>参数量</td><td>少</td><td>多（约4倍）</td></tr><tr><td>计算复杂度</td><td>低</td><td>较高</td></tr><tr><td>适用场景</td><td>短序列、简单任务</td><td>长序列、复杂任务</td></tr></tbody></table></div><h4 id="3-GRU（门控循环单元）"><a href="#3-GRU（门控循环单元）" class="headerlink" title="3. GRU（门控循环单元）"></a>3. GRU（门控循环单元）</h4><p>LSTM 的简化版本，保留核心门控机制，参数更少。</p><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">GRU vs LSTM：</span><br><span class="line"><span class="bullet">- </span>LSTM：3个门（遗忘、输入、输出）+ 独立的细胞状态 C</span><br><span class="line"><span class="bullet">- </span>GRU：2个门（更新门、重置门）+ 隐藏状态 H</span><br><span class="line"><span class="bullet">- </span>GRU 参数更少，训练更快，效果通常与 LSTM 相当</span><br></pre></td></tr></table></figure><h3 id="双向-RNN（Bidirectional-RNN）"><a href="#双向-RNN（Bidirectional-RNN）" class="headerlink" title="双向 RNN（Bidirectional RNN）"></a>双向 RNN（Bidirectional RNN）</h3><p>标准 RNN 只能利用<strong>过去的信息</strong>，无法看到“未来”。双向 RNN 通过两个方向的信息流解决这个问题。</p><figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">双向 RNN 结构图</span><br><span class="line"></span><br><span class="line">       前向 RNN ──────────────────────────────────────→</span><br><span class="line">                ┌─────────┐    ┌─────────┐    ┌─────────┐</span><br><span class="line">        x₀ ───→│  RNN    │───→│  RNN    │───→│  RNN    │───→</span><br><span class="line">                └─────────┘    └─────────┘    └─────────┘</span><br><span class="line">                     │              │              │</span><br><span class="line">                     ▼              ▼              ▼</span><br><span class="line">                ┌─────┐        ┌─────┐        ┌─────┐</span><br><span class="line">                │ <span class="built_in">concat</span>        │ <span class="built_in">concat</span>        │ <span class="built_in">concat</span></span><br><span class="line">                └─────┘        └─────┘        └─────┘</span><br><span class="line">                     ▲              ▲              ▲</span><br><span class="line">                ┌─────────┐    ┌─────────┐    ┌─────────┐</span><br><span class="line">        x₀ ───→│  RNN    │───→│  RNN    │───→│  RNN    │───→</span><br><span class="line">                └─────────┘    └─────────┘    └─────────┘</span><br><span class="line">        ←──────────────────────────────────────────────</span><br><span class="line">                   反向 RNN</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">输出 y_t = <span class="built_in">concat</span>(前向隐藏状态 h_t→, 反向隐藏状态 h_t←)</span><br><span class="line"></span><br><span class="line">优势：每个时间步的输出同时利用了上下文信息（过去+未来）</span><br></pre></td></tr></table></figure><p><strong>应用场景</strong>：</p><div class="table-container"><table><thead><tr><th>任务</th><th>为什么需要双向</th><th>示例</th></tr></thead><tbody><tr><td>命名实体识别</td><td>需要上下文判断”Apple”是公司还是水果</td><td>“Apple stock” vs “apple fruit”</td></tr><tr><td>情感分析</td><td>语义依赖前后文</td><td>“not good” 整体为负面</td></tr><tr><td>机器翻译</td><td>目标语言同时依赖源语言前后文</td><td>句法结构需整体理解</td></tr><tr><td>语音识别</td><td>音素依赖前后发音</td><td>连读、变调需要上下文</td></tr></tbody></table></div><h3 id="RNN-的应用场景"><a href="#RNN-的应用场景" class="headerlink" title="RNN 的应用场景"></a>RNN 的应用场景</h3><div class="table-container"><table><thead><tr><th>领域</th><th>任务</th><th>推荐架构</th></tr></thead><tbody><tr><td><strong>自然语言处理</strong></td><td>文本分类、情感分析</td><td>BiLSTM + Attention</td></tr><tr><td><strong>机器翻译</strong></td><td>序列到序列</td><td>LSTM/GRU + Attention + Transformer</td></tr><tr><td><strong>语音识别</strong></td><td>语音转文字</td><td>BiLSTM + CTC</td></tr><tr><td><strong>时间序列预测</strong></td><td>股票价格、天气</td><td>LSTM</td></tr><tr><td><strong>命名实体识别</strong></td><td>人名、地名识别</td><td>BiLSTM + CRF</td></tr></tbody></table></div><hr><h2 id="预训练语言模型与微调"><a href="#预训练语言模型与微调" class="headerlink" title="预训练语言模型与微调"></a>预训练语言模型与微调</h2><h3 id="NLP-范式的演进"><a href="#NLP-范式的演进" class="headerlink" title="NLP 范式的演进"></a>NLP 范式的演进</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">时间线 ──────────────────────────────────────────────────────────────────→</span><br><span class="line"></span><br><span class="line">规则时代 (<span class="number">1980</span>-<span class="number">1990</span>)     统计时代 (<span class="number">1990</span>-<span class="number">2018</span>)    预训练时代 (<span class="number">2018</span>-至今)</span><br><span class="line">        │                       │                        │</span><br><span class="line">        ▼                       ▼                        ▼</span><br><span class="line">    ┌──────┐                ┌──────┐                 ┌──────┐</span><br><span class="line">    │ 人工 │                │ 特征 │                 │ 预训练 │</span><br><span class="line">    │ 规则 │                │ 工程 │                 │ +微调 │</span><br><span class="line">    └──────┘                └──────┘                 └──────┘</span><br><span class="line">        │                       │                        │</span><br><span class="line">        ▼                       ▼                        ▼</span><br><span class="line">    • 词典匹配              • TF-IDF                • BERT</span><br><span class="line">    • 正则表达式            • 词向量(Word2Vec)       • GPT</span><br><span class="line">    • 语法规则              • 传统ML模型             • RoBERTa</span><br><span class="line">                                                     • T5</span><br></pre></td></tr></table></figure><div class="table-container"><table><thead><tr><th>范式</th><th>核心思想</th><th>代表技术</th><th>缺点</th></tr></thead><tbody><tr><td><strong>规则时代</strong></td><td>人工编写语言规则</td><td>正则表达式、词典</td><td>无法泛化，维护成本高</td></tr><tr><td><strong>统计时代</strong></td><td>特征工程 + 传统ML</td><td>TF-IDF、Word2Vec、SVM</td><td>特征工程依赖人工</td></tr><tr><td><strong>预训练时代</strong></td><td>通用语言知识 + 下游微调</td><td>BERT、GPT、RoBERTa</td><td>计算资源需求大</td></tr></tbody></table></div><h3 id="预训练-微调范式核心思想"><a href="#预训练-微调范式核心思想" class="headerlink" title="预训练-微调范式核心思想"></a>预训练-微调范式核心思想</h3><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">┌─────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                      预训练 + 微调范式                               │</span><br><span class="line">├─────────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                     │</span><br><span class="line">│  阶段<span class="number">1</span>：预训练（Pre-training）                                       │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │                        海量无标注文本                         │   │</span><br><span class="line">│  │              (Wikipedia, Books, Web Text)                    │   │</span><br><span class="line">│  │                           │                                  │   │</span><br><span class="line">│  │                           ▼                                  │   │</span><br><span class="line">│  │                    ┌─────────────┐                           │   │</span><br><span class="line">│  │                    │  预训练任务  │                           │   │</span><br><span class="line">│  │                    │ (MLM/NSP/LM) │                           │   │</span><br><span class="line">│  │                    └─────────────┘                           │   │</span><br><span class="line">│  │                           │                                  │   │</span><br><span class="line">│  │                           ▼                                  │   │</span><br><span class="line">│  │                    ┌─────────────┐                           │   │</span><br><span class="line">│  │                    │   基础模型   │ ← 学习通用语言知识         │   │</span><br><span class="line">│  │                    └─────────────┘                           │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                           │                                         │</span><br><span class="line">│                           │ 迁移                                     │</span><br><span class="line">│                           ▼                                         │</span><br><span class="line">│  阶段<span class="number">2</span>：微调（Fine-tuning）                                         │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │                    少量标注数据（下游任务）                    │   │</span><br><span class="line">│  │         (情感分类、命名实体识别、问答、翻译)                   │   │</span><br><span class="line">│  │                           │                                  │   │</span><br><span class="line">│  │                           ▼                                  │   │</span><br><span class="line">│  │                    ┌─────────────┐                           │   │</span><br><span class="line">│  │                    │   微调过程   │ ← 适配具体任务             │   │</span><br><span class="line">│  │                    └─────────────┘                           │   │</span><br><span class="line">│  │                           │                                  │   │</span><br><span class="line">│  │                           ▼                                  │   │</span><br><span class="line">│  │                    ┌─────────────┐                           │   │</span><br><span class="line">│  │                    │  任务专用模型 │                          │   │</span><br><span class="line">│  │                    └─────────────┘                           │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                                                                     │</span><br><span class="line">└─────────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p><strong>核心思想</strong>：</p><ol><li><strong>预训练</strong>：在海量无标注文本上学习通用语言知识（语法、语义、常识）</li><li><strong>微调</strong>：在少量标注数据上适配特定下游任务</li><li><strong>迁移</strong>：将通用知识迁移到具体应用，大幅降低数据需求</li></ol><h3 id="核心预训练模型"><a href="#核心预训练模型" class="headerlink" title="核心预训练模型"></a>核心预训练模型</h3><h4 id="BERT（Bidirectional-Encoder-Representations-from-Transformers）"><a href="#BERT（Bidirectional-Encoder-Representations-from-Transformers）" class="headerlink" title="BERT（Bidirectional Encoder Representations from Transformers）"></a>BERT（Bidirectional Encoder Representations from Transformers）</h4><p>Google 2018 年提出，革命性的双向编码器模型。</p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">BERT</span> 预训练任务</span><br><span class="line"></span><br><span class="line">任务<span class="number">1</span>：掩码语言模型（<span class="symbol">MLM</span> - <span class="symbol">Masked</span> <span class="symbol">Language</span> <span class="symbol">Model</span>）</span><br><span class="line">┌─────────────────────────────────────────────────────────────────┐</span><br><span class="line">│  输入: [<span class="symbol">CLS</span>] 我 爱 [<span class="symbol">MASK</span>] 京 [<span class="symbol">SEP</span>] 我 爱 中 国 [<span class="symbol">SEP</span>]              │</span><br><span class="line">│                    ↑                                            │</span><br><span class="line">│                被掩码的词                                       │</span><br><span class="line">│                                                                 │</span><br><span class="line">│  预测: [<span class="symbol">MASK</span>] 位置应该是什么词？ → <span class="string">&quot;北&quot;</span>                          │</span><br><span class="line">│                                                                 │</span><br><span class="line">│  作用：让模型学习双向上下文理解                                   │</span><br><span class="line">└─────────────────────────────────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">任务<span class="number">2</span>：下一句预测（<span class="symbol">NSP</span> - <span class="symbol">Next</span> <span class="symbol">Sentence</span> <span class="symbol">Prediction</span>）</span><br><span class="line">┌─────────────────────────────────────────────────────────────────┐</span><br><span class="line">│  输入: [<span class="symbol">CLS</span>] 今天天气很好 [<span class="symbol">SEP</span>] 我们去公园吧 [<span class="symbol">SEP</span>]                 │</span><br><span class="line">│                     ↑              ↑                            │</span><br><span class="line">│                  句子<span class="symbol">A</span>           句子<span class="symbol">B</span>                          │</span><br><span class="line">│                                                                 │</span><br><span class="line">│  预测：句子<span class="symbol">B</span>是否是句子<span class="symbol">A</span>的下一句？ → <span class="symbol">IsNext</span>（是）                  │</span><br><span class="line">│                                                                 │</span><br><span class="line">│  作用：让模型学习句子间的关系                                     │</span><br><span class="line">└─────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p><strong>BERT 的核心创新</strong>：</p><ul><li><strong>双向编码</strong>：同时利用左右上下文（区别于 GPT 的单向）</li><li><strong>Transformer 架构</strong>：自注意力机制，并行计算</li><li><strong>通用性强</strong>：一个模型适配多种 NLP 任务</li></ul><h4 id="GPT（Generative-Pre-trained-Transformer）"><a href="#GPT（Generative-Pre-trained-Transformer）" class="headerlink" title="GPT（Generative Pre-trained Transformer）"></a>GPT（Generative Pre-trained Transformer）</h4><p>OpenAI 开发的生成式预训练模型，擅长文本生成。</p><div class="table-container"><table><thead><tr><th>对比维度</th><th>BERT</th><th>GPT</th></tr></thead><tbody><tr><td><strong>架构</strong></td><td>仅编码器（Encoder-only）</td><td>仅解码器（Decoder-only）</td></tr><tr><td><strong>注意力方向</strong></td><td>双向（同时看左右）</td><td>单向（只看左边，自回归）</td></tr><tr><td><strong>预训练任务</strong></td><td>MLM + NSP</td><td>语言模型（预测下一个词）</td></tr><tr><td><strong>擅长任务</strong></td><td>理解类（分类、抽取）</td><td>生成类（写作、对话）</td></tr><tr><td><strong>代表模型</strong></td><td>BERT、RoBERTa、ALBERT</td><td>GPT-2、GPT-3、GPT-4</td></tr></tbody></table></div><figure class="highlight inform7"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">架构对比图</span><br><span class="line"></span><br><span class="line">BERT（双向）:                    GPT（单向自回归）:</span><br><span class="line">                               </span><br><span class="line">    <span class="comment">[CLS]</span> 我 爱 <span class="comment">[MASK]</span> 京         我 爱 北 京</span><br><span class="line">      ↑     ↑     ↑     ↑           ↑     ↑     ↑     ↑</span><br><span class="line">      │     │     │     │           │     │     │     │</span><br><span class="line">    ┌─┴──┐ ┌─┴──┐ ┌─┴──┐ ┌─┴──┐   ┌─┴──┐ ┌─┴──┐ ┌─┴──┐ ┌─┴──┐</span><br><span class="line">    │编码│ │编码│ │编码│ │编码│   │解码│ │解码│ │解码│ │解码│</span><br><span class="line">    │层  │ │层  │ │层  │ │层  │   │层  │ │层  │ │层  │ │层  │</span><br><span class="line">    └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘   └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘</span><br><span class="line">      │     │     │     │           │     │     │     │</span><br><span class="line">      └─────┴─────┴─────┘           └─────┘     │     │</span><br><span class="line">         信息双向流动                      只能看到左边的词</span><br></pre></td></tr></table></figure><h4 id="其他重要模型"><a href="#其他重要模型" class="headerlink" title="其他重要模型"></a>其他重要模型</h4><div class="table-container"><table><thead><tr><th>模型</th><th>发布方</th><th>核心特点</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>RoBERTa</strong></td><td>Facebook</td><td>BERT 改进版，更大数据+更长时间训练</td><td>各类理解任务</td></tr><tr><td><strong>ALBERT</strong></td><td>Google</td><td>参数共享，模型更小</td><td>资源受限场景</td></tr><tr><td><strong>DistilBERT</strong></td><td>Hugging Face</td><td>知识蒸馏，模型缩小40%</td><td>轻量级部署</td></tr><tr><td><strong>T5</strong></td><td>Google</td><td>Text-to-Text 统一框架</td><td>所有任务统一为生成</td></tr><tr><td><strong>XLNet</strong></td><td>CMU/Google</td><td>排列语言模型（PLM）</td><td>长文本理解</td></tr></tbody></table></div><h3 id="微调（Fine-tuning）流程"><a href="#微调（Fine-tuning）流程" class="headerlink" title="微调（Fine-tuning）流程"></a>微调（Fine-tuning）流程</h3><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">┌─────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                        微调流程示例（文本分类）                        │</span><br><span class="line">├─────────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                     │</span><br><span class="line">│  <span class="number">1</span>. 加载预训练模型                                                   │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │  from transformers import AutoModelForSequenceClassification  │   │</span><br><span class="line">│  │  model = AutoModelForSequenceClassification.from_pretrained(  │   │</span><br><span class="line">│  │      <span class="string">&quot;bert-base-chinese&quot;</span>,    <span class="comment"># 预训练模型名称                 │   │</span></span><br><span class="line">│  │      num_labels=<span class="number">2</span>             <span class="comment"># 分类类别数（正面/负面）         │   │</span></span><br><span class="line">│  │  )                                                           │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                           │                                         │</span><br><span class="line">│                           ▼                                         │</span><br><span class="line">│  <span class="number">2</span>. 准备下游任务数据                                                 │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │  训练数据：                                                   │   │</span><br><span class="line">│  │  <span class="string">&quot;这部电影太棒了！&quot;</span> → 正面                                    │   │</span><br><span class="line">│  │  <span class="string">&quot;剧情无聊，浪费时间&quot;</span> → 负面                                  │   │</span><br><span class="line">│  │  <span class="string">&quot;演技出色，特效震撼&quot;</span> → 正面                                  │   │</span><br><span class="line">│  │  ...（只需要几千条标注数据）                                  │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                           │                                         │</span><br><span class="line">│                           ▼                                         │</span><br><span class="line">│  <span class="number">3</span>. 添加任务特定层                                                   │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │  预训练 <span class="keyword">BERT </span>输出 (<span class="number">768</span>维) → 分类头 (<span class="number">2</span>维) → softmax → 概率     │   │</span><br><span class="line">│  │                                                             │   │</span><br><span class="line">│  │  • 预训练部分：参数更新（学习率较小）                         │   │</span><br><span class="line">│  │  • 分类头：参数从头训练（学习率较大）                         │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                           │                                         │</span><br><span class="line">│                           ▼                                         │</span><br><span class="line">│  <span class="number">4</span>. 训练微调                                                         │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │  超参数设置：                                                 │   │</span><br><span class="line">│  │  • 学习率：<span class="number">2</span>e<span class="number">-5</span>（比预训练小<span class="number">10</span><span class="number">-100</span>倍）                        │   │</span><br><span class="line">│  │  • 批次大小：<span class="number">16</span><span class="number">-32</span>                                          │   │</span><br><span class="line">│  │  • 训练轮数：<span class="number">2</span><span class="number">-5</span> epoch                                      │   │</span><br><span class="line">│  │  • 优化器：AdamW                                            │   │</span><br><span class="line">│  │                                                             │   │</span><br><span class="line">│  │  训练过程：                                                  │   │</span><br><span class="line">│  │  for epoch in range(<span class="number">3</span>):                                     │   │</span><br><span class="line">│  │      for <span class="keyword">batch </span>in data_loader:                              │   │</span><br><span class="line">│  │          loss = model(<span class="keyword">batch) </span>                               │   │</span><br><span class="line">│  │          loss.<span class="keyword">backward() </span>                                   │   │</span><br><span class="line">│  │          optimizer.step()                                   │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                           │                                         │</span><br><span class="line">│                           ▼                                         │</span><br><span class="line">│  <span class="number">5</span>. 评估与部署                                                       │</span><br><span class="line">│  ┌─────────────────────────────────────────────────────────────┐   │</span><br><span class="line">│  │  模型保存后可用于：                                           │   │</span><br><span class="line">│  │  • API 服务部署                                              │   │</span><br><span class="line">│  │  • 批量推理                                                  │   │</span><br><span class="line">│  │  • 边缘设备部署                                              │   │</span><br><span class="line">│  └─────────────────────────────────────────────────────────────┘   │</span><br><span class="line">│                                                                     │</span><br><span class="line">└─────────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><h3 id="微调的优势"><a href="#微调的优势" class="headerlink" title="微调的优势"></a>微调的优势</h3><div class="table-container"><table><thead><tr><th>对比维度</th><th>传统方法（从头训练）</th><th>预训练+微调</th></tr></thead><tbody><tr><td><strong>标注数据需求</strong></td><td>数万到数十万</td><td>几百到几千</td></tr><tr><td><strong>训练时间</strong></td><td>数天到数周</td><td>几分钟到几小时</td></tr><tr><td><strong>计算资源</strong></td><td>多 GPU，大规模集群</td><td>单 GPU，普通工作站</td></tr><tr><td><strong>模型性能</strong></td><td>依赖数据量</td><td>通常优于从头训练</td></tr><tr><td><strong>可迁移性</strong></td><td>每个任务单独训练</td><td>一个基础模型适配多任务</td></tr></tbody></table></div><h3 id="应用案例"><a href="#应用案例" class="headerlink" title="应用案例"></a>应用案例</h3><div class="table-container"><table><thead><tr><th>任务</th><th>预训练模型</th><th>微调数据</th><th>效果</th></tr></thead><tbody><tr><td>情感分析</td><td>BERT-base</td><td>10,000 条影评</td><td>准确率 94%</td></tr><tr><td>命名实体识别</td><td>BERT-base</td><td>5,000 条标注</td><td>F1 88%</td></tr><tr><td>问答系统</td><td>RoBERTa</td><td>SQuAD 2.0</td><td>EM 86%</td></tr><tr><td>文本摘要</td><td>T5</td><td>CNN/DailyMail</td><td>ROUGE 42</td></tr><tr><td>文本生成</td><td>GPT-2</td><td>特定领域语料</td><td>高质量生成</td></tr></tbody></table></div><hr><h2 id="三者对比与选型"><a href="#三者对比与选型" class="headerlink" title="三者对比与选型"></a>三者对比与选型</h2><h3 id="核心对比表"><a href="#核心对比表" class="headerlink" title="核心对比表"></a>核心对比表</h3><div class="table-container"><table><thead><tr><th>维度</th><th>CNN</th><th>RNN/LSTM</th><th>预训练模型 (BERT/GPT)</th></tr></thead><tbody><tr><td><strong>擅长数据类型</strong></td><td>图像、空间数据</td><td>时序数据、中等长度文本</td><td>长文本、复杂语言理解</td></tr><tr><td><strong>核心机制</strong></td><td>卷积+池化</td><td>循环隐藏状态</td><td>自注意力 + 预训练</td></tr><tr><td><strong>并行计算</strong></td><td>高</td><td>低（序列依赖）</td><td>高（Transformer）</td></tr><tr><td><strong>长距离依赖</strong></td><td>（感受野有限）</td><td>LSTM 可处理 100-500 步</td><td>可处理 512-2048 步</td></tr><tr><td><strong>参数量</strong></td><td>数百万到数千万</td><td>数百万到数千万</td><td>数亿到数千亿</td></tr><tr><td><strong>训练数据需求</strong></td><td>万到百万</td><td>万到百万</td><td>预训练：千亿级<br>微调：千到万</td></tr><tr><td><strong>硬件需求</strong></td><td>GPU（中）</td><td>GPU（中）</td><td>GPU（高）</td></tr><tr><td><strong>推理速度</strong></td><td>快</td><td>中（串行）</td><td>中（长文本较慢）</td></tr><tr><td><strong>可解释性</strong></td><td>特征可视化</td><td>较难</td><td>很难</td></tr></tbody></table></div><h3 id="选型决策树"><a href="#选型决策树" class="headerlink" title="选型决策树"></a>选型决策树</h3><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">开始</span><br><span class="line">  │</span><br><span class="line">  ▼</span><br><span class="line">数据类型是什么？</span><br><span class="line">  │</span><br><span class="line">  ├── 图像<span class="operator">/</span>视频</span><br><span class="line">  │     │</span><br><span class="line">  │     └── 数据量？</span><br><span class="line">  │           ├── 小（<span class="operator">&lt;</span><span class="number">1</span>万）→ 迁移学习（使用预训练 CNN）</span><br><span class="line">  │           └── 大（<span class="operator">&gt;</span><span class="number">1</span>万）→ 从头训练 CNN</span><br><span class="line">  │</span><br><span class="line">  ├── 时序数据<span class="operator">/</span>中等长度文本（<span class="operator">&lt;</span><span class="number">500</span>词）</span><br><span class="line">  │     │</span><br><span class="line">  │     └── 需要上下文方向？</span><br><span class="line">  │           ├── 只看过去（时间序列预测）→ 标准 RNN<span class="symbol">/LSTM</span></span><br><span class="line">  │           └── 需要双向理解（文本分类）→ BiLSTM</span><br><span class="line">  │</span><br><span class="line">  └── 长文本<span class="operator">/</span>复杂语言任务</span><br><span class="line">        │</span><br><span class="line">        ├── 理解类任务（分类、抽取、QA）</span><br><span class="line">        │     └── 资源充足？→ BERT<span class="symbol">/RoBERTa</span></span><br><span class="line">        │         └── 资源受限？→ DistilBERT<span class="symbol">/AlBERT</span></span><br><span class="line">        │</span><br><span class="line">        └── 生成类任务（写作、翻译、对话）</span><br><span class="line">              └── GPT 系列 <span class="symbol">/</span> T5</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><div class="table-container"><table><thead><tr><th>模型</th><th>一句话总结</th><th>何时使用</th></tr></thead><tbody><tr><td><strong>CNN</strong></td><td>“滑动窗口识别局部特征”</td><td>图像处理、需要快速推理</td></tr><tr><td><strong>RNN/LSTM</strong></td><td>“时序记忆，依赖过去”</td><td>时间序列、语音、中等长度文本</td></tr><tr><td><strong>BiRNN</strong></td><td>“前后文都要看”</td><td>需要上下文理解的任务（NER、情感分析）</td></tr><tr><td><strong>预训练模型</strong></td><td>“先学通用知识，再适配任务”</td><td>NLP 任务，特别是标注数据有限时</td></tr></tbody></table></div>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/7.%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E4%B8%89%E5%A4%A7%E6%94%AF%E6%9F%B1/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/7.%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E4%B8%89%E5%A4%A7%E6%94%AF%E6%9F%B1/"/>
    <published>2026-05-19T12:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="深度学习三大支柱：CNN、RNN-与预训练语言模型"><a href="#深度学习三大支柱：CNN、RNN-与预训练语言模型" class="headerlink" title="深度学习三大支柱：CNN、RNN 与预训练语言模型"></a>深度学习三大支柱：CNN、RNN 与预训练语言模型</h1><blockquote>
<p><strong>核心认知</strong>：深度学习并非单一技术，而是由不同架构组成的“武器库”——CNN 擅长捕捉空间特征（图像），RNN 擅长处理时序依赖（文本、语音），而预训练语言模型（PLM）则通过“预训练+微调”范式，彻底改变了自然语言处理的方式。</p>
</blockquote>
<p>本文将从原理到应用，系统讲解三种核心深度学习架构，帮助你建立完整的知识体系。</p>
<hr>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ol>
<li><a href="#卷积神经网络cnn">卷积神经网络（CNN）</a></li>
<li><a href="#循环神经网络rnn与双向rnn">循环神经网络（RNN）与双向RNN</a></li>
<li><a href="#预训练语言模型与微调">预训练语言模型与微调</a></li>
<li><a href="#三者对比与选型">三者对比与选型</a></li>
</ol>
<hr>
<h2 id="卷积神经网络（CNN）"><a href="#卷积神经网络（CNN）" class="headerlink" title="卷积神经网络（CNN）"></a>卷积神经网络（CNN）</h2><h3 id="为什么需要-CNN？"><a href="#为什么需要-CNN？" class="headerlink" title="为什么需要 CNN？"></a>为什么需要 CNN？</h3><p>传统的全连接神经网络在处理图像时会遇到两个致命问题：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>问题</th>
<th>说明</th>
<th>后果</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>参数爆炸</strong></td>
<td>一张 224×224 的彩色图片有 150,528 个像素，全连接层的参数数量 = 输入维度 × 输出维度</td>
<td>需要海量数据和计算资源</td>
</tr>
<tr>
<td><strong>丢失空间结构</strong></td>
<td>全连接层将像素展平为一维向量，忽略像素间的空间关系</td>
<td>无法识别边缘、形状等局部模式</td>
</tr>
</tbody>
</table>
</div>
<p><strong>CNN 的解决方案</strong>：通过<strong>局部连接</strong>和<strong>权重共享</strong>，大幅减少参数数量，同时保留空间结构信息。</p>
<h3 id="CNN-的核心组件"><a href="#CNN-的核心组件" class="headerlink" title="CNN 的核心组件"></a>CNN 的核心组件</h3><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">┌─────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                        CNN 整体架构                                  │</span><br><span class="line">├─────────────────────────────────────────────────────────────────────┤</span><br><span class="line">│                                                                     │</span><br><span class="line">│   输入图像        卷积层       池化层       卷积层       池化层      全连接层      输出   │</span><br><span class="line">│                                                                     │</span><br><span class="line">│   ┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐      ┌─┐   │</span><br><span class="line">│   │     │ ──→ │     │ ──→ │     │ ──→ │     │ ──→ │     │ ──→ │     │ ──→ │ │   │</span><br><span class="line">│   │<span class="number"> 224 </span>│      │<span class="number"> 222 </span>│      │<span class="number"> 111 </span>│      │<span class="number"> 110 </span>│      │<span class="number"> 55 </span> │      │ 4096│      │10│   │</span><br><span class="line">│   │ ×224│      │ ×222│      │ ×111│      │ ×110│      │ ×55 │      │     │      │ │   │</span><br><span class="line">│   │ ×3  │      │ ×32 │      │ ×32 │      │ ×64 │      │ ×64 │      │     │      └─┘   │</span><br><span class="line">│   └─────┘      └─────┘      └─────┘      └─────┘      └─────┘      └─────┘            │</span><br><span class="line">│    <span class="number"> 3 </span>           <span class="number"> 32 </span>         <span class="number"> 32 </span>         <span class="number"> 64 </span>         <span class="number"> 64 </span>        <span class="number"> 4096 </span>          <span class="number"> 10 </span> │</span><br><span class="line">│   (RGB通道)    (特征图)     (降维后)      (特征图)      (降维后)      (特征向量)      (类别) │</span><br><span class="line">│                                                                     │</span><br><span class="line">└─────────────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure>
<h4 id="1-卷积层（Convolutional-Layer）"><a href="#1-卷积层（Convolutional-Layer）" class="headerlink" title="1. 卷积层（Convolutional Layer）"></a>1. 卷积层（Convolutional Layer）</h4><p><strong>卷积核（Filter/Kernel）</strong>：一个小的权重矩阵（如 3×3 或 5×5），在输入图像上滑动，计算局部区域的点积，生成特征图。</p>
<figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">卷积操作可视化（步长=1，无填充）</span><br><span class="line"></span><br><span class="line">输入图像 (5×5)              卷积核 (3×3)              输出特征图 (3×3)</span><br><span class="line">┌─────────────────┐        ┌─────────┐              ┌─────────────────┐</span><br><span class="line">│<span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 0 </span>│        │<span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 1 </span>│              │<span class="number"> 4 </span><span class="number"> 3 </span><span class="number"> 4 </span>│</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span>│   ⊙    │<span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 0 </span>│     =      │<span class="number"> 2 </span><span class="number"> 4 </span><span class="number"> 3 </span>│</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 1 </span>│        │<span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 1 </span>│              │<span class="number"> 2 </span><span class="number"> 3 </span><span class="number"> 4 </span>│</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span>│        └─────────┘              └─────────────────┘</span><br><span class="line">│<span class="number"> 0 </span><span class="number"> 1 </span><span class="number"> 1 </span><span class="number"> 0 </span><span class="number"> 0 </span>│</span><br><span class="line">└─────────────────┘</span><br><span class="line"></span><br><span class="line">每个输出位置 = 卷积核与输入对应区域的逐元素乘积之和</span><br></pre></td></tr></table></figure>
<p><strong>关键参数</strong>：</p>]]>
    </summary>
    <title>深度学习三大支柱</title>
    <updated>2026-06-29T08:28:29.001Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="让-AI-学会“自我反思”：用-LangGraph-构建能不断改进的智能写作-Agent"><a href="#让-AI-学会“自我反思”：用-LangGraph-构建能不断改进的智能写作-Agent" class="headerlink" title="让 AI 学会“自我反思”：用 LangGraph 构建能不断改进的智能写作 Agent"></a>让 AI 学会“自我反思”：用 LangGraph 构建能不断改进的智能写作 Agent</h1><h2 id="一、问题的起源：一次生成真的够吗？"><a href="#一、问题的起源：一次生成真的够吗？" class="headerlink" title="一、问题的起源：一次生成真的够吗？"></a>一、问题的起源：一次生成真的够吗？</h2><p>传统的 AI Agent 通常采用“一次性生成”的模式：用户输入指令 → 模型输出结果 → 结束。这种方式存在一个明显的缺陷：</p><blockquote><p><strong>AI 无法审视自己的输出，也不知道自己写得对不对。</strong></p></blockquote><p>想象一下，如果让一个人类写文章：</p><ol><li>先写初稿 ✍️</li><li>再读一遍，找问题 🤔</li><li>不满意就修改 🔧</li><li>再读，再改，直到满意 ✅</li></ol><p>这种<strong>写作-反思-修改-再反思</strong>的循环，是人类提升作品质量的本能方法。那么，能不能让 AI 也学会这套流程？</p><p>答案是肯定的。<strong>LangGraph</strong> 提供了构建这种循环工作流的能力。</p><h2 id="二、什么是-LangGraph？"><a href="#二、什么是-LangGraph？" class="headerlink" title="二、什么是 LangGraph？"></a>二、什么是 LangGraph？</h2><p>LangGraph 是 LangChain 生态中用于构建<strong>有状态、多步骤、循环式 Agent</strong> 的框架。它的核心概念非常简单：</p><div class="table-container"><table><thead><tr><th>概念</th><th>英文</th><th>说明</th><th>类比</th></tr></thead><tbody><tr><td>状态</td><td>State</td><td>所有节点共享的“记事本”</td><td>白板</td></tr><tr><td>节点</td><td>Node</td><td>一个具体的操作/函数</td><td>工位上的一个员工</td></tr><tr><td>边</td><td>Edge</td><td>节点之间的连线</td><td>员工之间的传话路径</td></tr><tr><td>条件边</td><td>Conditional Edge</td><td>根据状态决定下一步走向</td><td>有决策权的经理</td></tr></tbody></table></div><p>与传统 Agent 最大的区别：LangGraph 天然支持<strong>循环</strong>（Loop），这是实现“自我反思”的基础。</p><h2 id="三、本次实现的工作流"><a href="#三、本次实现的工作流" class="headerlink" title="三、本次实现的工作流"></a>三、本次实现的工作流</h2><p>我们将构建一个具有<strong>自我反思能力</strong>的写作 Agent，它的工作流程如下：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">START</span> → 写作节点 → 反思节点 → 质量评分 ≥<span class="number">8</span>？ </span><br><span class="line">                              ↓</span><br><span class="line">                         是 → <span class="keyword">END</span></span><br><span class="line">                              ↓</span><br><span class="line">                         否 → 修改节点 → (回到反思节点)</span><br></pre></td></tr></table></figure><p>这是一个典型的<strong>反馈循环</strong>架构，广泛用于需要持续优化的场景。</p><span id="more"></span><h2 id="四、完整代码深度解析"><a href="#四、完整代码深度解析" class="headerlink" title="四、完整代码深度解析"></a>四、完整代码深度解析</h2><h3 id="4-1-环境准备"><a href="#4-1-环境准备" class="headerlink" title="4.1 环境准备"></a>4.1 环境准备</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install langgraph langchain-openai</span><br></pre></td></tr></table></figure><h3 id="4-2-定义全局状态（State）"><a href="#4-2-定义全局状态（State）" class="headerlink" title="4.2 定义全局状态（State）"></a>4.2 定义全局状态（State）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> TypedDict, Annotated</span><br><span class="line"><span class="keyword">from</span> langgraph.graph.message <span class="keyword">import</span> add_messages</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">State</span>(<span class="title class_ inherited__">TypedDict</span>):</span><br><span class="line">    messages: Annotated[<span class="built_in">list</span>, add_messages]  <span class="comment"># 对话历史，自动追加</span></span><br><span class="line">    quality_score: <span class="built_in">int</span>  <span class="comment"># 质量评分（0-10分）</span></span><br></pre></td></tr></table></figure><p><strong>关键概念：<code>add_messages</code></strong></p><ul><li>这是一个 reducer 函数，告诉 LangGraph 如何处理新消息</li><li>普通赋值会覆盖，但 <code>add_messages</code> 会<strong>自动追加</strong>到列表末尾</li><li>这使得我们可以保留完整的写作-修改历史</li></ul><h3 id="4-3-节点-1：写作节点（Writer）"><a href="#4-3-节点-1：写作节点（Writer）" class="headerlink" title="4.3 节点 1：写作节点（Writer）"></a>4.3 节点 1：写作节点（Writer）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">writer_node</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot; 正在撰写初稿...&quot;</span>)</span><br><span class="line">    prompt = <span class="string">&quot;请帮我写一篇关于&#x27;人工智能未来发展趋势&#x27;的短文，200字左右。&quot;</span></span><br><span class="line">    response = llm.invoke([&#123;<span class="string">&quot;role&quot;</span>: <span class="string">&quot;user&quot;</span>, <span class="string">&quot;content&quot;</span>: prompt&#125;])</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;messages&quot;</span>: [(<span class="string">&quot;assistant&quot;</span>, response.content)], <span class="string">&quot;quality_score&quot;</span>: <span class="number">0</span>&#125;</span><br></pre></td></tr></table></figure><p>这个节点负责生成初稿。返回值中的 <code>messages</code> 会被自动追加到状态中。</p><h3 id="4-4-节点-2：反思节点（Reflection）"><a href="#4-4-节点-2：反思节点（Reflection）" class="headerlink" title="4.4 节点 2：反思节点（Reflection）"></a>4.4 节点 2：反思节点（Reflection）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">reflection_node</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot; 正在反思和评估文章质量...&quot;</span>)</span><br><span class="line">    last_message = state[<span class="string">&quot;messages&quot;</span>][-<span class="number">1</span>].content</span><br><span class="line">    </span><br><span class="line">    prompt = <span class="string">f&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    请作为一位严苛的编辑，对以下文章进行反思和评分（0-10分）：</span></span><br><span class="line"><span class="string">    文章内容：<span class="subst">&#123;last_message&#125;</span></span></span><br><span class="line"><span class="string">    </span></span><br><span class="line"><span class="string">    如果文章逻辑清晰、用词准确且没有废话，请打8分以上；否则请打低分。</span></span><br><span class="line"><span class="string">    请严格按照以下格式输出：</span></span><br><span class="line"><span class="string">    评分：[数字]</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    response = llm.invoke([&#123;<span class="string">&quot;role&quot;</span>: <span class="string">&quot;user&quot;</span>, <span class="string">&quot;content&quot;</span>: prompt&#125;])</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 提取评分（实际生产建议用正则）</span></span><br><span class="line">    score = <span class="number">5</span></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;评分：&quot;</span> <span class="keyword">in</span> response.content:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            score = <span class="built_in">int</span>(response.content.split(<span class="string">&quot;评分：&quot;</span>)[<span class="number">1</span>].strip()[<span class="number">0</span>])</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot; 反思评分结果: <span class="subst">&#123;score&#125;</span>分&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;quality_score&quot;</span>: score&#125;</span><br></pre></td></tr></table></figure><p><strong>这个节点是整个架构的灵魂：</strong></p><ul><li>让 AI 对自己的输出进行<strong>元认知</strong>（对自己的认知过程进行认知）</li><li>输出一个量化的质量分数，作为路由决策的依据</li></ul><h3 id="4-5-条件边：路由决策（Router）"><a href="#4-5-条件边：路由决策（Router）" class="headerlink" title="4.5 条件边：路由决策（Router）"></a>4.5 条件边：路由决策（Router）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">should_rewrite</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="keyword">if</span> state[<span class="string">&quot;quality_score&quot;</span>] &gt;= <span class="number">8</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot; 质量达标，流程结束！&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;end&quot;</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot; 质量不达标，打回去重写！&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;rewrite&quot;</span></span><br></pre></td></tr></table></figure><p>这个函数就是“交通指挥员”，根据质量分数决定下一步走向。</p><h3 id="4-6-节点-3：修改节点（Refiner）"><a href="#4-6-节点-3：修改节点（Refiner）" class="headerlink" title="4.6 节点 3：修改节点（Refiner）"></a>4.6 节点 3：修改节点（Refiner）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">refiner_node</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot; 正在根据反思意见修改文章...&quot;</span>)</span><br><span class="line">    last_message = state[<span class="string">&quot;messages&quot;</span>][-<span class="number">1</span>].content</span><br><span class="line">    prompt = <span class="string">f&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    上一版文章写得不够好，请重新修改并润色以下文章，使其更专业、更流畅：</span></span><br><span class="line"><span class="string">    <span class="subst">&#123;last_message&#125;</span></span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    response = llm.invoke([&#123;<span class="string">&quot;role&quot;</span>: <span class="string">&quot;user&quot;</span>, <span class="string">&quot;content&quot;</span>: prompt&#125;])</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;messages&quot;</span>: [(<span class="string">&quot;assistant&quot;</span>, response.content)], <span class="string">&quot;quality_score&quot;</span>: <span class="number">0</span>&#125;</span><br></pre></td></tr></table></figure><p>修改节点会生成一个新版本，然后流程会再次进入反思节点，形成闭环。</p><h3 id="4-7-组装图（Graph）"><a href="#4-7-组装图（Graph）" class="headerlink" title="4.7 组装图（Graph）"></a>4.7 组装图（Graph）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langgraph.graph <span class="keyword">import</span> StateGraph, START, END</span><br><span class="line"></span><br><span class="line">builder = StateGraph(State)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加节点</span></span><br><span class="line">builder.add_node(<span class="string">&quot;writer&quot;</span>, writer_node)</span><br><span class="line">builder.add_node(<span class="string">&quot;reflect&quot;</span>, reflection_node)</span><br><span class="line">builder.add_node(<span class="string">&quot;refine&quot;</span>, refiner_node)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加边</span></span><br><span class="line">builder.add_edge(START, <span class="string">&quot;writer&quot;</span>)</span><br><span class="line">builder.add_edge(<span class="string">&quot;writer&quot;</span>, <span class="string">&quot;reflect&quot;</span>)</span><br><span class="line">builder.add_conditional_edges(<span class="string">&quot;reflect&quot;</span>, should_rewrite, &#123;</span><br><span class="line">    <span class="string">&quot;rewrite&quot;</span>: <span class="string">&quot;refine&quot;</span>, </span><br><span class="line">    <span class="string">&quot;end&quot;</span>: END</span><br><span class="line">&#125;)</span><br><span class="line">builder.add_edge(<span class="string">&quot;refine&quot;</span>, <span class="string">&quot;reflect&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译</span></span><br><span class="line">reflection_agent = builder.<span class="built_in">compile</span>()</span><br></pre></td></tr></table></figure><p><strong>这里的关键是第 4 条边：<code>refine → reflect</code></strong>，它形成了<strong>循环</strong>，让 Agent 可以反复打磨直到满意。</p><h3 id="4-8-运行-Agent"><a href="#4-8-运行-Agent" class="headerlink" title="4.8 运行 Agent"></a>4.8 运行 Agent</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">result = reflection_agent.invoke(&#123;<span class="string">&quot;messages&quot;</span>: [], <span class="string">&quot;quality_score&quot;</span>: <span class="number">0</span>&#125;)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;最终定稿：<span class="subst">&#123;result[<span class="string">&#x27;messages&#x27;</span>][-<span class="number">1</span>].content&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h2 id="五、运行效果演示"><a href="#五、运行效果演示" class="headerlink" title="五、运行效果演示"></a>五、运行效果演示</h2><figure class="highlight erlang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"> 启动带自我反思机制的写作 Agent...</span><br><span class="line"></span><br><span class="line"> 正在撰写初稿...</span><br><span class="line"> 正在反思和评估文章质量...</span><br><span class="line"> 反思评分结果: <span class="number">6</span>分</span><br><span class="line"> 质量不达标，打回去重写！</span><br><span class="line"></span><br><span class="line"> 正在根据反思意见修改文章...</span><br><span class="line"> 正在反思和评估文章质量...</span><br><span class="line"> 反思评分结果: <span class="number">9</span>分</span><br><span class="line"> 质量达标，流程结束！</span><br><span class="line"></span><br><span class="line"> 最终定稿文章：</span><br><span class="line">人工智能正在以前所未有的速度重塑人类社会的方方面面...</span><br></pre></td></tr></table></figure><h2 id="六、架构可视化"><a href="#六、架构可视化" class="headerlink" title="六、架构可视化"></a>六、架构可视化</h2><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">┌─────────────────────────────────────────────────────┐</span></span><br><span class="line"><span class="attribute">│                    全局状态 State                     │</span></span><br><span class="line"><span class="attribute">│  ┌─────────────────────────────────────────────┐   │</span></span><br><span class="line"><span class="attribute">│  │ messages</span><span class="punctuation">:</span> <span class="string">[初稿, 反思结果, 修改稿, 再反思...] │   │</span></span><br><span class="line"><span class="attribute">│  │ quality_score</span><span class="punctuation">:</span> <span class="string">动态更新的分数                 │   │</span></span><br><span class="line">│  └─────────────────────────────────────────────┘   │</span><br><span class="line">└─────────────────────────────────────────────────────┘</span><br><span class="line">                          ↓↑</span><br><span class="line">    ┌─────────┐    ┌─────────┐    ┌─────────┐</span><br><span class="line">    │ Writer  │ → │ Reflect │ → │ Refiner │</span><br><span class="line">    │  节点   │    │  节点   │    │  节点   │</span><br><span class="line">    └─────────┘    └─────────┘    └─────────┘</span><br><span class="line">         ↑              ↓              ↓</span><br><span class="line">         └──────────────┴──────────────┘</span><br><span class="line">                 (质量不达标时循环)</span><br></pre></td></tr></table></figure><h2 id="七、拓展应用场景"><a href="#七、拓展应用场景" class="headerlink" title="七、拓展应用场景"></a>七、拓展应用场景</h2><p>这个“生成-评估-改进”的循环架构可以迁移到多种场景：</p><h3 id="7-1-代码审查-Agent"><a href="#7-1-代码审查-Agent" class="headerlink" title="7.1 代码审查 Agent"></a>7.1 代码审查 Agent</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">coder_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 生成代码</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">code_review_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 检查代码规范、潜在bug、性能问题</span></span><br><span class="line">    <span class="comment"># 输出评分和改进建议</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">refactor_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 根据审查意见重构代码</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><h3 id="7-2-翻译校对-Agent"><a href="#7-2-翻译校对-Agent" class="headerlink" title="7.2 翻译校对 Agent"></a>7.2 翻译校对 Agent</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">translator_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 初译</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">quality_check_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 检查准确性、流畅度、术语一致性</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">proofreader_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 润色修改</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><h3 id="7-3-对话质量优化-Agent"><a href="#7-3-对话质量优化-Agent" class="headerlink" title="7.3 对话质量优化 Agent"></a>7.3 对话质量优化 Agent</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">responder_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 生成回复</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">safety_check_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 检查安全性、合规性、有用性</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">improve_node</span>(<span class="params">state</span>):</span><br><span class="line">    <span class="comment"># 优化回复质量</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><h2 id="八、进阶技巧"><a href="#八、进阶技巧" class="headerlink" title="八、进阶技巧"></a>八、进阶技巧</h2><h3 id="8-1-更精细的评分提取"><a href="#8-1-更精细的评分提取" class="headerlink" title="8.1 更精细的评分提取"></a>8.1 更精细的评分提取</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">extract_score</span>(<span class="params">text: <span class="built_in">str</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    pattern = <span class="string">r&quot;评分[:：]\s*(\d+)&quot;</span></span><br><span class="line">    <span class="keyword">match</span> = re.search(pattern, text)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">int</span>(<span class="keyword">match</span>.group(<span class="number">1</span>)) <span class="keyword">if</span> <span class="keyword">match</span> <span class="keyword">else</span> <span class="number">5</span></span><br></pre></td></tr></table></figure><h3 id="8-2-添加最大循环次数防止死循环"><a href="#8-2-添加最大循环次数防止死循环" class="headerlink" title="8.2 添加最大循环次数防止死循环"></a>8.2 添加最大循环次数防止死循环</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">State</span>(<span class="title class_ inherited__">TypedDict</span>):</span><br><span class="line">    messages: Annotated[<span class="built_in">list</span>, add_messages]</span><br><span class="line">    quality_score: <span class="built_in">int</span></span><br><span class="line">    rewrite_count: <span class="built_in">int</span>  <span class="comment"># 新增：记录重写次数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">should_rewrite</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="keyword">if</span> state[<span class="string">&quot;rewrite_count&quot;</span>] &gt;= <span class="number">3</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot; 已达最大重写次数，强制结束&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;end&quot;</span></span><br><span class="line">    <span class="keyword">if</span> state[<span class="string">&quot;quality_score&quot;</span>] &gt;= <span class="number">8</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;end&quot;</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;rewrite&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">refiner_node</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="comment"># ... 修改文章逻辑 ...</span></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="string">&quot;messages&quot;</span>: [(<span class="string">&quot;assistant&quot;</span>, response.content)], </span><br><span class="line">        <span class="string">&quot;quality_score&quot;</span>: <span class="number">0</span>,</span><br><span class="line">        <span class="string">&quot;rewrite_count&quot;</span>: state.get(<span class="string">&quot;rewrite_count&quot;</span>, <span class="number">0</span>) + <span class="number">1</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="8-3-保存反思日志"><a href="#8-3-保存反思日志" class="headerlink" title="8.3 保存反思日志"></a>8.3 保存反思日志</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">reflection_node</span>(<span class="params">state: State</span>):</span><br><span class="line">    <span class="comment"># ... 评分逻辑 ...</span></span><br><span class="line">    criticism = extract_criticism(response.content)  <span class="comment"># 提取具体意见</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;💡 改进建议: <span class="subst">&#123;criticism&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="comment"># 可以将 criticism 也存入 state 供修改节点参考</span></span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;quality_score&quot;</span>: score, <span class="string">&quot;feedback&quot;</span>: criticism&#125;</span><br></pre></td></tr></table></figure><h2 id="九、与传统-Agent-的对比"><a href="#九、与传统-Agent-的对比" class="headerlink" title="九、与传统 Agent 的对比"></a>九、与传统 Agent 的对比</h2><div class="table-container"><table><thead><tr><th>维度</th><th>传统 Agent</th><th>LangGraph 反思 Agent</th></tr></thead><tbody><tr><td>执行模式</td><td>线性（一次过）</td><td>循环（可反复优化）</td></tr><tr><td>自我纠错</td><td>不具备</td><td>天然支持</td></tr><tr><td>输出质量</td><td>依赖单次生成质量</td><td>可通过多轮打磨提升</td></tr><tr><td>可观测性</td><td>黑盒</td><td>白盒（每个节点可追踪）</td></tr><tr><td>适用场景</td><td>简单任务</td><td>需要质量保证的复杂任务</td></tr></tbody></table></div><h2 id="十、总结"><a href="#十、总结" class="headerlink" title="十、总结"></a>十、总结</h2><p>通过 LangGraph，我们用不到 100 行代码就构建了一个具备<strong>自我反思能力</strong>的智能 Agent。这种架构的核心价值在于：</p><ol><li><strong>质量可控</strong>：通过评分阈值，可以控制输出质量</li><li><strong>过程透明</strong>：每个节点的输入输出都可追踪</li><li><strong>易于扩展</strong>：添加新节点或修改路由逻辑非常灵活</li><li><strong>通用性强</strong>：写作-反思-改进的模式适用于大量场景</li></ol><p><strong>思考延伸：</strong></p><p>如果把这个架构和之前介绍的 Plan-and-Execute 结合起来，会碰撞出什么火花？一个能“先规划、再执行、同时自我反思”的 Agent，或许就是通往更智能 AI 系统的关键一步。</p><hr><p><strong>完整代码已附上，替换 API Key 即可运行体验。</strong></p><p><strong>如果这篇文章让你对 LangGraph 有了新的认识，欢迎点赞、收藏、转发！</strong></p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/langgraph%E5%AE%9E%E7%8E%B0%E8%87%AA%E6%88%91%E5%8F%8D%E6%80%9D/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/langgraph%E5%AE%9E%E7%8E%B0%E8%87%AA%E6%88%91%E5%8F%8D%E6%80%9D/"/>
    <published>2026-05-14T13:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="让-AI-学会“自我反思”：用-LangGraph-构建能不断改进的智能写作-Agent"><a href="#让-AI-学会“自我反思”：用-LangGraph-构建能不断改进的智能写作-Agent" class="headerlink" title="让 AI 学会“自我反思”：用 LangGraph 构建能不断改进的智能写作 Agent"></a>让 AI 学会“自我反思”：用 LangGraph 构建能不断改进的智能写作 Agent</h1><h2 id="一、问题的起源：一次生成真的够吗？"><a href="#一、问题的起源：一次生成真的够吗？" class="headerlink" title="一、问题的起源：一次生成真的够吗？"></a>一、问题的起源：一次生成真的够吗？</h2><p>传统的 AI Agent 通常采用“一次性生成”的模式：用户输入指令 → 模型输出结果 → 结束。这种方式存在一个明显的缺陷：</p>
<blockquote>
<p><strong>AI 无法审视自己的输出，也不知道自己写得对不对。</strong></p>
</blockquote>
<p>想象一下，如果让一个人类写文章：</p>
<ol>
<li>先写初稿 ✍️</li>
<li>再读一遍，找问题 🤔</li>
<li>不满意就修改 🔧</li>
<li>再读，再改，直到满意 ✅</li>
</ol>
<p>这种<strong>写作-反思-修改-再反思</strong>的循环，是人类提升作品质量的本能方法。那么，能不能让 AI 也学会这套流程？</p>
<p>答案是肯定的。<strong>LangGraph</strong> 提供了构建这种循环工作流的能力。</p>
<h2 id="二、什么是-LangGraph？"><a href="#二、什么是-LangGraph？" class="headerlink" title="二、什么是 LangGraph？"></a>二、什么是 LangGraph？</h2><p>LangGraph 是 LangChain 生态中用于构建<strong>有状态、多步骤、循环式 Agent</strong> 的框架。它的核心概念非常简单：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>概念</th>
<th>英文</th>
<th>说明</th>
<th>类比</th>
</tr>
</thead>
<tbody>
<tr>
<td>状态</td>
<td>State</td>
<td>所有节点共享的“记事本”</td>
<td>白板</td>
</tr>
<tr>
<td>节点</td>
<td>Node</td>
<td>一个具体的操作/函数</td>
<td>工位上的一个员工</td>
</tr>
<tr>
<td>边</td>
<td>Edge</td>
<td>节点之间的连线</td>
<td>员工之间的传话路径</td>
</tr>
<tr>
<td>条件边</td>
<td>Conditional Edge</td>
<td>根据状态决定下一步走向</td>
<td>有决策权的经理</td>
</tr>
</tbody>
</table>
</div>
<p>与传统 Agent 最大的区别：LangGraph 天然支持<strong>循环</strong>（Loop），这是实现“自我反思”的基础。</p>
<h2 id="三、本次实现的工作流"><a href="#三、本次实现的工作流" class="headerlink" title="三、本次实现的工作流"></a>三、本次实现的工作流</h2><p>我们将构建一个具有<strong>自我反思能力</strong>的写作 Agent，它的工作流程如下：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">START</span> → 写作节点 → 反思节点 → 质量评分 ≥<span class="number">8</span>？ </span><br><span class="line">                              ↓</span><br><span class="line">                         是 → <span class="keyword">END</span></span><br><span class="line">                              ↓</span><br><span class="line">                         否 → 修改节点 → (回到反思节点)</span><br></pre></td></tr></table></figure>
<p>这是一个典型的<strong>反馈循环</strong>架构，广泛用于需要持续优化的场景。</p>]]>
    </summary>
    <title>让 AI 学会“自我反思”：用 LangGraph 构建能不断改进的智能写作 Agent</title>
    <updated>2026-06-29T08:29:51.975Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="让-AI-学会“三思而后行”：用-Plan-and-Execute-架构构建复杂任务智能体"><a href="#让-AI-学会“三思而后行”：用-Plan-and-Execute-架构构建复杂任务智能体" class="headerlink" title="让 AI 学会“三思而后行”：用 Plan-and-Execute 架构构建复杂任务智能体"></a>让 AI 学会“三思而后行”：用 Plan-and-Execute 架构构建复杂任务智能体</h1><h2 id="一、为什么需要-Plan-and-Execute？"><a href="#一、为什么需要-Plan-and-Execute？" class="headerlink" title="一、为什么需要 Plan-and-Execute？"></a>一、为什么需要 Plan-and-Execute？</h2><p>传统的 ReAct 模式（Reasoning + Acting）虽然强大，但面对复杂任务时存在一个明显的缺陷：<strong>模型往往是“边想边做”，走一步看一步</strong>。这就像一个人在没有地图的情况下蒙眼走路——遇到岔路容易迷失方向，尤其是当任务包含多个依赖步骤时。</p><p>举个例子，用户提出这样的需求：</p><blockquote><p>“请帮我获取关于 AI 的英文文章摘要，并把它翻译成中文。”</p></blockquote><p>这个任务天然包含两个<strong>有依赖关系</strong>的步骤：</p><ol><li>必须先获取英文摘要（<code>get_article_summary</code>）</li><li>然后再翻译成中文（<code>translate_text</code>）</li></ol><p>如果让普通 Agent 来处理，它可能会：</p><ul><li>直接调用翻译工具（但还没有拿到原文）</li><li>或者先获取摘要后忘记翻译</li><li>或者在多步之间陷入混乱</li></ul><p><strong>Plan-and-Execute 架构</strong>正是为了解决这个问题而生的。它的核心理念是：</p><blockquote><p><strong>先规划（Plan），后执行（Execute）</strong> —— 就像项目经理先制定蓝图，再分配给团队成员执行。</p></blockquote><h2 id="二、架构原理：把-Agent-拆成“大脑”和“双手”"><a href="#二、架构原理：把-Agent-拆成“大脑”和“双手”" class="headerlink" title="二、架构原理：把 Agent 拆成“大脑”和“双手”"></a>二、架构原理：把 Agent 拆成“大脑”和“双手”</h2><p>Plan-and-Execute 架构将传统的 Agent 一分为二：</p><div class="table-container"><table><thead><tr><th>组件</th><th>英文名</th><th>职责</th><th>类比</th></tr></thead><tbody><tr><td>规划器</td><td>Planner</td><td>分析复杂任务，拆解成有序的步骤列表</td><td>项目经理</td></tr><tr><td>执行器</td><td>Executor</td><td>拿着工具列表，逐步执行计划</td><td>一线工程师</td></tr></tbody></table></div><p><strong>工作流程：</strong><br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">用户输入 → Planner（生成步骤列表）→ Executor（逐步执行）→ 返回结果</span><br></pre></td></tr></table></figure></p><p>这种分离带来的好处：</p><ul><li><strong>逻辑清晰</strong>：规划阶段不执行，执行阶段不思考</li><li><strong>可追溯</strong>：可以看到每一步的执行计划</li><li><strong>容错性强</strong>：某一步失败可以重新规划</li></ul><h2 id="三、完整代码解析"><a href="#三、完整代码解析" class="headerlink" title="三、完整代码解析"></a>三、完整代码解析</h2><h3 id="3-1-环境准备"><a href="#3-1-环境准备" class="headerlink" title="3.1 环境准备"></a>3.1 环境准备</h3><span id="more"></span><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install langchain langchain-openai langchain-experimental</span><br></pre></td></tr></table></figure><blockquote><p>注意：<code>langchain-experimental</code> 包含了 Plan-and-Execute 的实现</p></blockquote><h3 id="3-2-初始化大模型"><a href="#3-2-初始化大模型" class="headerlink" title="3.2 初始化大模型"></a>3.2 初始化大模型</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> ChatOpenAI</span><br><span class="line"></span><br><span class="line">llm = ChatOpenAI(</span><br><span class="line">    model=<span class="string">&quot;qwen3-max&quot;</span>,  <span class="comment"># 推荐使用 qwen-plus 或 qwen-max，规划阶段需要强逻辑能力</span></span><br><span class="line">    base_url=<span class="string">&quot;https://dashscope.aliyuncs.com/compatible-mode/v1&quot;</span>,</span><br><span class="line">    api_key=<span class="string">&quot;your-api-key&quot;</span>,</span><br><span class="line">    temperature=<span class="number">0</span>  <span class="comment"># 保持确定性输出</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><strong>为什么推荐 Qwen-Max？</strong> 规划阶段需要对任务进行语义理解和逻辑拆解，这对模型的推理能力要求很高。Qwen-Max 在这一方面表现优异。</p><h3 id="3-3-定义有依赖关系的工具"><a href="#3-3-定义有依赖关系的工具" class="headerlink" title="3.3 定义有依赖关系的工具"></a>3.3 定义有依赖关系的工具</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain.tools <span class="keyword">import</span> tool</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_article_summary</span>(<span class="params">topic: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取指定主题的英文科技文章摘要。参数 topic 为文章主题（如 AI, Blockchain）。&quot;&quot;&quot;</span></span><br><span class="line">    mock_articles = &#123;</span><br><span class="line">        <span class="string">&quot;AI&quot;</span>: <span class="string">&quot;Artificial Intelligence is rapidly transforming industries by automating complex tasks and providing deep insights from data.&quot;</span>,</span><br><span class="line">        <span class="string">&quot;Blockchain&quot;</span>: <span class="string">&quot;Blockchain is a decentralized ledger technology that ensures secure, transparent, and tamper-proof transactions.&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> mock_articles.get(topic, <span class="string">f&quot;Sorry, no article summary found for topic: <span class="subst">&#123;topic&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">translate_text</span>(<span class="params">text: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;将指定的英文文本翻译成流畅的中文。参数 text 为需要翻译的英文段落。&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;Artificial Intelligence&quot;</span> <span class="keyword">in</span> text:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;人工智能正在通过自动化复杂任务和提供深度的数据洞察，迅速改变各个行业。&quot;</span></span><br><span class="line">    <span class="keyword">elif</span> <span class="string">&quot;Blockchain&quot;</span> <span class="keyword">in</span> text:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;区块链是一种去中心化的账本技术，能够确保交易的安全、透明和防篡改。&quot;</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;[模拟翻译结果]：<span class="subst">&#123;text&#125;</span>&quot;</span></span><br></pre></td></tr></table></figure><p><strong>关键设计：</strong></p><ul><li>这两个工具天然存在依赖关系：<code>translate_text</code> 需要的 <code>text</code> 参数，必须由 <code>get_article_summary</code> 提供</li><li>清晰的 docstring 帮助 Planner 理解每个工具的功能</li></ul><h3 id="3-4-组装-Plan-and-Execute-架构"><a href="#3-4-组装-Plan-and-Execute-架构" class="headerlink" title="3.4 组装 Plan-and-Execute 架构"></a>3.4 组装 Plan-and-Execute 架构</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_experimental.plan_and_execute <span class="keyword">import</span> (</span><br><span class="line">    PlanAndExecute, </span><br><span class="line">    load_agent_executor, </span><br><span class="line">    load_chat_planner</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建规划器：负责拆解任务</span></span><br><span class="line">planner = load_chat_planner(llm)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建执行器：负责执行计划</span></span><br><span class="line">executor = load_agent_executor(</span><br><span class="line">    llm, </span><br><span class="line">    tools=[get_article_summary, translate_text], </span><br><span class="line">    verbose=<span class="literal">True</span>  <span class="comment"># 打印执行过程</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 组合成完整的智能体</span></span><br><span class="line">agent = PlanAndExecute(</span><br><span class="line">    planner=planner, </span><br><span class="line">    executor=executor, </span><br><span class="line">    verbose=<span class="literal">True</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="3-5-下达复杂指令并观察执行过程"><a href="#3-5-下达复杂指令并观察执行过程" class="headerlink" title="3.5 下达复杂指令并观察执行过程"></a>3.5 下达复杂指令并观察执行过程</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">complex_task = <span class="string">&quot;请帮我获取关于 AI 的英文文章摘要，并把它翻译成中文。&quot;</span></span><br><span class="line">result = agent.invoke(&#123;<span class="string">&quot;input&quot;</span>: complex_task&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;最终结果: <span class="subst">&#123;result[<span class="string">&#x27;output&#x27;</span>]&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h2 id="四、运行效果：亲眼见证“先想后做”"><a href="#四、运行效果：亲眼见证“先想后做”" class="headerlink" title="四、运行效果：亲眼见证“先想后做”"></a>四、运行效果：亲眼见证“先想后做”</h2><p>当您运行代码时，会看到类似如下的输出：</p><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">👤 用户指令</span><span class="punctuation">:</span> <span class="string">请帮我获取关于 AI 的英文文章摘要，并把它翻译成中文。</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&gt;</span> <span class="string">Entering new PlanAndExecute chain...</span></span><br><span class="line"><span class="punctuation">&gt;</span> <span class="string">Steps:</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">1. 使用 get_article_summary 工具获取关于 AI 的英文文章摘要。</span></span><br><span class="line"><span class="attribute">2. 将上一步获取到的英文摘要，使用 translate_text 工具翻译成中文。</span></span><br><span class="line"><span class="attribute">3. 输出最终翻译结果。</span></span><br><span class="line"><span class="attribute"></span></span><br><span class="line"><span class="attribute">&gt; Executing step 1</span><span class="punctuation">:</span> <span class="string">使用 get_article_summary 工具获取关于 AI 的英文文章摘要。</span></span><br><span class="line"><span class="attribute">Action</span><span class="punctuation">:</span> <span class="string">get_article_summary</span></span><br><span class="line"><span class="attribute">Action Input</span><span class="punctuation">:</span> <span class="string">&quot;AI&quot;</span></span><br><span class="line"><span class="attribute">Observation</span><span class="punctuation">:</span> <span class="string">&quot;Artificial Intelligence is rapidly transforming industries...&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&gt;</span> <span class="string">Executing step 2: 将上一步获取到的英文摘要，使用 translate_text 工具翻译成中文。</span></span><br><span class="line"><span class="attribute">Action</span><span class="punctuation">:</span> <span class="string">translate_text</span></span><br><span class="line"><span class="attribute">Action Input</span><span class="punctuation">:</span> <span class="string">&quot;Artificial Intelligence is rapidly transforming industries...&quot;</span></span><br><span class="line"><span class="attribute">Observation</span><span class="punctuation">:</span> <span class="string">&quot;人工智能正在通过自动化复杂任务和提供深度的数据洞察，迅速改变各个行业。&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&gt;</span> <span class="string">Executing step 3: 输出最终翻译结果。</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">🤖 最终结果</span><span class="punctuation">:</span> <span class="string">人工智能正在通过自动化复杂任务和提供深度的数据洞察，迅速改变各个行业。</span></span><br></pre></td></tr></table></figure><h2 id="五、Plan-and-Execute-vs-传统-Agent"><a href="#五、Plan-and-Execute-vs-传统-Agent" class="headerlink" title="五、Plan-and-Execute vs 传统 Agent"></a>五、Plan-and-Execute vs 传统 Agent</h2><div class="table-container"><table><thead><tr><th>对比维度</th><th>传统 ReAct Agent</th><th>Plan-and-Execute</th></tr></thead><tbody><tr><td>思考方式</td><td>边想边做，逐步推理</td><td>先整体规划，再执行</td></tr><tr><td>多步依赖处理</td><td>容易出错，可能遗漏步骤</td><td>天然支持，步骤有序</td></tr><tr><td>可解释性</td><td>黑盒，难以追踪</td><td>白盒，步骤清晰可见</td></tr><tr><td>执行效率</td><td>每次决策都要调用 LLM</td><td>规划一次，批量执行</td></tr><tr><td>适用场景</td><td>简单对话式任务</td><td>复杂的多步任务</td></tr></tbody></table></div><p><strong>选择建议：</strong></p><ul><li>简单问答、单步工具调用 → 传统 Agent</li><li>数据分析流水线、自动化报告生成 → Plan-and-Execute</li></ul><h2 id="六、进阶改进：让架构更强大"><a href="#六、进阶改进：让架构更强大" class="headerlink" title="六、进阶改进：让架构更强大"></a>六、进阶改进：让架构更强大</h2><h3 id="6-1-添加重规划（Re-planning）能力"><a href="#6-1-添加重规划（Re-planning）能力" class="headerlink" title="6.1 添加重规划（Re-planning）能力"></a>6.1 添加重规划（Re-planning）能力</h3><p>当某一步执行失败时，可以让 Planner 重新规划：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_experimental.plan_and_execute <span class="keyword">import</span> PlanAndExecute</span><br><span class="line"></span><br><span class="line">agent = PlanAndExecute(</span><br><span class="line">    planner=planner,</span><br><span class="line">    executor=executor,</span><br><span class="line">    re_plan=<span class="literal">True</span>,  <span class="comment"># 开启重规划</span></span><br><span class="line">    verbose=<span class="literal">True</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="6-2-添加更多工具"><a href="#6-2-添加更多工具" class="headerlink" title="6.2 添加更多工具"></a>6.2 添加更多工具</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">search_web</span>(<span class="params">query: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;搜索网络信息&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 调用搜索 API</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">send_email</span>(<span class="params">recipient: <span class="built_in">str</span>, content: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;发送邮件&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 发送邮件逻辑</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">tools = [get_article_summary, translate_text, search_web, send_email]</span><br></pre></td></tr></table></figure><h3 id="6-3-自定义-Planner-的提示词"><a href="#6-3-自定义-Planner-的提示词" class="headerlink" title="6.3 自定义 Planner 的提示词"></a>6.3 自定义 Planner 的提示词</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain.prompts <span class="keyword">import</span> PromptTemplate</span><br><span class="line"></span><br><span class="line">planner_prompt = PromptTemplate.from_template(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">你是一个任务规划专家。请将以下用户需求拆解成可执行的步骤列表。</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">用户需求：&#123;input&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">拆解规则：</span></span><br><span class="line"><span class="string">1. 每个步骤必须使用可用的工具完成</span></span><br><span class="line"><span class="string">2. 步骤之间要标注依赖关系</span></span><br><span class="line"><span class="string">3. 输出格式：Step 1: ...; Step 2: ...</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">可用工具：&#123;tools&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">请开始规划：</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span>)</span><br></pre></td></tr></table></figure><h2 id="七、常见问题与解决方案"><a href="#七、常见问题与解决方案" class="headerlink" title="七、常见问题与解决方案"></a>七、常见问题与解决方案</h2><p><strong>Q1: Planner 拆解出的步骤顺序不对怎么办？</strong></p><ul><li>在工具的 docstring 中明确说明前置条件</li><li>使用更强大的模型（如 qwen-max）</li></ul><p><strong>Q2: Executor 执行时参数传递失败？</strong></p><ul><li>检查工具函数的参数名是否清晰</li><li>在 docstring 中给出参数示例</li></ul><p><strong>Q3: 如何保存执行历史以便调试？</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">agent = PlanAndExecute(</span><br><span class="line">    planner=planner,</span><br><span class="line">    executor=executor,</span><br><span class="line">    return_intermediate_steps=<span class="literal">True</span>,  <span class="comment"># 返回中间步骤</span></span><br><span class="line">    verbose=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line">result = agent.invoke(&#123;<span class="string">&quot;input&quot;</span>: task&#125;)</span><br><span class="line"><span class="built_in">print</span>(result[<span class="string">&quot;intermediate_steps&quot;</span>])  <span class="comment"># 查看执行历史</span></span><br></pre></td></tr></table></figure><h2 id="八、总结"><a href="#八、总结" class="headerlink" title="八、总结"></a>八、总结</h2><p>Plan-and-Execute 架构通过<strong>分离规划与执行</strong>，赋予了 AI 智能体“三思而后行”的能力。这种架构特别适合：</p><ul><li>需要多步推理的复杂任务  </li><li>步骤之间有明确依赖关系的场景  </li><li>对过程可追溯性有要求的企业应用  </li></ul><p><strong>核心要点回顾：</strong></p><ul><li>Planner 负责“想”：拆解任务成步骤列表</li><li>Executor 负责“做”：调用工具执行每一步</li><li>分离带来的是更强的逻辑性和可解释性</li></ul><h2 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h2><p>如果用户的需求变成：</p><blockquote><p>“帮我找一篇关于区块链的文章，翻译成中文，然后发送到 my@email.com”</p></blockquote><p>你会如何扩展这个 Agent？欢迎在评论区分享你的设计思路！</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/langchain%E5%AE%9E%E7%8E%B0Plan-and-Execute/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/langchain%E5%AE%9E%E7%8E%B0Plan-and-Execute/"/>
    <published>2026-05-14T12:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="让-AI-学会“三思而后行”：用-Plan-and-Execute-架构构建复杂任务智能体"><a href="#让-AI-学会“三思而后行”：用-Plan-and-Execute-架构构建复杂任务智能体" class="headerlink" title="让 AI 学会“三思而后行”：用 Plan-and-Execute 架构构建复杂任务智能体"></a>让 AI 学会“三思而后行”：用 Plan-and-Execute 架构构建复杂任务智能体</h1><h2 id="一、为什么需要-Plan-and-Execute？"><a href="#一、为什么需要-Plan-and-Execute？" class="headerlink" title="一、为什么需要 Plan-and-Execute？"></a>一、为什么需要 Plan-and-Execute？</h2><p>传统的 ReAct 模式（Reasoning + Acting）虽然强大，但面对复杂任务时存在一个明显的缺陷：<strong>模型往往是“边想边做”，走一步看一步</strong>。这就像一个人在没有地图的情况下蒙眼走路——遇到岔路容易迷失方向，尤其是当任务包含多个依赖步骤时。</p>
<p>举个例子，用户提出这样的需求：</p>
<blockquote>
<p>“请帮我获取关于 AI 的英文文章摘要，并把它翻译成中文。”</p>
</blockquote>
<p>这个任务天然包含两个<strong>有依赖关系</strong>的步骤：</p>
<ol>
<li>必须先获取英文摘要（<code>get_article_summary</code>）</li>
<li>然后再翻译成中文（<code>translate_text</code>）</li>
</ol>
<p>如果让普通 Agent 来处理，它可能会：</p>
<ul>
<li>直接调用翻译工具（但还没有拿到原文）</li>
<li>或者先获取摘要后忘记翻译</li>
<li>或者在多步之间陷入混乱</li>
</ul>
<p><strong>Plan-and-Execute 架构</strong>正是为了解决这个问题而生的。它的核心理念是：</p>
<blockquote>
<p><strong>先规划（Plan），后执行（Execute）</strong> —— 就像项目经理先制定蓝图，再分配给团队成员执行。</p>
</blockquote>
<h2 id="二、架构原理：把-Agent-拆成“大脑”和“双手”"><a href="#二、架构原理：把-Agent-拆成“大脑”和“双手”" class="headerlink" title="二、架构原理：把 Agent 拆成“大脑”和“双手”"></a>二、架构原理：把 Agent 拆成“大脑”和“双手”</h2><p>Plan-and-Execute 架构将传统的 Agent 一分为二：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>组件</th>
<th>英文名</th>
<th>职责</th>
<th>类比</th>
</tr>
</thead>
<tbody>
<tr>
<td>规划器</td>
<td>Planner</td>
<td>分析复杂任务，拆解成有序的步骤列表</td>
<td>项目经理</td>
</tr>
<tr>
<td>执行器</td>
<td>Executor</td>
<td>拿着工具列表，逐步执行计划</td>
<td>一线工程师</td>
</tr>
</tbody>
</table>
</div>
<p><strong>工作流程：</strong><br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">用户输入 → Planner（生成步骤列表）→ Executor（逐步执行）→ 返回结果</span><br></pre></td></tr></table></figure></p>
<p>这种分离带来的好处：</p>
<ul>
<li><strong>逻辑清晰</strong>：规划阶段不执行，执行阶段不思考</li>
<li><strong>可追溯</strong>：可以看到每一步的执行计划</li>
<li><strong>容错性强</strong>：某一步失败可以重新规划</li>
</ul>
<h2 id="三、完整代码解析"><a href="#三、完整代码解析" class="headerlink" title="三、完整代码解析"></a>三、完整代码解析</h2><h3 id="3-1-环境准备"><a href="#3-1-环境准备" class="headerlink" title="3.1 环境准备"></a>3.1 环境准备</h3>]]>
    </summary>
    <title>让 AI 学会“三思而后行”：用 Plan-and-Execute 架构构建复杂任务智能体</title>
    <updated>2026-05-14T06:44:06.521Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="大模型" scheme="https://zhhll.com.cn/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <category term="大模型" scheme="https://zhhll.com.cn/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    <content>
      <![CDATA[<h1 id="从零开始：用-LangChain-构建带记忆的-AI-智能体（Agent）"><a href="#从零开始：用-LangChain-构建带记忆的-AI-智能体（Agent）" class="headerlink" title="从零开始：用 LangChain 构建带记忆的 AI 智能体（Agent）"></a>从零开始：用 LangChain 构建带记忆的 AI 智能体（Agent）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>OpenAI 的函数调用（Function Calling）功能让大模型可以调用外部工具，而 LangChain 则把这一过程封装得更加优雅。今天，我将带你一步步构建一个<strong>带记忆功能的 AI Agent</strong>，让它不仅能调用工具查询天气，还能记住你和它的每一次对话。</p><p>最终效果如下：<br><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">🤖 Agent 已就绪，输入 &#x27;退出&#x27; 结束对话...</span></span><br><span class="line"><span class="attribute"></span></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">北京天气怎么样？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">北京当前天气晴朗，气温25°C，微风。</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">那上海呢？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">上海现在下着小雨，气温20°C，东南风。</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">我刚才问的第一个城市是哪里？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">您第一个问的是北京的天气情况。</span></span><br></pre></td></tr></table></figure></p><h2 id="整体架构"><a href="#整体架构" class="headerlink" title="整体架构"></a>整体架构</h2><p>我们的 Agent 由三部分组成：</p><div class="table-container"><table><thead><tr><th>组件</th><th>作用</th><th>代码对应</th></tr></thead><tbody><tr><td>大脑</td><td>理解用户意图，决定是否调用工具</td><td><code>ChatOpenAI</code></td></tr><tr><td>手脚</td><td>执行具体任务（如查天气）</td><td><code>@tool</code> 装饰的函数</td></tr><tr><td>记忆</td><td>记住上下文和历史对话</td><td><code>InMemorySaver</code></td></tr></tbody></table></div><h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install langchain langchain-openai langgraph</span><br></pre></td></tr></table></figure><p>如果你使用的是阿里云百炼平台（本代码示例使用 <code>qwen3-max</code>），还需要配置相应的 API Key。</p><span id="more"></span><h2 id="完整代码解析"><a href="#完整代码解析" class="headerlink" title="完整代码解析"></a>完整代码解析</h2><h3 id="1-初始化大模型"><a href="#1-初始化大模型" class="headerlink" title="1. 初始化大模型"></a>1. 初始化大模型</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> ChatOpenAI</span><br><span class="line"></span><br><span class="line">llm = ChatOpenAI(</span><br><span class="line">    model=<span class="string">&quot;qwen3-max&quot;</span>,                              <span class="comment"># 使用的模型名称</span></span><br><span class="line">    base_url=<span class="string">&quot;https://dashscope.aliyuncs.com/compatible-mode/v1&quot;</span>,  <span class="comment"># API 地址</span></span><br><span class="line">    api_key=<span class="string">&quot;your-api-key-here&quot;</span>,                    <span class="comment"># 替换为你的真实 API Key</span></span><br><span class="line">    temperature=<span class="number">0</span>                                   <span class="comment"># 温度越低，回答越确定</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><ul><li><code>base_url</code> 指向阿里云百炼的兼容模式接口，支持 OpenAI 的调用格式</li><li><code>temperature=0</code> 让模型输出更稳定，适合工具调用场景</li></ul><h3 id="2-定义工具（Agent-的手脚）"><a href="#2-定义工具（Agent-的手脚）" class="headerlink" title="2. 定义工具（Agent 的手脚）"></a>2. 定义工具（Agent 的手脚）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_core.tools <span class="keyword">import</span> tool</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_weather</span>(<span class="params">city: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取指定城市的实时天气（这里用假数据模拟）&quot;&quot;&quot;</span></span><br><span class="line">    weather_data = &#123;</span><br><span class="line">        <span class="string">&quot;北京&quot;</span>: <span class="string">&quot;晴，气温 25°C，微风&quot;</span>,</span><br><span class="line">        <span class="string">&quot;上海&quot;</span>: <span class="string">&quot;小雨，气温 20°C，东南风&quot;</span>,</span><br><span class="line">        <span class="string">&quot;深圳&quot;</span>: <span class="string">&quot;多云，气温 28°C，微风&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> weather_data.get(city, <span class="string">f&quot;抱歉，暂时查不到 <span class="subst">&#123;city&#125;</span> 的天气信息。&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>关键点：</strong></p><ul><li><code>@tool</code> 装饰器会<strong>自动读取函数的 docstring</strong> 作为工具描述</li><li>大模型就是根据这个描述来判断何时调用该工具的</li><li>参数的类型和注释也会被自动提取</li></ul><h3 id="3-创建记忆模块"><a href="#3-创建记忆模块" class="headerlink" title="3. 创建记忆模块"></a>3. 创建记忆模块</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langgraph.checkpoint.memory <span class="keyword">import</span> InMemorySaver</span><br><span class="line"></span><br><span class="line">memory = InMemorySaver()</span><br></pre></td></tr></table></figure><ul><li><code>InMemorySaver</code> 将对话历史保存在内存中</li><li>程序重启后记忆会清空，适合开发和测试</li><li>生产环境可以替换为 <code>RedisSaver</code> 或 <code>PostgresSaver</code></li></ul><h3 id="4-组装-Agent"><a href="#4-组装-Agent" class="headerlink" title="4. 组装 Agent"></a>4. 组装 Agent</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain.agents <span class="keyword">import</span> create_agent</span><br><span class="line"></span><br><span class="line">agent = create_agent(</span><br><span class="line">    model=llm,</span><br><span class="line">    tools=[get_weather],</span><br><span class="line">    debug=<span class="literal">True</span>,               <span class="comment"># 开启调试，可以看到内部执行过程</span></span><br><span class="line">    checkpointer=memory       <span class="comment"># 传入记忆模块</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><ul><li><code>create_agent</code> 是 LangChain 提供的高级 API，简化了 Agent 创建流程</li><li><code>debug=True</code> 会打印出模型思考、工具调用等详细信息，非常有助于调试</li></ul><h3 id="5-开启对话循环"><a href="#5-开启对话循环" class="headerlink" title="5. 开启对话循环"></a>5. 开启对话循环</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">config = &#123;<span class="string">&quot;configurable&quot;</span>: &#123;<span class="string">&quot;thread_id&quot;</span>: <span class="string">&quot;chat-001&quot;</span>&#125;&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;🤖 Agent 已就绪，输入 &#x27;退出&#x27; 结束对话...&quot;</span>)</span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    user_input = <span class="built_in">input</span>(<span class="string">&quot;\n👤 你: &quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> user_input.lower() <span class="keyword">in</span> [<span class="string">&quot;退出&quot;</span>, <span class="string">&quot;exit&quot;</span>, <span class="string">&quot;quit&quot;</span>]:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    </span><br><span class="line">    messages = &#123;<span class="string">&quot;messages&quot;</span>: [(<span class="string">&quot;user&quot;</span>, user_input)]&#125;</span><br><span class="line">    response = agent.invoke(messages, config=config)</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;🤖 Agent: <span class="subst">&#123;response[<span class="string">&#x27;messages&#x27;</span>][-<span class="number">1</span>].content&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><ul><li><code>thread_id</code> 相当于会话 ID，同一个 ID 下的所有对话会被记忆模块串联起来</li><li>不同的 <code>thread_id</code> 之间的对话是隔离的</li></ul><h2 id="运行示例"><a href="#运行示例" class="headerlink" title="运行示例"></a>运行示例</h2><p>下面是一个完整的对话流程：</p><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">🤖 Agent 已就绪，输入 &#x27;退出&#x27; 结束对话...</span></span><br><span class="line"><span class="attribute"></span></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">你好，我叫小明</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">你好小明！有什么我可以帮你的吗？</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">北京今天天气如何？</span></span><br><span class="line"><span class="attribute">🔧 正在调用工具</span><span class="punctuation">:</span> <span class="string">get_weather(city=&#x27;北京&#x27;)</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">北京今天天气晴朗，气温25°C，微风。</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">那刚才那个城市有风吗？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">是的，北京今天有微风。您是问这个吧？</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">退出</span></span><br></pre></td></tr></table></figure><h2 id="进阶改进建议"><a href="#进阶改进建议" class="headerlink" title="进阶改进建议"></a>进阶改进建议</h2><h3 id="1-连接真实天气-API"><a href="#1-连接真实天气-API" class="headerlink" title="1. 连接真实天气 API"></a>1. 连接真实天气 API</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_weather_real</span>(<span class="params">city: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取真实天气&quot;&quot;&quot;</span></span><br><span class="line">    api_key = <span class="string">&quot;your-weather-api-key&quot;</span></span><br><span class="line">    url = <span class="string">f&quot;https://api.openweathermap.org/data/2.5/weather?q=<span class="subst">&#123;city&#125;</span>&amp;appid=<span class="subst">&#123;api_key&#125;</span>&amp;units=metric&amp;lang=zh_cn&quot;</span></span><br><span class="line">    <span class="comment"># 处理请求逻辑...</span></span><br></pre></td></tr></table></figure><h3 id="2-添加更多工具"><a href="#2-添加更多工具" class="headerlink" title="2. 添加更多工具"></a>2. 添加更多工具</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_current_time</span>() -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取当前时间&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line">    <span class="keyword">return</span> datetime.now().strftime(<span class="string">&quot;%Y-%m-%d %H:%M:%S&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@tool</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">calculate</span>(<span class="params">expression: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    <span class="string">&quot;&quot;&quot;计算数学表达式，如 &#x27;1+2*3&#x27;&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">str</span>(<span class="built_in">eval</span>(expression))</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;计算错误，请检查表达式&quot;</span></span><br></pre></td></tr></table></figure><h3 id="3-持久化记忆（生产环境）"><a href="#3-持久化记忆（生产环境）" class="headerlink" title="3. 持久化记忆（生产环境）"></a>3. 持久化记忆（生产环境）</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langgraph.checkpoint.sqlite <span class="keyword">import</span> SqliteSaver</span><br><span class="line"></span><br><span class="line">memory = SqliteSaver.from_conn_string(<span class="string">&quot;checkpoints.db&quot;</span>)</span><br></pre></td></tr></table></figure><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><p><strong>Q: 模型不调用工具怎么办？</strong></p><ul><li>检查工具函数的 docstring 是否清晰</li><li>降低 <code>temperature</code> 参数</li><li>尝试更明确的提示词，如“请用工具查询”</li></ul><p><strong>Q: 如何查看 Agent 内部执行过程？</strong></p><ul><li>设置 <code>debug=True</code></li><li>或使用 <code>langchain.callbacks.StdOutCallbackHandler</code></li></ul><p><strong>Q: 对话历史太长会不会超 Token？</strong></p><ul><li>LangChain 的记忆模块会自动处理消息截断</li><li>也可以配置 <code>max_tokens</code> 或使用 <code>trim_messages</code> 手动控制</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过不到 50 行代码，我们搭建了一个具备记忆能力和工具调用能力的 AI Agent。这种模式可以轻松扩展到：</p><ul><li>联网搜索</li><li>数据库查询</li><li>发送邮件</li><li>调用企业内部 API</li></ul><p>Agent 的魅力在于：<strong>大模型负责“思考”，工具负责“执行”</strong>。当两者结合，AI 就不只是聊天机器人，而是真正的数字助手。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/langchain%E5%AE%9E%E7%8E%B0%E5%9F%BA%E7%A1%80agent/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E5%A4%A7%E6%A8%A1%E5%9E%8B/langchain%E5%AE%9E%E7%8E%B0%E5%9F%BA%E7%A1%80agent/"/>
    <published>2026-05-14T11:12:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="从零开始：用-LangChain-构建带记忆的-AI-智能体（Agent）"><a href="#从零开始：用-LangChain-构建带记忆的-AI-智能体（Agent）" class="headerlink" title="从零开始：用 LangChain 构建带记忆的 AI 智能体（Agent）"></a>从零开始：用 LangChain 构建带记忆的 AI 智能体（Agent）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>OpenAI 的函数调用（Function Calling）功能让大模型可以调用外部工具，而 LangChain 则把这一过程封装得更加优雅。今天，我将带你一步步构建一个<strong>带记忆功能的 AI Agent</strong>，让它不仅能调用工具查询天气，还能记住你和它的每一次对话。</p>
<p>最终效果如下：<br><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">🤖 Agent 已就绪，输入 &#x27;退出&#x27; 结束对话...</span></span><br><span class="line"><span class="attribute"></span></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">北京天气怎么样？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">北京当前天气晴朗，气温25°C，微风。</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">那上海呢？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">上海现在下着小雨，气温20°C，东南风。</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">👤 你</span><span class="punctuation">:</span> <span class="string">我刚才问的第一个城市是哪里？</span></span><br><span class="line"><span class="attribute">🤖 Agent</span><span class="punctuation">:</span> <span class="string">您第一个问的是北京的天气情况。</span></span><br></pre></td></tr></table></figure></p>
<h2 id="整体架构"><a href="#整体架构" class="headerlink" title="整体架构"></a>整体架构</h2><p>我们的 Agent 由三部分组成：</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>组件</th>
<th>作用</th>
<th>代码对应</th>
</tr>
</thead>
<tbody>
<tr>
<td>大脑</td>
<td>理解用户意图，决定是否调用工具</td>
<td><code>ChatOpenAI</code></td>
</tr>
<tr>
<td>手脚</td>
<td>执行具体任务（如查天气）</td>
<td><code>@tool</code> 装饰的函数</td>
</tr>
<tr>
<td>记忆</td>
<td>记住上下文和历史对话</td>
<td><code>InMemorySaver</code></td>
</tr>
</tbody>
</table>
</div>
<h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install langchain langchain-openai langgraph</span><br></pre></td></tr></table></figure>
<p>如果你使用的是阿里云百炼平台（本代码示例使用 <code>qwen3-max</code>），还需要配置相应的 API Key。</p>]]>
    </summary>
    <title>从零开始：用 LangChain 构建带记忆的 AI 智能体（Agent）</title>
    <updated>2026-06-30T07:34:03.034Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="Transformer架构全景：从底层原理到设计精妙"><a href="#Transformer架构全景：从底层原理到设计精妙" class="headerlink" title="Transformer架构全景：从底层原理到设计精妙"></a>Transformer架构全景：从底层原理到设计精妙</h2><p>如果说深度学习是人工智能的引擎，那么Transformer无疑是这台引擎中最精密的“心脏”。它不仅彻底取代了RNN和LSTM，更成为了所有现代大语言模型（如GPT、BERT、Llama）的基石。</p><p>要真正理解Transformer，我们不能只停留在“它是什么”，更要探究“它为什么这样设计”。今天，我们将从底层原理出发，进行一次彻底的架构拆解。</p><h3 id="告别“金鱼记忆”：RNN的根本性缺陷"><a href="#告别“金鱼记忆”：RNN的根本性缺陷" class="headerlink" title="告别“金鱼记忆”：RNN的根本性缺陷"></a>告别“金鱼记忆”：RNN的根本性缺陷</h3><p>在Transformer出现之前，处理序列数据（如句子）的主流是循环神经网络（RNN）。你可以把它想象成一个正在排队传话的游戏：信息必须按顺序，从第一个词传递到最后一个词。</p><p>这种方式有两个致命的根本性缺陷：</p><ol><li><strong>无法并行计算</strong>：处理第10个词时，必须等前9个词都处理完毕。这导致训练速度极慢，GPU强大的并行计算能力完全无法施展。</li><li><strong>长距离依赖路径过长</strong>：这是RNN的“阿喀琉斯之踵”。信息从序列开头传递到结尾，需要经过N个时间步。路径越长，梯度在反向传播时就越容易消失或爆炸，导致模型“忘记”开头的信息。例如，在“我出生在法国…我会说法语”这句话中，RNN很难将开头的“法国”和结尾的“法语”有效关联起来。</li></ol><p>Transformer的出现，正是为了从根本上解决这两个问题。</p><p><img data-src="/images/Python/大模型/transformer架构.png" alt="transformer架构" style="zoom:67%;"></p><h3 id="Transformer-模型的整体结构"><a href="#Transformer-模型的整体结构" class="headerlink" title="Transformer 模型的整体结构"></a>Transformer 模型的整体结构</h3><p>Transformer 由 <strong>编码器（Encoder）</strong> 和 <strong>解码器（Decoder）</strong> 两部分组成：</p><ul><li><p><strong>编码器</strong>：由多个相同的层堆叠而成，每层包含：</p><ol><li><strong>自注意力机制（Self-Attention）</strong></li><li><strong>前馈神经网络（Feed-Forward Network）</strong></li><li><strong>残差连接（Residual Connection）</strong></li><li><strong>层归一化（Layer Normalization）</strong></li></ol></li><li><p><strong>解码器</strong>：与编码器结构类似，但多了一个<strong>编码器-解码器注意力机制（Encoder-Decoder Attention）</strong></p></li></ul><hr><h4 id="Transformer-各个组件的功能和联系"><a href="#Transformer-各个组件的功能和联系" class="headerlink" title="Transformer 各个组件的功能和联系"></a>Transformer 各个组件的功能和联系</h4><h5 id="1-嵌入表示层（Embedding-Layer）"><a href="#1-嵌入表示层（Embedding-Layer）" class="headerlink" title="1. 嵌入表示层（Embedding Layer）"></a>1. <strong>嵌入表示层（Embedding Layer）</strong></h5><ul><li>功能：将输入的离散词 ID 转换为连续向量表示（词向量）。</li><li>输入：词的索引（如 [102, 567, …]）</li><li>输出：词向量（如 [d_model, d_model, …]）</li><li>注意：词向量维度通常等于 <code>d_model</code>，即模型的隐藏层维度。</li></ul><blockquote><p> 与位置编码结合：嵌入表示层输出的向量通常会与 <strong>位置编码（Positional Encoding）</strong> 相加，以引入位置信息。</p></blockquote><hr><p><strong>位置编码 (Positional Encoding)</strong> 的组件。这是一种包含位置信息的向量，<strong>通常使用不同频率的正弦和余弦函数生成</strong>。</p><p>它将“我在第几个位置”的信息，以一种固定的模式注入到每个词的向量中。这样，模型既能并行计算，又能清晰地分辨出词语的先后顺序。</p><p>在不使用位置编码时，每个词出现在句子中的任何位置计算出来的注意力分数都是一样的，也就是的 你欠我100块钱  == 我欠你100块钱</p><blockquote><p>嵌入层只在第一层存在，保证编码输入在所有层中都是稳定的</p></blockquote><h5 id="2-注意力层（Attention-Layer）"><a href="#2-注意力层（Attention-Layer）" class="headerlink" title="2. 注意力层（Attention Layer）"></a>2. <strong>注意力层（Attention Layer）</strong></h5><ul><li><p>功能：计算输入之间的相关性，决定哪些词在处理当前词时更重要。用来整合上下文语义，是的序列中任意两个单词之间的依赖关系可以直接被建模而不基于传统的循环结构，从而更好地解决文本的长程依赖。它的核心思想是：让句子中的每个词都能直接“看到”并“关注”到句子中的其他所有词，无论距离多远。</p><p>这个过程可以用一个精妙的数学公式来描述：<strong>缩放点积注意力 (Scaled Dot-Product Attention)</strong>。</p><script type="math/tex; mode=display">\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V</script><p>这个公式看起来很复杂，但我们可以将其拆解为四个直观的步骤：</p><ol><li><p><strong>生成Q, K, V</strong>：对于输入序列中的每个词，模型都会通过线性变换生成三个向量：</p><ul><li><strong>查询 (Query, Q)</strong>：代表当前词“想寻找”什么信息。</li><li><strong>键 (Key, K)</strong>：代表每个词“拥有”什么信息，用于被查询。</li><li><strong>值 (Value, V)</strong>：代表每个词包含的“实际内容”。</li></ul></li><li><p><strong>计算相关性 (QKᵀ)</strong>：通过计算查询向量Q和所有键向量K的点积，我们得到了一个分数矩阵。这个分数代表了“当前词”与“句子中所有词”的相关性。分数越高，说明两者关系越紧密。</p></li><li><p><strong>缩放与归一化 (除以√dₖ并Softmax)</strong>：</p><ul><li><strong>缩放 (Scaling)</strong>：为什么要除以√dₖ？当向量维度dₖ很高时，点积的结果会变得非常大，导致Softmax函数进入梯度极小的区域（饱和区），从而阻碍模型学习。除以√dₖ可以将数值拉回到一个合适的范围，保证梯度的稳定流动。</li><li><strong>归一化 (Softmax)</strong>：将上一步得到的分数通过Softmax函数转换成一个概率分布。这些概率就是“注意力权重”，决定了在聚合信息时，应该给每个词的值向量V分配多少“注意力”。前向传播阶段(防止梯度爆炸)</li></ul><span id="more"></span></li><li><p><strong>加权求和 (乘以V)</strong>：最后，用得到的注意力权重对所有的值向量V进行加权求和。这样，我们就为当前词得到了一个全新的、融合了整个句子上下文信息的表示。</p></li></ol></li><li><p>类型：</p><ul><li><strong>自注意力（Self-Attention）</strong>：在编码器和解码器中都使用，允许每个位置关注输入序列中所有位置的信息。</li><li><strong>编码器-解码器注意力（Encoder-Decoder Attention）</strong>：在解码器中使用，允许解码器关注编码器的输出。</li></ul></li></ul><blockquote><p>注意力机制是 Transformer 的核心，它取代了传统的 RNN 结构，使模型可以并行处理序列。</p></blockquote><hr><h6 id="多视角洞察：多头注意力"><a href="#多视角洞察：多头注意力" class="headerlink" title="多视角洞察：多头注意力"></a>多视角洞察：多头注意力</h6><p>单一的注意力机制可能只关注到一种类型的关系。为了让模型能同时从多个角度理解句子，Transformer引入了<strong>多头注意力 (Multi-Head Attention)</strong>。</p><p>它的做法是：将原始的Q、K、V向量分别投影到多个低维度的子空间中。在每个子空间里，独立地执行一次自注意力计算。最后，将所有“头”的输出拼接起来，再进行一次线性变换。</p><p>这就像是让模型戴上多副不同功能的眼镜：一副关注语法结构，一副关注语义指代，另一副关注实体关系。最后，模型综合所有眼镜看到的信息，形成一个更全面、更深刻的理解。</p><blockquote><p>虽然每层都有多头注意力机制，但是所执行的任务不同，每层先从前一层学习，然后从不同角度去理解序列中词元的相关性</p></blockquote><h5 id="3-前馈神经网络（Feed-Forward-Network-FFN）"><a href="#3-前馈神经网络（Feed-Forward-Network-FFN）" class="headerlink" title="3. 前馈神经网络（Feed-Forward Network, FFN）"></a>3. <strong>前馈神经网络（Feed-Forward Network, FFN）</strong></h5><ul><li>功能：每个编码器和解码器层还包含一个<strong>前馈神经网络 (Feed-Forward Network, FFN)</strong>。它通常由两个线性变换和一个ReLU激活函数构成。通过全连接层对输入文本序列中的每个单词表示进行更复杂的变换。FFN的作用是对每个位置的表示进行独立的、非线性的变换，进一步增强了模型的表达能力。</li><li>结构：两层全连接网络，中间有激活函数（通常是 ReLU）。</li><li>特点：每个位置独立处理，参数共享。</li></ul><blockquote><p>FFN 是注意力层之后的非线性处理模块，增强模型表达能力。</p></blockquote><hr><h5 id="4-残差连接（Residual-Connection）"><a href="#4-残差连接（Residual-Connection）" class="headerlink" title="4. 残差连接（Residual Connection）"></a>4. <strong>残差连接（Residual Connection）</strong></h5><ul><li>功能：对应图中的Add部分。是一条分别作用在注意力层和位置感知前馈层当中的直连通路，被用于连接它们的输入和输出。从而使得信息流动更加高效，有利于模型的优化。就像是在高楼大厦旁修建的“紧急通道”，让梯度在反向传播时能直接流向底层，完美解决了深层网络中的梯度消失问题，帮助训练深层网络。反向传播阶段(防止梯度消失)</li><li>应用：在每个子层（注意力层、FFN 层）之后加上残差连接，即：<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">output = layer(<span class="built_in">input</span>) + <span class="built_in">input</span></span><br></pre></td></tr></table></figure></li></ul><blockquote><p>残差连接与层归一化一起使用，是 Transformer 可以堆叠多层的关键。</p></blockquote><hr><h5 id="5-层归一化（Layer-Normalization）"><a href="#5-层归一化（Layer-Normalization）" class="headerlink" title="5. 层归一化（Layer Normalization）"></a>5. <strong>层归一化（Layer Normalization）</strong></h5><ul><li>功能：对应图中的Norm部分。作用于注意力层和位置感知前馈层的输出表示序列中，对表示序列进行层归一化操作，同样起到稳定优化的作用。它将每一层神经元的激活值规范化，使得训练过程更加稳定，收敛速度更快。</li><li>应用：在每个子层（注意力、FFN）+ 残差连接后使用。</li></ul><blockquote><p>与 BatchNorm 不同，LayerNorm 是在特征维度上归一化，适合序列模型。</p></blockquote><hr><h5 id="6-编码器（Encoder）"><a href="#6-编码器（Encoder）" class="headerlink" title="6. 编码器（Encoder）"></a>6. <strong>编码器（Encoder）</strong></h5><ul><li><p>组成：</p><ul><li>N 个相同的编码器层（通常 N=6）</li><li>每个编码器层包括：<ol><li>自注意力层</li><li>前馈神经网络</li><li>残差连接 + 层归一化（两个）</li></ol></li></ul></li><li><p>输入：嵌入表示 + 位置编码</p></li><li>输出：上下文表示（contextual representations）</li></ul><hr><h5 id="7-解码器（Decoder）"><a href="#7-解码器（Decoder）" class="headerlink" title="7. 解码器（Decoder）"></a>7. <strong>解码器（Decoder）</strong></h5><p>解码器比编码器多两个关键设计，以服务于“生成”任务：</p><ul><li><strong>掩码自注意力 (Masked Self-Attention)</strong>：在生成文本时，模型不能“偷看”未来的词。比如预测第3个词时，只能看到第1、2个词。掩码机制通过在计算注意力分数时，将未来位置的分数设为负无穷，从而确保生成过程的因果逻辑。</li><li><strong>交叉注意力 (Cross-Attention)</strong>：这是解码器与编码器沟通的桥梁。在这里，查询(Q)来自解码器自身，而键(K)和值(V)则来自编码器的最终输出。这使得解码器在生成每一个词时，都能有选择地“查询”源句子的相关信息，确保了翻译或生成的准确性。</li></ul><ul><li><p>组成：</p><ul><li>N 个相同的解码器层（通常 N=6）</li><li>每个解码器层包括：<ol><li>自注意力层（带掩码，防止看到未来词）</li><li>编码器-解码器注意力层</li><li>前馈神经网络</li><li>每一层都加残差连接 + 层归一化</li></ol></li></ul></li><li><p>输入：</p><ul><li>目标序列的嵌入表示 + 位置编码</li></ul></li><li>输出：<ul><li>用于生成下一个词的表示</li></ul></li></ul><hr><h4 id="各组件之间的联系总结图（顺序处理）"><a href="#各组件之间的联系总结图（顺序处理）" class="headerlink" title="各组件之间的联系总结图（顺序处理）"></a>各组件之间的联系总结图（顺序处理）</h4><figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">输入序列</span><br><span class="line">  ↓</span><br><span class="line">嵌入表示层 + 位置编码</span><br><span class="line">  ↓</span><br><span class="line">编码器层（自注意力 + FFN） × <span class="built_in">N</span></span><br><span class="line">  ↓</span><br><span class="line">编码器输出（上下文表示）</span><br><span class="line">  ↓</span><br><span class="line">解码器层（自注意力 + 编码器-解码器注意力 + FFN） × <span class="built_in">N</span></span><br><span class="line">  ↓</span><br><span class="line">输出表示 → 线性层 + Softmax → 生成下一个词</span><br></pre></td></tr></table></figure><blockquote><p>Transformer 通过 <strong>嵌入层</strong> 表示输入，<strong>注意力机制</strong> 建模上下文依赖，<strong>前馈网络</strong> 提升非线性表达，<strong>残差连接和层归一化</strong> 提高训练稳定性，最终由 <strong>编码器和解码器堆叠</strong> 实现端到端的序列建模任务。</p></blockquote><h3 id="结语：分久必合的架构演进"><a href="#结语：分久必合的架构演进" class="headerlink" title="结语：分久必合的架构演进"></a>结语：分久必合的架构演进</h3><p>Transformer的诞生是AI历史上的分水岭。它通过自注意力机制实现了全局视野，通过并行计算释放了算力，通过模块化设计适应了各种任务。</p><p>从最初的Encoder-Decoder结构，到后来BERT专注于Encoder的理解能力，再到GPT将Decoder的生成能力发挥到极致，Transformer架构在不断演变。理解了这个基础结构，你就拿到了通往大模型世界的钥匙。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/6.transformer%E6%9E%B6%E6%9E%84%E8%AF%A6%E8%A7%A3/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/6.transformer%E6%9E%B6%E6%9E%84%E8%AF%A6%E8%A7%A3/"/>
    <published>2026-05-05T02:12:23.000Z</published>
    <summary>
      <![CDATA[<h2 id="Transformer架构全景：从底层原理到设计精妙"><a href="#Transformer架构全景：从底层原理到设计精妙" class="headerlink" title="Transformer架构全景：从底层原理到设计精妙"></a>Transformer架构全景：从底层原理到设计精妙</h2><p>如果说深度学习是人工智能的引擎，那么Transformer无疑是这台引擎中最精密的“心脏”。它不仅彻底取代了RNN和LSTM，更成为了所有现代大语言模型（如GPT、BERT、Llama）的基石。</p>
<p>要真正理解Transformer，我们不能只停留在“它是什么”，更要探究“它为什么这样设计”。今天，我们将从底层原理出发，进行一次彻底的架构拆解。</p>
<h3 id="告别“金鱼记忆”：RNN的根本性缺陷"><a href="#告别“金鱼记忆”：RNN的根本性缺陷" class="headerlink" title="告别“金鱼记忆”：RNN的根本性缺陷"></a>告别“金鱼记忆”：RNN的根本性缺陷</h3><p>在Transformer出现之前，处理序列数据（如句子）的主流是循环神经网络（RNN）。你可以把它想象成一个正在排队传话的游戏：信息必须按顺序，从第一个词传递到最后一个词。</p>
<p>这种方式有两个致命的根本性缺陷：</p>
<ol>
<li><strong>无法并行计算</strong>：处理第10个词时，必须等前9个词都处理完毕。这导致训练速度极慢，GPU强大的并行计算能力完全无法施展。</li>
<li><strong>长距离依赖路径过长</strong>：这是RNN的“阿喀琉斯之踵”。信息从序列开头传递到结尾，需要经过N个时间步。路径越长，梯度在反向传播时就越容易消失或爆炸，导致模型“忘记”开头的信息。例如，在“我出生在法国…我会说法语”这句话中，RNN很难将开头的“法国”和结尾的“法语”有效关联起来。</li>
</ol>
<p>Transformer的出现，正是为了从根本上解决这两个问题。</p>
<p><img src="/images/Python/大模型/transformer架构.png" alt="transformer架构" style="zoom:67%;"></p>
<h3 id="Transformer-模型的整体结构"><a href="#Transformer-模型的整体结构" class="headerlink" title="Transformer 模型的整体结构"></a>Transformer 模型的整体结构</h3><p>Transformer 由 <strong>编码器（Encoder）</strong> 和 <strong>解码器（Decoder）</strong> 两部分组成：</p>
<ul>
<li><p><strong>编码器</strong>：由多个相同的层堆叠而成，每层包含：</p>
<ol>
<li><strong>自注意力机制（Self-Attention）</strong></li>
<li><strong>前馈神经网络（Feed-Forward Network）</strong></li>
<li><strong>残差连接（Residual Connection）</strong></li>
<li><strong>层归一化（Layer Normalization）</strong></li>
</ol>
</li>
<li><p><strong>解码器</strong>：与编码器结构类似，但多了一个<strong>编码器-解码器注意力机制（Encoder-Decoder Attention）</strong></p>
</li>
</ul>
<hr>
<h4 id="Transformer-各个组件的功能和联系"><a href="#Transformer-各个组件的功能和联系" class="headerlink" title="Transformer 各个组件的功能和联系"></a>Transformer 各个组件的功能和联系</h4><h5 id="1-嵌入表示层（Embedding-Layer）"><a href="#1-嵌入表示层（Embedding-Layer）" class="headerlink" title="1. 嵌入表示层（Embedding Layer）"></a>1. <strong>嵌入表示层（Embedding Layer）</strong></h5><ul>
<li>功能：将输入的离散词 ID 转换为连续向量表示（词向量）。</li>
<li>输入：词的索引（如 [102, 567, …]）</li>
<li>输出：词向量（如 [d_model, d_model, …]）</li>
<li>注意：词向量维度通常等于 <code>d_model</code>，即模型的隐藏层维度。</li>
</ul>
<blockquote>
<p> 与位置编码结合：嵌入表示层输出的向量通常会与 <strong>位置编码（Positional Encoding）</strong> 相加，以引入位置信息。</p>
</blockquote>
<hr>
<p><strong>位置编码 (Positional Encoding)</strong> 的组件。这是一种包含位置信息的向量，<strong>通常使用不同频率的正弦和余弦函数生成</strong>。</p>
<p>它将“我在第几个位置”的信息，以一种固定的模式注入到每个词的向量中。这样，模型既能并行计算，又能清晰地分辨出词语的先后顺序。</p>
<p>在不使用位置编码时，每个词出现在句子中的任何位置计算出来的注意力分数都是一样的，也就是的 你欠我100块钱  == 我欠你100块钱</p>
<blockquote>
<p>嵌入层只在第一层存在，保证编码输入在所有层中都是稳定的</p>
</blockquote>
<h5 id="2-注意力层（Attention-Layer）"><a href="#2-注意力层（Attention-Layer）" class="headerlink" title="2. 注意力层（Attention Layer）"></a>2. <strong>注意力层（Attention Layer）</strong></h5><ul>
<li><p>功能：计算输入之间的相关性，决定哪些词在处理当前词时更重要。用来整合上下文语义，是的序列中任意两个单词之间的依赖关系可以直接被建模而不基于传统的循环结构，从而更好地解决文本的长程依赖。它的核心思想是：让句子中的每个词都能直接“看到”并“关注”到句子中的其他所有词，无论距离多远。</p>
<p>这个过程可以用一个精妙的数学公式来描述：<strong>缩放点积注意力 (Scaled Dot-Product Attention)</strong>。</p>
<script type="math/tex; mode=display">
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V</script><p>这个公式看起来很复杂，但我们可以将其拆解为四个直观的步骤：</p>
<ol>
<li><p><strong>生成Q, K, V</strong>：对于输入序列中的每个词，模型都会通过线性变换生成三个向量：</p>
<ul>
<li><strong>查询 (Query, Q)</strong>：代表当前词“想寻找”什么信息。</li>
<li><strong>键 (Key, K)</strong>：代表每个词“拥有”什么信息，用于被查询。</li>
<li><strong>值 (Value, V)</strong>：代表每个词包含的“实际内容”。</li>
</ul>
</li>
<li><p><strong>计算相关性 (QKᵀ)</strong>：通过计算查询向量Q和所有键向量K的点积，我们得到了一个分数矩阵。这个分数代表了“当前词”与“句子中所有词”的相关性。分数越高，说明两者关系越紧密。</p>
</li>
<li><p><strong>缩放与归一化 (除以√dₖ并Softmax)</strong>：</p>
<ul>
<li><strong>缩放 (Scaling)</strong>：为什么要除以√dₖ？当向量维度dₖ很高时，点积的结果会变得非常大，导致Softmax函数进入梯度极小的区域（饱和区），从而阻碍模型学习。除以√dₖ可以将数值拉回到一个合适的范围，保证梯度的稳定流动。</li>
<li><strong>归一化 (Softmax)</strong>：将上一步得到的分数通过Softmax函数转换成一个概率分布。这些概率就是“注意力权重”，决定了在聚合信息时，应该给每个词的值向量V分配多少“注意力”。前向传播阶段(防止梯度爆炸)</li>
</ul></li></ol></li></ul>]]>
    </summary>
    <title>Transformer架构全景</title>
    <updated>2026-06-26T07:59:36.052Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="Python" scheme="https://zhhll.com.cn/categories/Python/"/>
    <category term="Python" scheme="https://zhhll.com.cn/tags/Python/"/>
    <content>
      <![CDATA[<h1 id="Pandas-数据分析：80-的时间在清洗，这篇帮你省掉一半"><a href="#Pandas-数据分析：80-的时间在清洗，这篇帮你省掉一半" class="headerlink" title="Pandas 数据分析：80% 的时间在清洗，这篇帮你省掉一半"></a>Pandas 数据分析：80% 的时间在清洗，这篇帮你省掉一半</h1><p>做数据分析的人都知道一句话：<strong>“垃圾进，垃圾出”</strong>。不管你的图表画得多漂亮、模型多复杂，数据本身有问题，结论就是错的。</p><p>数据科学家 80% 的时间花在数据清洗和准备上。不是算法难，是数据脏——缺失值、重复行、格式混乱、异常值、字段名不一致……</p><p>Pandas 就是干这个的。它是 Python 数据分析的基石，能帮你把脏数据变成干净、可分析的表格。</p><h3 id="Pandas-的两个核心结构：Series-和-DataFrame"><a href="#Pandas-的两个核心结构：Series-和-DataFrame" class="headerlink" title="Pandas 的两个核心结构：Series 和 DataFrame"></a>Pandas 的两个核心结构：Series 和 DataFrame</h3><div class="table-container"><table><thead><tr><th style="text-align:left">结构</th><th style="text-align:left">维度</th><th style="text-align:left">类似</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Series</strong></td><td style="text-align:left">1维，一列数据</td><td style="text-align:left">Excel 里的一列</td></tr><tr><td style="text-align:left"><strong>DataFrame</strong></td><td style="text-align:left">2维，表格</td><td style="text-align:left">Excel 里的整张表</td></tr></tbody></table></div><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># DataFrame 就像一张表</span></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&#x27;姓名&#x27;</span>: [<span class="string">&#x27;张三&#x27;</span>, <span class="string">&#x27;李四&#x27;</span>, <span class="string">&#x27;王五&#x27;</span>],</span><br><span class="line">    <span class="string">&#x27;年龄&#x27;</span>: [<span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>],</span><br><span class="line">    <span class="string">&#x27;城市&#x27;</span>: [<span class="string">&#x27;北京&#x27;</span>, <span class="string">&#x27;上海&#x27;</span>, <span class="string">&#x27;广州&#x27;</span>]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>日常 99% 的工作都在跟 DataFrame 打交道——加载、清洗、筛选、聚合、输出。</p><h3 id="拿到数据第一件事：先”体检”"><a href="#拿到数据第一件事：先”体检”" class="headerlink" title="拿到数据第一件事：先”体检”"></a>拿到数据第一件事：先”体检”</h3><p>假设你拿到一份销售订单数据 <code>sales.csv</code>，不要急着分析，先体检。</p><h4 id="看一眼数据长什么样："><a href="#看一眼数据长什么样：" class="headerlink" title="看一眼数据长什么样："></a>看一眼数据长什么样：</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">df = pd.read_csv(<span class="string">&#x27;sales.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">df.head()      <span class="comment"># 前5行，看列名和样例数据</span></span><br><span class="line">df.tail()      <span class="comment"># 后5行，看数据末尾有没有异常</span></span><br><span class="line">df.sample(<span class="number">5</span>)   <span class="comment"># 随机5行，避免排序带来的偏见</span></span><br></pre></td></tr></table></figure><p>看整体概况：多少行、多少列、每列是什么类型、有没有空值</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df.info()</span><br></pre></td></tr></table></figure><p>输出里重点关注：</p><ul><li><strong>Non-Null Count</strong>：小于总行数说明有缺失值</li><li><strong>Dtype</strong>：object 表示字符串，需要确认是否需要转成日期或数字</li></ul><h4 id="看数值列的统计摘要："><a href="#看数值列的统计摘要：" class="headerlink" title="看数值列的统计摘要："></a>看数值列的统计摘要：</h4><span id="more"></span><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df.describe()</span><br></pre></td></tr></table></figure><p>输出里关注：</p><ul><li><strong>count</strong>：非空数量，进一步确认缺失</li><li><strong>min/max</strong>：最小值最大值，有没有明显异常（比如年龄出现 999）</li><li><strong>mean/std</strong>：均值和标准差，判断数据分布是否合理</li></ul><h4 id="专门看一下缺失值："><a href="#专门看一下缺失值：" class="headerlink" title="专门看一下缺失值："></a>专门看一下缺失值：</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">df.isnull().<span class="built_in">sum</span>()                     <span class="comment"># 每列缺了多少</span></span><br><span class="line">df.isnull().<span class="built_in">sum</span>() / <span class="built_in">len</span>(df) * <span class="number">100</span>     <span class="comment"># 缺失比例</span></span><br></pre></td></tr></table></figure><p>拿到”体检报告”之后，你才知道数据有哪些问题：哪些列有缺失、哪些列类型不对、哪些值明显异常。<strong>先诊断再动手，别上来就瞎洗。</strong></p><h3 id="数据清洗：处理重复值、缺失值、异常值"><a href="#数据清洗：处理重复值、缺失值、异常值" class="headerlink" title="数据清洗：处理重复值、缺失值、异常值"></a>数据清洗：处理重复值、缺失值、异常值</h3><h4 id="重复值：先看有没有，有就删"><a href="#重复值：先看有没有，有就删" class="headerlink" title="重复值：先看有没有，有就删"></a>重复值：先看有没有，有就删</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">df.duplicated().<span class="built_in">sum</span>()          <span class="comment"># 重复行数量</span></span><br><span class="line">df = df.drop_duplicates()      <span class="comment"># 删除重复行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果某几列组合应该唯一，按这几列去重</span></span><br><span class="line">df = df.drop_duplicates(subset=[<span class="string">&#x27;订单号&#x27;</span>, <span class="string">&#x27;客户ID&#x27;</span>])</span><br></pre></td></tr></table></figure><h4 id="缺失值：删还是填？"><a href="#缺失值：删还是填？" class="headerlink" title="缺失值：删还是填？"></a>缺失值：删还是填？</h4><p><strong>先判断严重程度：</strong></p><ul><li>某列缺失超过 50% → 考虑直接删掉这列</li><li>某列缺失较少 → 填充</li></ul><p><strong>删除：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 删掉包含任何空值的行（谨慎用，丢数据太多）</span></span><br><span class="line">df = df.dropna()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删掉&quot;客户ID&quot;或&quot;订单金额&quot;为空的行（关键字段不能丢）</span></span><br><span class="line">df = df.dropna(subset=[<span class="string">&#x27;客户ID&#x27;</span>, <span class="string">&#x27;订单金额&#x27;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删掉缺失超过一半的列</span></span><br><span class="line">threshold = <span class="built_in">len</span>(df) * <span class="number">0.5</span></span><br><span class="line">df = df.dropna(axis=<span class="number">1</span>, thresh=threshold)</span><br></pre></td></tr></table></figure><p><strong>填充（更常用）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 数字列：用中位数填充（比均值更稳健，不易受异常值影响）</span></span><br><span class="line">df[<span class="string">&#x27;年龄&#x27;</span>] = df[<span class="string">&#x27;年龄&#x27;</span>].fillna(df[<span class="string">&#x27;年龄&#x27;</span>].median())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分类列：用众数填充</span></span><br><span class="line">df[<span class="string">&#x27;城市&#x27;</span>] = df[<span class="string">&#x27;城市&#x27;</span>].fillna(df[<span class="string">&#x27;城市&#x27;</span>].mode()[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 时间序列：用前一个值填充（前向填充）</span></span><br><span class="line">df[<span class="string">&#x27;价格&#x27;</span>] = df[<span class="string">&#x27;价格&#x27;</span>].fillna(method=<span class="string">&#x27;ffill&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 时间序列：用插值填充</span></span><br><span class="line">df[<span class="string">&#x27;销售额&#x27;</span>] = df[<span class="string">&#x27;销售额&#x27;</span>].interpolate()</span><br></pre></td></tr></table></figure><h4 id="异常值：找出来，处理掉"><a href="#异常值：找出来，处理掉" class="headerlink" title="异常值：找出来，处理掉"></a>异常值：找出来，处理掉</h4><h5 id="方法一：IQR（四分位距）法——最常用"><a href="#方法一：IQR（四分位距）法——最常用" class="headerlink" title="方法一：IQR（四分位距）法——最常用"></a><strong>方法一：IQR（四分位距）法——最常用</strong></h5><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">detect_outliers</span>(<span class="params">df, col</span>):</span><br><span class="line">    Q1 = df[col].quantile(<span class="number">0.25</span>)</span><br><span class="line">    Q3 = df[col].quantile(<span class="number">0.75</span>)</span><br><span class="line">    IQR = Q3 - Q1</span><br><span class="line">    lower = Q1 - <span class="number">1.5</span> * IQR</span><br><span class="line">    upper = Q3 + <span class="number">1.5</span> * IQR</span><br><span class="line">    <span class="keyword">return</span> lower, upper</span><br><span class="line"></span><br><span class="line">lower, upper = detect_outliers(df, <span class="string">&#x27;订单金额&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 剔除异常值</span></span><br><span class="line">df = df[(df[<span class="string">&#x27;订单金额&#x27;</span>] &gt;= lower) &amp; (df[<span class="string">&#x27;订单金额&#x27;</span>] &lt;= upper)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或把异常值&quot;盖&quot;到边界（盖帽法）</span></span><br><span class="line">df[<span class="string">&#x27;订单金额&#x27;</span>] = df[<span class="string">&#x27;订单金额&#x27;</span>].clip(lower=lower, upper=upper)</span><br></pre></td></tr></table></figure><h5 id="方法二：业务规则过滤"><a href="#方法二：业务规则过滤" class="headerlink" title="方法二：业务规则过滤"></a><strong>方法二：业务规则过滤</strong></h5><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 年龄不能为负，不能超过120</span></span><br><span class="line">df = df[df[<span class="string">&#x27;年龄&#x27;</span>] &gt;= <span class="number">0</span>]</span><br><span class="line">df = df[df[<span class="string">&#x27;年龄&#x27;</span>] &lt;= <span class="number">120</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 单价不能为负</span></span><br><span class="line">df = df[df[<span class="string">&#x27;单价&#x27;</span>] &gt;= <span class="number">0</span>]</span><br></pre></td></tr></table></figure><h4 id="数据类型转换"><a href="#数据类型转换" class="headerlink" title="数据类型转换"></a>数据类型转换</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 字符串转数字（转不了的话变成 NaN，再处理）</span></span><br><span class="line">df[<span class="string">&#x27;价格&#x27;</span>] = pd.to_numeric(df[<span class="string">&#x27;价格&#x27;</span>], errors=<span class="string">&#x27;coerce&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 字符串转日期</span></span><br><span class="line">df[<span class="string">&#x27;订单日期&#x27;</span>] = pd.to_datetime(df[<span class="string">&#x27;订单日期&#x27;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 转为分类类型（节省内存）</span></span><br><span class="line">df[<span class="string">&#x27;城市&#x27;</span>] = df[<span class="string">&#x27;城市&#x27;</span>].astype(<span class="string">&#x27;category&#x27;</span>)</span><br></pre></td></tr></table></figure><h3 id="数据筛选：从数据里捞出你要的那部分"><a href="#数据筛选：从数据里捞出你要的那部分" class="headerlink" title="数据筛选：从数据里捞出你要的那部分"></a>数据筛选：从数据里捞出你要的那部分</h3><p><strong>布尔索引（最常用）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 单条件</span></span><br><span class="line">electronics = df[df[<span class="string">&#x27;品类&#x27;</span>] == <span class="string">&#x27;电子产品&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多条件 AND（用 &amp;）</span></span><br><span class="line">filtered = df[(df[<span class="string">&#x27;年龄&#x27;</span>] &gt; <span class="number">25</span>) &amp; (df[<span class="string">&#x27;城市&#x27;</span>] == <span class="string">&#x27;北京&#x27;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多条件 OR（用 |）</span></span><br><span class="line">filtered = df[(df[<span class="string">&#x27;品类&#x27;</span>] == <span class="string">&#x27;电子产品&#x27;</span>) | (df[<span class="string">&#x27;品类&#x27;</span>] == <span class="string">&#x27;图书&#x27;</span>)]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 简化 OR：用 isin()</span></span><br><span class="line">filtered = df[df[<span class="string">&#x27;品类&#x27;</span>].isin([<span class="string">&#x27;电子产品&#x27;</span>, <span class="string">&#x27;图书&#x27;</span>])]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 取反（NOT）</span></span><br><span class="line">filtered = df[~df[<span class="string">&#x27;品类&#x27;</span>].isin([<span class="string">&#x27;电子产品&#x27;</span>, <span class="string">&#x27;图书&#x27;</span>])]</span><br></pre></td></tr></table></figure><p><strong>query() 写法更接近自然语言：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">filtered = df.query(<span class="string">&quot;年龄 &gt; 25 and 城市 == &#x27;北京&#x27;&quot;</span>)</span><br><span class="line">filtered = df.query(<span class="string">&quot;品类 in [&#x27;电子产品&#x27;, &#x27;图书&#x27;]&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>选列：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 选一列</span></span><br><span class="line">names = df[<span class="string">&#x27;姓名&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 选多列</span></span><br><span class="line">subset = df[[<span class="string">&#x27;姓名&#x27;</span>, <span class="string">&#x27;年龄&#x27;</span>, <span class="string">&#x27;城市&#x27;</span>]]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删列</span></span><br><span class="line">df = df.drop(<span class="string">&#x27;无用列&#x27;</span>, axis=<span class="number">1</span>)</span><br></pre></td></tr></table></figure><h3 id="特征工程：从已有数据里”造”出新信息"><a href="#特征工程：从已有数据里”造”出新信息" class="headerlink" title="特征工程：从已有数据里”造”出新信息"></a>特征工程：从已有数据里”造”出新信息</h3><h4 id="算术运算创建新列"><a href="#算术运算创建新列" class="headerlink" title="算术运算创建新列"></a>算术运算创建新列</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">df[<span class="string">&#x27;总金额&#x27;</span>] = df[<span class="string">&#x27;单价&#x27;</span>] * df[<span class="string">&#x27;数量&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 条件创建</span></span><br><span class="line">df[<span class="string">&#x27;价格等级&#x27;</span>] = np.where(df[<span class="string">&#x27;单价&#x27;</span>] &gt; <span class="number">50</span>, <span class="string">&#x27;高&#x27;</span>, <span class="string">&#x27;低&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多条件</span></span><br><span class="line">conditions = [</span><br><span class="line">    df[<span class="string">&#x27;单价&#x27;</span>] &lt; <span class="number">20</span>,</span><br><span class="line">    df[<span class="string">&#x27;单价&#x27;</span>] &lt; <span class="number">50</span>,</span><br><span class="line">    df[<span class="string">&#x27;单价&#x27;</span>] &gt;= <span class="number">50</span></span><br><span class="line">]</span><br><span class="line">choices = [<span class="string">&#x27;低档&#x27;</span>, <span class="string">&#x27;中档&#x27;</span>, <span class="string">&#x27;高档&#x27;</span>]</span><br><span class="line">df[<span class="string">&#x27;档次&#x27;</span>] = np.select(conditions, choices)</span><br></pre></td></tr></table></figure><h4 id="从日期里提取信息"><a href="#从日期里提取信息" class="headerlink" title="从日期里提取信息"></a>从日期里提取信息</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">df[<span class="string">&#x27;订单日期&#x27;</span>] = pd.to_datetime(df[<span class="string">&#x27;订单日期&#x27;</span>])</span><br><span class="line"></span><br><span class="line">df[<span class="string">&#x27;年&#x27;</span>] = df[<span class="string">&#x27;订单日期&#x27;</span>].dt.year</span><br><span class="line">df[<span class="string">&#x27;月&#x27;</span>] = df[<span class="string">&#x27;订单日期&#x27;</span>].dt.month</span><br><span class="line">df[<span class="string">&#x27;季度&#x27;</span>] = df[<span class="string">&#x27;订单日期&#x27;</span>].dt.quarter</span><br><span class="line">df[<span class="string">&#x27;星期几&#x27;</span>] = df[<span class="string">&#x27;订单日期&#x27;</span>].dt.day_name()      <span class="comment"># Monday, Tuesday...</span></span><br><span class="line">df[<span class="string">&#x27;是否周末&#x27;</span>] = df[<span class="string">&#x27;订单日期&#x27;</span>].dt.dayofweek &gt;= <span class="number">5</span></span><br></pre></td></tr></table></figure><h4 id="分箱（把连续值切成几段）"><a href="#分箱（把连续值切成几段）" class="headerlink" title="分箱（把连续值切成几段）"></a>分箱（把连续值切成几段）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 等距分箱</span></span><br><span class="line">bins = [<span class="number">0</span>, <span class="number">18</span>, <span class="number">30</span>, <span class="number">45</span>, <span class="number">60</span>, <span class="number">100</span>]</span><br><span class="line">labels = [<span class="string">&#x27;&lt;18&#x27;</span>, <span class="string">&#x27;18-30&#x27;</span>, <span class="string">&#x27;30-45&#x27;</span>, <span class="string">&#x27;45-60&#x27;</span>, <span class="string">&#x27;60+&#x27;</span>]</span><br><span class="line">df[<span class="string">&#x27;年龄段&#x27;</span>] = pd.cut(df[<span class="string">&#x27;年龄&#x27;</span>], bins=bins, labels=labels)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等频分箱（每段样本数差不多）</span></span><br><span class="line">df[<span class="string">&#x27;收入等级&#x27;</span>] = pd.qcut(df[<span class="string">&#x27;收入&#x27;</span>], q=<span class="number">4</span>, labels=[<span class="string">&#x27;低&#x27;</span>, <span class="string">&#x27;中低&#x27;</span>, <span class="string">&#x27;中高&#x27;</span>, <span class="string">&#x27;高&#x27;</span>])</span><br></pre></td></tr></table></figure><h4 id="apply-自定义函数"><a href="#apply-自定义函数" class="headerlink" title="apply 自定义函数"></a>apply 自定义函数</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">calc_discount</span>(<span class="params">row</span>):</span><br><span class="line">    <span class="keyword">if</span> row[<span class="string">&#x27;数量&#x27;</span>] &gt;= <span class="number">10</span>:</span><br><span class="line">        <span class="keyword">return</span> row[<span class="string">&#x27;单价&#x27;</span>] * <span class="number">0.9</span></span><br><span class="line">    <span class="keyword">return</span> row[<span class="string">&#x27;单价&#x27;</span>]</span><br><span class="line"></span><br><span class="line">df[<span class="string">&#x27;折后单价&#x27;</span>] = df.apply(calc_discount, axis=<span class="number">1</span>)</span><br></pre></td></tr></table></figure><h3 id="数据聚合：把明细变成报表"><a href="#数据聚合：把明细变成报表" class="headerlink" title="数据聚合：把明细变成报表"></a>数据聚合：把明细变成报表</h3><h4 id="groupby-分组聚合——数据分析最核心的操作："><a href="#groupby-分组聚合——数据分析最核心的操作：" class="headerlink" title="groupby 分组聚合——数据分析最核心的操作："></a><strong>groupby 分组聚合——数据分析最核心的操作：</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 按品类分组，算总销售额</span></span><br><span class="line">df.groupby(<span class="string">&#x27;品类&#x27;</span>)[<span class="string">&#x27;总金额&#x27;</span>].<span class="built_in">sum</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按品类分组，算多个指标</span></span><br><span class="line">df.groupby(<span class="string">&#x27;品类&#x27;</span>)[<span class="string">&#x27;总金额&#x27;</span>].agg([<span class="string">&#x27;sum&#x27;</span>, <span class="string">&#x27;mean&#x27;</span>, <span class="string">&#x27;count&#x27;</span>, <span class="string">&#x27;std&#x27;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按多个维度分组</span></span><br><span class="line">df.groupby([<span class="string">&#x27;品类&#x27;</span>, <span class="string">&#x27;城市&#x27;</span>])[<span class="string">&#x27;总金额&#x27;</span>].<span class="built_in">sum</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 不同列用不同聚合函数</span></span><br><span class="line">df.groupby(<span class="string">&#x27;品类&#x27;</span>).agg(&#123;</span><br><span class="line">    <span class="string">&#x27;总金额&#x27;</span>: [<span class="string">&#x27;sum&#x27;</span>, <span class="string">&#x27;mean&#x27;</span>],</span><br><span class="line">    <span class="string">&#x27;数量&#x27;</span>: <span class="string">&#x27;sum&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;单价&#x27;</span>: <span class="string">&#x27;median&#x27;</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 命名聚合（推荐，结果更清晰）</span></span><br><span class="line">df.groupby(<span class="string">&#x27;品类&#x27;</span>).agg(</span><br><span class="line">    总销售额=(<span class="string">&#x27;总金额&#x27;</span>, <span class="string">&#x27;sum&#x27;</span>),</span><br><span class="line">    平均单价=(<span class="string">&#x27;单价&#x27;</span>, <span class="string">&#x27;mean&#x27;</span>),</span><br><span class="line">    订单数=(<span class="string">&#x27;订单号&#x27;</span>, <span class="string">&#x27;count&#x27;</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h4 id="数据透视表：把分组结果展开成表格"><a href="#数据透视表：把分组结果展开成表格" class="headerlink" title="数据透视表：把分组结果展开成表格"></a><strong>数据透视表：把分组结果展开成表格</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 行=品类，列=城市，值=总销售额</span></span><br><span class="line">pivot = pd.pivot_table(</span><br><span class="line">    df,</span><br><span class="line">    values=<span class="string">&#x27;总金额&#x27;</span>,</span><br><span class="line">    index=<span class="string">&#x27;品类&#x27;</span>,</span><br><span class="line">    columns=<span class="string">&#x27;城市&#x27;</span>,</span><br><span class="line">    aggfunc=<span class="string">&#x27;sum&#x27;</span>,</span><br><span class="line">    fill_value=<span class="number">0</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加总计行列</span></span><br><span class="line">pivot = pd.pivot_table(</span><br><span class="line">    df,</span><br><span class="line">    values=<span class="string">&#x27;总金额&#x27;</span>,</span><br><span class="line">    index=<span class="string">&#x27;品类&#x27;</span>,</span><br><span class="line">    columns=<span class="string">&#x27;城市&#x27;</span>,</span><br><span class="line">    aggfunc=<span class="string">&#x27;sum&#x27;</span>,</span><br><span class="line">    margins=<span class="literal">True</span>,</span><br><span class="line">    margins_name=<span class="string">&#x27;总计&#x27;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="数据输出：把结果保存下来"><a href="#数据输出：把结果保存下来" class="headerlink" title="数据输出：把结果保存下来"></a>数据输出：把结果保存下来</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 保存为 CSV</span></span><br><span class="line">df.to_csv(<span class="string">&#x27;清洗后数据.csv&#x27;</span>, index=<span class="literal">False</span>, encoding=<span class="string">&#x27;utf-8-sig&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存为 Excel</span></span><br><span class="line">df.to_excel(<span class="string">&#x27;报表.xlsx&#x27;</span>, index=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存多个 Sheet</span></span><br><span class="line"><span class="keyword">with</span> pd.ExcelWriter(<span class="string">&#x27;报表.xlsx&#x27;</span>) <span class="keyword">as</span> writer:</span><br><span class="line">    df1.to_excel(writer, sheet_name=<span class="string">&#x27;销售明细&#x27;</span>, index=<span class="literal">False</span>)</span><br><span class="line">    df2.to_excel(writer, sheet_name=<span class="string">&#x27;汇总&#x27;</span>, index=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存为 JSON</span></span><br><span class="line">df.to_json(<span class="string">&#x27;数据.json&#x27;</span>, orient=<span class="string">&#x27;records&#x27;</span>, force_ascii=<span class="literal">False</span>)</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/23.pandas%E6%93%8D%E4%BD%9C/</id>
    <link href="https://zhhll.com.cn/2026/Python/23.pandas%E6%93%8D%E4%BD%9C/"/>
    <published>2026-05-03T09:40:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="Pandas-数据分析：80-的时间在清洗，这篇帮你省掉一半"><a href="#Pandas-数据分析：80-的时间在清洗，这篇帮你省掉一半" class="headerlink" title="Pandas 数据分析：80% 的时间在清洗，这篇帮你省掉一半"></a>Pandas 数据分析：80% 的时间在清洗，这篇帮你省掉一半</h1><p>做数据分析的人都知道一句话：<strong>“垃圾进，垃圾出”</strong>。不管你的图表画得多漂亮、模型多复杂，数据本身有问题，结论就是错的。</p>
<p>数据科学家 80% 的时间花在数据清洗和准备上。不是算法难，是数据脏——缺失值、重复行、格式混乱、异常值、字段名不一致……</p>
<p>Pandas 就是干这个的。它是 Python 数据分析的基石，能帮你把脏数据变成干净、可分析的表格。</p>
<h3 id="Pandas-的两个核心结构：Series-和-DataFrame"><a href="#Pandas-的两个核心结构：Series-和-DataFrame" class="headerlink" title="Pandas 的两个核心结构：Series 和 DataFrame"></a>Pandas 的两个核心结构：Series 和 DataFrame</h3><div class="table-container">
<table>
<thead>
<tr>
<th style="text-align:left">结构</th>
<th style="text-align:left">维度</th>
<th style="text-align:left">类似</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>Series</strong></td>
<td style="text-align:left">1维，一列数据</td>
<td style="text-align:left">Excel 里的一列</td>
</tr>
<tr>
<td style="text-align:left"><strong>DataFrame</strong></td>
<td style="text-align:left">2维，表格</td>
<td style="text-align:left">Excel 里的整张表</td>
</tr>
</tbody>
</table>
</div>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># DataFrame 就像一张表</span></span><br><span class="line">df = pd.DataFrame(&#123;</span><br><span class="line">    <span class="string">&#x27;姓名&#x27;</span>: [<span class="string">&#x27;张三&#x27;</span>, <span class="string">&#x27;李四&#x27;</span>, <span class="string">&#x27;王五&#x27;</span>],</span><br><span class="line">    <span class="string">&#x27;年龄&#x27;</span>: [<span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>],</span><br><span class="line">    <span class="string">&#x27;城市&#x27;</span>: [<span class="string">&#x27;北京&#x27;</span>, <span class="string">&#x27;上海&#x27;</span>, <span class="string">&#x27;广州&#x27;</span>]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>日常 99% 的工作都在跟 DataFrame 打交道——加载、清洗、筛选、聚合、输出。</p>
<h3 id="拿到数据第一件事：先”体检”"><a href="#拿到数据第一件事：先”体检”" class="headerlink" title="拿到数据第一件事：先”体检”"></a>拿到数据第一件事：先”体检”</h3><p>假设你拿到一份销售订单数据 <code>sales.csv</code>，不要急着分析，先体检。</p>
<h4 id="看一眼数据长什么样："><a href="#看一眼数据长什么样：" class="headerlink" title="看一眼数据长什么样："></a>看一眼数据长什么样：</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">df = pd.read_csv(<span class="string">&#x27;sales.csv&#x27;</span>)</span><br><span class="line"></span><br><span class="line">df.head()      <span class="comment"># 前5行，看列名和样例数据</span></span><br><span class="line">df.tail()      <span class="comment"># 后5行，看数据末尾有没有异常</span></span><br><span class="line">df.sample(<span class="number">5</span>)   <span class="comment"># 随机5行，避免排序带来的偏见</span></span><br></pre></td></tr></table></figure>
<p>看整体概况：多少行、多少列、每列是什么类型、有没有空值</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df.info()</span><br></pre></td></tr></table></figure>
<p>输出里重点关注：</p>
<ul>
<li><strong>Non-Null Count</strong>：小于总行数说明有缺失值</li>
<li><strong>Dtype</strong>：object 表示字符串，需要确认是否需要转成日期或数字</li>
</ul>
<h4 id="看数值列的统计摘要："><a href="#看数值列的统计摘要：" class="headerlink" title="看数值列的统计摘要："></a>看数值列的统计摘要：</h4>]]>
    </summary>
    <title>Pandas数据炼金术</title>
    <updated>2026-07-03T07:15:17.189Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="Python" scheme="https://zhhll.com.cn/categories/Python/"/>
    <category term="Python" scheme="https://zhhll.com.cn/tags/Python/"/>
    <content>
      <![CDATA[<h1 id="SQLAlchemy-2-0：Python-最强大的-ORM，这次终于不”重”了"><a href="#SQLAlchemy-2-0：Python-最强大的-ORM，这次终于不”重”了" class="headerlink" title="SQLAlchemy 2.0：Python 最强大的 ORM，这次终于不”重”了"></a>SQLAlchemy 2.0：Python 最强大的 ORM，这次终于不”重”了</h1><p>写过 Python Web 的人应该都听过 SQLAlchemy——Python 生态里最成熟、功能最全的 ORM。但很多人被它的学习曲线劝退了，觉得”太重”。</p><p>2.0 版本不太一样了：查询语法统一了、异步原生支持了、类型提示完整了。它不再是那个”功能强大但难上手”的框架，而是一个现代 Python 的标配工具。</p><h3 id="SQLAlchemy-是什么？解决什么问题？"><a href="#SQLAlchemy-是什么？解决什么问题？" class="headerlink" title="SQLAlchemy 是什么？解决什么问题？"></a>SQLAlchemy 是什么？解决什么问题？</h3><p>写 Python 操作数据库，最原始的方式是拼 SQL 字符串：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cursor.execute(<span class="string">f&quot;SELECT * FROM users WHERE age &gt; <span class="subst">&#123;age&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p>拼字符串容易出事——SQL 注入、类型错误、不同数据库语法不一样。</p><p>ORM（对象关系映射）把”数据库表”映射成”Python 类”，你操作对象就是在操作数据库。SQLAlchemy 是 Python 里最成熟的 ORM。</p><p><strong>SQLAlchemy 2.0 的三个核心变化：</strong></p><ol><li><strong>查询统一了</strong>：1.x 里 Core 和 ORM 两套 API，2.0 统一用 <code>select()</code></li><li><strong>异步原生支持</strong>：<code>async/await</code> 操作数据库，配合 FastAPI 很顺畅</li><li><strong>类型提示完善</strong>：<code>Mapped</code> + <code>mapped_column</code>，IDE 能帮你自动补全</li></ol><hr><h3 id="准备工作：安装和连接"><a href="#准备工作：安装和连接" class="headerlink" title="准备工作：安装和连接"></a>准备工作：安装和连接</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pip install sqlalchemy</span><br><span class="line">pip install asyncpg  <span class="comment"># PostgreSQL 异步驱动</span></span><br><span class="line">pip install aiosqlite <span class="comment"># SQLite 异步驱动（开发用）</span></span><br></pre></td></tr></table></figure><p><strong>同步连接（简单场景）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> create_engine</span><br><span class="line"></span><br><span class="line">engine = create_engine(<span class="string">&quot;sqlite:///./app.db&quot;</span>, echo=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p><strong>异步连接（生产推荐）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy.ext.asyncio <span class="keyword">import</span> create_async_engine</span><br><span class="line"></span><br><span class="line">engine = create_async_engine(</span><br><span class="line">    <span class="string">&quot;postgresql+asyncpg://user:password@localhost:5432/mydb&quot;</span>,</span><br><span class="line">    echo=<span class="literal">False</span>,</span><br><span class="line">    pool_size=<span class="number">10</span>,</span><br><span class="line">    max_overflow=<span class="number">20</span>,</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><code>echo=True</code> 会在控制台打印 SQL 语句，开发阶段方便调试，生产环境关掉。</p><h3 id="定义模型：一张表就是一个类"><a href="#定义模型：一张表就是一个类" class="headerlink" title="定义模型：一张表就是一个类"></a>定义模型：一张表就是一个类</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> DeclarativeBase, Mapped, mapped_column</span><br><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> String, Boolean, func</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span>(<span class="title class_ inherited__">DeclarativeBase</span>):</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    __tablename__ = <span class="string">&quot;users&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">id</span>: Mapped[<span class="built_in">int</span>] = mapped_column(primary_key=<span class="literal">True</span>, autoincrement=<span class="literal">True</span>)</span><br><span class="line">    username: Mapped[<span class="built_in">str</span>] = mapped_column(String(<span class="number">50</span>), unique=<span class="literal">True</span>, nullable=<span class="literal">False</span>)</span><br><span class="line">    email: Mapped[<span class="built_in">str</span> | <span class="literal">None</span>] = mapped_column(String(<span class="number">100</span>), nullable=<span class="literal">True</span>)</span><br><span class="line">    is_active: Mapped[<span class="built_in">bool</span>] = mapped_column(Boolean, default=<span class="literal">True</span>)</span><br><span class="line">    created_at: Mapped[datetime] = mapped_column(server_default=func.now())</span><br></pre></td></tr></table></figure><!--  more --><p><strong>关键点：</strong></p><ul><li><code>__tablename__</code> 指定数据库表名</li><li><code>Mapped[T]</code> 声明字段类型，IDE 能识别</li><li><code>mapped_column()</code> 定义列属性（类型、长度、唯一性、默认值）</li><li><code>server_default=func.now()</code> 表示默认值由数据库生成，不是 Python 生成</li></ul><p><strong>创建表：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">init_db</span>():</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> engine.begin() <span class="keyword">as</span> conn:</span><br><span class="line">        <span class="keyword">await</span> conn.run_sync(Base.metadata.create_all)</span><br></pre></td></tr></table></figure><h3 id="CRUD：增删改查"><a href="#CRUD：增删改查" class="headerlink" title="CRUD：增删改查"></a>CRUD：增删改查</h3><p><strong>准备工作：创建会话工厂</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy.ext.asyncio <span class="keyword">import</span> async_sessionmaker</span><br><span class="line"></span><br><span class="line">AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_db</span>():</span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">with</span> AsyncSessionLocal() <span class="keyword">as</span> session:</span><br><span class="line">        <span class="keyword">yield</span> session</span><br></pre></td></tr></table></figure><p><strong>创建（Create）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">db, username: <span class="built_in">str</span>, email: <span class="built_in">str</span> = <span class="literal">None</span></span>):</span><br><span class="line">    user = User(username=username, email=email)</span><br><span class="line">    db.add(user)</span><br><span class="line">    <span class="keyword">await</span> db.commit()</span><br><span class="line">    <span class="keyword">await</span> db.refresh(user)  <span class="comment"># 刷新获取自增 id</span></span><br><span class="line">    <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure><p><code>refresh</code> 让对象从数据库重新加载一遍，拿到 <code>id</code> 这种自动生成的值。</p><p><strong>批量创建：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">create_users</span>(<span class="params">db, users_data: <span class="built_in">list</span></span>):</span><br><span class="line">    users = [User(**data) <span class="keyword">for</span> data <span class="keyword">in</span> users_data]</span><br><span class="line">    db.add_all(users)</span><br><span class="line">    <span class="keyword">await</span> db.commit()</span><br><span class="line">    <span class="keyword">return</span> users</span><br></pre></td></tr></table></figure><p><strong>查询（Read）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> select</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_user_by_id</span>(<span class="params">db, user_id: <span class="built_in">int</span></span>):</span><br><span class="line">    stmt = select(User).where(User.<span class="built_in">id</span> == user_id)</span><br><span class="line">    result = <span class="keyword">await</span> db.execute(stmt)</span><br><span class="line">    <span class="keyword">return</span> result.scalar_one_or_none()</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_active_users</span>(<span class="params">db, limit: <span class="built_in">int</span> = <span class="number">100</span></span>):</span><br><span class="line">    stmt = select(User).where(User.is_active == <span class="literal">True</span>).limit(limit)</span><br><span class="line">    result = <span class="keyword">await</span> db.execute(stmt)</span><br><span class="line">    <span class="keyword">return</span> result.scalars().<span class="built_in">all</span>()</span><br></pre></td></tr></table></figure><p><strong>更新（Update）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">update_user_email</span>(<span class="params">db, user_id: <span class="built_in">int</span>, new_email: <span class="built_in">str</span></span>):</span><br><span class="line">    user = <span class="keyword">await</span> get_user_by_id(db, user_id)</span><br><span class="line">    <span class="keyword">if</span> user:</span><br><span class="line">        user.email = new_email</span><br><span class="line">        <span class="keyword">await</span> db.commit()</span><br><span class="line">        <span class="keyword">await</span> db.refresh(user)</span><br><span class="line">    <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure><p><strong>删除（Delete）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">delete_user</span>(<span class="params">db, user_id: <span class="built_in">int</span></span>) -&gt; <span class="built_in">bool</span>:</span><br><span class="line">    user = <span class="keyword">await</span> get_user_by_id(db, user_id)</span><br><span class="line">    <span class="keyword">if</span> user:</span><br><span class="line">        <span class="keyword">await</span> db.delete(user)</span><br><span class="line">        <span class="keyword">await</span> db.commit()</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span></span><br></pre></td></tr></table></figure><h3 id="关系映射：表之间怎么关联"><a href="#关系映射：表之间怎么关联" class="headerlink" title="关系映射：表之间怎么关联"></a>关系映射：表之间怎么关联</h3><h4 id="一对多：一个用户有多篇文章"><a href="#一对多：一个用户有多篇文章" class="headerlink" title="一对多：一个用户有多篇文章"></a><strong>一对多：一个用户有多篇文章</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">User</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    __tablename__ = <span class="string">&quot;users&quot;</span></span><br><span class="line">    <span class="comment"># ...</span></span><br><span class="line">    posts: Mapped[<span class="built_in">list</span>[<span class="string">&quot;Post&quot;</span>]] = relationship(</span><br><span class="line">        back_populates=<span class="string">&quot;author&quot;</span>,</span><br><span class="line">        lazy=<span class="string">&quot;selectin&quot;</span>   <span class="comment"># 预加载，避免 N+1</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Post</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    __tablename__ = <span class="string">&quot;posts&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">id</span>: Mapped[<span class="built_in">int</span>] = mapped_column(primary_key=<span class="literal">True</span>)</span><br><span class="line">    title: Mapped[<span class="built_in">str</span>] = mapped_column(String(<span class="number">200</span>))</span><br><span class="line">    user_id: Mapped[<span class="built_in">int</span>] = mapped_column(ForeignKey(<span class="string">&quot;users.id&quot;</span>))</span><br><span class="line">    </span><br><span class="line">    author: Mapped[<span class="string">&quot;User&quot;</span>] = relationship(back_populates=<span class="string">&quot;posts&quot;</span>)</span><br></pre></td></tr></table></figure><p><code>lazy=&quot;selectin&quot;</code> 的意思是：查询用户的时候，顺便把关联的文章也查出来。不加的话，访问 <code>user.posts</code> 时会再触发一次查询（这就是 N+1 问题）。</p><h4 id="多对多：用户和角色"><a href="#多对多：用户和角色" class="headerlink" title="多对多：用户和角色"></a><strong>多对多：用户和角色</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> Table, Column, Integer, ForeignKey</span><br><span class="line"></span><br><span class="line">user_role_table = Table(</span><br><span class="line">    <span class="string">&quot;user_roles&quot;</span>,</span><br><span class="line">    Base.metadata,</span><br><span class="line">    Column(<span class="string">&quot;user_id&quot;</span>, Integer, ForeignKey(<span class="string">&quot;users.id&quot;</span>)),</span><br><span class="line">    Column(<span class="string">&quot;role_id&quot;</span>, Integer, ForeignKey(<span class="string">&quot;roles.id&quot;</span>)),</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Role</span>(<span class="title class_ inherited__">Base</span>):</span><br><span class="line">    __tablename__ = <span class="string">&quot;roles&quot;</span></span><br><span class="line">    <span class="built_in">id</span>: Mapped[<span class="built_in">int</span>] = mapped_column(primary_key=<span class="literal">True</span>)</span><br><span class="line">    name: Mapped[<span class="built_in">str</span>] = mapped_column(String(<span class="number">50</span>), unique=<span class="literal">True</span>)</span><br><span class="line">    users: Mapped[<span class="built_in">list</span>[<span class="string">&quot;User&quot;</span>]] = relationship(</span><br><span class="line">        secondary=user_role_table,</span><br><span class="line">        back_populates=<span class="string">&quot;roles&quot;</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line"><span class="comment"># User 里加：</span></span><br><span class="line">roles: Mapped[<span class="built_in">list</span>[<span class="string">&quot;Role&quot;</span>]] = relationship(</span><br><span class="line">    secondary=user_role_table,</span><br><span class="line">    back_populates=<span class="string">&quot;users&quot;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><code>secondary</code> 指定中间表，多对多就是通过中间表关联两个表。</p><h4 id="预加载关联数据："><a href="#预加载关联数据：" class="headerlink" title="预加载关联数据："></a><strong>预加载关联数据：</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> selectinload, joinedload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查用户的时候把文章也带出来</span></span><br><span class="line">stmt = select(User).options(selectinload(User.posts))</span><br><span class="line">users = <span class="keyword">await</span> db.execute(stmt)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查文章的时候把作者也带出来</span></span><br><span class="line">stmt = select(Post).options(joinedload(Post.author))</span><br><span class="line">posts = <span class="keyword">await</span> db.execute(stmt)</span><br></pre></td></tr></table></figure><p><code>selectinload</code> 是额外查一次（两条 SQL），<code>joinedload</code> 是 JOIN 一次性查（一条 SQL）。一对多场景用 <code>selectinload</code> 更安全，不会出现数据膨胀。</p><h3 id="过滤、排序、分页"><a href="#过滤、排序、分页" class="headerlink" title="过滤、排序、分页"></a>过滤、排序、分页</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> select, and_, or_, desc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 条件过滤</span></span><br><span class="line">stmt = select(User).where(User.age &gt; <span class="number">18</span>)</span><br><span class="line">stmt = select(User).where(and_(User.age &gt; <span class="number">18</span>, User.is_active == <span class="literal">True</span>))</span><br><span class="line">stmt = select(User).where(or_(User.role == <span class="string">&quot;admin&quot;</span>, User.role == <span class="string">&quot;superuser&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># IN 查询</span></span><br><span class="line">stmt = select(User).where(User.<span class="built_in">id</span>.in_([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模糊匹配</span></span><br><span class="line">stmt = select(User).where(User.username.like(<span class="string">&quot;%admin%&quot;</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 排序</span></span><br><span class="line">stmt = select(User).order_by(desc(User.created_at))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分页</span></span><br><span class="line">page = <span class="number">2</span></span><br><span class="line">per_page = <span class="number">20</span></span><br><span class="line">stmt = select(User).offset((page-<span class="number">1</span>)*per_page).limit(per_page)</span><br></pre></td></tr></table></figure><p><strong>聚合查询：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> func</span><br><span class="line"></span><br><span class="line"><span class="comment"># 计数</span></span><br><span class="line">total = <span class="keyword">await</span> db.execute(select(func.count()).select_from(User))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按状态分组统计</span></span><br><span class="line">stmt = select(User.is_active, func.count(User.<span class="built_in">id</span>)).group_by(User.is_active)</span><br><span class="line">results = <span class="keyword">await</span> db.execute(stmt)</span><br></pre></td></tr></table></figure><h3 id="异步：2-0-最重要的特性"><a href="#异步：2-0-最重要的特性" class="headerlink" title="异步：2.0 最重要的特性"></a>异步：2.0 最重要的特性</h3><p>SQLAlchemy 2.0 的异步是原生的，不是靠线程池模拟的。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 所有数据库操作都加 await</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_user_stats</span>(<span class="params">db</span>):</span><br><span class="line">    stmt = select(User.is_active, func.count(User.<span class="built_in">id</span>)).group_by(User.is_active)</span><br><span class="line">    result = <span class="keyword">await</span> db.execute(stmt)</span><br><span class="line">    <span class="keyword">return</span> result.<span class="built_in">all</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 批量操作</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">batch_update</span>(<span class="params">db, user_ids: <span class="built_in">list</span>, is_active: <span class="built_in">bool</span></span>):</span><br><span class="line">    stmt = select(User).where(User.<span class="built_in">id</span>.in_(user_ids))</span><br><span class="line">    result = <span class="keyword">await</span> db.execute(stmt)</span><br><span class="line">    users = result.scalars().<span class="built_in">all</span>()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> user <span class="keyword">in</span> users:</span><br><span class="line">        user.is_active = is_active</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">await</span> db.commit()</span><br></pre></td></tr></table></figure><p><strong>FastAPI 集成：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI, Depends</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.ext.asyncio <span class="keyword">import</span> AsyncSession</span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.get(<span class="params"><span class="string">&quot;/users/&#123;user_id&#125;&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">get_user</span>(<span class="params">user_id: <span class="built_in">int</span>, db: AsyncSession = Depends(<span class="params">get_db</span>)</span>):</span><br><span class="line">    user = <span class="keyword">await</span> get_user_by_id(db, user_id)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> user:</span><br><span class="line">        <span class="keyword">return</span> &#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;not found&quot;</span>&#125;</span><br><span class="line">    <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.post(<span class="params"><span class="string">&quot;/users&quot;</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">username: <span class="built_in">str</span>, db: AsyncSession = Depends(<span class="params">get_db</span>)</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> create_user(db, username)</span><br></pre></td></tr></table></figure><h3 id="生产环境配置"><a href="#生产环境配置" class="headerlink" title="生产环境配置"></a>生产环境配置</h3><p><strong>连接池配置：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">engine = create_async_engine(</span><br><span class="line">    <span class="string">&quot;postgresql+asyncpg://user:pass@localhost/db&quot;</span>,</span><br><span class="line">    pool_size=<span class="number">10</span>,        <span class="comment"># 连接池大小</span></span><br><span class="line">    max_overflow=<span class="number">20</span>,     <span class="comment"># 超出 pool_size 最多额外创建多少</span></span><br><span class="line">    pool_timeout=<span class="number">30</span>,     <span class="comment"># 获取连接超时（秒）</span></span><br><span class="line">    pool_recycle=<span class="number">3600</span>,   <span class="comment"># 连接回收时间（秒）</span></span><br><span class="line">    pool_pre_ping=<span class="literal">True</span>,  <span class="comment"># 使用前检查连接是否还活着</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><strong>事务管理：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">transfer_money</span>(<span class="params">db, from_id, to_id, amount</span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 加悲观锁，防止并发问题</span></span><br><span class="line">        from_user = <span class="keyword">await</span> db.execute(</span><br><span class="line">            select(User).where(User.<span class="built_in">id</span> == from_id).with_for_update()</span><br><span class="line">        )</span><br><span class="line">        <span class="comment"># ...</span></span><br><span class="line">        <span class="keyword">await</span> db.commit()</span><br><span class="line">    <span class="keyword">except</span> Exception:</span><br><span class="line">        <span class="keyword">await</span> db.rollback()</span><br><span class="line">        <span class="keyword">raise</span></span><br></pre></td></tr></table></figure><p><strong>数据库迁移（Alembic）：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pip install alembic</span><br><span class="line">alembic init alembic</span><br><span class="line">alembic revision --autogenerate -m <span class="string">&quot;add user table&quot;</span></span><br><span class="line">alembic upgrade <span class="built_in">head</span></span><br></pre></td></tr></table></figure><h3 id="性能优化：N-1-和批量操作"><a href="#性能优化：N-1-和批量操作" class="headerlink" title="性能优化：N+1 和批量操作"></a>性能优化：N+1 和批量操作</h3><h4 id="N-1-查询：最常见的问题"><a href="#N-1-查询：最常见的问题" class="headerlink" title="N+1 查询：最常见的问题"></a><strong>N+1 查询：最常见的问题</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 问题代码：先查用户列表（1次查询），每个用户访问 posts 时再查一次（N次）</span></span><br><span class="line">users = <span class="keyword">await</span> db.execute(select(User))</span><br><span class="line"><span class="keyword">for</span> user <span class="keyword">in</span> users.scalars():</span><br><span class="line">    <span class="built_in">print</span>(<span class="built_in">len</span>(user.posts))   <span class="comment"># 触发 N 次额外查询</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 解决方案：用 selectinload 预加载</span></span><br><span class="line">stmt = select(User).options(selectinload(User.posts))</span><br><span class="line">users = <span class="keyword">await</span> db.execute(stmt)</span><br></pre></td></tr></table></figure><h4 id="批量插入：比逐条插入快几十倍"><a href="#批量插入：比逐条插入快几十倍" class="headerlink" title="批量插入：比逐条插入快几十倍"></a><strong>批量插入：比逐条插入快几十倍</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> insert</span><br><span class="line"></span><br><span class="line"><span class="comment">#  逐条插入，慢</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10000</span>):</span><br><span class="line">    db.add(User(username=<span class="string">f&quot;user_<span class="subst">&#123;i&#125;</span>&quot;</span>))</span><br><span class="line"><span class="keyword">await</span> db.commit()</span><br><span class="line"></span><br><span class="line"><span class="comment">#  批量插入，快</span></span><br><span class="line">stmt = insert(User).values([&#123;<span class="string">&quot;username&quot;</span>: <span class="string">f&quot;user_<span class="subst">&#123;i&#125;</span>&quot;</span>&#125; <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10000</span>)])</span><br><span class="line"><span class="keyword">await</span> db.execute(stmt)</span><br><span class="line"><span class="keyword">await</span> db.commit()</span><br></pre></td></tr></table></figure><h4 id="只查需要的字段，别-SELECT-："><a href="#只查需要的字段，别-SELECT-：" class="headerlink" title="只查需要的字段，别 SELECT *："></a><strong>只查需要的字段，别 <code>SELECT *</code>：</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 只需要 id 和 username，别查所有字段</span></span><br><span class="line">stmt = select(User.<span class="built_in">id</span>, User.username).where(User.is_active == <span class="literal">True</span>)</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/22.SQLAlchemy/</id>
    <link href="https://zhhll.com.cn/2026/Python/22.SQLAlchemy/"/>
    <published>2026-05-01T14:40:23.000Z</published>
    <summary>
      <![CDATA[<h1 id="SQLAlchemy-2-0：Python-最强大的-ORM，这次终于不”重”了"><a href="#SQLAlchemy-2-0：Python-最强大的-ORM，这次终于不”重”了" class="headerlink" title="SQLAlchemy 2]]>
    </summary>
    <title>SQLAlchemy</title>
    <updated>2026-07-03T06:50:38.875Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="从PyTorch的积木到AI的引擎：一文读懂Transformer入门"><a href="#从PyTorch的积木到AI的引擎：一文读懂Transformer入门" class="headerlink" title="从PyTorch的积木到AI的引擎：一文读懂Transformer入门"></a>从PyTorch的积木到AI的引擎：一文读懂Transformer入门</h2><p>掌握了PyTorch的核心——张量、自动微分、损失函数和优化器。已经能用<code>torch.nn.Linear</code>搭建网络，理解<code>backward()</code>如何计算梯度。是时候从“玩具模型”迈向真正驱动现代人工智能的引擎了。这个引擎，就是Transformer。</p><p>Transformer不是一种新的编程语言，也不是一个独立的软件。它是一个<strong>模型架构</strong>，一套精妙的神经网络设计蓝图。它由Google Brain团队在2017年的里程碑论文《Attention Is All You Need》中提出，并彻底颠覆了人工智能的格局。</p><p>要理解Transformer，我们必须先回到它出现之前的时代，看看它究竟解决了什么痛点。</p><p>在Transformer之前，处理序列数据（如一句话、一段音频）的主流是循环神经网络（RNN）及其变体LSTM。RNN的工作方式像一个勤奋的朗读者，它按顺序一个字一个字地处理句子。当它读到第N个词时，它只能依靠之前N-1个词留下的“记忆”（隐藏状态）来理解上下文。</p><p>这种方式有两个致命缺陷：</p><ul><li><strong>无法并行</strong>：因为必须等前一个词处理完才能处理下一个，所以训练速度非常慢，无法充分利用GPU的强大算力。</li><li><strong>长距离依赖问题</strong>：当句子很长时，RNN很难记住开头的信息。就像你读一本长篇小说，读到结尾时可能已经忘了主角的名字。</li></ul><p>Transformer的出现，一举解决了这两个问题。它完全摒弃了循环结构，转而拥抱一个名为<strong>自注意力（Self-Attention）</strong>的机制。这个机制的核心思想非常直观：让句子中的每个词都去“关注”句子中的所有其他词，从而直接捕捉任意两个词之间的关系，无论它们相距多远。</p><p>这种设计带来了两大革命性优势：</p><ul><li><strong>极致并行</strong>：模型可以一次性看到整个句子，所有词的计算都可以同时进行，训练效率得到指数级提升。</li><li><strong>强大的长程建模能力</strong>：通过自注意力，句子开头的词和结尾的词可以直接“对话”，完美解决了长距离依赖问题。</li></ul><p>凭借其无与伦比的效率和性能，Transformer迅速从自然语言处理（NLP）领域扩张到计算机视觉（CV）、语音处理乃至多模态领域，成为当今所有大语言模型（如GPT、BERT）的绝对基石。</p><p>理解了Transformer的“为什么”，我们再来拆解它的“怎么做”。一个标准的Transformer模型，其内部结构精巧而优雅，主要由以下几个核心组件构成。</p><!--  more --><p>在将文本输入模型之前，首先要将每个词（或子词）转换成一个计算机可以理解的数字向量，这个过程叫<strong>词嵌入（Word Embedding）</strong>。例如，“国王”和“王后”的向量在空间中的距离会很近，而“苹果”的向量则会离它们很远。</p><p>但Transformer是并行处理所有词的，它本身并不理解词的顺序。为了让模型知道“我打他”和“他打我”的区别，我们必须为每个词的嵌入向量加上一个<strong>位置编码（Positional Encoding）</strong>。这个编码是一个根据词在句子中的位置生成的独特向量，它告诉模型每个词的先后顺序。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">PositionalEncoder</span>(nn.Module):</span><br><span class="line">    <span class="comment"># d_model: 嵌入向量的维度</span></span><br><span class="line">    <span class="comment"># max_seq_len: 最大序列长度</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, d_model, max_seq_len=<span class="number">80</span></span>):</span><br><span class="line">        <span class="built_in">super</span>(PositionalEncoder, <span class="variable language_">self</span>).__init__()</span><br><span class="line">        <span class="variable language_">self</span>.d_model = d_model</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 创建一个 PE 矩阵 根据Transformer论文中的位置编码公式，用正弦和余弦函数生成位置编码。</span></span><br><span class="line">        <span class="comment"># 偶数位置用正弦函数：sin(pos / 10000^(2i/d_model))</span></span><br><span class="line">        <span class="comment"># 奇数位置用余弦函数：cos(pos / 10000^(2i/d_model))</span></span><br><span class="line">        pe = torch.zeros(max_seq_len, d_model)</span><br><span class="line">        <span class="keyword">for</span> pos <span class="keyword">in</span> <span class="built_in">range</span>(max_seq_len):</span><br><span class="line">            <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, d_model, <span class="number">2</span>):</span><br><span class="line">                <span class="comment"># 使用公式计算 sin 和 cos</span></span><br><span class="line">                pe[pos, i] = math.sin(pos / (<span class="number">10000</span> ** (<span class="number">2</span> * i / d_model)))</span><br><span class="line">                pe[pos, i + <span class="number">1</span>] = math.cos(pos / (<span class="number">10000</span> ** (<span class="number">2</span> * (i + <span class="number">1</span>) / d_model)))</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 添加一个批次维度 (1, max_seq_len, d_model) 便于与输入张量相加</span></span><br><span class="line">        pe = pe.unsqueeze(<span class="number">0</span>)</span><br><span class="line">        <span class="comment"># 将位置编码注册为缓冲区，使其不会被优化器更新，但在模型保存和加载时会自动处理</span></span><br><span class="line">        <span class="variable language_">self</span>.register_buffer(<span class="string">&#x27;pe&#x27;</span>, pe)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        <span class="comment"># 缩放嵌入向量</span></span><br><span class="line">        x = x * math.sqrt(<span class="variable language_">self</span>.d_model)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 获取序列长度</span></span><br><span class="line">        seq_len = x.size(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 将位置编码加到嵌入向量上</span></span><br><span class="line">        x = x + <span class="variable language_">self</span>.pe[:, :seq_len].detach()  <span class="comment"># detach() 可以防止位置编码被更新，确保位置编码不会参与梯度计算</span></span><br><span class="line">        <span class="keyword">return</span> x</span><br><span class="line">    </span><br><span class="line"><span class="comment"># 测试 PositionalEncoder</span></span><br><span class="line"><span class="comment"># 假设 d_model 为 512，序列长度为 20</span></span><br><span class="line">d_model = <span class="number">512</span></span><br><span class="line">max_seq_len = <span class="number">80</span></span><br><span class="line">batch_size = <span class="number">32</span></span><br><span class="line">seq_len = <span class="number">20</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建位置编码层</span></span><br><span class="line">pos_encoder = PositionalEncoder(d_model, max_seq_len)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模拟输入张量 (batch_size, seq_len, d_model)</span></span><br><span class="line">x = torch.randn(batch_size, seq_len, d_model)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加位置编码</span></span><br><span class="line">output = pos_encoder(x)</span><br><span class="line"><span class="built_in">print</span>(output.shape)  <span class="comment"># 输出: torch.Size([32, 20, 512])</span></span><br></pre></td></tr></table></figure><p>这是Transformer的“心脏”。它的任务是让模型学会关注句子中最重要的部分。我们可以把它想象成一个信息检索系统，包含三个角色：</p><ul><li><strong>Query（查询）</strong>：代表“我当前想找什么信息”。</li><li><strong>Key（键）</strong>：代表“我身上有什么信息”。</li><li><strong>Value（值）</strong>：代表“我实际包含的信息内容”。</li></ul><p>模型通过计算Query和所有Key的相似度，来决定从对应的Value中提取多少信息。这个过程会生成一个全新的、融合了上下文信息的表示向量。</p><p>为了从不同角度理解句子，Transformer会使用<strong>多头注意力（Multi-Head Attention）</strong>。它相当于开了多个“观察视角”，有的头可能关注语法结构，有的头关注指代关系，最后将所有头的观察结果综合起来，形成一个更全面、更丰富的理解。</p><p>在注意力层之后，模型还会通过一个<strong>前馈网络（Feed-Forward Network）</strong>。这是一个简单的全连接网络，独立地对每个位置的向量进行非线性变换，进一步提取和整合特征。</p><p>一个完整的Transformer模型通常采用<strong>编码器-解码器（Encoder-Decoder）</strong>架构。</p><ul><li><strong>编码器（Encoder）</strong>：负责“阅读理解”。它接收输入句子，通过多层堆叠的自注意力和前馈网络，将其转换成一个富含上下文信息的特征表示。</li><li><strong>解码器（Decoder）</strong>：负责“写作生成”。它接收编码器的输出，并以自回归的方式（即一个词接一个词地）生成目标句子。在生成每个词时，它会通过<strong>交叉注意力（Cross-Attention）</strong>机制，回头去“查阅”编码器提供的原文信息，确保翻译或生成的准确性。</li></ul><p>从理论到实践，我们来看看如何用PyTorch这个强大的工具箱，将Transformer的设计蓝图变为现实。这就像用乐高积木，按照图纸一步步搭建出宏伟的城堡。</p><p>首先，我们需要定义模型的核心——自注意力模块。这个过程完全由PyTorch的张量运算构成。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ScaledDotProductAttention</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, d_k</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.d_k = d_k <span class="comment"># 键向量的维度</span></span><br><span class="line">        <span class="comment"># 将一组原始分数（logits）转换成概率分布。把任意数值变成 0 到 1 之间的概率值，并且保证这些概率值加起来等于 1</span></span><br><span class="line">        <span class="variable language_">self</span>.softmax = nn.Softmax(dim=-<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, Q, K, V</span>):</span><br><span class="line">        <span class="comment"># 1. 计算Q和K的相似度 (矩阵乘法)</span></span><br><span class="line">        scores = torch.matmul(Q, K.transpose(-<span class="number">1</span>, -<span class="number">2</span>))</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 2. 缩放，防止点积过大导致Softmax梯度消失</span></span><br><span class="line">        scores = scores / math.sqrt(<span class="variable language_">self</span>.d_k)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 3. 应用Softmax，得到注意力权重</span></span><br><span class="line">        attention_weights = <span class="variable language_">self</span>.softmax(scores)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 4. 用权重对V加权求和，得到最终输出</span></span><br><span class="line">        output = torch.matmul(attention_weights, V)</span><br><span class="line">        <span class="keyword">return</span> output</span><br></pre></td></tr></table></figure><p>这段代码精确地实现了自注意力的数学公式。<code>Q</code>, <code>K</code>, <code>V</code>都是PyTorch张量，<code>torch.matmul</code>是矩阵乘法，<code>nn.Softmax</code>是激活函数。你看，复杂的注意力机制，底层依然是你最熟悉的PyTorch操作。</p><p>接下来，我们将多个注意力头组合起来，并加上残差连接和层归一化，构建一个完整的编码器层。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MultiHeadAttention</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, d_model, num_heads</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.d_model = d_model <span class="comment"># 词向量的总维度，比如512</span></span><br><span class="line">        <span class="variable language_">self</span>.num_heads = num_heads <span class="comment"># “头”的数量，比如8个</span></span><br><span class="line">        <span class="variable language_">self</span>.d_k = d_model // num_heads <span class="comment"># 每个“头”的维度，512/8=64</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 这三个线性层负责把输入 x 转换成 Q(查询), K(键), V(值)</span></span><br><span class="line">        <span class="variable language_">self</span>.W_q = nn.Linear(d_model, d_model)</span><br><span class="line">        <span class="variable language_">self</span>.W_k = nn.Linear(d_model, d_model)</span><br><span class="line">        <span class="variable language_">self</span>.W_v = nn.Linear(d_model, d_model)</span><br><span class="line">        <span class="comment"># 这个线性层负责把所有“头”的结果融合起来</span></span><br><span class="line">        <span class="variable language_">self</span>.W_o = nn.Linear(d_model, d_model) </span><br><span class="line">        <span class="comment"># 计算注意力的“工具”</span></span><br><span class="line">        <span class="variable language_">self</span>.attention = ScaledDotProductAttention(<span class="variable language_">self</span>.d_k)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        batch_size, seq_len, _ = x.shape</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 1. 线性投影并分割成多个头</span></span><br><span class="line">        <span class="comment"># 用线性层把输入 x 转换成查询向量 Q。此时 Q 的形状是 (batch_size, seq_len, d_model)</span></span><br><span class="line">        <span class="comment"># view().transpose(1, 2): 它把 Q 的最后一个维度（比如512）切分成 num_heads 个（比如8个）小向量，每个小向量的维度是 d_k（比如64）。然后通过转置，把“头”的维度放到前面。</span></span><br><span class="line">        <span class="comment"># 意义是：原来我们只有一个大的查询向量，现在我们把它分成了8个“小专家”（头）。每个“小专家”都从自己的视角去观察整个句子</span></span><br><span class="line">        Q = <span class="variable language_">self</span>.W_q(x).view(batch_size, seq_len, <span class="variable language_">self</span>.num_heads, <span class="variable language_">self</span>.d_k).transpose(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">        K = <span class="variable language_">self</span>.W_k(x).view(batch_size, seq_len, <span class="variable language_">self</span>.num_heads, <span class="variable language_">self</span>.d_k).transpose(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">        V = <span class="variable language_">self</span>.W_v(x).view(batch_size, seq_len, <span class="variable language_">self</span>.num_heads, <span class="variable language_">self</span>.d_k).transpose(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 2. 并行计算多头注意力</span></span><br><span class="line">        head_outputs = []</span><br><span class="line">        <span class="comment"># 让每一个“小专家”（头）独立工作</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="variable language_">self</span>.num_heads):</span><br><span class="line">            head_output = <span class="variable language_">self</span>.attention(Q[:, i], K[:, i], V[:, i])</span><br><span class="line">            <span class="comment"># 每个头都会输出一个结果，我们把所有头的结果都收集到 head_outputs 列表里</span></span><br><span class="line">            head_outputs.append(head_output)</span><br><span class="line">            </span><br><span class="line">        <span class="comment"># 3. 把他们的成果拼接起来。torch.cat(..., dim=-1) 会在最后一个维度上进行拼接，把8个维度为 d_k (64) 的向量，重新拼成一个维度为 d_model (8 * 64 = 512) 的大向量。</span></span><br><span class="line">        concatenated = torch.cat(head_outputs, dim=-<span class="number">1</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 4. 最终线性变换。把所有“小专家”的观点综合起来，形成一个最终的、更全面的理解</span></span><br><span class="line">        output = <span class="variable language_">self</span>.W_o(concatenated)</span><br><span class="line">        <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"><span class="comment"># 一个完整的编码器层还包括前馈网络和残差连接</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EncoderLayer</span>(nn.Module):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, d_model, ffn_hidden, num_heads</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        <span class="variable language_">self</span>.attention = MultiHeadAttention(d_model, num_heads)</span><br><span class="line">        <span class="comment"># 前馈网络，一个简单的两层全连接网络</span></span><br><span class="line">        <span class="variable language_">self</span>.ffn = nn.Sequential(</span><br><span class="line">            nn.Linear(d_model, ffn_hidden), <span class="comment"># 先放大维度</span></span><br><span class="line">            nn.ReLU(), <span class="comment"># 加一个非线性激活函数</span></span><br><span class="line">            nn.Linear(ffn_hidden, d_model) <span class="comment"># 再变回原来的维度</span></span><br><span class="line">        )</span><br><span class="line">        <span class="comment"># 两个层归一化，用来稳定训练</span></span><br><span class="line">        <span class="variable language_">self</span>.layer_norm1 = nn.LayerNorm(d_model)</span><br><span class="line">        <span class="variable language_">self</span>.layer_norm2 = nn.LayerNorm(d_model)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self, x</span>):</span><br><span class="line">        <span class="comment"># 残差连接 + 层归一化</span></span><br><span class="line">        x = <span class="variable language_">self</span>.layer_norm1(x + <span class="variable language_">self</span>.attention(x))</span><br><span class="line">        x = <span class="variable language_">self</span>.layer_norm2(x + <span class="variable language_">self</span>.ffn(x))</span><br><span class="line">        <span class="keyword">return</span> x</span><br></pre></td></tr></table></figure><p>通过组合这些模块，我们就可以搭建出完整的Transformer模型。这个过程让你深刻理解到，看似神秘的“大模型”，其内部是由一个个清晰、可解释的PyTorch模块精密协作而成的。</p><p>当然，在现实中，我们很少从零开始写代码。Hugging Face的<code>transformers</code>库提供了成千上万预训练好的Transformer模型，让我们能像搭积木一样快速应用。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="comment"># 使用国内镜像</span></span><br><span class="line">os.environ[<span class="string">&#x27;HF_ENDPOINT&#x27;</span>] = <span class="string">&#x27;https://hf-mirror.com&#x27;</span></span><br><span class="line"><span class="keyword">from</span> transformers <span class="keyword">import</span> pipeline</span><br><span class="line"></span><br><span class="line"><span class="comment"># 一行代码加载一个情感分析模型</span></span><br><span class="line">classifier = pipeline(<span class="string">&quot;sentiment-analysis&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 直接使用</span></span><br><span class="line">result = classifier(<span class="string">&quot;I love learning about Transformer with PyTorch!&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(result)</span><br><span class="line"><span class="comment"># 输出: [&#123;&#x27;label&#x27;: &#x27;POSITIVE&#x27;, &#x27;score&#x27;: 0.9998&#125;]</span></span><br></pre></td></tr></table></figure><p>这行简洁代码的背后，是Hugging Face库用PyTorch实现的、结构极其复杂的BERT或GPT模型。它已经学习了海量文本，我们只需调用它，就能获得强大的AI能力。</p><p>从PyTorch的<code>nn.Linear</code>和<code>backward()</code>，到Transformer的自注意力机制，再到Hugging Face的预训练模型，你已经走完了一条从基础到前沿的完整路径。</p><p>Transformer并非遥不可及的黑箱，它是由你熟悉的PyTorch积木搭建而成的宏伟建筑。理解它的工作原理，不仅让你能更好地使用现有的大模型，更为你未来探索和创新打开了大门。现在，你已经手握开启AI新世界的钥匙。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/5.transformer%E5%85%A5%E9%97%A8/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/5.transformer%E5%85%A5%E9%97%A8/"/>
    <published>2026-05-01T13:12:23.000Z</published>
    <summary>
      <![CDATA[<h2 id="从PyTorch的积木到AI的引擎：一文读懂Transformer入门"><a href="#从PyTorch的积木到AI的引擎：一文读懂Transformer入门" class="headerlink" title="从PyTorch的积木到AI的引擎：一文读懂]]>
    </summary>
    <title>Transformer入门</title>
    <updated>2026-05-23T14:21:21.432Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="Python" scheme="https://zhhll.com.cn/categories/Python/"/>
    <category term="Python" scheme="https://zhhll.com.cn/tags/Python/"/>
    <content>
      <![CDATA[<h2 id="Python-装饰器：别被-符号吓到，它就是个”包装盒”"><a href="#Python-装饰器：别被-符号吓到，它就是个”包装盒”" class="headerlink" title="Python 装饰器：别被 @ 符号吓到，它就是个”包装盒”"></a>Python 装饰器：别被 @ 符号吓到，它就是个”包装盒”</h2><p>第一次看到 <code>@</code> 符号的时候，我也觉得像魔法——在函数上面加一行，行为就变了，完全不知道发生了什么。</p><p>后来明白了：<strong>装饰器就是个”包装盒”</strong>。你把一个函数放进去，它给你包装一下，加些功能，再还给你。函数还是那个函数，只是多了点本事。</p><h3 id="装饰器到底是什么？三个例子看懂"><a href="#装饰器到底是什么？三个例子看懂" class="headerlink" title="装饰器到底是什么？三个例子看懂"></a>装饰器到底是什么？三个例子看懂</h3><h4 id="例子1：函数可以赋值给变量"><a href="#例子1：函数可以赋值给变量" class="headerlink" title="例子1：函数可以赋值给变量"></a><strong>例子1：函数可以赋值给变量</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;Hello, <span class="subst">&#123;name&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">say_hello = greet   <span class="comment"># 把函数赋值给变量</span></span><br><span class="line">say_hello(<span class="string">&quot;Alice&quot;</span>)  <span class="comment"># &quot;Hello, Alice&quot;</span></span><br></pre></td></tr></table></figure><p>函数跟数字、字符串一样，可以到处传递。</p><h4 id="例子2：函数可以作为参数传给另一个函数"><a href="#例子2：函数可以作为参数传给另一个函数" class="headerlink" title="例子2：函数可以作为参数传给另一个函数"></a><strong>例子2：函数可以作为参数传给另一个函数</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">func, value</span>):</span><br><span class="line">    <span class="keyword">return</span> func(value)</span><br><span class="line"></span><br><span class="line">apply(greet, <span class="string">&quot;Bob&quot;</span>)   <span class="comment"># &quot;Hello, Bob&quot;</span></span><br></pre></td></tr></table></figure><h4 id="例子3：函数可以返回另一个函数"><a href="#例子3：函数可以返回另一个函数" class="headerlink" title="例子3：函数可以返回另一个函数"></a><strong>例子3：函数可以返回另一个函数</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">make_multiplier</span>(<span class="params">factor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">multiplier</span>(<span class="params">x</span>):</span><br><span class="line">        <span class="keyword">return</span> x * factor</span><br><span class="line">    <span class="keyword">return</span> multiplier</span><br><span class="line"></span><br><span class="line">double = make_multiplier(<span class="number">2</span>)</span><br><span class="line">double(<span class="number">5</span>)   <span class="comment"># 10</span></span><br></pre></td></tr></table></figure><p>装饰器就是这三个能力的组合：<strong>接收一个函数，在它外面包一层新功能，返回包装后的函数。</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">my_decorator</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;执行前&quot;</span>)</span><br><span class="line">        result = func()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;执行后&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@my_decorator</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">say_hello</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello!&quot;</span>)</span><br><span class="line"></span><br><span class="line">say_hello()</span><br><span class="line"><span class="comment"># 执行前</span></span><br><span class="line"><span class="comment"># Hello!</span></span><br><span class="line"><span class="comment"># 执行后</span></span><br></pre></td></tr></table></figure><span id="more"></span><p><code>@my_decorator</code> 等价于 <code>say_hello = my_decorator(say_hello)</code>。就是把 <code>say_hello</code> 放进 <code>my_decorator</code> 里包装一下，再赋值回来。</p><h3 id="基础装饰器：给函数加”前后置”逻辑"><a href="#基础装饰器：给函数加”前后置”逻辑" class="headerlink" title="基础装饰器：给函数加”前后置”逻辑"></a>基础装饰器：给函数加”前后置”逻辑</h3><p>最常用的场景：在函数执行前后自动做点事——记日志、计时、权限检查。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timer</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)   </span><span class="comment"># 后面会说为什么一定要加这行</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.perf_counter()</span><br><span class="line">        result = func(*args, **kwargs)</span><br><span class="line">        elapsed = time.perf_counter() - start</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 耗时: <span class="subst">&#123;elapsed:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@timer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">slow_task</span>():</span><br><span class="line">    time.sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;完成&quot;</span></span><br><span class="line"></span><br><span class="line">slow_task()   <span class="comment"># slow_task 耗时: 1.0002s</span></span><br></pre></td></tr></table></figure><p><code>*args, **kwargs</code> 是为了让装饰器能接受任意参数的函数——不管你传什么，我都原样传进去。</p><h3 id="为什么一定要加-functools-wraps"><a href="#为什么一定要加-functools-wraps" class="headerlink" title="为什么一定要加 @functools.wraps"></a>为什么一定要加 <code>@functools.wraps</code></h3><p>不加的话，被装饰函数的”身份信息”会丢失：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">bad_decorator</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="keyword">return</span> func()</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@bad_decorator</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;打招呼&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">greet.__name__   <span class="comment"># &#x27;wrapper&#x27;，不是 &#x27;greet&#x27;</span></span><br><span class="line">greet.__doc__    <span class="comment"># None，文档丢了</span></span><br></pre></td></tr></table></figure><p>调试的时候，堆栈里全是 <code>wrapper</code>，根本不知道是哪个函数出问题。用 <code>@functools.wraps</code> 解决：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">good_decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)   </span><span class="comment"># 这行把 greet 的名字、文档、签名都复制到 wrapper 上</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="keyword">return</span> func()</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@good_decorator</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;打招呼&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">greet.__name__   <span class="comment"># &#x27;greet&#x27; </span></span><br><span class="line">greet.__doc__    <span class="comment"># &#x27;打招呼&#x27; </span></span><br></pre></td></tr></table></figure><p><strong>写装饰器的铁律：每个装饰器里都用 <code>@functools.wraps</code>。</strong></p><h3 id="带参数的装饰器：三层嵌套"><a href="#带参数的装饰器：三层嵌套" class="headerlink" title="带参数的装饰器：三层嵌套"></a>带参数的装饰器：三层嵌套</h3><p>上面的装饰器都是”固定行为”——计时就是计时，不让你配置。</p><p>如果需要配置参数，比如”重试3次”、”重试间隔2秒”，就需要三层嵌套：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">retry</span>(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span></span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> func(*args, **kwargs)</span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    <span class="keyword">if</span> attempt == max_attempts:</span><br><span class="line">                        <span class="keyword">raise</span></span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;第<span class="subst">&#123;attempt&#125;</span>次失败，<span class="subst">&#123;delay&#125;</span>秒后重试...&quot;</span>)</span><br><span class="line">                    time.sleep(delay)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">unstable_request</span>():</span><br><span class="line">    <span class="comment"># 可能失败的操作</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p><strong>三层各管一摊：</strong></p><div class="table-container"><table><thead><tr><th style="text-align:left">层级</th><th style="text-align:left">作用</th><th style="text-align:left">参数</th></tr></thead><tbody><tr><td style="text-align:left">最外层 <code>retry</code></td><td style="text-align:left">接收配置参数</td><td style="text-align:left"><code>max_attempts</code>、<code>delay</code></td></tr><tr><td style="text-align:left">中间层 <code>decorator</code></td><td style="text-align:left">接收被装饰的函数</td><td style="text-align:left"><code>func</code></td></tr><tr><td style="text-align:left">最内层 <code>wrapper</code></td><td style="text-align:left">执行实际逻辑</td><td style="text-align:left"><code>*args, **kwargs</code></td></tr></tbody></table></div><p>这个模式看着复杂，但写多了就熟了——<strong>外层接配置，中层接函数，内层接调用参数。</strong></p><h3 id="实际场景：这几种装饰器最常用"><a href="#实际场景：这几种装饰器最常用" class="headerlink" title="实际场景：这几种装饰器最常用"></a>实际场景：这几种装饰器最常用</h3><h4 id="场景1：计时器（监控接口性能）"><a href="#场景1：计时器（监控接口性能）" class="headerlink" title="场景1：计时器（监控接口性能）"></a><strong>场景1：计时器（监控接口性能）</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@timer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">query_database</span>():</span><br><span class="line">    <span class="comment"># 慢查询</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>API 响应慢的时候，加上 <code>@timer</code> 就能知道每个函数耗时多少，快速定位瓶颈。</p><h4 id="场景2：重试（调用外部服务）"><a href="#场景2：重试（调用外部服务）" class="headerlink" title="场景2：重试（调用外部服务）"></a><strong>场景2：重试（调用外部服务）</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@retry(<span class="params">max_attempts=<span class="number">3</span>, delay=<span class="number">1</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">call_third_party_api</span>():</span><br><span class="line">    <span class="comment"># 网络请求可能失败</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>外部服务不稳定的时候，自动重试比人工介入快多了。</p><h4 id="场景3：缓存（避免重复计算）"><a href="#场景3：缓存（避免重复计算）" class="headerlink" title="场景3：缓存（避免重复计算）"></a><strong>场景3：缓存（避免重复计算）</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@lru_cache(<span class="params">maxsize=<span class="number">128</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">expensive_compute</span>(<span class="params">n</span>):</span><br><span class="line">    <span class="comment"># 耗时计算</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p><code>functools.lru_cache</code> 是 Python 内置的缓存装饰器，同一个参数只算一次。</p><h4 id="场景4：权限检查（Web-开发）"><a href="#场景4：权限检查（Web-开发）" class="headerlink" title="场景4：权限检查（Web 开发）"></a><strong>场景4：权限检查（Web 开发）</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@login_required</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">dashboard</span>():</span><br><span class="line">    <span class="comment"># 只有登录用户能访问</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>Flask、FastAPI 里这类装饰器随处可见。</p><h4 id="场景5：日志（调试生产问题）"><a href="#场景5：日志（调试生产问题）" class="headerlink" title="场景5：日志（调试生产问题）"></a><strong>场景5：日志（调试生产问题）</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@log_call</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_order</span>(<span class="params">order_id</span>):</span><br><span class="line">    <span class="comment"># 自动记录入参和返回值</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>出问题的时候看日志就知道”哪个函数被调了、传了什么参数、返回了什么”。</p><h3 id="多个装饰器叠加：顺序怎么算"><a href="#多个装饰器叠加：顺序怎么算" class="headerlink" title="多个装饰器叠加：顺序怎么算"></a>多个装饰器叠加：顺序怎么算</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@decorator_a</span></span><br><span class="line"><span class="meta">@decorator_b</span></span><br><span class="line"><span class="meta">@decorator_c</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等价于：</span></span><br><span class="line">func = decorator_a(decorator_b(decorator_c(func)))</span><br></pre></td></tr></table></figure><p><strong>执行顺序：从下往上包装，从上往下执行。</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@log</span></span><br><span class="line"><span class="meta">@timer</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">work</span>():</span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># work() 执行时：</span></span><br><span class="line"><span class="comment"># log 的前置 → timer 的前置 → work → timer 的后置 → log 的后置</span></span><br></pre></td></tr></table></figure><p>想清楚包装顺序再叠加，不然行为可能跟你预期的不一样。</p><h3 id="类装饰器：需要状态的时候用"><a href="#类装饰器：需要状态的时候用" class="headerlink" title="类装饰器：需要状态的时候用"></a>类装饰器：需要状态的时候用</h3><p>函数装饰器每次调用都是独立的，如果需要”累计调用次数”这种状态，用类装饰器。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CountCalls</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, func</span>):</span><br><span class="line">        <span class="variable language_">self</span>.func = func</span><br><span class="line">        <span class="variable language_">self</span>.count = <span class="number">0</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__call__</span>(<span class="params">self, *args, **kwargs</span>):</span><br><span class="line">        <span class="variable language_">self</span>.count += <span class="number">1</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;调用了 <span class="subst">&#123;self.count&#125;</span> 次&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>.func(*args, **kwargs)</span><br><span class="line"></span><br><span class="line"><span class="meta">@CountCalls</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello&quot;</span>)</span><br><span class="line"></span><br><span class="line">greet()   <span class="comment"># 调用了 1 次</span></span><br><span class="line">greet()   <span class="comment"># 调用了 2 次</span></span><br><span class="line">greet()   <span class="comment"># 调用了 3 次</span></span><br></pre></td></tr></table></figure><p><code>__call__</code> 让类的实例可以像函数一样被调用。<code>CountCalls</code> 的实例 <code>greet</code> 被调用时，走的是 <code>__call__</code> 方法。</p><h3 id="异步装饰器：用于-async-def"><a href="#异步装饰器：用于-async-def" class="headerlink" title="异步装饰器：用于 async def"></a>异步装饰器：用于 async def</h3><p>如果你的函数是 <code>async def</code>，装饰器也必须是异步的：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">async_timer</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">    @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">    <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">        start = time.perf_counter()</span><br><span class="line">        result = <span class="keyword">await</span> func(*args, **kwargs)   <span class="comment"># 注意这里用 await</span></span><br><span class="line">        elapsed = time.perf_counter() - start</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;<span class="subst">&#123;func.__name__&#125;</span> 耗时: <span class="subst">&#123;elapsed:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@async_timer</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch</span>():</span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span></span><br></pre></td></tr></table></figure><p>如果装饰器想同时支持同步和异步，用 <code>inspect.iscoroutinefunction(func)</code> 判断，写两个分支。</p><h3 id="一个完整的实战：带超时的重试装饰器"><a href="#一个完整的实战：带超时的重试装饰器" class="headerlink" title="一个完整的实战：带超时的重试装饰器"></a>一个完整的实战：带超时的重试装饰器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">retry_with_timeout</span>(<span class="params">max_attempts=<span class="number">3</span>, timeout=<span class="number">5</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">    重试装饰器，支持超时控制</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">decorator</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta">        @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line">        <span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">wrapper</span>(<span class="params">*args, **kwargs</span>):</span><br><span class="line">            <span class="keyword">for</span> attempt <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, max_attempts + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">await</span> asyncio.wait_for(</span><br><span class="line">                        func(*args, **kwargs),</span><br><span class="line">                        timeout=timeout</span><br><span class="line">                    )</span><br><span class="line">                <span class="keyword">except</span> asyncio.TimeoutError:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;第<span class="subst">&#123;attempt&#125;</span>次超时&quot;</span>)</span><br><span class="line">                    <span class="keyword">if</span> attempt == max_attempts:</span><br><span class="line">                        <span class="keyword">raise</span></span><br><span class="line">                <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                    <span class="built_in">print</span>(<span class="string">f&quot;第<span class="subst">&#123;attempt&#125;</span>次失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">                    <span class="keyword">if</span> attempt == max_attempts:</span><br><span class="line">                        <span class="keyword">raise</span></span><br><span class="line">                    <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">return</span> wrapper</span><br><span class="line">    <span class="keyword">return</span> decorator</span><br><span class="line"></span><br><span class="line"><span class="meta">@retry_with_timeout(<span class="params">max_attempts=<span class="number">3</span>, timeout=<span class="number">2</span></span>)</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">fetch_data</span>():</span><br><span class="line">    <span class="comment"># 可能超时或失败的操作</span></span><br><span class="line">    <span class="keyword">await</span> asyncio.sleep(<span class="number">3</span>)  <span class="comment"># 模拟慢请求</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span></span><br></pre></td></tr></table></figure><p>这个装饰器解决了一个真实问题：外部 API 可能超时，也可能临时报错，自动重试+超时控制比手工处理可靠得多。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/21.%E8%A3%85%E9%A5%B0%E5%99%A8/</id>
    <link href="https://zhhll.com.cn/2026/Python/21.%E8%A3%85%E9%A5%B0%E5%99%A8/"/>
    <published>2026-04-29T11:16:23.000Z</published>
    <summary>
      <![CDATA[<h2 id="Python-装饰器：别被-符号吓到，它就是个”包装盒”"><a href="#Python-装饰器：别被-符号吓到，它就是个”包装盒”" class="headerlink" title="Python 装饰器：别被 @ 符号吓到，它就是个”包装盒”"></a>Python 装饰器：别被 @ 符号吓到，它就是个”包装盒”</h2><p>第一次看到 <code>@</code> 符号的时候，我也觉得像魔法——在函数上面加一行，行为就变了，完全不知道发生了什么。</p>
<p>后来明白了：<strong>装饰器就是个”包装盒”</strong>。你把一个函数放进去，它给你包装一下，加些功能，再还给你。函数还是那个函数，只是多了点本事。</p>
<h3 id="装饰器到底是什么？三个例子看懂"><a href="#装饰器到底是什么？三个例子看懂" class="headerlink" title="装饰器到底是什么？三个例子看懂"></a>装饰器到底是什么？三个例子看懂</h3><h4 id="例子1：函数可以赋值给变量"><a href="#例子1：函数可以赋值给变量" class="headerlink" title="例子1：函数可以赋值给变量"></a><strong>例子1：函数可以赋值给变量</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">greet</span>(<span class="params">name</span>):</span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;Hello, <span class="subst">&#123;name&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">say_hello = greet   <span class="comment"># 把函数赋值给变量</span></span><br><span class="line">say_hello(<span class="string">&quot;Alice&quot;</span>)  <span class="comment"># &quot;Hello, Alice&quot;</span></span><br></pre></td></tr></table></figure>
<p>函数跟数字、字符串一样，可以到处传递。</p>
<h4 id="例子2：函数可以作为参数传给另一个函数"><a href="#例子2：函数可以作为参数传给另一个函数" class="headerlink" title="例子2：函数可以作为参数传给另一个函数"></a><strong>例子2：函数可以作为参数传给另一个函数</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">apply</span>(<span class="params">func, value</span>):</span><br><span class="line">    <span class="keyword">return</span> func(value)</span><br><span class="line"></span><br><span class="line">apply(greet, <span class="string">&quot;Bob&quot;</span>)   <span class="comment"># &quot;Hello, Bob&quot;</span></span><br></pre></td></tr></table></figure>
<h4 id="例子3：函数可以返回另一个函数"><a href="#例子3：函数可以返回另一个函数" class="headerlink" title="例子3：函数可以返回另一个函数"></a><strong>例子3：函数可以返回另一个函数</strong></h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">make_multiplier</span>(<span class="params">factor</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">multiplier</span>(<span class="params">x</span>):</span><br><span class="line">        <span class="keyword">return</span> x * factor</span><br><span class="line">    <span class="keyword">return</span> multiplier</span><br><span class="line"></span><br><span class="line">double = make_multiplier(<span class="number">2</span>)</span><br><span class="line">double(<span class="number">5</span>)   <span class="comment"># 10</span></span><br></pre></td></tr></table></figure>
<p>装饰器就是这三个能力的组合：<strong>接收一个函数，在它外面包一层新功能，返回包装后的函数。</strong></p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">my_decorator</span>(<span class="params">func</span>):</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">wrapper</span>():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;执行前&quot;</span>)</span><br><span class="line">        result = func()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;执行后&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">return</span> wrapper</span><br><span class="line"></span><br><span class="line"><span class="meta">@my_decorator</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">say_hello</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Hello!&quot;</span>)</span><br><span class="line"></span><br><span class="line">say_hello()</span><br><span class="line"><span class="comment"># 执行前</span></span><br><span class="line"><span class="comment"># Hello!</span></span><br><span class="line"><span class="comment"># 执行后</span></span><br></pre></td></tr></table></figure>]]>
    </summary>
    <title>Python装饰器</title>
    <updated>2026-07-03T06:35:45.335Z</updated>
  </entry>
  <entry>
    <author>
      <name>ZH</name>
    </author>
    <category term="深度学习" scheme="https://zhhll.com.cn/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <category term="深度学习" scheme="https://zhhll.com.cn/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    <content>
      <![CDATA[<h2 id="PyTorch训练全流程：从微分原理到梯度下降的完整实现"><a href="#PyTorch训练全流程：从微分原理到梯度下降的完整实现" class="headerlink" title="PyTorch训练全流程：从微分原理到梯度下降的完整实现"></a>PyTorch训练全流程：从微分原理到梯度下降的完整实现</h2><p>深度学习模型的训练过程，本质上是一个不断自我修正的闭环系统。在这个系统中，权重和偏置是模型需要学习的核心参数，损失函数是衡量模型表现的标尺，而反向传播与梯度下降则是驱动模型优化的引擎。理解这些组件如何协同工作，是掌握PyTorch的关键。</p><h3 id="模型参数：权重与偏置的数学本质"><a href="#模型参数：权重与偏置的数学本质" class="headerlink" title="模型参数：权重与偏置的数学本质"></a>模型参数：权重与偏置的数学本质</h3><p>在神经网络中，权重和偏置是模型内部的可学习参数，它们决定了输入数据如何被变换以产生预测结果。</p><p>权重是连接神经元之间的参数，本质上是输入特征对输出结果的贡献系数。在全连接层中，若输入特征为x，权重为w，则权重决定了输入信号在传递到下一层时的缩放比例。权重矩阵的数值大小和分布直接影响模型的表达能力和复杂度。</p><p>偏置是每个神经元自带的可学习参数，用于在加权求和后提供线性偏移量。其数学表达式为z = w^T x + b，其中b的存在使得激活函数可以在非原点位置产生响应。合理的偏置初始化可以使神经元在训练初期保持活跃状态。</p><blockquote><p>激活函数就是把线性函数转化为非线性函数</p></blockquote><p>在PyTorch中，这些参数被封装在<code>nn.Module</code>的子类中，通过<code>model.parameters()</code>可以获取所有需要训练的权重和偏置。</p><h3 id="训练数据：训练值与目标值的角色"><a href="#训练数据：训练值与目标值的角色" class="headerlink" title="训练数据：训练值与目标值的角色"></a>训练数据：训练值与目标值的角色</h3><p>训练过程依赖于成对的数据：训练值和目标值。</p><p>训练值是模型的输入数据，通常表示为X。在图像分类任务中，训练值可能是像素矩阵；在回归任务中，可能是特征向量。训练值经过模型的前向传播，产生预测结果。</p><p>目标值是训练数据对应的真实标签，通常表示为y。它是模型学习的”标准答案”，用于与模型的预测结果进行比较。目标值的质量直接决定了模型学习的方向。</p><p>在训练循环中，训练值和目标值通常以批次（batch）的形式被加载到内存中，以便进行高效的并行计算。</p><span id="more"></span><h3 id="损失函数：衡量模型表现的标尺"><a href="#损失函数：衡量模型表现的标尺" class="headerlink" title="损失函数：衡量模型表现的标尺"></a>损失函数：衡量模型表现的标尺</h3><p>损失函数是连接模型预测与真实标签的桥梁，它量化了模型预测值与目标值之间的差异。损失值越小，说明模型的预测越接近真实值。</p><p>常见的损失函数包括均方误差和交叉熵损失。均方误差常用于回归问题，计算预测值与目标值之差的平方。交叉熵损失常用于分类问题，衡量预测概率分布与真实概率分布的差异。</p><p>在PyTorch中，损失函数通常被实例化为一个对象，如<code>nn.MSELoss()</code>或<code>nn.CrossEntropyLoss()</code>，然后在训练循环中被调用以计算损失值。</p><h3 id="反向传播与梯度下降：模型优化的引擎"><a href="#反向传播与梯度下降：模型优化的引擎" class="headerlink" title="反向传播与梯度下降：模型优化的引擎"></a>反向传播与梯度下降：模型优化的引擎</h3><p>反向传播和梯度下降是模型学习的核心机制，它们共同完成了从误差计算到参数更新的全过程。</p><p>反向传播利用链式法则，从损失值开始，沿着网络从输出层向输入层反向传递，计算每个参数对于总误差的梯度。这个过程回答了”每个参数应该为多少误差负责”的问题。</p><p>梯度下降是优化器利用反向传播计算出的梯度，按照某种策略来更新网络的权重和偏置，目标是使损失函数的值最小化。常见的优化器包括随机梯度下降和Adam。</p><h3 id="微分在训练中的核心作用"><a href="#微分在训练中的核心作用" class="headerlink" title="微分在训练中的核心作用"></a>微分在训练中的核心作用</h3><p>微分在训练中的作用体现在梯度计算环节。当损失函数计算出预测值与目标值的差异后，需要知道每个权重和偏置对总误差的贡献程度，这就需要对损失函数关于每个参数求偏导。</p><p>以简单的线性模型y = wx + b为例，损失函数L = (y_pred - y_true)²。为了更新权重w，需要计算∂L/∂w；为了更新偏置b，需要计算∂L/∂b。这些偏导数就是梯度，它们指示了参数应该调整的方向和幅度。</p><p>PyTorch的<code>torch.autograd</code>模块自动完成了这些微分计算。当调用<code>loss.backward()</code>时，系统会自动构建计算图，应用链式法则，计算出所有参数的梯度，并存储在参数的<code>.grad</code>属性中。</p><h3 id="实战：完整的训练循环解析"><a href="#实战：完整的训练循环解析" class="headerlink" title="实战：完整的训练循环解析"></a>实战：完整的训练循环解析</h3><p>让我们通过一个完整的训练循环，看看这些组件如何协同工作。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 准备数据</span></span><br><span class="line"><span class="comment"># x是输入 y是目标值</span></span><br><span class="line">x = torch.tensor([[<span class="number">1.0</span>],[<span class="number">2.0</span>],[<span class="number">3.0</span>],[<span class="number">4.0</span>]])</span><br><span class="line">y = torch.tensor([[<span class="number">2.0</span>],[<span class="number">4.0</span>],[<span class="number">6.0</span>],[<span class="number">8.0</span>]])  <span class="comment"># 理想关系是 y = 2x</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义模型</span></span><br><span class="line"><span class="comment"># 输入是一维，输出是一维</span></span><br><span class="line">model = torch.nn.Linear(<span class="number">1</span>,<span class="number">1</span>)</span><br><span class="line"><span class="comment"># 定义损失函数 均方误差</span></span><br><span class="line">loss = torch.nn.MSELoss()</span><br><span class="line"><span class="comment"># 定义优化器  随机梯度下降，学习率为0.01</span></span><br><span class="line">optimizer = torch.optim.SGD(model.parameters(),lr=<span class="number">0.01</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 训练模型</span></span><br><span class="line">epochs = <span class="number">1000</span></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(epochs):</span><br><span class="line">    <span class="comment"># 前向传播</span></span><br><span class="line">    y_pred = model(x)</span><br><span class="line">    <span class="comment"># 计算损失</span></span><br><span class="line">    l = loss(y_pred,y)</span><br><span class="line">    <span class="comment"># 清空梯度（ 先把上一轮的 .grad 归零，pytorch默认是累加梯度，如果不清零，会造成梯度爆炸）</span></span><br><span class="line">    optimizer.zero_grad()</span><br><span class="line">    <span class="comment"># 反向传播（计算梯度，此时梯度被累加到 .grad 中）</span></span><br><span class="line">    l.backward()</span><br><span class="line">    <span class="comment"># 更新参数（优化器读取 .grad，更新权重 w）</span></span><br><span class="line">    optimizer.step()</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (epoch+<span class="number">1</span>) % <span class="number">100</span> == <span class="number">0</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&#x27;epoch <span class="subst">&#123;epoch+<span class="number">1</span>&#125;</span>, loss <span class="subst">&#123;l.item()&#125;</span>&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&#x27;w:<span class="subst">&#123;model.weight.item()&#125;</span>, b:<span class="subst">&#123;model.bias.item()&#125;</span>&#x27;</span>) <span class="comment"># w会无限接近于2，b无限接近于0</span></span><br></pre></td></tr></table></figure><p>在这个训练循环中，我们可以看到：</p><p>训练值X通过模型的前向传播产生预测值y_pred。损失函数计算预测值y_pred与目标值y之间的差异，得到损失值loss。反向传播计算损失值对模型参数（权重和偏置）的梯度，这个过程用到了微分。优化器根据梯度更新权重和偏置，使损失值逐渐减小。</p><p>通过成千上万次的迭代，模型的权重和偏置不断调整，最终使预测值越来越接近目标值，完成模型的学习过程。</p><h3 id="关键细节与避坑指南"><a href="#关键细节与避坑指南" class="headerlink" title="关键细节与避坑指南"></a>关键细节与避坑指南</h3><p>在实战中，有几个个细节决定了训练的成败：</p><ul><li><p><strong>梯度清零是必须的</strong>。PyTorch 的设计哲学是允许梯度累加，这在处理超大模型显存不足（需要模拟大 Batch Size）时很有用。但在常规训练中，如果不执行 <code>optimizer.zero_grad()</code>，梯度会不断叠加，导致参数更新方向错误，模型无法收敛。</p></li><li><p><strong>动态图的优势</strong>。PyTorch 的计算图是动态构建的，这意味着你可以在循环、条件判断中使用 Python 原生的控制流，而不需要像 TensorFlow 1.x 那样构建静态图。这让调试变得异常轻松。</p></li></ul><h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h3><p>深度学习模型的训练是一个精密的协同过程。权重和偏置是模型需要学习的知识，训练值和目标值是学习的素材，损失函数是学习的标尺，而反向传播与梯度下降则是学习的方法。微分作为梯度计算的核心，连接了损失函数与参数更新，是整个训练流程的数学基础。理解这些组件的角色和相互作用，是掌握PyTorch深度学习框架的关键。</p>]]>
    </content>
    <id>https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/4.PyTorch%E8%AE%AD%E7%BB%83/</id>
    <link href="https://zhhll.com.cn/2026/Python/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/4.PyTorch%E8%AE%AD%E7%BB%83/"/>
    <published>2026-04-29T11:12:23.000Z</published>
    <summary>
      <![CDATA[<h2 id="PyTorch训练全流程：从微分原理到梯度下降的完整实现"><a href="#PyTorch训练全流程：从微分原理到梯度下降的完整实现" class="headerlink" title="PyTorch训练全流程：从微分原理到梯度下降的完整实现"></a>PyTorch训练全流程：从微分原理到梯度下降的完整实现</h2><p>深度学习模型的训练过程，本质上是一个不断自我修正的闭环系统。在这个系统中，权重和偏置是模型需要学习的核心参数，损失函数是衡量模型表现的标尺，而反向传播与梯度下降则是驱动模型优化的引擎。理解这些组件如何协同工作，是掌握PyTorch的关键。</p>
<h3 id="模型参数：权重与偏置的数学本质"><a href="#模型参数：权重与偏置的数学本质" class="headerlink" title="模型参数：权重与偏置的数学本质"></a>模型参数：权重与偏置的数学本质</h3><p>在神经网络中，权重和偏置是模型内部的可学习参数，它们决定了输入数据如何被变换以产生预测结果。</p>
<p>权重是连接神经元之间的参数，本质上是输入特征对输出结果的贡献系数。在全连接层中，若输入特征为x，权重为w，则权重决定了输入信号在传递到下一层时的缩放比例。权重矩阵的数值大小和分布直接影响模型的表达能力和复杂度。</p>
<p>偏置是每个神经元自带的可学习参数，用于在加权求和后提供线性偏移量。其数学表达式为z = w^T x + b，其中b的存在使得激活函数可以在非原点位置产生响应。合理的偏置初始化可以使神经元在训练初期保持活跃状态。</p>
<blockquote>
<p>激活函数就是把线性函数转化为非线性函数</p>
</blockquote>
<p>在PyTorch中，这些参数被封装在<code>nn.Module</code>的子类中，通过<code>model.parameters()</code>可以获取所有需要训练的权重和偏置。</p>
<h3 id="训练数据：训练值与目标值的角色"><a href="#训练数据：训练值与目标值的角色" class="headerlink" title="训练数据：训练值与目标值的角色"></a>训练数据：训练值与目标值的角色</h3><p>训练过程依赖于成对的数据：训练值和目标值。</p>
<p>训练值是模型的输入数据，通常表示为X。在图像分类任务中，训练值可能是像素矩阵；在回归任务中，可能是特征向量。训练值经过模型的前向传播，产生预测结果。</p>
<p>目标值是训练数据对应的真实标签，通常表示为y。它是模型学习的”标准答案”，用于与模型的预测结果进行比较。目标值的质量直接决定了模型学习的方向。</p>
<p>在训练循环中，训练值和目标值通常以批次（batch）的形式被加载到内存中，以便进行高效的并行计算。</p>]]>
    </summary>
    <title>PyTorch训练</title>
    <updated>2026-06-24T10:18:33.690Z</updated>
  </entry>
</feed>
