第01章 信号的本质------从模拟波形到AI中的数据张量
1.1 经典回顾:信号是什么?
在奥本海姆的教材中,信号是信息的数学表示------一个随时间(或空间)变化的函数。
- 连续信号:x(t), t∈Rx(t),\; t \in \mathbb{R}x(t),t∈R ------ 无限精度、无限采样率
- 离散信号:xn, n∈Zxn,\; n \in \mathbb{Z}xn,n∈Z ------ 离散时间点上的值
你学过的分类:
- 连续 vs 离散:时间轴是否连续
- 周期 vs 非周期:是否存在 T 使得 x(t) = x(t+T)
- 能量信号 vs 功率信号:总能量有限 vs 平均功率有限
- 确定信号 vs 随机信号:可预测 vs 需要概率描述
关键性质速查
| 性质 | 连续形式 | 离散形式 |
|---|---|---|
| 能量 | E=∫∣x(t)∣2 dtE = \int |x(t)|^2\, dtE=∫∣x(t)∣2dt | E=∑∣xn∣2E = \sum |xn|^2E=∑∣xn∣2 |
| 功率 | P=lim1T∫∣x(t)∣2 dtP = \lim \frac{1}{T}\int |x(t)|^2\, dtP=limT1∫∣x(t)∣2dt | P=lim1N∑∣xn∣2P = \lim \frac{1}{N}\sum |xn|^2P=limN1∑∣xn∣2 |
| 周期 | x(t)=x(t+T0)x(t) = x(t+T_0)x(t)=x(t+T0) | xn=xn+N0xn = xn+N_0xn=xn+N0 |
1.2 直觉重塑:信号就是数据
停一下,换一个视角。
当你看一个信号 x(t)=sin(2π⋅440⋅t)x(t) = \sin(2\pi\cdot 440\cdot t)x(t)=sin(2π⋅440⋅t) 时,你看到的是"一个440Hz的正弦波"。
但如果你用AI的视角看,你看到的是一个一维张量,每个元素携带一个数值,按顺序排列。
这个视角转换是整本教材的起点:
信号处理的信号 ←→ AI中的数据
─────────────────────────────────────────────
连续信号 x(t) ←→ 连续函数(Neural ODE的输入)
离散信号 x[n] ←→ 一维张量 / 向量
图像 I(x,y) ←→ 二维张量 / 矩阵
彩色图像 I(x,y,c) ←→ 三维张量 (C×H×W)
视频 V(x,y,t,c) ←→ 四维张量 (C×T×H×W)
批量数据 ←→ 五维张量 (B×C×T×H×W)
信号的"维数"对应AI的"张量阶数"
在信号处理中,你学过:
- 一维信号:音频、心电图、股票价格
- 二维信号:图像、地形图
- 三维信号:视频、CT扫描体数据
在AI中,这就是张量的阶数(rank):
python
import torch
# 一维信号 → 1D张量(向量)
audio = torch.randn(16000) # 1秒16kHz音频
# 二维信号 → 2D张量(矩阵)
image_gray = torch.randn(28, 28) # MNIST灰度图
# 三维信号 → 3D张量
image_rgb = torch.randn(3, 28, 28) # RGB图像 (C, H, W)
# 四维信号 → 4D张量
video = torch.randn(3, 100, 28, 28) # 100帧视频 (C, T, H, W)
# 五维信号 → 5D张量
batch_video = torch.randn(32, 3, 100, 28, 28) # 批量视频 (B, C, T, H, W)
1.3 深度映射:信号概念到AI概念的逐一对应
1.3.1 连续信号 → 连续函数(Neural ODE)
在经典信号处理中,连续信号 x(t) 是定义在实数轴上的函数。
在AI中,Neural ODE 正是处理连续信号的框架:
dxdt=fθ(x,t) \frac{dx}{dt} = f_\theta(x, t) dtdx=fθ(x,t)
这里 f_θ 是一个参数化的神经网络,它定义了一个连续时间动力系统。输入信号 x(t) 不再是离散的向量序列,而是一条连续的轨迹。
python
# Neural ODE 的概念性代码
from torchdiffeq import odeint
class ODEFunc(torch.nn.Module):
def __init__(self):
super().__init__()
self.net = torch.nn.Sequential(
torch.nn.Linear(2, 50),
torch.nn.Tanh(),
torch.nn.Linear(50, 2)
)
def forward(self, t, y):
# t: 连续时间标量
# y: 当前状态 (batch_size, state_dim)
return self.net(y) # 返回 dx/dt
# 从初始状态出发,沿连续时间积分
func = ODEFunc()
trajectory = odeint(func, y0, t=torch.linspace(0, 1, 100))
# trajectory[t] 就是连续信号 x(t) 的离散近似
关键洞察:经典信号处理假设信号是"给定的";Neural ODE 假设信号是"动力系统产生的"。这是从"被动描述信号"到"主动建模信号生成过程"的飞跃。
1.3.2 离散信号 → Token序列
离散信号 xn 在AI中最直接的对应是序列数据:
音频波形采样: x[0], x[1], x[2], ... → 原始音频token
文本token序列: t[0], t[1], t[2], ... → 词嵌入序列
时间序列: p[0], p[1], p[2], ... → 股票/传感器数据
但有一个关键区别:信号处理中的离散信号通常是均匀采样的,而AI中的token可能来自不同的"采样"方式。
python
# 信号处理:均匀采样
import numpy as np
t = np.linspace(0, 1, 1000) # 1000个均匀采样点
x = np.sin(2 * np.pi * 5 * t) # 5Hz正弦波
# AI文本:非均匀"采样"
# "Hello world" → [15496, 995] (GPT-2 tokenizer)
# 每个token的"时间间隔"不相等------这是和信号处理的一个关键差异
1.3.3 图像信号 → 特征图(Feature Map)
在信号处理中,图像 I(x, y) 是一个二维信号,每个点有一个强度值。
在CNN中,每一层的输出都是一个特征图------本质上也是二维信号,但每个点不再是一个标量,而是一个向量(通道维度):
信号处理: I(x, y) → 标量值的2D排列
CNN层输出: F(x, y, c) → 向量值的2D排列
python
# 一张 224×224 的RGB图像
img = torch.randn(1, 3, 224, 224) # (B, C, H, W)
# 通过ResNet的第一层卷积
conv1 = torch.nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
feat = conv1(img) # (1, 64, 112, 112)
# feat 就是一个 64通道的 112×112 的"信号"
# 它和原始图像一样是2D信号,只是每个点从3维变成了64维
关键洞察 :深度学习中的"特征提取"可以理解为信号变换------把原始信号变换到更高维的"特征空间"中,每一层都在做这件事。
1.3.4 随机信号 → 概率分布与噪声
信号处理中的随机信号用概率密度函数描述。在AI中:
- 随机信号 x(t)x(t)x(t) 的统计特性 ↔ 概率分布 p(x)p(x)p(x)
- 均值 μ=Ex\mu = Exμ=Ex ↔ 分布的期望
- 自相关 R(τ)=Ex(t)x(t+τ)R(\tau) = Ex(t)x(t+\\tau)R(τ)=Ex(t)x(t+τ) ↔ 协方差矩阵
- 白噪声 w(t)w(t)w(t) ↔ 标准正态分布 N(0,1)\mathcal{N}(0,1)N(0,1)
扩散模型(Diffusion Model)的核心就是逐步向信号添加白噪声,然后学习如何逆转这个过程:
python
# 扩散模型的前向过程:逐步添加噪声
def forward_diffusion(x0, t, T=1000):
"""
x0: 原始信号(如一张图片)
t: 当前时间步
T: 总时间步数
"""
# 噪声调度
beta = torch.linspace(1e-4, 0.02, T)
alpha = 1 - beta
alpha_bar = torch.cumprod(alpha, dim=0)
# 信号处理的视角:x(t) = √(ᾱ_t) * x(0) + √(1-ᾱ_t) * w
# 其中 w ~ N(0, I) 是白噪声
noise = torch.randn_like(x0)
x_t = torch.sqrt(alpha_bar[t]) * x0 + torch.sqrt(1 - alpha_bar[t]) * noise
return x_t, noise
# 当 t=0 时,x_t ≈ x0(几乎没有噪声)
# 当 t=T 时,x_t ≈ 纯噪声(信号被完全淹没)
# 这就是一个"信号退化"过程------经典的信噪比(SNR)递减
1.4 信号的运算 → 数据的变换
你在信号处理中学过的基本运算,在AI中都有对应:
时移 → 位置偏移
- 信号处理:x(t−t0)x(t - t_0)x(t−t0) 表示延迟 t0t_0t0
- AI对应:位置编码中的偏移
python
# 序列的循环移位
x = torch.tensor([1, 2, 3, 4, 5])
x_shifted = torch.roll(x, shifts=2) # [4, 5, 1, 2, 3]
# 这就是离散信号的时移 x[n - 2]
尺度变换 → 上下采样
- 信号处理:x(at)x(at)x(at),a>1a>1a>1 表示压缩,a<1a<1a<1 表示展宽
- AI对应:上采样 / 下采样 / 池化
python
# 下采样(时间压缩)
pool = torch.nn.MaxPool1d(kernel_size=2, stride=2)
x = torch.randn(1, 1, 100) # 100个时间步
x_down = pool(x) # 50个时间步 --- x(2t)
# 上采样(时间展宽)
up = torch.nn.Upsample(scale_factor=2, mode='linear')
x_up = up(x_down) # 100个时间步 --- 回到原始长度
反转 → 序列翻转
- 信号处理:x(−t)x(-t)x(−t) 表示时间反转
- AI对应:双向RNN、反向序列处理
python
# 序列反转
x = torch.tensor([1, 2, 3, 4, 5])
x_reversed = torch.flip(x, dims=[0]) # [5, 4, 3, 2, 1]
# x[-n] 的离散版本
叠加 → 批处理
- 信号处理:x1(t)+x2(t)x_1(t) + x_2(t)x1(t)+x2(t) 信号叠加
- AI对应:批处理中的并行计算
python
# 批处理 = 多个信号同时处理
batch = torch.randn(32, 100) # 32个信号,每个100点
# 线性层对每个信号独立做相同的变换
linear = torch.nn.Linear(100, 50)
output = linear(batch) # (32, 50) --- 32个信号同时变换
1.5 信号的表示:从基函数展开到Embedding
经典视角:信号在不同基下的展开
你在傅里叶分析中学过,任何信号都可以用一组基函数展开:
x(t)=∑kak ϕk(t) x(t) = \sum_k a_k\, \phi_k(t) x(t)=k∑akϕk(t)
其中 {ϕk}\{\phi_k\}{ϕk} 是基函数,{ak}\{a_k\}{ak} 是展开系数。
AI视角:Embedding就是信号在学习到的基下的展开
词嵌入(Word Embedding)的本质:
one-hot向量 → 嵌入向量
[0,0,1,0,0] → [0.2, -0.5, 0.8, ...]
这等价于:
e(word)=∑iai⋅ϕi e(\text{word}) = \sum_i a_i \cdot \phi_i e(word)=i∑ai⋅ϕi
其中 ϕi\phi_iϕi 是嵌入矩阵的列向量(学习到的"基函数"),aia_iai 是one-hot编码中的权重。
python
# Embedding = 学习到的基函数展开
vocab_size = 10000
embed_dim = 256
embedding = torch.nn.Embedding(vocab_size, embed_dim)
# 输入:one-hot编码(或其等价的索引)
word_idx = torch.tensor([42]) # 第42个词
# 输出:在256维"基函数"空间中的展开系数
vec = embedding(word_idx) # (1, 256)
# 嵌入矩阵的每一列是一个"基函数"
# embedding.weight[:, 42] 就是第42个词的"基函数"
# vec 就是展开系数(在这里,因为是one-hot,所以就是那一列本身)
关键洞察:
- 信号处理中,基函数是人为选定的(正弦、余弦、小波...)
- AI中,基函数是从数据中学习的(嵌入矩阵的列向量)
- 这是从"固定表示"到"可学习表示"的范式转变
1.6 信号的能量与范数
经典定义
- 连续信号能量:E=∫∣x(t)∣2 dtE = \int |x(t)|^2\, dtE=∫∣x(t)∣2dt
- 离散信号能量:E=∑∣xn∣2E = \sum |xn|^2E=∑∣xn∣2
用向量的语言:信号的能量就是向量的L2范数的平方。
AI中的对应
python
# 信号能量 = L2范数的平方
x = torch.tensor([1.0, 2.0, 3.0])
energy = torch.norm(x, p=2) ** 2 # 14.0 = 1+4+9
# 权重衰减(Weight Decay)就是在惩罚参数的"能量"
# L2正则化 = ||θ||² = 参数信号的能量
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4)
# weight_decay 就是让参数信号的"能量"不要太大
为什么这很重要?
在信号处理中,能量有限的信号叫"能量信号",它们的行为是"好的"------傅里叶变换存在,可以做频谱分析。
在AI中,权重衰减就是在约束网络参数的"能量"------防止参数变得太大,从而防止过拟合。这不是巧合,而是同一个数学原理在不同领域的体现。
1.7 信号的稀疏性 → 模型的稀疏性
经典视角
很多真实信号在某个变换域下是稀疏的:
- 音频信号在频域下稀疏(只有少数频率分量显著)
- 图像信号在小波域下稀疏(边缘信息集中在少数小波系数中)
AI视角
深度学习中的稀疏性:
- 稀疏激活:ReLU激活函数使很多神经元输出为0
- 稀疏注意力:只关注序列中的少数位置
- 稀疏模型:MoE(Mixture of Experts)只激活部分专家
python
# ReLU = 信号的稀疏化
x = torch.randn(100)
x_sparse = torch.relu(x)
sparsity = (x_sparse == 0).float().mean()
print(f"稀疏度: {sparsity:.1%}") # 通常约50%
# 稀疏注意力 = 只保留信号的主要频率分量
# 类似于信号处理中的"保留前k个傅里叶系数"
1.8 思考题:面对提问时你的回答
Q1: "信号和数据有什么区别?"
回答:信号是数据的数学抽象。数据是具体的(一张图片、一段音频),信号是通用的数学框架。AI中的数据张量就是多维离散信号------这个视角让你能用信号处理的工具(傅里叶变换、滤波、采样定理)来理解和改进AI模型。
Q2: "为什么AI要用张量而不是直接用数组?"
回答 :张量和多维数组在存储上是相同的,但张量强调的是代数结构------它支持线性变换、缩并、外积等运算。这和信号处理中"信号是函数空间中的元素"是同一个思想:信号不只是数值的集合,它是一个可以被变换、分析、分解的数学对象。
Q3: "Neural ODE和普通神经网络有什么本质区别?"
回答:普通神经网络处理离散信号------输入一个向量,输出一个向量。Neural ODE处理连续信号------它定义了一个微分方程 dx/dt = f_θ(x, t),输出是连续轨迹。就像信号处理中离散信号和连续信号的区别:一个在离散时间点上定义,一个在连续时间轴上定义。Neural ODE的优势是内存效率(可以用伴随方法反向传播)和对不规则采样数据的天然支持。
Q4: "扩散模型和信号处理有什么关系?"
回答:扩散模型的前向过程就是经典的信号退化------逐步向信号添加白噪声,信噪比单调递减。反向过程就是信号恢复------从纯噪声中重建原始信号。这和通信中的信道模型完全同构:发送端(干净信号)→ 信道(加噪)→ 接收端(含噪信号)→ 均衡器(去噪网络)。
本章总结
| 信号处理概念 | AI对应概念 | 数学本质 |
|---|---|---|
| 连续信号 x(t)x(t)x(t) | Neural ODE的连续轨迹 | R→R\mathbb{R}\to\mathbb{R}R→R 的函数 |
| 离散信号 xnxnxn | Token序列、时间序列 | Z→R\mathbb{Z}\to\mathbb{R}Z→R 的函数 |
| 图像 I(x,y)I(x,y)I(x,y) | 特征图 Feature Map | R2→R\mathbb{R}^2\to\mathbb{R}R2→R 的函数 |
| 信号能量 | L2范数、权重衰减 | ∣x∣2|x|^2∣x∣2 |
| 信号稀疏性 | ReLU、稀疏注意力 | ∣x∣0|x|_0∣x∣0 小 |
| 基函数展开 | Embedding | x=∑akϕkx = \sum a_k\phi_kx=∑akϕk |
| 随机信号 | 扩散模型中的噪声 | 概率分布 |
| 时移 | 位置编码 | x(t−t0)x(t-t_0)x(t−t0) |
| 尺度变换 | 上下采样 | x(at)x(at)x(at) |
核心心智模型:
信号处理: 信号 → 系统 → 输出
↕ ↕ ↕
AI: 数据 → 网络 → 预测
下一章预告:信号需要系统来处理。下一章我们来看"系统"这个概念如何映射到神经网络------LTI系统的线性、时不变性、因果性,在AI中分别意味着什么?