GDL - Steerable CNNs¶
GDL - 可控卷积神经网络 ¶
During the lectures, you have learnt that the symmetries of a machine learning task can be modelled with groups. In the previous tutorial, you have also studied the framework of Group-Convolutional Neural Networks (GCNNs), which describes a neural architecture design equivariant to general groups.
在讲座中,您已经了解到机器学习任务的对称性可以用群来建模。在之前的教程中,您还学习了群卷积神经网络(GCNNs)的框架,该框架描述了一种对一般群等变的神经网络架构设计。
The feature maps of a GCNN are functions over the elements of the group. A naive implementation of group-convolution requires computing and storing a response for each group element. For this reason, the GCNN framework is not particularly convenient to implement networks equivariant to groups with infinite elements.
GCNN 的特征图是群元素上的函数。群卷积的简单实现需要为每个群元素计算和存储响应。因此,GCNN 框架在实现对具有无限元素的群等变的网络时并不特别方便。
Steerable CNNs are a more general framework which solves this issue. The key idea is that, instead of storing the value of a feature map on each group element, the model stores the Fourier transform of this feature map, up to a finite number of frequencies.
可控卷积神经网络是一个更通用的框架,可以解决这个问题。其关键思想是,模型不是在每个群元素上存储特征图的值,而是存储该特征图的傅里叶变换,直到有限数量的频率。
In this tutorial, we will first introduce some Representation theory and Fourier theory (non-commutative harmonic analysis) and, then, we will explore how this idea is used in practice to implement Steerable CNNs.
在本教程中,我们将首先介绍一些表示论和傅里叶理论(非交换谐波分析),然后,我们将探讨如何在实践中使用这一思想来实现 Steerable CNNs。
Prerequisite Knowledge¶ 先决知识
Throughout this tutorial, we will assume you are already familiar with some concepts of group theory, such as groups, group actions (in particular on functions), semi-direct product and order of a group, as well as basic linear algebra.
在整个教程中,我们将假设您已经熟悉一些群论的概念,例如群、群作用(特别是在函数上的作用)、半直积和群的阶,以及基本线性代数。
We start by importing the necessary packages. You can run the following command to install all the requirements:
我们首先导入必要的软件包。您可以运行以下命令来安装所有要求:
> pip install torch torchvision numpy matplotlib git+https://github.com/AMLab-Amsterdam/lie_learn escnn scipy
[1]:
import torch
import numpy as np
import scipy
import os
np.set_printoptions(precision=3, suppress=True, linewidth=10000, threshold=100000)
import matplotlib
%matplotlib inline
import matplotlib.pyplot as plt
# If the fonts in the plots are incorrectly rendered, comment out the next two lines
from IPython.display import set_matplotlib_formats
set_matplotlib_formats('svg', 'pdf') # For export
matplotlib.rcParams['lines.linewidth'] = 2.0
import urllib.request
from urllib.error import HTTPError
CHECKPOINT_PATH = "../../saved_models/DL2/GDL"
/opt/conda/lib/python3.8/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
/tmp/ipykernel_109/1932627903.py:13: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
set_matplotlib_formats('svg', 'pdf') # For export
[2]:
# Create checkpoint path if it doesn't exist yet
os.makedirs(CHECKPOINT_PATH, exist_ok=True)
# Files to download
pretrained_files = [
"steerable_c4-pretrained.ckpt",
"steerable_so2-pretrained.ckpt",
"steerable_c4-accuracies.npy",
"steerable_so2-accuracies.npy",
]
# Github URL where saved models are stored for this tutorial
base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/DL2/GDL/"
# For each file, check whether it already exists. If not, try downloading it.
for file_name in pretrained_files:
file_path = os.path.join(CHECKPOINT_PATH, file_name)
if not os.path.isfile(file_path):
file_url = base_url + file_name
print(f"Downloading {file_url}...")
try:
urllib.request.urlretrieve(file_url, file_path)
except HTTPError as e:
print("Something went wrong. Please contact the author with the full output including the following error:\n", e)
1. Representation Theory and Harmonic Analysis of Compact Groups¶
1. 紧致群的表示理论与调和分析 ¶
We will make use of the escnn
library throughout this tutorial. You can also find its documentation here.
在整个教程中,我们将使用 escnn
库。您也可以在此处找到其文档。
[3]:
try:
from escnn.group import *
except ModuleNotFoundError: # Google Colab does not have escnn installed by default. Hence, we do it here if necessary
!pip install --quiet git+https://github.com/AMLab-Amsterdam/lie_learn escnn
from escnn.group import *
First, let’s create a group. We will use the Cyclic Group escnn
, a groups are instances of the abstract class escnn.group.Group
, which provides some useful functionalities. We instantiate groups via a factory method. To build the cyclic group of order
首先,让我们创建一个群。我们将使用循环群 escnn
中,群是抽象类 escnn.group.Group
的实例,该类提供了一些有用的功能。我们通过工厂方法实例化群。要构建阶为
[4]:
G = cyclic_group(N=8)
# We can verify that the order of this group is 8:
G.order()
[4]:
8
A group is a collection of group elements together with a binary operation to combine them. This is implemented in the class escnn.group.GroupElement
. We can access the identity element
一个群是群元素的集合,并带有一个用于组合它们的二元运算。这在类 escnn.group.GroupElement
中实现。我们可以访问单位元素
[5]:
G.identity
[5]:
0[2pi/8]
or sample a random element as
或随机抽取一个元素作为
[6]:
G.sample()
[6]:
1[2pi/8]
Group elements can be combined via the binary operator @
; we can also take the inverse of an element using ~
:
群元素可以通过二元运算符 @
组合;我们也可以使用 ~
取一个元素的逆:
[7]:
a = G.sample()
b = G.sample()
print(a)
print(b)
print(a @ b)
print(~a)
6[2pi/8]
1[2pi/8]
7[2pi/8]
2[2pi/8]
Representation theory is a fundamental element in Steerable CNNs and to construct a Fourier theory over groups. In this first section, we will introduce the essential concepts.
表示论是可转向卷积神经网络和构建群上的傅里叶理论的基本元素。在第一部分中,我们将介绍基本概念。
1.1 Group Representation¶
1.1 群体表示 ¶
A linear group representation
紧致群
In other words,
换句话说,
Example: the Trivial Representation¶
示例:平凡表示
The simplest example of group representation is the trivial representation which maps every element to
群表示的最简单例子是平凡表示,它将每个元素映射到
[8]:
rho = G.trivial_representation
rho
is an instance of escnn.group.Representation
. This class provides some functionalities to work with group representations. We can also use it as a callable function to compute the representation of a group element; this will return a squared matrix as a numpy.array
. Let verify that the trivial representation does indeed verify the condition above:
rho
是 escnn.group.Representation
的一个实例。这个类提供了一些功能来处理群表示。我们也可以将其用作可调用函数来计算群元素的表示;这将返回一个作为 numpy.array
的方阵。让我们验证平凡表示确实验证了上述条件:
[9]:
g1 = G.sample()
g2 = G.sample()
print(rho(g1) @ rho(g2))
print(rho(g1 @ g2))
[[1.]]
[[1.]]
Note that the trivial representation has size
注意,平凡表示的大小为
[10]:
rho.size
[10]:
1
Example: rotations¶ 示例:旋转
Another common example of group representations is given by 2D rotations. Let
另一个常见的群表示例是二维旋转。设
where
其中
[11]:
G = so2_group()
rho = G.standard_representation()
g1 = G.sample()
g2 = G.sample()
print(f'g1={g1}, g2={g2}, g1 * g2 = {g1 @ g2}')
print()
print('rho(g1) @ rho(g2)')
print(rho(g1) @ rho(g2))
print()
print('rho(g1 * g2)')
print(rho(g1 @ g2))
g1=4.83985258221817, g2=4.721165128388411, g1 * g2 = 3.277832403426995
rho(g1) @ rho(g2)
[[-0.991 0.136]
[-0.136 -0.991]]
rho(g1 * g2)
[[-0.991 0.136]
[-0.136 -0.991]]
QUESTION 1¶ 问题 1 ¶
Show that any representation
证明任何表示
let
be the identity element. Then, is the identity matrix of size .
令 为单位元素。那么, 是大小为 的单位矩阵。let
and be its inverse (i.e. ). Then, .
令 和 为其逆(即 )。然后, 。
ANSWER 1¶ 答案 1 ¶
First question. First, note that for any
第一个问题。首先,注意对于任何
Because
因为
Second question. Note that
第二个问题。请注意
Using the fact
利用
Direct Sum¶ 直和
We can combine representations to build a larger representation via the direct sum.
我们可以通过直和将表示结合起来构建更大的表示。
Given representations
给定表示
Its action is therefore given by the independent actions of
因此,其作用由
Let’s see an example:
让我们看一个例子:
[12]:
rho_sum = rho + rho
g = G.sample()
print(rho(g))
print()
print(rho_sum(g))
[[-0.943 -0.332]
[ 0.332 -0.943]]
[[-0.943 -0.332 0. 0. ]
[ 0.332 -0.943 0. 0. ]
[ 0. 0. -0.943 -0.332]
[ 0. 0. 0.332 -0.943]]
Note that the direct sum of two representations has size equal to the sum of their sizes:
注意,两个表示的直和的大小等于它们大小的总和:
[13]:
rho.size, rho_sum.size
[13]:
(2, 4)
We can combine arbitrary many representations in this way, e.g.
我们可以通过这种方式组合任意多的表示,例如
[14]:
rho_sum = rho + rho + rho + rho
# or, more simply:
rho_sum = directsum([rho, rho, rho, rho])
rho_sum.size
[14]:
8
The Regular Representation¶
正则表示
Another important representation is the regular representation. The regular representation describes the action of a group
另一个重要的表示是正则表示。正则表示描述了群
The set of functions over
在
The regular representation of
The new function
新函数
QUESTION 2¶ 问题 2 ¶
Show that the space of functions over
证明在
ANSWER 2¶ 答案 2 ¶
Let
设
The scalar multiplication is also defined pointwise as
标量乘法也被逐点定义为
We now verify the required properties of a vector space.
我们现在验证向量空间所需的性质。
associativity:
结合性:commutativity:
交换性:identity: define the function
;
身份:定义函数 ;inverse: define
; then
逆: 定义 ; 然后compatibility:
兼容性:identity (multiplication):
恒等(乘法):distributivity (vector):
分配率 (vector):distributivity (scalar):
分配率(标量):
For finite groups, we can generate this representation. We assume that the
对于有限群,我们可以生成这个表示。我们假设第
[15]:
G = cyclic_group(8)
rho = G.regular_representation
[16]:
# note that the size of the representation is equal to the group's order |G|
rho.size
[16]:
8
the identity element maps a function to itself, so the entries are not permuted
恒等元素将函数映射到自身,因此条目不被置换
[17]:
rho(G.identity)
[17]:
array([[ 1., 0., -0., 0., -0., -0., 0., -0.],
[ 0., 1., 0., -0., -0., -0., -0., -0.],
[-0., 0., 1., -0., -0., 0., -0., 0.],
[ 0., -0., -0., 1., 0., -0., -0., -0.],
[-0., -0., -0., 0., 1., 0., -0., -0.],
[-0., -0., 0., -0., 0., 1., -0., 0.],
[ 0., -0., -0., -0., -0., -0., 1., -0.],
[-0., -0., 0., -0., -0., 0., -0., 1.]])
The regular representation of the rotation by
通过
[18]:
rho(G.element(1))
[18]:
array([[ 0., -0., 0., -0., -0., 0., -0., 1.],
[ 1., 0., -0., -0., -0., -0., 0., -0.],
[-0., 1., 0., -0., 0., -0., -0., -0.],
[-0., 0., 1., 0., -0., 0., -0., 0.],
[-0., -0., -0., 1., 0., -0., 0., -0.],
[-0., 0., -0., 0., 1., 0., 0., -0.],
[-0., -0., -0., -0., 0., 1., 0., 0.],
[-0., 0., -0., -0., 0., -0., 1., 0.]])
Let’s see an example of the action on a function. We consider a function which is zero on all group elements apart from the identity (
让我们看看一个函数上的动作例子。我们考虑一个在除单位元 (
[19]:
f = np.zeros(8)
f[0] = 1
f
[19]:
array([1., 0., 0., 0., 0., 0., 0., 0.])
Observe that
注意
[20]:
rho(G.identity) @ f
[20]:
array([ 1., 0., -0., 0., -0., -0., 0., -0.])
[21]:
rho(G.element(1)) @ f
[21]:
array([ 0., 1., -0., -0., -0., -0., -0., -0.])
[22]:
rho(G.element(6)) @ f
[22]:
array([ 0., -0., 0., -0., -0., -0., 1., -0.])
QUESTION 3¶ 问题 3 ¶
Prove the result above.
证明上述结果。
ANSWER 3¶ 答案 3 ¶
Let’s call
我们称
which is zero everywhere apart from
除了
We now want to show that
我们现在想要证明
Equivalent Representations¶
等效表示
Two representations
如果且仅当它们通过基变换
Equivalent representations behave similarly since their composition is basis-independent as seen by
等价表示表现相似,因为它们的组成是基于独立的基础,如所见
Direct sum and change of basis matrices provide a way to combine representations to construct larger and more complex representations. In the next example, we concatenate two trivial representations and two regular representations and apply a random change of basis
直和和基变换矩阵提供了一种组合表示以构建更大和更复杂表示的方法。在下一个例子中,我们连接两个平凡表示和两个正则表示,并应用一个随机基变换
[23]:
d = G.trivial_representation.size * 2 + G.regular_representation.size * 2
Q = np.random.randn(d, d)
rho = directsum(
[G.trivial_representation, G.regular_representation, G.regular_representation, G.trivial_representation],
change_of_basis=Q
)
[24]:
rho.size
[24]:
18
Irreducible Representations (or Irreps)¶
不可约表示(或 Irreps)
Under minor conditions, any representation can be decomposed in this way, that is, any representation
在较小的条件下,任何表示都可以以这种方式分解,即任何紧群
The set of irreducible representations of a group
一个群的不可约表示集
We can access the irreps of a group via the irrep()
method. The trivial representation is always an irreducible representation. For
我们可以通过 irrep()
方法访问一个群的不可约表示。平凡表示总是一个不可约表示。对于
[25]:
rho_0 = G.irrep(0)
print(rho_0 == G.trivial_representation)
rho_0(G.sample())
True
[25]:
array([[1.]])
The next irrep
下一个不可约表示
[26]:
rho = G.irrep(1)
g = G.sample()
print(g)
print()
print(rho(g))
1[2pi/8]
[[ 0.707 -0.707]
[ 0.707 0.707]]
Irreducible representations provide the building blocks to construct any representation
不可约表示提供了构建任何表示的基本单元
where
其中
Internally, any escnn.group.Representation
is indeed implemented as a list of irreps (representing the index set id
.
在内部,任何 escnn.group.Representation
实际上都实现为不可约表示的列表(表示索引集 id
标识。
Let’s see an example. Let’s take the regular representaiton of
让我们看一个例子。让我们取
[27]:
rho = G.regular_representation
rho.irreps
[27]:
[(0,), (1,), (2,), (3,), (4,)]
[28]:
rho.change_of_basis
[28]:
array([[ 0.354, 0.5 , 0. , 0.5 , 0. , 0.5 , 0. , 0.354],
[ 0.354, 0.354, 0.354, 0. , 0.5 , -0.354, 0.354, -0.354],
[ 0.354, 0. , 0.5 , -0.5 , 0. , -0. , -0.5 , 0.354],
[ 0.354, -0.354, 0.354, -0. , -0.5 , 0.354, 0.354, -0.354],
[ 0.354, -0.5 , 0. , 0.5 , -0. , -0.5 , 0. , 0.354],
[ 0.354, -0.354, -0.354, 0. , 0.5 , 0.354, -0.354, -0.354],
[ 0.354, -0. , -0.5 , -0.5 , 0. , 0. , 0.5 , 0.354],
[ 0.354, 0.354, -0.354, -0. , -0.5 , -0.354, -0.354, -0.354]])
[29]:
# let's access second irrep
rho_id = rho.irreps[1]
rho_1 = G.irrep(*rho_id)
# we verify it is the irrep j=1 we described before
rho_1(g)
[29]:
array([[ 0.707, -0.707],
[ 0.707, 0.707]])
Finally, let’s verify that this direct sum and this change of basis indeed yield the regular representation
最后,让我们验证这个直和和这个基变换确实产生了正则表示
[30]:
# evaluate all the irreps in rho.irreps:
irreps = [
G.irrep(*irrep)(g) for irrep in rho.irreps
]
# build the direct sum
direct_sum = np.asarray(scipy.sparse.block_diag(irreps, format='csc').todense())
print('Regular representation of', g)
print(rho(g))
print()
print('Direct sum of the irreps:')
print(direct_sum)
print()
print('Apply the change of basis on the direct sum of the irreps:')
print(rho.change_of_basis @ direct_sum @ rho.change_of_basis_inv)
print()
print('Are the two representations equal?', np.allclose(rho(g), rho.change_of_basis @ direct_sum @ rho.change_of_basis_inv))
Regular representation of 1[2pi/8]
[[ 0. -0. 0. -0. -0. 0. -0. 1.]
[ 1. 0. -0. -0. -0. -0. 0. -0.]
[-0. 1. 0. -0. 0. -0. -0. -0.]
[-0. 0. 1. 0. -0. 0. -0. 0.]
[-0. -0. -0. 1. 0. -0. 0. -0.]
[-0. 0. -0. 0. 1. 0. 0. -0.]
[-0. -0. -0. -0. 0. 1. 0. 0.]
[-0. 0. -0. -0. 0. -0. 1. 0.]]
Direct sum of the irreps:
[[ 1. 0. 0. 0. 0. 0. 0. 0. ]
[ 0. 0.707 -0.707 0. 0. 0. 0. 0. ]
[ 0. 0.707 0.707 0. 0. 0. 0. 0. ]
[ 0. 0. 0. 0. -1. 0. 0. 0. ]
[ 0. 0. 0. 1. 0. 0. 0. 0. ]
[ 0. 0. 0. 0. 0. -0.707 -0.707 0. ]
[ 0. 0. 0. 0. 0. 0.707 -0.707 0. ]
[ 0. 0. 0. 0. 0. 0. 0. -1. ]]
Apply the change of basis on the direct sum of the irreps:
[[ 0. -0. 0. -0. -0. 0. -0. 1.]
[ 1. 0. -0. 0. -0. -0. 0. -0.]
[-0. 1. 0. -0. 0. -0. -0. -0.]
[-0. 0. 1. 0. -0. 0. -0. 0.]
[-0. -0. -0. 1. 0. -0. 0. -0.]
[-0. 0. -0. 0. 1. 0. 0. -0.]
[-0. -0. -0. -0. 0. 1. 0. 0.]
[-0. 0. -0. -0. 0. -0. 1. 0.]]
Are the two representations equal? True
1.2 Fourier Transform¶ 1.2 傅里叶变换 ¶
We can finally approach the harmonic analysis of functions over a group
我们终于可以研究群
Note that a representation
注意,表示
This result gives us a way to parameterize functions over the group. This is the focus of this section. In particular, this is useful to parameterize functions over groups with infinite elements.
这个结果为我们提供了一种对群上的函数进行参数化的方法。这是本节的重点。特别是,这对于对具有无限元素的群上的函数进行参数化非常有用。
In this section, we will first consider the dihedral group
在本节中,我们将首先考虑二面体群
[31]:
G = dihedral_group(8)
G.order()
[31]:
16
[32]:
# element representing the reflection (-) and no rotations
G.reflection
[32]:
(-, 0[2pi/8])
[33]:
# element representing a rotation by pi/2 (i.e. 2 * 2pi/8) and no reflections (+)
G.element((0, 2))
[33]:
(+, 2[2pi/8])
[34]:
# reflection followed by a rotation by pi/2
print(G.element((0, 2)) @ G.reflection)
# we can also directly generate this element as
print(G.element((1, 2)))
(-, 2[2pi/8])
(-, 2[2pi/8])
[35]:
# a rotation by pi/2 followed by a reflection is equivalent to a reclection followed by a rotation by 6*2pi/8
G.reflection @ G.element((0, 2))
[35]:
(-, 6[2pi/8])
The list of all elements in the group is obtaied as:
组中所有元素的列表如下所示:
[36]:
G.elements
[36]:
[(+, 0[2pi/8]),
(+, 1[2pi/8]),
(+, 2[2pi/8]),
(+, 3[2pi/8]),
(+, 4[2pi/8]),
(+, 5[2pi/8]),
(+, 6[2pi/8]),
(+, 7[2pi/8]),
(-, 0[2pi/8]),
(-, 1[2pi/8]),
(-, 2[2pi/8]),
(-, 3[2pi/8]),
(-, 4[2pi/8]),
(-, 5[2pi/8]),
(-, 6[2pi/8]),
(-, 7[2pi/8])]
Fourier and Inverse Fourier Transform¶
傅里叶和逆傅里叶变换 ¶
For most groups, the entries of the irreps don’t only span the space of functions but form also a basis (i.e. these functions are mutually orthogonal to each other). Therefore, we can write a function
对于大多数群,不可约表示的条目不仅跨越函数空间,而且还形成一个基(即这些函数彼此正交)。因此,我们可以将函数
where
其中
We rewrite this expression in a cleaner form by using the following fact. If
我们通过使用以下事实将此表达式重写为更简洁的形式。如果
By definining
通过将
Similarly, we can project a general function
同样,我们可以通过以下方式将一般函数
The projection over all entries of
将
which we refer to as Fourier Transform.
我们称之为傅里叶变换。
If the group
如果群
For a finite group Group.irreps()
method. Let’s see an example:
对于有限群 Group.irreps()
方法来获取其所有不可约表示。让我们来看一个例子:
[37]:
irreps = G.irreps()
print(f'The dihedral group D8 has {len(irreps)} irreps')
The dihedral group D8 has 7 irreps
[38]:
# the first one, is the 1-dimensional trivial representation
print(irreps[0] == G.trivial_representation == G.irrep(0, 0))
True
QUESTION 4¶ 问题 4 ¶
We can now implement the Fourier Transform and the Inverse Fourier Transform for the Dihedral Group
我们现在可以为二面体群
[39]:
def fourier_transform_D8(f: np.array):
# the method gets in input a function on the elements of D_8
# and should return a dictionary mapping each irrep's `id` to the corresponding Fourier Transform
# The i-th element of `f` stores the value of the function on the group element `G.elements[i]`
G = dihedral_group(8)
assert f.shape == (16,), f.shape
ft = {}
########################
# INSERT YOUR CODE HERE:
for rho in G.irreps():
d = rho.size
rho_g = np.stack([rho(g) for g in G.elements], axis=0)
ft[rho.id] = (f.reshape(-1, 1, 1) * rho_g).mean(0) * np.sqrt(d)
########################
return ft
[40]:
def inverse_fourier_transform_D8(ft: dict):
# the method gets in input a dictionary mapping each irrep's `id` to the corresponding Fourier Transform
# and should return the function `f` on the elements of D_8
# The i-th element of `f` stores the value of the function on the group element `G.elements[i]`
G = dihedral_group(8)
f = np.zeros(16)
########################
# INSERT YOUR CODE HERE:
for rho in G.irreps():
d = rho.size
for i, g in enumerate(G.elements):
f[i] += np.sqrt(d) * (ft[rho.id] * rho(g)).sum()
########################
return f
We now want to verify that the Fourier Transform and the Inverse Fourier Transform are inverse of each other:
我们现在想要验证傅里叶变换和逆傅里叶变换是彼此的逆:
[41]:
f = np.random.randn(16)
ft = fourier_transform_D8(f)
new_f = inverse_fourier_transform_D8(ft)
assert np.allclose(f, new_f)
Parameterizing functions over infinite groups¶
对无限群进行函数参数化
This allows us to also parameterize functions over infinite groups, such as
这也使我们能够对无限群的函数进行参数化,例如
[42]:
G = o2_group()
[43]:
# the group has infinite many elements, so the `order` method just returns -1
G.order()
[43]:
-1
The equations remain the same, but this group has an infinite number of irreps. We can, however, parameterize a function over the group by only considering a finite number of irreps in the sum inside the definition of Inverse Fourier Transform. Let
方程保持不变,但该群有无限多个不可约表示。然而,我们可以通过在逆傅里叶变换的定义中仅考虑有限个不可约表示来对群上的函数进行参数化。设
Inverse Fourier Transform:
逆傅里叶变换:
and Fourier Transform: 和傅里叶变换: