编码器是自编码器从数据中学习过程的首要阶段。它的主要职责是接收可能高维且复杂的输入数据,并将其转化为更紧凑、低维度的表示。可以将其视为一个信息精炼器,负责捕捉输入中最显著的特征,同时丢弃噪声或冗余。这种压缩表示通常被称为潜在空间表示或编码。通常,编码器是使用一系列神经网络层构建的。对于处理扁平或向量数据的标准自编码器(比如我们在第一章中讨论过的),这些层通常是全连接(或密集)层。编码器中这些层的显著特点是它们会逐步降低维度。例如,如果你的输入数据有784个特征,编码器中的第一个隐藏层可能有256个神经元,下一个128个,依此类推,直到编码器的最后一层(瓶颈层)输出所需的低维编码。digraph G { rankdir=LR; graph [fontname="sans-serif"]; node [shape=box, style="filled", fontname="sans-serif", margin=0.2]; edge [fontname="sans-serif"]; input_data [label="输入 X\n(例如,784 维)", fillcolor="#74c0fc", width=2, height=1]; subgraph cluster_encoder { label = "编码器网络"; labeljust="l"; labelloc="t"; bgcolor="#e9ecef"; node [fillcolor="#91a7ff"]; el1 [label="隐藏层 1\n(例如,256 个神经元)", width=2, height=0.9]; el2 [label="隐藏层 2\n(例如,128 个神经元)", width=2, height=0.9]; } bottleneck_label [label="潜在表示 z\n(例如,64 维)", fillcolor="#5c7cfa", width=2, height=1]; input_data -> el1 [arrowhead=vee]; el1 -> el2 [arrowhead=vee]; el2 -> bottleneck_label [arrowhead=vee]; }上图说明了一个典型的编码器结构,其中数据从高维输入流经神经元数量递减的层,最终形成低维的潜在表示。编码器中的每一层都对其输入进行一次转换。这通常涉及一个线性操作(将输入乘以权重矩阵并加上偏置向量),接着是一个非线性激活函数。从数学上讲,对于编码器中的单层,给定输入 $x'$(它可以是原始输入 $x$,也可以是前一个编码器层的输出)的输出 $h$ 可以表示为: $$h = \sigma(W x' + b)$$ 这里,$W$ 是权重矩阵,$b$ 是偏置向量,$\sigma$ 是激活函数。编码器层中常用的激活函数包括:ReLU (修正线性单元): $f(x) = \max(0, x)$。它计算效率高,并有助于缓解梯度消失问题。Sigmoid: $f(x) = \frac{1}{1 + e^{-x}}$。它将值压缩到0和1之间,可能有用,但对于非常大或非常小的输入,可能会遇到梯度消失问题。Tanh (双曲正切): $f(x) = \tanh(x)$。它将值压缩到-1和1之间,通常比Sigmoid更受欢迎,因为其输出是零中心的。非线性激活函数很重要;没有它们,一堆线性层就只会等同于一个单一的线性变换,从而严重限制了编码器可以学习的函数的复杂性。编码器的“奥妙”在于它如何学习每层的权重 $W$ 和偏置 $b$。在自编码器的训练过程中,整个网络,包括编码器,都会被优化以最小化重建误差(如在章节介绍中讨论重建损失函数时所述)。这意味着编码器不仅仅是随机压缩数据。相反,它学习执行一种压缩,这种压缩会保留对解码器重建原始输入最为必要的信息。因此,编码器学习了一种有意义的方式,将输入 $x$ 映射到潜在表示 $z$: $$z = \text{编码器}(x)$$ 这个学得的潜在向量 $z$ 是输入的压缩本质,当我们研究瓶颈层以及如何提取和使用这些特征时,我们将进一步讨论这一点。这种压缩的质量十分重要,因为它直接影响原始数据重建的程度,以及(对于本课程而言更重要的是)这些学得的特征对于其他机器学习任务的有用程度。