高斯混合模型(GMM)拟合效果差

2 投票
2 回答
2813 浏览
提问于 2025-04-18 09:30

我在玩Scikit-learn的GMM(高斯混合模型)功能。首先,我创建了一个沿着直线x=y的分布。

from sklearn import mixture
import numpy as np 
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

line_model = mixture.GMM(n_components = 99)
#Create evenly distributed points between 0 and 1.
xs = np.linspace(0, 1, 100)
ys = np.linspace(0, 1, 100)

#Create a distribution that's centred along y=x
line_model.fit(zip(xs,ys))
plt.plot(xs, ys)
plt.show()

这产生了预期的分布效果:

分布图

接下来,我对这个分布进行了GMM拟合,并绘制了结果:

#Create the x,y mesh that will be used to make a 3D plot
x_y_grid = []
for x in xs:
    for y in ys:
        x_y_grid.append([x,y])

#Calculate a probability for each point in the x,y grid.
x_y_z_grid = []
for x,y in x_y_grid:
    z = line_model.score([[x,y]])
    x_y_z_grid.append([x,y,z])

x_y_z_grid = np.array(x_y_z_grid)

#Plot probabilities on the Z axis.
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(x_y_z_grid[:,0], x_y_z_grid[:,1], 2.72**x_y_z_grid[:,2])
plt.show()

结果的概率分布在x=0x=1的地方有一些奇怪的尾巴,并且在角落(x=1, y=1 和 x=0,y=0)也有额外的概率。

概率分布 n=99

使用n_components=5时也显示出这种行为:

概率分布 n=5

这是GMM本身的特性,还是实现上有什么问题,或者我做错了什么呢?

编辑:从模型中获取分数似乎消除了这种行为——这是应该的吗?

我在同一个数据集上训练这两个模型(x=y,从x=0到x=1)。简单地通过GMM的score方法检查概率似乎消除了这种边界效应。这是为什么呢?我在下面附上了图和代码。

检查不同领域的分数影响分布。

# Creates a line of 'observations' between (x_small_start, x_small_end)
# and (y_small_start, y_small_end). This is the data both gmms are trained on.
x_small_start = 0
x_small_end = 1
y_small_start = 0
y_small_end = 1

# These are the range of values that will be plotted
x_big_start = -1
x_big_end = 2
y_big_start = -1
y_big_end = 2


shorter_eval_range_gmm = mixture.GMM(n_components = 5)
longer_eval_range_gmm = mixture.GMM(n_components = 5)

x_small = np.linspace(x_small_start, x_small_end, 100)
y_small = np.linspace(y_small_start, y_small_end, 100)
x_big = np.linspace(x_big_start, x_big_end, 100)
y_big = np.linspace(y_big_start, y_big_end, 100)

#Train both gmms on a distribution that's centered along y=x
shorter_eval_range_gmm.fit(zip(x_small,y_small))
longer_eval_range_gmm.fit(zip(x_small,y_small))


#Create the x,y meshes that will be used to make a 3D plot
x_y_evals_grid_big = []
for x in x_big:
    for y in y_big:
        x_y_evals_grid_big.append([x,y])
x_y_evals_grid_small = []

for x in x_small:
    for y in y_small:
        x_y_evals_grid_small.append([x,y])

#Calculate a probability for each point in the x,y grid.
x_y_z_plot_grid_big = []
for x,y in x_y_evals_grid_big:
    z = longer_eval_range_gmm.score([[x, y]])
    x_y_z_plot_grid_big.append([x, y, z])
x_y_z_plot_grid_big = np.array(x_y_z_plot_grid_big)

x_y_z_plot_grid_small = []
for x,y in x_y_evals_grid_small:
    z = shorter_eval_range_gmm.score([[x, y]])
    x_y_z_plot_grid_small.append([x, y, z])
x_y_z_plot_grid_small = np.array(x_y_z_plot_grid_small)


