<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on LunaTide's Blog</title><link>https://lunatide.tech/post/</link><description>Recent content in Posts on LunaTide's Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>LunaTide's Blog</copyright><lastBuildDate>Sun, 22 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://lunatide.tech/post/index.xml" rel="self" type="application/rss+xml"/><item><title>CS229 Lecture 11</title><link>https://lunatide.tech/p/cs229-lecture-11/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-11/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-11/pic1.jpg" alt="Featured image of post CS229 Lecture 11" /&gt;&lt;h2 id="无限个假设的情况"&gt;无限个假设的情况
&lt;/h2&gt;&lt;p&gt;介绍之前给出一些定义。&lt;/p&gt;
&lt;p&gt;给定集合 $S = {x^{(1)}, \ldots, x^{(d)}}$，其中 $x^{(i)} \in \mathcal{X}$，我们称 $\mathcal{H}$ 打散（shatter）$S$ 如果 $\mathcal{H}$ 能实现 $S$ 上任意一种标记。即对任意标签集 ${y^{(1)}, \ldots, y^{(d)}}$，存在 $h \in \mathcal{H}$ 使得 $h(x^{(i)}) = y^{(i)}$。给定集合 $\mathcal{H}$，定义 VC 维（Vapnik–Chervonenkis dimension）为 $\mathcal{H}$ 能打散的最大集合的大小，用 $VC(\mathcal{H})$ 表示，如果 $\mathcal{H}$ 能打散任意数量的集合，那么 $VC(\mathcal{H}) = \infty$。&lt;/p&gt;
&lt;p&gt;二维线性分类器（$h(x) = 1{\theta_0 + \theta_1 x_1 + \theta_2 x_2 \ge 0}$）的假设类 $\mathcal{H}$ 能否将上述情形打散呢？答案是肯定的，如下图所示：&lt;/p&gt;
&lt;img src="a.jpg" style="zoom:67%;" /&gt;
&lt;p&gt;此外，不难看出 $\mathcal{H}$ 无法打散 4 个点的情形，所以 $VC(\mathcal{H}) = 3$。&lt;/p&gt;
&lt;p&gt;需要注意的是，尽管 $VC(\mathcal{H}) = 3$，但仍然有 3 个点的情形无法被打散，如下图所示：&lt;/p&gt;
&lt;img src="b.jpg" style="zoom:67%;" /&gt;
&lt;p&gt;换句话说，在 VC 维的定义下，为了证明 $VC(\mathcal{H}) \ge d$，只要证明至少有一个规模为 $d$ 的集合能被打散即可。&lt;/p&gt;
&lt;p&gt;接下来给出学习理论中的重要定理：&lt;/p&gt;
&lt;h3 id="定理"&gt;定理
&lt;/h3&gt;&lt;p&gt;给定 $\mathcal{H}$，令 $d = VC(\mathcal{H})$，那么有至少 $1 - \delta$ 的概率，对于任意 $h \in \mathcal{H}$，&lt;/p&gt;
$$
|\epsilon(h) - \hat{\epsilon}(h)| \le O\left(\sqrt{\frac{d}{m} \log \frac{m}{d} + \frac{1}{m} \log \frac{1}{\delta}}\right)
$$&lt;p&gt;因为，至少有 $1 - \delta$ 的概率，我们有：&lt;/p&gt;
$$
\epsilon(\hat{h}) \le \epsilon(h^*) + O\left(\sqrt{\frac{d}{m} \log \frac{m}{d} + \frac{1}{m} \log \frac{1}{\delta}}\right)
$$&lt;p&gt;换句话说，如果假设类的 VC 维有限，那么当 $m$ 比较大的时候一致收敛就会成立，和之前一样，这就能让我们用 $\epsilon(h^*)$ 的形式给出 $\epsilon(h)$ 的上界。我们还有如下推论：&lt;/p&gt;
&lt;h3 id="推论"&gt;推论
&lt;/h3&gt;&lt;p&gt;为了让 $|\epsilon(h) - \hat{\epsilon}(h)| \le \gamma$ 对于任意 $h \in \mathcal{H}$ 有至少 $1 - \delta$ 的概率成立（从而可以推出 $\epsilon(\hat{h}) \le \epsilon(h^*) + 2\gamma$），只要满足&lt;/p&gt;
$$
m = O_{\gamma,\delta}(d)
$$&lt;p&gt;换句话说，要使假设类 $\mathcal{H}$ 的表现比较好，我们需要和其 VC 维线性关系的数据，实际中，VC 维往往和参数数量呈线性关系，所以一般情况下训练样本数要和参数数量呈线性关系。&lt;/p&gt;
&lt;p&gt;备注：有关 VC 维的部分这里比较简略，可以参考相关课程或教材。&lt;/p&gt;
&lt;h2 id="正则化与模型选择"&gt;正则化与模型选择
&lt;/h2&gt;&lt;p&gt;这一讲主要介绍如何权衡偏差与方差，选择适合的模型以及参数&lt;/p&gt;
&lt;h3 id="交叉验证"&gt;交叉验证
&lt;/h3&gt;&lt;p&gt;假设给定训练集 $S$，我们已经了解经验风险最小化，来看下面一个使用经验风险最小化推导的算法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 $S$ 上训练每个模型 $M_i$，得到假设 $h_i$&lt;/li&gt;
&lt;li&gt;选择具有最小训练误差的假设&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个算法是没有用的。考虑选择多项式的次数，多项式次数越高，那么模型在训练集上的表现就越好，因此就有较小的训练误差。所以这个模型会选择一个高方差、高次数的多项式模型，这通常是一个糟糕的选择。&lt;/p&gt;
&lt;p&gt;现在有一个效果更好的算法，叫做保留交叉验证（hold-out cross validation），也叫做简单交叉验证（simple cross validation），步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将 $S$ 随机分为 $S_{\text{train}}$（一般为 70%）和 $S_{\text{cv}}$（剩余的 30%），这里 $S_{\text{cv}}$ 被称为保留交叉验证集&lt;/li&gt;
&lt;li&gt;在 $S_{\text{train}}$ 训练每个模型 $M_i$ 获得 $h_i$&lt;/li&gt;
&lt;li&gt;选择在交叉验证集上误差 $\hat{\epsilon}&lt;em&gt;{S&lt;/em&gt;{\text{cv}}}(h_i)$ 最小的假设 $h_i$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过在我们没有训练过的样本 $S_{\text{cv}}$ 上做测试，我们得到了对 $h_i$ 的真实泛化误差的更好估计，而且可以选择具有最小泛化误差估计的假设。通常，交叉验证集合的比例选在 $\frac{1}{4} - \frac{1}{3}$ 之间，30% 是一个经典的选择。&lt;/p&gt;
&lt;p&gt;另一种选择是，将步骤 3 替换为根据 $\arg\min_i \hat{\epsilon}&lt;em&gt;{S&lt;/em&gt;{\text{cv}}}(h_i)$ 选择模型 $M_i$，然后在全体数据 $S$ 上重新训练 $M_i$。这通常是一个好方法。&lt;/p&gt;
&lt;p&gt;保留交叉验证的劣势在于浪费了约 30% 的数据，即使我们选择在全体数据上重新训练模型，我们仍然是在 $0.7m$ 的数据上找到最优模型，因为我们每次只是在 $0.7m$ 的数据上训练模型。当数据充足或廉价时问题不大，但数据较少时需要更好的方法。&lt;/p&gt;
&lt;p&gt;下面是一个方法，被称为 $k$ 折交叉验证（$k$-fold cross validation），每次保留的数据更少：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将 $S$ 随机划分为 $k$ 个不相交的子集，每个子集有 $m/k$ 个训练样本，记为 $S_1, \ldots, S_k$&lt;/li&gt;
&lt;li&gt;对每个模型 $M_i$，进行如下评估：
&lt;ul&gt;
&lt;li&gt;对 $j = 1, \ldots, k$
&lt;ul&gt;
&lt;li&gt;在 $S_1 \cup \cdots \cup S_{j-1} \cup S_{j+1} \cup \cdots \cup S_k$ 上训练 $M_i$ 得到 $h_{ij}$&lt;/li&gt;
&lt;li&gt;在 $S_j$ 上测试 $h_{ij}$，得到 $\hat{\epsilon}&lt;em&gt;{S_j}(h&lt;/em&gt;{ij})$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;计算 $\hat{\epsilon}&lt;em&gt;{S_j}(h&lt;/em&gt;{ij})$ 的平均值来估计模型 $M_i$ 的泛化误差&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;选择具有最小泛化误差估计的模型 $M_i$，在 $S$ 上重新训练模型&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$k$ 的经典选择是 10。尽管每次验证的数据只有 $1/k$，但计算成本更高，因为需要训练 $k$ 次。&lt;/p&gt;
&lt;p&gt;当数据非常少时，可以选择 $k = m$，即每次只留下一个样本作为测试，这种方法称为留一法交叉验证（leave-one-out cross validation）。&lt;/p&gt;
&lt;h3 id="特征选择"&gt;特征选择
&lt;/h3&gt;&lt;p&gt;一种特殊的模型选择方法被称为特征选择。设想一个监督学习问题，其特征数 $n$ 非常大（例如 $n \gg m$），但实际上只有一部分特征与任务相关。&lt;/p&gt;
&lt;p&gt;在这样的背景下，我们可以使用特征选择算法减少特征数量。给定 $n$ 个特征，有 $2^n$ 个可能的特征子集，因此特征选择问题可以看作在 $2^n$ 个模型中选择。&lt;/p&gt;
&lt;p&gt;由于 $2^n$ 通常非常大，我们使用启发式搜索算法，例如前向搜索：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化 $\mathcal{F} = \emptyset$&lt;/li&gt;
&lt;li&gt;重复：
&lt;ul&gt;
&lt;li&gt;对于 $i = 1, \ldots, n$，如果 $i \notin \mathcal{F}$，令 $\mathcal{F}_i = \mathcal{F} \cup {i}$，并使用交叉验证评估 $\mathcal{F}_i$&lt;/li&gt;
&lt;li&gt;令 $\mathcal{F}$ 为表现最好的特征子集&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;输出整个过程中表现最好的特征子集&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也可以使用反向搜索，从 $\mathcal{F} = {1, \ldots, n}$ 开始，每次移除一个特征。&lt;/p&gt;
&lt;p&gt;包装器特征选择（wrapper methods）通常效果较好，但计算成本较高。&lt;/p&gt;
&lt;p&gt;另一类方法是过滤式特征选择（filter methods），其计算成本更低。我们可以定义一个评分函数 $S(i)$ 来衡量特征 $x_i$ 的重要性，例如互信息：&lt;/p&gt;
$$
\mathrm{MI}(x_i, y) = \sum_{x_i \in \{0,1\}} \sum_{y \in \{0,1\}} p(x_i, y)\log \frac{p(x_i, y)}{p(x_i)p(y)}
$$&lt;p&gt;互信息也可以表示为 KL 散度：&lt;/p&gt;
$$
\mathrm{MI}(x_i, y) = \mathrm{KL}(p(x_i, y) \,\|\, p(x_i)p(y))
$$&lt;p&gt;KL 散度衡量两个分布的差异。如果 $x_i$ 与 $y$ 独立，则互信息为 0；否则互信息越大表示相关性越强。&lt;/p&gt;
&lt;p&gt;最后，可以根据评分函数对特征排序，并选择前 $k$ 个特征。&lt;/p&gt;</description></item><item><title>CS229 Lecture 10</title><link>https://lunatide.tech/p/cs229-lecture-10/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-10/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-10/pic1.jpg" alt="Featured image of post CS229 Lecture 10" /&gt;&lt;h2 id="smo算法"&gt;SMO算法
&lt;/h2&gt;&lt;p&gt;John Platt 发明的 SMO（序列最小优化）算法提供了一种有效的方法来解决由 SVM 的推导得到的对偶问题。部分是为了激发 SMO 算法，部分是因为它本身很有趣，让我们首先讨论坐标上升算法。&lt;/p&gt;
&lt;h3 id="坐标上升"&gt;坐标上升
&lt;/h3&gt;&lt;p&gt;考虑尝试解决无约束的优化问题：&lt;/p&gt;
$$
\max_{\alpha} W(\alpha_1, \alpha_2, \ldots, \alpha_m)
$$&lt;p&gt;在这里，我们将 $W$ 视为参数 $\alpha_i$ 的某个函数，现在忽略此问题与 SVM 之间的任何关系。我们已经看到了两种优化算法，梯度上升和牛顿方法。我们将在这里考虑的新算法称为坐标上升：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;迭代直至收敛：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对 $i = 1, \ldots, m$，
$$
\alpha_i := \arg\max_{\hat{\alpha}_i} W(\alpha_1, \ldots, \alpha_{i-1}, \hat{\alpha}_i, \alpha_{i+1}, \ldots, \alpha_m)
$$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，在该算法的最内层循环中，除了一些固定的 $\alpha_j$ 之外，我们将保持所有变量，并且仅针对参数 $\alpha_i$ 重新优化 $W$。在这里给出的这个方法的版本中，内循环按 $\alpha_1, \alpha_2, \ldots, \alpha_m, \alpha_1, \alpha_2, \ldots$ 的顺序重新优化变量（一个更复杂的版本可能会选择其他顺序；例如，我们可以选择使 $W(\alpha)$ 获得最大增长的变量）。&lt;/p&gt;
&lt;p&gt;当函数 $W$ 碰巧具有内循环中的 “arg max” 可以有效地执行的形式时，那么坐标上升是相当有效的算法。这是一张坐标上升的图片：&lt;/p&gt;
&lt;img src="a.jpg" style="zoom:67%;" /&gt;
&lt;h3 id="smo"&gt;SMO
&lt;/h3&gt;&lt;p&gt;我们通过对 SMO 算法的简单推导来结束对 SVM 的讨论。&lt;/p&gt;
&lt;p&gt;这是我们想要解决的（对偶）优化问题：&lt;/p&gt;
$$
\max_{\alpha} \; W(\alpha) = \sum_{i=1}^{m} \alpha_i - \frac{1}{2} \sum_{i,j=1}^{m} y^{(i)} y^{(j)} \alpha_i \alpha_j \langle x^{(i)}, x^{(j)} \rangle
$$$$
\text{s.t. } 0 \le \alpha_i \le C,\; i = 1, \ldots, m \tag{18}
$$$$
\sum_{i=1}^{m} \alpha_i y^{(i)} = 0 \tag{19}
$$&lt;p&gt;假设我们有一组满足约束条件 (18)–(19) 的 $\alpha_i$。现在，假设我们想要保持 $\alpha_2, \ldots, \alpha_m$ 固定，并采取坐标上升法，并关于 $\alpha_1$ 重新优化目标。我们可以取得任何进展吗？答案是否定的，因为约束 (19) 确保了&lt;/p&gt;
$$
\alpha_1 y^{(1)} = - \sum_{i=2}^{m} \alpha_i y^{(i)}
$$&lt;p&gt;或者，通过将两边乘以 $y^{(1)}$，我们等价地有&lt;/p&gt;
$$
\alpha_1 = - y^{(1)} \sum_{i=2}^{m} \alpha_i y^{(i)}
$$&lt;p&gt;（这一步使用了 $y^{(1)} \in {-1, 1}$ 因此 $(y^{(1)})^2 = 1$。所以如果我们保持保持 $\alpha_2, \ldots, \alpha_m$ 固定，那么 $\alpha_1$ 完全由其他 $\alpha_i$ 确定，那么我们就不能在不违反优化问题中的约束 (19) 的情况下对 $\alpha_1$ 进行任何改变。）&lt;/p&gt;
&lt;p&gt;因此，如果我们想要更新 $\alpha_i$ 中某些项，我们必须同时更新它们中的至少两个以便保持满足约束。这激发了 SMO 算法，它简单地执行以下操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;重复直至收敛：
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;选择一对 $\alpha_i$ 和 $\alpha_j$ 进行下一次更新（选择在全局最大值上取得最大增量的两个坐标）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start="2"&gt;
&lt;li&gt;关于 $\alpha_i$ 和 $\alpha_j$ 重新优化 $W(\alpha)$，同时保持所有其他 $\alpha_k\ (k \ne i,j)$ 固定。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了测试该算法的收敛性，我们可以检查 KKT 条件（公式 14–16）是否在某个 tol 范围内满足。这里，tol 是收敛容差参数，并且通常设置为约 0.01 至 0.001。（有关详细信息，请参论文和伪代码。）&lt;/p&gt;
&lt;p&gt;SMO 是一种有效算法的关键原因对 $\alpha_i, \alpha_j$ 的更新可以有效地进行。现在让我们简要推导高效更新的主要思路。&lt;/p&gt;
&lt;p&gt;假设我们目前有一些 $\alpha_i$ 满足约束条件 (18)–(19)，并假设我们决定保持 $\alpha_3, \ldots, \alpha_m$ 固定，并且想要关于 $\alpha_1$ 和 $\alpha_2$（满足约束条件）重新优化 $W(\alpha_1, \alpha_2, \ldots, \alpha_m)$。由等式 (19)，我们有&lt;/p&gt;
$$
\alpha_1 y^{(1)} + \alpha_2 y^{(2)} = - \sum_{i=3}^{m} \alpha_i y^{(i)}
$$&lt;p&gt;由于右侧是固定的（因为我们已经固定了 $\alpha_3, \ldots, \alpha_m$），我们可以用某个常数 $\xi$ 表示这项：&lt;/p&gt;
$$
\alpha_1 y^{(1)} + \alpha_2 y^{(2)} = \xi
$$&lt;p&gt;因此，我们可以将对 $\alpha_1$ 和 $\alpha_2$ 的约束描述如下：&lt;/p&gt;
&lt;img src="b.jpg" style="zoom:50%;" /&gt;
&lt;p&gt;根据约束 (18)，我们知道 $\alpha_1$ 和 $\alpha_2$ 必须位于所示的框 $[0, C] \times [0, C]$ 内。还绘制了直线 $\alpha_1 y^{(1)} + \alpha_2 y^{(2)} = \xi$，其中 $\alpha_1$ 和 $\alpha_2$ 必须在该直线上。还要注意，从这些约束中，我们知道 $L \le \alpha_2 \le H$；否则，$(\alpha_1, \alpha_2)$ 不能同时满足框和直线约束。在这个例子中，$L = 0$。但是根据直线 $\alpha_1 y^{(1)} + \alpha_2 y^{(2)} = \xi$ 的样子，这不是必然的情况；但更一般地，在 $\alpha_2$ 的允许值上将存在某个下限 $L$ 和某个上限 $H$，这将确保 $\alpha_1, \alpha_2$ 位于框 $[0, C] \times [0, C]$ 内。&lt;/p&gt;
&lt;p&gt;使用等式 (20)，我们也可以将 $\alpha_1$ 写为 $\alpha_2$ 的函数：&lt;/p&gt;
$$
\alpha_1 = (\xi - \alpha_2 y^{(2)}) y^{(1)}
$$&lt;p&gt;（我们再次使用 $y^{(1)} \in {-1, 1}$ 的事实，因此 $(y^{(1)})^2 = 1$。）因此，可以写出目标函数 $W(\alpha)$：&lt;/p&gt;
$$
W(\alpha_1, \alpha_2, \ldots, \alpha_m) = W((\xi - \alpha_2 y^{(2)}) y^{(1)}, \alpha_2, \ldots, \alpha_m)
$$&lt;p&gt;将 $\alpha_3, \ldots, \alpha_m$ 视为常数，可以验证这是关于 $\alpha_2$ 的二次函数。即，对于某些 $a, b, c$，可以以 $a\alpha_2^2 + b\alpha_2 + c$ 的形式表示。如果我们忽略“框”约束 (18)（或等效地，$L \le \alpha_2 \le H$），那么我们可以通过将其导数设为零并求解来轻松地最大化该二次函数。我们将 $\alpha_2^{\text{new, unclipped}}$ 表示 $\alpha_2$ 的结果值。如果我们想要相对于 $\alpha_2$ 最大化 $W$ 但受制于框约束，我们可以得到（利用二次函数的性质）：&lt;/p&gt;
$$
\alpha_2^{\text{new}} =
\begin{cases}
H &amp; \text{如果 } \alpha_2^{\text{new, unclipped}} &gt; H \\
\alpha_2^{\text{new, unclipped}} &amp; \text{如果 } L \le \alpha_2^{\text{new, unclipped}} \le H \\
L &amp; \text{如果 } \alpha_2^{\text{new, unclipped}} &lt; L
\end{cases}
$$&lt;p&gt;最后，找到了 $\alpha_2^{\text{new}}$，我们可以使用公式 (20) 找到 $\alpha_1^{\text{new}}$ 的最优值。&lt;/p&gt;
&lt;p&gt;还有一些非常简单的细节，可以在 Platt 的论文中了解：一个是用于选择下一个 $\alpha_i, \alpha_j$ 进行更新的启发式选择；另一个是在运行 SMO 算法时如何更新 $b$。&lt;/p&gt;
&lt;h2 id="学习理论"&gt;学习理论
&lt;/h2&gt;&lt;h3 id="偏差方差的权衡"&gt;偏差/方差的权衡
&lt;/h3&gt;&lt;p&gt;首先看下图：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-10/c.jpg"
width="908"
height="255"
srcset="https://lunatide.tech/p/cs229-lecture-10/c_hu_7e8df5217b6de894.jpg 480w, https://lunatide.tech/p/cs229-lecture-10/c_hu_b751b1ac2079ce66.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="356"
data-flex-basis="854px"
&gt;&lt;/p&gt;
&lt;p&gt;我们的目标函数是二次的，第一张图采用一次函数拟合，第三张图采用五次函数拟合，这两种情况下的泛化误差都很大，但是产生的原因是不同的。第一张图在训练集上误差就很大，并且无论训练集有多少数据，得到的效果都不会太好，因为一次函数很难捕捉二次函数的特点，这种情形称为欠拟合，偏差很大；第三张图中训练集上的误差为 0，这种情形往往是只捕捉到训练数据的模式，但是没有捕捉到 $x, y$ 更广泛的模式，如果我们计算不在训练集中的数据的损失函数，会发现损失往往非常大，这种情形称为过拟合，方差很大。在实际操作过程中，我们需要对偏差、方差进行权衡，既不能使用太简单的模型（欠拟合），也不能使用太复杂的模型（过拟合）。&lt;/p&gt;
&lt;h3 id="预先准备"&gt;预先准备
&lt;/h3&gt;&lt;p&gt;这里介绍两个后续讨论需要用到的引理&lt;/p&gt;
&lt;h4 id="引理"&gt;引理
&lt;/h4&gt;&lt;p&gt;$A_1, A_2, \ldots, A_k$ 是 $k$ 个不同事件，那么&lt;/p&gt;
$$
P(A_1 \cup \cdots \cup A_k) \le P(A_1) + \cdots + P(A_k)
$$&lt;p&gt;
非常熟悉的公理，不予证明&lt;/p&gt;
&lt;h4 id="引理-1"&gt;引理
&lt;/h4&gt;&lt;p&gt;设 $Z_1, \ldots, Z_m$ 是 $m$ 个独立同分布，服从伯努利分布 $b(1, \phi)$，即
$P(Z_i = 1) = \phi,; P(Z_i = 0) = 1 - \phi$，令
$\hat{\phi} = \frac{1}{m} \sum_{i=1}^{m} Z_i$，那么对任意固定 $\gamma &amp;gt; 0$，有&lt;/p&gt;
$$
P(|\hat{\phi} - \phi| &gt; \gamma) \le 2 \exp(-2\gamma^2 m)
$$&lt;p&gt;这里老师没给出证明，我自己之前写过一个初等证明——传送门，后面会整理老师给的补充材料里的证明，这里先使用这个结论。&lt;/p&gt;
&lt;p&gt;为了简化讨论，这里将问题限制在二元分类问题中，即 $y \in {0,1}$，假设训练集
$S = {(x^{(i)}, y^{(i)}); i = 1, \ldots, m}$，$(x^{(i)}, y^{(i)})$ 来自同一分布 $\mathcal{D}$ 的样本，对于一个假设 $h$，定义训练误差（或经验风险）：&lt;/p&gt;
$$
\hat{\epsilon}(h) = \frac{1}{m} \sum_{i=1}^{m} 1\{h(x^{(i)}) \ne y^{(i)}\}
$$&lt;p&gt;不难看出，上述量即为 $h$ 在训练集中误分类的比例。如果我们想明确表达 $\hat{\epsilon}(h)$ 和某个训练集 $S$ 的关系，我们可以写为 $\hat{\epsilon}_S(h)$。此外，我们还定义泛化误差为：&lt;/p&gt;
$$
\epsilon(h) = P_{(x,y)\sim \mathcal{D}}(h(x) \ne y)
$$&lt;p&gt;即为从分布 $\mathcal{D}$ 中抽取一个样本 $(x,y)$，$h$ 分类错误的概率。我们的学习算法实际上是在做经验风险最小化（ERM），即找到&lt;/p&gt;
$$
\hat{h} = \arg\min_{h \in \mathcal{H}} \hat{\epsilon}(h)
$$&lt;h2 id="有限个假设的情况"&gt;有限个假设的情况
&lt;/h2&gt;&lt;p&gt;我们先开始考虑假设类有限的学习问题，假设 $\mathcal{H} = {h_1, \ldots, h_k}$ 有 $k$ 个将 $x$ 映射到 ${0,1}$ 的函数，经验风险最小化即为从 $k$ 个假设中选择训练误差最小的函数。&lt;/p&gt;
&lt;p&gt;现在任取 $h_i \in \mathcal{H}$，对于 $\mathcal{D}$ 中样本 $(x,y)$，考虑伯努利随机变量
$Z = 1{h_i(x) \ne y}$，类似地定义 $Z_j = 1{h_i(x^{(j)}) \ne y^{(j)}}$，&lt;/p&gt;
&lt;p&gt;由于我们的样本是从 $\mathcal{D}$ 获得的独立同分布随机变量，所以 $Z, Z_j$ 同分布。此外，训练误差可以写为&lt;/p&gt;
$$
\hat{\epsilon}(h_i) = \frac{1}{m} \sum_{j=1}^{m} Z_j
$$&lt;p&gt;因此，$\hat{\epsilon}(h_i)$ 是 $m$ 个随机变量的均值，这些随机变量独立同分布，都来自均值为 $\epsilon(h)$ 的伯努利分布，所以我们可以使用 Hoeffding 不等式，得到&lt;/p&gt;
$$
P(|\epsilon(h_i) - \hat{\epsilon}(h_i)| &gt; \gamma) \le 2 \exp(-2\gamma^2 m)
$$&lt;p&gt;这说明对于某个特定的 $h_i$，当 $m$ 很大时，其训练误差和泛化误差非常接近。但是我们不仅仅希望 $\epsilon(h_i)$ 和 $\hat{\epsilon}(h_i)$ 很接近，我们希望对每个 $h \in \mathcal{H}$ 上述事实都成立。令 $A_i$ 表示事件
$|\epsilon(h_i) - \hat{\epsilon}(h_i)| &amp;gt; \gamma$，我们已经证明了&lt;/p&gt;
$$
P(A_i) \le 2 \exp(-2\gamma^2 m)
$$&lt;p&gt;因此使用第一个引理可得&lt;/p&gt;
$$
\begin{aligned}
P(\exists h_i \in \mathcal{H}, |\epsilon(h_i) - \hat{\epsilon}(h_i)| &gt; \gamma)&amp;= P(A_1 \cup \cdots \cup A_k) \\&amp;\le \sum_{i=1}^{k} P(A_i) \\&amp;\le \sum_{i=1}^{k} 2 \exp(-2\gamma^2 m) \\&amp;\le 2k \exp(-2\gamma^2 m)
\end{aligned}
$$&lt;p&gt;两边同时减 1 得&lt;/p&gt;
$$
P(\forall h_i \in \mathcal{H}, |\epsilon(h_i) - \hat{\epsilon}(h_i)| \le \gamma)\ge 1 - 2k \exp(-2\gamma^2 m)
$$&lt;p&gt;上述不等式的含义为，有至少 $1 - 2k \exp(-2\gamma^2 m)$ 的概率，对任意 $h \in \mathcal{H}$，$\epsilon(h)$ 落在 $\hat{\epsilon}(h)$ 附近的 $\gamma$ 范围内，这被称为一致收敛，因为上界对每个 $h \in \mathcal{H}$ 成立。注意上述结论中有 3 个重要的量——$m, \gamma$ 以及概率，其中一个量可以用另外两个量来约束。&lt;/p&gt;
&lt;p&gt;例如考虑如下问题，给定 $\gamma, \delta$，$m$ 需要多大，才能保证至少有 $1 - \delta$ 概率使得训练误差在泛化误差 $\gamma$ 范围内，我们令&lt;/p&gt;
$$
1 - 2k \exp(-2\gamma^2 m) \ge 1 - \delta
$$&lt;p&gt;可得&lt;/p&gt;
$$
2k \exp(-2\gamma^2 m) \le \delta\;\Rightarrow\;m \ge \frac{1}{2\gamma^2} \log \frac{2k}{\delta}
$$&lt;p&gt;这说明当 $m \ge \frac{1}{2\gamma^2} \log \frac{2k}{\delta}$ 时，我们有 $1 - \delta$ 的概率使得训练误差在泛化误差 $\gamma$ 范围内。&lt;/p&gt;
&lt;p&gt;类似的，我们可以固定 $m$ 和 $\delta$，在之前的问题中解出 $\gamma$，令&lt;/p&gt;
$$
1 - 2k \exp(-2\gamma^2 m) \ge 1 - \delta
$$&lt;p&gt;可得&lt;/p&gt;
$$
\gamma \ge \sqrt{\frac{1}{2m} \log \frac{2k}{\delta}}
$$&lt;p&gt;取
$\gamma = \sqrt{\frac{1}{2m} \log \frac{2k}{\delta}}$，
我们有 $1 - \delta$ 的概率使得
&lt;/p&gt;
$$
|\epsilon(h) - \hat{\epsilon}(h)| \le \sqrt{\frac{1}{2m} \log \frac{2k}{\delta}}
$$&lt;p&gt;现在假设一致收敛成立，即 $|\epsilon(h) - \hat{\epsilon}(h)| \le \gamma$ 对每个 $h \in \mathcal{H}$，接下来证明 $\hat{h} = \arg\min_{h \in \mathcal{H}} \hat{\epsilon}(h)$ 的泛化误差上界。定义 $h&amp;rsquo; = \arg\min_{h \in \mathcal{H}} \epsilon(h)$，注意 $h&amp;rsquo;$ 为最优假设，那么&lt;/p&gt;
$$
\begin{aligned}
\epsilon(\hat{h})&amp;\le \hat{\epsilon}(\hat{h}) + \gamma \\&amp;\le \hat{\epsilon}(h') + \gamma \\&amp;\le \epsilon(h') + 2\gamma
\end{aligned}
$$&lt;p&gt;第一个和第三个不等号是因为 $|\epsilon(h) - \hat{\epsilon}(h)| \le \gamma$ 对每个 $h \in \mathcal{H}$ 都成立，第二个不等号是因为 $\hat{h} = \arg\min_{h \in \mathcal{H}} \hat{\epsilon}(h)$，所以如果一致收敛成立，那么 $\hat{h}$ 的泛化误差最多和最优假设相差 $2\gamma$。&lt;/p&gt;
&lt;p&gt;将以上内容总结为定理：&lt;/p&gt;
&lt;h3 id="定理"&gt;定理
&lt;/h3&gt;&lt;p&gt;令 $|\mathcal{H}| = k$，$m$ 和 $\delta$ 为任意固定值，那么至少有 $1 - \delta$ 的概率，我们有：&lt;/p&gt;
$$
\epsilon(\hat{h}) \le \left( \min_{h \in \mathcal{H}} \epsilon(h) \right)+ 2 \sqrt{\frac{1}{2m} \log \frac{2k}{\delta}}
$$&lt;p&gt;结合之前的讨论，固定 $\gamma, \delta$，当
$m \ge \frac{1}{2\gamma^2} \log \frac{2k}{\delta}$ 时，一致收敛成立，因此：
&lt;/p&gt;
$$
\epsilon(\hat{h}) \le \left( \min_{h \in \mathcal{H}} \epsilon(h) \right) + 2\gamma
$$&lt;h3 id="推论"&gt;推论
&lt;/h3&gt;&lt;p&gt;令 $|\mathcal{H}| = k$，$\gamma$ 和 $\delta$ 为任意的固定值，那么要使&lt;/p&gt;
$$
\epsilon(\hat{h}) \le \left( \min_{h \in \mathcal{H}} \epsilon(h) \right) + 2\gamma
$$&lt;p&gt;有大于等于 $1 - \delta$ 的概率发生，只要有&lt;/p&gt;
$$
m \ge \frac{1}{2\gamma^2} \log \frac{2k}{\delta}= O\left(\frac{1}{\gamma^2} \log \frac{k}{\delta}\right)
$$</description></item><item><title>CS229 Lecture 9</title><link>https://lunatide.tech/p/cs229-lecture-9/</link><pubDate>Mon, 09 Mar 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-9/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-9/pic1.jpg" alt="Featured image of post CS229 Lecture 9" /&gt;&lt;h2 id="贝叶斯统计和正则化"&gt;贝叶斯统计和正则化
&lt;/h2&gt;&lt;p&gt;在这个部分中，我们将讨论另一个对抗过拟合的工具。&lt;/p&gt;
&lt;p&gt;在本章开始的时候，我们利用最大似然估计讨论参数拟合，利用如下方式选择参数：&lt;/p&gt;
$$
\theta_{\mathrm{ML}} = \arg\max_{\theta} \prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}; \theta)
$$&lt;p&gt;在随后的讨论中，我们将 $\theta$ 视为一个未知的参数，这种将 $\theta$ 看成恒定但是未知的值是频率学派的观点。而另一种进行参数估计的方法是使用贝叶斯的视角，将 $\theta$ 视为一个未知的随机变量，在这种方法下，我们要给 $\theta$ 一个先验分布 $p(\theta)$，用来表达该参数的先验信息。给定训练集 ${(x^{(i)}, y^{(i)})}_{i=1}^{m}$，当我们需要对一个新的值进行预测的时候，我们可以计算参数的后验分布：&lt;/p&gt;
$$
p(\theta \mid S)= \frac{p(S \mid \theta)p(\theta)}{p(S)}= \frac{\left(\prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}, \theta)\right)p(\theta)}
{\int_{\theta}\left(\prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}, \theta)\right)p(\theta)\, d\theta}
\tag{1}
$$&lt;p&gt;上述等式中，$\prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}, \theta)$ 来自你使用的机器学习模型。&lt;/p&gt;
&lt;p&gt;如果有一个新的测试样本 $x$，然后要求我们对这个样本进行预测，我们可以用 $\theta$ 的后验分布计算每个分类标签的后验分布：&lt;/p&gt;
$$
p(y \mid x, S) = \int_{\theta} p(y \mid x, \theta)\, p(\theta \mid S)\, d\theta
\tag{2}
$$&lt;p&gt;其中 $p(y \mid x, \theta)$ 可以利用之前的等式计算出来。因此，如果我们的目标是预测给定 $x$，$y$ 的期望，那么我们将输出&lt;/p&gt;
$$
\mathbb{E}[y \mid x, S] = \int_y y p(y \mid x, S)\, dy
$$&lt;p&gt;我们叙述的过程可以被认为是在做“完全贝叶斯”预测，其中我们的预测是根据后验分布 $p(\theta \mid S)$ 计算均值得到的。不幸的是，这种后验分布的计算是非常困难的。这是因为需要在等式（1）中对（通常是高维的）$\theta$ 进行积分，通常不能以闭合形式完成。&lt;/p&gt;
&lt;p&gt;因此，在实际中我们常用点估计代替 $p(y \mid x, S)$。$\theta$ 的最大后验估计由下公式给出：&lt;/p&gt;
$$
\theta_{\mathrm{MAP}} = \arg\max_{\theta} \prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}, \theta)\, p(\theta)
\tag{3}
$$&lt;p&gt;注意到这个公式和最大似然估计的公式基本相同，除了最后增加了先验分布 $p(\theta)$。&lt;/p&gt;
&lt;p&gt;在实际中，$\theta$ 先验分布的常见选择是假设 $\theta \sim \mathcal{N}(0, \tau^2 I)$。使用这样的先验分布，拟合出来的参数 $\theta_{\mathrm{MAP}}$ 的范数将比用极大似然估计得到的参数 $\theta_{\mathrm{ML}}$ 小。在实际中，这使得贝叶斯最大后验估计相比较于最大似然估计更容易避免过拟合。&lt;/p&gt;
&lt;h2 id="感知机和大型边界分类器"&gt;感知机和大型边界分类器
&lt;/h2&gt;&lt;p&gt;在学习理论的最后一部分，我们将介绍一种不同的机器学习模型。在之前的内容中，我们考虑的都是批量学习，即给我们训练集去学习，然后在测试集上评估假设 $h$。在这一部分中，我们将考虑在线学习，这种算法会在学习的过程中给出预测。&lt;/p&gt;
&lt;p&gt;回顾感知机算法，参数为 $\theta \in \mathbb{R}^{n+1}$，根据如下公式预测&lt;/p&gt;
$$
h_{\theta}(x) = g(\theta^T x)
\tag{1}
$$&lt;p&gt;其中&lt;/p&gt;
$$
g(z)=
\begin{cases}1, &amp; \text{if } z \ge 0 \\-1, &amp; \text{if } z &lt; 0
\end{cases}
$$&lt;p&gt;给定一个训练样本 $(x,y)$，感知机学习法则根据如下规则更新参数。如果 $h_{\theta}(x)=y$，那么不更新参数。否则，按如下方式更新参数&lt;/p&gt;
$$
\theta := \theta + yx
$$&lt;p&gt;这里 $y \in {+1,-1}$。&lt;/p&gt;
&lt;p&gt;下面一个定理给出感知机算法的误差上界，注意这个上界和样本序列个数 $m$ 以及数据维度 $n$ 没有明确地关系。&lt;/p&gt;
&lt;h3 id="定理"&gt;定理
&lt;/h3&gt;&lt;p&gt;设有一个样本序列 $(x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)}) \ldots (x^{(m)}, y^{(m)})$，假设对每个 $i$ 都有 $|x^{(i)}| \le D$，此外存在单位长度的向量 $u$（$|u|_2 = 1$）使得对序列中每个样本都有 $y^{(i)} \cdot (u^T x^{(i)}) \ge \gamma$，（例如，$u^T x^{(i)} \ge \gamma$，如果 $y^{(i)}=1$，并且 $u^T x^{(i)} \le -\gamma$，如果 $y^{(i)}=-1$，因此以至少 $\gamma$ 的距离分开样本数据）。那么感知机算法在序列上给出错误总数最多是 $(D/\gamma)^2$。&lt;/p&gt;
&lt;p&gt;证明：感知机只在犯错误的样本上更新权重。令 $\theta^{(k)}$ 为犯第 $k$ 个错误时候的权重。则 $\theta^{(1)}=\vec{0}$（因为权重被初始化为 0），并且如果第 $k$ 个错误发生在样本 $(x^{(i)}, y^{(i)})$ 上，那么 $g((x^{(i)})^T \theta^{(k)}) \ne y^{(i)}$，这意味着&lt;/p&gt;
$$
(x^{(i)})^T \theta^{(k)} y^{(i)} \le 0
\tag{2}
$$&lt;p&gt;另外根据感知机的学习法则，我们有 $\theta^{(k+1)} = \theta^{(k)} + y^{(i)}x^{(i)}$，所以&lt;/p&gt;
$$
\begin{aligned}
(\theta^{(k+1)})^T u&amp;= (\theta^{(k)})^T u + y^{(i)}(x^{(i)})^T u \\&amp;\ge (\theta^{(k)})^T u + \gamma
\end{aligned}
$$&lt;p&gt;利用归纳法可得&lt;/p&gt;
$$
(\theta^{(k+1)})^T u \ge k\gamma
\tag{3}
$$&lt;p&gt;另外，我们有&lt;/p&gt;
$$
\begin{aligned}
\|\theta^{(k+1)}\|^2&amp;= \|\theta^{(k)} + y^{(i)}x^{(i)}\|^2 \\&amp;= \|\theta^{(k)}\|^2 + \|x^{(i)}\|^2 + 2y^{(i)}(x^{(i)})^T \theta^{(k)} \\&amp;\le \|\theta^{(k)}\|^2 + \|x^{(i)}\|^2 \\&amp;\le \|\theta^{(k)}\|^2 + D^2
\end{aligned}
\tag{4}
$$&lt;p&gt;其中第一个不等号是因为公式（2），利用归纳法可得&lt;/p&gt;
$$
\|\theta^{(k+1)}\|^2 \le kD^2
\tag{5}
$$&lt;p&gt;结合（3）和（4）可得&lt;/p&gt;
$$
\begin{aligned}
\sqrt{k}D&amp;\ge \|\theta^{(k+1)}\| \\&amp;\ge (\theta^{(k+1)})^T u \\&amp;\ge k\gamma
\end{aligned}
$$&lt;p&gt;第二个不等号是因为 $u$ 是单位向量（并且 $z^T u = |z| \cdot |u| \cos \theta \le |z| \cdot |u|$，其中 $\theta$ 是 $z$ 和 $u$ 的夹角）。上述结果表明&lt;/p&gt;
$$
k \le (D/\gamma)^2
$$&lt;p&gt;因此，如果感知机犯了第 $k$ 个错误，那么必有 $k \le (D/\gamma)^2$。&lt;/p&gt;</description></item><item><title>CS229 Lecture 8</title><link>https://lunatide.tech/p/cs229-lecture-8/</link><pubDate>Sun, 01 Mar 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-8/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-8/pic1.jpg" alt="Featured image of post CS229 Lecture 8" /&gt;&lt;h2 id="核方法"&gt;核方法
&lt;/h2&gt;&lt;p&gt;回到我们对线性回归的讨论中，我们遇到了一个问题，其中输入 $x$ 是房子的生活区域。我们考虑使用特征 $x^2$ 和 $x^3$ 进行回归以获得二次函数。为了区分这两组变量，我们将“原始”输入值称为问题的输入属性（在本例中为 $x$，生活区域）。当它被映射到一组新的最终传递给学习算法时，我们将这些新的量称为输入特征。（不幸的是，不同的作者使用不同的术语来描述这两件事，但是我们将在这份讲义中一致地使用这个术语。）我们还将 $\phi$ 表示特征映射，它将属性映射到特征。例如，在我们的例子中，我们有：&lt;/p&gt;
$$
\phi(x) =
\begin{bmatrix}
x \\
x^2 \\
x^3
\end{bmatrix}
$$&lt;p&gt;我们可能想使用某些特征 $\phi(x)$ 来学习，而不是使用原始输入属性 $x$ 来使用 SVM。要做到这一点，我们只需要检查我们之前的算法，并用 $\phi(x)$ 替换其中的 $x$。&lt;/p&gt;
&lt;p&gt;由于算法可以完全根据内积 $\langle x, z \rangle$ 编写，这意味着我们将用 $\langle \phi(x), \phi(z) \rangle$ 替换所有这些内积。特别地，给定特征映射 $\phi$，我们将相应的核定义为：&lt;/p&gt;
$$
K(x, z) = \phi(x)^T \phi(z)
$$&lt;p&gt;然后，在我们之前在算法中有 $\langle x, z \rangle$ 的地方，我们可以简单地用 $K(x, z)$ 替换它。我们的算法现在将在使用特征 $\phi$ 的情况下学习。&lt;/p&gt;
&lt;p&gt;现在，给定 $\phi$，我们可以通过计算 $\phi(x)$ 和 $\phi(z)$ 并求其内积来计算 $K(x, z)$。但更有趣的是，通常，$K(x, z)$ 的计算成本可能非常低廉，即使 $\phi(x)$ 本身的计算成本非常高（可能因为它是一个维数很高的向量）。在这样的情况下，通过使用高效算法计算 $K(x, z)$，我们可以使 SVM 在给出的高维特征空间中学习，但同时不需要显式地计算 $\phi(x)$。&lt;/p&gt;
&lt;p&gt;来看一个例子。假设 $x, z \in \mathbb{R}^n$，并考虑：&lt;/p&gt;
$$
K(x, z) = (x^T z)^2
$$&lt;p&gt;上式也可以写为&lt;/p&gt;
$$
\begin{aligned}
K(x, z)
&amp;= \left( \sum_{i=1}^{n} x_i z_i \right)
\left( \sum_{j=1}^{n} x_j z_j \right) \\
&amp;= \sum_{i=1}^{n} \sum_{j=1}^{n} x_i x_j z_i z_j \\
&amp;= \sum_{i,j=1}^{n} (x_i x_j)(z_i z_j)
\end{aligned}
$$&lt;p&gt;因此，我们看到 $K(x, z) = \phi(x)^T \phi(z)$，其中的特征映射（此处针对 $n=3$ 的情况给出）为：&lt;/p&gt;
$$
\phi(x) =
\begin{bmatrix}
x_1 x_1 \\
x_1 x_2 \\
x_1 x_3 \\
x_2 x_1 \\
x_2 x_2 \\
x_2 x_3 \\
x_3 x_1 \\
x_3 x_2 \\
x_3 x_3
\end{bmatrix}
$$&lt;p&gt;注意，虽然计算 $\phi(x)$ 需要 $O(n^2)$ 的时间，但是计算 $K(x, z)$ 只需要 $O(n)$ 时间——和输入属性的维度呈线性关系。&lt;/p&gt;
&lt;p&gt;对于相关的核，考虑：&lt;/p&gt;
$$
\begin{aligned}
K(x, z)&amp;= (x^T z + c)^2 \\&amp;= \left( \sum_{i=1}^{n} x_i z_i + c \right)^2 \\&amp;= \sum_{i,j=1}^{n} (x_i x_j)(z_i z_j)+ \sum_{i=1}^{n} (\sqrt{2c}\, x_i)(\sqrt{2c}\, z_i)+ c^2
\end{aligned}
$$&lt;p&gt;
相应的特征映射为（再次给出 $n = 3$ 的情形）&lt;/p&gt;
$$
\phi(x) =
\begin{bmatrix}
x_1 x_1 \\
x_1 x_2 \\
x_1 x_3 \\
x_2 x_1 \\
x_2 x_2 \\
x_2 x_3 \\
x_3 x_1 \\
x_3 x_2 \\
x_3 x_3 \\
\sqrt{2c}\, x_1 \\
\sqrt{2c}\, x_2 \\
\sqrt{2c}\, x_3 \\
c
\end{bmatrix}
$$&lt;p&gt;参数 $c$ 控制 $x_i$（一阶）和 $x_i x_j$（二阶）项之间的相对权重。&lt;/p&gt;
&lt;p&gt;进一步推广，核
&lt;/p&gt;
$$
K(x, z) = (x^T z + c)^d
$$&lt;p&gt;
对应于映射到 $\binom{n+d}{d}$ 维特征空间的特征映射，对应于形式为 $x_{i_1} x_{i_2} \dots x_{i_d}$ 的所有次数最多为 $d$ 的单项式。然而，尽管在这个 $O(n^d)$ 维空间中，计算 $K(x, z)$ 仍然只需要 $O(n)$ 的时间，因此我们永远不需要在这个非常高维的特征空间中明确地表示特征向量。&lt;/p&gt;
&lt;p&gt;现在，让我们从不同的角度谈论核。直观地，如果 $\phi(x)$ 和 $\phi(z)$ 靠得很近，那么我们可以期望
&lt;/p&gt;
$$
K(x, z) = \phi(x)^T \phi(z)
$$&lt;p&gt;
很大。相反，如果 $\phi(x)$ 和 $\phi(z)$ 相距很远，比如几乎彼此正交，则
&lt;/p&gt;
$$
K(x, z) = \phi(x)^T \phi(z)
$$&lt;p&gt;
将很小。因此，我们可以将 $K(x, z)$ 视为对 $\phi(x)$ 和 $\phi(z)$ 或者 $x$ 和 $z$ 之间相似度的度量。&lt;/p&gt;
&lt;p&gt;鉴于这种直觉，假设对于你正在研究的一些学习问题，你已经提出了某个函数 $K(x, z)$，你认为它可能是 $x$ 和 $z$ 相似程度的合理度量。例如，也许你选择了&lt;/p&gt;
$$
K(x, z) = \exp \left( - \frac{\lVert x - z \rVert^2}{2\sigma^2} \right)
$$&lt;p&gt;这是 $x$ 和 $z$ 相似性的合理度量，当 $x$ 和 $z$ 接近时，上式接近 $1$，当 $x$ 和 $z$ 相距很远时，上式接近 $0$。我们可以将这个 $K$ 用作 SVM 中的核吗？在这个特定的例子中，答案是肯定的。（这个内核称为高斯核，对应于无限维特征映射。）&lt;/p&gt;
&lt;p&gt;更一般地说，给定某个函数 $K$，我们如何判断它是否是一个有效的核；即，我们如何判断是否存在某个特征映射 $\phi$，使得对于所有 $x, z$，都有
&lt;/p&gt;
$$
K(x, z) = \phi(x)^T \phi(z)
$$&lt;p&gt;现在假设 $K$ 确实对应于某个特征映射 $\phi$ 的有效核。现在，考虑有限的 $m$ 点（不一定是训练集）${x^{(1)}, \dots, x^{(m)}}$，并且定义 $m \times m$ 矩阵 $K$，使得其第 $(i, j)$ 个元素为 $K(x^{(i)}, x^{(j)})$。该矩阵称为核矩阵。请注意，由于核函数 $K(x, z)$ 和核矩阵 $K$ 之间存在明显的密切关系，我们经常滥用符号使用 $K$ 来表示核函数 $K(x, z)$ 和核矩阵 $K$。&lt;/p&gt;
&lt;p&gt;现在，如果 $K$ 是有效的核，则&lt;/p&gt;
$$
K_{ij}=
K(x^{(i)}, x^{(j)})=
\phi(x^{(i)})^T \phi(x^{(j)})=
\phi(x^{(j)})^T \phi(x^{(i)})=
K(x^{(j)}, x^{(i)})=
K_{ji}
$$&lt;p&gt;因此 $K$ 是对称矩阵。更重要的是，令 $\phi_k(x)$ 表示向量 $\phi(x)$ 的第 $k$ 个坐标，我们发现对于任何向量 $z$，我们都有&lt;/p&gt;
$$
\begin{aligned}
z^T K z&amp;=
\sum_i \sum_j z_i K_{ij} z_j \\&amp;=
\sum_i \sum_j z_i \phi(x^{(i)})^T \phi(x^{(j)}) z_j \\&amp;=
\sum_i \sum_j z_i \sum_k \phi_k(x^{(i)}) \phi_k(x^{(j)}) z_j \\&amp;=
\sum_k \sum_i \sum_j z_i \phi_k(x^{(i)}) \phi_k(x^{(j)}) z_j \\&amp;=
\sum_k \left( \sum_i z_i \phi_k(x^{(i)}) \right)^2 \\&amp;\ge 0
\end{aligned}
$$&lt;p&gt;由于 $z$ 是任意的，这表明 $K$ 是半正定的（$K \ge 0$）。&lt;/p&gt;
&lt;p&gt;因此，我们已经证明，如果 $K$ 是有效核（即，如果它对应于某些特征映射 $\phi$），则相应的核矩阵 $K \in \mathbb{R}^{m \times m}$ 是半正定对称矩阵。更一般地说，这不仅是 $K$ 成为有效核（也称为 Mercer 核）的必要条件，而且是充分条件。以下结果归功于 Mercer。&lt;/p&gt;
&lt;h3 id="定理mercer"&gt;定理（Mercer）
&lt;/h3&gt;&lt;p&gt;设 $K \in \mathbb{R}^n \times \mathbb{R}^n \to \mathbb{R}$。那么 $K$ 是有效（Mercer）核的充要条件是对任意 ${x^{(1)}, \dots, x^{(m)}}$（$m &amp;lt; \infty$），相应的核矩阵是正半定对称矩阵。&lt;/p&gt;
&lt;p&gt;给定函数 $K$，除了试图找到与其对应的特征映射 $\phi$ 之外，该定理还提供了另一种测试它是否是有效核的方法。&lt;/p&gt;
&lt;p&gt;核在支持向量机方面的应用应该已经很清楚了，所以我们不会在这里花太多时间。但请记住，核的概念比 SVM 具有更广泛的适用性。具体来说，如果你有任何学习算法只需要输入属性向量之间的内积 $\langle x, z \rangle$ 来编写，那么用 $K(x, z)$ 替换它，其中 $K$ 是一个核，你可以“神奇地”允许你的算法在对应于 $K$ 的高维特征空间中有效地工作。例如，该技巧可以与感知机一起应用以导出核感知机算法。我们将在本课程后面看到的许多算法也适用于这种方法，后者被称为“核技巧”。&lt;/p&gt;
&lt;h2 id="正则化和不可分的情形"&gt;正则化和不可分的情形
&lt;/h2&gt;&lt;p&gt;到目前为止所呈现的 SVM 的推导都是假设数据是线性可分的。虽然通过将数据映射到高维特征空间通常会增加数据可分的可能性，但我们无法保证它始终如此。此外，在某些情况下，我们并不希望找到分离超平面是我们想要的，因为这可能容易受到异常值的影响。例如，下图左边显示了一个最佳间隔分类器，当在左上角区域（右图）中添加了一个异常值时，它会使决策边界产生戏剧性的移动，并且得到的分类器的间隔小得多。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-8/p1.jpg"
width="681"
height="262"
srcset="https://lunatide.tech/p/cs229-lecture-8/p1_hu_c959586d10182c8a.jpg 480w, https://lunatide.tech/p/cs229-lecture-8/p1_hu_37f59065253d5c14.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="259"
data-flex-basis="623px"
&gt;&lt;/p&gt;
&lt;p&gt;为了使算法适用于线性不可分数据集以及对异常值不太敏感，我们重新设置我们的优化问题（使用 $\ell_1$ 正则化），如下所示：&lt;/p&gt;
$$
\begin{aligned}
\min_{w,b,\xi} \quad &amp; \frac{1}{2} \lVert w \rVert^2 + C \sum_{i=1}^{m} \xi_i \\
\text{s.t.} \quad
&amp; y^{(i)} (w^T x^{(i)} + b) \ge 1 - \xi_i, \quad i = 1, \dots, m \\
&amp; \xi_i \ge 0, \quad i = 1, \dots, m
\end{aligned}
$$&lt;p&gt;因此，现在允许样本具有小于 $1$ 的（函数）间隔，并且如果某样本具有函数间隔 $1 - \xi_i$（$\xi_i &amp;gt; 0$），则我们付出的代价为目标函数的成本增加 $C \xi_i$。参数 $C$ 控制了使 $\lVert w \rVert^2$ 变小（我们之前看到的这项使间隔变大）和确保大多数样本具有至少 $1$ 的函数间隔的双重目标之间的相对权重。&lt;/p&gt;
&lt;p&gt;将上述条件修改为&lt;/p&gt;
$$
\begin{aligned}
1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) &amp;\le 0, \quad i = 1, \dots, m \\
-\xi_i &amp;\le 0, \quad i = 1, \dots, m
\end{aligned}
$$&lt;p&gt;我们可以得到拉格朗日算子&lt;/p&gt;
$$
\begin{aligned}
\mathcal{L}(w,b,\xi,\alpha,r)&amp;= \frac{1}{2} \lVert w \rVert^2+ C \sum_{i=1}^{m} \xi_i \\&amp;\quad - \sum_{i=1}^{m} \alpha_i \big[ y^{(i)} (w^T x^{(i)} + b) - 1 + \xi_i \big]- \sum_{i=1}^{m} r_i \xi_i
\end{aligned}
$$&lt;p&gt;这里，$\alpha_i$ 和 $r_i$ 是我们的拉格朗日乘子（约束为 $\ge 0$）。求偏导并令其为 $0$ 可得：&lt;/p&gt;
$$
\nabla_w \mathcal{L}(w,b,\xi,\alpha,r)=
w - \sum_{i=1}^{m} \alpha_i y^{(i)} x^{(i)} = 0
$$$$
\nabla_b \mathcal{L}(w,b,\xi,\alpha,r)=
\sum_{i=1}^{m} \alpha_i y^{(i)} = 0
$$$$
\nabla_{\xi_i} \mathcal{L}(w,b,\xi,\alpha,r)=
C - \alpha_i - r_i = 0
$$&lt;p&gt;化简第一个式子可得&lt;/p&gt;
$$
w = \sum_{i=1}^{m} \alpha_i y^{(i)} x^{(i)}
$$&lt;p&gt;带入原式可得&lt;/p&gt;
$$
\begin{aligned}
\mathcal{L}(w,b,\xi,\alpha,r)&amp;= \sum_{i=1}^{m} \alpha_i- \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{m}
y^{(i)} y^{(j)} \alpha_i \alpha_j
(x^{(i)})^T x^{(j)} \\
&amp;\quad - b \sum_{i=1}^{m} \alpha_i y^{(i)}+ \sum_{i=1}^{m} (C - \alpha_i - r_i) \xi_i
\end{aligned}
$$&lt;p&gt;注意到&lt;/p&gt;
$$
\sum_{i=1}^{m} \alpha_i y^{(i)} = 0,
\qquad
\alpha_i + r_i = C
$$&lt;p&gt;所以&lt;/p&gt;
$$
\mathcal{L}(w,b,\xi,\alpha,r)=
\sum_{i=1}^{m} \alpha_i- \frac{1}{2} \sum_{i=1}^{m} \sum_{j=1}^{m}
y^{(i)} y^{(j)} \alpha_i \alpha_j
(x^{(i)})^T x^{(j)}
$$&lt;p&gt;由 $\xi_i \ge 0$ 且 $r_i \ge 0$ 可得&lt;/p&gt;
$$
\alpha_i \le C,
\qquad
\alpha_i \ge 0
$$&lt;p&gt;因此对偶问题为&lt;/p&gt;
$$
\begin{aligned}
\max_{\alpha} \quad
W(\alpha)&amp;=
\sum_{i=1}^{m} \alpha_i- \frac{1}{2}
\sum_{i=1}^{m} \sum_{j=1}^{m}
y^{(i)} y^{(j)} \alpha_i \alpha_j
(x^{(i)})^T x^{(j)} \\
\text{s.t.} \quad
&amp; 0 \le \alpha_i \le C, \quad i = 1, \dots, m \\
&amp; \sum_{i=1}^{m} \alpha_i y^{(i)} = 0
\end{aligned}
$$&lt;p&gt;和以前一样，我们像公式 (9) 那样用 $\alpha$ 表示 $w$。这样在求解对偶问题后，我们可以继续使用公式 (13) 进行预测。注意，在添加 $\ell_1$ 正则化后，对偶问题的唯一变化是原来的约束 $0 \le \alpha_i$ 现在变为 $0 \le \alpha_i \le C$；注意此时 $b$ 的计算也必须修改。&lt;/p&gt;
&lt;p&gt;此外，KKT 对偶互补条件（在下一节中将有助于测试 SMO 算法的收敛）是：&lt;/p&gt;
$$
\alpha_i = 0
\;\Rightarrow\;
y^{(i)} (w^T x^{(i)} + b) \ge 1
\tag{14}
$$$$
\alpha_i = C
\;\Rightarrow\;
y^{(i)} (w^T x^{(i)} + b) \le 1
\tag{15}
$$$$
0 &lt; \alpha_i &lt; C
\;\Rightarrow\;
y^{(i)} (w^T x^{(i)} + b) = 1
\tag{16}
$$&lt;p&gt;上述等式的证明如下：&lt;/p&gt;
&lt;p&gt;首先由上一讲的等式可得互补条件&lt;/p&gt;
$$
\alpha_i \big( 1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) \big) = 0
$$&lt;p&gt;以及&lt;/p&gt;
$$
r_i \xi_i = 0
$$&lt;p&gt;并且有&lt;/p&gt;
$$
1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) \le 0,
\qquad
\xi_i \ge 0
$$&lt;p&gt;如果 $\alpha_i = 0$，则由&lt;/p&gt;
$$
\alpha_i + r_i = C
$$&lt;p&gt;可得&lt;/p&gt;
$$
r_i = C
$$&lt;p&gt;因此 $r_i &amp;gt; 0$，由 $r_i \xi_i = 0$ 得&lt;/p&gt;
$$
\xi_i = 0
$$&lt;p&gt;由约束&lt;/p&gt;
$$
1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) \le 0
$$&lt;p&gt;可得&lt;/p&gt;
$$
y^{(i)} (w^T x^{(i)} + b) \ge 1
$$&lt;p&gt;如果 $\alpha_i = C$，则&lt;/p&gt;
$$
r_i = C - \alpha_i = 0
$$&lt;p&gt;此时由&lt;/p&gt;
$$
\alpha_i \big( 1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) \big) = 0
$$&lt;p&gt;且 $\alpha_i = C &amp;gt; 0$，得到&lt;/p&gt;
$$
1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) = 0
$$&lt;p&gt;即&lt;/p&gt;
$$
y^{(i)} (w^T x^{(i)} + b) = 1 - \xi_i \le 1
$$&lt;p&gt;如果 $0 &amp;lt; \alpha_i &amp;lt; C$，则由&lt;/p&gt;
$$
\alpha_i + r_i = C
$$&lt;p&gt;可知&lt;/p&gt;
$$
r_i &gt; 0
$$&lt;p&gt;由 $r_i \xi_i = 0$ 得&lt;/p&gt;
$$
\xi_i = 0
$$&lt;p&gt;再由&lt;/p&gt;
$$
\alpha_i \big( 1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) \big) = 0
$$&lt;p&gt;且 $\alpha_i &amp;gt; 0$，得到&lt;/p&gt;
$$
1 - \xi_i - y^{(i)} (w^T x^{(i)} + b) = 0
$$&lt;p&gt;因为 $\xi_i = 0$，于是&lt;/p&gt;
$$
y^{(i)} (w^T x^{(i)} + b) = 1
$$&lt;p&gt;现在，剩下的就是给出一个实际解决对偶问题的算法，我们将在下一节中介绍。&lt;/p&gt;</description></item><item><title>CS229 Lecture 7</title><link>https://lunatide.tech/p/cs229-lecture-7/</link><pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-7/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-7/pic1.jpg" alt="Featured image of post CS229 Lecture 7" /&gt;&lt;h2 id="最优间隔分类器"&gt;最优间隔分类器
&lt;/h2&gt;&lt;p&gt;给定一个训练集，从我们之前的讨论中可以看出，一个自然的想法是试图找到一个最大化（几何）间隔的决策边界，因为这将反映出对训练集的一个非常有信心的预测和一个对训练数据良好的“拟合”。具体而言，这将导致分类器将正和负训练样本用一个“间隙”（几何间隔）分开。&lt;/p&gt;
&lt;p&gt;现在，假设我们得到一个线性可分的训练集；即，可以使用某个分离超平面分离正负样本。我们如何找到达到最大几何间隔的那个？我们可以提出以下优化问题：&lt;/p&gt;
$$
\max_{\gamma, w, b} \ \gamma
$$$$
\text{s.t.} \quad y^{(i)}(w^T x^{(i)} + b) \ge \gamma, \quad i = 1, \dots, m
$$$$
\|w\| = 1
$$&lt;p&gt;即，我们希望最大化 $\gamma$，使得每个训练样本具有至少 $\gamma$ 的函数间隔。$|w| = 1$ 的约束确保函数间隔等于几何间隔，因此我们也保证所有几何间隔至少为 $\gamma$。因此，解决该问题将导致 $(w,b)$ 相对于训练集具有最大可能的几何间隔。&lt;/p&gt;
&lt;p&gt;如果我们能解决上面的优化问题，我们就完成了。但是 $|w| = 1$ 约束是一个棘手的（非凸的）条件，并且这个问题肯定不是我们可以使用标准优化软件来解决的任何格式。所以，让我们尝试将问题转化为更好的问题。考虑：&lt;/p&gt;
$$
\max_{\hat{\gamma}, w, b} \ \frac{\hat{\gamma}}{\|w\|}
$$$$
\text{s.t.} \quad y^{(i)}(w^T x^{(i)} + b) \ge \hat{\gamma}, \quad i = 1, \dots, m
$$&lt;p&gt;这里，我们将最大化 $\hat{\gamma}/|w|$，使得函数间隔都至少为 $\hat{\gamma}$。由于几何间隔和函数间隔的关系为 $\gamma = \hat{\gamma}/|w|$，这将给出我们想要的答案。此外，我们已经摆脱了约束 $|w| = 1$。缺点是我们仍然有一个棘手的（再次，非凸）目标函数 $\hat{\gamma}/|w|$；而且，我们仍然没有任何可以解决这种形式的优化问题的现成软件。&lt;/p&gt;
&lt;p&gt;回想一下我们之前的讨论，我们可以在 $w$ 和 $b$ 上添加任意缩放约束而不改变任何东西。这是我们现在使用的关键想法。我们将引入缩放约束，即 $w,b$ 相对于训练集的函数间隔必须为 1：&lt;/p&gt;
$$
\hat{\gamma} = 1
$$&lt;p&gt;由于将 $w$ 和 $b$ 乘以某个常数将导致函数间隔乘以相同的常数（这是缩放约束），并且实际上我们可以进行重新缩放 $w,b$ 的操作。对上述问题应用此性质，并注意到最大化 $\gamma/|w| = 1/|w|$ 与最小化 $|w|^2$ 相同，我们现在有以下优化问题：&lt;/p&gt;
$$
\min_{w,b} \ \frac{1}{2} \|w\|^2
$$$$
\text{s.t.} \quad y^{(i)}(w^T x^{(i)} + b) \ge 1, \quad i = 1, \dots, m
$$&lt;p&gt;我们现在已经将问题转化为可以高效解决的形式。以上是具有凸二次目标和仅有线性约束的优化问题。它的解为我们提供了&lt;strong&gt;最优间隔分类器&lt;/strong&gt;。这个优化问题可以使用商业二次规划（QP）程序来解决。&lt;/p&gt;
&lt;p&gt;虽然我们现在可以解决问题，但我们后面还是要讨论拉格朗日对偶问题。这将推导出我们的优化问题的对偶形式，它将在允许我们使用核方法（kernel）让最优间隔分类器在非常高维空间中高效运转方面发挥关键作用。对偶形式还将允许我们推导出一种有效的算法来解决上述优化问题，该优化问题通常比通用 QP 软件做得更好。&lt;/p&gt;
&lt;h2 id="拉格朗日对偶"&gt;拉格朗日对偶
&lt;/h2&gt;&lt;p&gt;让我们暂时搁置 SVM 和最大间隔分类器，然后讨论如何解决约束优化问题。&lt;/p&gt;
&lt;p&gt;考虑如下形式的问题：&lt;/p&gt;
$$
\min_{w} \ f(w)
$$$$
\text{s.t.} \quad h_i(w) = 0, \quad i = 1, \dots, l
$$&lt;p&gt;在这种方法中，我们将拉格朗日算子定义为&lt;/p&gt;
$$
\mathcal{L}(w, \beta)=
f(w)+
\sum_{i=1}^{l} \beta_i h_i(w)
$$&lt;p&gt;这里，$\beta_i$ 被称为拉格朗日乘子。然后我们求出 $\mathcal{L}$ 的偏导数并设为零：&lt;/p&gt;
$$
\frac{\partial \mathcal{L}}{\partial w_i} = 0,
\qquad
\frac{\partial \mathcal{L}}{\partial \beta_i} = 0
$$&lt;p&gt;接着求解 $w$ 和 $\beta$。&lt;/p&gt;
&lt;p&gt;在本节中，我们将此推广为带约束的优化问题，在这些问题中我们可能存在不等式以及等式约束。由于时间的限制，我们无法详细介绍拉格朗日二元对偶理论，但我们将给出主要思想和结果，然后应用于我们的最优间隔分类器的优化问题。&lt;/p&gt;
&lt;p&gt;考虑以下内容，我们将其称为&lt;strong&gt;原始优化问题&lt;/strong&gt;：&lt;/p&gt;
$$
\min_{w} \ f(w)
$$$$
\text{s.t.} \quad g_i(w) \le 0, \quad i = 1, \dots, k
$$$$
h_i(w) = 0, \quad i = 1, \dots, l
$$&lt;p&gt;为了解决这个问题，我们首先定义&lt;strong&gt;广义拉格朗日算子&lt;/strong&gt;：&lt;/p&gt;
$$
\mathcal{L}(w, \alpha, \beta)=
f(w)+
\sum_{i=1}^{k} \alpha_i g_i(w)+
\sum_{i=1}^{l} \beta_i h_i(w)
$$&lt;p&gt;这里，$\alpha_i$ 和 $\beta_i$ 是拉格朗日乘子。考虑&lt;/p&gt;
$$
\theta_P(w)=
\max_{\alpha, \beta : \alpha_i \ge 0}
\mathcal{L}(w, \alpha, \beta)
$$&lt;p&gt;这里，“$P$” 下标代表“原始”。假设给定某个 $w$。如果 $w$ 违反任何原始约束（即，对于某个 $i$，$g_i(w) &amp;gt; 0$ 或 $h_i(w) \neq 0$），那么可以验证&lt;/p&gt;
$$
\theta_P(w)=
\max_{\alpha, \beta : \alpha_i \ge 0}
\left(
f(w)+
\sum_{i=1}^{k} \alpha_i g_i(w)+
\sum_{i=1}^{l} \beta_i h_i(w)
\right)
\tag{1}
$$$$
= \infty
\tag{2}
$$&lt;p&gt;相反，如果对于特定的 $w$ 确实满足约束，则 $\theta_P(w) = f(w)$。因此，&lt;/p&gt;
$$
\theta_P(w)=
\begin{cases}
f(w) &amp; \text{如果 } w \text{ 满足原始约束} \\
\infty &amp; \text{其他}
\end{cases}
$$&lt;p&gt;因此，对于所有满足原始约束的 $w$，$\theta_P$ 与我们的问题中的目标具有相同的值，并且如果 $w$ 违反约束条件，那么 $\theta_P$ 为正无穷大。因此，如果我们考虑最小化问题&lt;/p&gt;
$$
\min_w \theta_P(w)=
\min_w \max_{\alpha,\beta:\alpha_i \ge 0} \mathcal{L}(w,\alpha,\beta)
$$&lt;p&gt;我们看到它与我们原始的原始问题是同一个问题（即具有相同的解）。为了以后的使用，我们还将目标的最优值定义为&lt;/p&gt;
$$
p' = \min_w \theta_P(w)
$$&lt;p&gt;我们称之为原始问题的值。&lt;/p&gt;
&lt;p&gt;现在，让我们看一个稍微不同的问题。我们定义&lt;/p&gt;
$$
\theta_D(\alpha,\beta)=
\min_w \mathcal{L}(w,\alpha,\beta)
$$&lt;p&gt;这里，下标 “$D$” 代表“对偶”。还要注意，在 $\theta_P$ 的定义中，我们关于 $\alpha,\beta$ 进行了优化（最大化），而这里是关于 $w$ 的最小化。&lt;/p&gt;
&lt;p&gt;我们现在可以提出对偶优化问题：&lt;/p&gt;
$$
\max_{\alpha,\beta:\alpha_i \ge 0} \theta_D(\alpha,\beta)=
\max_{\alpha,\beta:\alpha_i \ge 0} \min_w \mathcal{L}(w,\alpha,\beta)
$$&lt;p&gt;这与上面显示的原始问题完全相同，只是现在交换了 “max” 和 “min” 的顺序。我们还将对偶问题的目标的最优值定义为&lt;/p&gt;
$$
d' = \max_{\alpha,\beta:\alpha_i \ge 0} \theta_D(\alpha,\beta)
$$&lt;p&gt;原始和对偶问题之间有什么关系？很容易证明如下这一点：&lt;/p&gt;
$$
d'=
\max_{\alpha,\beta:\alpha_i \ge 0} \min_w \mathcal{L}(w,\alpha,\beta)
\le
\min_w \max_{\alpha,\beta:\alpha_i \ge 0} \mathcal{L}(w,\alpha,\beta)=
p'
$$&lt;p&gt;事实上，我们显然有&lt;/p&gt;
$$
\min_w \mathcal{L}(w,\alpha,\beta)
\le
\mathcal{L}(w,\alpha,\beta)
\le
\max_{\alpha,\beta:\alpha_i \ge 0} \mathcal{L}(w,\alpha,\beta)
$$&lt;p&gt;从而&lt;/p&gt;
$$
\min_w \mathcal{L}(w,\alpha,\beta)
\le
\min_w \max_{\alpha,\beta:\alpha_i \ge 0} \mathcal{L}(w,\alpha,\beta)=
p'
$$&lt;p&gt;并且&lt;/p&gt;
$$
d'=
\max_{\alpha,\beta:\alpha_i \ge 0} \min_w \mathcal{L}(w,\alpha,\beta)
\le
\min_w \max_{\alpha,\beta:\alpha_i \ge 0} \mathcal{L}(w,\alpha,\beta)=
p'
$$&lt;p&gt;但是，在某些条件下，我们会有&lt;/p&gt;
$$
d' = p'
$$&lt;p&gt;这样我们就可以解决对偶问题来代替原始问题。让我们看看这些条件是什么。&lt;/p&gt;
&lt;p&gt;假设 $f$ 和 $g_i$ 是凸函数（即 Hessian 矩阵是正定的），$h_i$ 是仿射函数（即 $h_i(w) = a_i^T w + b$）。进一步假设 $g_i$ 约束是（严格地）可行的；这意味着存在某个 $w$ 使得对所有的 $i$ 有 $g_i(w) &amp;lt; 0$。&lt;/p&gt;
&lt;p&gt;在我们的上述假设下，必将存在 $w&amp;rsquo;, \alpha&amp;rsquo;, \beta&amp;rsquo;$，使得 $w&amp;rsquo;$ 是原始问题的解，$\alpha&amp;rsquo;, \beta&amp;rsquo;$ 是对偶问题的解，而且&lt;/p&gt;
$$
p' = d' = \mathcal{L}(w', \alpha', \beta')
$$&lt;p&gt;此外，$w&amp;rsquo;, \alpha&amp;rsquo;, \beta&amp;rsquo;$ 满足 Karush-Kuhn-Tucker (KKT) 条件，如下：&lt;/p&gt;
$$
\frac{\partial}{\partial w_i} \mathcal{L}(w', \alpha', \beta') = 0,
\quad i = 1, \dots, n
\tag{3}
$$$$
\frac{\partial}{\partial \beta_i} \mathcal{L}(w', \alpha', \beta') = 0,
\quad i = 1, \dots, l
\tag{4}
$$$$
\alpha'_i g_i(w') = 0,
\quad i = 1, \dots, k
\tag{5}
$$$$
g_i(w') \le 0,
\quad i = 1, \dots, k
\tag{6}
$$$$
\alpha'_i \ge 0,
\quad i = 1, \dots, k
\tag{7}
$$&lt;p&gt;此外，如果某个 $w&amp;rsquo;, \alpha&amp;rsquo;, \beta&amp;rsquo;$ 满足 KKT 条件，那么它也是原始和对偶问题的解。&lt;/p&gt;
&lt;p&gt;我们注意方程 (5)，这被称为 &lt;strong&gt;KKT 对偶互补条件&lt;/strong&gt;。具体来说，它推出如果 $\alpha&amp;rsquo;_i &amp;gt; 0$，那么 $g_i(w&amp;rsquo;) = 0$。（即，“$g_i(w&amp;rsquo;) \le 0$” 约束是&lt;strong&gt;有效的&lt;/strong&gt;，这意味着它取等号而不是不等号。）稍后，这将是证明 SVM 只有少量“支持向量”的关键；当我们讨论 SMO 算法时，KKT 对偶互补条件也将为我们提供收敛性测试。&lt;/p&gt;
&lt;h2 id="最优间隔分类器-1"&gt;最优间隔分类器
&lt;/h2&gt;&lt;p&gt;之前，我们提出了以下（原始）优化问题来找到最优间隔分类器：&lt;/p&gt;
$$
\min_{w,b} \ \frac{1}{2}\|w\|^2
$$$$
\text{s.t.} \quad y^{(i)}(w^T x^{(i)} + b) \ge 1, \ i = 1, \dots, m
$$&lt;p&gt;我们可以将限制条件写成&lt;/p&gt;
$$
g_i(w) = -y^{(i)}(w^T x^{(i)} + b) + 1 \le 0
$$&lt;p&gt;我们对每个训练样本都有一个这样的约束。注意，根据 KKT 对偶互补条件，我们将仅对函数间隔等于 1 的训练样本具有 $\alpha_i &amp;gt; 0$（即对应于取等号的约束条件，$g_i(w) = 0$）。请看下图，其中实线表示具有最大间隔分离的超平面。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-7/p1.jpg"
width="493"
height="339"
srcset="https://lunatide.tech/p/cs229-lecture-7/p1_hu_23e88243116dffc.jpg 480w, https://lunatide.tech/p/cs229-lecture-7/p1_hu_bb1bfdf4d419014c.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="145"
data-flex-basis="349px"
&gt;&lt;/p&gt;
&lt;p&gt;间距最小的点恰好是最接近决策边界的点；这里，这些点是平行于决策边界的虚线上三个点（一个负样本和两个正样本）。因此，在我们的优化问题的最优解中，只有三个 $\alpha_i$，即对应于这三个训练样本的 $\alpha_i$ 将是非零的。这三个点在这个问题中被称为&lt;strong&gt;支持向量&lt;/strong&gt;。支持向量的数量比训练集的大小小得多的事实将在以后有用。&lt;/p&gt;
&lt;p&gt;让我们继续。当我们推导问题的对偶形式时，需要注意的一个关键想法是我们将尝试仅根据内积 $\langle x^{(i)}, x^{(j)} \rangle$ 来编写算法（在输入特征空间中的点之间将其视为 $(x^{(i)})^T x^{(j)}$）。当我们应用内核技巧时，我们可以用这些内积来表达我们的算法，这一事实将非常关键。&lt;/p&gt;
&lt;p&gt;当我们为优化问题构造拉格朗日算子时，我们有：&lt;/p&gt;
$$
\mathcal{L}(w,\alpha,\beta)=
\frac{1}{2}\|w\|^2-
\sum_{i=1}^{m}
\alpha_i
\left[
y^{(i)}(w^T x^{(i)} + b) - 1
\right]
\tag{8}
$$&lt;p&gt;注意，只有 $\alpha_i$ 乘子而没有 $\beta_i$ 乘子，因为问题只有不等式约束。&lt;/p&gt;
&lt;p&gt;让我们找到问题的对偶形式。为此，我们需要首先关于 $w,b$ 最小化 $\mathcal{L}(w,\alpha,\beta)$（对于固定的 $\alpha_i$），从而得到 $\theta_D$。我们通过令 $\mathcal{L}$ 关于 $w$ 和 $b$ 的偏导数为零来做到这一点。我们有：&lt;/p&gt;
$$
\nabla_w \mathcal{L}(w,\alpha,\beta)=
w-
\sum_{i=1}^{m}
\alpha_i y^{(i)} x^{(i)}=
0
$$&lt;p&gt;这意味着&lt;/p&gt;
$$
w=
\sum_{i=1}^{m}
\alpha_i y^{(i)} x^{(i)}
\tag{9}
$$&lt;p&gt;关于 $b$ 的求导，我们得到&lt;/p&gt;
$$
\nabla_b \mathcal{L}(w,\alpha,\beta)=
\sum_{i=1}^{m}
\alpha_i y^{(i)}=
0
\tag{10}
$$&lt;p&gt;如果我们利用等式 (9) 中 $w$ 的定义并将其带入拉格朗日算子（等式 (8)），并简化，我们得到&lt;/p&gt;
$$
\mathcal{L}(w,\alpha,\beta)=
\sum_{i=1}^{m} \alpha_i-
\frac{1}{2}
\sum_{i=1}^{m}
\sum_{j=1}^{m}
y^{(i)} y^{(j)}
\alpha_i \alpha_j
(x^{(i)})^T x^{(j)}-
b \sum_{i=1}^{m} \alpha_i y^{(i)}
$$&lt;p&gt;但是根据公式 (10)，最后一项为 0，所以我们得到&lt;/p&gt;
$$
\mathcal{L}(w,\alpha,\beta)=
\sum_{i=1}^{m} \alpha_i-
\frac{1}{2}
\sum_{i=1}^{m}
\sum_{j=1}^{m}
y^{(i)} y^{(j)}
\alpha_i \alpha_j
(x^{(i)})^T x^{(j)}
$$&lt;p&gt;回想一下，通过关于 $w,b$ 最小化 $\mathcal{L}$，我们得到了上面的等式。将这与约束 $\alpha_i \ge 0$ 和约束 (10) 放在一起，我们得到以下对偶优化问题：&lt;/p&gt;
$$
\max_{\alpha}
\quad
W(\alpha)=
\sum_{i=1}^{m} \alpha_i-
\frac{1}{2}
\sum_{i=1}^{m}
\sum_{j=1}^{m}
y^{(i)} y^{(j)}
\alpha_i \alpha_j
\langle x^{(i)}, x^{(j)} \rangle
$$$$
\text{s.t.}
\quad
\alpha_i \ge 0,
\quad i = 1, \dots, m
$$$$
\sum_{i=1}^{m} \alpha_i y^{(i)} = 0
$$&lt;p&gt;不难验证在我们的优化问题中确实满足 $p&amp;rsquo; = d&amp;rsquo;$ 和 KKT 条件（等式 (3)–(7)）所需的条件。因此，我们可以解决对偶问题来代替解决原始问题。具体来说，在上面的对偶问题中，我们有一个最大化问题，其中参数是 $\alpha_i$。我们稍后将讨论我们将用于解决对偶问题的特定算法，但如果我们确实能够解决它（即在满足约束条件下找到最大化 $W(\alpha)$ 的 $\alpha$），然后我们可以使用等式 (9) 返回作为 $\alpha$ 的函数的最优 $w$。找到 $w&amp;rsquo;$ 后，通过考虑原始问题，找到截距项 $b$ 的最优值也是直截了当的：&lt;/p&gt;
$$
b'=-
\frac{
\max_{i: y^{(i)}=-1} {w'}^T x^{(i)}+
\min_{i: y^{(i)}=1} {w'}^T x^{(i)}
}{2}
\tag{11}
$$&lt;p&gt;事实上，对于 $\alpha_i \ne 0$，我们有&lt;/p&gt;
$$
g_i(w)=- y^{(i)}(w^T x^{(i)} + b) + 1=
0
$$&lt;p&gt;所以&lt;/p&gt;
$$
y^{(i)}(w^T x^{(i)} + b) = 1
\quad \Longrightarrow \quad
w^T x^{(i)} + b = y^{(i)}
\quad \Longrightarrow \quad
b = y^{(i)} - w^T x^{(i)}
$$&lt;p&gt;注意到&lt;/p&gt;
$$
g_i(w)=- y^{(i)}(w^T x^{(i)} + b) + 1 \le 0
\quad \Longleftrightarrow \quad
y^{(i)}(w^T x^{(i)} + b) \ge 1
$$&lt;p&gt;如果 $y^{(i)} = 1$，那么&lt;/p&gt;
$$
w^T x^{(i)} + b \ge 1
$$&lt;p&gt;如果 $y^{(i)} = -1$，那么&lt;/p&gt;
$$
w^T x^{(i)} + b \le -1
$$&lt;p&gt;从而满足&lt;/p&gt;
$$
g_i(w)=- y^{(i)}(w^T x^{(i)} + b) + 1=
0
$$&lt;p&gt;的点必然为&lt;/p&gt;
$$
\arg \min_{i: y^{(i)}=1} w^T x^{(i)},
\qquad
\arg \max_{i: y^{(i)}=-1} w^T x^{(i)}
$$&lt;p&gt;结合之前的式子以及最优的 $w$ 为 $w&amp;rsquo;$ 可得&lt;/p&gt;
$$
b=
1-
\min_{i: y^{(i)}=1} {w'}^T x^{(i)}=-1-
\max_{i: y^{(i)}=-1} {w'}^T x^{(i)}
$$&lt;p&gt;两式相加可得&lt;/p&gt;
$$
b'=-
\frac{
\max_{i: y^{(i)}=-1} {w'}^T x^{(i)}+
\min_{i: y^{(i)}=1} {w'}^T x^{(i)}
}{2}
$$&lt;p&gt;
在继续之前，让我们更仔细地看一下等式 (9)，它给出了 $w$ 的最佳值（$\alpha$ 的最佳值）。假设我们已经将模型的参数拟合到训练集，现在希望在新的输入 $x$ 处进行预测。我们将计算 $w^T x + b$，并且当且仅当该项大于零时才预测 $y = 1$。但是使用 (9)，这项也可以写成：&lt;/p&gt;
$$
\begin{align}
w^T x + b&amp;=
\left(
\sum_{i=1}^{m}
\alpha_i y^{(i)} x^{(i)}
\right)^T x+ b
\tag{12}
\\&amp;=
\sum_{i=1}^{m}
\alpha_i y^{(i)}
\langle x^{(i)}, x \rangle+ b
\tag{13}
\end{align}
$$&lt;p&gt;
因此，如果我们找到 $\alpha_i$，为了进行预测，我们必须计算仅取决于 $x$ 与训练集中的点之间的内积。此外，我们之前看到除了支持向量之外，$\alpha_i$ 都将为零。因此，上述求和中的许多项将为零，并且我们确实只需要找到 $x$ 和支持向量之间的内积（通常只有一小部分）就能计算 (13) 并进行预测。&lt;/p&gt;
&lt;p&gt;通过讨论优化问题的对偶形式，我们获得了对问题结构的重要洞察，并且还能够仅根据输入特征向量之间的内积来编写整个算法。在下一节中，我们将利用属性核方法应用于我们的分类问题。由此产生的算法，&lt;strong&gt;支持向量机&lt;/strong&gt;，将能够在非常高维空间中有效地学习。&lt;/p&gt;</description></item><item><title>concrete mathematics hw1</title><link>https://lunatide.tech/p/concrete-mathematics-hw1/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/concrete-mathematics-hw1/</guid><description>&lt;img src="https://lunatide.tech/p/concrete-mathematics-hw1/pic1.jpg" alt="Featured image of post concrete mathematics hw1" /&gt;&lt;p&gt;过年几天没怎么看公开课，这两天补一下&lt;/p&gt;
&lt;h2 id="1"&gt;1.
&lt;/h2&gt;&lt;p&gt;当 $n=2$ 时，两个集合为 ${1}, {2}$，交集为空，所以从 1 推到 2 的时候有问题。&lt;/p&gt;
&lt;h2 id="2"&gt;2.
&lt;/h2&gt;&lt;p&gt;设 $T(n)$ 是把 $n$ 个盘子从 A 移动到目标 B 的最少次数，初步可以得到递推式
&lt;/p&gt;
$$
T(n)\le 3T(n - 1) + 2
$$&lt;p&gt;模拟一下盘子的移动过程。&lt;/p&gt;
&lt;p&gt;首先我们要让 $n-1$ 个较小的盘子从 A 移动到 B，然后让之前 A 上最大的盘子移动到 C，这一步要至少 $T(n-1)+1$ 步（+1 意思是最大的盘子移动）。&lt;/p&gt;
&lt;p&gt;我们希望最大的盘子在 B 的最下方，所以还得先让 B 上较小的那些盘子回到 A，因此又需要 $T(n-1)+1$ 步。&lt;/p&gt;
&lt;p&gt;最后最大盘到 B 后我们还需要把 $n-1$ 个盘子移动到 B，所以加起来至少需要&lt;/p&gt;
$$
3T(n-1)+2
$$&lt;p&gt;因此&lt;/p&gt;
$$
T(n)=3T(n-1)+2=3^n-1
$$&lt;h2 id="3"&gt;3.
&lt;/h2&gt;&lt;p&gt;设 $T(n)$ 是 $n$ 个盘子所有合法的状态集合，$T(1)=3, T(2)=9$，我们 guess
&lt;/p&gt;
$$
T(n)=3^n
$$&lt;p&gt;假设 $n-1$ 时结论成立，下证 $n$ 时结论也成立。&lt;/p&gt;
&lt;p&gt;回忆上一道题的过程，其实虽然一共有 5 步，但是这是一个类似周期的过程。总有一个时刻是最大的盘子在 A、B 或者 C 的柱子上，然后剩下的 $n-1$ 个盘子在做移动。&lt;/p&gt;
&lt;p&gt;因此总共就是&lt;/p&gt;
$$
3 \times 3^{n-1}=3^n
$$&lt;p&gt;得证。&lt;/p&gt;
&lt;h2 id="4"&gt;4.
&lt;/h2&gt;&lt;p&gt;设 $T(n)$ 是在合法状态下，存在 $n$ 个圆盘需要最多的移动次数$\le 2^n-1$&lt;/p&gt;
&lt;p&gt;当 $n=1$ 时，需要一次移动，显然成立。&lt;/p&gt;
&lt;p&gt;假设 $n-1$ 时结论成立，也就是说最多移动次数$\le 2^{n-1}-1$&lt;/p&gt;
&lt;p&gt;$n$ 个盘子时候的第一个合法状态，如果最大的盘子已经在目标 B 柱上，那么只需要 $T(n-1)$ 次操作就可以，显然成立。&lt;/p&gt;
&lt;p&gt;若最大盘子不在 B 柱上，我们首先需要将 $n-1$ 个盘子移动到中间柱 C，然后把大盘移动到 B，最后递归剩下的盘子，最多 $2^{n-1}-1$。&lt;/p&gt;
&lt;p&gt;因此总步数就是&lt;/p&gt;
$$
2^n-1
$$&lt;p&gt;得证。&lt;/p&gt;
&lt;h2 id="5"&gt;5.
&lt;/h2&gt;&lt;p&gt;不能，一个新的圆和原来的圆最多增加两个交点，最多增加六块区域，6+8=14&lt;/p&gt;
&lt;h2 id="6"&gt;6.
&lt;/h2&gt;&lt;p&gt;每多一条直线，和原有的每条直线增加一个交点，也就是增加n-1个交点，最多增加n-2个新区域&lt;/p&gt;
&lt;p&gt;设有界区域的最大个数是$a_n$个，那么$a_n=a_{n-1}+n-2$
&lt;/p&gt;
$$
a_1=a_2=0,a_n=\sum_{i=0}^{n-2}i
$$&lt;h2 id="7"&gt;7.
&lt;/h2&gt;&lt;p&gt;$H(1)=J(2)-J(1)=0 \neq 2$&lt;/p&gt;
&lt;h2 id="8"&gt;8.
&lt;/h2&gt;&lt;p&gt;从$Q_2$写到$Q_6$，容易发现，$Q_k=Q_{k-5},k \ge 5$&lt;/p&gt;
&lt;h2 id="9"&gt;9.
&lt;/h2&gt;&lt;h3 id="a"&gt;(a)
&lt;/h3&gt;&lt;p&gt;令
&lt;/p&gt;
$$
x_n=\frac{x_1+\cdots+x_{n-1}}{n-1},
$$&lt;p&gt;
证明 $P(n)\Rightarrow P(n-1)$&lt;/p&gt;
&lt;p&gt;取任意 $x_1,\dots,x_{n-1}\ge0$，记&lt;/p&gt;
$$
S=x_1+\cdots+x_{n-1},\qquad
x_n=\frac{S}{n-1}.
$$$$
x_1\cdots x_{n-1}\cdot\frac{S}{n-1}
\le
\left(
\frac{S+\frac{S}{n-1}}{n}
\right)^n.
$$$$
x_1\cdots x_{n-1}\cdot\frac{S}{n-1}
\le
\left(\frac{S}{n-1}\right)^n.
$$&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;若 $S=0$，则 $x_1=\cdots=x_{n-1}=0$，显然 $P(n-1)$ 成立；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若 $S&amp;gt;0$，两边同除以 $\dfrac{S}{n-1}&amp;gt;0$，得&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
$$
x_1\cdots x_{n-1}
\le
\left(\frac{S}{n-1}\right)^{n-1}=
\left(
\frac{x_1+\cdots+x_{n-1}}{n-1}
\right)^{n-1}.
$$&lt;p&gt;因此&lt;/p&gt;
$$
P(n)\Rightarrow P(n-1)\quad (n&gt;1).
$$&lt;h3 id="b"&gt;(b)
&lt;/h3&gt;&lt;p&gt;设 $x_1,\dots,x_{2n}\ge 0$，记&lt;/p&gt;
$$
A=\frac{x_1+\dots+x_n}{n},\qquad
B=\frac{x_{n+1}+\dots+x_{2n}}{n}.
$$&lt;p&gt;由 $P(n)$ 得&lt;/p&gt;
$$
x_1\cdots x_n \le A^n,
\qquad
x_{n+1}\cdots x_{2n} \le B^n.
$$&lt;p&gt;因此&lt;/p&gt;
$$
\prod_{i=1}^{2n} x_i=
(x_1\cdots x_n)(x_{n+1}\cdots x_{2n})
\le
A^n B^n=
(AB)^n.
$$&lt;p&gt;由 $P(2)$&lt;/p&gt;
$$
(AB)^n
\le
\left(\frac{A+B}{2}\right)^{2n}.
$$&lt;p&gt;于是&lt;/p&gt;
$$
\prod_{i=1}^{2n} x_i
\le
(AB)^n
\le
\left(\frac{A+B}{2}\right)^{2n}.
$$&lt;p&gt;而&lt;/p&gt;
$$
\frac{A+B}{2}=
\frac{
\frac{x_1+\dots+x_n}{n}+
\frac{x_{n+1}+\dots+x_{2n}}{n}
}{2}=
\frac{x_1+\dots+x_{2n}}{2n}.
$$&lt;p&gt;因此&lt;/p&gt;
$$
\prod_{i=1}^{2n} x_i
\le
\left(
\frac{x_1+\dots+x_{2n}}{2n}
\right)^{2n}.
$$&lt;p&gt;得证&lt;/p&gt;</description></item><item><title>CS229 Lecture 6</title><link>https://lunatide.tech/p/cs229-lecture-6/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-6/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-6/pic1.jpg" alt="Featured image of post CS229 Lecture 6" /&gt;&lt;h2 id="针对文本分类的事件模型"&gt;针对文本分类的事件模型
&lt;/h2&gt;&lt;p&gt;接下里用一个文本分类方面的模型来对生成学习算法进行收尾，之前说的朴素贝叶斯方法已经可以解决很多分类问题了，不过还有另外一个算法在针对文本的分类效果要更好&lt;/p&gt;
&lt;p&gt;在文本分类的特定上下文中，朴素贝叶斯使用了所谓的 &lt;strong&gt;多变量伯努利事件模型&lt;/strong&gt;。在这个模型中，我们假设生成电子邮件的方式是首先随机确定(根据类先验$p(y)$) 邮件是垃圾邮件还是非垃圾邮件，然后发送邮件的人通过字典根据概率$p(x_i = 1 \mid y)$ 独立决定是否包括邮件中的每个单词。因此，邮件的概率由&lt;br&gt;
&lt;/p&gt;
$$
p(y)\prod_{i=1}^{n} p(x_i \mid y)
$$&lt;p&gt;
给出。&lt;/p&gt;
&lt;p&gt;这里有个不同的模型，称为&lt;strong&gt;多项事件模型&lt;/strong&gt;。为了描述这个模型，我们将使用不同的表示法和一组特征来表示电子邮件。我们令 $x_i$ 表示电子邮件中第 $i$ 个单词的标识。因此，$x_i$ 现在是一个整数，取值为 ${1, \dots, |V|}$，其中 $|V|$ 是我们词汇量的大小（字典）。$n$ 个单词的电子邮件现在由长度为 $n$ 的向量 $(x_1, x_2, \dots, x_n)$ 表示；请注意，$n$ 可能因不同文档而异。例如，如果电子邮件以 “A NIPS …” 开头，那么 $x_1 = 1$（“a” 是字典中的第 1 个单词），$x_2 = 35000$（如果 “nips” 是字典中的第 35000 个单词）。&lt;/p&gt;
&lt;p&gt;在多项事件模型中，我们假设生成电子邮件的方式是通过如下随机过程：首先确定邮件是垃圾邮件/非垃圾邮件（根据 $p(y)$）。然后，电子邮件的发件人首先从某个多项分布生成 $x_1$（$p(x_1 \mid y)$）。接下来，第二个单词 $x_2$ 独立于 $x_1$ 且从相同的多项分布中选择，并且类似地对于 $x_3, x_4$ 等等，直到已经生成了电子邮件的所有 $n$ 个单词。因此，邮件的总概率由&lt;br&gt;
&lt;/p&gt;
$$
p(y)\prod_{i=1}^{n} p(x_i \mid y)
$$&lt;p&gt;
给出。请注意，此公式看起来与我们之前在多变量伯努利事件模型下的邮件概率公式类似，但公式中的项现在意味着非常不同的事物。特别是 $x_j \mid y$ 现在服从多项分布，而不是伯努利分布。&lt;/p&gt;
&lt;p&gt;我们的新模型的参数如前所述是 $\phi_y = p(y)$，$\phi_{k \mid y=1} = p(x_j = k \mid y = 1)$（对于任何 $j$），以及 $\phi_{k \mid y=0} = p(x_j = k \mid y = 0)$。注意，我们假设 $p(x_j \mid y)$ 对于 $j$ 的所有值是相同的（即，生成单词的分布不依赖于其在电子邮件中的位置 $j$）。&lt;/p&gt;
&lt;p&gt;如果给定训练集 ${(x^{(i)}, y^{(i)}); i = 1, \dots, m}$，其中\(x^{(i)} = (x^{(i)}_1, x^{(i)}_2, \dots, x^{(i)}_{n_i})\)（这里，$n_i$ 是第 $i$ 个训练样本的单词数），数据的似然性由下式给出：
&lt;/p&gt;
$$
\mathcal{L}(\phi_y, \phi_{k \mid y=0}, \phi_{k \mid y=1})=
\prod_{i=1}^{m} p(x^{(i)}, y^{(i)})=
\prod_{i=1}^{m}
\left(
\prod_{j=1}^{n_i} p(x^{(i)}_j \mid y^{(i)}; \phi_{k \mid y=0}, \phi_{k \mid y=1})
\right)p(y^{(i)}; \phi_y)
$$&lt;p&gt;最大化上式会产生参数的最大似然估计：&lt;/p&gt;
$$
\phi_{k \mid y=1}=\frac{\sum_{i=1}^{m} \sum_{j=1}^{n_i} \mathbf{1}\{x^{(i)}_j = k \land y^{(i)} = 1\}}{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\} n_i}
$$$$
\phi_{k \mid y=0}=\frac
{\sum_{i=1}^{m} \sum_{j=1}^{n_i} \mathbf{1}\{x^{(i)}_j = k \land y^{(i)} = 0\}}
{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\} n_i}
$$$$
\phi_y=\frac
{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\}}
{m}
$$&lt;p&gt;如果我们在估计 $\phi_{k \mid y=1}$ 和 $\phi_{k \mid y=0}$ 时应用拉普拉斯平滑，我们对分子加 1，对分母加 $|V|$，并得到：&lt;/p&gt;
$$
\phi_{k \mid y=1}=
\frac{
\sum_{i=1}^{m} \sum_{j=1}^{n_i}
\mathbf{1}\{x^{(i)}_j = k \land y^{(i)} = 1+ 1
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1+ |V|
}
$$$$
\phi_{k \mid y=0}=
\frac{
\sum_{i=1}^{m} \sum_{j=1}^{n_i}
\mathbf{1}\{x^{(i)}_j = k \land y^{(i)} = 0\}+ 1
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\} n_i + |V|
}
$$&lt;p&gt;虽然不一定是最好的分类算法，但朴素贝叶斯分类器通常效果非常好。考虑到它的简单性和易于实现，它通常也非常适合作为第一个尝试。&lt;/p&gt;
&lt;h2 id="支持向量机"&gt;支持向量机
&lt;/h2&gt;&lt;p&gt;这一章节主要讲的是&lt;strong&gt;支持向量机(SVM)学习算法&lt;/strong&gt;，SVM是现成的最好的监督学习算法。为了讲述SVM，我们首先需要讨论间隔以及将数据以大的“间隔”分开的想法。接下来，我们将讨论最优间隔分类器，这将引导我们对拉格朗日对偶的讨论。我们还将看到核方法，它提供了一种在非常高维（例如无限维）特征空间中有效应用SVM的方法，最后，我们会使用SMO算法结束这章，该方法可以有效地实现支持向量机&lt;/p&gt;
&lt;h3 id="边界直觉"&gt;边界：直觉
&lt;/h3&gt;&lt;p&gt;我们将通过讨论间隔来开始我们关于SVM的讨论。本节将给出关于间隔和我们预测的“信心”的直觉；这些想法将在第3节中正式提出。&lt;/p&gt;
&lt;p&gt;考虑logistic回归，其中概率 $p(y = 1 \mid x; \theta)$ 由 $h_\theta(x) = g(\theta^T x)$ 建模。然后，当且仅当 $h_\theta(x) \ge 0.5$ 时，我们才会预测输入 $x$ 为“1”，或者等价的，当且仅当 $\theta^T x \ge 0$。&lt;/p&gt;
&lt;p&gt;考虑一个正训练样本 $(y = 1)$。$\theta^T x$ 越大，$h_\theta(x) = p(y = 1 \mid x; w, b)$ 也越大，因此标记为1的“置信度”也越高。因此，非正式地，我们可以对 $y = 1$ 进行非常自信的预测，如果 $\theta^T x \gg 0$。类似地，我们将logistic回归视为对 $y = 0$ 进行非常自信的预测，如果 $\theta^T x \ll 0$。&lt;/p&gt;
&lt;p&gt;给定一个训练集，再次非正式地看起来我们已经找到了一个对训练数据很好的拟合，如果我们可以找到 $\theta$，以便每当 $y^{(i)} = 1$ 时，$\theta^T x^{(i)} \gg 0$，并且每当 $y^{(i)} = 0$ 时，$\theta^T x^{(i)} \ll 0$，因为这将反映所有训练样本的非常有信心（和正确）的分类。这似乎是一个很好的目标，我们很快就会使用函数间隔的概念来形式化这个想法。&lt;/p&gt;
&lt;p&gt;对于不同的直觉，请考虑下图，其中 x 代表正训练样本，o 代表负训练样本。决策边界（这是由等式 $\theta^T x = 0$ 给出的线，也称为&lt;strong&gt;分离超平面&lt;/strong&gt;）也在图中，并且三个点也被标记为 A、B 和 C。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-6/p1.jpg"
width="428"
height="330"
srcset="https://lunatide.tech/p/cs229-lecture-6/p1_hu_aa6a43531a60774f.jpg 480w, https://lunatide.tech/p/cs229-lecture-6/p1_hu_4f137f80d66a21b5.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="129"
data-flex-basis="311px"
&gt;&lt;/p&gt;
&lt;p&gt;注意到A点离决策边界很远。如果我们被要求在A处对 $y$ 的值进行预测，那么我们应该非常确信 $y = 1$。相反，C点非常接近决策边界，虽然它位于我们预测 $y = 1$ 的决策边界的一侧，但似乎只是对决策边界的一个小变化很容易导致我们的预测改变为 $y = 0$。因此，我们对A处的预测比对C的预测更有信心。B点位于这两种情况之间，更广泛地说，我们看到如果一点远离分离超平面，那么我们对预测可能会更有信心。&lt;/p&gt;
&lt;p&gt;同样，非正式地，我们认为，如果给定一个训练集，我们设法找到一个决策边界，使我们能够在训练样例上做出所有正确和自信（意味着远离决策边界）的预测，那将是非常好的。我们稍后将使用几何间隔的概念将其形式化。&lt;/p&gt;
&lt;h3 id="符号"&gt;符号
&lt;/h3&gt;&lt;p&gt;为了使我们对SVM的讨论更容易，我们首先需要引入一个新的符号来讨论分类。我们将考虑用于解决带有标签 $y$ 和特征 $x$ 的二元分类问题的线性分类器。从现在开始，我们将使用 $y \in {-1, 1}$（而不是 ${0, 1}$）来表示类标签。此外，我们将使用参数 $w, b$，而不是使用向量 $\theta$ 来参数化我们的线性分类器，并将分类器改写为&lt;/p&gt;
$$
h_{w,b}(x) = g(w^T x + b)
$$&lt;p&gt;这里，如果 $z \ge 0$，则 $g(z) = 1$，否则 $g(z) = -1$。这个 “$w, b$” 表示法允许我们明确地将截距项 $b$ 与其他参数分开处理。（我们也放弃了以前让 $x_0 = 1$ 成为输入特征向量中的额外坐标的约定。）因此，$b$ 扮演以前 $\theta_0$ 的角色，$w$ 扮演 $(\theta_1, \dots, \theta_n)^T$ 的角色。&lt;/p&gt;
&lt;p&gt;还要注意，根据我们上面对 $g$ 的定义，我们的分类器将直接预测 $1$ 或 $-1$（参见感知机算法），而不是首先进行估计 $y$ 为 $1$ 的概率的中间步骤（这是 logistic 回归所做的）。&lt;/p&gt;
&lt;h3 id="函数和几何间隔"&gt;函数和几何间隔
&lt;/h3&gt;&lt;p&gt;让我们形式化函数间隔和几何间隔的概念。给定一个训练样本 $(x^{(i)}, y^{(i)})$，我们根据训练样本定义 $(w,b)$ 的&lt;strong&gt;函数间隔&lt;/strong&gt;为&lt;/p&gt;
$$
\hat{\gamma}^{(i)} = y^{(i)} (w^T x^{(i)} + b)
$$&lt;p&gt;注意，如果 $y^{(i)} = 1$，那么为了使函数间隔变大（即，为了使我们的预测有信心和正确），我们需要 $w^T x^{(i)} + b$ 为大的正数。相反，如果 $y^{(i)} = -1$，那么为了使函数间隔变大，我们需要 $w^T x^{(i)} + b$ 为（绝对值）大的负数。此外，如果 $y^{(i)} (w^T x^{(i)} + b) &amp;gt; 0$，那么我们对这个例子的预测是正确的。因此，大的函数间隔代表了自信和正确的预测。&lt;/p&gt;
&lt;p&gt;对于上面给出的线性分类器 $g$（取 ${-1,1}$ 中的值），函数间隔的一个属性使得它不是一个非常好的置信度量。给定我们的选择 $g$，我们注意到如果我们用 $2w$ 代替 $w$，用 $2b$ 代替 $b$，那么因为&lt;/p&gt;
$$
g(w^T x + b) = g(2w^T x + 2b)
$$&lt;p&gt;所以这根本不会改变 $h_{w,b}(x)$。即，$g$，因此也是 $h_{w,b}(x)$，仅取决于 $w^T x + b$ 的符号，而不取决于其模长。然而，用 $(2w, 2b)$ 代替 $(w, b)$ 也会导致我们的函数间隔乘以 2。因此，通过利用对 $w$ 和 $b$ 的缩放，我们可以使函数间隔任意大，但实际上没有改变任何有意义的事情，因此强制某种归一化条件可能是有意义的，例如 $|w|_2 = 1$；也就是说，我们可以用 $(w/|w|_2, b/|w|_2)$ 代替 $(w, b)$，然后考虑 $(w/|w|_2, b/|w|_2)$ 的函数间隔。&lt;/p&gt;
&lt;p&gt;给定训练集&lt;/p&gt;
$$
S = \{(x^{(i)}, y^{(i)}); i = 1, \dots, m\}
$$&lt;p&gt;我们还将 $(w,b)$ 关于 $S$ 的函数间隔定义为训练样本中最小的函数间隔，用 $\hat{\gamma}$ 表示，因此可以写成：&lt;/p&gt;
$$
\hat{\gamma} = \min_{i=1,\dots,m} \hat{\gamma}^{(i)}
$$&lt;p&gt;接下来，我们来谈谈&lt;strong&gt;几何间隔&lt;/strong&gt;。请看下图：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-6/p2.jpg"
width="388"
height="282"
srcset="https://lunatide.tech/p/cs229-lecture-6/p2_hu_61ca2fb55350e50e.jpg 480w, https://lunatide.tech/p/cs229-lecture-6/p2_hu_ecdc56822db7d5a1.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="137"
data-flex-basis="330px"
&gt;&lt;/p&gt;
&lt;p&gt;上图展示了对应于 $(w,b)$ 的决策边界以及向量 $w$。注意，$w$ 与分离超平面正交。考虑 A 处的点，它代表某个训练样本 $x^{(i)}$，标签为 $y^{(i)} = 1$，它与决策边界的距离为 $\gamma^{(i)}$，由线段 $AB$ 给出。&lt;/p&gt;
&lt;p&gt;我们怎样才能得到 $\gamma^{(i)}$ 的值？注意到 $w/|w|$ 是和 $w$ 同向的单位长度向量。由于 A 代表 $x^{(i)}$，因此我们发现点 $B$ 由&lt;/p&gt;
$$
x^{(i)} - \gamma^{(i)} \frac{w}{\|w\|}
$$&lt;p&gt;给出。但这一点在决策边界上，决策边界上的所有点 $x$ 都满足方程 $w^T x + b = 0$。因此，&lt;/p&gt;
$$
w^T \left( x^{(i)} - \gamma^{(i)} \frac{w}{\|w\|} \right) + b = 0
$$&lt;p&gt;解出 $\gamma^{(i)}$ 得到&lt;/p&gt;
$$
\gamma^{(i)}=
\frac{w^T x^{(i)} + b}{\|w\|}=
\left( \frac{w}{\|w\|} \right)^T x^{(i)} + \frac{b}{\|w\|}
$$&lt;p&gt;这是针对图中 A 处的正训练样本的情况得到的，其中处于决策边界的“正”侧是好的。更一般地，我们定义 $(w,b)$ 相对于训练样本 $(x^{(i)}, y^{(i)})$ 的几何间隔为&lt;/p&gt;
$$
\gamma^{(i)}=
y^{(i)} \left(
\left( \frac{w}{\|w\|} \right)^T x^{(i)} + \frac{b}{\|w\|}
\right)
$$&lt;p&gt;请注意，如果 $|w| = 1$，那么函数间隔等于几何间隔——这就为我们提供了一种方法来联系这两种不同的间隔概念。此外，几何间隔对于重新缩放参数是不变的；即，如果我们用 $2w$ 代替 $w$，用 $2b$ 代替 $b$，则几何间隔不会改变。事实上，这将在以后派上用场。具体来说，由于这种参数缩放的不变性，当试图将 $w$ 和 $b$ 拟合到训练数据时，我们可以对 $w$ 施加任意缩放约束而不改变任何重要的东西；例如，我们可以要求 $|w| = 1$，或 $|w|_1 = 5$，或 $|w_1 + b| + |w_2| = 2$，只需重新调整 $w$ 和 $b$ 即可满足其中任何一个。&lt;/p&gt;
&lt;p&gt;给定训练集&lt;/p&gt;
$$
S = \{(x^{(i)}, y^{(i)}); i = 1, \dots, m\}
$$&lt;p&gt;我们还将 $(w,b)$ 关于 $S$ 的几何间隔定义为训练样本中最小的几何间隔，用 $\gamma$ 表示，因此可以写成：&lt;/p&gt;
$$
\gamma = \min_{i=1,\dots,m} \gamma^{(i)}
$$</description></item><item><title>CS229 Lecture 5(2)</title><link>https://lunatide.tech/p/cs229-lecture-52/</link><pubDate>Thu, 22 Jan 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-52/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-52/pic1.jpg" alt="Featured image of post CS229 Lecture 5(2)" /&gt;&lt;h2 id="朴素贝叶斯"&gt;朴素贝叶斯
&lt;/h2&gt;&lt;p&gt;在GDA中，特征向量x是连续的值为实数的向量。下面我们要讲的是当 \(x_i\) 是离散的时候所使用的另一种学习算法。&lt;/p&gt;
&lt;p&gt;下面就来看一个样例，来尝试建立一个邮件筛选器，使用机器学习的方法。这回咱们要来对邮件信息进行分类，来判断是否为商业广告邮件还是非垃圾邮件。在学会了怎么实现之后，我们就可以让邮件阅读器能够自动对垃圾信息进行过滤，或者单独把这些垃圾邮件放进一个单独的文件夹中。对邮件进行分类是一个案例，属于文本分类这一更广泛问题集合。&lt;/p&gt;
&lt;p&gt;假设我们有了一个训练集（也就是一堆已经标好了是否为垃圾邮件的邮件）。要构建垃圾邮件分类器，咱们先要开始确定用来描述一封邮件的特征 \(x_i\) 有哪些。&lt;/p&gt;
&lt;p&gt;我们将用一个特征向量来表示一封邮件，这个向量的长度等于字典中单词的个数。如果邮件中包含了字典中的第 \(i\) 个单词，那么令$x_i = 1$，反之则$x_i = 0$。例如:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-52/p1.jpg"
width="369"
height="262"
srcset="https://lunatide.tech/p/cs229-lecture-52/p1_hu_24d2c244daf379eb.jpg 480w, https://lunatide.tech/p/cs229-lecture-52/p1_hu_781b30d3469ce0de.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="140"
data-flex-basis="338px"
&gt;&lt;/p&gt;
&lt;p&gt;就用来表示一个邮件，其中包含了两个单词 “a” 和 “buy”，但没有单词 “aardvark”、“aardwolf” 或者 “zymurgy”。这个单词集合编码整理成的特征向量也成为词汇表，所以特征向量 x 的维度就等于词汇表的长度。&lt;/p&gt;
&lt;p&gt;选好了特征向量了，接下来就是建立一个生成模型。所以我们必须对 $p(x \mid y)$ 进行建模。但是，假如我们的单词有五万个词，则特征向量 $x \in {0, 1}^{50000}$（即 $x$ 是一个 50000 维的向量，其值是 0 或者 1），如果我们要对这样的 $x$ 进行多项式分布的建模，那么就可能有 $2^{50000}$ 种可能的输出，然后就要用一个 $(2^{50000} - 1)$ 维的参数向量。这样参数明显太多了。&lt;/p&gt;
&lt;p&gt;要给 $p(x \mid y)$ 建模，先来做一个非常强的假设。我们假设特征向量 $x_i$ 对于给定的 $y$ 是独立的。这个假设也叫做&lt;strong&gt;朴素贝叶斯假设&lt;/strong&gt;，基于此假设衍生的算法也就叫做&lt;strong&gt;朴素贝叶斯分类器&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;例如，如果 $y = 1$ 意味着一个邮件是垃圾邮件；然后其中 “buy” 是第2087个单词，而 “price” 是第39831个单词；那么接下来我们就假设，如果我告诉你 $y = 1$，也就是说某一个特定的邮件是垃圾邮件，那么对于 $x_{2087}$（也就是单词 buy 是否出现在邮件里）的了解并不会影响你对 $x_{39831}$（单词 price 出现的位置）的采信值。更正规一点，可以写成 $p(x_{2087} \mid y) = p(x_{2087} \mid y, x_{39831})$。（要注意这个并不是说 $x_{2087}$ 和 $x_{39831}$ 这两个特征是独立的，那样就变成了$p(X_{2087})=p(x_{2087} \mid x_{39831})$，相反，我们只假设$x_{2087}$和$x_{39831}$关于$y$条件独立）&lt;/p&gt;
&lt;p&gt;我们现在有：&lt;/p&gt;
$$
\begin{array}{lcl}
p(x_1, \ldots, x_{50000} \mid y)
&amp; = &amp; p(x_1 \mid y)\, p(x_2 \mid y, x_1)\, p(x_3 \mid y, x_1, x_2)\, \ldots \, p(x_{50000} \mid y, x_1, \ldots, x_{49999}) \\
&amp; = &amp; p(x_1 \mid y)\, p(x_2 \mid y)\, p(x_3 \mid y)\, \ldots \, p(x_{50000} \mid y) \\
&amp; = &amp; \prod_{i=1}^{n} p(x_i \mid y)
\end{array}
$$&lt;p&gt;
第一个等式简单地遵循概率的通用属性，第二个等式使用 NB 假设。我们注意到，即使朴素贝叶斯假设是一个非常强的假设，所得的算法在许多问题上也能很好地工作。&lt;/p&gt;
&lt;p&gt;我们的模型通过$\phi_{j \mid y=1} = p(x_j = 1 \mid y = 1), \phi_{j \mid y=0} = p(x_j = 1 \mid y = 0)$，以及$\phi_y = p(y = 1)$来参数化。像往常一样，给定训练集${(x^{(i)}, y^{(i)}); i = 1, \ldots, m}$，数据的似然性为：
&lt;/p&gt;
$$
\mathcal{L}(\phi_y, \phi_{j \mid y=0}, \phi_{j \mid y=1})
= \prod_{i=1}^{m} p(x^{(i)}, y^{(i)})
$$&lt;p&gt;关于 $\phi_y, \phi_{j \mid y=0}, \phi_{j \mid y=1}$ 最大化上述给出最大似然估计：&lt;/p&gt;
$$
\phi_{j \mid y=1}
= \frac{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1 \wedge x_j^{(i)} = 1\}}
{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\}}
$$$$
\phi_{j \mid y=0}
= \frac{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0 \wedge x_j^{(i)} = 1\}}
{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\}}
$$$$
\phi_y
= \frac{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\}}{m}
$$&lt;p&gt;证明如下：&lt;/p&gt;
&lt;p&gt;不难看出&lt;/p&gt;
$$
\begin{array}{rcl}
p(y) &amp; = &amp; (\phi_y)^y (1 - \phi_y)^{1-y}
\end{array}
$$$$
\begin{array}{rcl}
p(x \mid y = k) &amp; = &amp; \prod_{j=1}^{n} (\phi_{j \mid y = k})^{x_j}
(1 - \phi_{j \mid y = k})^{1-x_j}
\end{array}
$$&lt;p&gt;令 $\varphi$ 表示参数集&lt;br&gt;
${\phi_y, \phi_{j \mid y=0}, \phi_{j \mid y=1}, j = 1, \ldots, n}$，&lt;br&gt;
所以对数似然函数 $\ell(\varphi)$ 为&lt;/p&gt;
$$
\begin{array}{rcl}
\ell(\varphi)&amp; = &amp; \log \prod_{i=1}^{m} p(x^{(i)}, y^{(i)}; \varphi) \\&amp; = &amp; \sum_{i=1}^{m} \log p(x^{(i)}, y^{(i)}; \varphi) \\&amp; = &amp; \sum_{i=1}^{m} \log \big( p(x^{(i)} \mid y^{(i)}) p(y^{(i)}) \big) \\&amp; = &amp; \sum_{i=1}^{m} \log
\prod_{j=1}^{n}
(\phi_{j \mid y=y^{(i)}})^{x_j^{(i)}}
(1 - \phi_{j \mid y=y^{(i)}})^{1-x_j^{(i)}}
(\phi_y)^{y^{(i)}}
(1 - \phi_y)^{1-y^{(i)}} \\&amp; = &amp; \sum_{i=1}^{m} \sum_{j=1}^{n}
\Big(
x_j^{(i)} \log(\phi_{j \mid y=y^{(i)}})+ (1 - x_j^{(i)}) \log(1 - \phi_{j \mid y=y^{(i)}})
\Big) + \sum_{i=1}^{m}
\Big(
y^{(i)} \log \phi_y+ (1 - y^{(i)}) \log(1 - \phi_y)
\Big)
\end{array}
$$&lt;p&gt;先关于 $\phi_{j \mid y=k}$ 求梯度&lt;/p&gt;
$$
\begin{array}{lcl}
\nabla_{\phi_{j \mid y=k}} \ell(\varphi)&amp; = &amp; \sum_{i=1}^{m}
\Big(
x_j^{(i)} \frac{1}{\phi_{j \mid y=y^{(i)}}} \mathbf{1}\{y^{(i)} = k\}+ (1 - x_j^{(i)}) \frac{1}{1 - \phi_{j \mid y=y^{(i)}}} (-1)\mathbf{1}\{y^{(i)} = k\}
\Big) \\&amp; = &amp; \sum_{i=1}^{m}
\frac{\mathbf{1}\{y^{(i)} = k\}}
{\phi_{j \mid y=y^{(i)}} (1 - \phi_{j \mid y=y^{(i)}})}
\Big(
x_j^{(i)} (1 - \phi_{j \mid y=y^{(i)}})- (1 - x_j^{(i)}) \phi_{j \mid y=y^{(i)}}
\Big) \\&amp; = &amp; \frac{1}{\phi_{j \mid y=k} (1 - \phi_{j \mid y=k})}
\sum_{i=1}^{m}
\mathbf{1}\{y^{(i)} = k\}
\Big(
x_j^{(i)} - \phi_{j \mid y=k}
\Big)
\end{array}
$$&lt;p&gt;
令上式为0可得
&lt;/p&gt;
$$
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = k\}
\big(x_j^{(i)} - \phi_{j \mid y=k}\big)= 0
\;\Rightarrow\;
\left(\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = k\}\right)\phi_{j \mid y=k}= \sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = k\} x_j^{(i)}= \sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = k \wedge x_j^{(i)} = 1\}
\;\Rightarrow\;
\phi_{j \mid y=k}=
\frac{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = k \wedge x_j^{(i)} = 1\}
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = k\}
}
$$&lt;p&gt;
从而&lt;/p&gt;
$$
\begin{array}{lcl}
\phi_{j \mid y=0}
&amp; = &amp;
\displaystyle
\frac{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0 \wedge x_j^{(i)} = 1\}
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\}
}
\qquad
\phi_{j \mid y=1}
&amp; = &amp;
\displaystyle
\frac{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1 \wedge x_j^{(i)} = 1\}
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\}
}
\end{array}
$$&lt;p&gt;关于 $\phi_y$ 求梯度可得&lt;/p&gt;
$$
\begin{array}{lcl}
\nabla_{\phi_y} \ell(\varphi)&amp; = &amp;
\displaystyle
\sum_{i=1}^{m}
\nabla_{\phi_y}
\Big(
y^{(i)} \log \phi_y+ (1 - y^{(i)}) \log(1 - \phi_y)
\Big) \\&amp; = &amp;
\displaystyle
\sum_{i=1}^{m}
\left(
y^{(i)} \frac{1}{\phi_y}- (1 - y^{(i)}) \frac{1}{1 - \phi_y}
\right) \\&amp; = &amp;
\displaystyle
\frac{1}{\phi_y (1 - \phi_y)}
\sum_{i=1}^{m}
\Big(
y^{(i)} (1 - \phi_y)- (1 - y^{(i)}) \phi_y
\Big) \\&amp; = &amp;
\displaystyle
\frac{1}{\phi_y (1 - \phi_y)}
\sum_{i=1}^{m}
\big(
y^{(i)} - \phi_y
\big)
\end{array}
$$&lt;p&gt;令上式为 $0$ 可得&lt;/p&gt;
$$
\begin{array}{lcl}
\phi_y
&amp; = &amp;
\displaystyle
\frac{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\}}{m}
\end{array}
$$&lt;p&gt;在上面的等式中，“$\wedge$”符号表示 “and”。这些参数具有非常自然的解释。例如，$\phi_{j \mid y=1}$ 是单词 $j$ 出现在垃圾邮件（$y = 1$）中的比例。在拟合了所有这些参数之后，为了对具有特征 $x$ 的新样本进行预测，我们就可以简单地进行计算&lt;/p&gt;
$$
\begin{array}{lcl}
p(y = 1 \mid x)&amp; = &amp;
\displaystyle
\frac{p(x \mid y = 1) p(y = 1)}{p(x)}\\&amp; = &amp;
\displaystyle
\frac{
\left( \prod_{i=1}^{n} p(x_i \mid y = 1) \right) p(y = 1)
}{
\left( \prod_{i=1}^{n} p(x_i \mid y = 1) \right) p(y = 1)+
\left( \prod_{i=1}^{n} p(x_i \mid y = 0) \right) p(y = 0)
}
\end{array}
$$&lt;p&gt;并选择具有较高后验概率的类别。&lt;/p&gt;
&lt;p&gt;最后，我们注意到虽然我们已经开发了朴素贝叶斯算法，主要是针对特征 $x_i$ 是二值的问题，将其推广到 $x_i$ 可以取 ${1,2,\ldots,k_i}$ 很简单。在这里，我们只是将 $p(x_i \mid y)$ 建模为多项分布而不是伯努利分布。实际上，即使某些原始输入属性（例如，房屋的生活区域，如我们之前的示例中）是连续值，将其离散化也是很常见的，即将其转换为一小组离散值，然后应用朴素贝叶斯。例如，如果我们使用某些特征 $x_i$ 来表示生活区域，我们可以将连续值离散化如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Living area (sq. feet)&lt;/th&gt;
&lt;th&gt;&amp;lt; 400&lt;/th&gt;
&lt;th&gt;400–800&lt;/th&gt;
&lt;th&gt;800–1200&lt;/th&gt;
&lt;th&gt;1200–1600&lt;/th&gt;
&lt;th&gt;&amp;gt; 1600&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$x_i$&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;因此，对于居住面积为890平方英尺的房屋，我们将相应特征 $x_i$ 的值设置为 3然后我们可以应用朴素贝叶斯算法，并使用多项分布对 $p(x_i \mid y)$ 建模，如前所述。当原始的连续值属性没有通过多元正态分布很好地建模时，
对特征进行离散化并使用朴素贝叶斯（而不是 GDA）通常会产生更好的分类器。&lt;/p&gt;
&lt;h2 id="拉普拉斯平滑"&gt;拉普拉斯平滑
&lt;/h2&gt;&lt;p&gt;我们已经描述过的朴素贝叶斯算法可以很好地解决许多问题，但是有一个简单的改变可以使它更好地工作，特别是对于文本分类。让我们简要讨论当前形式的算法问题，然后讨论我们如何解决它。&lt;/p&gt;
&lt;p&gt;考虑垃圾邮件/电子邮件分类，让我们假设，在完成 CS229 并完成项目的优秀工作后，您决定在 2003 年 6 月左右将您所做的工作提交给 NIPS 会议进行发布因为您最终在电子邮件中讨论会议，所以您也开始收到带有 “nips” 字样的消息。但这是你的第一篇 NIPS 论文，直到这个时候，你还没有看到任何包含 “nips” 这个词的电子邮件；特别是 “nips” 并没有出现在你的垃圾邮件/非垃圾邮件的训练集中。假设 “nips” 是字典中的第 35000 个单词，那么你的朴素贝叶斯垃圾邮件过滤器已经选择了参数$\phi_{35000 \mid y}$ 的最大似然估计值：&lt;/p&gt;
$$
\begin{array}{lcl}
\phi_{35000 \mid y=1}&amp; = &amp;
\displaystyle
\frac{
\sum_{i=1}^{m} \mathbf{1}\{x_{35000}^{(i)} \wedge y^{(i)} = 1\}
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\}
}
= 0\\
\phi_{35000 \mid y=0}&amp; = &amp;
\displaystyle
\frac{
\sum_{i=1}^{m} \mathbf{1}\{x_{35000}^{(i)} \wedge y^{(i)} = 0\}
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\}
}= 0
\end{array}
$$&lt;p&gt;即，因为它之前从未在垃圾邮件或非垃圾邮件训练样本中看到过 “nips”，它认为在任一类型的电子邮件中看到它的概率为零。因此，当试图确定这些包含 “nips” 的消息之一是否是垃圾邮件时，它会计算后验概率，并得到&lt;/p&gt;
$$
\begin{array}{lcl}
p(y = 1 \mid x)&amp; = &amp;
\displaystyle
\frac{
\left( \prod_{i=1}^{n} p(x_i \mid y = 1) \right) p(y = 1)
}{
\left( \prod_{i=1}^{n} p(x_i \mid y = 1) \right) p(y = 1)+
\left( \prod_{i=1}^{n} p(x_i \mid y = 0) \right) p(y = 0)
} \\
&amp; = &amp; \displaystyle \frac{0}{0}
\end{array}
$$&lt;p&gt;这是因为$\prod_{i=1}^{n} p(x_i \mid y)$中的每一项都包含$p(x_{35000} \mid y) = 0$。因此，我们的算法获得 $0/0$，并且不知道如何进行预测。&lt;/p&gt;
&lt;p&gt;更广泛地，仅仅因为你之前没有在有限训练集中看到它，估计一些事件的概率为零在统计上是个坏主意。考虑估计取值在 ${1,\ldots,k}$ 的多项分布随机变量 $z$ 的均值问题。我们可以用$\phi_j = p(z = j)$参数化我们的多项式。给定一组 $m$ 个独立观测变量${z^{(1)}, \ldots, z^{(m)}}$，最大似然估计由下式给出&lt;/p&gt;
$$
\begin{array}{lcl}
\phi_j&amp; = &amp;
\displaystyle
\frac{\sum_{i=1}^{m} \mathbf{1}\{z^{(i)} = j\}}{m}
\end{array}
$$&lt;p&gt;正如我们之前看到的，如果我们使用这些最大似然估计，那么一些 $\phi_j$ 可能最终为零，这就会产生问题。为了避免这种情况，我们可以使用&lt;strong&gt;拉普拉斯平滑&lt;/strong&gt;，它取代上面的估计&lt;/p&gt;
$$
\begin{array}{lcl}
\phi_j&amp; = &amp;
\displaystyle
\frac{\sum_{i=1}^{m} \mathbf{1}\{z^{(i)} = j\} + 1}{m + k}
\end{array}
$$&lt;p&gt;这里，我们对分子加 1，对分母加 $k$。注意$\sum_{j=1}^{k} \phi_j = 1$仍然成立。对于所有 $j$，$\phi_j \neq 0$。在某些（可以说是相当强的）条件下，可以证明拉普拉斯平滑实际上给出了 $\phi_j$ 的最优估计。&lt;/p&gt;
&lt;p&gt;返回我们的朴素贝叶斯分类器，使用拉普拉斯平滑，我们因此得到以下参数估计：&lt;/p&gt;
$$
\begin{array}{lcl}
\phi_{35000 \mid y=1}
&amp; = &amp;
\displaystyle
\frac{
\sum_{i=1}^{m} \mathbf{1}\{x_{35000}^{(i)} \wedge y^{(i)} = 1\} + 1
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 1\} + 2
}
\\
\phi_{35000 \mid y=0}
&amp; = &amp;
\displaystyle
\frac{
\sum_{i=1}^{m} \mathbf{1}\{x_{35000}^{(i)} \wedge y^{(i)} = 0\} + 1
}{
\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\} + 2
}
\end{array}
$$</description></item><item><title>CS229 Lecture 5</title><link>https://lunatide.tech/p/cs229-lecture-5/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-5/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-5/pic1.jpg" alt="Featured image of post CS229 Lecture 5" /&gt;&lt;h2 id="生成学习算法"&gt;生成学习算法
&lt;/h2&gt;&lt;p&gt;目前为止说过的学习算法的模型都是$p(y \mid x;\theta)$，也就是给定$x$下的$y$的条件分布，以$\theta$为参数。例如，逻辑回归就是以$h_{\theta}(x)=g(\theta^Tx)$作为$p(y \mid x;\theta)$的模型，这里的$g$是一个sigmoid函数，接下来要讲一个不同类型的学习算法。&lt;/p&gt;
&lt;p&gt;设想有这样的一种分类问题，我们要学习基于一个动物的某个特征来辨别它是大象(y=1)还是小狗(y=0)。给定一个训练集，用逻辑回归或者基础版的感知器算法这样的一个算法能找到一条直线，作为区分大象和小狗的边界。然后我们只要看值落在哪个区域就可以分辨出是大象还是小狗。&lt;/p&gt;
&lt;p&gt;或者还有另外一种算法。首先，观察大象，然后我们针对大象的样子来进行建模。然后再观察小狗，针对小狗的样子另外建立一个模型。最后我们只需要把动物和这两个模型比对，越接近哪个就是哪个动物。&lt;/p&gt;
&lt;p&gt;例如逻辑回归之类的直接试图建立$p(y \mid x)$的算法，以及感知器算法等直接用投图的思路来判断对应$X$的值落到了{0，1}中哪个区域的算法，这些都叫判别式学习算法。和之前的这些判别式算法不同，下面我们要讲的新算法是对$p(y \mid x)$和$p(y)$来进行建模。这类算法叫做&lt;strong&gt;生成学习算法&lt;/strong&gt;。例如如果$y$是用来表示一个样例是小狗(0)或者大象(1)，那么$p(x \mid y=0)$就是对小狗特征的分布的建模，反之，$p(x \mid y=1)$就是对大象特征分布的建模。&lt;/p&gt;
&lt;p&gt;在对 $p(y)$（称为&lt;strong&gt;分类先验&lt;/strong&gt;）和 $p(x \mid y)$ 进行建模之后，我们的算法可以使用贝叶斯法则在给定 $x$ 的情况下输出后验分布：
&lt;/p&gt;
$$
p(y \mid x) = \frac{p(x \mid y)p(y)}{p(x)}.
$$&lt;p&gt;这里，分母
&lt;/p&gt;
$$
p(x) = p(x \mid y=1)p(y=1) + p(x \mid y=0)p(y=0)
$$&lt;p&gt;
给出，因此也可以用我们学习到的 $p(x \mid y)$ 和 $p(y)$ 表示。实际上，如果为了进行预测而计算 $p(y \mid x)$，那么我们实际上并不需要计算分母，因为&lt;/p&gt;
$$
\arg\max_y p(y \mid x)
= \arg\max_y \frac{p(x \mid y)p(y)}{p(x)}
= \arg\max_y p(x \mid y)p(y).
$$&lt;h2 id="高斯判别分析"&gt;高斯判别分析
&lt;/h2&gt;&lt;p&gt;我们要学习的第一个生成学习算法就是高斯判别分析，在这个模型里面，我们假设$p(x \mid y)$是一个多元正态分布。所以首先我们简单讲下多元正态分布的一些特点&lt;/p&gt;
&lt;h3 id="多元正态分布"&gt;多元正态分布
&lt;/h3&gt;&lt;p&gt;$n$ 维的多元正态分布，也称为多元高斯分布，由&lt;strong&gt;均值向量&lt;/strong&gt; $\mu \in \mathbb{R}^n$ 以及&lt;strong&gt;协方差矩阵&lt;/strong&gt;$\Sigma \in \mathbb{R}^{n \times n}$ 参数化，其中 $\Sigma \ge 0$ 是半正定对称矩阵。多元正态分布也写为 $\mathcal{N}(\mu, \Sigma)$，其密度由下式给出：&lt;/p&gt;
$$
p(x; \mu, \Sigma)
= \frac{1}{(2\pi)^{n/2} \lvert \Sigma \rvert^{1/2}}
\exp\!\left(
-\frac{1}{2}(x-\mu)^T \Sigma^{-1} (x-\mu)
\right).
$$&lt;p&gt;
在上面的等式中，“$|\Sigma|$”的意思是矩阵 $\Sigma$ 的行列式，对于一个在 $\mathcal{N}(\mu,\Sigma)$ 分布中的随机变量 $X$，其平均值就是$\mu$了
&lt;/p&gt;
$$
E[X] = \int_x x\, p(x;\mu,\Sigma)\, dx = \mu.
$$&lt;p&gt;
向量值随机变量 $Z$ 的&lt;strong&gt;协方差&lt;/strong&gt;定义为$\mathrm{Cov}(Z) = E[(Z - E[Z])(Z - E[Z])^T]$。这概括了实值随机变量方差的概念。协方差也可以定义为$\mathrm{Cov}(Z) = E[ZZ^T] - (E[Z])(E[Z])^T$。如果 $X \sim \mathcal{N}(\mu,\Sigma)$，那么&lt;/p&gt;
$$
\mathrm{Cov}(X) = \Sigma
$$&lt;h3 id="高斯判别分析模型"&gt;高斯判别分析模型
&lt;/h3&gt;&lt;p&gt;假如我们有一个分类问题，其中输入特征$x$是一系列的连续随机变量，那就可以使用高斯判别分析模型，其中对$p(x \mid y)$用多元正态分布来进行建模，这个模型为：
&lt;/p&gt;
$$
\begin{array}{c}
y \sim \mathrm{Bernoulli}(\phi) \\
x \mid y=0 \sim \mathcal{N}(\mu_0,\Sigma) \\
x \mid y=1 \sim \mathcal{N}(\mu_1,\Sigma)
\end{array}
$$&lt;p&gt;分布写出来的具体形式如下：&lt;/p&gt;
$$
\begin{array}{rcl}
p(y) &amp;=&amp; \phi^y(1-\phi)^{1-y} \\
p(x \mid y=0) &amp;=&amp; \dfrac{1}{(2\pi)^{n/2}\,|\Sigma|^{1/2}}
\exp\!\left(-\dfrac{1}{2}(x-\mu_0)^T\Sigma^{-1}(x-\mu_0)\right) \\
p(x \mid y=1) &amp;=&amp; \dfrac{1}{(2\pi)^{n/2}\,|\Sigma|^{1/2}}
\exp\!\left(-\dfrac{1}{2}(x-\mu_1)^T\Sigma^{-1}(x-\mu_1)\right)
\end{array}
$$&lt;p&gt;
这里，我们的模型的参数是 $\phi, \Sigma, \mu_0, \mu_1$。（虽然存在两个不同的均值向量 $\mu_0$ 和 $\mu_1$，但是该模型通常仅使用同一个协方差矩阵 $\Sigma$。）数据的对数似然性由下式给出：&lt;/p&gt;
$$
\ell(\phi,\mu_0,\mu_1,\Sigma)
= \log \prod_{i=1}^m p(x^{(i)}, y^{(i)};\phi,\mu_0,\mu_1,\Sigma)
$$$$
= \log \prod_{i=1}^m
p(x^{(i)} \mid y^{(i)};\mu_0,\mu_1,\Sigma)\,
p(y^{(i)};\phi).
$$&lt;p&gt;通过参数最大化$l$，然后就能找到该参数组合对应的最大似然估计如下
&lt;/p&gt;
$$
\begin{array}{rcl}
\phi
&amp;=&amp; \dfrac{1}{m}\displaystyle\sum_{i=1}^m 1\{y^{(i)} = 1\} \\
\mu_0
&amp;=&amp; \dfrac{\displaystyle\sum_{i=1}^m 1\{y^{(i)} = 0\}\, x^{(i)}}
{\displaystyle\sum_{i=1}^m 1\{y^{(i)} = 0\}} \\
\mu_1
&amp;=&amp; \dfrac{\displaystyle\sum_{i=1}^m 1\{y^{(i)} = 1\}\, x^{(i)}}
{\displaystyle\sum_{i=1}^m 1\{y^{(i)} = 1\}} \\
\Sigma
&amp;=&amp; \dfrac{1}{m}\displaystyle\sum_{i=1}^m
\big(x^{(i)} - \mu_{y^{(i)}}\big)
\big(x^{(i)} - \mu_{y^{(i)}}\big)^T
\end{array}
$$&lt;p&gt;
推导过程如下：&lt;/p&gt;
&lt;p&gt;先观察 $P(x \mid y)$ 的形式，可以得到如下公式&lt;/p&gt;
$$
P(x\mid y)=\frac{1}{(2\pi)^{n/2}\,|\Sigma|^{1/2}}
\exp\!\left(-\frac{1}{2}(x-\mu_y)^{T}\Sigma^{-1}(x-\mu_y)\right)
$$&lt;p&gt;
接着计算 $\log P(x,y)$&lt;/p&gt;
$$
\begin{array}{rcl}
\log P(x,y)
&amp; = &amp;
\log P(x\mid y)P(y)\\
&amp; = &amp;
\log\Bigg(
\frac{1}{(2\pi)^{n/2}\lvert\Sigma\rvert^{1/2}}
\exp\!\left(
-\frac{1}{2}(x-\mu_y)^T\Sigma^{-1}(x-\mu_y)
\right)
\phi^{1(y=1)}
(1-\phi)^{1(y=0)}
\Bigg)\\
&amp; = &amp;
\,-\frac{n}{2}\log(2\pi)
\,-\frac{1}{2}\log\lvert\Sigma\rvert
\,-\frac{1}{2}(x-\mu_y)^T\Sigma^{-1}(x-\mu_y)+ 1(y=1)\log\phi+ 1(y=0)\log(1-\phi)
\end{array}
$$&lt;p&gt;
对数似然函数为：&lt;/p&gt;
$$
\begin{aligned}
\ell(\phi,\mu_0,\mu_1,\Sigma)
&amp;= \log\prod_{i=1}^{m} p(x^{(i)},y^{(i)};\phi,\mu_0,\mu_1,\Sigma) \\
&amp;= \sum_{i=1}^{m}
\log p(x^{(i)},y^{(i)};\phi,\mu_0,\mu_1,\Sigma) \\
&amp;= \sum_{i=1}^{m}\Bigg(
\log\frac{1}{(2\pi)^{n/2}\det(\Sigma)^{1/2}}
-\frac{1}{2}(x^{(i)}-\mu_{y^{(i)}})^T
\Sigma^{-1}(x^{(i)}-\mu_{y^{(i)}})+ 1(y^{(i)}=1)\log\phi+ 1(y^{(i)}=0)\log(1-\phi)
\Bigg)
\end{aligned}
$$&lt;p&gt;
关于 $\phi$ 求梯度
&lt;/p&gt;
$$
\begin{array}{rcl}
\displaystyle\frac{\partial \ell}{\partial \phi}
&amp;=&amp; \displaystyle
\sum_{i=1}^{m}
\left(
\frac{\mathbf{1}\{y^{(i)}=1\}}{\phi}
-\frac{\mathbf{1}\{y^{(i)}=0\}}{1-\phi}
\right)
= 0
&amp;\Longrightarrow&amp;
\displaystyle\sum_{i=1}^{m}\mathbf{1}\{y^{(i)}=1\}
= m\phi
&amp;\Longrightarrow&amp;
\displaystyle
\phi=\frac{1}{m}\sum_{i=1}^{m}\mathbf{1}\{y^{(i)}=1\}
\end{array}
$$&lt;p&gt;关于 $\mu_1,\mu_0$ 求梯度
&lt;/p&gt;
$$
\begin{array}{rcl}
\nabla_{\mu_1}\ell
&amp;=&amp; \displaystyle
\sum_{i=1}^{m}
\Sigma^{-1}(x^{(i)}-\mu_{y^{(i)}})\,
1_{y^{(i)}=1}
= 0
&amp;\Longrightarrow&amp;
\sum_{i=1}^{m}
(x^{(i)}-\mu_1)\,
1_{y^{(i)}=1}
= 0
&amp;\Longrightarrow&amp;
\mu_1=\frac{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\} \, x^{(i)}}{\sum_{i=1}^{m} \mathbf{1}\{y^{(i)} = 0\}}
\end{array}
$$&lt;p&gt;
关于 $\Sigma$ 的极大似然估计
&lt;/p&gt;
$$
\nabla_A \log|A| = (A^{-1})^T,
\nabla_A(x^T A y) = xy^T
$$$$
\begin{array}{rcl}
\nabla_{\Sigma^{-1}}\ell
&amp;=&amp; \nabla_{\Sigma^{-1}}
\left(
\frac{m}{2}\log|\Sigma^{-1}|
\right)
-\frac{1}{2}\nabla_{\Sigma^{-1}}
\sum_{i=1}^{m}
(x^{(i)}-\mu_{y^{(i)}})^T
\Sigma^{-1}
(x^{(i)}-\mu_{y^{(i)}})
= 0
&amp;\Longrightarrow&amp;
\displaystyle
\frac{m}{2}\Sigma
-\frac{1}{2}\sum_{i=1}^{m}
(x^{(i)}-\mu_{y^{(i)}})
(x^{(i)}-\mu_{y^{(i)}})^T
= 0
&amp;\Longrightarrow&amp;
\displaystyle
\Sigma
=\frac{1}{m}\sum_{i=1}^{m}
(x^{(i)}-\mu_{y^{(i)}})
(x^{(i)}-\mu_{y^{(i)}})^T
\end{array}
$$&lt;p&gt;因此结论成立。&lt;/p&gt;
&lt;h2 id="讨论高斯判别分析与logistic回归"&gt;讨论：高斯判别分析与logistic回归
&lt;/h2&gt;&lt;p&gt;高斯判别分析模型与逻辑回归有很有趣的相关性。如果我们把变量 $ p(y = 1 \mid x; \phi, \mu_0, \mu_1, \Sigma) $作为一个 \(x\) 的函数，就会发现可以用如下的形式来表达：&lt;/p&gt;
$$
p(y = 1 \mid x; \phi, \Sigma, \mu_0, \mu_1)=\frac{1}{1 + \exp(-\theta^T x)}
$$&lt;p&gt;其中的 \(\theta\) 是对 \(\phi, \Sigma, \mu_0, \mu_1\) 的某种函数。这就是逻辑回归（也是一种判别分析算法）用来对\( p(y = 1 \mid x) \)建模的形式。&lt;/p&gt;
&lt;p&gt;Proof：&lt;/p&gt;
&lt;p&gt;先计算 \( P(x) \)&lt;/p&gt;
$$
P(x)=P(y = 1) P(x \mid y = 1)+P(y = 0) P(x \mid y = 0)
$$$$
\left.
\begin{array}{l}
P(x)=
\phi
\dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac{1}{2}(x-\mu_1)^T \Sigma^{-1}(x-\mu_1)
\right)+
(1-\phi)
\dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac{1}{2}(x-\mu_0)^T \Sigma^{-1}(x-\mu_0)
\right)
\end{array}
\right.
$$&lt;p&gt;利用贝叶斯公式计算 \( P(y \mid x) \)，分别对 \( y = 1, y = 0 \) 计算：&lt;/p&gt;
$$
P(y \mid x)=
\frac{P(x \mid y) P(y)}{P(x)}
$$&lt;p&gt;因此，&lt;/p&gt;
$$
\left.
\begin{array}{lcl}
P(y=1 \mid x)
&amp; = &amp;
\dfrac{P(x \mid y=1)P(y=1)}
{\phi \dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac12 (x-\mu_1)^T\Sigma^{-1}(x-\mu_1)\right)
+(1-\phi)\dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac12 (x-\mu_0)^T\Sigma^{-1}(x-\mu_0)\right)} \\[1em]
&amp; = &amp;
\dfrac{\dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac12 (x-\mu_1)^T\Sigma^{-1}(x-\mu_1)\right)\phi}
{\phi \dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac12 (x-\mu_1)^T\Sigma^{-1}(x-\mu_1)\right)
+(1-\phi)\dfrac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}}
\exp\!\left(-\dfrac12 (x-\mu_0)^T\Sigma^{-1}(x-\mu_0)\right)} \\[1em]
&amp; = &amp;
\dfrac{1}
{1+\dfrac{1-\phi}{\phi}
\exp\!\left(
-\dfrac12 (x-\mu_0)^T\Sigma^{-1}(x-\mu_0)
+\dfrac12 (x-\mu_1)^T\Sigma^{-1}(x-\mu_1)
\right)}
\end{array}
\right.
$$&lt;p&gt;
计算指数部分
&lt;/p&gt;
$$
\left.
\begin{array}{lcl}-\dfrac12 (x-\mu_0)^T\Sigma^{-1}(x-\mu_0)+ \dfrac12 (x-\mu_1)^T\Sigma^{-1}(x-\mu_1)&amp; = &amp;
\dfrac{1}{2}
\Big(
x^T\Sigma^{-1}x- 2\mu_1^T\Sigma^{-1}x+ \mu_1^T\Sigma^{-1}\mu_1 - x^T\Sigma^{-1}x+ 2\mu_0^T\Sigma^{-1}x- \mu_0^T\Sigma^{-1}\mu_0
\Big) \\&amp; = &amp;
\dfrac{1}{2}
\Big(
2(\mu_0^T\Sigma^{-1}-\mu_1^T\Sigma^{-1})x+ \mu_1^T\Sigma^{-1}\mu_1- \mu_0^T\Sigma^{-1}\mu_0
\Big) \\&amp; = &amp;
(\mu_0^T\Sigma^{-1}-\mu_1^T\Sigma^{-1})x+ \dfrac{1}{2}
\big(
\mu_1^T\Sigma^{-1}\mu_1- \mu_0^T\Sigma^{-1}\mu_0
\big)
\end{array}
\right.
$$&lt;p&gt;
so
&lt;/p&gt;
$$
\left.
\begin{array}{lcl}
P(y=1 \mid x)
&amp; = &amp;
\dfrac{1}
{1 + \dfrac{1-\phi}{\phi}
\exp\!\left(
(\mu_0^T\Sigma^{-1}-\mu_1^T\Sigma^{-1})x+ \dfrac12(\mu_1^T\Sigma^{-1}\mu_1-\mu_0^T\Sigma^{-1}\mu_0)
\right)} \\
&amp; = &amp;
\dfrac{1}
{1 + \exp\!\left(
(\mu_0^T\Sigma^{-1}-\mu_1^T\Sigma^{-1})x+ \dfrac12(\mu_1^T\Sigma^{-1}\mu_1-\mu_0^T\Sigma^{-1}\mu_0)+ \ln\dfrac{1-\phi}{\phi}
\right)}
\end{array}
\right.
$$&lt;p&gt;
令
&lt;/p&gt;
$$
\left.
\theta=
(\mu_1^T\Sigma^{-1}-\mu_0^T\Sigma^{-1})^T= \Sigma^{-1}(\mu_1-\mu_0) \qquad
\theta_0=-\dfrac12(\mu_1^T\Sigma^{-1}\mu_1-\mu_0^T\Sigma^{-1}\mu_0)- \ln\dfrac{1-\phi}{\phi}
\right.
$$&lt;p&gt;
从而
&lt;/p&gt;
$$
\left.
p(y=1 \mid x;\phi,\Sigma,\mu_0,\mu_1)=
\dfrac{1}{1+\exp\!\big(-(\theta^T x+\theta_0)\big)}
\right.
$$&lt;p&gt;
那么这两个模型，我们应该如何选择呢？一般来说，高斯判别分析和logistic回归，对同一个训练集，可能给出的判别曲线是不一样的。哪一个更好一点呢？&lt;/p&gt;</description></item><item><title>CS229 Lecture 4</title><link>https://lunatide.tech/p/cs229-lecture-4/</link><pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-4/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-4/pic1.jpg" alt="Featured image of post CS229 Lecture 4" /&gt;&lt;p&gt;现在是2026年01月01号的00:00，该更新note了！新年快乐！&lt;/p&gt;
&lt;h2 id="另外一种最大化ltheta的算法"&gt;另外一种最大化$l(\theta)$的算法
&lt;/h2&gt;&lt;p&gt;回到logistic回归，其中$g(z)$是sigmoid函数，现在让我们讨论一种最大化$l(\theta)$的不同算法&lt;/p&gt;
&lt;p&gt;我们先想一下求一个方程零点的牛顿法。假设我们有一个从实数到实数$ f : \mathbb{R} \to \mathbb{R} $，然后要找到一个$\theta$，来满足 $f(\theta)=0$，其中$0 \in \mathbb{R}$。牛顿法就是对 $\theta$ 进行如下的更新：
&lt;/p&gt;
$$
\theta := \theta - \frac{f(\theta)}{f'(\theta)}\tag{1}
$$&lt;p&gt;
这个方法可以通过一个很自然的解释，我们可以把它理解成用一个线性函数来对函数 $f$ 进行逼近，这条直线是 $f$ 的切线，求解线性函数等于0的位置，并让下一个$\theta$ 的猜测为线性函数为0的地方&lt;/p&gt;
&lt;p&gt;下面是牛顿法的图解&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-4/p1.jpg"
width="887"
height="251"
srcset="https://lunatide.tech/p/cs229-lecture-4/p1_hu_6f45aff28d8f90d3.jpg 480w, https://lunatide.tech/p/cs229-lecture-4/p1_hu_14fecb85cb6da5d2.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="353"
data-flex-basis="848px"
&gt;&lt;/p&gt;
&lt;p&gt;在最左边的图中，我们可以看到函数 $f$ 就是沿着 $y = 0$ 的一条直线。这时候是想要找一个 $\theta$ 来让函数值等于0，这时候发现这个 $\theta$ 值大概在1.3左右。假设我们设定初始值$\theta=4.5$。牛顿法就是在$\theta=4.5$的地方画一条切线（中间的图，这样就给出了下一个 $\theta$ 猜测值的地方，也就是这个切线的零点，大概是2.8。最右面的图中是再次进行一次这样的迭代后的结果，这时候的 $\theta$ 大概为1.8。这样多次迭代过后，很快就能接近 $\theta=1.3$&lt;/p&gt;
&lt;p&gt;牛顿方法给出了一种求解$f(\theta)=0$的方法。如果我们想用它来最大化某些函数 $l$ 该怎么做？$l$ 的最大值对应于其一阶导数 $l&amp;rsquo;(\theta)$ 为零的点。因此，通过令 $f(\theta)=l&amp;rsquo;(\theta)$，我们可以用相同的算法来最大化 $l$，并且我们获得更新规则：
&lt;/p&gt;
$$
\theta := \theta-\frac{l'(\theta)}{l''(\theta)}\tag{2}
$$&lt;p&gt;
（最小化 $l$ 也是使用上述更新规则）&lt;/p&gt;
&lt;p&gt;最后，在我们的logistic回归背景中，$\theta$ 是一个有值的向量，所以我们要对牛顿法进行扩展来适应这个情况。牛顿法进行扩展到多维情况，也叫牛顿-拉普森法（Newton-Raphson method）吗，如下
&lt;/p&gt;
$$
\theta := \theta - H^{-1} \nabla_\theta \ell(\theta)\tag{3}
$$&lt;p&gt;
在这里，\( \nabla \ell(\theta) \) 和往常一样，是 \( \ell(\theta) \) 关于 \( \theta_i \) 的偏导数的向量。而 \(H\) 是一个 \( n \times n \) 矩阵（实际上是\( (n+1) \times (n+1) \)，假设我们包括截距项），称为 Hessian。矩阵的每一项由下式给出：&lt;/p&gt;
$$
H_{ij} = \frac{\partial^2 \ell(\theta)}{\partial \theta_i \, \partial \theta_j}\tag{4}
$$&lt;p&gt;
牛顿法通常都能比（批量）梯度下降法收敛得更快，而且达到最小值所需要的迭代次数也低很多。然而，牛顿法中的单次迭代往往要比梯度下降法的单步耗费更多的性能开销，因为要查找和转换一个 \( n \times n \) 的Hessian矩阵；不过只要这个n不是太大，牛顿法通常还是更快一些。只用牛顿法来logistic回归中求似然函数 $l(\theta)$ 的最大值的时候，得到这个结果的方法叫做&lt;strong&gt;Fisher积分&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="广义线性模型"&gt;广义线性模型
&lt;/h2&gt;&lt;p&gt;到目前为止，我们已经看到了回归样例和分类样例。在回归样例中，我们有 \( y \mid x; \theta \sim \mathcal{N}(\mu, \sigma^2) \)， 在分类中， \( y \mid x; \theta \sim \text{Bernoulli}(\phi) \)， \( \mu \) 和 \( \phi \) 定义为 \( x \) 和 \(\theta\) 的函数。在本节中，我们将展示这两种方法都是更广泛的模型族的特例，称为&lt;strong&gt;广义线性模型（GLM）&lt;/strong&gt;。我们还将展示如何推导 GLM 族中的其他模型，并将其应用于其他分类和回归问题。&lt;/p&gt;
&lt;h3 id="指数族"&gt;指数族
&lt;/h3&gt;&lt;p&gt;为了完成GLM，我们将首先定义指数族分布。我们说一类分布属于指数族，如果它们可以表达为如下形式：
&lt;/p&gt;
$$
p(y; \eta) = b(y)\,\exp\!\big(\eta^{T} T(y) - a(\eta)\big)\tag{5}
$$&lt;p&gt;
在这里，$\eta$ 称为分布的&lt;strong&gt;自然参数&lt;/strong&gt;（也称为&lt;strong&gt;规范参数&lt;/strong&gt;）；$T(y)$是&lt;strong&gt;充分统计量&lt;/strong&gt;，我们目前用的这些分布中通常 $T(y)=y$；而$a(\eta)$是一个&lt;strong&gt;对数分割函数&lt;/strong&gt;。$e^{-a(\eta)}$这个量本质上扮演了归一化常数的角色，也就是确保$p(y;\eta)$的综合或者积分等于1&lt;/p&gt;
&lt;p&gt;定义分布族的$T,a,b$由$\eta$参数化；当我们改变$\eta$的时候，我们在这个族中会获得不同的分布&lt;/p&gt;
&lt;p&gt;现在我们看到的伯努利分布和高斯分布就都属于指数分布族。伯努利分布的均值是$ \phi $，也写作Bernoulli($ \phi $)，确定的分布是$y \in {0,1}$，因此有 $p(y=1;\phi) = \phi;p(y=0;\phi)=1-\phi$。这时候只要修改$ \phi $，我们获得不同均值的伯努利分布。我们现在表明，这类伯努利分布，即通过变化$ \phi $获得的分布，是指数族；即，可以选择$T,a,b$，使得等式(5)恰好成为伯努利分布&lt;/p&gt;
&lt;p&gt;我们现在将伯努利分布改写：
&lt;/p&gt;
$$
\begin{aligned}
p(y; \eta)
&amp;= \phi^{y}(1-\phi)^{1-y} \\
&amp;= \exp\!\big(y \log \phi + (1-y)\log(1-\phi)\big) \\
&amp;= \exp\!\left(\left(\log\frac{\phi}{1-\phi}\right)y + \log(1-\phi)\right)
\end{aligned}
$$&lt;p&gt;
因此，自然参数由$ \phi=log(\phi/(1-\phi)) $给出，有趣的是，如果我们翻转这个定义，用$\eta$来解 $\phi$ 就会得到 $\phi=1/(1+e^{-\eta})$。这正好就是之前我们刚刚见过的sigmoid函数！在我们把logistic回归作为一种广义线性模型（GLM）时还会遇到这个情况。
&lt;/p&gt;
$$
\begin{aligned}
T(y) &amp;= y \\
a(\eta) &amp;= -\log(1-\phi) \\
&amp;= \log(1 + e^{\eta}) \\
b(y) &amp;= 1
\end{aligned}
$$&lt;p&gt;
上面的这组式子就表明了伯努利分布可以写成等式(6)的形式，使用一组合适的$T,a,b$&lt;/p&gt;
&lt;p&gt;接下来继续看高斯分布。回顾一下，当推导出线性回归的时候，\( \sigma^2 \) 的值对 \(\theta\) 和 \( h(x) \) 的最终选择没有影响。因此，我们可以选择任意值的 \( \sigma^2 \) 而不改变任何东西。为了简化下面的推导，让我们令 \( \sigma^2 = 1 \)，然后我们有：
&lt;/p&gt;
$$
\begin{aligned}
p(y; \mu)
&amp;= \frac{1}{\sqrt{2\pi}}
\exp\!\left(-\frac{1}{2}(y-\mu)^2\right) \\
&amp;= \frac{1}{\sqrt{2\pi}}
\exp\!\left(-\frac{1}{2}y^2\right)
\cdot
\exp\!\left(\mu y - \frac{1}{2}\mu^2\right)
\end{aligned}
$$&lt;p&gt;因此，我们看到高斯分布是指数族，并且有：&lt;/p&gt;
$$
\begin{aligned}
\eta &amp;= \mu \\
T(y) &amp;= y \\
a(\eta) &amp;= mu^2/2 \\
&amp;= \eta^2/2 \\
b(y) &amp;= (1/\sqrt{2\pi}) \exp\!\left(-y^2/2\right)
\end{aligned}
$$&lt;p&gt;
指数分布族里面还有很多其他的分布，例如多项式分布，这个待会就能看到；泊松分布，用来对计数类数据进行建模；伽马和指数分布，用于对连续的、非负的随机变量进行建 模，例如时间间隔；$\beta$ 和狄利克雷分布，用于概率的分布；还有很多，在下一节我们会描述建模型的一般方法，其中$y$（给定$ x $和$\theta$）来自任何这些分布&lt;/p&gt;
&lt;h3 id="构造glm"&gt;构造GLM
&lt;/h3&gt;&lt;p&gt;设想你要构建一个模型，来估计在给定的某个小时内来到你的商店的顾客人数（或者是你的网站的页面访问次数），基于某些确定的特征$ x $，例如商店的促销，最近的广告，天气，今天周几等等。我们已经知道泊松分布通常能适合用来对方可数目进行建模。知道了这个之后，怎么来建立一个模型来解决咱们这个具体问题呢？非常幸运的是，泊松分布是属于指数分布族的一个分部，所以我们可以使用一个广义线性模型（GLM）&lt;/p&gt;
&lt;p&gt;更一般地，考虑一个分类或者回归问题，我们希望将某个随机变量$y$的值预测为$ x $的函数。为了得到这个问题的GLM，我们将对给定$ x $和关于我们模型的$y$的条件分布做出以下三个假设：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;$y \mid x, \theta \sim \text{Exponential Family}(\eta)$，其中在给定 \( x \) 和 \(\theta\) 的条件下，\(y\) 的分布属于指数分布族，参数为 \(\eta\)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;给定$ x $，目的是要预测对应这个给定$ x $的T(y)的期望值。在我们的大多数例子中，我们有$T(y)=y$，所以着意味着我们希望我们的预测 $ h(x) $​满足&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$h(x) = E[y \mid x]$。（注意，这个假设通过对 $h_\theta(x)$ 的选择而满足，在逻辑回归和线性回归中都是如此。例如在逻辑回归中，&lt;/p&gt;
$$
h_\theta(x) = P(y = 1 \mid x; \theta)
= 0 \cdot P(y = 0 \mid x; \theta) + 1 \cdot P(y = 1 \mid x; \theta)
= E[y \mid x; \theta]
$$&lt;p&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自然参数 $\eta$ 和输入值 $ x $ 是线性相关的，$\eta = \theta^T x$，或者如果 $\eta$ 是有值的向量，则有 $\eta_i = \theta_i^T x$。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些假设中的第三个看上去是最不和合理的假设，在我们构建GLM的方法中可能更好将其看作“设计选择”，而不是作为假设本身。这三个假设/设计选择将使我们能够得到一种非常优雅的学习算法，即GLM，它具有很多理想的属性，如易于学习。此外，得到的模型通常非常有效地用于对$y$上的不同类型的分布进行建模；例如，我们将很快展示logistic回归和普通最小二乘都可以作为GLM导出&lt;/p&gt;
&lt;h3 id="普通最小二乘"&gt;普通最小二乘
&lt;/h3&gt;&lt;p&gt;为了表明普通最小二乘是GLM模型族的特例，考虑目标变量$y$(也称为GLM术语中的&lt;strong&gt;响应变量&lt;/strong&gt;)是连续的情形，并且我们将给定的$ x $的$y$的条件分布建模为高斯分布&lt;/p&gt;
$$\mathcal{N}(\mu,\ \sigma^2)$$&lt;p&gt;，其中$ \mu $可以是依赖$ x $的一个函数。这样，我们就让上面的指数分布族的($\eta$)分布成为了一个高斯分布。在前面的内容中我们提到过，在把高斯分布写成指数分布族的分布的时候，有$ \mu = \eta $。所以就能得到下面的等式
&lt;/p&gt;
$$
\begin{aligned}
h_\theta(x) &amp;= E[y \mid x; \theta] \\
&amp;= \mu \\
&amp;= \eta \\
&amp;= \theta^T x
\end{aligned}
$$&lt;p&gt;
第一行的等式是基于假设 2；第二个等式是基于定理：当 $ y \mid x; \theta \sim \mathcal{N}(\mu, \sigma^2) $，则 $y$ 的期望就是 $ \mu $；第三个等式是基于假设 1，以及之前我们此前将高斯分布写成指数族分布的时候推导出来的性质 $ \mu = \eta $；最后一个等式就是基于假设 3。&lt;/p&gt;
&lt;h3 id="logistic回归"&gt;Logistic回归
&lt;/h3&gt;&lt;p&gt;我们现在考虑 Logistic 回归。这里我们对二元分类问题感兴趣，所以 $y \in {0, 1}$。鉴于 $y$ 是二值的，因此选择伯努利分布族来模拟给定 $ x $，$y$ 的条件分布似乎是自然的。在我们将伯努利分布表示为指数族分布时，我们得$\phi = \dfrac{1}{1 + e^{-\eta}}$。此外，请注意，如果$ y \mid x; \theta \sim \text{Bernoulli}(\phi) $，因此$E[y \mid x; \theta] = \phi$。因此，遵循与普通最小二乘法相似的推导，我们得到：&lt;/p&gt;
$$
\begin{aligned}
h_\theta(x) &amp;= E[y \mid x; \theta] \\
&amp;= \phi \\
&amp;= \dfrac{1}{1 + e^{-\eta}} \\
&amp;= \dfrac{1}{1 + e^{-\theta^T x}}
\end{aligned}
$$&lt;p&gt;因此，这给出了形如$h_\theta = \dfrac{1}{1 + e^{-\theta^T x}}$的假设函数。如果你以前想知道我们如何提出 logistic 函数$\dfrac{1}{1 + e^{-z}}$的形式，这给出了一个答案：一旦我们假设 $y$ 关于 $ x $ 的分布是伯努利分布，它就是 GLM 和指数族分布产生的结果。为了介绍更多的术语，给定分布的均值作为自然参数的函数$g , (g(\eta) = E[T(y); \eta])$被称为&lt;strong&gt;正则响应函数&lt;/strong&gt;。它的逆$g^{-1}$
称为&lt;strong&gt;正则关联函数&lt;/strong&gt;。因此，高斯族的正则响应函数是单位函数；伯努利的正则响应函数是 logistic 函数。&lt;/p&gt;
&lt;h3 id="softmax回归"&gt;Softmax回归
&lt;/h3&gt;&lt;p&gt;让我们看一下GLM的另一个例子。考虑一个分类问题，其中响应变量$y$可以取$k$个值中的任意一个，因此$y\in{1,2,&amp;hellip;,k}$。例如，我们可能希望将电子邮件分类为垃圾邮件，个人邮件和工作相关邮件等三类，而不是将电子邮件分类为垃圾邮件或非垃圾邮件这两类。响应变量仍然是离散的，但现在可以采用两个以上的值。因此，我们将根据多项分布对其进行建模&lt;/p&gt;
&lt;p&gt;让我们推导下GLM来建模这种多类别分类问题。为此，我们将首先将多项分布表示为指数族分布&lt;/p&gt;
&lt;p&gt;为了在 $k$ 个可能的结果上参数化多项分布，我们可以使用 $k$ 个参数$\phi_1, \ldots, \phi_k$指定每个结果的概率。然而，这些参数将是冗余的，或者更正式地，它们将不是独立的（因为知道 $\phi_i$ 的任何 $k-1$ 个值可以唯一地确定最后一个，因为它们必须满足$\sum_{i=1}^k \phi_i = 1$）。因此，我们将仅使用 $k-1$ 个参数，$\phi_1, \ldots, \phi_{k-1}$来参数化多项分布，其中
$\phi_i = p(y = i; \varphi)$，并且$p(y = k; \varphi) = 1 - \sum_{i=1}^{k-1} p(y = i; \varphi)$。为了方便起见，我们令$\phi_k = 1 - \sum_{i=1}^{k-1} \phi_i$，但我们应该记住，这不是一个参数，而是由$\phi_1, \ldots, \phi_{k-1}$完全确定。&lt;/p&gt;
&lt;p&gt;为了将多项分布表示为指数族分布，我们将$T(y) \in \mathbb{R}^{k-1}$定义如下：&lt;/p&gt;
$$
T(1)=
\begin{bmatrix}
1\\0\\0\\ \vdots\\0
\end{bmatrix},
\quad
T(2)=
\begin{bmatrix}
0\\1\\0\\ \vdots\\0
\end{bmatrix},
\quad
T(3)=
\begin{bmatrix}
0\\0\\1\\ \vdots\\0
\end{bmatrix},
\ \ldots,\
T(k-1)=
\begin{bmatrix}
0\\0\\0\\ \vdots\\1
\end{bmatrix},
\quad
T(k)=
\begin{bmatrix}
0\\0\\0\\ \vdots\\0
\end{bmatrix}.
$$&lt;p&gt;与我们之前的例子不同，这里我们没有$T(y) = y$；此外，$T(y)$ 现在是 $k-1$ 维向量，而不是实数。我们将用$(T(y))_i$来表示向量 $T(y)$ 的第 $i$ 个元素。&lt;/p&gt;
&lt;p&gt;我们介绍一个非常有用的符号。如果参数为真，则示性函数$1{\cdot}$的值为 $1$，否则为 $0$，即
&lt;/p&gt;
$$
1\{\text{True}\} = 1,\qquad
1\{\text{False}\} = 0.
$$&lt;p&gt;例如，$1{2 = 3} = 0$并且$1{3 = 5 - 2} = 1$。因此，我们也可以将 $T(y)$ 和 $y$ 之间的关系写为$(T(y))_i = 1{y = i}$此外，我们有$E[(T(y))_i] = P(y = i) = \phi_i$&lt;/p&gt;
&lt;p&gt;我们现在准备证明多项式是指数族的成员。我们有：&lt;/p&gt;
$$
\begin{array}{rcl}
p(y;\phi)
&amp;=&amp; \phi_1^{1\{y=1\}} \, \phi_2^{1\{y=2\}} \cdots \phi_k^{1\{y=k\}} \\[0.6em]
&amp;=&amp; \phi_1^{1\{y=1\}} \, \phi_2^{1\{y=2\}} \cdots \phi_k^{\,1-\sum_{i=1}^{k-1} 1\{y=i\}} \\[0.6em]
&amp;=&amp; \phi_1^{(T(y))_1} \, \phi_2^{(T(y))_2} \cdots \phi_k^{\,1-\sum_{i=1}^{k-1} (T(y))_i} \\[0.6em]
&amp;=&amp; \exp\big((T(y))_1 \log(\phi_1) + (T(y))_2 \log(\phi_2) + \cdots + \big(1-\sum_{i=1}^{k-1} (T(y))_i\big)\log(\phi_k)\big) \\[0.6em]
&amp;=&amp; \exp\big((T(y))_1 \log(\phi_1/\phi_k) + (T(y))_2 \log(\phi_2/\phi_k) + \cdots + (T(y))_{k-1} \log(\phi_{k-1}/\phi_k) + \log(\phi_k)\big) \\[0.6em]
&amp;=&amp; b(y)\exp\big(\eta^T T(y) - a(\eta)\big)
\end{array}
$$&lt;p&gt;
其中&lt;/p&gt;
$$
\begin{aligned}
\eta &amp;=
\begin{bmatrix}
\log(\phi_1/\phi_k) \\
\log(\phi_2/\phi_k) \\
\vdots \\
\log(\phi_{k-1}/\phi_k)
\end{bmatrix}, \\
a(\eta) &amp;= -\log(\phi_k), \\
b(y) &amp;= 1.
\end{aligned}
$$&lt;p&gt;
关联函数（对于 $i = 1,\ldots,k$）由下式给出：&lt;/p&gt;
$$
\eta_i = \log \frac{\phi_i}{\phi_k}.
$$&lt;p&gt;为方便起见，我们还定义$\eta_k = \log(\phi_k/\phi_k) = 0$。反转关联函数并导出响应函数，我们得到：&lt;/p&gt;
$$
\begin{aligned}
e^{\eta_i} &amp;= \frac{\phi_i}{\phi_k}, \\
\phi_k e^{\eta_i} &amp;= \phi_i, \\
\phi_k \sum_{i=1}^k e^{\eta_i} &amp;= \sum_{i=1}^k \phi_i = 1.
\end{aligned}
$$&lt;p&gt;这意味着 $\phi_k = 1 / \sum_{i=1}^k e^{\eta_i}$，可以将其代入上式以给出响应函数&lt;/p&gt;
$$
\phi_i = \frac{e^{\eta_i}}{\sum_{j=1}^k e^{\eta_j}}.
$$&lt;p&gt;从 $\eta$ 到 $\phi$ 的映射函数称为 &lt;strong&gt;softmax 函数&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;为了完成我们的模型，我们使用前面给出的假设 3，即 $\eta_i$ 与 $ x $ 线性相关。因此，有$\eta_i = \theta_i^T x$（对于 $i = 1,\ldots,k-1$），其中 $\theta_1,\ldots,\theta_{k-1} \in \mathbb{R}^{n+1}$ 是我们模型的参数。为了符号方便，我们还可以定义 $\theta_k = 0$，
所以 $\eta_k = \theta_k^T x = 0$，如之前所述。因此，我们的模型假设给定 $ x $，$y$ 的条件分布由下式给出：
&lt;/p&gt;
$$
\begin{array}{rcl}
p(y=i \mid x;\theta)
&amp;=&amp; \phi_i \\[6pt]
&amp;=&amp; \dfrac{e^{\eta_i}}{\sum_{j=1}^k e^{\eta_j}} \\[10pt]
&amp;=&amp; \dfrac{e^{\theta_i^T x}}{\sum_{j=1}^k e^{\theta_j^T x}}.
\end{array}
\tag{8}
$$&lt;p&gt;该模型适用于分类问题，其中 $y \in {1,\ldots,k}$，称为 &lt;strong&gt;softmax 回归&lt;/strong&gt;。它是 logistic 回归的推广。&lt;/p&gt;
&lt;p&gt;我们的假设将输出&lt;/p&gt;
$$
\begin{array}{rcl}
h_\theta(x) &amp;=&amp; \mathbb{E}[T(y)\mid x;\theta] \\[0.8em]
&amp;=&amp; \mathbb{E}\!\left[\begin{array}{c}
1\{y=1\} \\
1\{y=2\} \\
\vdots \\
1\{y=k-1\}
\end{array}\;\middle|\; x;\theta\right] \\[1.0em]
&amp;=&amp; \begin{bmatrix}
\phi_1 \\
\phi_2 \\
\vdots \\
\phi_{k-1}
\end{bmatrix} \\[1.0em]
&amp;=&amp; \begin{bmatrix}
\dfrac{e^{\theta_1^T x}}{\sum_{j=1}^k e^{\theta_j^T x}} \\
\dfrac{e^{\theta_2^T x}}{\sum_{j=1}^k e^{\theta_j^T x}} \\
\vdots \\
\dfrac{e^{\theta_{k-1}^T x}}{\sum_{j=1}^k e^{\theta_j^T x}}
\end{bmatrix}
\end{array}
$$&lt;p&gt;
换句话说，对于 $i = 1,\ldots,k$ 的每个值，我们的假设将输出 $p(y=i \mid x;\theta)$ 的概率估计值。（尽管如上定义的 $ h(x) $ 仅为 $k-1$ 维，但由 $1 - \sum_{i=1}^{k-1} \phi_i$显然可以得到 $p(y=k \mid x;\theta)$。）&lt;/p&gt;
&lt;p&gt;最后，让我们讨论参数拟合。类似于我们对普通最小二乘和 logistic 回归的原始推导，如果我们有一个训练集
${(x^{(i)},y^{(i)}); i=1,\ldots,m}$并且想要学习这个模型的参数 $ \theta_i $，我们首先写下对数似然：
&lt;/p&gt;
$$
\begin{array}{rcl}
\ell(\theta)
&amp;=&amp; \displaystyle \sum_{i=1}^m \log p\!\left(y^{(i)} \mid x^{(i)};\theta\right) \\[0.8em]
&amp;=&amp; \displaystyle \sum_{i=1}^m \log \prod_{l=1}^k
\left(
\frac{e^{\theta_l^T x^{(i)}}}
{\sum_{j=1}^k e^{\theta_j^T x^{(i)}}}
\right)^{1\{y^{(i)}=l\}}
\end{array}
$$&lt;p&gt;
为了获得上面的第二行，我们使用等式 (8) 中给出的$p(y \mid x;\theta)$ 的定义。我们现在可以通过使用诸如梯度上升或牛顿法之类的方法，关于 $\theta$ 来最大化 $ \ell(\theta) $，从而获得参数的最大似然估计。&lt;/p&gt;</description></item><item><title>CS231 第六讲 CNN架构</title><link>https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/</link><pubDate>Sun, 28 Dec 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/</guid><description>&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/pic1.jpg" alt="Featured image of post CS231 第六讲 CNN架构" /&gt;&lt;h2 id="cnn架构"&gt;CNN架构
&lt;/h2&gt;&lt;h3 id="常用的层"&gt;常用的层
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;归一层&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;归一层的工作原理分为两个部分，第一步是将输入数据归一化为标准正态分布，均值为0，标准差为1，然后进行缩放和偏移，通过乘以某个值调整中心偏差，再进行偏移以改变均值位置，所有归一化层都采用这样的技术，它们之间的区别在于如何计算统计量，均值和标准差，以及将这些统计量应用到哪些值&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;层归一化&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是最常用的归一化层，如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p1.jpg"
width="2658"
height="1236"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p1_hu_8e5b8c14f46507c5.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p1_hu_6125d2d72a95eac4.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="215"
data-flex-basis="516px"
&gt;&lt;/p&gt;
&lt;p&gt;下面的图片展示了几种不同的归一化方法和它们各自张量的哪些维度上计算均值和方差&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p2.jpg"
width="2678"
height="1124"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p2_hu_8c8070081479b484.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p2_hu_e118f457702eaa3a.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="238"
data-flex-basis="571px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dropout层&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Dropout层的核心思想是在训练时添加随机性，而在测试时移除，目的是让模型难以过拟合训练数据，但会提升泛化能力，具体实现如下图，我们实际上随机将某些输出或激活值归零&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p3.jpg"
width="2300"
height="1066"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p3_hu_d4416d654f911647.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p3_hu_668e2bc786b2af24.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="215"
data-flex-basis="517px"
&gt;&lt;/p&gt;
&lt;p&gt;下面是伪代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34; Vanilla Dropout: Not recommended implementation (see notes below) &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="c1"&gt;# probability of keeping a unit active. higher = less dropout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34; X contains the data &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# forward pass for example 3-layer neural network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;U1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# first dropout mask&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;U1&lt;/span&gt; &lt;span class="c1"&gt;# drop!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;U2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# second dropout mask&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H2&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;U2&lt;/span&gt; &lt;span class="c1"&gt;# drop!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# backward pass: compute gradients... (not shown)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# perform parameter update... (not shown)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ensembled forward pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# NOTE: scale the activations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# NOTE: scale the activations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;上述方法需要注意一点：在预测的时候要乘以dropout概率$p$，这是因为假设输入为$x$，其期望输出为$px$，所以为了保持一致，预测时要乘以dropout概率$p$。这要会产生一个问题：预测时增加了运算量，一个改进方式如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Inverted Dropout: Recommended implementation example.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;We drop and scale at train time and don&amp;#39;t do anything at test time.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="c1"&gt;# probability of keeping a unit active. higher = less dropout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# forward pass for example 3-layer neural network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;U1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# first dropout mask. Notice /p!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;U1&lt;/span&gt; &lt;span class="c1"&gt;# drop!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;U2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="c1"&gt;# second dropout mask. Notice /p!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H2&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;U2&lt;/span&gt; &lt;span class="c1"&gt;# drop!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# backward pass: compute gradients... (not shown)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# perform parameter update... (not shown)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ensembled forward pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# no scaling necessary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;H2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="激活函数"&gt;激活函数
&lt;/h3&gt;&lt;p&gt;激活函数的核心作用是为模型引入非线性&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;sigmoid函数&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;sigmoid函数的表达式如下
&lt;/p&gt;
$$
\sigma(x)=1/(1+e^{-x})
$$&lt;p&gt;
&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p4.jpg"
width="894"
height="764"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p4_hu_b57a1845e02d9f2b.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p4_hu_ba4f7b2f039043ca.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="117"
data-flex-basis="280px"
&gt;&lt;/p&gt;
&lt;p&gt;sigmoid函数主要有以下的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;经过多层sigmoid后，反向传播时梯度会越来越小&lt;/li&gt;
&lt;li&gt;由于Sigmoid函数输出结果都大于0，由乘法门的含义可知，这会导致梯度的符号都相同，这也不利于训练。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;ReLU&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ReLU的表达式如下
&lt;/p&gt;
$$
f(x)=max(0,x)
$$&lt;p&gt;
&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p5.jpg"
width="862"
height="556"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p5_hu_186f2a0b5a4d924d.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p5_hu_e3c81398522eaa.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="155"
data-flex-basis="372px"
&gt;&lt;/p&gt;
&lt;p&gt;ReLU在正区域不会出现梯度消失的情况，但是在负区域还是会出现梯度为0的情况，所以我们基本上覆盖了输入域的一半，这个肯定比sigmoid函数牛逼，并且只需要计算0和x的最大值也比sigmoid函数效率更高&lt;/p&gt;
&lt;p&gt;但是还是有上面的问题，对于任何负输入，会得到零梯度&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GELU&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GELU的表达式如下
&lt;/p&gt;
$$
f(x)=x*\phi(x)
$$&lt;p&gt;
&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p6.jpg"
width="778"
height="620"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p6_hu_8a2ef6b94e0307da.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p6_hu_bee4dcedc10f4dbf.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="125"
data-flex-basis="301px"
&gt;&lt;/p&gt;
&lt;p&gt;GELU在接近零的邻域内保留激活函数的非平坦区域，核心思想就是平滑0处的非连续跳跃&lt;/p&gt;
&lt;p&gt;那么这些CNN中的激活函数在哪里用&lt;/p&gt;
&lt;p&gt;答：通常放在线性算子之后（比如全连接层，卷积层）&lt;/p&gt;
&lt;h3 id="残差网络"&gt;残差网络
&lt;/h3&gt;&lt;p&gt;如果在普通CNN网络上不断堆叠更深的层，不断叠加新层，让网络变得越来越大，会发生什么年？&lt;/p&gt;
&lt;p&gt;他们发现二十层模型的测试误差实际上低于56层模型，你可能会认为这是过拟合导致的，但是其实当我们看训练误差，20层模型的训练误差也更低，如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p7.jpg"
width="1808"
height="928"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p7_hu_4ae60adbd6d07ff5.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p7_hu_fdda0a8249a2e219.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="194"
data-flex-basis="467px"
&gt;&lt;/p&gt;
&lt;p&gt;所以为什么会56层模型表现不如20层模型，更深的模型有更强的表示能力，理论上它们能表示浅层网络能处理的所有模型，因此可能的输入与输出之间的映射关系对于大型网络时小型网络的超集，因为从理论上讲，你可以想象将某些层设置为恒等函数，这些层不做任何操作，如果你将一半的层设置为无操作，你拥有的表示能力与模型完全相同，大小减半，所以说不是这些模型更差，但在表示能力方便，它们实际上更难优化，因为深层网络的可能模型集合更大，并且包含所有浅层网络可能学习到的模型&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p8.jpg"
width="1302"
height="504"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p8_hu_a95c3166ff23aa69.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p8_hu_f64b8a8f1ae25326.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="258"
data-flex-basis="620px"
&gt;&lt;/p&gt;
&lt;p&gt;那么深层模型如何至少与浅层模型一样好，如下图，我们有一个一层模型和一个两层模型，如果我们让其中一个层几乎成为单位矩阵，模型至少应该和浅层模型一样好&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p9.jpg"
width="1746"
height="832"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p9_hu_c6caf2ef4c9ba2c5.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p9_hu_5782450a2169eb04.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="209"
data-flex-basis="503px"
&gt;&lt;/p&gt;
&lt;p&gt;那么我们如何将这种直觉融入模型，我们希望它可以和浅层模型一样优秀，我们通过拟合来实现，所谓的残差映射，而非直接拟合底层映射&lt;/p&gt;
&lt;p&gt;直觉是一种观察到的现象，这些大型网络在训练和测试误差上表现更差，因为它们难以优化，因此直觉是我们需要构建能够轻松模拟浅层网络的模型，使其至少与浅层模型一样好，它们通过添加残差连接实现了这一点，以便轻松复制值，将其融入架构本身，而不是在卷积层之间学习恒等映射&lt;/p&gt;
&lt;h3 id="如何初始化各层的权重值"&gt;如何初始化各层的权重值
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Kaiming初始化&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;hs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Din&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Dout&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dims&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Din&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Dout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Din&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;图像归一化要点总结&lt;/strong&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;code&gt;norm_pixel[i,j,c] = (pixsl[i,j,c] - np.mean(pixel[:,:,c])) / np.std(pixel[:,:,c])&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正则化&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;训练：加入某种形式的随机性
&lt;/p&gt;
$$
y = f_w(x, z)
$$&lt;p&gt;测试：对随机性取平均
&lt;/p&gt;
$$
y = f(x) = E_z [ f(x, z) ] = \int p(z) f(x, z) dz
$$&lt;p&gt;
&lt;strong&gt;数据增强&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1.水平翻转&lt;/p&gt;
&lt;p&gt;这对日常物体很有用，因为大多数物体具有对称性&lt;/p&gt;
&lt;p&gt;2.调整大小和缩减，方案如下&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p10.jpg"
width="1758"
height="892"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p10_hu_48b27289ba26a653.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%85%AD%E8%AE%B2-cnn%E6%9E%B6%E6%9E%84/p10_hu_1dec8882eec0268.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="473px"
&gt;&lt;/p&gt;</description></item><item><title>CS231 第五讲 基于CNN的图像分类</title><link>https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/</link><pubDate>Sat, 27 Dec 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/</guid><description>&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/pic1.jpg" alt="Featured image of post CS231 第五讲 基于CNN的图像分类" /&gt;&lt;h2 id="卷积神经网络"&gt;卷积神经网络
&lt;/h2&gt;&lt;p&gt;我们需要做的就是添加几个可以适合我们的计算图的新类型的节点，具体来说，我们只需要讨论两个运算符就可以构建更强大的网络，就是卷积层，然后是池化层，这是我们在处理图像时经常使用的另一个层&lt;/p&gt;
&lt;p&gt;我们之前介绍的神经网络每层被称作全连接层，它是将图像的像素展平为一个大向量，进行矩阵乘法，进行ReLU，一个大问题是它破坏了图像的空间结构，比如说，图像实际上不是一维物体，是二维得到，二维结构对于这些图像的内容很重要，当你通过将原始像素拉伸称为一个大向量来构建线性分类器时，你基本上忽略了神经网络架构设计中输入数据的重要因素，所以当考虑为图像设计神经网络架构的时候，我们尤其要思考我们的网络还有哪些设计，我们可以将哪些其他计算原语插入到我们的计算图中&lt;/p&gt;
&lt;p&gt;这就引出了卷积神经网络，所以卷积神经网络基本上是一类神经网络架构，它由线性层，非线性层，卷积层，池化层构成，有时还会将其他几个层拼接在一起形成这些神经网络架构，输入原始像素值，，然后输出图像的一些预测或者分数&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p1.jpg"
width="1884"
height="920"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p1_hu_7f484792e3d5a2bd.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p1_hu_c04c426c947ddfac.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="204"
data-flex-basis="491px"
&gt;&lt;/p&gt;
&lt;p&gt;它们的一般结构通常会有一些前缀，一些网络主体，即卷积层，池化层和非线性的一些交错序列，可以被认为是为图像提取一些有用的特征表示，在此基础上，它们通常会是一些全连接层，有时候只有一层，有时候不止一层，可以将其视作多重感知器完全连接的网络分类器，它位于网络卷积部分的顶部，并从中获取特征&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p2.jpg"
width="1842"
height="780"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p2_hu_6ebd7abbac36a606.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p2_hu_8194f7fd1119a775.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="236"
data-flex-basis="566px"
&gt;&lt;/p&gt;
&lt;p&gt;至关重要的是，通过最小化训练数据集的损失，整个系统通过梯度下降进行端到端调整&lt;/p&gt;
&lt;p&gt;全连接层如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p3.jpg"
width="1856"
height="864"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p3_hu_8f108b048ce14fb1.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p3_hu_fc50cc9854d0998f.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="214"
data-flex-basis="515px"
&gt;&lt;/p&gt;
&lt;p&gt;卷积层如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p4.jpg"
width="1860"
height="942"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p4_hu_a5534140f8f69ad6.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p4_hu_22c3bf3dbba2b659.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="473px"
&gt;&lt;/p&gt;
&lt;p&gt;也就是说我们不会把图像拉伸成一个大向量，而是要保持图像的3D空间结构&lt;/p&gt;
&lt;h3 id="卷积层"&gt;卷积层
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p5.jpg"
width="1898"
height="950"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p5_hu_50afeb2575b26d79.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p5_hu_bf9139844d4782b3.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="199"
data-flex-basis="479px"
&gt;&lt;/p&gt;
&lt;p&gt;因此，如上图，我们将卷积滤波器放到图像的某个块上，这个5x5x3滤波器和该空间位置上的某个5x5x3的图像块对其，然后计算两者之间的内积，这将为我们提供一个标量数，告诉我们该图像块和模版的对齐程度&lt;/p&gt;
&lt;p&gt;然后我们重复这个过程并且将该模块滑动到图像中的任何位置，把模版放在每个地方，我们将再次计算模版的匹配分数，该分数表示该图像部分与该模版的匹配程度，然后我们把得到的 匹配分数放在一个平面上，现在这平面是一个二维平面，基本上每个点都对应着平面上每个点与输入图像对应部分和滤波器的对齐程度&lt;/p&gt;
&lt;p&gt;而实际中的运算我们需要多个滤波器，输出如下&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p6.jpg"
width="1868"
height="946"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p6_hu_9e0ed8521290e798.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p6_hu_7ed72043e8a79128.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="473px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;填充&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在进行卷积的时候，特征图的空间尺寸会缩小，我们想让所有东西保持相同的尺寸，所以一个技巧就是填充&lt;/p&gt;
&lt;p&gt;在计算卷积运算符之前，会在周围添加额外的零，效果如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p7.jpg"
width="1888"
height="938"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p7_hu_4df61768fc591fd2.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p7_hu_521c56bdb1716611.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="201"
data-flex-basis="483px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感受野&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在考虑单个卷积时，每个输出都在查看输入的这个局部区域，因第一层卷积的输出只能查看图像的一部分，其大小与正在学习的卷积核相同&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p8.jpg"
width="1892"
height="966"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p8_hu_281d99955ecceb95.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p8_hu_51ab310e9a941e27.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="195"
data-flex-basis="470px"
&gt;&lt;/p&gt;
&lt;p&gt;但是如果我们构建了一个将多个卷积堆叠在一起的ConvNet，如上图，这些感受野就会通过网络被放大，这里的每个条目都依赖于它之前一层的局部区域，因此，当有这些卷积时，即使每个单独的卷积都在查看它之前层中的局部邻域，当在多个层中堆叠卷积时，每个卷积所查看的原始输入的有效大小都会在网络的过程中增长，我们称之为有效感受野，所以卷积的有效感受野基本就是原始图像中有多少像素有机会影响下游网络的一次激活，这个有效感受野基本上随着卷积层的数量线性增长&lt;/p&gt;
&lt;p&gt;有一个问题，当我们最终在网络末端做出分类决策时，我们希望我们的分类决策基本上能够汇总整个图像的全局信息，但是要很多卷积层才能做到这点，所以这里的技巧就是添加一些方法来更快增加有效感受野&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;步幅&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;上面的例子中，我们每次移动一个单位，而实际中可以移动多个单位，每次移动的单位数量就叫做步长，记作S，输出如下&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p9.jpg"
width="1866"
height="912"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p9_hu_b8c2eb236a09fcae.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p9_hu_bad6e35a193f2c37.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="204"
data-flex-basis="491px"
&gt;&lt;/p&gt;
&lt;h3 id="池化层"&gt;池化层
&lt;/h3&gt;&lt;p&gt;池化层基本上是神经网络内部下采样的另一种方法，因此，我们看到，步幅卷积是我们可以在神经网络内部进行下采样的一种方法，下蔡样可以让我们在深入网络时更快地建立感受场，池化层是一种廉价的下采样方法，不需要花费太多的计算。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p10.jpg"
width="1836"
height="836"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p10_hu_64bf9648fa2bb8e4.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p10_hu_df4dffc9c8996704.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="219"
data-flex-basis="527px"
&gt;&lt;/p&gt;
&lt;p&gt;我们采用了几种不同的下采样机制，最常用的实际上是最大值，被称为最大池化，因此，在最大池化中，我们要做的是取单个深度切片，将其划分为不重叠的区域&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p11.jpg"
width="1818"
height="922"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p11_hu_d5481af0e5f64666.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%94%E8%AE%B2-%E5%9F%BA%E4%BA%8Ecnn%E7%9A%84%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p11_hu_2e5489d313549c7f.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="473px"
&gt;&lt;/p&gt;
&lt;p&gt;所以池化的目的是降低数据维度，图片展示的是最大值池化，实际中还有平均值池化&lt;/p&gt;
&lt;p&gt;池化层也有对应的步长，填充参数，计算维度的方式和之前相同&lt;/p&gt;</description></item><item><title>CS231 第四讲 神经网络与反向传播</title><link>https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/</link><pubDate>Fri, 12 Dec 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/</guid><description>&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/pic1.jpg" alt="Featured image of post CS231 第四讲 神经网络与反向传播" /&gt;&lt;h2 id="反向传播"&gt;反向传播
&lt;/h2&gt;&lt;p&gt;反向传播是计算梯度的一种方法，这种方法需要利用计算图，计算图的每个节点表示我们执行的每一步计算，例如上一讲讲的损失函数的计算图如下&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p1.jpg"
width="1656"
height="822"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p1_hu_aad3f6f24ba91cf5.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p1_hu_ca20441b05b28d.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="201"
data-flex-basis="483px"
&gt;&lt;/p&gt;
&lt;p&gt;第一个节点计算得分s，第二个节点计算折页损失，最后一个节点计算总损失（加上正则项）&lt;/p&gt;
&lt;p&gt;计算图的计算步骤分为前向传播以及反向传播，刚刚描述的步骤为前向传播，现在我们结合下图理解反向传播计算梯度的思路&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p2.jpg"
width="1700"
height="860"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p2_hu_f3a6d3794d241d32.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p2_hu_88d45fae6ec3b452.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="474px"
&gt;&lt;/p&gt;
&lt;p&gt;对于每个节点，假设我们已知输出的梯度$\frac{\partial L}{\partial z}$，现在我们要计算输入的梯度$\frac{\partial L}{\partial x}$,$\frac{\partial L}{\partial y}$，计算的思路很简单，利用链式法则，首先计算“局部梯度”$\frac{\partial z}{\partial x}$,$\frac{\partial z}{\partial y}$，然后利用链式法则可得
&lt;/p&gt;
$$
\frac{\partial L}{\partial x}
=\frac{\partial L}{\partial z}\,\frac{\partial z}{\partial x}\,\frac{\partial L}{\partial y}
=\frac{\partial L}{\partial z}\,\frac{\partial z}{\partial y}
$$&lt;p&gt;
计算过程如下图所示&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p3.jpg"
width="1776"
height="926"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p3_hu_d912d6d39e3633e0.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E5%9B%9B%E8%AE%B2-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD/p3_hu_3a0d14b51fa5ba50.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="460px"
&gt;&lt;/p&gt;
&lt;p&gt;如果节点有多个输出$z_j$，那么输入的梯度$\frac{\partial L}{\partial x}$,$\frac{\partial L}{\partial y}$需要累加，具体公式为
&lt;/p&gt;
$$
\frac{\partial L}{\partial x}
= \sum_{j} \frac{\partial L}{\partial z_{j}} \,\frac{\partial z_{j}}{\partial x},
\qquad
\frac{\partial L}{\partial y}
= \sum_{j} \frac{\partial L}{\partial z_{j}} \,\frac{\partial z_{j}}{\partial y}
$$&lt;p&gt;
计算过程如下图所示&lt;/p&gt;</description></item><item><title>CS229 Lecture 3</title><link>https://lunatide.tech/p/cs229-lecture-3/</link><pubDate>Wed, 10 Dec 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-3/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-3/pic1.jpg" alt="Featured image of post CS229 Lecture 3" /&gt;&lt;h2 id="最小二乘法的概率解释"&gt;最小二乘法的概率解释
&lt;/h2&gt;&lt;p&gt;在面对回归问题的时候，我们会思考，为什么选择线性回归，为什么选择最小二乘法成本函数 &lt;strong&gt;J&lt;/strong&gt; ？在本节里会给出一系列的概率基本假设，基于这些假设，可以推出最小二乘法是一种非常自然的算法&lt;/p&gt;
&lt;p&gt;首先假设目标变量和输入值存在下面这种等量关系
&lt;/p&gt;
$$
y^{(i)}=\theta^Tx^{(i)}+\epsilon^{(i)}
$$&lt;p&gt;
上式中的 $\epsilon^{(i)}$ 是误差项，用于存放由于建模所忽略的变量导致的效果或者随机的噪音信息。进一步假设$\epsilon^{(i)}$是独立同分布的（IID），服从高斯分布，其平均值为0，方差为$\sigma^2$，这样就可以把这个假设写成&amp;quot;$\epsilon^{(i)} \sim N(0,\sigma^2)$&amp;quot;，然后$\epsilon^{(i)}$的密度函数就是：
&lt;/p&gt;
$$
p(\epsilon^{(i)}) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(\epsilon^{(i)})^2}{2\sigma^2} \right)
$$&lt;p&gt;这意味着存在下面的等量关系：&lt;/p&gt;
$$
p(y^{(i)} \mid x^{(i)}; \theta) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(y^{(i)} - \theta^T x^{(i)})^2}{2\sigma^2} \right)
$$&lt;p&gt;
这里的记号 &amp;ldquo;$p(y^{(i)} \mid x^{(i)}; \theta)$&amp;ldquo;表示的是这是一个对于给定$x^{(i)}$的$y^{(i)}$的分布，用$\theta$进行了参数化，这里不能用&amp;rdquo;$p(y^{(i)} \mid x^{(i)}, \theta)$&amp;ldquo;来当作条件，因为$\theta$并不是一个随机变量，也可以将$y^{(i)}$的分布写成$y^{(i)} \mid x^{(i)};\theta \sim N(\theta^Tx^{(i)},\sigma^2)$&lt;/p&gt;
&lt;p&gt;给定一个 $X$ 为设计矩阵，包含了全部$x^{(i)}$，然后再给定$\theta$，那么$y^{(i)}$的分布是什么？数据的概率由$p(\vec{y} \mid X;\theta)$的形式给出。在$\theta$取某个固定值的情况下，这个等式通常可以看作一个$\vec{y}$的函数。当我们把它当作 $\theta$ 的函数时，就称它为似然函数
&lt;/p&gt;
$$
L(\theta)=L(\theta;X,\vec{y})= p(\vec{y} \mid X;\theta)
$$&lt;p&gt;
结合之前对 $\epsilon^{(i)}$ 的独立性假设(这里对$y^{(i)}$ 以及给定的$x^{(i)}$也都做同样假设)，就可以把上面这个等式改写成下面的形式
&lt;/p&gt;
$$
\begin{aligned}
L(\theta) &amp;= \prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}; \theta) \\
&amp;= \prod_{i=1}^{m} \left[ \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(y^{(i)} - \theta^T x^{(i)})^2}{2\sigma^2} \right) \right]
\end{aligned}
$$&lt;p&gt;
现在，给定了 $y^{(i)}$ 和 $x^{(i)}$ 之间关系的概率模型了，用什么方法来选择咱们对参数 $\theta$ 的最佳猜测呢，最大似然法告诉我们要选择能让数据的似然函数尽可能大的 $\theta$ 。也就是说，咱们要找的 $\theta$ 能够让函数 $L(\theta)$ 取到最大值&lt;/p&gt;
&lt;p&gt;为了运算方便，实际中我们选择最大化对数似然函数$l(\theta)$:
&lt;/p&gt;
$$
\begin{aligned}
\ell(\theta) &amp;= \log L(\theta) \\
&amp;= \log \prod_{i=1}^{m} \left[ \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(y^{(i)} - \theta^T x^{(i)})^2}{2\sigma^2} \right) \right] \\
&amp;= \sum_{i=1}^{m} \log \left[ \frac{1}{\sqrt{2\pi}\sigma} \exp\left( -\frac{(y^{(i)} - \theta^T x^{(i)})^2}{2\sigma^2} \right) \right] \\
&amp;= m \log \frac{1}{\sqrt{2\pi}\sigma}
\;-\; \frac{1}{2\sigma^2} \sum_{i=1}^{m} (y^{(i)} - \theta^T x^{(i)})^2
\end{aligned}
$$&lt;p&gt;
因此，对$l(\theta)$的最大值也就意味着下面这个子式取到最小值
&lt;/p&gt;
$$
\frac{1}{2} \sum_{i=1}^{m} (y^{(i)} - \theta^T x^{(i)})^2
$$&lt;p&gt;
上式即为$J(\theta)$，我们最初的最小二乘成本函数&lt;/p&gt;
&lt;p&gt;总结：在对数据进行概率假设的基础上，最小二乘回归得到的 $\theta$ 和最大似然法估计的 $\theta$ 是一致的。所以这是一系列的假设，其前提是认为最小二乘回归能够被判定为一种非常自然的方法，这种方法正好就进行了最大似然估计&lt;/p&gt;
&lt;p&gt;还要注意，在刚才的讨论中，我们最终对 $\theta$ 的选择并不依赖 $\sigma^2$ ，而且也确实在不知道 $\sigma^2$ 的情况下就找到了结果。&lt;/p&gt;
&lt;h2 id="局部加权线性回归"&gt;局部加权线性回归
&lt;/h2&gt;&lt;p&gt;假如问题从 $x \in R$来预测 y。下面第一幅图显示了使用 $y=\theta_0+\theta_1x$来对一个数据集来进行拟合。我们明显能看出来这个数据的趋势不是一条严格的直线，所以用直线进行的拟合就不是好的方法。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-3/p1.jpg"
width="1494"
height="430"
srcset="https://lunatide.tech/p/cs229-lecture-3/p1_hu_451db920bd6b617.jpg 480w, https://lunatide.tech/p/cs229-lecture-3/p1_hu_4b4f8c0d802f27a8.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="347"
data-flex-basis="833px"
&gt;&lt;/p&gt;
&lt;p&gt;那要是我们添加一个二次项，用 $y=\theta_0 + \theta_1x + \theta_2x^2$来拟合（上面中间的图），明显如果我们对特征补充得越多，效果就越好。不过增加太多的特征也会造成危险，看第三张图就是拟合5项多项式$y=\sum_{j=0}^{5}\theta_jx^j$的结果，可以看到，虽然拟合曲线完美地通过了所有当前数据集中的数据，但我们明显不能认为这个曲线是一个合适的预测工具，比如针对不同的居住面积 $x$ 来预测房屋价格 $y$ ，左边的图是一个&lt;strong&gt;欠拟合&lt;/strong&gt;的例子，明显看到漏掉了数据集中的结构信息，而最右边的图是&lt;strong&gt;过拟合&lt;/strong&gt;的例子&lt;/p&gt;
&lt;p&gt;因此，如上面例子所示，特征的选择对于确保学习算法的良好性能很重要，在本节，我们会简单地谈谈局部加权线性回归算法，这里假设有足够的训练数据，使得特征选择不那么重要&lt;/p&gt;
&lt;p&gt;在原始版本的线性回归算法中，要对一个查询点 $x$ 进行预测，比如要衡量 $h(x)$，要经过下面的步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用参数 $\theta$ 进行拟合，让数据集中的值与拟合算出的值差值平方 $(y^{(i)} - \theta^Tx^{(i)})^2$最小（最小二乘法的思想）&lt;/li&gt;
&lt;li&gt;输出 $\theta^Tx$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相应地，在 LWR 局部加权线性回归的方法中，步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用参数 $\theta$ 进行拟合，让加权距离$w^{(i)}(y^{(i)}-\theta^Tx^{(i)})^2$最小&lt;/li&gt;
&lt;li&gt;输出$\theta^Tx$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面式子中的 $w^{(i)}$是非负的权值，直观地，如果$w^{(i)}$对于特定的 $i$ 很大，那么在选择 $\theta$ 时，我们将努力使 $(y^{(i)}-\theta^Tx^{(i)})$变小。如果$w^{(i)}$很小，则拟合中几乎忽略了 $(y^{(i)}-\theta^Tx^{(i)})^2$ 误差项&lt;/p&gt;
&lt;p&gt;对于权值的选择可以使用下面这个比较标准的公式：
&lt;/p&gt;
$$
w^{(i)}=\exp(- \frac{(x^{(i)}-x)^2}{2\tau^2} )
$$&lt;p&gt;
参数$\tau$控制训练样本的权重随着其$x^{(i)}$距查询点$x$的距离而下降的速度，$\tau$称为&lt;strong&gt;带宽参数&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;局部加权线性回归是我们看到的&lt;strong&gt;非参数&lt;/strong&gt;算法的第一个例子。我们之前看到的(未加权)线性回归算法被称为&lt;strong&gt;参数&lt;/strong&gt;算法，因为其具有固定的，有限数量的参数($\theta$)，这些参数由数据拟合。一旦我们拟合了$\theta_i$并将它们存储起来，我们就不再需要保留训练数据来做出对未来的预测，相反，如果用局部加权线性回归算法，我们就必须一直保留着整个训练集，这里的&amp;quot;非参数&amp;quot;粗略的指为了呈现出假设h遂着数据集的规模大增长而线性增长，我们需要用一定顺序保存一些数据的规模&lt;/p&gt;
&lt;h2 id="分类与逻辑回归"&gt;分类与逻辑回归
&lt;/h2&gt;&lt;p&gt;分类问题其实和回归问题很像，只不过我们现在要来预测的$y$的值只局限于少数的若干个离散值。首先关注的是二值化分类问题，也就是说咋们要判断的 $y$ 只有两个取值，0或者1（这里说到的大多数内容也将推广到多类情况）。例如，如果我们正在尝试为电子邮件构建垃圾邮件分类器，则 $x^{(i)}$ 可能是电子邮件的某些特征，如果是垃圾邮件，则$y$为1，否则为0。0也称为&lt;strong&gt;负类&lt;/strong&gt;，1表示&lt;strong&gt;正类&lt;/strong&gt;，它们有时也用符号&amp;rdquo;-&amp;ldquo;和&amp;rdquo;+&amp;ldquo;表示，给定$x^{(i)}$，相应的$y^{(i)}$也称为训练样本的&lt;strong&gt;标签&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="logistic回归"&gt;Logistic回归
&lt;/h3&gt;&lt;p&gt;我们可以忽略$y$是离散值的事实来处理分类问题，并使用我们的旧线性回归算法来尝试预测给定的$x$的$y$。但是，很容易构造此方法的效果非常差的例子。直觉上，当我们知道$y \in {0,1}$，所以$h_0(x)$的值如果大于1或者小于0就没有意义了，就是说$y$的值必然应当是0和1这两个值中的一个&lt;/p&gt;
&lt;p&gt;所以咱们就改变一下假设函数$h_0(x)$的形式，来解决这个问题。比如咱们可以选择下面这个函数：
&lt;/p&gt;
$$
h_0(x)=g(\theta^Tx)= \frac{1}{1+e^{-\theta^Tx}}
$$&lt;p&gt;
其中有:
&lt;/p&gt;
$$
g(z) = \frac{1}{1+e^{-z}}
$$&lt;p&gt;
这个函数就是我们所熟悉的sigmoid函数，也叫做logistic函数，下面是它的图像&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-3/p2.jpg"
width="976"
height="694"
srcset="https://lunatide.tech/p/cs229-lecture-3/p2_hu_bc222519dbcf63cf.jpg 480w, https://lunatide.tech/p/cs229-lecture-3/p2_hu_388959615ff42ec7.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="140"
data-flex-basis="337px"
&gt;&lt;/p&gt;
&lt;p&gt;注意到，当 $z \to \infty$ 时 $g(z)$ 趋近于 1，而当 $z \to -\infty$ 时 $g(z)$ 趋近于 0。 此外，$g(z)$ 和 $h(x)$ 的值总是在 0 和 1 之间波动。 我们保持 $x_0 = 1$ 的约定，所以$\theta^T x = \theta_0 + \sum_{j=1}^{n} \theta_j x_j$&lt;/p&gt;
&lt;p&gt;现在咱们就把 g 作为选定的函数了。当然其他的从0到1之间光滑递增的函数也可以使用，不过后面我们会了解到选择g的一些原因，对这个逻辑函数的选择是很自然的。在继续深入之前，在继续深入之前，下面通过导数讲解下这个函数的一些性质
&lt;/p&gt;
$$
\begin{aligned}
g'(z) &amp;= \frac{d}{dz} \frac{1}{1 + e^{-z}} \\
&amp;= \frac{1}{(1 + e^{-z})^{2}} \, (e^{-z}) \\
&amp;= \frac{1}{(1 + e^{-z})} \left( 1 - \frac{1}{1 + e^{-z}} \right) \\
&amp;= g(z)(1 - g(z)).
\end{aligned}
$$&lt;p&gt;
给定了逻辑回归模型了，咱们怎么去拟合一个合适的 $\theta$ 呢？我们之前已经看到了在一系列前提下，最小二乘法回归可以通过最大似然估计来推出，那么接下来就给我们这个分类模型做一系列的统计学假设，然后用最大似然法拟合参数&lt;/p&gt;
&lt;p&gt;首先假设：
&lt;/p&gt;
$$
P(y = 1 \mid x;\theta) = h_\theta(x)
$$$$
P(y = 0 \mid x;\theta) = 1 - h_\theta(x)
$$&lt;p&gt;更简洁的写法是：&lt;/p&gt;
$$
p(y \mid x;\theta) = (h_\theta(x))^{y}\,(1 - h_\theta(x))^{1 - y}
$$&lt;p&gt;
假设m个训练样本都是各自独立生成的，那么就可以按如下的方式来写参数的似然函数：
&lt;/p&gt;
$$
\begin{aligned}
L(\theta) &amp;= p(\vec{y} \mid X; \theta) \\
&amp;= \prod_{i=1}^{m} p(y^{(i)} \mid x^{(i)}; \theta) \\
&amp;= \prod_{i=1}^{m} \left( h_\theta(x^{(i)})^{\,y^{(i)}} \, (1 - h_\theta(x^{(i)}))^{\,1 - y^{(i)}} \right)
\end{aligned}
$$&lt;p&gt;
然后和之前一样，取个对数就很容易计算最大值
&lt;/p&gt;
$$
\begin{aligned}
\ell(\theta) &amp;= \log L(\theta) \\
&amp;= \sum_{i=1}^{m}\Big( y^{(i)} \log h(x^{(i)}) + \big(1 - y^{(i)}\big)\log\big(1 - h(x^{(i)})\big) \Big)
\end{aligned}
$$&lt;p&gt;
怎么让似然函数最大？就跟之前在线性回归的时候用了求导数的方法类似，咱们这次就是用&lt;strong&gt;梯度上升法&lt;/strong&gt;。还是写成向量的形式，然后更新，就是$\theta := \theta + \alpha \nabla_{\theta}\ell(\theta)$。（因为找最大值，所以是加号），还是先从只有一组训练样本(x,y)开始，然后求导数来退出随机梯度上升规则：
&lt;/p&gt;
$$
\begin{aligned}
\frac{\partial}{\partial \theta_j}\ell(\theta)
&amp;= \left( y \frac{1}{g(\theta^T x)} - (1-y)\frac{1}{1-g(\theta^T x)} \right)
\frac{\partial}{\partial \theta_j} g(\theta^T x) \\
&amp;= \left( y \frac{1}{g(\theta^T x)} - (1-y)\frac{1}{1-g(\theta^T x)} \right)
g(\theta^T x)\bigl(1-g(\theta^T x)\bigr)\,
\frac{\partial}{\partial \theta_j}\theta^T x \\
&amp;= \Bigl( y\bigl(1-g(\theta^T x)\bigr) - (1-y)g(\theta^T x) \Bigr)\,x_j \\
&amp;= \bigl( y - h_\theta(x) \bigr)\,x_j
\end{aligned}
$$&lt;p&gt;上面的式子里，我们用到了对函数求导的定理$g&amp;rsquo;(z)=g(z)(1-g(z))$。然后就用到了随机梯度上升规则：
&lt;/p&gt;
$$
\theta_j := \theta_j + \alpha\bigl(y^{(i)}-h_\theta(x^{(i)})\bigr)x_j^{(i)}
$$&lt;p&gt;
如果我们将其和 &lt;strong&gt;LMS&lt;/strong&gt; 更新规则相对比，就能发现看上去挺相似的；&lt;strong&gt;不过这并不是同一个算法&lt;/strong&gt;，因为这里的$h_0(x^{(i)})$现在定义成了一个$\theta^Tx^{(i)}$的非线性函数尽管如此，我们面对不同的学习问题使用了不同的算法，却得到了看上去一样的更新规则，这是巧合吗，我们学到GLM广义线性模型的时候就会得到答案了。&lt;/p&gt;
&lt;h2 id="题外话感知器学习算法"&gt;题外话：感知器学习算法
&lt;/h2&gt;&lt;p&gt;现在简单聊一个算法，它的历史很有趣，并且之后讲学习理论的时候还要讲到它。设想一下，对逻辑回归方法修改一下，“强迫” 它输出的值要么是0要么是1。要实现这个目的，很自然就应该把函数 $g$ 的定义修改一下，改成一个阙值函数
&lt;/p&gt;
$$
g(z)=
\begin{cases}
1, &amp; z \ge 0 \\
0, &amp; z &lt; 0
\end{cases}
$$&lt;p&gt;
若是我们还像之前一样令 $h_\theta(x)=g(\theta^Tx)$，但用刚刚上面的阙值函数作为 $g$ 的定义，然后如果我们用了下面的更新规则：
&lt;/p&gt;
$$
\theta_j := \theta_j + \alpha(y^{(i)}-h_\theta(x^{(i)}))x_j^{(i)}
$$&lt;p&gt;
这样我们就得到了感知器学习算法&lt;/p&gt;</description></item><item><title>CS229作业0</title><link>https://lunatide.tech/p/cs229%E4%BD%9C%E4%B8%9A0/</link><pubDate>Sat, 06 Dec 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229%E4%BD%9C%E4%B8%9A0/</guid><description>&lt;img src="https://lunatide.tech/p/cs229%E4%BD%9C%E4%B8%9A0/pic1.jpg" alt="Featured image of post CS229作业0" /&gt;&lt;h2 id="1-gradients-and-hessians"&gt;1. Gradients and Hessians
&lt;/h2&gt;&lt;h3 id="a"&gt;a.
&lt;/h3&gt;&lt;p&gt;由第一项，我们可以得到：&lt;/p&gt;
$$
f_1(x) = \frac{1}{2} x^T A x + b^T x
$$&lt;p&gt;因为：&lt;/p&gt;
$$
\nabla_x(x^T A x) = (A + A^T)x
$$&lt;p&gt;因此：&lt;/p&gt;
$$
\nabla_x\left( \frac{1}{2} x^T A x \right)
= \frac{1}{2}(A + A^T)x
$$&lt;p&gt;因为 \(A\) 是对称矩阵（\(A^T = A\)），所以：&lt;/p&gt;
$$
\frac{1}{2}(A + A)x = Ax
$$&lt;p&gt;第二项：&lt;/p&gt;
$$
f_2(x) = b^T x = \sum_i b_i x_i
$$&lt;p&gt;梯度为：&lt;/p&gt;
$$
\nabla_x(b^T x) = b
$$&lt;p&gt;因此：&lt;/p&gt;
$$
\nabla f(x) = Ax + b
$$&lt;h3 id="b"&gt;b.
&lt;/h3&gt;&lt;p&gt;令 \(z = h(x)\)，则：&lt;/p&gt;
$$
f(x) = g(z) = g(h(x))
$$&lt;p&gt;对每个分量有：&lt;/p&gt;
$$
\frac{\partial f}{\partial x_i}
= g'(h(x)) \frac{\partial h(x)}{\partial x_i}
$$&lt;p&gt;因此：&lt;/p&gt;
&lt;div&gt;
$$
\nabla f(x)
=
\begin{pmatrix}
g'(h(x)) \frac{\partial h}{\partial x_1} \\
g'(h(x)) \frac{\partial h}{\partial x_2} \\
\vdots \\
g'(h(x)) \frac{\partial h}{\partial x_n}
\end{pmatrix}
=
g'(h(x)) \nabla h(x)
$$
&lt;/div&gt;
&lt;h3 id="c"&gt;c.
&lt;/h3&gt;&lt;p&gt;由 a 得：&lt;/p&gt;
$$
(\nabla f(x))_i
= \sum_{j=1}^n a_{ij} x_j + b_i
$$&lt;p&gt;Hessian 的第 \(i, j\) 项为：&lt;/p&gt;
&lt;div&gt;
$$
(\nabla^2 f(x))_{ij}
=
\frac{\partial}{\partial x_j}
\left(
\sum_{k=1}^n a_{ik} x_k + b_i
\right)
$$
&lt;/div&gt;
&lt;p&gt;利用：&lt;/p&gt;
$$
\frac{\partial}{\partial x_j}(a_{ik} x_k)
= a_{ik}\delta_{kj}
$$&lt;p&gt;所以：&lt;/p&gt;
$$
(\nabla^2 f(x))_{ij}
= \sum_{k=1}^n a_{ik}\delta_{kj}
= a_{ij}
$$&lt;p&gt;因此：&lt;/p&gt;
$$
\nabla^2 f(x) = A
$$</description></item><item><title>CS229 Lecture 2</title><link>https://lunatide.tech/p/cs229-lecture-2/</link><pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs229-lecture-2/</guid><description>&lt;img src="https://lunatide.tech/p/cs229-lecture-2/pic1.jpg" alt="Featured image of post CS229 Lecture 2" /&gt;&lt;h2 id="线性回归"&gt;线性回归
&lt;/h2&gt;&lt;p&gt;有如下数据集&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs229-lecture-2/p1.jpg"
width="826"
height="326"
srcset="https://lunatide.tech/p/cs229-lecture-2/p1_hu_6de0275242601ffb.jpg 480w, https://lunatide.tech/p/cs229-lecture-2/p1_hu_f210478988d82bd6.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="253"
data-flex-basis="608px"
&gt;&lt;/p&gt;
&lt;p&gt;在上图中，输入特征$x$是$\mathbb{R}^2$范围取值的一个二维向量，$x_1^{(i)}$就是训练集中第$i$个房屋的面积，而$x_2^{(i)}$就是训练集中第$i$个房屋的我是数量，这只是举个例子，设计算法的时候你可以自己设计特征量&lt;/p&gt;
&lt;p&gt;然后我们可以把$y$假设为一个以$x$为变量的线性函数
&lt;/p&gt;
$$
h_\theta(x)=\theta_0+\theta_1x_1+\theta_2x_2
$$&lt;p&gt;
这里的$\theta_i$是参数，也叫权重，是从$X$到$Y$的线性函数映射的空间参数，在不引起混淆的情况下可以把$h_\theta(x)$中的$\theta$省略，另外，为了简化我们设$x_0=1$，简化后就有
&lt;/p&gt;
$$
h(x)=\sum_{i=0}^{n} \theta x_i=\theta^T x
$$&lt;p&gt;
等式最右边的$\theta$和$x$都是向量，$x$是输入变量的个数（就是特征量个数）&lt;/p&gt;
&lt;p&gt;现在，给定了一个&lt;strong&gt;训练集&lt;/strong&gt;，我们该如何挑选参数$\theta$，一个看上去比较合理的方法是让$h(x)$尽量逼近$y$,若是要用公式的形式来表示，就要定义一个函数，由此来衡量对于每个不同的$\theta$值，$h(x^{(i)})$与对应的$y^{(i)}$的距离，用如下的方式定义了一个&lt;strong&gt;成本函数&lt;/strong&gt;
&lt;/p&gt;
$$
J(\theta)=\frac{1}{2}\sum_{i=1}^{n}{(h_\theta(x^{(i)})-y^{(i)})^2}
$$&lt;p&gt;
你会发现这个函数和常规最小二乘法拟合模型中的最小二乘法成本函数非常相似&lt;/p&gt;
&lt;h3 id="最小均方算法lms"&gt;最小均方算法(LMS)
&lt;/h3&gt;&lt;p&gt;我们要让$J(\theta)$最小，我们考虑用&lt;strong&gt;梯度下降法&lt;/strong&gt;，这个方法就是从某一个$\theta$的初始值开始，然后逐渐重复更新
&lt;/p&gt;
$$
\theta_j:=\theta_j-\alpha \frac{\partial}{\partial \theta_j} J{(\theta)}
$$&lt;p&gt;
在这个式子中，$\alpha$是&lt;strong&gt;学习率&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;要实现这个算法，我们要知道右边的导数项是什么，让我们来计算一下
&lt;/p&gt;
$$
\begin{align*}
\frac{\partial}{\partial \theta_j} J(\theta)
&amp;= \frac{\partial}{\partial \theta_j} \frac{1}{2}(h_\theta(x) - y)^2 \\[6pt]
&amp;= (h_\theta(x) - y)\, \frac{\partial}{\partial \theta_j}(h_\theta(x) - y) \\[6pt]
&amp;= (h_\theta(x) - y)\, \frac{\partial}{\partial \theta_j}\left(\sum_{i=0}^n \theta_i x_i - y\right) \\[6pt]
&amp;= (h_\theta(x) - y)\, x_j
\end{align*}
$$&lt;p&gt;对单个训练样本，更新规则如下：&lt;/p&gt;
$$
\theta_j := \theta_j + \alpha \left( y^{(i)} - h_\theta(x^{(i)}) \right) x_j^{(i)}
$$&lt;p&gt;
这个规则也称为&lt;strong&gt;LMS&lt;/strong&gt;更新规则，也称为&lt;strong&gt;Widrow-Hoff&lt;/strong&gt;学习规则，具体的算法如下&lt;/p&gt;
&lt;p&gt;重复直到收敛{&lt;/p&gt;
&lt;p&gt;对每个$j$:
&lt;/p&gt;
$$
\theta_j:=\theta_j+\alpha\sum_{i=1}^m{(y^{(i)}-h_\theta(x^{(i)}))}
$$&lt;p&gt;
}&lt;/p&gt;
&lt;p&gt;这个方法叫做&lt;strong&gt;批量梯度下降法(batch gradient descent)&lt;/strong&gt;，此外还有一种方法&lt;/p&gt;
&lt;p&gt;Loop{&lt;/p&gt;
&lt;p&gt;​ for i=1 to m{&lt;/p&gt;
&lt;p&gt;​ 对每个$j$:
&lt;/p&gt;
$$
\theta_j := \theta_j + \alpha(y^{(i)}-h_{\theta}(x^{(i)}))x_j^{(i)}
$$&lt;p&gt;​ }&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;这个算法叫做&lt;strong&gt;随机梯度下降法(SGD)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在批量梯度下降算法中，我们要扫描整个训练集，才会更新一次，当数据集的量非常庞大的时候，会有很大计算量，而随机梯度下降算法在遇到训练样本的时候仅根据该单个训练样本的误差梯度更新参数，所以随机梯度下降往往比批量梯下降更快接近最小值。&lt;/p&gt;
&lt;p&gt;注意，它可能不会收敛到最小值，$\theta$会在$J(\theta)$的最小值附近震荡&lt;/p&gt;
&lt;h3 id="正规方程"&gt;正规方程
&lt;/h3&gt;&lt;p&gt;这是第二种方法，这种方法中我们通过求导让导数等于0的方式找到取得最小值的地方，给定一个训练集，把设计矩阵$X$设置为一个$x*n$的矩阵(实际上是$m * (n + 1)$，如果包含截距项)，该矩阵的每行是个训练样本
&lt;/p&gt;
$$
X =
\begin{bmatrix}
-(x^{(1)})^{T}- \\
-(x^{(2)})^T- \\
\vdots \\
-(x^{(m)})^T-
\end{bmatrix}
$$&lt;p&gt;另外，令 $\vec{y}$ 为包含训练集中所有目标值的 $m$ 维向量：&lt;/p&gt;
$$
\vec{y} =
\begin{bmatrix}
y^{(1)} \\
y^{(2)} \\
\vdots \\
y^{(m)}
\end{bmatrix}
$$&lt;p&gt;由于 $h_\theta(x^{(i)}) = (x^{(i)})^T \theta$，我们可以很容易地验证：&lt;/p&gt;
&lt;div&gt;
$$
X\theta - \vec{y} =
\begin{pmatrix}
(x^{(1)})^T\theta \\\
\vdots \\\
(x^{(m)})^T\theta
\end{pmatrix}
-
\begin{pmatrix}
y^{(1)} \\\
\vdots \\\
y^{(m)}
\end{pmatrix}
=
\begin{pmatrix}
(x^{(1)})^T\theta - y^{(1)} \\\
\vdots \\\
(x^{(m)})^T\theta - y^{(m)}
\end{pmatrix}
$$
&lt;/div&gt;
对于向量 $z$，则有 $z^T z = z^2$，因此利用这个性质，可以推导出：
$$
\begin{align*}
\frac{1}{2}(X\theta - \vec{y})^T (X\theta - \vec{y})
&amp;= \frac{1}{2} \sum_{i=1}^{m} \left( h_\theta(x^{(i)}) - y^{(i)} \right)^2
= J(\theta)
\end{align*}
$$&lt;p&gt;
关于$\theta$求梯度我们就可以得到：
&lt;/p&gt;
$$
\begin{align*}
\nabla_\theta J(\theta)
&amp;= \nabla_\theta \frac{1}{2} (X\theta - \vec{y})^T (X\theta - \vec{y}) \\[6pt]
&amp;= \frac{1}{2} \nabla_\theta (\theta^T X^T X \theta - \theta^T X^T \vec{y} - \vec{y}^T X \theta + \vec{y}^T \vec{y}) \\[6pt]
&amp;= \frac{1}{2} \nabla_\theta (\theta^T X^T X \theta - 2\theta^T X^T \vec{y}) \\[6pt]
&amp;= \frac{1}{2} (2 X^T X \theta - 2 X^T \vec{y}) \\[6pt]
&amp;= X^T X \theta - X^T \vec{y}
\end{align*}
$$&lt;p&gt;
第四个等号利用了&lt;/p&gt;
$$
\nabla_\theta (\theta^T A \theta) = (A + A^T)\theta
$$$$
\nabla_\theta (\theta^T x) = x
$$&lt;p&gt;令梯度为 0 可得 &lt;strong&gt;正规方程&lt;/strong&gt;：&lt;/p&gt;
$$
X^T X \theta = X^T \vec{y}
$$&lt;p&gt;因此，通过等式以解析形式给出使$J(\theta)$最小化的 $\theta$ 的值：&lt;/p&gt;
$$
\theta = (X^T X)^{-1} X^T \vec{y}
$$</description></item><item><title>CS231 第三讲 正则化与优化</title><link>https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/</link><pubDate>Wed, 26 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/</guid><description>&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/pic1.jpg" alt="Featured image of post CS231 第三讲 正则化与优化" /&gt;&lt;h2 id="损失函数"&gt;损失函数
&lt;/h2&gt;&lt;p&gt;给定一个图片数据集 ${(x_i, y_i)}_{i=1}^N$，$x_i$ 为图片，$y_i$ 为标签，损失为
&lt;/p&gt;
$$
L = \frac{1}{N} \sum_i L_i(f(x_i, W), y_i) \tag{1}
$$&lt;p&gt;
这是一种衡量模型预测与训练数据匹配程度的损失，我们希望这个值越低越好，这代表模型很好地拟合了训练数据&lt;/p&gt;
&lt;h3 id="多类svm损失"&gt;多类SVM损失
&lt;/h3&gt;&lt;p&gt;给一个$s=f(x_i,W)$&lt;/p&gt;
&lt;p&gt;SVM损失定义为
&lt;/p&gt;
$$
\begin{aligned}
L_i &amp;= \sum_{j \neq y_i} \begin{cases} 0 &amp; \text{如果} s_{y_i} \ge s_j + 1 \\ s_j - s_{y_i} + 1 &amp; \text{其他} \end{cases} \\
&amp;= \sum_{j \neq y_i} \max(0, s_j - s_{y_i} + 1)
\end{aligned}\tag{2}
$$&lt;p&gt;
SVM损失函数的形式如下
&lt;/p&gt;
$$
L_i = \sum_{j \neq y_i} \max(0, s_j - s_{y_i} + 1)\tag{3}
$$&lt;p&gt;
&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p4.jpg"
width="440"
height="330"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p4_hu_9fcac2f2d3d5b1c2.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p4_hu_34019f7dfcff4d05.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
&gt;&lt;/p&gt;
&lt;h3 id="softmax分类器"&gt;Softmax分类器
&lt;/h3&gt;&lt;p&gt;首先定义如下概率&lt;/p&gt;
$$
P(Y = k | X = x_i) = \frac{e^{s_k}}{\sum_j e^{s_j}} \quad s = f(x_i; W)\tag{4}
$$&lt;p&gt;我们想最大化对数似然函数，而这也等价于最小化如下式子&lt;/p&gt;
$$
L_i = -\log P(Y = y_i | X = x_i) = -\log \left( \frac{e^{s_{y_i}}}{\sum_j e^{s_j}} \right)\tag{5}
$$&lt;p&gt;
&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p2.jpg"
width="1914"
height="926"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p2_hu_d9c6bf83bdcdc52d.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p2_hu_e056fde0d5beac53.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="206"
data-flex-basis="496px"
&gt;&lt;/p&gt;
&lt;h3 id="损失函数的正则化项"&gt;损失函数的正则化项
&lt;/h3&gt;$$
L(W) = \frac{1}{N} \sum_{i=1}^{N} L_i(f(x_i, W), y_i) + \lambda R(W)\tag{6}
$$&lt;p&gt;它的作用是防止模型在训练数据上表现过好，因此正则化的目的就是让它在训练数据表现更差，在测试集表现更好，关于这里的$\lambda$，这是正则化强度，这也是一个超参数，这个参数用于控制模型对训练数据的拟合程度&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p1.jpg"
width="1842"
height="904"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p1_hu_7d2528d4f1142dc1.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p1_hu_194156a355770f94.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="203"
data-flex-basis="489px"
&gt;&lt;/p&gt;
&lt;p&gt;上图是一个例子，目标是拟合这些数据点，有f1和f2两种模型，f1穿过了所有数据点，所以训练或数据损失会很低，因为几乎完美拟合，但是在测试新数据上，f2可能表现更好，因此不要过度拟合数据，越简单的模型可能效果更好&lt;/p&gt;
&lt;p&gt;比较常见的正则化项如下&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p3.jpg"
width="1046"
height="310"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p3_hu_cfd19289c5b397a7.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p3_hu_7a61ce87cfb6465b.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="337"
data-flex-basis="809px"
&gt;&lt;/p&gt;
&lt;p&gt;所以为什么我们要对模型正则化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;它允许我们对于权重表达某种偏好&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;让模型更简单从而在测试数据上表现更好&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过增加曲率改进优化&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="优化"&gt;优化
&lt;/h2&gt;&lt;h3 id="梯度下降"&gt;梯度下降
&lt;/h3&gt;&lt;p&gt;这个我们非常熟悉了，只要跟随梯度，所以计算梯度就可以了
&lt;/p&gt;
$$
\nabla_W L = \frac{1}{N} \sum_{i=1}^{N} \nabla_W L_i(f(x_i, W), y_i) + \lambda \nabla_W R(W)\tag{7}
$$&lt;h3 id="随机梯度下降sgd"&gt;随机梯度下降(SGD)
&lt;/h3&gt;&lt;p&gt;我们之前说过可以通过遍历整个训练集，对每个i计算损失$L_i$并且汇总整个训练集，但是这样计算量太大，SGD的核心是查看一个子集代替整个训练集，每次称为一个小批量或者一批数据&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p5.jpg"
width="1376"
height="668"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p5_hu_17e8f542392f8e9e.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p5_hu_f325946cb45eab60.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="205"
data-flex-basis="494px"
&gt;&lt;/p&gt;
&lt;p&gt;但是我们会遇到一些问题，当在鞍点或者局部最优点的时候，直观点如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p6.jpg"
width="1008"
height="328"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p6_hu_48aa6e57c10c8b4d.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p6_hu_9f2538d26c4103f2.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="307"
data-flex-basis="737px"
&gt;&lt;/p&gt;
&lt;p&gt;所以我们就引入了动量，你可以用高中物理学过的知识想象一下，动不了了给个动量他就能朝着预期方向继续前行&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p7.jpg"
width="1858"
height="870"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p7_hu_f73ff6a0bbf75e03.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p7_hu_4c65c1396d9622dd.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="213"
data-flex-basis="512px"
&gt;&lt;/p&gt;
&lt;h3 id="rmsprop优化器"&gt;RMSProp优化器
&lt;/h3&gt;&lt;p&gt;在梯度下降中，很容易出现参数更新不稳定，也就是振荡很大的情况，RMSProp就是改进了这个问题，维护了一个梯度平方的“指数加权移动平均”，说的直白点，它可以自适应学习率，在剧烈变化的方向降低学习率&lt;/p&gt;
&lt;h3 id="adam优化器"&gt;Adam优化器
&lt;/h3&gt;&lt;p&gt;Adam优化器是现在最流行的，它实际上是带动量动量的RMSProp&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p8.jpg"
width="1896"
height="884"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p8_hu_17e9a99e4b7d774.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%B8%89%E8%AE%B2-%E6%AD%A3%E5%88%99%E5%8C%96%E4%B8%8E%E4%BC%98%E5%8C%96/p8_hu_a44f7510cc853f0c.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="214"
data-flex-basis="514px"
&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;first_moment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# 一阶矩初始化&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;second_moment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# 二阶矩初始化为&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_iterations&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compute_gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 当前参数 x 的梯度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;first_moment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beta1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;first_moment&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;beta1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="c1"&gt;# 动量，对梯度做指数平均让梯度更平滑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;second_moment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beta2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;second_moment&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;beta2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="c1"&gt;# RMSProp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 因为 m_0=0，所以前几步的 m_t 偏小，用 1 - β1^t 修正&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;first_unbias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_moment&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;beta1&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;second_unbias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;second_moment&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;beta2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;first_unbias&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;second_unbias&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>CS231 第二讲 图像分类</title><link>https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/</link><pubDate>Tue, 25 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/</guid><description>&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/pic1.jpg" alt="Featured image of post CS231 第二讲 图像分类" /&gt;&lt;p&gt;开始学cs231n了，期望是一周3-4节，尽量4周完成掉&lt;/p&gt;
&lt;p&gt;课程主页:https://cs231n.stanford.edu/&lt;/p&gt;
&lt;p&gt;作业:https://cs231n.stanford.edu/schedule.html&lt;/p&gt;
&lt;h2 id="图像分类"&gt;图像分类
&lt;/h2&gt;&lt;p&gt;图像通常由数据矩阵定义，更一般地说是数据张量，识别图像对于机器来说是个很大的挑战，举个例子，人类不管从什么角度看一个物体他都是一样的，但是当一个摄像机对准一个物体并转动，像素值实时都在改变，除此之外，光照，物体遮挡等等对于图像的识别来说都是挑战&lt;/p&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;下面是分别对应步骤2和3的接口&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p1.jpg"
width="1854"
height="880"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p1_hu_3e9bd396b0bdb806.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p1_hu_261c4bfc88a957fb.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="210"
data-flex-basis="505px"
&gt;&lt;/p&gt;
&lt;h3 id="nearest-neighbor-classifier"&gt;Nearest Neighbor Classifier
&lt;/h3&gt;&lt;p&gt;设定一个距离函数，对于一对图像（query data和training data），返回一个定义两者相似度的值&lt;/p&gt;
&lt;p&gt;下面是两种常见的计算距离的方式&lt;/p&gt;
&lt;p&gt;首先是L1距离，定义为两个图像所有像素差绝对值的总和&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p2.jpg"
width="1894"
height="938"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p2_hu_db923737d54a3ba5.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p2_hu_106a6601c2dedb01.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="201"
data-flex-basis="484px"
&gt;&lt;/p&gt;
&lt;p&gt;不过我们不难发现，训练函数是$O(1)$的，而预测函数是$O(n)$的，这并不是我们想要的&lt;/p&gt;
&lt;p&gt;因此我们把Nearest Neighbor自然推广到k-Nearest Neighbor，k值该如何选择才比较合适，以及距离函数该如何选择，这两个量就被称作超参数，也就是需要决策的变量Ruhr设置超参数有很多办法，第一种方法是把部分训练数据作为验证集，在训练集上训练模型，然后通过验证集的效果来选择超参数，然后用超参数对测试集进行结果复现，并在测试集验证&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p3.jpg"
width="1760"
height="254"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p3_hu_f86f9e48c99f29ae.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p3_hu_dfe8be4051b98bf4.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="692"
data-flex-basis="1662px"
&gt;&lt;/p&gt;
&lt;p&gt;更好的方法是用交叉验证设置超参数，把训练数据分成若干分区，然后每个分区轮流作为验证集，在一组数据上训练数据然后再下一组数据评估模型，迭代，最后取结果的评估值作为结果&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p4.jpg"
width="1880"
height="952"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p4_hu_54295db4767449af.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p4_hu_5a2b131c36886179.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="473px"
&gt;&lt;/p&gt;
&lt;p&gt;在实际中，k Nearest Neighbor从来不用，首先因为效率太低，其次它的距离是按照每个像素点之间来计算的，因此很容易识别错误，例如把颜色相近的猫识别成老虎，亦或者当有物体遮挡的时候，你去计算他的L2距离是相等的，但是实际上差别却很大，最后，当数据的维度很大的时候，计算会非常的慢&lt;/p&gt;
&lt;h3 id="线性分类器"&gt;线性分类器
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p5.jpg"
width="1814"
height="930"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p5_hu_b4abae43c7a8c598.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p5_hu_7bb20d3e6bc9b7ae.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="195"
data-flex-basis="468px"
&gt;&lt;/p&gt;
&lt;p&gt;线性分类器就是给定一张图片，转换成向量然后计算
&lt;/p&gt;
$$
f(x,W)=Wx + b\tag{1}
$$&lt;p&gt;
这里输出结果是10维向量，然后根据分量决定图片属于哪一类&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p6.jpg"
width="1846"
height="918"
srcset="https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p6_hu_3303b58d1e787dcc.jpg 480w, https://lunatide.tech/p/cs231-%E7%AC%AC%E4%BA%8C%E8%AE%B2-%E5%9B%BE%E5%83%8F%E5%88%86%E7%B1%BB/p6_hu_f286048cfe01c24a.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="201"
data-flex-basis="482px"
&gt;&lt;/p&gt;
&lt;p&gt;但是线性分类器并不是所有东西都能解决（就比如上图，根据结果图片是狗，😓），对于无法分类大量分离的数据实例就无法解决&lt;/p&gt;</description></item><item><title>鱼书笔记-与学习相关的技巧</title><link>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%B8%8E%E5%AD%A6%E4%B9%A0%E7%9B%B8%E5%85%B3%E7%9A%84%E6%8A%80%E5%B7%A7/</link><pubDate>Tue, 25 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%B8%8E%E5%AD%A6%E4%B9%A0%E7%9B%B8%E5%85%B3%E7%9A%84%E6%8A%80%E5%B7%A7/</guid><description>&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%B8%8E%E5%AD%A6%E4%B9%A0%E7%9B%B8%E5%85%B3%E7%9A%84%E6%8A%80%E5%B7%A7/pic3.jpg" alt="Featured image of post 鱼书笔记-与学习相关的技巧" /&gt;&lt;h2 id="参数的更新"&gt;参数的更新
&lt;/h2&gt;</description></item><item><title>鱼书笔记-误差反向传播法</title><link>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/</link><pubDate>Sat, 22 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/</guid><description>&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/pic3.jpg" alt="Featured image of post 鱼书笔记-误差反向传播法" /&gt;&lt;h2 id="计算图"&gt;计算图
&lt;/h2&gt;&lt;h3 id="用计算图求解"&gt;用计算图求解
&lt;/h3&gt;&lt;p&gt;我们先来看一个简单的问题&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：太郎在超市买了2个100日元一个的苹果，消费税是10%，请计算支付金额&lt;/p&gt;
&lt;p&gt;如何用计算图表示，这个非常简单，小学生都能看懂&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p1.jpg"
width="1124"
height="170"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p1_hu_c763e06ef16f8f75.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p1_hu_a974abff9f5f7f9b.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="661"
data-flex-basis="1586px"
&gt;&lt;/p&gt;
&lt;p&gt;或者也可以把运算的数字放在圆圈外面，如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p2.jpg"
width="1106"
height="364"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p2_hu_3c07ffa66494cb4.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p2_hu_6971d7a979731625.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="303"
data-flex-basis="729px"
&gt;&lt;/p&gt;
&lt;p&gt;上面说的这种便是正向传播运算，也就是我们的正常运算的逻辑，但是这章的主题是反向传播，我们来看看这是什么&lt;/p&gt;
&lt;h2 id="反向传播"&gt;反向传播
&lt;/h2&gt;&lt;h3 id="加法节点的反向传播"&gt;加法节点的反向传播
&lt;/h3&gt;&lt;p&gt;以z=x+y为例，左图为正向传播，右图为反向传播&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p4.jpg"
width="1102"
height="470"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p4_hu_71293138d0a489ff.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p4_hu_d1a0215d56a37084.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="234"
data-flex-basis="562px"
&gt;&lt;/p&gt;
&lt;h3 id="乘法节点的反向传播"&gt;乘法节点的反向传播
&lt;/h3&gt;&lt;p&gt;以z=xy为例&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p5.jpg"
width="1104"
height="464"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p5_hu_ba0582f616665e58.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p5_hu_5ddc10bebd2767f8.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="237"
data-flex-basis="571px"
&gt;&lt;/p&gt;
&lt;h3 id="回到开头的例子"&gt;回到开头的例子
&lt;/h3&gt;&lt;p&gt;所以重新思考开头的那个买苹果的例子，要解的就是苹果的价格，苹果的个数，消费税这三个变量之间各自如何影响最终支付的金额，相当于求“支付金额关于苹果价格的导数”，“支付金额关于苹果个数的导数“，”支付金额关于消费税的导数”，反向传播的过程如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p7.jpg"
width="1086"
height="394"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p7_hu_6e58a8b53df4470c.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p7_hu_73b8e0d011eac9f3.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="275"
data-flex-basis="661px"
&gt;&lt;/p&gt;
&lt;p&gt;如图， 苹果价格的导数是2.2，苹果个数的导数是110，消费税的导数是200，意思就是，如果消费税和苹果的价值增长同样的值，消费税将对最终金额产生200倍左右的影响，苹果的价格将产生2.2倍大小的影响（不过这个例子在中两者的量纲不同）&lt;/p&gt;
&lt;h2 id="简单层的实现"&gt;简单层的实现
&lt;/h2&gt;&lt;p&gt;本节用python实现购买苹果的例子&lt;/p&gt;
&lt;h3 id="乘法层的实现"&gt;乘法层的实现
&lt;/h3&gt;&lt;p&gt;层的实现中有两个共通的方法&lt;code&gt;forwar()&lt;/code&gt;和&lt;code&gt;backward()&lt;/code&gt;。&lt;code&gt;forward()&lt;/code&gt;对应正向传播，&lt;code&gt;backward()&lt;/code&gt;对应反向传播。&lt;/p&gt;
&lt;p&gt;然后来实现乘法层&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MulLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dy&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;__init__()&lt;/code&gt;中会初始化实例变量x和y，它们用于保存正向传播时的输出值。&lt;code&gt;forward()&lt;/code&gt;接收x和y两个参数，将它们相乘后输出。&lt;code&gt;backward()&lt;/code&gt;将从上游传来的导数(dout)乘以正向传播的翻转值，然后传给下游&lt;/p&gt;
&lt;h3 id="加法层的实现"&gt;加法层的实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dy&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;加法层不需要初始化，实现非常简单&lt;/p&gt;
&lt;h3 id="例子"&gt;例子
&lt;/h3&gt;&lt;p&gt;接下来看个实际操作的例子&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p8.jpg"
width="1100"
height="486"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p8_hu_ac8715a36385d57a.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p8_hu_ab01627bd0215312.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="226"
data-flex-basis="543px"
&gt;&lt;/p&gt;
&lt;p&gt;上图可以像如下一样实现&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;apple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;apple_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orange_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#layer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mul_apple_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MulLayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mul_apple_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MulLayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;add_apple_orange_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AddLayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mul_tax_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MulLayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# forward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;apple_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul_apple_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;app_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orange_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul_orange_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;orange_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;all_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_apple_orange_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apple_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;orange_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul_tax_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;tax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#backward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dprice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dall_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dtax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul_tax_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dprice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dapple_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dorange_nprice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;add_apple_orange_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dall_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dorange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dorange_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul_orange_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dorange_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dapple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dapple_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mul_apple_layer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dapple_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dapple_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dapple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dorange_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dtax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="激活函数层的实现"&gt;激活函数层的实现
&lt;/h2&gt;&lt;h3 id="relu层"&gt;ReLU层
&lt;/h3&gt;&lt;p&gt;激活函数ReLU由下式表示
&lt;/p&gt;
$$
y = \begin{cases}
x &amp; (x&gt;0) \\
0 &amp; (x \le 0)
\end{cases}\tag{1}
$$&lt;p&gt;
通过式(1)，可以求出y关于x的导数，如下式
&lt;/p&gt;
$$
\frac{\partial y}{\partial x}
= \begin{cases}
1 &amp; (x&gt;0) \\
0 &amp; (x \le 0)
\end{cases}\tag{2}
$$&lt;p&gt;
接下来实现一下ReLU层&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReLU&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;ReLU由实例变量mask。这个变量mask是由True/False构成的NumPy数组，它会把正向传播时输入的x的元素中小于等于0的地方保存为True，其他地方（大于0的元素）保存为False&lt;/p&gt;
&lt;h3 id="sigmoid层"&gt;Sigmoid层
&lt;/h3&gt;&lt;p&gt;接下来来实现一下sigmoid函数，sigmoid函数如下式所示
&lt;/p&gt;
$$
y = \frac{1}{1 + \exp(-x)}\tag{3}
$$&lt;p&gt;
用计算图表示上式，如下所示&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p9.jpg"
width="1096"
height="298"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p9_hu_de7f31f4d586ab20.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p9_hu_f72385624df72258.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="367"
data-flex-basis="882px"
&gt;&lt;/p&gt;
&lt;p&gt;然后我们来看下反向传播是怎么样的&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p10.jpg"
width="1110"
height="252"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p10_hu_600fea825121c194.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p10_hu_a7eff441b05d1f2f.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="440"
data-flex-basis="1057px"
&gt;&lt;/p&gt;
&lt;p&gt;上图就是Sigmoid函数的反向传播过程，如果你看懂了上面的内容相信这个不难理解&lt;/p&gt;
&lt;p&gt;我们在反向传输的过程中只需要专注于它的输入和输出就可以，不用在意繁琐的过程&lt;/p&gt;
&lt;p&gt;输出的结果此外， $\frac{\partial L}{\partial y} y^{2} \exp(-x)$ 可以进一步整理如下：&lt;/p&gt;
$$
\begin{aligned}
\frac{\partial L}{\partial y} y^{2} \exp(-x)
&amp;= \frac{\partial L}{\partial y} \frac{1}{(1+\exp(-x))^{2}} \exp(-x) \\
&amp;= \frac{\partial L}{\partial y} \frac{1}{1+\exp(-x)} \frac{\exp(-x)}{1+\exp(-x)} \\
&amp;= \frac{\partial L}{\partial y} \, y (1-y)
\end{aligned}\tag{4}
$$&lt;p&gt;
实现一下Sigmoid层&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sigmoid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Nonoe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="affinesoftmax层的实现"&gt;Affine/Softmax层的实现
&lt;/h2&gt;&lt;h3 id="affine层"&gt;Affine层
&lt;/h3&gt;&lt;p&gt;神经网络的正向传播中，为了计算加权信号的总和，使用了矩阵的积乘运算(NumPy中是np.dot)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;神经网络的正向传播中进行的矩阵的乘积运算在几何学领域被称为“仿射变换”。因此，这里将进行仿射变换的处理实现称为“Affine层”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将这里进行的求矩阵的乘积和偏置的和的运算用计算图表示出来。将乘积运算用“dot”节点表示的话，则np.dot(X,W) + B的运算可以用下图的计算图来表示出来，另外，在各个变量的上方标记了它们的形状&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p11.jpg"
width="1054"
height="474"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p11_hu_c7ac00e1e2bde49d.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p11_hu_c7d4e8346cdbb164.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="222"
data-flex-basis="533px"
&gt;&lt;/p&gt;
&lt;p&gt;上图是比较简单的计算图，不过要注意X,W,B是矩阵&lt;/p&gt;
&lt;p&gt;考虑上图的反向传播，以矩阵为对象的反向传播，按矩阵的各个元素进行计算时，步骤和以标量为对象的计算图相同。&lt;/p&gt;
&lt;p&gt;我们可以写出计算图的反向传播，如下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p12.jpg"
width="1100"
height="604"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p12_hu_361113057a849de9.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p12_hu_77920e1df90925cd.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="182"
data-flex-basis="437px"
&gt;&lt;/p&gt;
&lt;p&gt;观察一下上图中各个变量的形状，X和$\frac{\partial L}{\partial \mathbf{X}}$形状相同，W和$\frac{\partial L}{\partial \mathbf{W}}$，形状相同，从下式就可以看出X和$\frac{\partial L}{\partial \mathbf{X}}$形状相同
&lt;/p&gt;
$$
\mathbf{X} = (x_0, x_1, \cdots, x_n)\\
\frac{\partial L}{\partial \mathbf{X}}
= \left(
\frac{\partial L}{\partial x_0},
\frac{\partial L}{\partial x_1},
\cdots,
\frac{\partial L}{\partial x_n}
\right)\tag{5}
$$&lt;h3 id="批版本的affine层"&gt;批版本的Affine层
&lt;/h3&gt;&lt;p&gt;前面介绍的Affine层的输入X是以单个数据为对象的。现在我们考虑N个数据一起进行正向传播的情况&lt;/p&gt;
&lt;p&gt;下图是批版本的affine层的计算图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p13.jpg"
width="1102"
height="648"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p13_hu_2ff5cead460a712f.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p13_hu_1b8b992e5ba36f4a.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="170"
data-flex-basis="408px"
&gt;&lt;/p&gt;
&lt;p&gt;现在输入X的形状是(N,2)。之后就和前面一样&lt;/p&gt;
&lt;p&gt;正向传播时，偏置被加到$X·W$的各个数据上。比如，N=2时，偏置会分别加到这两个数据上，因此反向传播时，各个数据的反向传播的值需要汇总为偏置的元素&lt;/p&gt;
&lt;p&gt;Affine的实现如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Affine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;W&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;delf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="softmax-with-loss层"&gt;Softmax-with-Loss层
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p14.jpg"
width="1078"
height="328"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p14_hu_80956ba1efd69ec0.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p14_hu_68b310ecc0ace31f.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="328"
data-flex-basis="788px"
&gt;&lt;/p&gt;
&lt;p&gt;之前说过softmax函数会将输入值正规化（将输出值的和调整为1）然后再输出。另外，因为手写数字识别要进行10类分类，所以向Softmax层的输入也有10个&lt;/p&gt;
&lt;p&gt;下面来实现Softmax层，计算图如下图所示&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p15.jpg"
width="1094"
height="388"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p15_hu_f27b6bfc356a92b.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p15_hu_8f2141866336470b.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="281"
data-flex-basis="676px"
&gt;&lt;/p&gt;
&lt;p&gt;上图的计算图可以简化成下图&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p16.jpg"
width="1088"
height="696"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p16_hu_1f231fea0554fdb5.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E8%AF%AF%E5%B7%AE%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD%E6%B3%95/p16_hu_f733698c8643aa10.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="156"
data-flex-basis="375px"
&gt;&lt;/p&gt;
&lt;p&gt;上图的计算图中，softmax函数记为Softmax层，交叉熵误差记为Cross Entropy error层。这里假设要进行三类分类，从前面的层接收三个输入，Softmax层将输入(a1,a2,a3)正规化，输出(y1,y2,y3)Cross Entropy Error层接收Softmax的输出(y1,y2,y3)和教师标签(t1,t2,t3)，从这些数据中输出损失L&lt;/p&gt;
&lt;p&gt;上图要注意的是反向传播的结果，Softmax层的反向传播得到了(y1-t1,y2-t2,y3-t3)这样漂亮的结果。由于(y1,y2,y3)是Softmax层的输出，(t1,t2,t3)是监督数据，所以(y1-t1,y2-t2,y3-t3)是Softmax层的输出和教师标签的差分。神经网络会把这个差分表示的误差传递给前面的层。&lt;/p&gt;
&lt;p&gt;神经网络学习的目的就是通过调整权重参数，使神经网络的输出接近教师标签。因此，必须将神经网络的输出与教师标签的误差高效地传递给前面的层&lt;/p&gt;
&lt;p&gt;现在实现一下Softmax-with-Loss层&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SoftmaxWithLoss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# 损失&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# softmax的输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# 监督数据（one-hot vector）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cross_entropy_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>鱼书笔记-神经网络的学习</title><link>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%AD%A6%E4%B9%A0/</link><pubDate>Mon, 17 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%AD%A6%E4%B9%A0/</guid><description>&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%AD%A6%E4%B9%A0/pic3.jpg" alt="Featured image of post 鱼书笔记-神经网络的学习" /&gt;&lt;h2 id="从数据中学习"&gt;从数据中学习
&lt;/h2&gt;&lt;p&gt;神经网络的特征就是从数据中学习（由数据自动决定权重参数的值）&lt;/p&gt;
&lt;h3 id="数据驱动"&gt;数据驱动
&lt;/h3&gt;&lt;p&gt;我们接着上一章最后手写数字识别的话题，思考一下会发现如果设计一个能自动识别5的算法还是挺困难的（至少我是这样认为的），所以我们应该考虑通过有效利用数据来解决这个问题，一种方案是从图像中提取特征量，再用机器学习技术学习这些特征量的模式&lt;/p&gt;
&lt;p&gt;机器学习的方法中，由机器从收集到的数据中找到规律性。但是将图像转换为向量时使用的特征量仍是由人设计的，对于不同的问题，必须使用合适的特征量，才能得到好的结果&lt;/p&gt;
&lt;p&gt;还有一种是神经网络（深度学习）的方法，该方法不存在人为介入，神经网络会直接学习图像本身&lt;/p&gt;
&lt;h3 id="训练数据和测试数据"&gt;训练数据和测试数据
&lt;/h3&gt;&lt;p&gt;机器学习中把数据分成训练数据和测试数据两部分，首先用训练数据进行学习，寻找最优的参数，然后用测试数据评价训练得到的模型的实际能力，为了正确评价模型的泛化能力，就必须划分训练数据和测试数据，训练数据也被称作&lt;strong&gt;监督数据&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;泛化能力是指处理未被观察过的数据的能力。机器学习的目标就是为了提高泛化能力&lt;/p&gt;
&lt;p&gt;因此，仅仅用一个数据集去学习和评价参数，无法正确评价，只用某个数据集过度拟合的状态称为过拟合&lt;/p&gt;
&lt;h2 id="损失函数"&gt;损失函数
&lt;/h2&gt;&lt;p&gt;神经网络的学习通过某个指标来表示现在的状态。然后以这个指标为基准，寻找最优权重参数。这个指标被称为&lt;strong&gt;损失函数&lt;/strong&gt;。损失函数可以使用任意参数，但一般用均方误差和交叉熵误差等。&lt;/p&gt;
&lt;h3 id="均方误差"&gt;均方误差
&lt;/h3&gt;&lt;p&gt;如下式
&lt;/p&gt;
$$
E = \frac{1}{2} \sum_k (y_k - t_k) ^ 2 \tag{1}
$$&lt;p&gt;
这里$y_k$是表示神经网络的输出，$t_k$是表示监督数据，$k$表示数据的维数，如式(1)所示，均方误差会计算神经网络的输出和正确解监督数据的各个元素之差的平方，再求总和。python实现均方误差的实现方式如下所示&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mean_squared_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="交叉熵误差"&gt;交叉熵误差
&lt;/h3&gt;&lt;p&gt;交叉熵误差如下式所示
&lt;/p&gt;
$$
E = - \sum_k (t_k \log{y_k}) \tag{2}
$$&lt;p&gt;
$y_k$是神经网络的输出，$t_k$是正确解标签(采用one-hot表示)。交叉熵误差的值是由正确解标签所对应的输出结果决定的。&lt;/p&gt;
&lt;p&gt;根据对数函数的性质我们可以知道，正确解标签对应的输出越大，式(2)的值就越靠近0；输出为1时，交叉熵的误差为0。如果正确解标签对应的输出较小，(2)的值就越大。&lt;/p&gt;
&lt;p&gt;下面实现一下交叉熵误差&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cross_entropy_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;y和t在这里是NumPy数组，加上一个delta是为了防止-inf的发生&lt;/p&gt;
&lt;h3 id="mini-batch学习"&gt;mini-batch学习
&lt;/h3&gt;&lt;p&gt;前面说的都是单个数据的损失函数。如果要求所有训练数据的损失函数的总和，以交叉熵误差为例，可以写成下面的式(3)
&lt;/p&gt;
$$
E = -\frac{1}{N} \sum_{n} \sum_{k} t_{nk}\,\log y_{nk} \tag{3}
$$&lt;p&gt;
假设一共有N个数据，$t_{nk}$表示第n个数据的第k个元素的值&lt;/p&gt;
&lt;p&gt;这个式子就是把单个数据的损失函数的式扩大到了N份数据，不过最后还要除以N进行正规化。&lt;/p&gt;
&lt;p&gt;MNIST数据集的训练数据有60000个，用全部数据来计算损失函数的值所花费的时间太长，所以我们从中选取一部分。神经网络的学习也是从训练数据中选出一批数据（称为mini-batch)，然后对每个mini-batch进行学习。&lt;/p&gt;
&lt;h3 id="mini-batch版交叉熵误差的实现"&gt;mini-batch版交叉熵误差的实现
&lt;/h3&gt;&lt;p&gt;对于mini-batch的交叉熵误差，只要改良一下之前实现对应单个数据的交叉熵误差就可以。这里实现一个可以同时处理单个数据和批量数据两种情况的函数&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cross_entropy_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndim&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这里，y是神经网络的输出，t是监督数据。y的维度为1时，即求单个数据的交叉熵误差时，需要改变数据的形状。并且，当输入为mini-batch时，要用batch的个数进行正规化，计算单个数据的平均交叉熵误差&lt;/p&gt;
&lt;p&gt;此外，当监督数据时标签形式(非one-hot表示，而是像&amp;quot;2&amp;quot; &amp;ldquo;7&amp;quot;这样的)交叉熵误差可以如下实现&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cross_entropy_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndim&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;由于one-hot表示中t为0的元素的交叉熵误差也为0，因此针对这些元素的计算可以忽略。只要可以获得神经网络在正确解标签的输出，就可以计算交叉熵误差,t为one-hot表示时通过&lt;code&gt;t * np.log(y)&lt;/code&gt;计算的地方t为标签形式时，可以用&lt;code&gt;np.log(y[np.arange(batch_size),t])&lt;/code&gt;表示实现相同的处理&lt;/p&gt;
&lt;h3 id="为什么要设定损失函数"&gt;为什么要设定损失函数
&lt;/h3&gt;&lt;p&gt;假设有一个神经网络，对其中一个权重参数的损失函数求导，如果这个导数的值为负，说明使该权重参数向正正方向改变，可以减小损失函数的值；反之亦然，以及当导数的值为0时候，无论权重参数往哪个方向，损失函数的值都不会改变。而如果用识别精度作为指标，则参数的导数在绝大多数地方都为0&lt;/p&gt;
&lt;h2 id="梯度法"&gt;梯度法
&lt;/h2&gt;&lt;p&gt;梯度的方向不一定指向最小值，但是沿着梯度的方向能够最大限度地减小函数的值&lt;/p&gt;
&lt;p&gt;梯度法是什么，就是让函数的取值沿着梯度的方向前进一段距离，在新的地方重新求梯度，然后再沿着梯度方向前进，像这样反复，逐渐减小函数值，然后我们用数学式来表示梯度法，如下式(4)
&lt;/p&gt;
$$
x_0 = x_0 - \eta \frac{\partial f}{\partial x_0}\\
x_1 = x_1 - \eta \frac{\partial f}{\partial x_1}\tag{4}
$$&lt;p&gt;上式的$\eta$表示更新量，在神经网络的学习中，称为学习率，决定了在一次学习中，应该学习多少，以及在多大程度上更新参数&lt;/p&gt;
&lt;p&gt;接下来用python实现下梯度下降法&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gradient_descet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;init_x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;lr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;step_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init_x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step_num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;grad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numerical_gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;grad&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;参数f是要进行最优化的函数，init_x是初始值，lr是学习率，step_num是梯度法的重复次数，numerical_gradient(f,x)会求函数的梯度&lt;/p&gt;
&lt;h3 id="神经网络的梯度"&gt;神经网络的梯度
&lt;/h3&gt;&lt;p&gt;神经网络的学习也要求梯度，这里所说的梯度是指损失函数关于权重参数的梯度，例如一个形状2x3的权重$W$的神经网络，损失函数用L表示。此时，梯度可以用$\frac{\partial L}{\partial \mathbf{W}}$表示
&lt;/p&gt;
$$
\mathbf{W} =
\begin{pmatrix}
w_{11} &amp; w_{12} &amp; w_{13} \\
w_{21} &amp; w_{22} &amp; w_{23}
\end{pmatrix}\\
\frac{\partial L}{\partial \mathbf{W}} =
\begin{pmatrix}
\frac{\partial L}{\partial w_{11}} &amp; \frac{\partial L}{\partial w_{12}} &amp; \frac{\partial L}{\partial w_{13}} \\
\frac{\partial L}{\partial w_{21}} &amp; \frac{\partial L}{\partial w_{22}} &amp; \frac{\partial L}{\partial w_{23}}
\end{pmatrix}\tag{5}
$$</description></item><item><title>鱼书笔记-神经网络(下)</title><link>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/</link><pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/</guid><description>&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/pic3.jpg" alt="Featured image of post 鱼书笔记-神经网络(下)" /&gt;&lt;p&gt;&lt;strong&gt;以下内容皆基于鱼书《深度学习入门基于python的理论与实现》&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="3层神经网络的实现"&gt;3层神经网络的实现
&lt;/h2&gt;&lt;p&gt;开始进行神经网络的实现，以下图的三层神经网络为例&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p1.jpg"
width="1042"
height="544"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p1_hu_cdf36966537c22.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p1_hu_9b4cdbc9d917229e.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="191"
data-flex-basis="459px"
&gt;&lt;/p&gt;
&lt;h3 id="符号确认"&gt;符号确认
&lt;/h3&gt;&lt;p&gt;首先导入符号$w_{12}^{(1)}$, $a_{1}^{(1)}$等，如下图，权重和隐藏层的神经元右上角有一个&amp;quot;(1)&amp;quot;，它表示权重和神经元的层号，此外，权重右下角的两个数字，它们是后一层的神经元和前一层的神经元的索引号，比如$w_{12}^{(1)}$表示前一层的第二个神经元$x_2$到后一层的第1个神经元$a_{1}^{(1)}$的权重。权重右下角按照&amp;quot;后一层的索引号、前一层的索引号&amp;quot;的顺序排序&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p2.jpg"
width="1016"
height="482"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p2_hu_159bca39b7693e1a.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p2_hu_717ed6910b15a5d1.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="210"
data-flex-basis="505px"
&gt;&lt;/p&gt;
&lt;h3 id="各层间信号传递的实现"&gt;各层间信号传递的实现
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p3.jpg"
width="1020"
height="624"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p3_hu_f87bf8a9583f1ac2.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p3_hu_a84fadd1538e9919.jpg 1024w"
loading="lazy"
alt="从输入层到第1层的信号传递"
class="gallery-image"
data-flex-grow="163"
data-flex-basis="392px"
&gt;&lt;/p&gt;
&lt;p&gt;上图增加了表示偏置的神经元&amp;quot;1&amp;quot;。偏置的右下角的索引号只有一个因为前一层的偏置神经元只有一个&lt;/p&gt;
&lt;p&gt;现在通过加权信号和偏置的和计算表示$a_{1}^{(1)}$。
&lt;/p&gt;
$$
a_{1}^{(1)} = w_{11}^{(1)} x_{1} + w_{12}^{(1)} x_{2} + b_{1}^{(1)}\tag{8}
$$&lt;p&gt;
如果用矩阵的乘法运算，则可以将第1层的加权和表示成下面的式(9)
&lt;/p&gt;
$$
A^{(1)} = XW^{(1)} + B^{(1)}
\tag{9}
$$&lt;p&gt;
其中，$A^{(1)}$、$X$、$B^{(1)}$、$W^{(1)}$ 如下所示：
&lt;/p&gt;
$$
A^{(1)} = \begin{pmatrix}
a_{1}^{(1)} &amp; a_{2}^{(1)} &amp; a_{3}^{(1)}
\end{pmatrix},
\quad
X = \begin{pmatrix}
x_1 &amp; x_2
\end{pmatrix},
\quad
B^{(1)} = \begin{pmatrix}
b_{1}^{(1)} &amp; b_{2}^{(1)} &amp; b_{3}^{(1)}
\end{pmatrix}
$$$$
W^{(1)} = \begin{pmatrix}
w_{11}^{(1)} &amp; w_{21}^{(1)} &amp; w_{31}^{(1)} \\
w_{12}^{(1)} &amp; w_{22}^{(1)} &amp; w_{32}^{(1)}
\end{pmatrix}
$$&lt;p&gt;然后用NumPy多维数组来实现式(9)，输入信号，权重，偏置设置成任意值&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;W1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;B1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# (2,3)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#(2,)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#(3.)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;A1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;B1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;W1是2x3的数组，X是元素个数为2的一维数组。这里，W1和X的对应维度的元素个数也保持了一致。&lt;/p&gt;
&lt;p&gt;然后我们用python来实现第一层激活函数的计算过程&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Z1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Z1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这里说的sigmoid函数就是之前定义的那个，它会接收NumPy数组，然后返回元素个数相同的NumPy数组&lt;/p&gt;
&lt;p&gt;下面我们来实现第1层到第2层的信号传递&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;W2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;B2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Z1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#(3,)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#(3,2)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#(2,)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;A2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Z1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;B2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Z2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;除了第一层的输出变成了第二层的输入，这个实现和刚才的一样&lt;/p&gt;
&lt;p&gt;最后是第二层到输出层的信号传递，输出层的实现也和之前的实现基本相同，不过，最后的激活函数和之前的隐藏层有所不同&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;identity_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;W3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;B3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;A3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Z2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;W3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;B3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;identity_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这里定义了&lt;code&gt;identity_function()&lt;/code&gt;函数（恒等函数），并将其作为输出层的激活函数。&lt;/p&gt;
&lt;h3 id="代码总结"&gt;代码总结
&lt;/h3&gt;&lt;p&gt;按照神经网络的实现惯例，把权重记为大写字母W1，其他都用小写字母表示&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init_network&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;W1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;W2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;W3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;W1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;W2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;W3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;a1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;z1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;a2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;z2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;a3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;W3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;identity_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init_network&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# [ 0.31682708 0.69627909]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这里定义了&lt;code&gt;init_network()&lt;/code&gt;和&lt;code&gt;forward()&lt;/code&gt;函数，&lt;code&gt;init_network()&lt;/code&gt;函数会进行权重和偏置的初始化，并将它们保存在字典变量network中。&lt;code&gt;forward()&lt;/code&gt;函数中则封装了将输入信号转换为输出信号的处理过程&lt;/p&gt;
&lt;h2 id="输出层的设计"&gt;输出层的设计
&lt;/h2&gt;&lt;p&gt;神经网络要根据情况改变输出层的激活函数。一般而言，回归问题用恒等函数，分类问题用softmax函数。&lt;/p&gt;
&lt;h3 id="恒等函数和softmax函数"&gt;恒等函数和softmax函数
&lt;/h3&gt;&lt;p&gt;恒等函数会将输入按原样输出&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p4.jpg"
width="458"
height="258"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p4_hu_77896edcbee24fff.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p4_hu_91aeb402bca64790.jpg 1024w"
loading="lazy"
alt="恒等函数"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;分类问题中的softmax函数可以用下面的式(10)表示
&lt;/p&gt;
$$
y_k = \frac{\exp(a_k)}{\sum_{i=1}^{n} \exp(a_i)}\tag{10}
$$&lt;p&gt;
上式表示假设输出层共有n个神经元，计算第k个神经元的输出$y_k$，如式(10)所示，softmax函数的分子是输入信号$a_k$的指数函数，分母是所有输入信号的指数函数的和&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p5.jpg"
width="410"
height="262"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p5_hu_6074506a6f9a8be2.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8B/p5_hu_7fc477ccae4b8112.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="156"
data-flex-basis="375px"
&gt;&lt;/p&gt;
&lt;p&gt;接下来来实现softmax函数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exp_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sum_exp_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp_a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sub_exp_a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="实现softmax函数时的注意事项"&gt;实现softmax函数时的注意事项
&lt;/h3&gt;&lt;p&gt;上面的softmax函数在计算上有一定的缺陷，就是溢出的问题，softmax函数的实现中要进行指数函数的运算，但是此时指数函数的值很容易变得非常大，比如$e^{1000}$的结果会返回一个表示无穷大大inf，在这些超大值之间进行除法运算，结果会出现不确定的情况，softmax可以像如下(11)改进
&lt;/p&gt;
$$
\begin{aligned}
y_k &amp;= \frac{\exp(a_k)}{\sum_{i=1}^{n} \exp(a_i)}
= \frac{C \exp(a_k)}{C \sum_{i=1}^{n} \exp(a_i)} \\[6pt]
&amp;= \frac{\exp(a_k + \log C)}{\sum_{i=1}^{n} \exp(a_i + \log C)} \\[6pt]
&amp;= \frac{\exp(a_k + C')}{\sum_{i=1}^{n} \exp(a_i + C')}
\end{aligned}
\tag{11}
$$&lt;p&gt;
先在分子和分母上都乘以C（一个任意的常数），然后把C移动到指数函数中，记为$log C$。最后把$logC$替换为另外一个符号$C'$&lt;/p&gt;
&lt;p&gt;综上，我们来实现下最终版的softmax函数&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exp_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sum_exp_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp_a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sum_exp_a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="softmax函数的特征"&gt;softmax函数的特征
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;输出总和为1，因为这个性质我们才可以把softmax函数的输出解释为“概率”&lt;/li&gt;
&lt;li&gt;使用了softmax函数各个元素之间的大小关系也不会改变，因为exp是单调递增的&lt;/li&gt;
&lt;li&gt;神经网络一般只会把输出值最大的神经元所对应的类别作为识别结果。使用softmax函数输出值最大的神经元的位置也不会变，因此输出层的softmax函数一般会被忽略&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="输出层的神经元数量"&gt;输出层的神经元数量
&lt;/h3&gt;&lt;p&gt;输出层的神经元数量需要根据待解决的问题来决定。对于分类问题，输出层的神经元数量一般设定为类别的数量。比如，对于某个输入图像，预测是图中的数字0到9中的哪个的问题，可以把输出层的神经元设定为10个，然后把这十个神经元按照从上到下，从0-9依次编号，并且值用不同的灰度表示，颜色越深，输出的值就越大&lt;/p&gt;
&lt;h2 id="手写数字识别"&gt;手写数字识别
&lt;/h2&gt;&lt;p&gt;开始解决实际问题，假设学习已经全部结束，我们使用学习到的参数，先实现神经网络的“推理处理”。这个推理处理也称为神经网络的&lt;strong&gt;前向传播&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="mnist数据集"&gt;MNIST数据集
&lt;/h3&gt;&lt;p&gt;MNIST数据集是由0到9的数字图像构成的。训练图像有6万张，测试图像有1万张，这些图像可以用于学习与推理。MNIST数据集的一般使用方法是，先用训练图像进行学习，再用学习到的模型度量能在能在多大程度上对测试图像进行正确的分类&lt;/p&gt;
&lt;p&gt;MNIST的图像数据是28像素x28像素的灰度图像（1通道），各个像素的取值在0到255之间。每个图像都相应地标有“7” “2” “1”等标签。&lt;/p&gt;
&lt;h2 id="从数据中学习"&gt;从数据中学习
&lt;/h2&gt;&lt;h3 id="数据驱动"&gt;数据驱动
&lt;/h3&gt;&lt;p&gt;如何实现数字“5”的识别，如果要设计一个能将5正确分类的程序&lt;/p&gt;</description></item><item><title>鱼书笔记-神经网络(上)</title><link>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/</link><pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/</guid><description>&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic3.jpg" alt="Featured image of post 鱼书笔记-神经网络(上)" /&gt;&lt;p&gt;&lt;strong&gt;以下内容皆基于鱼书《深度学习入门基于python的理论与实现》&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="从感知机到神经网络"&gt;从感知机到神经网络
&lt;/h2&gt;&lt;h3 id="感知机回顾"&gt;感知机回顾
&lt;/h3&gt;&lt;p&gt;用图来表示神经网络，类比感知机，我们把左边的一列称为&lt;strong&gt;输入层&lt;/strong&gt;，最右边的称之为&lt;strong&gt;输出层&lt;/strong&gt;，中间的称为&lt;strong&gt;中间层&lt;/strong&gt;(也称为隐藏层，因为神经元肉眼看不见)，我们知道当感知机接受$x_1,x_2$两个输入信号，输出$y$时，可以用如下的数学式来表示
&lt;/p&gt;
$$
y =
\begin{cases}
0, &amp; b + w_1 x_1 + w_2 x_2 \le 0 \\
1, &amp; b + w_1 x_1 + w_2 x_2 &gt; 0
\end{cases}
\tag{1}
$$&lt;p&gt;
$b$是偏置，用于控制神经元被激活的容易程度，而$w_1,w_2$是表示各个信号的权重的参数，用于控制各个信号的重要性&lt;/p&gt;
&lt;p&gt;我们现在可以通过调用一个函数来替代(1)中分case讨论的情况来简化(1)，改写成如下形式
&lt;/p&gt;
$$
y = h(b + w_1x_1+ w_2x_2)\tag{2}
$$$$
h(x) =
\begin{cases}
0, &amp; x \le 0 \\
1, &amp; x &gt; 0
\end{cases}\tag{3}
$$&lt;h3 id="激活函数引入"&gt;激活函数引入
&lt;/h3&gt;&lt;p&gt;刚才的h(x)把输入信号的总和转换成了输出信号，h(x)就被称为&lt;strong&gt;激活函数(activation function)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;现在进一步改写式(2)，写成如下形式
&lt;/p&gt;
$$
a = b + w_1x_1 + w_2x_2\tag{4}
$$$$
y = h(a)\tag{5}
$$&lt;p&gt;首先，式(4)计算加权输入信号的和偏置的总和，然后用(5)的h函数转换为输出&lt;/p&gt;
&lt;h2 id="激活函数"&gt;激活函数
&lt;/h2&gt;&lt;h3 id="sigmoid函数"&gt;sigmoid函数
&lt;/h3&gt;&lt;p&gt;神经网络中经常使用的一个激活函数就是&lt;strong&gt;sigmoid函数&lt;/strong&gt;
&lt;/p&gt;
$$
h(x)=\frac{1}{1+e^{-x}} \quad (\text{sigmoid function})\tag{6}
$$&lt;p&gt;
实际上，感知机和神经网络的主要区别就在于激活函数，其他方面基本都是一样的&lt;/p&gt;
&lt;h3 id="阶跃函数的实现"&gt;阶跃函数的实现
&lt;/h3&gt;&lt;p&gt;阶跃函数如(3)所示，当输入超过0时，输出1，否则输出0，可以用如下代码简单实现&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;step_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个代码中参数x只能接受实数。例如不允许&lt;code&gt;step_function(np.array([1.0,2.0]))&lt;/code&gt;，所以我们把它修改为支持NumPy数组的实现&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;step_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="阶跃函数的图形"&gt;阶跃函数的图形
&lt;/h3&gt;&lt;p&gt;接下来我们就用图来表示上面定义的阶跃函数&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pylab&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;step_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ylim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#y轴范围&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/figure1.jpg"
width="640"
height="480"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/figure1_hu_b73f7a1f6afaf12d.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/figure1_hu_35f69effb2292b01.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
&gt;&lt;/p&gt;
&lt;h3 id="sigmoid函数的实现"&gt;sigmoid函数的实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sigmoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;之所以sigmoid函数的实现支持NumPy数组，就是因为NumPy的广播功能，如果在标量和NumPy数组之间进行运算，标量会和NumPy数组的各个元素进行运算，&lt;code&gt;np.exp(-x)&lt;/code&gt;会生成NumPy数组，所以&lt;code&gt;1/(1 + np.exp(-x))&lt;/code&gt;的运算将会在NumPy数组的各个元素间进行&lt;/p&gt;
&lt;h3 id="sigmoid函数和阶跃函数的比较"&gt;sigmoid函数和阶跃函数的比较
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic4.jpg"
width="1070"
height="1034"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic4_hu_28a352c733757a52.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic4_hu_e5092fec73d6ba9.jpg 1024w"
loading="lazy"
alt="阶跃函数与sigmoid函数"
class="gallery-image"
data-flex-grow="103"
data-flex-basis="248px"
&gt;&lt;/p&gt;
&lt;p&gt;观察可以发现，首先区别就是平滑性，sigmoid函数是一条平滑的曲线，输出随着输入发生连续性的变化。而阶跃函数以0为界，输出发生急剧性的变化。因此我们可以知道，感知机的神经元之间流动的是0或1的二元信号，神经网络中流动的是连续的实数值信号。&lt;/p&gt;
&lt;p&gt;然后说一下阶跃函数和sigmoid函数的共同性质，两者的结构均是“输入小时输出接近0；输入大时，输出靠近1”，以及不管输入是什么值，输出信号的值都在0和1中间&lt;/p&gt;
&lt;h3 id="非线性函数"&gt;非线性函数
&lt;/h3&gt;&lt;p&gt;阶跃函数和sigmoid函数都是非线性函数&lt;/p&gt;
&lt;p&gt;神经网络的激活函数必须使用非线性函数，因为如果使用线性函数，加深神经网络的层数就没有意义了（应该很好理解，很多线型函数复合仍然是线性的，就不具体说了）&lt;/p&gt;
&lt;h3 id="relu函数"&gt;ReLU函数
&lt;/h3&gt;&lt;p&gt;最近比较常见的是ReLU函数&lt;/p&gt;
&lt;p&gt;ReLU函数在输入大于0时，直接输出该值；在输入小于等于0的时候，输出0&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic5.jpg"
width="1088"
height="716"
srcset="https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic5_hu_9c986dae1cf1af53.jpg 480w, https://lunatide.tech/p/%E9%B1%BC%E4%B9%A6%E7%AC%94%E8%AE%B0-%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8A/pic5_hu_b422439f22956271.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="151"
data-flex-basis="364px"
&gt;&lt;/p&gt;
&lt;p&gt;ReLU函数可以表示为以下数学式
&lt;/p&gt;
$$
h(x) =\begin{cases}
x, &amp; x &gt; 0 \\
0, &amp; x \le 0
\end{cases}
\tag{7}
$$&lt;p&gt;
ReLU函数的实现也非常简单&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;relu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="多维数组的运算"&gt;多维数组的运算
&lt;/h2&gt;&lt;h3 id="多维数组"&gt;多维数组
&lt;/h3&gt;&lt;p&gt;首先假定有一个一维数组&lt;code&gt;A = np.array[1,2,3,4]&lt;/code&gt;，数组的维数可以通过&lt;code&gt;np.ndim&lt;/code&gt;得到。数组的形状可以通过实例变量shape获得，A由四个元素构成，是一维的，所以A.shape就是（4，），这个结果是个元组，这个一维数组为了保证和多维一样的格式，所以仍然被写成元组&lt;/p&gt;
&lt;h3 id="矩阵乘法"&gt;矩阵乘法
&lt;/h3&gt;&lt;p&gt;不再赘述&lt;/p&gt;
&lt;h3 id="神经网络的内积"&gt;神经网络的内积
&lt;/h3&gt;</description></item><item><title>CMU15445-Project01</title><link>https://lunatide.tech/p/cmu15445-project01/</link><pubDate>Sat, 25 Oct 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/cmu15445-project01/</guid><description>&lt;img src="https://lunatide.tech/p/cmu15445-project01/good.jpg" alt="Featured image of post CMU15445-Project01" /&gt;&lt;p&gt;四个Projects都做完了，但是还在刷Leaderboa，有空就update博客内容&lt;/p&gt;</description></item><item><title>计算几何专项</title><link>https://lunatide.tech/p/%E8%AE%A1%E7%AE%97%E5%87%A0%E4%BD%95%E4%B8%93%E9%A1%B9/</link><pubDate>Thu, 16 Oct 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/%E8%AE%A1%E7%AE%97%E5%87%A0%E4%BD%95%E4%B8%93%E9%A1%B9/</guid><description>&lt;img src="https://lunatide.tech/p/%E8%AE%A1%E7%AE%97%E5%87%A0%E4%BD%95%E4%B8%93%E9%A1%B9/codeforce_%E5%89%AF%E6%9C%AC.jpg" alt="Featured image of post 计算几何专项" /&gt;&lt;p&gt;&lt;a class="link" href="https://vjudge.net/problem/POJ-2318" title="POJ-2318"
target="_blank" rel="noopener"
&gt;POJ-2318&lt;/a&gt;&lt;/p&gt;</description></item><item><title>ICPC日常训练01</title><link>https://lunatide.tech/p/icpc%E6%97%A5%E5%B8%B8%E8%AE%AD%E7%BB%8301/</link><pubDate>Thu, 09 Oct 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/icpc%E6%97%A5%E5%B8%B8%E8%AE%AD%E7%BB%8301/</guid><description>&lt;img src="https://lunatide.tech/p/icpc%E6%97%A5%E5%B8%B8%E8%AE%AD%E7%BB%8301/codeforce_%E5%89%AF%E6%9C%AC.jpg" alt="Featured image of post ICPC日常训练01" /&gt;&lt;p&gt;日常的训练，回顾一下做的几道题，实现不发&lt;/p&gt;
&lt;h2 id="icpc-hangzhou-reginal-f-fuzzy-ranking"&gt;ICPC Hangzhou Reginal F Fuzzy Ranking
&lt;/h2&gt;&lt;p&gt;不太会做，看了一下题解，下面是看完题解后的思路&lt;/p&gt;
&lt;p&gt;直接为任意“前→后”对连边会太多，在同一排列中，只连相邻元素 $a_j\to a_{j+1}$ 即可：因为一条从前往后的有向路径已经能覆盖该排列中任意“前→后”的可达关系。对所有 $k$ 个排列加边&lt;/p&gt;
&lt;p&gt;对图做一次 Tarjan/Kosaraju缩点，得到每个学校所属的 SCC 编号bel[x]，以及按拓扑序逆序排列的分量列表 scc。把每个榜单按 bel重新映射成“分量编号序列”。&lt;/p&gt;
&lt;p&gt;在某个榜单中，原先相邻的两点之间有边，缩点后相同分量的编号在该榜单上必然连成一段。因此每个榜单都可以被切成若干段，每段恰好由同一个 SCC 的元素组成。这个性质让我们可以把任意区间 $[l,r]$ 拆解为：左端一个不完整段 + 中间若干完整段 + 右端一个不完整段。&lt;/p&gt;
&lt;p&gt;对每个榜单i:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Segment：对位置轴进行分段。令 Segment= (L, R) 表示pos所在的那段在该榜单上的左右端点下标。线性扫，遇到一段相同编号的区间 $[L,R]$ 就把这对端点填给其中的每个位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dp 前缀：统计从开头到位置 $j$ 为止、同段内能形成的“模糊对”数量。若 $j$ 落在段 $[L,R]$，新增的成对数等于该段里被纳入的元素个数减一，即 $j-L$。因此定义
&lt;/p&gt;
$$
dp[id][j] = dp[id][j-1] + (j-L).
$$&lt;p&gt;
这样任意区间 $[x,y]$ 的“在同一段内形成的对数”就能通过 $dp$ 差分 $dp[id][y]-dp[id][x]$ 得到。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;总计复杂度$O(nk)$&lt;/p&gt;
&lt;h2 id="cf1841e-fill-the-matrix"&gt;CF1841E Fill the Matrix
&lt;/h2&gt;&lt;p&gt;题目说有一个$n * n$的正方形矩阵，在第$i$列，前$a_i$个格子都是黑色，剩下的格子是白色，在矩阵放入1到m到整数，每个格子最多放一个且黑色格子不能放，定义美丽值为数字 $j$ 和 $j+1$ 位于&lt;strong&gt;同一行&lt;/strong&gt;，且 $j+1$ 位于 $j$ 的&lt;strong&gt;右边相邻的格子&lt;/strong&gt;，求放置m个整数后最大美丽值&lt;/p&gt;
&lt;p&gt;我们一定先选一个最长的白色格子全填进去，直到填满或者数不够。因为如果不继续填等于说最后填的那个数旁边的格子就浪费了，所以关键就在于要把所有行的白色连续横段都计算出来，按长度分组，记为cnt[n]，代表长度n的横段有几个，直接暴力扫迷线维护复杂度是$O(n^2)$的，直接炸了，所以我们用线段树来维护最大值和位置然后再分治来统计cnt数组，相当于构建一颗笛卡尔树，每次把区间按最大值分为左右两部分，统计每层能产生的白格段，最后用贪心计算答案就可以了，这样最后复杂度就控制在$O(nlogn)$&lt;/p&gt;
&lt;h2 id="cf2056d-unique-median-2200"&gt;CF2056D Unique Median 2200
&lt;/h2&gt;&lt;p&gt;给了一个整数数组b，当排序后满足$b⌊\frac{m+1}{2}⌋=b⌈\frac{m+1}{2}⌉$时我们说数组是好的，题目说给一个长度为n的数组a，计算好子数组数量&lt;/p&gt;
&lt;p&gt;首先观察好数组定义，我们可以很容易发现奇数长度的数组一定是好数组，所以我们只要看偶数长度的数组就可以了，很困难，想了一段时间，一开始的想法是如果我们要让偶数的数组是好的，那么当且仅当存在某个数$v$，使得在选取的子数组中$v \ge m/2$，也就是说至少一半都是同一个数，但是肯定会有重复用容斥修正，减掉重复的就可以，实现很麻烦，写了很久，而且还发现是错误的，对于[1,1,2,3]按照这个思路明显错的，而且发现正着处理非常麻烦困难，于是决定反向试试，从所有子数组中减掉&amp;quot;坏的&amp;quot;，再修正&lt;/p&gt;
&lt;p&gt;很快就发现了一些东西，对于一个偶数的数组，只要让排序后第k个数小于k+1个数一定是坏的，再具体点，我们选定两个相邻的数x，y，我们只要让左中位数小于等于x，右中位数大于等于y就可以了，然后用&lt;strong&gt;前缀和+map即可&lt;/strong&gt;，并且小于等于x的数的数量和大于等于y的数字数量必须要一样多，那么我们可以把数组按照值分为3类，一种小于等于x我们记为-1，大于等于y记为+1，介于x和y之间的我们直接清空，因为出现就相当于直接破坏了我们的数组&lt;/p&gt;
&lt;p&gt;最后就是用容斥进行修正，只要x和y相差超过2就记为重复的减掉就可以了&lt;/p&gt;
&lt;h2 id="cf1854a2--dual-hard-version"&gt;CF1854A2 Dual (Hard Version)
&lt;/h2&gt;&lt;p&gt;一道思维题，给了一个数组，可以对于$i,j$的元素进行操作，使得$a_i := a_i + a_j$&lt;/p&gt;
&lt;p&gt;要求在31次操作内让数组不递减&lt;/p&gt;
&lt;p&gt;首先当所有元素都大于等于0或者全部都小于等于0的时候很显然，我们只需要把右边的数依次加上左边的数肯定是对的（小于等于0同理反过来就行）&lt;/p&gt;
&lt;p&gt;所以关键是要看数组里有正有负，我们设最大的正数是mx，绝对值最大的负数是mi，正数数量是cp，负数数量是cm，那么我们有两种选择，一种是都统一成非负，一种是统一成非正，只要符号统一我们就方便了，拿统一非负举例（非正同理），我们其实就是要让最大的正数足够大，能够覆盖所有负数，我们首先计算mx经过几次翻倍可以大于mi，然后让他翻倍，之后让所有的负数都加上这个mx它们都变成正数了，然后我们就判断一下这两种选择哪个更优即可&lt;/p&gt;</description></item><item><title>Lab1 DataLab</title><link>https://lunatide.tech/p/lab1-datalab/</link><pubDate>Sun, 28 Sep 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/lab1-datalab/</guid><description>&lt;img src="https://lunatide.tech/p/lab1-datalab/csapp.jpg" alt="Featured image of post Lab1 DataLab" /&gt;</description></item><item><title>lecture03 Bits,Bytes and Integer cout</title><link>https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/</link><pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/</guid><description>&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/csappp.jpg" alt="Featured image of post lecture03 Bits,Bytes and Integer cout" /&gt;&lt;h2 id="加法"&gt;加法
&lt;/h2&gt;&lt;h3 id="无符号加法"&gt;无符号加法
&lt;/h3&gt;&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/addition.jpg"
width="1902"
height="1160"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/addition_hu_2c2b2ad97cb05e19.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/addition_hu_4fac26a627e6d383.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="163"
data-flex-basis="393px"
&gt;&lt;/p&gt;
&lt;p&gt;无符号的加法很简单，和二进制加法一样，只不过要把overflow的删除，并且把结果取模即可&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/visualizing.jpg"
width="1072"
height="1038"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/visualizing_hu_7f948e5cc8d4b0e7.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/visualizing_hu_a251ef1a212f1206.jpg 1024w"
loading="lazy"
alt="可视化整数加法图"
class="gallery-image"
data-flex-grow="103"
data-flex-basis="247px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/%E5%8F%AF%E8%A7%86%E5%8C%96%E6%97%A0%E7%AC%A6%E5%8F%B7%E5%8A%A0%E6%B3%95%E5%9B%BE.jpg"
width="1080"
height="1060"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/%E5%8F%AF%E8%A7%86%E5%8C%96%E6%97%A0%E7%AC%A6%E5%8F%B7%E5%8A%A0%E6%B3%95%E5%9B%BE_hu_b17e528b163a78d4.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/%E5%8F%AF%E8%A7%86%E5%8C%96%E6%97%A0%E7%AC%A6%E5%8F%B7%E5%8A%A0%E6%B3%95%E5%9B%BE_hu_9cf951470edc2c6f.jpg 1024w"
loading="lazy"
alt="可视化无符号加法图"
class="gallery-image"
data-flex-grow="101"
data-flex-basis="244px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;检测无符号数中加法的溢出&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;令 $s = U +_w^u V$ 为无符号整数 $U$ 和 $V$ 的和，那么当且仅当 $s &amp;lt; U$（或等价的 $s &amp;lt; V$）时产生溢出，这是因为：&lt;/p&gt;
$$
\begin{aligned}
s &amp;= U + V - 2^w &lt; U \\
s &amp;= U + V - 2^w &lt; V
\end{aligned}
$$&lt;h2 id="补码加法"&gt;补码加法
&lt;/h2&gt;&lt;p&gt;![](截屏2025-09-28 15.57.52.jpg)&lt;/p&gt;
&lt;p&gt;对满足 $-2^{w-1} \leq x$，$y \leq 2^{w-1}-1$ 的整数 $x$ 和 $y$，有：&lt;/p&gt;
$$
x +_w^t y =
\begin{cases}
x + y - 2^w, &amp; 2^{w-1} \leq x + y \quad \text{正溢出} \\
x + y, &amp; -2^{w-1} \leq x + y &lt; 2^{w-1} \quad \text{正常} \\
x + y + 2^w, &amp; x + y &lt; -2^{w-1} \quad \text{负溢出}
\end{cases}
$$&lt;p&gt;
两个数的$w$位补码之和与无符号之和有完全相同的位级表示&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;利用左移做乘法&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大多数机器中左移比乘法快
&lt;ul&gt;
&lt;li&gt;编译器会自动生成这样的代码&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;方法的证明：&lt;/p&gt;
&lt;p&gt;假设 $x$ 的 $w$ 位二进制表示为 $\left[x_{w-1}, x_{w-2}, \cdots, x_{0}\right]$，那么 $\left[x_{w-1}, x_{w-2}, \cdots, x_{0}, 0, \cdots, 0\right]$ 给出了 $x2^k$ 的 $w+k$ 位二进制表示：&lt;/p&gt;
$$
\begin{aligned}
B2U_{w+k}\left(\left[x_{w-1}, x_{w-2}, \cdots, x_{0}, 0, \cdots, 0\right]\right) &amp;= \sum_{i=0}^{w-1} x_{i} 2^{i+k} \\
&amp;= \left[\sum_{i=0}^{w-1} x_{i} 2^{i}\right] \cdot 2^{k} \\
&amp;= x 2^{k}
\end{aligned}
$$&lt;p&gt;对于固定长度的表示，高 $k$ 位被丢弃，左移 $k$ 位的二进制表示为
&lt;/p&gt;
$$
\left[x_{w-k-1}, x_{w-k-2}, \cdots, x_{0}, 0, \cdots, 0\right]
$$&lt;p&gt;所以
&lt;/p&gt;
$$
\begin{aligned}
B2U_{w}\left(\left[x_{w-k-1}, x_{w-k-2}, \cdots, x_{0}, 0, \cdots, 0\right]\right) &amp;= \sum_{i=0}^{w-k-1} x_{i} 2^{i+k} \\
&amp;= \left[\sum_{i=0}^{w-k-1} x_{i} 2^{i}\right] \cdot 2^{k} \\
&amp;= \left[\sum_{i=0}^{w-1} x_{i} 2^{i}\right] \cdot 2^{k} \mod 2^w \\
&amp;= x2^k \mod 2^w
\end{aligned}
$$&lt;p&gt;所以对于无符号整数
&lt;/p&gt;
$$
B2U_{w}\left(\left[x_{w-k-1}, x_{w-k-2}, \cdots, x_{0}, 0, \cdots, 0\right]\right) = \text{UMult}_w(x,2^k)
$$&lt;p&gt;对于有符号整数，利用
&lt;/p&gt;
$$
\text{TMult}_w(u,v) = U2T_w((u \cdot v) \mod 2^w)
$$&lt;p&gt;
可以得到相同的结果。&lt;/p&gt;
&lt;p&gt;一般情形&lt;/p&gt;
&lt;p&gt;现在考虑一般的情形，假设我们需要计算 $u \times K$，其中 $K$ 为常数，将 $K$ 表达为一组 $0$ 和 $1$ 交替的序列
&lt;/p&gt;
$$
[(0 \cdots 0)(1 \cdots 1)(0 \cdots 0) \cdots (1 \cdots 1)]
$$&lt;p&gt;考虑一组从位置 $n$ 到位置 $m$ 的连续 $1$，那么可以用如下方式计算这部分的对于乘积的影响：
&lt;/p&gt;
$$
\begin{aligned}
&amp;(x &lt;&lt; n) + (x &lt;&lt; (n-1)) + \cdots + (x &lt;&lt; m) \\
&amp;(x &lt;&lt; (n+1)) - (x &lt;&lt; m)
\end{aligned}
$$&lt;h2 id="移位操作和二进制乘除法的联系"&gt;移位操作和二进制乘除法的联系
&lt;/h2&gt;&lt;h3 id="使用移位操作代替除以-2-的幂无符号"&gt;使用移位操作代替除以 2 的幂（无符号）
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;$u &amp;raquo; k$ 给出 $\lfloor u / 2^{k} \rfloor$&lt;/li&gt;
&lt;li&gt;使用逻辑移位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/mul.jpg"
width="958"
height="268"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/mul_hu_f3787a342be1b508.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/mul_hu_d43e82d7c355e31d.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="357"
data-flex-basis="857px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;证明：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假设 $x$ 的 $w$ 位二进制表示为 $\left[x_{w-1}, x_{w-2}, \cdots, x_{0}\right]$，右移 $k$ 位的二进制表示为 $\left[0, \cdots, 0, x_{w-1}, x_{w-2}, \cdots, x_{k}\right]$&lt;/p&gt;
$$
\begin{aligned}
B2U_w(\left[0, \cdots, 0, x_{w-1}, x_{w-2}, \cdots, x_{k}\right]) &amp;= \sum_{i=0}^{w-k-1} x_{i+k} 2^{i} \\
B2U_{w}\left(\left[x_{w-1}, x_{w-2}, \cdots, x_{0}\right]\right) &amp;= \sum_{i=0}^{w-1} x_{i} 2^{i} \\
&amp;= 2^k \sum_{i=0}^{w-k-1} x_{i+k} 2^{i} + \sum_{i=0}^{k-1} x_{i} 2^{i} \\
&amp;= 2^k B2U_w(\left[0, \cdots, 0, x_{w-1}, x_{w-2}, \cdots, x_{k}\right]) + \sum_{i=0}^{k-1} x_{i} 2^{i}
\end{aligned}
$$&lt;p&gt;所以
&lt;/p&gt;
$$
\lfloor x / 2^{k} \rfloor = x &gt;&gt; k
$$&lt;h3 id="使用移位操作代替除以-2-的幂有符号"&gt;使用移位操作代替除以 2 的幂（有符号）
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;$u &amp;raquo; k$ 给出 $\lfloor u / 2^{k} \rfloor$&lt;/li&gt;
&lt;li&gt;使用算数移位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 $u &amp;gt; 0$ 时上述方法和无符号情形一致，但是当 $u &amp;lt; 0$ 时则会产生有问题的结果，例如取 $u = -12340$：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/power.jpg"
width="946"
height="330"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/power_hu_1746ae406e481f89.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/power_hu_908a3cf5ecc7fa72.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="286"
data-flex-basis="688px"
&gt;&lt;/p&gt;
&lt;p&gt;考虑 $k=4,8$ 时的结果，在 C 语言中实际结果为 $-771$ 和 $-48$，之所以和 C 语言中的结果不同，是因为上述算法朝着离 $0$ 更远的方向舍入，所以当 $u &amp;lt; 0$ 时要向上舍入，达到上述效果利用如下事实即可
&lt;/p&gt;
$$
\lceil x / y \rceil = \lfloor (x + y - 1) / y \rfloor
$$&lt;h2 id="unsigned"&gt;Unsigned
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;必须做一个明确的分配而不是暗示&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;容易犯错误，例如下面的代码会出现无限循环&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;unsigend&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可能会变的很诡异&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define DELTA sizeof(int)&lt;/span&gt;&lt;span class="c1"&gt;//默认int的size是一个unsigned的size_t
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;DELTA&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="n"&gt;DELTA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;//和上面的代码出现一样的问题
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;正确的代码如下&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;什么时候用无符号整数&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;执行模块化算术时使用&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多精度算术&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在使用位表示集时使用&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;逻辑右移，无符号扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（最好不要用unsigned）&lt;/p&gt;
&lt;h2 id="内存中数字的一些底层表示"&gt;内存中数字的一些底层表示
&lt;/h2&gt;&lt;h3 id="word-size"&gt;Word Size
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;硬件本身并不一定定义字长大小&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;任何给定的计算机都具有字长&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;直到现在，大部份机器都采用32 bits(4 bytes)作为字长&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址限制为4 GB($2^{32}$bytes)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;越来越多机器具有64位字长&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;机器仍然支持多种数据格式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字长的分数或整数倍&lt;/li&gt;
&lt;li&gt;但总是整数比特&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/2020050814.jpg"
width="416"
height="626"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/2020050814_hu_cbf826a69c37947c.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/2020050814_hu_7ea3a5b3a9ada827.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="66"
data-flex-basis="159px"
&gt;&lt;/p&gt;
&lt;h3 id="比特顺序"&gt;比特顺序
&lt;/h3&gt;&lt;p&gt;分为大端法和小端法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Example
&lt;ul&gt;
&lt;li&gt;变量x的value是0x01234567&lt;/li&gt;
&lt;li&gt;地址是0x100&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/123.jpg"
width="2124"
height="584"
srcset="https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/123_hu_3b8e65817cb04c38.jpg 480w, https://lunatide.tech/p/lecture03-bitsbytes-and-integer-cout/123_hu_254e6da27a3b978d.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="363"
data-flex-basis="872px"
&gt;&lt;/p&gt;</description></item><item><title>lecture02 Bits,Bytes and Integer</title><link>https://lunatide.tech/p/lecture02-bitsbytes-and-integer/</link><pubDate>Wed, 24 Sep 2025 00:00:00 +0000</pubDate><guid>https://lunatide.tech/p/lecture02-bitsbytes-and-integer/</guid><description>&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/csapp.jpg" alt="Featured image of post lecture02 Bits,Bytes and Integer" /&gt;&lt;h2 id="bits"&gt;Bits
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;为什么要用比特&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;易于用双稳态元件存储&lt;/li&gt;
&lt;li&gt;能在有噪声且不精确的电线上可靠传输&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/why-bits.jpg"
width="796"
height="249"
srcset="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/why-bits_hu_c5c40ed1d2998a92.jpg 480w, https://lunatide.tech/p/lecture02-bitsbytes-and-integer/why-bits_hu_98223fba1481aae8.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="319"
data-flex-basis="767px"
&gt;&lt;/p&gt;
&lt;p&gt;我们可以用二进制来表示浮点数。在一个二进制的小数中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小数点左边的第一位权重为 2^0，向左依次为 2^1、2^2、…&lt;/li&gt;
&lt;li&gt;小数点右边的第一位权重为 2^-1，向右依次为 2^-2、2^-3、…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，如果将数字写成 32 位或者 64 位的字符串会很麻烦。通常将 4 bits 为一组，用十六进制表示。这样十六进制与二进制的转换就非常方便。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/encoding.jpg"
width="1970"
height="1236"
srcset="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/encoding_hu_bd765ce93df5dcb0.jpg 480w, https://lunatide.tech/p/lecture02-bitsbytes-and-integer/encoding_hu_466d84b6ec8eb5a7.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="159"
data-flex-basis="382px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字节&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个字节等于 8 比特&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="布尔代数"&gt;布尔代数
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;比特之间的关系（操作）&lt;/li&gt;
&lt;li&gt;1 代表 true，0 代表 false&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/boolenan.jpg"
width="1454"
height="716"
srcset="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/boolenan_hu_fbbcc4b46b964464.jpg 480w, https://lunatide.tech/p/lecture02-bitsbytes-and-integer/boolenan_hu_d2d97a4fcc876b4a.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="203"
data-flex-basis="487px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example：表示与操作集合&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表示方式：
&lt;ul&gt;
&lt;li&gt;一个长度为 $w $的位向量表示集合 ${0, 1, …, w-1}$ 的子集&lt;/li&gt;
&lt;li&gt;如果 $a_j = 1，j ∈ A$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;for example：{0, 3, 5, 6}
&lt;ul&gt;
&lt;li&gt;01101001&lt;/li&gt;
&lt;li&gt;76543210&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于上面的这个例子，“01101001”代表的就是 “76543210” 中各个位的数字在集合中是否出现，出现即为 1，反之为 0。&lt;/p&gt;
&lt;h3 id="对比逻辑运算符"&gt;对比逻辑运算符
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;amp;&amp;amp;、||、 !&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;!0x41 → 0x00&lt;/li&gt;
&lt;li&gt;!0x99 → 0x01&lt;/li&gt;
&lt;li&gt;!!ox41 → 0x01&lt;/li&gt;
&lt;li&gt;0x69 &amp;amp;&amp;amp; 0x55 → 0x01&lt;/li&gt;
&lt;li&gt;p &amp;amp;&amp;amp; *p（避免空指针访问，如果 p 是 NULL，就不会解引用）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="移位操作"&gt;移位操作
&lt;/h2&gt;&lt;h3 id="左移x--y"&gt;左移：x &amp;laquo; y
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;向左移动位次，多余的bits扔掉，添加相对应数目的0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="右移x--y"&gt;右移：x &amp;raquo; y
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;逻辑右移&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;和左移类似&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;算数右移&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若第一个数为1，把原本填充的0改为填充1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="整数编码"&gt;整数编码
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;无符号数&lt;/p&gt;
&lt;p&gt;$B2U(X) = \sum_{i = 0}^{w-1} x_i \cdot 2^i $&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;补码(Two&amp;rsquo;s Complement)&lt;/p&gt;
&lt;p&gt;$B2T(X) = -x_{w-1} \cdot 2^{w-1} + \sum_{i=0}^{w-2} x_i \cdot 2^i$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;数值范围&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;W = 16&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value Type&lt;/th&gt;
&lt;th&gt;Formula / Binary Pattern&lt;/th&gt;
&lt;th&gt;Decimal&lt;/th&gt;
&lt;th&gt;Hex&lt;/th&gt;
&lt;th&gt;Binary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unsigned Values&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UMin&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;00000000 00000000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UMax&lt;/td&gt;
&lt;td&gt;2&lt;sup&gt;W&lt;/sup&gt; - 1&lt;/td&gt;
&lt;td&gt;65535&lt;/td&gt;
&lt;td&gt;0xFFFF&lt;/td&gt;
&lt;td&gt;11111111 11111111&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Two&amp;rsquo;s Complement Values&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TMin&lt;/td&gt;
&lt;td&gt;-2&lt;sup&gt;W-1&lt;/sup&gt; / 100&amp;hellip;0&lt;/td&gt;
&lt;td&gt;-32768&lt;/td&gt;
&lt;td&gt;0x8000&lt;/td&gt;
&lt;td&gt;10000000 00000000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TMax&lt;/td&gt;
&lt;td&gt;2&lt;sup&gt;W-1&lt;/sup&gt; - 1 / 011&amp;hellip;1&lt;/td&gt;
&lt;td&gt;32767&lt;/td&gt;
&lt;td&gt;0x7FFF&lt;/td&gt;
&lt;td&gt;01111111 11111111&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Other Values&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;111&amp;hellip;1&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;0xFFFF&lt;/td&gt;
&lt;td&gt;11111111 11111111&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;000&amp;hellip;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;00000000 00000000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;因此我们可以得到一个关系如下：&lt;/p&gt;
&lt;p&gt;​ $TMin = TMax + 1$&lt;/p&gt;
&lt;p&gt;​ $UMax = 2 \times TMax + 1$&lt;/p&gt;
&lt;h3 id="转换"&gt;转换
&lt;/h3&gt;&lt;p&gt;有符号整型和无符号整型和十进制的转换关系如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/123.jpg"
width="398"
height="604"
srcset="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/123_hu_cab4bce5ee716725.jpg 480w, https://lunatide.tech/p/lecture02-bitsbytes-and-integer/123_hu_65c664ea5f3229e8.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="65"
data-flex-basis="158px"
&gt;&lt;/p&gt;
&lt;p&gt;显然上述转换是可逆的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\text{U2B}(x)=\text{B2U}^{-1}(x)$&lt;/li&gt;
&lt;li&gt;$\text{T2B}(x)=\text{B2T}^{-1}(x)$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;利用复合关系可以得到有符号整型以及无符号整型的转换关系：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/111.jpg"
width="980"
height="420"
srcset="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/111_hu_5752f9e9974505ba.jpg 480w, https://lunatide.tech/p/lecture02-bitsbytes-and-integer/111_hu_f1bdac0e7da0cc11.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="233"
data-flex-basis="560px"
&gt;&lt;/p&gt;
&lt;p&gt;对于$\text {TMin} \leqslant x \leqslant \text{TMax}$
&lt;/p&gt;
$$
T 2 U_w(x)=\left\{\begin{array}{ll}
x+2^{w}, &amp; x&lt;0 \\
x, &amp; x \geqslant 0
\end{array}\right.
$$&lt;p&gt;
对于$0 \leqslant x \leqslant \text{UMax}$
&lt;/p&gt;
$$
U 2 T_{w}(u)=\left\{\begin{array}{ll}
u, &amp; u \leqslant \text{TMax} \\
u-2^{w}, &amp; u&gt;\text{TMax}
\end{array}\right.
$$&lt;p&gt;下图为补码到无符号数的转换关系&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/222.jpg"
width="2078"
height="1344"
srcset="https://lunatide.tech/p/lecture02-bitsbytes-and-integer/222_hu_71294f1650b786d6.jpg 480w, https://lunatide.tech/p/lecture02-bitsbytes-and-integer/222_hu_7b8b43f5f3a611ca.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="154"
data-flex-basis="371px"
&gt;&lt;/p&gt;
&lt;p&gt;C语言中的转换如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;ux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//显示转换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;uy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//隐式转换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;uy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ty&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="表达式求值规则"&gt;表达式求值规则
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;若单个表达式中&lt;strong&gt;同时出现无符号数与有符号数&lt;/strong&gt;，则&lt;strong&gt;有符号值会被隐式转换为无符号数&lt;/strong&gt;（包括比较运算 &lt;code&gt;&amp;lt;&lt;/code&gt;、&lt;code&gt;&amp;gt;&lt;/code&gt;、&lt;code&gt;==&lt;/code&gt;、&lt;code&gt;&amp;lt;=&lt;/code&gt;、&lt;code&gt;&amp;gt;=&lt;/code&gt;）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;举例：W = 32 位&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;常量定义&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TMIN = -2,147,483,648&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TMAX = 2,147,483,647&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;常量 1&lt;/th&gt;
&lt;th&gt;常量 2&lt;/th&gt;
&lt;th&gt;实际类型&lt;/th&gt;
&lt;th&gt;关系&lt;/th&gt;
&lt;th&gt;结果&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;假&lt;/td&gt;
&lt;td&gt;两者均为 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;普通有符号比较&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt; 被转成 &lt;code&gt;4294967295U&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2147483648&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;普通有符号比较&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2147483648&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2147483648&lt;/code&gt; 被转成 &lt;code&gt;2147483648U&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;普通有符号比较&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(unsigned)-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-2&lt;/code&gt; 被转成 &lt;code&gt;4294967294U&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2147483648U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;无符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;前者 &amp;lt; 后者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2147483647&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(int)2147483648U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;有符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;真&lt;/td&gt;
&lt;td&gt;后者溢出成 &lt;code&gt;-2147483648&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="小结有符号与无符号强制转换的基本规则"&gt;小结：有符号与无符号强制转换的基本规则
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位模式保持不变&lt;/strong&gt;&lt;br&gt;
强制转换时，内存中的 0/1 序列原样复制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;仅重新解释&lt;/strong&gt;&lt;br&gt;
同一段位模式，按目标类型（有符号或无符号）重新解读。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可能产生“意外”&lt;/strong&gt;&lt;br&gt;
数值可能突然加上或减去 2^W（W 为位数）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;表达式中的混合类型&lt;/strong&gt;&lt;br&gt;
只要表达式里&lt;strong&gt;同时出现&lt;/strong&gt;&lt;code&gt;signed int&lt;/code&gt;与&lt;code&gt;unsigned int&lt;/code&gt; ，&lt;strong&gt;编译器会把 &lt;code&gt;signed int&lt;/code&gt; 隐式转换成 &lt;code&gt;unsigned int&lt;/code&gt;&lt;/strong&gt;，再参与运算。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="扩展截断"&gt;扩展，截断
&lt;/h2&gt;&lt;h3 id="扩展"&gt;扩展
&lt;/h3&gt;&lt;p&gt;无符号扩展至需要在前面的bits补0,有符号扩展如下&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;任务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;给定w-bit的有符号整型x&lt;/li&gt;
&lt;li&gt;将其转换为w+k-bit的有符号整型，值不变&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;规则：&lt;/p&gt;
&lt;p&gt;将符号位复制k份&lt;/p&gt;
&lt;p&gt;$X&amp;rsquo; = X_{w-1},\dots,X_{w-1},X_{w-1},X_{w-2},\dots,X_0$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;补码的符号扩展&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;定义宽度为 $w$ 的位向量 $\vec{x} = [x_{w-1}, x_{w-2}, \dots, x_0]$ 和宽度为 $w&amp;rsquo;$ 的位向量 $\vec{x}&amp;rsquo; = [x&amp;rsquo;&lt;em&gt;{w&amp;rsquo;-1}, x&amp;rsquo;&lt;/em&gt;{w&amp;rsquo;-2}, \dots, x&amp;rsquo;&lt;em&gt;0]$，其中 $w&amp;rsquo; &amp;gt; w$。则$B2T_w(\vec{x}) = B2T&lt;/em&gt;{w&amp;rsquo;}(\vec{x}&amp;rsquo;)$，&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;补码数值的符号扩展&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;令$w&amp;rsquo; = w + k$，我们想要证明的是
&lt;/p&gt;
$$
B2T_{w+k} ([x_{w-1}, \cdots, x_{w-1}, x_{w-2}, \cdots, x_0]) = B2T_w([x_{w-1}, x_{w-2}, \cdots, x_0])
$$&lt;p&gt;下面的证明是对$k$进行归纳。也就是说，如果我们能够证明符号扩展一位保持了数值不变，那么符号扩展任意位都能保持这种属性。因此，证明的任务就变为了：
&lt;/p&gt;
$$
B2T_{w+1} ([x_{w-1}, x_{w-1}, x_{w-2}, \cdots, x_0]) = B2T_w([x_{w-1}, x_{w-2}, \cdots, x_0])
$$&lt;p&gt;展开左边的表达式，得到：
&lt;/p&gt;
$$
\begin{aligned}
B2T_{w+1} ([x_{w-1}, x_{w-1}, x_{w-2}, \cdots, x_0]) &amp;= -x_{w-1}2^w + \sum_{i=0}^{w-1}x_i2^i \\
&amp;= -x_{w-1}2^w + x_{w-1}2^{w-1} + \sum_{i=0}^{w-2}x_i2^i \\
&amp;= -x_{w-1}(2^w - 2^{w-1}) + \sum_{i=0}^{w-2}x_i2^i \\
&amp;= -x_{w-1}2^{w-1} + \sum_{i=0}^{w-2}x_i2^i \\
&amp;= B2T_w([x_{w-1}, x_{w-2}, \cdots, x_0])
\end{aligned}
$$&lt;p&gt;我们使用的关键属性是$2^w - 2^{w-1} = 2^{w-1}$。因此，加上一个权值为$-2^w$的位，和将一个权值为 $-2^{w-1}$的位转换为一个权值为 $2^{w-1}$ 的位，这两项运算的综合效果就会保持原始的数值。&lt;/p&gt;
&lt;h3 id="截断"&gt;截断
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;无符号截断&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;原始 $w$ 位：&lt;br&gt;
&lt;/p&gt;
$$
\mathrm{B2U}_w(X)=\sum_{i=0}^{w-1} x_i\cdot2^i
$$&lt;p&gt;截断为 $k$ 位后：&lt;br&gt;
&lt;/p&gt;
$$
\mathrm{B2U}_k(X)=\sum_{i=0}^{k-1} x_i\cdot2^i = \mathrm{B2U}_w(X)\bmod 2^k
$$&lt;p&gt;&lt;strong&gt;推导&lt;/strong&gt;
&lt;/p&gt;
$$
\begin{aligned}
B2U_w([x_{w-1}, x_{w-2}, \cdots, x_0]) \mod 2^k &amp;= \left[ \sum_{i=0}^{w-1} x_i 2^i \right] \mod 2^k \\
&amp;= \left[ \sum_{i=0}^{k-1} x_i 2^i \right] \mod 2^k \\
&amp;= \sum_{i=0}^{k-1} x_i 2^i \\
&amp;= B2U_k([x_{k-1}, x_{k-2}, \cdots, x_0])
\end{aligned}
$$&lt;p&gt;&lt;strong&gt;有符号截断&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;原始 $w$ 位：&lt;br&gt;
&lt;/p&gt;
$$
\mathrm{B2T}_w(X)=-x_{w-1}\cdot2^{w-1}+\sum_{i=0}^{w-2} x_i\cdot2^i
$$&lt;p&gt;
截断为 $k$ 位后：&lt;br&gt;
&lt;/p&gt;
$$
\mathrm{B2T}_k(X)=-x_{k-1}\cdot2^{k-1}+\sum_{i=0}^{k-2} x_i\cdot2^i = \mathrm{U2T}_k\!\bigl(\mathrm{B2U}_w(X)\bmod 2^k\bigr)
$$</description></item></channel></rss>