查看全课大纲(第 76 / 82 节)
  1. 1.函数与数据泛化
  2. 2.向量:从几何直觉到高维空间
  3. 3.矩阵与张量
  4. 4.特征值与特征向量:矩阵变换的不动方向
  5. 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
  6. 6.概率与统计:机器学习的数学基础
  7. 7.独立性与随机变量:贝叶斯公式与二项分布
  8. 8.期望、方差与协方差:随机变量的数字画像
  9. 9.联合概率:当一条轴不够用的时候
  10. 10.概率分布(一):从伯努利到伽马函数
  11. 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
  12. 12.机器学习的模型:找输入与输出的关系
  13. 13.机器学习的应用
  14. 14.过拟合
  15. 15.机器学习的分类
  16. 16.有监督学习:从房价预测说起
  17. 17.损失函数
  18. 18.梯度下降(上):更新规则与学习速率
  19. 19.梯度下降(2)
  20. 20.牛顿方法
  21. 21.KNN简介
  22. 22.距离测算
  23. 23.朴素贝叶斯
  24. 24.朴素贝叶斯的实际应用
  25. 25.信息熵:决策树构建的度量基础
  26. 26.信息增益与决策树的构建
  27. 27.ID3算法
  28. 28.决策树节点的划分
  29. 29.CART树
  30. 30.Bagging
  31. 31.Boosting:自适应提升算法
  32. 32.Gradient Boosting
  33. 33.线性模型:回归与分类的同一副骨架
  34. 34.最小二乘:把最优参数直接解出来
  35. 35.最小二乘法的几何与概率视角
  36. 36.极大似然:给最小二乘一个概率出身
  37. 37.逻辑回归(1):把线性模型接到分类上
  38. 38.逻辑回归(2):损失函数与多分类
  39. 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
  40. 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
  41. 41.优化方法(1):坐标下降与前向选择
  42. 42.优化方法(2):前向策略与最小角回归
  43. 43.拉格朗日乘子法(1):从无约束到等式约束
  44. 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
  45. 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
  46. 46.最大间隔:把分类问题变成优化问题
  47. 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
  48. 48.支持向量:边界上的少数派
  49. 49.SVM 计算过程:一笔到底算完,再走进核方法
  50. 50.贝叶斯公式:概率即信念
  51. 51.隐含变量和概率模型:给看不见的原因建模
  52. 52.EM 算法:期望与最大化的迭代
  53. 53.琴生不等式:EM 的数学钥匙
  54. 54.三个硬币问题:EM 的复盘与衔接
  55. 55.高斯混合模型:用隐变量解释双峰数据
  56. 56.隐马尔可夫模型
  57. 57.隐马尔可夫模型(2):前向算法与动态混合模型
  58. 58.隐马尔可夫模型在 NLP 中的运用
  59. 59.EM 算法:把「为什么收敛」推到底
  60. 60.HMM 在 NLP 中的运用:从分词到机器翻译
  61. 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
  62. 62.高维下的高斯模型
  63. 63.主题模型:从词袋到隐含主题
  64. 64.Dirichlet 分布(1)
  65. 65.Dirichlet 分布(2)
  66. 66.统计学习内容回顾
  67. 67.K-means 算法
  68. 68.PCA 降维
  69. 69.SVD 降维
  70. 70.从聚类到流形
  71. 71.局部线性嵌入
  72. 72.随机邻域嵌入:SNE 与 t-SNE
  73. 73.无参模型:让数据自己说话
  74. 74.高斯过程:函数空间里的正态分布
  75. 75.高斯过程(2):噪声、正则化与核的线性组合
  76. 76.神经网络简介:从感知器到自编码器
  77. 77.反向传播:把误差一层层传回去
  78. 78.卷积神经网络(CNN):让特征自己学出来
  79. 79.循环神经网络(RNN):给网络装上记忆
  80. 80.深度学习回顾:静态一类,动态一类
  81. 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
  82. 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传

神经网络简介:从感知器到自编码器

约 15 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 机器学习算法精讲

前面十八章里我们学的模型——线性模型、逻辑回归、决策树、SVM、各种概率模型——都属于「传统」机器学习的地盘。从这一章开始,进入今天最重要的主角:神经网络。本节先做三件事:第一,把人工神经网络和生物大脑的关系说清楚;第二,看明白单个神经元其实就是我们学过的逻辑回归,而前馈网络不过是一堆逻辑回归排成层、用矩阵运算批量跑;第三,认识一个特殊的结构——自编码器,它既能压缩、也能加密。最后我们停在损失函数的门口:误差怎么一层层「传回来」,正是下一节反向传播要回答的问题。

💡 核心导读

  • 正名:我们现在做的神经网络,准确称呼是人工神经网络(Artificial Neural Networks)——它只是「类似于」大脑的数学模型,并不是真正的大脑;但它已从单独的计算领域,成长为机器学习里最重要的一环。
  • 神经元 = 逻辑回归:输入向量乘权重求和、加偏置、过 sigmoid——一个神经元做的计算,正是第八章学过的逻辑回归。
  • 前馈网络:一层层「线性组合 + 非线性激活」堆叠——zl=Wlal−1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l,al=σ(zl)\mathbf{a}^l = \sigma(\mathbf{z}^l);整体一定是非线性函数,核心构成却是线性的计算方式。
  • 自编码器:输入输出都是 NN 维向量——把六维压到三维再重构,是压缩;输入 student、输出 35、72、89、47、47,是加密。
  • 下一节钩子:构造损失函数时一般希望它是「连续」的——这样里边的误差才能不断地传递回来。

从感知器说起:借「大脑」之名

这一章讲 neural networks(神经网络)。讲师开场先摆出两个长得像的英文词:perception 与 perceptron。前者是「我们自己的认知」,后者是最早做出来的一个神经网络的工具或模型,中文叫感知器——一个词是人的认知,一个是照着它造出来的机器模型,差得其实很远。

最早的憧憬很自然:我们的大脑由很多神经构成,人们希望通过数学模型解释基本神经运作的规律,了解甚至「再造」人的神经思维和方式,通过它了解整个智能背后的奥秘。最早做神经网络模型的时候,人们确实充满了这种憧憬。

但后来逐渐发现:这套模型,也许和我们的神经相去甚远——不过这并不影响我们用它去做其他不同方向的研究。所以第一件要强调的事是:我们现在做的神经网络,应该叫 artificial neural networks,即人工神经网络,只不过大家一般都简称「神经网络」。两者之间一定要明确区分:这种模型只是类似于大脑,并不是真正大脑的情况。

还有一段定位史值得记住。神经网络最开始可以认为是单独的一种不同的计算,有人管它叫一个领域;但渐渐地,它已经默认归到今天所说的机器学习领域里,成为机器学习里面最重要的一环——而它引申出的各种 Deep Learning(深度学习)方法,也正是今天研究的重点(第二十章我们再回头看深度学习的版图)。

神经元:一个逻辑回归单元

从最基本的第一件事开始:一个神经元。跟以前所有的 machine learning 问题一样,给定一个输入,是一个 nn 维的向量,x∈Rnx \in \mathbb{R}^n。神经元内部先做线性组合:每个输入 xjx_j 乘上对应的权重 wjw_j,全部加起来,再加上一个偏置项 bb。

这个偏置怎么表述?别忘了我们正常的线性函数都是 wx+bw x + b 的形式(第八章线性模型,8.1 节)。处理办法是老技巧:放一个永远等于 1 的输入 x0x_0,让它连到这一层各神经元的权重就是 b1,b2,…b_1, b_2, \ldots——「这个我们已经在最开始讲过了」(4.1 节引入的 x0≡1x_0 \equiv 1,4.4 节房价例里它就是 ax+bax+b 里的 bb)。

线性组合之后,再经过一个 sigmoid 函数,出来就得到最后的结果。sigmoid 我们是老朋友了(1.1 节第一次提过它;8.5 节逻辑回归的正片用的就是它):

f(z)=11+e−zf(z) = \frac{1}{1 + e^{-z}}

它把定义域从负无穷到正无穷,压缩到 0 与 1 之间。

现在问大家一个问题,讲师当年也是这么问课堂的:这个简单的神经元模型,是否似曾相识?——正是我们曾经学过的逻辑回归(8.5 节):用这种方式进行计算,映射到最终的一个结果。也就是说,一个神经元,就是一个逻辑回归。

前馈网络:把逻辑回归排成层

单个神经元会算了,多个的情况怎么办?想象一个现在最传统的前馈神经网络:第 l−1l-1 层里有 Nl−1N_{l-1} 个节点,第 ll 层有 NlN_l 个节点,每一个节点之间都有一个权重,记作 wijlw^l_{ij}。

这个下标要专门讲一下,讲师特意停下来强调「大家要区分一下」:wijlw^l_{ij} 表示的是第 l−1l-1 层里边的第 jj 个节点,到第 ll 层的第 ii 个节点的权重——方向是从 jj 到 ii、从上一层到下一层,指的就是连接它们的「这条线」。权重本身构成一个大矩阵,所以所有的线性组合都可以通过矩阵相乘的方式来实现。

单个节点:z 是激活之前的结果

对于第 ll 层的第 ii 个节点,它收到的线性组合是:

zil=∑j=1Nl−1wijlajl−1+bilz^l_i = \sum_{j=1}^{N_{l-1}} w^l_{ij} a^{l-1}_j + b^l_i

其中 ajl−1a^{l-1}_j 是上一层第 jj 个节点的输出。注意这个 zz 的身份:它是通过这个节点的 activation function(激活函数)之前的结果——上一层所有的输出信息,乘上 WW、加上偏置,汇到这个节点上来,先攒成 zz,还没有过激活函数。

整层向量化:矩阵形式

一个节点会算了,一层呢?第 ll 层一共有 NlN_l 个节点,每个节点都有自己的线性组合。把「每个 ww 乘上输入」的复杂公式变成一个矩阵的相乘,就得到整层的计算:

zl=Wlal−1+bl,al=σ(zl)\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l, \qquad \mathbf{a}^l = \sigma(\mathbf{z}^l)

σ\sigma 逐元素地作用在 zl\mathbf{z}^l 的每个分量上。这样,把这一层所有节点的输出 al\mathbf{a}^l 算出来,它又变成下一层的输入——不断地进行前馈的传递和传播,就构成了一个从前到后的复杂神经网络。

讲师把这整件事用最简单的描述串了一遍:我们要做的,就是 wx+bw x + b 这个已知的线性组合,乘上一个 ff(非线性激活函数),变成下一层——稍稍区分一下,这里是 W1\mathbf{W}^1 和 b1\mathbf{b}^1;这一层的输出当成下一层的输入(讲师说「第二层的输出可以当为第三层的权重」时当场改口——是当输入),再乘上 W2\mathbf{W}^2、加上 b2\mathbf{b}^2、再过一个 ff,变成第二层的输出;再往下做一步就是乘 W3\mathbf{W}^3、加 b3\mathbf{b}^3、前面再加一个输出——三层,最后是 yy:

y=σ(W3 σ(W2 σ(W1x+b1)+b2)+b3)\mathbf{y} = \sigma\left(\mathbf{W}^3\, \sigma\left(\mathbf{W}^2\, \sigma\left(\mathbf{W}^1 \mathbf{x} + \mathbf{b}^1\right) + \mathbf{b}^2\right) + \mathbf{b}^3\right)

给这件事定个性:整个神经网络所做的,是先进行线性组合,加一个非线性激活函数(一般是 sigmoid);把输出的结果再做线性组合,再经过一个 sigmoid;再把得到的结果做线性组合,再加一个 sigmoid。所以,第一,它一定是一个非线性函数,因为它经过了多层的激活函数;但它核心构成,是一个线性的计算方式。看似复杂,实际上大家可以想想——就是这么一个简单的过程。

换个角度:一堆逻辑回归的集合

学到这里再回头看:一个多层的、有多个节点的神经网络,它做的事情,就是把每一个节点——每一个节点不就是一个 logistic regression 嘛——把多个逻辑回归做一个集合,同时满足某些需求和条件,这就是一个前馈的神经网络。课件里有一页把这个一句话钉死了:

A neural network = running several logistic regressions at the same time. The LRs are then feed into another logistic regression function. (一个神经网络 = 同时跑好几个逻辑回归;这些逻辑回归的输出,再喂进另一个逻辑回归函数。)

一个诚实的旁白:它真的在解释大脑吗

讲师在这里停下来讲了一段很实在的话。学到这一步,如果你还在纠结「神经网络是不是在试图解释大脑的工作原理」,你会发现:按这个理解,也许我们大脑就像很多个线性拟合一样——听起来也是怪怪的。也许我们到今天,一直都没有搞清楚大脑的神经到底是怎么工作的;我们猜想它感觉起来要比这复杂一些。就算真是同样的道理,我们也没有特别强烈的、从生理学拿到的证据,说大脑的神经是类似于这样工作的。

但回过头来,无论怎么样,我们做这件事,是希望用这种数学模型去解决生活中遇到的很多问题——就类似于其他的 machine learning,一样的道理。

代码:亲手跑一遍前向传播

公式看完了,跑一遍。下面是一个 3 输入、2 隐藏、1 输出的小网络(讲义设定的小例子——讲师板书讲的是结构与流程,数字是讲义配的),把每一层的 zz 和 aa 都打印出来:

import math

def sigmoid(z):
    """Sigmoid 激活函数: f(z) = 1 / (1 + e^{-z})"""
    return 1.0 / (1.0 + math.exp(-z))

# 一个 3 输入 -> 2 隐藏 -> 1 输出 的小网络(讲义设定的小例子)
# 第一层权重 W1 (2x3): 第 j 个输入到第 i 个隐藏神经元
W1 = [[0.4, -0.2, 0.6],
      [0.1, 0.5, -0.3]]
b1 = [0.1, -0.2]

# 第二层权重 W2 (1x2): 第 j 个隐藏神经元到输出神经元
W2 = [[0.7, -0.4]]
b2 = [0.2]

x = [1.0, 0.5, -0.5]

# --- 第 1 层到第 2 层: 线性组合 z = W a + b ---
z1 = []
for i in range(2):
    z1.append(W1[i][0] * x[0] + W1[i][1] * x[1] + W1[i][2] * x[2] + b1[i])
a1 = [sigmoid(z) for z in z1]  # 经过激活函数, 变成这层的输出

# --- 第 2 层到第 3 层(输出层) ---
z2 = [W2[0][0] * a1[0] + W2[0][1] * a1[1] + b2[0]]
y = sigmoid(z2[0])

print("输入 x =", x)
print(f"隐藏层 线性组合 z1 = [{z1[0]:.4f}, {z1[1]:.4f}]   <- 激活函数之前的结果")
print(f"隐藏层 输出     a1 = [{a1[0]:.4f}, {a1[1]:.4f}]   <- sigmoid 之后, 变成下一层的输入")
print(f"输出层 线性组合 z2 = [{z2[0]:.4f}]")
print(f"网络最终输出    y  = {y:.4f}")
输入 x = [1.0, 0.5, -0.5]
隐藏层 线性组合 z1 = [0.1000, 0.3000]   <- 激活函数之前的结果
隐藏层 输出     a1 = [0.5250, 0.5744]   <- sigmoid 之后, 变成下一层的输入
输出层 线性组合 z2 = [0.3377]
网络最终输出    y  = 0.5836

逐行对着读。z1z_1 那一行完全可以直接手算验证:第一个隐藏神经元收到的是 0.4×1.0+(−0.2)×0.5+0.6×(−0.5)+0.1=0.10.4 \times 1.0 + (-0.2) \times 0.5 + 0.6 \times (-0.5) + 0.1 = 0.1,第二个是 0.1+0.25+0.15−0.2=0.30.1 + 0.25 + 0.15 - 0.2 = 0.3——这就是「上一层所有信息乘上 WW 再加偏置,汇到节点上」的原始形态。a1a_1 是 sigmoid 之后的输出:0.10.1 过了 sigmoid 变 0.52500.5250,0.30.3 变 0.57440.5744——都落进了 0 与 1 之间。然后这两个数变成下一层的输入:输出神经元拿它们做线性组合得 z2=0.3377z_2 = 0.3377,再过一次 sigmoid,得到网络最终输出 y=0.5836y = 0.5836。整个过程没有一步是黑盒:三层函数嵌套,每一层都是「乘权重、加偏置、过 sigmoid」。特别看输出神经元干的事——拿上一层的两个数做线性组合、过 sigmoid 出一个 0 到 1 的数——一个不折不扣的逻辑回归(8.5 节)。

自编码器:输入什么,就输出什么

到这里,神经网络的基本盘已经有了。讲师接着介绍一个比较重要的神经网络:Autoencoder,自编码器——课件里给它的定义是「a neural network used to learn efficient coding」,一个用来学习高效编码的神经网络。

先看以前的神经网络输出什么。以前的神经网络是一个向量输入,经过很多层、通过不同的隐藏计算,最后得到一个输出——这个输出 yy 往往属于一个数,y∈Ry \in \mathbb{R}。讲师说到这里还当场纠正了自己:「是一个常——不要长,这是一个——Sorry,是一个实数;就是一个向量,或者只是一个数而已。」

但实际上,神经网络的功能远比这强大:我们可以构造出「输入一个向量,输出另一个向量」的网络,里边的权重、参数的调整,完全可以通过给定的一对一对的训练数据来达到。

压缩:六维进、三维转、六维出

看右边这个自编码器的例子(讲师板书结构):它的输入是六维的,从 x1x_1 到 x6x_6(还要加上一个偏置,「所以这个永远是一,大家可以不用纠结它」);它的输出也是六维的。我们要做的事情是:输入 1、2、3、4、5、6,输出的结果也应该是 1、2、3、4、5、6——也就是说,输入什么向量,训练数据里它的输出就应该是 exactly、完全相同的这个向量。

训练了半天,这些权重在干什么?前一段权重(我们管它叫 WW)通过「线性组合加非线性激活函数」的映射,把六维空间的向量映射到一个三维空间——隐含节点是三个。回过头来,这三个隐含节点里的数值要重构:类似地也是「线性加激活函数」的方式,把原先的六维数据,通过这个三维重新构造出来。

大家想,这件事在做什么?不就是在做压缩嘛——把一个六维空间的数据压缩到一个三维空间,同时还要有一种方法,把六维空间的数通过三维空间的组合重新重构出来。

加密:student 进、数字出

这还是一个特别好的加密的方式。刚才说输入 123456、输出也是 123456,两个向量完全相同的时候,这个叫压缩;那假设输入的是 123456,输出来的是 10011003 这样的不同的数,或者输出的是 a、b、c、d、e、f——那我们做的就是把这些数字加密成字母。或者简单说,把字母输入 student,出来的是 35、72、89、47、47 等等——这种对应关系,它们中间的关系就是一种加密。所以自编码器还可以做很多类似于这样不同的事情。

它与以前学过的机器学习有一个非常不同的地方,值得单独点出来:它的关键好处是只需要给定 XX、YY 一对一对的数据,但这个 XX 属于 NN 维,同时 YY 也属于 NN 维——以前我们多半是 Y∈RY \in \mathbb{R},不说 Y∈RNY \in \mathbb{R}^N。输入输出之间存在某种变换关系,我们就可以通过权重调整来得到它。

代码:六维压三维,再重构

把上面那个「6 进 3 转 6 出」的结构跑起来。下面代码里编码用 WW(输入到隐层)、解码用 UU(隐层到输出)两组参数——正是讲师点名的两组权重。我们让三个隐含节点分别只看一对输入的「对比」(讲义设定):第一个只看 (x1,x2)(x_1, x_2)、第二个只看 (x3,x4)(x_3, x_4)、第三个只看 (x5,x6)(x_5, x_6):

import math

def sigmoid(z):
    return 1.0 / (1.0 + math.exp(-z))

# 自编码器: 6 维输入 -> 3 个隐含节点 -> 6 维输出(讲义设定的小例子)
# W: 输入到隐层的权重 (3x6);  U: 隐层到输出的权重 (6x3)
W = [[ 2, -2,  0,  0,  0,  0],
     [ 0,  0,  2, -2,  0,  0],
     [ 0,  0,  0,  0,  2, -2]]
b = [-0.5, -0.5, -0.5]

U = [[ 2, 0, 0], [-2, 0, 0],
     [ 0, 2, 0], [ 0, -2, 0],
     [ 0, 0, 2], [ 0, 0, -2]]
c = [-0.9, -0.9, -0.9, -0.9, -0.9, -0.9]

def autoencoder(x):
    """前向传播: 编码 6 -> 3, 解码 3 -> 6"""
    h = [sigmoid(sum(W[i][j] * x[j] for j in range(6)) + b[i]) for i in range(3)]
    x_hat = [sigmoid(sum(U[k][i] * h[i] for i in range(3)) + c[k]) for k in range(6)]
    return h, x_hat

