Python 为什么AIC/BIC标准估计给出的高斯混合密度与我的数据很差?

Python 为什么AIC/BIC标准估计给出的高斯混合密度与我的数据很差?,python,scikit-learn,gaussian,kernel-density,Python,Scikit Learn,Gaussian,Kernel Density,我有一个数据集1D link:,它的值从21000到8000000不等。当我绘制对数值的直方图时,我可以看到大致有两个峰值。我尝试使用Python中的sklearn包拟合高斯混合。我试图根据最低的AIC/BIC找到最好的n_组件。对于全协方差_类型,BIC的最佳值为44,AIC i的最佳值为98,最多测试100。但一旦我用了这些数字,我的拟合就很差了。此外,我还测试了所有其他协方差类型,但我无法拟合我的数据。我只试了两次,我的身材好了很多 这是44个组件的图 这是两个组件的图 这是我试图绘制

我有一个数据集1D link:,它的值从21000到8000000不等。当我绘制对数值的直方图时,我可以看到大致有两个峰值。我尝试使用Python中的sklearn包拟合高斯混合。我试图根据最低的AIC/BIC找到最好的n_组件。对于全协方差_类型,BIC的最佳值为44,AIC i的最佳值为98,最多测试100。但一旦我用了这些数字,我的拟合就很差了。此外,我还测试了所有其他协方差类型,但我无法拟合我的数据。我只试了两次,我的身材好了很多

这是44个组件的图

这是两个组件的图

这是我试图绘制的直方图,数据+密度,拟合高斯混合的pdf

clf=GMM(38,covariance_type='full').fit(b)
n, bins, patches = plt.hist(b,bins='auto',density=True,color='#0504aa',alpha=0.7, rwidth=0.85)
xpdf=np.linspace(b.min(),b.max(),len(bins)).reshape(-1,1)
density= np.exp(clf.score_samples(xpdf))
plt.plot(xpdf,density,'-r')
print("Best number of K by BIC is", bics.index(min(bics)))
print("Best number of K by AIC is", aics.index(min(aics)))
在这里,我绘制的柱状图中,bins=50,顶部柱状图用于原始数据集=3915;根据BIC的建议,使用n_分量=44的10000个样本中的底部样本。它看起来很适合GMM44

我的问题是,导致这些结果的错误在哪里?是不是因为我的数据不适合高斯混合?2我的实现有错吗?我感谢您对解决此问题的帮助或建议。通过更新直方图图,GMM似乎很好地拟合了数据。然而,我不明白为什么第一个情节hist+kde不适合。我猜是因为hist和kde使用不同的y刻度,但不确定。
谢谢

有几件事正在进行:

根据我的经验,BIC的额外惩罚通常更好,但是使用完整的贝叶斯模型会给你更多的控制 你的数据不能很好地用高斯近似。40个组件后,BIC不会升高很多,额外的组件只能略微改善贴合度
拟合模型不是确定性的。重新安装模型将导致不同AIC/BIC的参数略有不同。如果你做了一些工作,例如,你的数据似乎是由一些非常常见的值和许多相对罕见的值组成的。这种区别会混淆混合模型,因此我会尝试以不同的方式对待它们。如果你不在乎这个区别,那就像以前一样使用GMM。我注意到这一点的方式是使用越来越精细的直方图分块,并注意到计数保持不变,表示点质量

我们可以通过numpy.unique了解这些是什么,例如:

似乎没有什么好的断点可供使用,所以我任意选择那些出现10倍以上的值作为我将特别处理的流行值,即作为点质量,我们可以通过执行以下操作来提取这些值:

cutoff = 10

popular = set(values[counts > cutoff])
unpopular = [x for x in data if x not in popular]
我们可以将其绘制在直方图中,并将常用值的计数叠加为增量峰值,从而得出:

顶部的箭头表示峰值从图的顶部一直延伸到489个位置,这些位置完全控制着不受欢迎的值,并解释了为什么GMM使用这些数据失败得如此严重,尤其是在日志转换之后

我将使用高斯KDE对不受欢迎的数据进行建模,但如果您愿意,可以使用GMM。使用KDE的一个优点是它的精确性;给定一些数据、内核和带宽,您将始终得到相同的结果。GMM要复杂得多,每次都不可能得到相同的参数。这就是说,SciPy中KDE带宽的参数化是不幸的,但幸运的是,我们不需要太多的控制,因为分布是最复杂的

import scipy.stats as sps

kde = sps.gaussian_kde(np.log(unpopular), 0.2)
你可以设计这个来说服自己它做的是正确的:

x = np.linspace(11, 16, 501)

plt.hist(np.log(unpopular), 50, density=True)
plt.plot(x, kde(x))
但我不会在这里包括它的输出。接下来,我们将获得一些流行值的汇总统计信息,并定义我们的函数以从中提取单个样本:

pop_values = values[counts > cutoff]
pop_counts = counts[counts > cutoff]
pop_weights = pop_counts / sum(pop_counts)

pop_prop = sum(pop_counts) / len(data)

def draw_sample():
    if np.random.rand() < pop_prop:
        return np.random.choice(pop_values, p=pop_weights)
    else:
        return int(np.exp(unpop_kde.resample(1)))

samples_10k = [draw_sample() for _ in range(10000)]

这将生成示例示例示例_值可能是您希望从类似分布中获得的最终结果,但值的变化要大得多。您可能希望使用幂律分布来选择示例类,例如Pitman–Yor进程,而不是我使用的Dirichlet,但它们没有内置到NumPy中,因此需要更多的代码

您有多少数据?你为什么要这么做?i、 e.在您安装模型后,您将如何处理该模型?数据包含3915个点。拟合的原因是从拟合后的高斯模型中采样。此处的数据链接可能会有所帮助,如果您想从中取样,只需使用您可以使用的大部分组件,例如100。否则的话,一个新的选择可能会更容易。当然,计算起来更简单,谢谢!但我想表明,该模型与数据吻合得很好。使用GMM,我可以说我根据BIC->选择了最佳数量的组件,然后显示密度图,以确认拟合。之后,我可以放心地轻松取样。此外,当分量较大时,比如说100,一些高斯分布的概率非常小,比如0.0001,这就像过拟合。谢谢,谢谢。。评论很好,尤其是我对这些东西还不熟悉。关于你显示的代码,我无法得到类似的图
就像你的一样。我刚刚上传了数据集,因为它可能有助于了解它失败的原因。非常感谢,让我澄清一下,因为数据的范围很广21k-8M,所以它可能会被传播。模型应与数据近似。我不一定要像数据集一样采样,否则只需重新采样而不拟合模型。问题1:为什么GMM失败?如果我们使用多个组件,为什么我们不能捕获整个数据集?我真的看不出来。2如果kde很好,为什么我们不使用它而不将数据分为流行/非流行?3 kde是否会导致过度拟合?1数据不是高斯分布!计数的分布几乎是帕累托分布,因此违反了许多常见的假设。2 KDE基本上是一个GMM,有很多成分和固定方差,它仍然假设数据是高斯分布的,因此我的过滤尝试使数据更接近高斯分布,因此不会违反太多的模型假设。这就是为什么你要做中间的图,我包括了直方图的代码是数据,曲线是KDE。它让你看到它不是太合适了,也就是说,对于你的用例来说,它太摇摆了,谢谢。我会试着理解答案+自己试试。但是,如果我们想要一个比您提供的模型更简单的模型,我们可以只使用kde来处理整个数据吗?或者这不会像GMM那样工作?谢谢,正如我说的,KDE基本上是GMM的一个特例。。。因此,同样的免责声明也适用。你的数据不是高斯混合的,所以试图将其建模为高斯混合,总是会在更新时做错误的事情。我已经从中学到了。但是,我想有44个组件的GMM非常适合,请参见更新的直方图。如果是这样的话,我想错误在于用kde绘制直方图
import scipy.stats as sps

kde = sps.gaussian_kde(np.log(unpopular), 0.2)
x = np.linspace(11, 16, 501)

plt.hist(np.log(unpopular), 50, density=True)
plt.plot(x, kde(x))
pop_values = values[counts > cutoff]
pop_counts = counts[counts > cutoff]
pop_weights = pop_counts / sum(pop_counts)

pop_prop = sum(pop_counts) / len(data)

def draw_sample():
    if np.random.rand() < pop_prop:
        return np.random.choice(pop_values, p=pop_weights)
    else:
        return int(np.exp(unpop_kde.resample(1)))

samples_10k = [draw_sample() for _ in range(10000)]
# smaller values will tend to result in more "spikey" classes
alpha = 20

# number of samples to generate
N = 3000

# num components used for finite approximation to dirichlet process, just keep this relatively big
K = 1000

class_values = np.random.lognormal(13, 1, K).astype(int)
class_weight = np.random.dirichlet(np.full(K, alpha/K))
sample_class = np.random.choice(K, N, p=class_weight)
sample_values = class_values[sample_class]