使用NumPy生成多元正态分布样本时偶现负维度错误排查
这个错误跟你的协方差矩阵半正定与否完全没关系——make_spd_matrix生成的确实是正定矩阵,不会触发这类错误。真正的问题出在你分配给某一类的样本数变成了负数,当np.random.multivariate_normal接收到负数的size参数时,就会抛出这个"negative dimensions are not allowed"的报错。
为什么会出现负数样本数?
大概率是你手动按权重分配样本数时的浮点数精度问题:
比如你可能用了类似n_samples = np.array(weights * N, dtype=int)的代码,浮点数的舍入误差可能让某个权重乘以N后得到一个极小的负数(比如-0.0000000001),或者在调整样本数总和时操作失误——比如当各类样本数总和不等于N,手动增减某个类的数量时不小心让它变成了负数。
举个例子:如果N=20,k=5,你想均匀分配4个样本给每个类,但浮点数计算后可能得到[4,4,4,4,3],总和19,若错误地给某个类减1而非给最后一个类加1,就可能出现负数样本数。
解决方案:
1. 用更可靠的方式生成样本数
直接用np.random.multinomial来分配样本数,它会自动保证总和等于N,且每个类的样本数都是非负整数:
import numpy as np from sklearn.datasets import make_spd_matrix k = 5 N = 20 # 示例:均匀权重,可替换为自定义权重 weights = np.ones(k) / k # 生成每个类的样本数,总和严格等于N n_samples_per_class = np.random.multinomial(N, weights)
2. 手动处理权重的精度问题
如果必须手动计算权重,记得先截断负数并重新归一化,避免出现无效权重:
# 假设weights是自定义计算得到的 weights = ... # 截断负数到0,避免出现负权重 weights = np.clip(weights, 0, 1) # 重新归一化,确保权重总和为1 weights /= weights.sum() # 计算样本数并调整总和 n_samples_per_class = np.array(weights * N, dtype=int) diff = N - n_samples_per_class.sum() if diff != 0: # 将差值加到第一个类,也可选择其他类 n_samples_per_class[0] += diff # 确保所有样本数至少为1,避免空样本 n_samples_per_class = np.maximum(n_samples_per_class, 1)
3. 生成样本前做参数检查
在调用np.random.multivariate_normal前,先确认样本数是正的,避免传入无效参数:
means = [[-20.0, -20.0], [-20.0, -6.666666666666666], [-20.0, 6.666666666666668], [-6.666666666666666, -20.0], [-6.666666666666666, -6.666666666666666]] cov = make_spd_matrix(2) samples = [] for mean, n in zip(means, n_samples_per_class): if n > 0: samples.append(np.random.multivariate_normal(mean, cov, size=n)) samples = np.concatenate(samples)
总结
这个偶现错误完全是样本数分配环节的问题,跟协方差矩阵无关。只要确保每个类的样本数都是正整数,且总和等于N,就能彻底解决这个问题。
内容的提问来源于stack exchange,提问作者Preetom Saha Arko