for label, x in [("x_A", [0.9, 0.1, 0.8, 0.2, 0.7, 0.3]),
                 ("x_B", [0.2, 0.8, 0.3, 0.7, 0.1, 0.9])]:
    h, x_hat = autoencoder(x)
    mse = sum((xi - xh) ** 2 for xi, xh in zip(x, x_hat)) / 6
    print(f"输入 {label} = {x}")
    print(f"  三维隐含表示 h     = [{h[0]:.4f}, {h[1]:.4f}, {h[2]:.4f}]")
    print(f"  重构输出 x_hat = [{', '.join(f'{v:.4f}' for v in x_hat)}]")
    print(f"  重构均方误差 MSE   = {mse:.4f}")
输入 x_A = [0.9, 0.1, 0.8, 0.2, 0.7, 0.3]
  三维隐含表示 h     = [0.7503, 0.6682, 0.5744]
  重构输出 x_hat = [0.6458, 0.0831, 0.6074, 0.0965, 0.5619, 0.1142]
  重构均方误差 MSE   = 0.0277
输入 x_B = [0.2, 0.8, 0.3, 0.7, 0.1, 0.9]
  三维隐含表示 h     = [0.1545, 0.2142, 0.1091]
  重构输出 x_hat = [0.3564, 0.2299, 0.3842, 0.2094, 0.3359, 0.2463]
  重构均方误差 MSE   = 0.1800

先读 hh 那两行。xAx_A 的三对输入都是「前大后小」(0.9 大过 0.1、0.8 大过 0.2、0.7 大过 0.3),三个隐含节点全部偏高(0.7503、0.6682、0.5744);xBx_B 每一对都反过来,hh 全部掉到低位(0.1545、0.2142、0.1091)。六维压到三维,留下的是什么,由权重 WW 决定——我们设定它保留「成对对比」,它就只记对比。这也顺带说明了为什么权重需要「学」:想让隐含表示保留数据里真正重要的东西,随手拍脑袋是不行的。

再看重构:x_hat 并不等于原输入——xAx_A 的均方误差 0.0277,xBx_B 的 0.1800。原因很直白:这组 WW 和 UU 是讲义随手设定的,没有经过任何训练。怎么系统地调整这两组参数,让重构误差降下来?这就需要把「误差」变成一个可以优化的目标——损失函数,并且让误差能够「传回来」指导每一层权重的调整。这正是下一节反向传播的正片。

损失函数与「误差传回来」

无论是普通的前馈神经网络(输出一个数或一个向量),还是自编码器,总之我们需要做的事情都是:通过调整里边的参数,来达到最好的结果。

如果输出最后是一个数,我们可以构造出 yiy_i 减去网络的输出——讲师原话:「或者不叫 HWH_W 了,因为这是个神经网络了,我们就管它叫 HNH_N 吧」——把两者的差别做平方、做加和,这不就是我们构造的一个损失函数嘛(4.2 节的老三样):

J=∑i=1N(yi−HN(xi))2J = \sum_{i=1}^{N} \left( y_i - H_N(x_i) \right)^2

而对于 autoencoder,假设输入不同的数的时候,可以把输出归一化之后求 cross entropy(交叉熵)——8.5 节逻辑回归用过的那一款——或者去定义「重构的两者之间的距离到底怎么计算」。有了这个距离,就不断地调整里边的 WW(输入到隐层)和 UU(隐层到输出)这两组参数,使它们之间的损失函数最小。

最后是讲师留给下一节的钩子,也是本节真正的收尾:构造损失函数的时候,一般希望大家构造一个连续的损失函数——这样它后面传递的时候,里边的误差才能不断地传递回来。误差怎么一层层传回来?下一节:反向传播(Back Propagation)。

📝 动手练一练

  1. 压缩还是加密:同样是「6 维输入 → 3 维隐含 → 6 维输出」的自编码器—— (a) 当训练数据是「输入 (1,2,3,4,5,6)(1,2,3,4,5,6)、输出 (1,2,3,4,5,6)(1,2,3,4,5,6)」这样的自身配对时,它学到的能力叫什么? (b) 当训练数据是「输入 student、输出 35、72、89、47、47」这样的配对时呢? (c) 它跟这门课以前学的任务相比,在数据形态上最本质的不同是什么?