#Plot probabilities on the Z axis.
fig = plt.figure()
fig.suptitle("Probability of different x,y pairs")

ax1 = fig.add_subplot(1, 2, 1, projection='3d')
ax1.plot(x_y_z_plot_grid_big[:,0], x_y_z_plot_grid_big[:,1], np.exp(x_y_z_plot_grid_big[:,2]))
ax1.set_xlabel('X Label')
ax1.set_ylabel('Y Label')
ax1.set_zlabel('Probability')
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
ax2.plot(x_y_z_plot_grid_small[:,0], x_y_z_plot_grid_small[:,1], np.exp(x_y_z_plot_grid_small[:,2]))
ax2.set_xlabel('X Label')
ax2.set_ylabel('Y Label')
ax2.set_zlabel('Probability')

plt.show()

2 个回答

1

编辑:
这其实是对的。和Ronald P.讨论后发现,你无法得到Gibbs效应,因为高斯分布不能通过“变成负数”来相互补偿,因为概率必须大于0。这似乎只是一个简单的绘图问题……请看他的回答!无论如何,我建议使用二维数据来测试GMM,而不是一维的线。

GMM正在根据你提供的数据进行拟合 - 具体来说:

xs = np.linspace(0, 1, 100)
ys = np.linspace(0, 1, 100)

因为数据在0和1的地方结束,所以GMM试图模拟这个事实:-0.01和1.01在技术上是超出了训练数据的范围,应该被评估为非常低的概率。这样做的结果是,它创建了一个扩展较小的高斯分布(较小的协方差/更高的精度),以覆盖数据的边缘,并模拟数据停止的事实。

我预计添加足够的高斯分布会导致一种伪Gibbs现象的效果,你可以在从5到99的变化中看到这种情况。要准确模拟边缘,你需要一个无限混合模型。这就像无限频率成分一样——你是在用一组基函数(在这里是高斯分布)来表示一个“信号”,在GMM中也是如此!

5

这里没有适配上的问题,而是你使用的可视化方式有点问题。一个提示是连接(0,1,5)和(0,1,0)的那条直线,这实际上只是两个点之间的连接线(这是因为读取点的顺序造成的)。虽然这条线的两个端点在你的数据中确实存在,但这条线上的其他点实际上并不存在。

我个人认为,用三维图(线条)来表示一个表面并不是个好主意,正是因为上面提到的原因。我建议你使用表面图或轮廓图来代替。

试试这个:

from sklearn import mixture
import numpy as np 
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

line_model = mixture.GMM(n_components = 99)
#Create evenly distributed points between 0 and 1.
xs = np.atleast_2d(np.linspace(0, 1, 100)).T
ys = np.atleast_2d(np.linspace(0, 1, 100)).T

#Create a distribution that's centred along y=x
line_model.fit(np.concatenate([xs, ys], axis=1))
plt.scatter(xs, ys)
plt.show()

#Create the x,y mesh that will be used to make a 3D plot
X, Y = np.meshgrid(xs, ys)
x_y_grid = np.c_[X.ravel(), Y.ravel()]

#Calculate a probability for each point in the x,y grid.
z = line_model.score(x_y_grid)
z = z.reshape(X.shape)

#Plot probabilities on the Z axis.
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, z)
plt.show()

从学术的角度来看,我对用二维混合模型在二维空间中拟合一维线的目标感到不太舒服。使用高斯混合模型(GMM)进行流形学习时,至少需要法向方向的方差为零,这样就会变成一个狄拉克分布。在数值和分析上,这种做法是不稳定的,应该避免(在GMM拟合中似乎有一些稳定化的技巧,因为模型在直线法向方向上的方差相对较大)。

在绘制数据时,建议使用plt.scatter而不是plt.plot,因为在拟合它们的联合分布时,没有必要将这些点连接起来。

希望这些能帮助你更好地理解你的问题。

撰写回答