<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Gru on Jeanphilo Blog</title><link>https://shio-chan-dev.github.io/jeanblog/zh/tags/gru/</link><description>Recent content in Gru on Jeanphilo Blog</description><generator>Hugo -- 0.161.1</generator><language>zh-cn</language><lastBuildDate>Thu, 23 Apr 2026 15:27:55 +0800</lastBuildDate><atom:link href="https://shio-chan-dev.github.io/jeanblog/zh/tags/gru/index.xml" rel="self" type="application/rss+xml"/><item><title>Seq2Seq 与 Encoder-Decoder：从翻译任务到最小可运行 PyTorch 实现</title><link>https://shio-chan-dev.github.io/jeanblog/zh/ai/llm/seq2seq-encoder-decoder-pytorch-minimal/</link><pubDate>Thu, 23 Apr 2026 15:27:55 +0800</pubDate><guid>https://shio-chan-dev.github.io/jeanblog/zh/ai/llm/seq2seq-encoder-decoder-pytorch-minimal/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;
这篇文章不把 seq2seq 和 encoder-decoder 当成术语表来讲，而是从一个最小翻译任务出发，解释为什么“输入一段序列、输出另一段序列”会自然逼出编码器和解码器的分工，最后收束成一份最小可运行的 PyTorch GRU 实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="从一个最小翻译任务开始"&gt;从一个最小翻译任务开始&lt;/h2&gt;
&lt;p&gt;假设源序列是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; I love apples &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;目标序列是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; 我 喜欢 苹果 &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当模型要生成“苹果”时，它至少要解决三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它必须知道整句英文大意，而不只是当前一个词&lt;/li&gt;
&lt;li&gt;它必须记住自己前面已经生成了“我 喜欢”&lt;/li&gt;
&lt;li&gt;它必须按顺序一个词一个词地产生输出，而不是一次吐出整个目标序列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你只用一个普通分类器把源句子映射成一个类别，这个任务做不成。
因为这里的输出不是一个固定标签，而是长度可变的目标序列。&lt;/p&gt;
&lt;p&gt;所以这里天然会逼出一个更具体的数据流：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先把源序列读完，压成可传递的状态&lt;/li&gt;
&lt;li&gt;再从 &lt;code&gt;&amp;lt;bos&amp;gt;&lt;/code&gt; 开始，逐步生成目标序列&lt;/li&gt;
&lt;li&gt;每生成一步，都要同时依赖“源侧信息”和“目标侧历史”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这就是 &lt;code&gt;sequence-to-sequence&lt;/code&gt; 的最小问题形态。
&lt;code&gt;seq2seq&lt;/code&gt; 说的是任务：输入一段序列，输出另一段序列。
&lt;code&gt;encoder-decoder&lt;/code&gt; 说的是实现：先编码输入，再逐步解码输出。&lt;/p&gt;
&lt;p&gt;下面不先堆名词，直接按这个压力把代码一步一步长出来。&lt;/p&gt;
&lt;h2 id="快速掌握地图"&gt;快速掌握地图&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;问题形态：&lt;code&gt;src -&amp;gt; encoder -&amp;gt; hidden/context -&amp;gt; decoder -&amp;gt; logits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;核心目标：学习条件概率 &lt;code&gt;p(y_t | y_{&amp;lt;t}, x)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;最小实现：&lt;code&gt;Embedding + GRU Encoder + GRU Decoder + output projection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;何时适用：翻译、摘要、改写、问答这类“输入序列 -&amp;gt; 输出序列”任务&lt;/li&gt;
&lt;li&gt;明显局限：如果所有源信息都被压进一个固定长度向量，长句子会吃力&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="这篇文章重点深挖的两个概念"&gt;这篇文章重点深挖的两个概念&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;隐藏状态交接&lt;/strong&gt;：encoder 到底把什么交给 decoder&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;右移目标序列与 teacher forcing&lt;/strong&gt;：训练时 decoder 为什么不能直接喂完整真实答案&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="大师级心智模型"&gt;大师级心智模型&lt;/h2&gt;
&lt;p&gt;这类模型的核心抽象不是“两个 RNN 拼起来”，而是：&lt;/p&gt;</description></item></channel></rss>