我使用此处给出的等式在Python中构建了一个包裹的双变量高斯分布: http://www.aos.wisc.edu/~dvimont/aos575/Handouts/bivariate_notes.pdf 但是,我不明白为什么尽管已经合并了归一化常数,但我的分布总和不能达到1。
对于U x U格子,
import numpy as np
from math import *
U = 60
m = np.arange(U)
i = m.reshape(U,1)
j = m.reshape(1,U)
sigma = 0.1
ii = np.minimum(i, U-i)
jj = np.minimum(j, U-j)
norm_constant = 1/(2*pi*sigma**2)
xmu = (ii-0)/sigma; ymu = (jj-0)/sigma
rhs = np.exp(-.5 * (xmu**2 + ymu**2))
ker = norm_constant * rhs
>> ker.sum() # area of each grid is 1
15.915494309189533
我确信我正在思考这个问题的方式根本就缺失了,并且怀疑需要某种额外的规范化,尽管我无法绕过它。
更新:
感谢其他人提出的富有洞察力的建议,我重写了我的代码以将L1规范化应用于内核。然而,似乎在通过FFt进行2D卷积的情况下,将范围保持为[0,U]仍然能够返回令人信服的结果:
U = 100
Ukern = np.copy(U)
#Ukern = 15
m = np.arange(U)
i = m.reshape(U,1)
j = m.reshape(1,U)
sigma = 2.
ii = np.minimum(i, Ukern-i)
jj = np.minimum(j, Ukern-j)
xmu = (ii-0)/sigma; ymu = (jj-0)/sigma
ker = np.exp(-.5 * (xmu**2 + ymu**2))
ker /= np.abs(ker).sum()
''' Point Density '''
ido = np.random.randint(U, size=(10,2)).astype(np.int)
og = np.zeros((U,U))
np.add.at(og, (ido[:,0], ido[:,1]), 1)
''' Convolution via FFT and inverse-FFT '''
v1 = np.fft.fft2(ker)
v2 = np.fft.fft2(og)
v0 = np.fft.ifft2(v2*v1)
dd = np.abs(v0)
plt.plot(ido[:,1], ido[:,0], 'ko', alpha=.3)
plt.imshow(dd, origin='origin')
plt.show()
答案 0 :(得分:4)
正如您所看到的,这看起来与高斯内核完全不同。你的大部分功能从0到1消失。看看内核本身就会发现所有的值确实真的很快就会消失:
>>> ker[0:5, 0:5]
array([[ 1.592e+001, 3.070e-021, 2.203e-086, 5.879e-195, 0.000e+000],
[ 3.070e-021, 5.921e-043, 4.248e-108, 1.134e-216, 0.000e+000],
[ 2.203e-086, 4.248e-108, 3.048e-173, 8.136e-282, 0.000e+000],
[ 5.879e-195, 1.134e-216, 8.136e-282, 0.000e+000, 0.000e+000],
[ 0.000e+000, 0.000e+000, 0.000e+000, 0.000e+000, 0.000e+000]])
你得到的15.915的总和值基本上只是ker [0,0]。所有这些告诉你的是你没有正确构建网格。
请记住,在计算机上创建内核时,您必须在适当的位置对其进行采样。过于粗略地采样会导致你的总和不正确。
首先,如果您想要以mu=0
为中心的完整密度,则必须从i
到j
取-U // 2
和U // 2
。但是为了解决您的分辨率问题,我建议在-0.5和0.5之间取U
个分数。
import numpy as np
import matplotlib.pyplot as plt
U = 60
m = np.linspace(-0.5, 0.5, U) # 60 points between -1 and 1
delta = m[1] - m[0] # delta^2 is the area of each grid cell
(x, y) = np.meshgrid(m, m) # Create the mesh
sigma = 0.1
norm_constant = 1 / (2 * np.pi * sigma**2)
rhs = np.exp(-.5 * (x**2 + y**2) / sigma**2)
ker = norm_constant * rhs
print(ker.sum() * delta**2)
plt.contour(x, y, ker)
plt.axis('equal')
plt.show()
在这种情况下,知道选择的范围(-0.5到0.5)取决于您的功能。例如,如果您现在使用sigma = 2
,您会发现您的金额将无法再次使用,因为现在您的过于精细。将您的范围设置为参数的函数 - 例如-5 * sigma
到5 * sigma
- 可能是最佳选择。
答案 1 :(得分:3)
注意:如下面的评论中所述,此解决方案仅在您尝试构建高斯卷积核(或高斯滤波器)用于图像处理时才有效。它不是一个正确归一化的高斯密度函数,但它是用于从图像中去除高斯噪声的形式。
您缺少L1规范化:
ker /= np.abs(ker).sum()
这会使你的内核表现得像一个实际的密度函数。由于您的网格的值可能会有很大差异,因此需要进行上述标准化步骤。
事实上,你所拥有的高斯nornalization常数可能只是使用上面的L1规范。如果我没有注意到,你正试图创建一个高斯卷积,上面是通常的规范化技术。
正如@Praveen所说,你的第二个错误就是你需要从[-U//2, U//2]
中抽取网格。你可以这样做:
i, j = np.mgrid[-U//2:U//2+1, -U//2:U//2+1]
最后,如果你要做的是构建一个高斯滤波器,那么内核的大小通常是从sigma(以避免远离中心的零点)估算为U//2 <= t * sigma
,其中t
}是截断参数,通常设置为t=3
或t=4
。