👉 点击查看参考答案

(a) 压缩——六维数据经由三维隐含层中转,再重构回六维;(b) 加密——学的是两组 NN 维向量之间的对应(映射)关系,中间的关系就是「密文」;(c) 自编码器的 X∈RNX \in \mathbb{R}^N 且 Y∈RNY \in \mathbb{R}^N,输入输出同维;以前的任务多半是 Y∈RY \in \mathbb{R},输出只是一个数。

  1. 单神经元手算:输入 x=(2,1,1)x = (2, 1, 1),权重 w=(0.5,−1,3)w = (0.5, -1, 3),偏置 b=−3b = -3。 (a) 手算线性组合 zz 与输出 a=σ(z)a = \sigma(z)(提示:zz 会算出一个很好看的数); (b) 把 bb 从 −3-3 改成 00 再算一次,输出变成多少? (c) 说说偏置 bb 在神经元里扮演的角色——它和 4.4 节线性模型里的哪个老朋友是同一个身份?
👉 点击查看参考答案

(a) z=0.5×2+(−1)×1+3×1+(−3)=1−1+3−3=0z = 0.5 \times 2 + (-1) \times 1 + 3 \times 1 + (-3) = 1 - 1 + 3 - 3 = 0,所以 a=σ(0)=0.5a = \sigma(0) = 0.5——正好落在 sigmoid 的正中点上。 (b) z=3z = 3,a=σ(3)=1/(1+e−3)≈0.9526a = \sigma(3) = 1/(1 + e^{-3}) \approx 0.9526,输出被大幅推向 1。 (c) 偏置是不看输入、由节点自己决定的「底子」:它把整个线性组合整体平移——正是 4.4 节 x0≡1x_0 \equiv 1 所承担的 θ0\theta_0(ax+bax + b 里的截距 bb)在神经元里的同一个身份。bb 从 −3-3 抬到 00、zz 整体抬高 3,输出就从 0.5 跳到 0.95——这就是「底子」的力量。

本章小结

本节三步走。第一步,正名:从最早的感知器(perceptron)讲起——人工神经网络受生物大脑启发而生,但与真实神经机制「相去甚远」;它只是类似于大脑的数学模型,却已从单独的计算领域成长为机器学习里最重要的一环,其引申的深度学习是今天的研究重点。第二步,结构:单个神经元 = 逻辑回归(线性组合 + 偏置 + sigmoid,8.5 节);前馈网络把逻辑回归排成层,整层向量化为 zl=Wlal−1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l、al=σ(zl)\mathbf{a}^l = \sigma(\mathbf{z}^l),一层层前馈传递——整体一定是非线性函数(多层激活),核心构成却是线性的计算方式;一句话:一堆逻辑回归同时跑,输出再喂给下一个逻辑回归。第三步,自编码器:输入输出都是 NN 维——六维压到三维再重构是压缩;输入 student 输出 35、72、89、47、47 是加密;这与以前 Y∈RY \in \mathbb{R} 的任务划出了分界线。收在门口:损失函数(平方和 / 交叉熵)要构造得连续——这样误差才能不断传递回来。怎么传?下一节反向传播见。

📋 行动清单

  • 口述「神经元 = 逻辑回归」:不看笔记,向同学讲清单个神经元的计算与 8.5 节逻辑回归的一一对应关系。
  • 维度检查:对于 zl=Wlal−1+bl\mathbf{z}^l = \mathbf{W}^l \mathbf{a}^{l-1} + \mathbf{b}^l,若第 l−1l-1 层有 5 个节点、第 ll 层有 3 个节点,Wl\mathbf{W}^l 是几行几列的矩阵?(3 行 5 列——行数是本层节点数,列数是上一层节点数。)
  • 跑改代码:把自编码器代码块的输入换成你自己的六维向量(每个分量取 0 到 1 之间),观察 hh 与重构输出的变化方向。
  • 带着问题进下一节:为什么损失函数要「连续」?「误差不断传递回来」到底怎么传?下一节《反向传播》揭晓。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第19章课件:神经网络(BP/CNN/RNN)
    下载