先从基础概念说起。很多人一听到“深度学习”,首先想到的是高深莫测、难以理解,但拆解来看,它其实是机器学习的一个分支,核心思路是从一层又一层的特征表示中自动学习。不要小看这个“层次”结构,现代深度学习模型动辄几十层甚至上百层,而这些层次都是通过数据自动训练得到的。相比之下,传统机器学习有时也被称为“浅层学习”——深度与浅层的本质区别,就在这里。
那么这些分层表示是如何获得的呢?依靠的是神经网络。你可以把深度神经网络想象成一道多级信息蒸馏工序:信息穿过一层又一层的过滤器,每一步都在提炼,纯度越来越高,最终得到的结果也越来越精准。
从技术角度讲,深度学习就是一种多层的数据表示学习方法。

先聊聊机器学习
传统上,计算机是如何工作的?程序员编写规则(也就是程序),计算机按照规则将输入转化为输出。这被称为符号主义人工智能,擅长处理定义明确的逻辑问题,比如早期的五子棋。然而,一旦遇到图像分类、语音识别、自然语言翻译这类模糊、复杂的问题,规则就难以编写了。
机器学习的思路恰好相反:机器读取输入数据和对应的答案,自行找出其中的规律。换句话说,机器学习系统是“训练”出来的,而不是一行一行代码写出来的。举个例子,如果你想给度假照片自动打标签,那就把一大堆已经标注好的照片输入机器学习系统,系统会自行学习照片与标签之间的统计规律。
定义很简单:机器学习就是在预定义的可能性空间内,借助反馈信号的指引,从输入数据中寻找有用的特征表示和规则。

深度学习到底是如何工作的?
步骤其实非常清晰:
a. 首先给神经网络的权重(也称为参数)随机赋值
b. 经过一串随机变换,得到一个预测值Y'
c. 通过损失函数(也称为目标函数或代价函数),计算出预测值Y'与真实值Y之间的差距,即损失值
d. 将这个损失值作为反馈信号,使用优化器对权重值进行微调,目标是降低当前样本的损失
e. 重复步骤b到d足够多次,直到得到损失值最小的神经网络——这就是一个训练好的模型

神经网络的数学基础
聊完概念,需要接触一下数学。别紧张,本质上就是几个核心概念。
神经网络的数据表示
目前所有机器学习系统都使用张量(tensor)作为基本数据结构。这个术语很重要,TensorFlow的名字就来源于此。简单来说,张量就是一个数据容器,里面通常存放数字,所以也叫数字容器。如果你熟悉矩阵(2阶张量),那就容易理解了——张量是矩阵向任意维度的推广。张量的维度通常称为“轴”。
张量由三个关键属性定义:
•轴:轴的个数
•形状:张量沿每个轴的维度大小(元素个数)
•数据类型(dtype):float16、float32、float64、uint8、string等等
标量(0阶张量)
只有一个数字的张量叫做标量,也称为0阶张量或0维张量。例如NumPy中的np.array(3),它的轴数为0,形状是()。
向量(1阶张量)
由数字组成的数组称为向量,也叫1阶张量。例如np.array([4, 1, 5]),轴数为1,形状是(3,)。注意,这个向量有3个元素,所以称为3维向量。但3维向量与3维张量不是一回事——3维向量只有一个轴,沿这个轴有3个维度。
矩阵(2阶张量)
由向量组成的数组称为矩阵,也叫2阶张量。矩阵有两个轴:行和列。实际中的向量数据,例如形状为(samples, features)的2阶张量,每个样本都是一个数值向量——这也是向量数据库存储的基本单位。
3阶张量与更高阶的张量
将多个矩阵打包成一个新数组,就得到了3阶张量。再往上,将多个3阶张量打包,就是4阶张量。实际中的例子很常见:时间序列数据是形状为(samples, timesteps, features)的3阶张量;图像数据是(samples, height, width, channels)的4阶张量;视频数据则是(samples, frames, height, width, channels)的5阶张量。

