常用算法速查
NMS(非极值大抑制)
NMS(非极大值抑制)是目标检测领域的一个重要后处理步骤。
核心目标:在模型预测出大量的候选边界框的后,NMS负责筛选掉哪些重叠度很高的切置信度较低的框,最终为每个目标只保留一个最佳的边界框。
核心原理:NMS的算法思想非常直观,可以概括为一个贪心算法。
- 准备工作:
- 获取所有预测框的列表
B,每个框包含其坐标(x1, y1, x2, y2) - 获取每个框的置信度列表
S - 定义一个
IoU阈值,例如0.5。IoU表示两个框面积的交(Intersection)并(Union)比。
- 获取所有预测框的列表
- 算法流程:
- 排序:根据置信度分数
S对B进行排序。使得分数最高的框排在前面。 - 选择与抑制:
- 从排序后的列表
B中选取第一个框M,并将其作为最终的检测结果之一。 - 遍历列表中剩余的所有框,计算它们与
M的IoU,如果大于IoU阈值,则认为该框跟M是同一个物体。由于M分数更高,我们应该抑制(丢掉)这个框。
- 从排序后的列表
- 迭代:
- 从列表中删除
M和被抑制的框。 - 重复步骤2,直到列表为空。
- 从列表中删除
- 排序:根据置信度分数
- 最终结果:最终结果就是2.2中筛选出的
M集合。
有偏/无偏样本方差
为什么有两种方差呢? 这是因为我们通常无法得到全部数据(总体Population),只能得到其中的一小部分样本(Sample)。我们的目标是使用这一小部分样本,来估计全体数据的特征。
总体方差(Popularity Variance,$\sigma^2$)
定义:当你拥有所有可能的数据点时,你可以计算出真实的方差。 公式:$\sigma^{2}=\frac{1}{N}\sum_{i=1}^{N}(x_{i}-\mu)^{2}$
- $\mu$是总体的真实均值。 具体含义:每个数据点与真实均值差的平方的期望。
样本方差(Sample Variance)
现实中,我们基本不可能拿到总体数据。我们只能随机抽取一批样本,比如随机找100个18岁男性来测量身高。现在,我们的目标是用这100个人的数据来估计全国所有人的身高方差。
这时我们就面临一个选择,这直接导致了"有偏"和"无偏"的区别。
A. 有偏样本方差(Biased Sample Variance,$S_{n}^{2}$)
这是最直观的估计方法。我们就像计算总体方差,只不过用的是样本数据。 公式:$S_{n}^{2}=\frac{1}{n}\sum\limits_{i=1}^{n}(x_{i}-\bar{x})^2$
- $n$:样本中数据点数量。
- $\bar{x}$:样本的平均值,注意这里并不是$\mu$。 为什么是有偏的?这里“偏”指的是系统性偏差。如果我们反复从总体中抽取样本,并由这个$\frac{1}{n}$的公式去计算方差,我们得到的这些方差的平均值,会系统性的小于总体的真实方差$\sigma^{2}$,因为每组样本天然的距离其中心$\bar{x}$的距离要小于真实的均值$\mu$,所以$\frac{1}{n}$作为除数算出来的方差实际上是比真实方差要小的。
B.无偏样本方差(Unbiased Sample Variance,$S_{n-1}^{2}$)
公式:$S_{n-1}^{2} =\frac{1}{n-1}\sum\limits_{i=1}^{n}(x_{i}-\bar{x})^{2}$
- 直观理解:这里以自由度的角度来理解,样本方差表示的是数据的离散程度,当你计算出样本均值$\bar{x}$之后,实际上你的$n$的数据点就不再是完全的"自由"的了。你只要知道其中的$n-1$个数据点和样本均值$\bar{x}$,最后的一个数据点就唯一确定了(因为所有数据点减去均值的和必须为0)。所以在计算离散程度的时候,实际上只有$n-1$个独立的信息。
- 贝塞尔校正(Bessel’s Correction):通过公式证明方差的无偏估计的方法。
数学期望
数学期望也被称为期望值、均值,描述了一个随机变量在大量重复实验中,其结果的平均值。
范数
范数的核心作用是:衡量一个数学对象(比如向量、矩阵、函数)的“大小”或“长度”。 数学定义:一个函数$\lVert\cdot\rVert$,要成为范数,必须对任何向量$x$和$y$,以及任何标量$c$,满足以下三点:
- 非负性
- $\lVert x \rVert \ge 0$:任何东西的大小都不能是负数。
- $\lVert x \rVert = 0$:当且仅当$x$是零向量(即所有元素都为0)。什么都没有的时候才是0。
- 齐次性
- $\lVert c \times x \rVert = \vert c \vert \times \lVert x \rVert$:如果把一个向量放大$c$倍,那么相应的他的长度也应该放大$\vert c \vert$倍。
- 三角不等式:
- $\lVert x + y \rVert \le \lVert x \rVert + \lVert y \rVert$:从远点直接到$x+y$的距离,一定比先到$x$再到$x+y$的距离短。
常见范数
假如我们有一个向量:$x=[x_{1}, x_{2},...,x_{n}]$。
L2范数
也叫“欧几里得距离”。 公式:$\lVert x \rVert_{2} = \sqrt{\sum\limits_{i=1}^{n}x_{i}^{2}}$ 几何意义:空间中两点间的直线距离。 单位圆:所有L2范数为1的向量组成图形是一个标准的圆形。
L1范数
也叫“曼哈顿距离”或“出租车范数”。 公式:$\Vert x \Vert_{1} = \sum\limits_{i=1}^{n}\vert x_{i} \vert$ 几何意义:向量各个分量绝对值之和,代表在城市街区中移动的距离。 单位圆:所有L1范数为1的向量组成的图形是一个菱形。
L$\infty$范数
也叫“最大范数”或“切比雪夫范数”。 公式:$\Vert x \Vert_{\infty} = \max(\vert x_{1} \vert, \vert x_{2} \vert, ..., \vert x_{n} \vert)$ 几何意义:向量各个分量绝对值最大值。 单位圆:所有L$\infty$范数为1的向量组成的图形是一个正方形。
总结
选择哪种范数取决于你要解决的问题。不同的范数有不同的特性,可以带来不同的效果。
- 机器学习中的正则化
- 问题:训练模型时,我们希望模型的参数(权重)不要过大,防止模型过于复杂而产生“过拟合”。
- 解决方法:在损失函数中加入一个惩罚项,这个惩罚项就是模型参数向量的范数。
- L2 正则化 (Ridge Regression):使用L2范数作为惩罚。它会倾向于让所有参数都比较小,但不会变为0。这使得模型权重分布更平滑。
- L1 正则化 (LASSO Regression):使用L1范数作为惩罚。它有一个非常重要的特性:会倾向于让许多参数变为精确的0。这相当于自动进行了“特征选择”,把不重要的特征剔除掉,从而得到一个更“稀疏”(sparse)的模型。
- 衡量误差
- 在预测任务中,我们需要一个指标来衡量“预测值”和“真实值”之间的差距。这个差距可以用它们差向量的范数来表示。
- 均方根误差 (RMSE):本质上是误差向量的 L2范数。它对较大的误差给予更高的权重。
- 平均绝对误差 (MAE):本质上是误差向量的 L1范数。它对所有误差一视同仁,对异常值不那么敏感。
- 图像处理
- 比较两张图片的相似度,可以把图片的像素值看作一个高维向量,然后计算两个向量之间的距离(范数)。
- L∞范数可以用来衡量两张图片之间“最差”的差异,即差异最大的那个像素点的差异值。
Lipschitz Continuity(利普希茨连续性)
这就是 SpectralNorm 的理论基础。
一个函数$f$如果是K-Lipschitz连续的,那么对于其定义域内的任意两个点$x_{1}$和$x_{2}$,都满足:
- 直观解释:这个不等式意味着函数的“陡峭程度”有限。输入一个微小变化,不会引起输出的巨大变化。$K$就是这个陡峭程度的上限,称为利普希茨常数。
- 与梯度的关系:如果一个函数是
1-Lipschitz连续($K=1$),那么它的梯度范数(norm)在任何地方都不会超过1。这意味着梯度不会爆炸。 - 输入空间(Domain)的范数$\Vert x_{1} - x_{2} \Vert$和输出空间(Codomain)的范数$\lVert f(x_{1}) - f(x_{2}) \rVert$,可以是任何范数,甚至可以不同,只是K可能会变化。
- 这是因为:在有限维向量空间(如 $R^{n}$)中,所有范数都是等价的 (Equivalent)。
- 核心解释:对于任意两种范数$\Vert \cdot \Vert_{a}$和$\Vert \cdot \Vert_{b}$,一定存在两个正常数$c_{1}$和$c_{2}$,使得对于空间中任何非零向量$v$,都满足:$c_{1} \times \Vert v \Vert_{a} \le \Vert v \Vert_{b} \le c_{2} \times \Vert v \Vert_{a}$。
- 这为什么重要? 这意味着,如果一个函数对于某一种范数是利普希茨连续的,那么它对于任何其他范数也都是利普希茨连续的,只是利普希茨常数$K$可能会改变。这让我们在选择范数时有很大的灵活性。
- 举例说明:L1范数与L2范数在$R^2$中的等价性
- 让我们考虑一个二维向量 $v = (x, y)$。
- 它的L1范数是 $\Vert v \Vert_{1} = |x| + |y|$。
- 它的L2范数是 $\Vert v \Vert_{2} = \sqrt{x^2 + y^2}$。
- 我们可以证明它们之间存在如下关系:
- $\Vert v \Vert_{2} \le \Vert v \Vert_{1}$:因为 $(|x|+|y|)^2 = x^2+y^2+2|xy| \ge x^2+y^2$,两边开方即可得到。
- $\Vert v \Vert_{1} \le \sqrt{2} \Vert v \Vert_{2}$:根据柯西-施瓦茨不等式,我们可以得到这个界限。
- 因此,在二维空间中,我们找到了 $c_1=1$ 和 $c_2=\sqrt{2}$,使得 $1 \cdot \Vert v \Vert_{2} \le \Vert v \Vert_{1} \le \sqrt{2} \cdot \Vert v \Vert_{2}$。这证明了L1和L2范数在$R^2$中是等价的。
- 对利普希茨连续性的影响:
- 假设函数 $f$ 对于L2范数是K-利普希茨连续的,即 $\Vert f(x_1) - f(x_2) \Vert_2 \le K \Vert x_1 - x_2 \Vert_2$。
- 如果我们想知道它对于L1范数是否连续,我们可以利用上面的等价关系进行放缩,从而得到一个新的利普希茨常数 $K'$。这保证了连续性的性质本身不会改变。
幂迭代
幂迭代算法的步骤如下:
- 随机初始化一个与权重矩阵输出维度相同的向量 $\mathbf{u}$。
- 循环迭代(在GAN的谱归一化中,通常迭代1次就足够):
- $\mathbf{v} = W^T \mathbf{u}$ (计算 $W^T$ 与 $\mathbf{u}$ 的乘积)
- $\mathbf{v} = \frac{\mathbf{v}}{||\mathbf{v}||_2}$ (归一化 $\mathbf{v}$)
- $\mathbf{u} = W \mathbf{v}$ (计算 $W$ 与 $\mathbf{v}$ 的乘积)
- $\mathbf{u} = \frac{\mathbf{u}}{||\mathbf{u}||_2}$ (归一化 $\mathbf{u}$)
- 最大奇异值(谱范数)$\sigma(W)$ 最终可以由 $\mathbf{u}^T W \mathbf{v}$ 来近似。
这个过程实际上是在寻找与 $W^T W$ 的最大特征值相关联的特征向量。通过反复与 $W$ 和 $W^T$ 相乘,向量 $\mathbf{u}$ 和 $\mathbf{v}$ 会逐渐对齐到 $W$ 的最大左右奇异向量上。在训练过程中,$\mathbf{u}$ 的值会被保存下来,作为下一次迭代的起点,从而进一步提高效率。
泊松图像融合 (Poisson Image Editing)
核心目标:将一个源图像中的物体或区域无缝地融合到目标图像中,使得融合结果看起来自然,没有生硬的边缘。
1. 问题:为什么不能直接复制粘贴?
直接将一个区域的像素复制粘贴到另一个图像上,通常会产生很强的“违和感”,主要原因有:
- 光照和色调不匹配:源区域和目标区域的光照条件、颜色风格往往不同,导致明显的边缘痕迹。
- 边缘生硬:即使色调相似,复制的边缘也常常是突兀的。
2. 核心思想:保留“梯度”,而非“像素值”
泊松融合的聪明之处在于,它认为一个物体的“身份”更多地由其内部的“纹理”和“细节”决定,而不是其绝对的颜色值。在图像中,“纹理”和“细节”就是梯度(Gradient)——即像素颜色在空间上的变化率。
泊松融合的策略:
- 复制梯度:我们只从源图像中“复制”我们感兴趣区域的梯度场。
- 适应背景:我们将这个梯度场“粘贴”到目标图像上,并要求系统求解一个新的像素区域,这个新区域需要满足两个条件:
- 其内部的梯度要尽可能地与我们复制过来的源梯度场保持一致(保留了细节)。
- 其边界的像素颜色要与目标图像的背景完全一致(实现了无缝连接)。
3. 数学直觉:一个插值问题
整个过程可以看作是一个巨大的插值问题。
- 我们有一个区域 $\Omega$,其内部的“颜色变化趋势”(梯度)是已知的(来自源图像),而其边界的颜色值也是已知的(来自目标图像)。
- 我们的任务是求解区域 $\Omega$ 内部所有像素的颜色值,使得它们既能满足内部的梯度要求,又能与边界平滑地衔接。
这个问题在数学上可以被构建成一个泊松方程:
$$ \Delta f = \text{div}(\mathbf{v}) \quad \text{over } \Omega $$- $f$:我们要求解的目标图像区域。
- $\Delta$:拉普拉斯算子,表示梯度的散度。
- $\mathbf{v}$:我们从源图像中提取的梯度场。
- 边界条件:$f$ 在 $\Omega$ 的边界上的值,等于目标图像在同一位置的值。
通过求解这个偏微分方程,我们就能得到一个全新的、完美融合的像素区域 $f$。
4. 算法步骤概览
- 定义蒙版 (Mask):在源图像 $S$ 中,手动或自动圈出要复制的区域 $\Omega$。
- 计算源梯度场:计算 $S$ 在区域 $\Omega$ 内的梯度场 $\mathbf{v}$。
- 设定边界条件:获取目标图像 $T$ 在 $\Omega$ 边界上的像素值。
- 求解泊松方程:在目标图像的指定位置,使用源梯度场 $\mathbf{v}$ 作为引导,使用目标图像的边界值作为约束,求解方程,得到融合后的新像素值。
- 替换区域:将计算出的新像素值替换掉目标图像对应的区域。
5. 应用
- 无缝克隆 (Seamless Cloning):最经典的应用,将一个物体无缝融合到新背景中。
- 纹理映射 (Texture Mapping):将一个物体的纹理“贴”到另一个物体上。
- 图像修复/去瑕疵:通过用周围的“好”纹理(梯度)来填充一个损坏的区域,实现自然的修复。
- 风格迁移:在一定程度上,可以用于传递图像的局部风格。
"""
通过近似把泊松方程(连续的微分方程)转换为线性方程组进行求解
"""
import cv2
import numpy as np
import scipy.sparse
from scipy.sparse.linalg import spsolve
# 1. 准备输入
# src: 源图像 (从中抠图)
# dst: 目标图像 (粘贴到这里)
# mask: 蒙版,白色区域代表要融合的区域
# 2. 识别区域内的像素
# 获取所有蒙版内像素的坐标 (y, x)
region_coords = np.where(mask == 255)
num_pixels_in_region = len(region_coords[0])
# 建立像素坐标到线性方程索引的映射
# e.g., coord_to_index[(y, x)] = 0, 1, 2, ...
coord_to_index = {coord: i for i, coord in enumerate(zip(*region_coords))}
# 3. 构建稀疏矩阵 A
# 使用 lil_matrix 格式,因为它适合增量式构建
A = scipy.sparse.lil_matrix((num_pixels_in_region, num_pixels_in_region))
# 4. 构建向量 b (处理三个颜色通道)
b_r = np.zeros(num_pixels_in_region)
b_g = np.zeros(num_pixels_in_region)
b_b = np.zeros(num_pixels_in_region)
# 5. 填充 A 和 b
# 遍历蒙版内的每一个像素 p
for i in range(num_pixels_in_region):
y, x = region_coords[0][i], region_coords[1][i]
# --- 填充矩阵 A ---
A[i, i] = 4 # 对角线元素
# --- 填充向量 b 和 A 的非对角线 ---
laplacian_guidance = 0
# 遍历 p 的四个邻居 (neighbor)
for ny, nx in [(y-1, x), (y+1, x), (y, x-1), (y, x+1)]:
# 计算源图像的拉普拉斯散度部分 v_pq = g_p - g_q
# 这里为了简化,我们直接使用∇²f = ∇²g,即b向量直接用源的拉普拉斯
# 而不是混合梯度方法 v = g_p - g_q
# 检查邻居是否在蒙版内
if mask[ny, nx] == 255:
# 邻居也在区域内,是未知数
j = coord_to_index[(ny, nx)]
A[i, j] = -1
else:
# 邻居在边界外,是已知数,加到 b 上
b_r[i] += dst[ny, nx, 2] # OpenCV a BGR
b_g[i] += dst[ny, nx, 1]
b_b[i] += dst[ny, nx, 0]
# OpenCV的Laplacian计算是 g(x+1) + g(x-1) + g(y+1) + g(y-1) - 4*g(x)
# 所以b中还需加上源的拉普拉斯值
laplacian_src_bgr = cv2.Laplacian(src, cv2.CV_64F, ksize=1)
for i in range(num_pixels_in_region):
y, x = region_coords[0][i], region_coords[1][i]
b_b[i] += laplacian_src_bgr[y, x, 0]
b_g[i] += laplacian_src_bgr[y, x, 1]
b_r[i] += laplacian_src_bgr[y, x, 2]
# 6. 求解 Ax = b
# 将 A 转换为 CSR (Compressed Sparse Row) 格式以提高求解效率
A_csr = A.tocsr()
# 分别求解三个颜色通道
x_r = spsolve(A_csr, b_r)
x_g = spsolve(A_csr, b_g)
x_b = spsolve(A_csr, b_b)
# 7. 将解 x 复制回目标图像
result = dst.copy()
for i in range(num_pixels_in_region):
y, x = region_coords[0][i], region_coords[1][i]
# 注意裁剪到 [0, 255] 范围
result[y, x, 2] = np.clip(x_r[i], 0, 255)
result[y, x, 1] = np.clip(x_g[i], 0, 255)
result[y, x, 0] = np.clip(x_b[i], 0, 255)
# 8. 显示结果
# cv2.imshow('Poisson Blending Result', result)
"""
opencv中带的计算
"""
import cv2
import numpy as np
# 读取源图像、目标图像和蒙版
src = cv2.imread('source_image.jpg')
dst = cv2.imread('destination_image.jpg')
# 创建一个蒙版 (mask),注意必须是单通道灰度图
mask = np.zeros(src.shape[:2], dtype=np.uint8)
# ... 在mask上绘制你想要抠取的区域,比如一个白色矩形或圆形 ...
# poly = np.array([[x1,y1], [x2,y2], ...], np.int32)
# cv2.fillPoly(mask, [poly], (255, 255, 255))
# 假设我们已经创建好了mask
# 找到蒙版区域的中心点,用于指定粘贴位置
# 注意:这里center的坐标是相对于目标图像的
center = (dst.shape[1] // 2, dst.shape[0] // 2)
# 调用泊松融合函数
# cv2.NORMAL_CLONE: 普通泊松融合
# cv2.MIXED_CLONE: 混合梯度融合,纹理更丰富时效果更好
output = cv2.seamlessClone(src, dst, mask, center, cv2.NORMAL_CLONE)
# 显示结果
cv2.imshow('Seamless Clone', output)
cv2.waitKey(0)
cv2.destroyAllWindows()泰勒展开
泰勒展开是一个用无限项的多项式来近似一个函数的方法。如果一个函数在某点附近是“光滑”的(即无限可导),我们就可以在该点周围用一个多项式来无限逼近它。
核心思想:如果我们知道函数在某一点 $a$ 的所有信息(函数值、一阶导数、二阶导数…),我们就能“预测”出它在 $a$ 附近任何一点 $x$ 的值。
通用公式: 对于一个在点 $a$ 无限可导的函数 $f(x)$,其在 $a$ 点的泰勒展开为:
$$ f(x) = \sum_{n=0}^{\infty} \frac{f^{(n)}(a)}{n!}(x-a)^n = f(a) + f'(a)(x-a) + \frac{f''(a)}{2!}(x-a)^2 + \frac{f'''(a)}{3!}(x-a)^3 + \dots $$- $f^{(n)}(a)$ 表示函数 $f$ 在点 $a$ 的 $n$ 阶导数。
- $n!$ 表示 $n$ 的阶乘。
麦克劳林展开 (Maclaurin Series)
当 $a=0$ 时,泰勒展开有一个特殊的名字,叫做麦克劳林展开。这是最常用的一种形式:
$$ f(x) = \sum_{n=0}^{\infty} \frac{f^{(n)}(0)}{n!}x^n = f(0) + f'(0)x + \frac{f''(0)}{2!}x^2 + \frac{f'''(0)}{3!}x^3 + \dots $$常见函数的麦克劳林展开
- $e^x$ $$ e^x = 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \dots = \sum_{n=0}^{\infty} \frac{x^n}{n!} $$
- $sin(x)$ $$ sin(x) = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \dots = \sum_{n=0}^{\infty} (-1)^n \frac{x^{2n+1}}{(2n+1)!} $$
- $cos(x)$ $$ cos(x) = 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + \dots = \sum_{n=0}^{\infty} (-1)^n \frac{x^{2n}}{(2n)!} $$
直观理解
- 第0项 ($f(a)$):最粗糙的近似,直接用 $a$ 点的函数值当作 $x$ 点的函数值。
- 第1项 ($f'(a)(x-a)$):用 $a$ 点的切线来近似函数,考虑了函数的一阶变化率。
- 第2项 ($\frac{f''(a)}{2!}(x-a)^2$):在切线的基础上,加入了二阶导数(凹凸性)的信息,使得近似曲线的弯曲程度更接近原函数。
- 更高项:不断加入更高阶的导数信息,让多项式在细节上越来越贴近原函数。
虚数
虚数是数学中的一个重要概念,它扩展了实数的范畴,使得所有多项式方程都有解。虚数的核心是虚数单位 $i$。
虚数单位 $i$
- 定义:虚数单位 $i$ 被定义为满足 $i^2 = -1$ 的数。
- 性质:
- $i = \sqrt{-1}$
- $i^1 = i$
- $i^2 = -1$
- $i^3 = i^2 \cdot i = -i$
- $i^4 = i^2 \cdot i^2 = (-1) \cdot (-1) = 1$
- $i$ 的幂次以 4 为周期循环:$i, -1, -i, 1, i, -1, -i, 1, \dots$
虚数与复数
- 虚数 (Imaginary Number):形如 $bi$ 的数,其中 $b$ 是一个实数,且 $b \neq 0$。例如,$3i$, $-0.5i$, $\sqrt{2}i$ 都是虚数。
- 复数 (Complex Number):形如 $a + bi$ 的数,其中 $a$ 和 $b$ 都是实数。
- $a$ 称为复数的实部 (Real Part)。
- $b$ 称为复数的虚部 (Imaginary Part)。
- 当 $b=0$ 时,复数 $a+0i$ 就是实数 $a$。
- 当 $a=0$ 且 $b \neq 0$ 时,复数 $0+bi$ 就是纯虚数 $bi$。
- 因此,实数和纯虚数都是复数的特例。
复平面 (Complex Plane)
复数 $a+bi$ 可以用复平面上的一个点 $(a, b)$ 或一个从原点指向 $(a, b)$ 的向量来表示。
- 实轴 (Real Axis):对应于实数 $a$ 的横轴。
- 虚轴 (Imaginary Axis):对应于虚数 $bi$ 的纵轴。
虚数的意义与应用
虚数和复数在数学、物理、工程等领域有着极其广泛的应用:
- 代数:使得所有一元多项式方程都有解(代数基本定理)。
- 信号处理:傅里叶变换广泛使用复数来分析信号的频率成分。
- 量子力学:量子态的描述离不开复数。
- 电路分析:交流电路中的阻抗、电压和电流常用复数表示,简化了计算。
- 控制理论:系统稳定性分析中,复数根扮演着关键角色。
- 流体力学:复变函数可以用来解决二维流体流动问题。
虚数提供了一种优雅而强大的方式来描述和解决现实世界中的许多问题,尤其是在涉及旋转、振动和波动的现象中。
欧拉公式
欧拉公式被誉为“数学中最美丽的公式”,它将五个最重要的数学常数($e, i, \pi, 1, 0$)联系在一起,并揭示了指数函数与三角函数之间深刻的内在联系。
公式:
$$ e^{i\theta} = \cos(\theta) + i\sin(\theta) $$- $e$: 自然常数,约等于 2.718,是自然增长的极限。
- $i$: 虚数单位,满足 $i^2 = -1$。它代表了从实数轴到虚数轴的“旋转90度”。
- $\theta$: 一个实数,通常表示角度(以弧度为单位)。
- $\cos(\theta)$ 和 $\sin(\theta)$: 我们熟悉的三角函数,描述了单位圆上点的坐标。
几何意义:复平面上的单位圆 欧拉公式的几何意义非常直观:
- $e^{i\theta}$ 代表了复平面上的一个点。
- 这个点位于以原点为中心的单位圆上。
- 角度 $\theta$ 就是该点与正实数轴的夹角。
- $\cos(\theta)$ 是这个点的实部(横坐标)。
- $\sin(\theta)$ 是这个点的虚部(纵坐标)。 所以,$e^{i\theta}$ 描述了一个在单位圆上不断旋转的点。
与泰勒展开的联系(证明) 欧拉公式最直观的证明方式就是通过泰勒展开(准确地说是麦克劳林展开)。
- 我们已知 $e^x$ 的展开式: $$ e^x = 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \frac{x^4}{4!} + \dots $$
- 现在,我们将 $x$ 替换为 $i\theta$: $$ e^{i\theta} = 1 + (i\theta) + \frac{(i\theta)^2}{2!} + \frac{(i\theta)^3}{3!} + \frac{(i\theta)^4}{4!} + \dots $$
- 利用 $i$ 的幂次规律($i^1=i, i^2=-1, i^3=-i, i^4=1, \dots$)来简化上式: $$ e^{i\theta} = 1 + i\theta - \frac{\theta^2}{2!} - i\frac{\theta^3}{3!} + \frac{\theta^4}{4!} + i\frac{\theta^5}{5!} - \dots $$
- 将上式中的实部和虚部(含有 $i$ 的项)分开重新组合:
- 实部:$1 - \frac{\theta^2}{2!} + \frac{\theta^4}{4!} - \dots$
- 虚部:$i(\theta - \frac{\theta^3}{3!} + \frac{\theta^5}{5!} - \dots)$
- 我们惊奇地发现,分离出来的这两部分正是 $\cos(\theta)$ 和 $\sin(\theta)$ 的泰勒展开式!
- 实部 = $\cos(\theta)$
- 虚部 = $i\sin(\theta)$
- 因此,我们证明了 $e^{i\theta} = \cos(\theta) + i\sin(\theta)$。
“上帝公式” 当 $\theta = \pi$ 时,我们得到欧拉公式的一个特例:
$$ e^{i\pi} = \cos(\pi) + i\sin(\pi) = -1 + i \cdot 0 = -1 $$整理后即为:
$$ e^{i\pi} + 1 = 0 $$这个等式将数学中五个最基本的常数 $e, i, \pi, 1, 0$ 简洁地联系在了一起,因此被很多人称为“上帝公式”。
共轭
可以把共轭想象成一个事物天生的“镜像”。这个镜像有一个神奇的特征:当你把一个事物和它的共轭通过某种运算(通常是乘法)结合在一起的时候,原来的那个事物中“复杂”的部分就会消失,结果变得非常的“纯粹”。
- 在复数中:麻烦的部分是虚数单位,所以$a+bi$的共轭就是$a-bi$,因为乘了之后得到$a^{2} + b^{2}$
- 在根式中:麻烦的就是$\sqrt{x}$
- 再推广一下:$cos(h) - 1$的共轭就是$cos(h) + 1$
三角函数
三角函数是数学中非常重要的一类函数,它们描述了直角三角形中角与边长的关系,以及周期性现象。在机器学习和深度学习中,三角函数常用于周期性特征的编码(如时间序列)、旋转变换、傅里叶变换等。
1. 基本定义 (直角三角形)
对于一个直角三角形,设一个锐角为 $\theta$,其对边为 $a$,邻边为 $b$,斜边为 $c$:
- 正弦 (Sine):$\sin(\theta) = \frac{对边}{斜边} = \frac{a}{c}$
- 余弦 (Cosine):$\cos(\theta) = \frac{邻边}{斜边} = \frac{b}{c}$
- 正切 (Tangent):$\tan(\theta) = \frac{对边}{邻边} = \frac{a}{b} = \frac{\sin(\theta)}{\cos(\theta)}$
2. 常用恒等式
2.1 倒数关系
- $\csc(\theta) = \frac{1}{\sin(\theta)}$
- $\sec(\theta) = \frac{1}{\cos(\theta)}$
- $\cot(\theta) = \frac{1}{\tan(\theta)}$
2.2 勾股定理恒等式
- $\sin^2(\theta) + \cos^2(\theta) = 1$
- $1 + \tan^2(\theta) = \sec^2(\theta)$
- $1 + \cot^2(\theta) = \csc^2(\theta)$
2.3 诱导公式 (周期性、对称性)
- $\sin(-\theta) = -\sin(\theta)$ (奇函数)
- $\cos(-\theta) = \cos(\theta)$ (偶函数)
- $\sin(\theta \pm 2k\pi) = \sin(\theta)$ (周期性)
- $\cos(\theta \pm 2k\pi) = \cos(\theta)$ (周期性)
- $\sin(\frac{\pi}{2} - \theta) = \cos(\theta)$
- $\cos(\frac{\pi}{2} - \theta) = \sin(\theta)$
- $\sin(\pi - \theta) = \sin(\theta)$
- $\cos(\pi - \theta) = -\cos(\theta)$
2.4 常用极限
- $lim_{h \to \infty}\frac{sin(h)}{h}=1$ 通过夹逼定理证明:单位圆、x轴交点B、弧度$h$、两个三角形面积、一个扇形面积。
- $lim_{h \to \infty}\frac{cos(h) - 1}{h} = 0$,根据上面公式证明:上下乘以分子的共轭$cos(h) + 1$,然后拆分。
3. 和差角公式
- $\sin(A \pm B) = \sin(A)\cos(B) \pm \cos(A)\sin(B)$
- $\cos(A \pm B) = \cos(A)\cos(B) \mp \sin(A)\sin(B)$
- $\tan(A \pm B) = \frac{\tan(A) \pm \tan(B)}{1 \mp \tan(A)\tan(B)}$
4. 倍角公式
- $\sin(2\theta) = 2\sin(\theta)\cos(\theta)$
- $\cos(2\theta) = \cos^2(\theta) - \sin^2(\theta) = 2\cos^2(\theta) - 1 = 1 - 2\sin^2(\theta)$
- $\tan(2\theta) = \frac{2\tan(\theta)}{1 - \tan^2(\theta)}$
5. 半角公式
- $\sin^2(\frac{\theta}{2}) = \frac{1 - \cos(\theta)}{2}$
- $\cos^2(\frac{\theta}{2}) = \frac{1 + \cos(\theta)}{2}$
- $\tan^2(\frac{\theta}{2}) = \frac{1 - \cos(\theta)}{1 + \cos(\theta)}$
6. 积化和差公式
- $\sin(A)\cos(B) = \frac{1}{2}[\sin(A+B) + \sin(A-B)]$
- $\cos(A)\sin(B) = \frac{1}{2}[\sin(A+B) - \sin(A-B)]$
- $\cos(A)\cos(B) = \frac{1}{2}[\cos(A+B) + \cos(A-B)]$
- $\sin(A)\sin(B) = -\frac{1}{2}[\cos(A+B) - \cos(A-B)]$
7. 和差化积公式
- $\sin(A) + \sin(B) = 2\sin(\frac{A+B}{2})\cos(\frac{A-B}{2})$
- $\sin(A) - \sin(B) = 2\cos(\frac{A+B}{2})\sin(\frac{A-B}{2})$
- $\cos(A) + \cos(B) = 2\cos(\frac{A+B}{2})\cos(\frac{A-B}{2})$
- $\cos(A) - \cos(B) = -2\sin(\frac{A+B}{2})\sin(\frac{A-B}{2})$
这些公式在解决各种数学问题,尤其是在物理、工程和计算机图形学中都有广泛的应用。
光流 (Optical Flow)
光流是计算机视觉中的一个重要概念,用于描述视频或图像序列中物体或摄像机的运动。它将图像中每个像素点的运动表示为一个二维向量,这个向量就是该像素点的光流向量。
1. 核心思想
光流的核心思想是跟踪图像中像素点的运动。想象一下,在一段视频中,一个像素点 $(x, y)$ 在第一帧的位置,经过一小段时间 $\Delta t$ 后,移动到了第二帧的 $(x + \Delta x, y + \Delta y)$ 位置。光流的目标就是找到这个位移向量 $(\Delta x, \Delta y)$。
2. 基本假设
为了计算光流,我们通常需要依赖以下几个基本假设:
亮度恒定假设 (Brightness Constancy): 这是光流法最核心的假设。它认为,一个正在运动的物体的像素,在连续的两帧图像中其亮度(灰度值)是保持不变的。 用数学公式表示为:
$$ I(x, y, t) = I(x + \Delta x, y + \Delta y, t + \Delta t) $$其中,$I(x, y, t)$ 是在时间 $t$ 时,坐标 $(x, y)$ 处的像素灰度值。
小运动假设 (Small Motion): 假设物体在两帧之间的运动是微小的。这样,我们可以使用泰勒展开来近似 $I(x + \Delta x, y + \Delta y, t + \Delta t)$。
3. 光流方程
基于以上两个假设,我们可以推导出光流方程 (Optical Flow Equation):
对 $I(x + \Delta x, y + \Delta y, t + \Delta t)$ 进行一阶泰勒展开:
$$ I(x + \Delta x, y + \Delta y, t + \Delta t) \approx I(x, y, t) + \frac{\partial I}{\partial x}\Delta x + \frac{\partial I}{\partial y}\Delta y + \frac{\partial I}{\partial t}\Delta t $$根据亮度恒定假设,我们有 $I(x, y, t) = I(x + \Delta x, y + \Delta y, t + \Delta t)$,所以:
$$ \frac{\partial I}{\partial x}\Delta x + \frac{\partial I}{\partial y}\Delta y + \frac{\partial I}{\partial t}\Delta t = 0 $$两边同时除以 $\Delta t$:
$$ \frac{\partial I}{\partial x}\frac{\Delta x}{\Delta t} + \frac{\partial I}{\partial y}\frac{\Delta y}{\Delta t} + \frac{\partial I}{\partial t} = 0 $$令 $u = \frac{\Delta x}{\Delta t}$ 和 $v = \frac{\Delta y}{\Delta t}$ 分别表示像素在 $x$ 和 $y$ 方向上的速度(即光流)。$I_x = \frac{\partial I}{\partial x}$,$I_y = \frac{\partial I}{\partial y}$,$I_t = \frac{\partial I}{\partial t}$ 分别表示图像在 $x, y, t$ 方向上的偏导数。于是我们得到最终的光流方程:
$$ I_x u + I_y v + I_t = 0 $$或者写成梯度形式:
$$ \nabla I \cdot (u, v)^T + I_t = 0 $$
4. 孔径问题 (Aperture Problem)
光流方程是一个关于两个未知数 $(u, v)$ 的线性方程,但我们只有一个方程,所以无法直接求解。这就是所谓的孔径问题。
想象一下,通过一个小孔(孔径)观察一个正在移动的边缘。我们只能感知到垂直于边缘方向的运动,而无法确定平行于边缘方向的运动。
5. 解决方法
为了解决孔径问题,我们需要引入额外的约束。常见的方法有:
a. Lucas-Kanade (LK) 稀疏光流
- 核心思想:假设在一个小的邻域窗口(例如 $3 \times 3$ 或 $5 \times 5$)内的所有像素具有相同的光流向量 $(u, v)$。
- 方法:对于窗口内的每个像素,我们都有一个光流方程。这样,我们就可以得到一个超定方程组(多个方程,两个未知数),然后通过最小二乘法求解。
- 优点:计算简单,对噪声不敏感。
- 缺点:只能计算稀疏特征点(如角点)的光流,无法计算整个图像的光流。
b. Horn-Schunck (HS) 稠密光流
- 核心思想:引入一个全局平滑约束,假设光流场是平滑变化的,即相邻像素的光流向量相似。
- 方法:通过最小化一个能量函数来求解光流,该能量函数包含两项:
- 数据项:保证解符合光流方程。
- 平滑项:保证光流场平滑。
- 优点:可以计算图像中每个像素点的光流(稠密光流)。
- 缺点:计算量大,平滑假设在物体边缘处不成立。
6. Python OpenCV 实现 (Lucas-Kanade)
import numpy as np
import cv2
# 读取视频
cap = cv2.VideoCapture('your_video.mp4')
# Lucas-Kanade 参数
lk_params = dict( winSize = (15, 15),
maxLevel = 2,
criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# Shi-Tomasi 角点检测参数
feature_params = dict( maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7,
blockSize = 7 )
# 创建随机颜色用于绘制光流轨迹
color = np.random.randint(0, 255, (100, 3))
# 读取第一帧并找到角点
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 创建一个蒙版图像用于绘制轨迹
mask = np.zeros_like(old_frame)
while(1):
ret, frame = cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算光流
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
# 选择好的点
good_new = p1[st==1]
good_old = p0[st==1]
# 绘制轨迹
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1)
img = cv2.add(frame, mask)
cv2.imshow('frame', img)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
# 更新前一帧和前一点
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)
cv2.destroyAllWindows()
cap.release()极大似然估计 (Maximum Likelihood Estimation, MLE)
极大似然估计(MLE)是一种在给定观察数据的情况下,用来估计统计模型参数的方法。它的核心思想是:找到一组参数,使得观察到当前数据的概率最大。
1. 核心原理
假设我们有一个概率分布 $P(x; \theta)$,其中 $x$ 是随机变量,$\theta$ 是我们需要估计的参数。 如果我们进行了一系列独立的实验,观察到了一组数据 $X = \{x_1, x_2, \dots, x_n\}$。
那么,这组数据出现的联合概率(即似然函数)为:
$$ L(\theta) = P(x_1, x_2, \dots, x_n; \theta) = \prod_{i=1}^n P(x_i; \theta) $$MLE 的目标就是找到一个 $\hat{\theta}$,使得 $L(\theta)$ 最大:
$$ \hat{\theta}_{MLE} = \arg\max_{\theta} L(\theta) $$为了计算方便,我们通常对似然函数取对数,将乘积转换为求和,得到对数似然函数 (Log-Likelihood):
$$ \ell(\theta) = \ln L(\theta) = \sum_{i=1}^n \ln P(x_i; \theta) $$因为 $\ln$ 是单调递增函数,所以最大化 $L(\theta)$ 等价于最大化 $\ell(\theta)$。
求解步骤:
- 写出似然函数 $L(\theta)$。
- 取对数得到 $\ell(\theta)$。
- 对 $\theta$ 求导数,并令导数为 0。
- 求解方程得到 $\hat{\theta}$。
2. 使用场景
MLE 是统计学和机器学习中最基础、最常用的参数估计方法之一。
- 参数估计:已知数据分布形式(如正态分布、伯努利分布),估计其参数(如均值、方差、概率 $p$)。
- 逻辑回归 (Logistic Regression):逻辑回归的损失函数(交叉熵损失)本质上就是负的对数似然函数。最小化损失函数等价于最大化似然函数。
- 深度学习:许多神经网络模型的训练过程(最小化交叉熵)都可以看作是极大似然估计。
- 隐马尔可夫模型 (HMM):用于参数估计。
3. 示例:估计正态分布的参数
假设我们有一组数据 $X = \{x_1, \dots, x_n\}$ 服从正态分布 $N(\mu, \sigma^2)$。我们需要估计 $\mu$ 和 $\sigma^2$。
正态分布的概率密度函数为:
$$ f(x; \mu, \sigma^2) = \frac{1}{\sqrt{2\pi\sigma^2}} e^{-\frac{(x-\mu)^2}{2\sigma^2}} $$Python 示例:
import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
from scipy.stats import norm
# 1. 生成模拟数据
np.random.seed(42)
true_mu = 5.0
true_sigma = 2.0
n_samples = 1000
data = np.random.normal(true_mu, true_sigma, n_samples)
# 2. 手动推导的解析解 (Closed-form solution)
# 对于正态分布,MLE 的解析解非常简单:
mu_mle = np.mean(data)
sigma_mle = np.std(data, ddof=0) # 注意:MLE估计的方差是有偏的,分母是n而不是n-1
print(f"真实参数: mu={true_mu}, sigma={true_sigma}")
print(f"解析解 MLE: mu={mu_mle:.4f}, sigma={sigma_mle:.4f}")
# 3. 数值优化解法 (General method)
# 当无法求出解析解时,可以使用梯度下降或其它优化算法
# 这里演示如何定义负对数似然函数并最小化它
def neg_log_likelihood(params, data):
"""
负对数似然函数
params: [mu, sigma]
"""
mu, sigma = params
if sigma <= 0:
return np.inf # 标准差必须为正
# 计算每个数据点的概率密度 pdf
# pdf = (1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-0.5 * ((data - mu) / sigma)**2)
# 为了数值稳定性,直接使用 scipy.stats.norm.logpdf
log_likelihood = np.sum(norm.logpdf(data, loc=mu, scale=sigma))
return -log_likelihood # 我们要最大化似然,等价于最小化负似然
# 初始猜测
initial_guess = [0.0, 1.0]
# 使用数值优化寻找最优参数
result = minimize(neg_log_likelihood, initial_guess, args=(data,), method='Nelder-Mead')
print(f"数值优化 MLE: mu={result.x[0]:.4f}, sigma={result.x[1]:.4f}")
# 可视化
x_axis = np.linspace(0, 10, 100)
plt.hist(data, bins=30, density=True, alpha=0.6, color='g', label='Data Histogram')
plt.plot(x_axis, norm.pdf(x_axis, mu_mle, sigma_mle), 'r-', lw=2, label='MLE Fitted Gaussian')
plt.legend()
plt.title('Maximum Likelihood Estimation of Gaussian Distribution')
plt.show()