信号处理中的复变函数求导:用Wirtinger导数搞定实值复变函数的梯度下降
信号处理中的复变函数求导:用Wirtinger导数搞定实值复变函数的梯度下降
在信号复原和优化问题中,工程师们经常需要处理复信号。比如设计滤波器时,我们可能需要对复值参数进行优化;或者在相位恢复问题中,需要最小化某个实值损失函数。传统复变函数的导数定义过于严格,导致大多数实值复变函数(如信号的能量、模平方等)都无法求导。这正是Wirtinger导数大显身手的地方——它打破了传统限制,让我们能像处理实变量函数一样对复变量进行求导和优化。
1. 为什么传统复变函数求导在工程中不够用
当我们处理FFT变换后的信号时,即使原始信号是实值的,变换结果也往往是复数。考虑一个简单的例子:设计一个数字滤波器,其频率响应$H(e^{j\omega})$是复值的。如果要优化这个滤波器,使其在某些频段的响应满足要求,就需要定义一个实值的评价函数(比如实际响应与理想响应的均方误差),然后对这个函数关于复值系数求导。
传统复变函数的可导性(即解析性)要求非常苛刻——必须满足柯西-黎曼条件:
$$ \frac{\partial u}{\partial x} = \frac{\partial v}{\partial y}, \quad \frac{\partial u}{\partial y} = -\frac{\partial v}{\partial x} $$
其中$f(z) = u(x,y) + jv(x,y)$。这意味着:
- 绝大多数实值复变函数(如模平方$|z|^2$)都不满足这个条件
- 无法直接用于梯度下降等优化算法
工程中常见的实值复变函数包括:
- 信号能量:$f(z) = |z|^2 = z\overline{z}$
- 评价函数:$f(z) = |g(z)|^2$,其中$g(z)$是某个复值变换
- 损失函数:如复信号重构误差的模
2. Wirtinger导数的核心思想与计算规则
Wirtinger导数提供了一种实用的解决方案,它将复数变量$z$和其共轭$\overline{z}$视为独立变量。定义Wirtinger导数为:
$$ \frac{\partial}{\partial z} = \frac{1}{2}\left(\frac{\partial}{\partial x} - j\frac{\partial}{\partial y}\right), \quad \frac{\partial}{\partial \overline{z}} = \frac{1}{2}\left(\frac{\partial}{\partial x} + j\frac{\partial}{\partial y}\right) $$
这种定义带来了几个关键优势:
- 对实值函数求导更简单:实值函数关于$\overline{z}$的导数就是$z$导数的共轭
- 符合工程直觉:可以像处理实变量一样处理复数变量
- 兼容传统导数:对解析函数,$\frac{\partial f}{\partial \overline{z}} = 0$
常用Wirtinger导数公式:
| 函数形式 | $\frac{\partial f}{\partial z}$ | $\frac{\partial f}{\partial \overline{z}}$ |
|---|---|---|
| $z$ | 1 | 0 |
| $\overline{z}$ | 0 | 1 |
| $ | z | ^2 = z\overline{z}$ |
| $ | g(z) | ^2$ |
提示:在实际计算时,可以先把函数表示为$z$和$\overline{z}$的组合,然后分别对两者求偏导,就像处理普通的多元函数一样。
3. 梯度下降法中的Wirtinger导数应用
在信号复原问题中,梯度下降法的更新步骤需要计算实值目标函数关于复参数的梯度。使用Wirtinger导数,最速下降方向为:
$$ \Delta z = -\mu \frac{\partial F}{\partial \overline{z}} $$
其中$\mu$是学习率,$F$是实值目标函数。
MATLAB实现示例:
% 信号复原问题的梯度下降 function z_est = signal_recovery(y, A, max_iter, step_size) % y: 观测信号, A: 测量矩阵, z_est: 估计信号 z_est = randn(size(A,2),1) + 1j*randn(size(A,2),1); % 随机初始化 for iter = 1:max_iter residual = y - A*z_est; grad = -A'*residual; % Wirtinger导数 z_est = z_est - step_size * grad; if mod(iter,100)==0 fprintf('Iter %d, error=%.4f\n', iter, norm(residual)); end end endPython实现示例:
import numpy as np def gradient_descent(A, y, lr=0.01, max_iter=1000): m, n = A.shape z = np.random.randn(n) + 1j*np.random.randn(n) # 复数随机初始化 for i in range(max_iter): error = y - A @ z grad = -A.conj().T @ error # Wirtinger导数 z -= lr * grad if i % 100 == 0: print(f"Iter {i}, error={np.linalg.norm(error):.4f}") return z4. 工程实践中的常见问题与解决方案
4.1 复数梯度下降的学习率选择
复数变量的梯度下降比实数情况更复杂,因为梯度方向不仅影响幅度还影响相位。实践中发现:
- 初始学习率通常取实数情况的1/2到1/5
- 可以使用自适应学习率方法(如Adam的复数版本)
推荐的学习率调整策略:
- 开始时使用较大学习率快速下降
- 接近收敛时减小学习率精细调整
- 监控目标函数值,如果出现震荡就降低学习率
4.2 相位恢复问题的特殊处理
在相位恢复(如从强度测量重建信号)问题中,目标函数通常形如:
$$ F(z) = \sum_i (|a_i^H z|^2 - y_i)^2 $$
其Wirtinger导数为:
$$ \frac{\partial F}{\partial \overline{z}} = 2 \sum_i (|a_i^H z|^2 - y_i)(a_i a_i^H z) $$
这种情况下,梯度计算可以优化为:
def phase_retrieval_grad(A, z, y): Az = A @ z magnitude = np.abs(Az)**2 error = magnitude - y grad = 2 * (A.conj().T @ (error * Az)) return grad4.3 避免局部极小值的技巧
复数优化问题往往有更多局部极小值,可以采用以下策略:
- 多次随机初始化:从不同的初始点开始运行算法
- 动量项:在梯度更新中加入历史梯度信息
- 模拟退火:偶尔接受"不好"的更新以跳出局部极小
复数梯度下降带动量的实现:
function [z_est, errors] = grad_descent_momentum(y, A, max_iter, step_size, beta) z_est = randn(size(A,2),1) + 1j*randn(size(A,2),1); velocity = zeros(size(z_est)); errors = zeros(max_iter,1); for iter = 1:max_iter residual = y - A*z_est; grad = -A'*residual; velocity = beta*velocity + (1-beta)*grad; z_est = z_est - step_size * velocity; errors(iter) = norm(residual); end end5. 进阶应用:Wirtinger导数在深度学习中的应用
现代信号处理越来越多地使用神经网络,而复数神经网络也需要处理实值损失函数对复参数的求导问题。Wirtinger导数为这种情况提供了理论基础。
复数神经网络的梯度计算要点:
- 将复数权重分解为实部和虚部:$w = w_r + jw_i$
- 计算损失函数对$w$和$\overline{w}$的Wirtinger导数
- 更新规则:
$$ \Delta w = -\mu \frac{\partial L}{\partial \overline{w}} $$
PyTorch实现示例:
import torch class ComplexLinear(torch.nn.Module): def __init__(self, in_features, out_features): super().__init__() self.weight = torch.nn.Parameter( torch.randn(out_features, in_features, dtype=torch.complex64)) def forward(self, x): return torch.matmul(x, self.weight.t()) # 使用Wirtinger导数的优化 model = ComplexLinear(10, 2) optimizer = torch.optim.Adam(model.parameters(), lr=0.001) for epoch in range(100): optimizer.zero_grad() output = model(input_data) loss = torch.sum(torch.abs(output - target)**2) # 复数MSE损失 loss.backward() optimizer.step()在实际项目中,我发现复数神经网络的训练稳定性比实数网络更敏感,通常需要更小的学习率和更多的正则化。一个实用的技巧是在初期冻结虚部参数,先训练实部,等损失开始下降后再解冻所有参数。