神经网络的“齿轮”:张量运算
所有计算机程序最终都能简化为对二进制输入的一些二进制运算。类似地,深度神经网络学到的所有变换,也都能简化为对数值数据张量的各种运算。
逐元素运算
逐元素运算,就是分别应用到张量的每个元素上。参与运算的张量形状必须相同。例如加法、减法、乘法、除法,以及常用的relu激活函数——np.maximum(z, 0.),大于0时输出等于输入,小于等于0时输出为0。
张量积(点积)
这是最常见也最有用的张量运算之一,千万不要与逐元素乘积混淆。NumPy中使用np.dot(x, y)实现。数学上通常用点表示:z = x·y。两个向量的点积是一个标量,而且只有元素个数相同的向量才能进行。一个矩阵与一个向量的点积,返回的是一个向量,元素是向量与矩阵每一行的点积。矩阵之间的点积,要求x.shape[1] == y.shape[0],结果形状是(x.shape[0], y.shape[1])。
张量变形
张量变形就是重新排列张量的行和列,变成想要的形状。变形后元素个数不变。一种特殊的变形是转置——将行和列互换。
张量运算的几何解释
平移、旋转、缩放、倾斜,这些基本几何操作都能表示为张量运算。与任意矩阵做点积,就是一次线性变换。线性变换加上平移,就是仿射变换。这里有一个关键点:如果使用relu激活函数,多次仿射变换就不再等价于一次仿射变换了——否则,一个没有激活函数的多层神经网络,本质上就是一个线性模型。
深度学习的几何解释
神经网络完全由一系列张量运算组成,而这些运算不过是输入数据的简单几何变换。因此,你可以把神经网络看作高维空间中非常复杂的几何变换,通过一系列简单步骤来实现。机器学习的目标,就是为高维空间中复杂、高度折叠的数据流形(一个连续的表面)找到简洁的表示。深度学习擅长将复杂的几何变换逐步分解成一系列基本变换。
神经网络的“引擎”:基于梯度的优化
回到之前的步骤,a只是输入/输出的代码,b和c是应用张量运算。真正的难点在于d:更新模型权重。对于某个权重系数,你怎么知道应该增大还是减小?变化多少?一个笨办法是保持其他权重不变,只尝试这一个系数,尝试不同取值,然后对所有系数重复。但系数往往成千上万,甚至上百万,效率太低。更好的方法:梯度下降。
导数
假设有一个光滑连续的函数f(x) = y。x的微小变化只会引起y的微小变化。在某个点p附近,如果x变化足够小,可以将f近似看作斜率为a的线性函数。这个斜率a就是f在p点的导数。a < 0说明x微增会使f(x)减小;a > 0则会使f(x)增大。
梯度
导数可以应用于任何连续光滑的函数。张量运算的导数称为梯度。对于标量函数,导数是函数曲线的局部斜率;对于张量函数,梯度表示多维表面的曲率。好比物体位置对时间的梯度是速度,二阶梯度是加速度。
随机梯度下降
步骤d中需要更新权重。假设函数是可微的,可以计算出梯度,然后沿着梯度的反方向更新权重,每次损失都会减小一点。具体过程:抽取一批训练样本x和对应目标y_true;在x上运行模型得到预测值y_pred(前向传播);计算损失值;计算损失对模型参数的梯度(反向传播);将参数沿梯度的反方向移动一小步,减小损失。这种方法称为小批量随机梯度下降(SGD)。“随机”是因为每批数据都是随机抽取的。如果每次迭代都在所有数据上运行,称为批量梯度下降,但计算成本太高。折中方案是选择一个合理的小批量大小。
神经网络的每个权重系数都是空间中的一个自由维度。你可以想象二维损失表面上的梯度下降,但不要指望真正可视化神经网络的训练——毕竟你无法用人类能理解的方式去可视化一百万维空间。在低维表示中建立的直觉,在实践中不一定总是准确的。
链式求导:反向传播
前面假设函数可微,所以容易计算梯度。但实际中如何计算复杂表达式的梯度?依靠反向传播算法。核心是链式法则:grad(y, x) == grad(y, x1) * grad(x1, x)。知道f和g的导数,就能求出fg的导数。加上更多中间函数,就像一条链。将链式法则应用到神经网络梯度计算上,就是反向传播。现代框架如TensorFlow,支持基于计算图的自动微分——你只需写出前向传播,无需额外工作。GradientTape就是一个API,让你利用TensorFlow的自动微分能力,它是一个Python作用域,能以计算图(tape)的形式记录其中运行的张量运算。
实践:用Keras识别手写数字
讲了这么多理论,不如来点实际的。经典问题:将手写数字灰度图像(28×28像素)分到10个类别(0到9)。数据集是MNIST,机器学习界的“Hello World”。
加载数据
from tensorflow.keras.datasets import mnist 一行代码搞定。train_images和train_labels是训练集,模型从这里学习;test_images和test_labels是测试集,用于验证。训练集包含60000张图片,测试集包含10000张,每张图片28×28像素。
构建网络
模型很简单:keras.Sequential([layers.Dense(512, activation="relu"), layers.Dense(10, activation="softmax")])。神经网络的核心组件是层(layer),大多数深度学习工作就是将简单的层连接起来,实现渐进式的数据蒸馏。本例有两个Dense层,每层对输入数据执行简单的张量运算(relu和softmax),这些运算涉及权重张量——也就是层的参数,其中保存了模型学到的知识。
编译
model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["accuracy"])。指定优化器、损失函数和监控指标。sparse_categorical_crossentropy是损失函数,提供反馈信号;rmsprop优化器通过SGD降低损失。
准备数据
先将图像数据变形:train_images.reshape((60000, 28*28)),然后归一化到[0,1]区间:astype("float32") / 255。测试集同样处理。
拟合模型
model.fit(train_images, train_labels, epochs=5, batch_size=128)。模型开始迭代,每批128个样本,共5轮。每批数据上,模型计算损失对权重的梯度,然后将权重向减小损失的方向移动。5轮后训练精度达到98.9%。
预测
取测试集前10张图片:test_digits = test_images[0:10],然后model.predict(test_digits)。返回的数组里每个值是对应数字属于0-9的概率。例如,第7个概率最大,那么这张图片很可能是7。检查测试标签,果然一致。
评估
model.evaluate(test_images, test_labels)得到测试精度约97.8%,比训练精度98.9%低一些。这个差距就是过拟合——模型在训练数据上学得太好,对新数据的泛化能力打了折扣。
