深度学习中的傅里叶位置编码

深度学习中的傅里叶位置编码

  • 介绍
      • [1. 核心矛盾:神经网络的"低通滤波器"特性](#1. 核心矛盾:神经网络的“低通滤波器”特性)
      • [2. 数学表达](#2. 数学表达)
      • [3. 两种主流的应用视角](#3. 两种主流的应用视角)
        • [A. 在 Transformer 中的"相对位置感知"](#A. 在 Transformer 中的“相对位置感知”)
        • [B. 在坐标感知任务(如 NeRF/3D 视觉)中的"特征增强"](#B. 在坐标感知任务(如 NeRF/3D 视觉)中的“特征增强”)
      • [4. 为什么它有效?(直观理解)](#4. 为什么它有效?(直观理解))
      • [5. 进阶形式:RFF (Random Fourier Features)](#5. 进阶形式:RFF (Random Fourier Features))
      • 代码实现示例
      • 总结

介绍

在深度学习中,傅里叶位置编码(Fourier Positional Encoding) 是一项将连续坐标(或离散索引)映射到高维特征空间的技术。无论是 Transformer 处理序列,还是 NeRF 处理 3D 坐标,其核心逻辑基本一致。


1. 核心矛盾:神经网络的"低通滤波器"特性

深度学习模型(尤其是多层感知机 MLP)存在一种现象,被称为 光谱偏差(Spectral Bias)

  • 问题: 神经网络倾向于优先学习数据中的低频分量 (平滑的部分),而很难捕捉高频分量(精细的纹理、急剧变化的边界)。
  • 表现: 如果直接将原始坐标 ( x , y , z ) (x, y, z) (x,y,z) 输入网络,生成的图像或模型往往非常模糊,丢失了大量细节。

傅里叶位置编码的作用就是预先将输入"拉伸"到高频空间,强迫模型感知细微的变化。


2. 数学表达

最常见的傅里叶编码形式(如在 NeRF 或 Transformer 中使用的)是将一个标量值 v v v 映射为一个向量:

γ ( v ) = sin ⁡ ( 2 0 π v ) , cos ⁡ ( 2 0 π v ) , ... , sin ⁡ ( 2 L − 1 π v ) , cos ⁡ ( 2 L − 1 π v ) \gamma(v) = \\sin(2\^0 \\pi v), \\cos(2\^0 \\pi v), \\dots, \\sin(2\^{L-1} \\pi v), \\cos(2\^{L-1} \\pi v) γ(v)=sin(20πv),cos(20πv),...,sin(2L−1πv),cos(2L−1πv)

  • 多尺度频率: 通过指数级增加频率( 2 0 , 2 1 , ... 2^0, 2^1, \dots 20,21,...),编码涵盖了从全局(低频)到局部细节(高频)的所有特征。
  • 周期性: 使用 sin ⁡ \sin sin 和 cos ⁡ \cos cos 保证了编码在空间上的连续性。

3. 两种主流的应用视角

A. 在 Transformer 中的"相对位置感知"

在 NLP 中,模型本质上是无序的(词袋模型)。傅里叶编码(Sinusoidal Encoding)提供了位置信息:

  • 距离度量: 由于三角函数的特性,任意两个位置 p o s pos pos 和 p o s + k pos+k pos+k 之间的编码关系可以通过一个线性变换来表示。这意味着模型可以更容易地学到词与词之间的相对距离
B. 在坐标感知任务(如 NeRF/3D 视觉)中的"特征增强"

在处理几何数模或 3D 场景时:

  • 高维投影: 将 3 维坐标投影到(例如)256 维的空间。
  • 类似于算术: 就像我们用二进制表示数字一样,低位(高频)变化极快,代表微小的位移;高位(低频)变化缓慢,代表宏观的位置。这让 MLP 能够像"查表"一样精准地定位空间中的每一个点。

4. 为什么它有效?(直观理解)

想象你要在地图上定位一个点:

  1. 如果只给你经纬度(原始坐标),数值变化很小,模型很难区分 100.001 100.001 100.001 和 100.002 100.002 100.002 的区别。
  2. 傅里叶编码相当于给地图加了无数层不同粗细的网格
  • 第一层网格很大(低频)。
  • 最后一层网格细如发丝(高频)。
  • 模型通过查看点在所有网格中的相对位置,就能瞬间锁定极高精度的坐标。

5. 进阶形式:RFF (Random Fourier Features)

在最新的研究中,研究者发现不一定要用固定的 2 n 2^n 2n 频率,可以使用随机采样的高斯频率:

ϕ ( x ) = cos ⁡ ( 2 π B x ) , sin ⁡ ( 2 π B x ) T \phi(\mathbf{x}) = \\cos(2\\pi \\mathbf{B}\\mathbf{x}), \\sin(2\\pi \\mathbf{B}\\mathbf{x})^T ϕ(x)=cos(2πBx),sin(2πBx)T

其中 B \mathbf{B} B 是从高斯分布中随机生成的矩阵。这种方法(常见于 Fourier Feature Networks )可以根据任务需求,通过调整高斯分布的标准差( σ \sigma σ)来控制模型捕捉细节的能力:

  • σ \sigma σ 越大: 捕捉的细节越细,但如果过大可能导致混叠噪声。
  • σ \sigma σ 越小: 结果越平滑。

代码实现示例

常规实现
python 复制代码
class FourierPositionalEncoding(nn.Module):
    """Fourier feature encoding for spatial coordinates.
    Maps scalar values to high-dimensional space using sinusoidal functions,
    helping the network capture fine spatial details even with large coordinate values.
    """

    def __init__(self, input_dim: int, num_frequencies: int = 10, log_scale: bool = True):
        super().__init__()
        self.input_dim = input_dim
        self.num_frequencies = num_frequencies
        self.output_dim = input_dim * 2 * num_frequencies
        if log_scale:
            freq_bands = 2.0 ** torch.linspace(0, num_frequencies - 1, num_frequencies)
        else:
            freq_bands = torch.linspace(1.0, 2.0 ** (num_frequencies - 1), num_frequencies)
        # shape: (num_frequencies,)
        self.register_buffer("freq_bands", freq_bands)

    def forward(self, x: Tensor) -> Tensor:
        """
        Args:
            x: (..., input_dim)
        Returns:
            (..., output_dim)
        """
        # x_proj shape: (..., input_dim, num_frequencies)
        x_proj = x.unsqueeze(-1) * self.freq_bands * math.pi
        # sin/cos shape: (..., input_dim * num_frequencies)
        sin_feat = torch.sin(x_proj).flatten(-2)
        cos_feat = torch.cos(x_proj).flatten(-2)
        return torch.cat([sin_feat, cos_feat], dim=-1)
RFF
python 复制代码
import math

import torch
from torch import nn


class RandomFourierPositionEncoding(nn.Module):
    """
    随机傅里叶特征对 3D 坐标编码。
    坐标先归一化到 [-1,1](基于场景包围盒),再映射到高维。
    这样对"坐标值很大"的场景保持稳定。
    """

    def __init__(self, d_out: int, d_pos: int = 3, n_freq: int = 64, sigma: float = 1.0):
        super().__init__()
        # 固定随机频率矩阵,不参与梯度
        B = torch.randn(d_pos, n_freq) * sigma  # (d_pos, n_freq)
        self.register_buffer("B", B)
        self.proj = nn.Linear(n_freq * 2, d_out)  # sin+cos → d_out

    def forward(self, xyz: torch.Tensor) -> torch.Tensor:
        """
        xyz: (..., d_pos)  已归一化坐标
        return: (..., d_out)
        """
        x = 2 * math.pi * xyz @ self.B  # (..., n_freq)
        x = torch.cat([x.sin(), x.cos()], dim=-1)  # (..., 2*n_freq)
        return self.proj(x)

总结

傅里叶位置编码不仅仅是给输入加点"佐料",它是改变了模型观察数据的分辨率。它将简单的数值转化为一组丰富的频率信号,让神经网络这个"近视眼"戴上了一副高倍率的显微镜。

相关推荐
半兽先生9 小时前
05阶段:NLP自然语言处理基础
人工智能·自然语言处理
盈飞无限9 小时前
SPC选型:智能VS传统,谁更懂中国制造?
人工智能·制造
li-xun9 小时前
LINUX DO 社区注册机制调整与公益 AI 服务动态
linux·运维·人工智能
云烟成雨TD9 小时前
Spring AI 1.x 系列【50】可观测性:接入 Prometheus + Grafana
人工智能·spring·prometheus
不当菜鸡的程序媛9 小时前
Policy model
深度学习
梦梦代码精10 小时前
为什么这个开源的AI平台会火?有点东西。。。
人工智能·算法·机器学习·docker·开源
大模型真好玩10 小时前
智能体从入门到精通:6个必学GitHub开源项目
人工智能·agent·deepseek
源图客10 小时前
Aitoearn:OPC(一人公司)的AI内容智能体
人工智能·dreamweaver
chlorine510 小时前
【神经网络】——卷积层、池化层、线性层
深度学习·神经网络·cnn