我正在尝试在 Java 中实现模糊 C-Means 算法的一个版本,并且我试图通过只计算一次可以计算一次的所有内容来进行一些优化。
这是一个迭代算法,关于矩阵的更新,像素 x 簇成员矩阵U
(一行中的值之和必须为 1.0),这是我要优化的更新规则:
其中 x 是矩阵的元素X
(像素 x 特征),v 属于矩阵V
(簇 x 特征)。是一个参数,m
范围从1.1
到,是簇的数量。使用的距离是欧几里得范数。infinity
c
如果我不得不以一种平庸的方式实现这个公式,我会这样做:
for(int i = 0; i < X.length; i++)
{
int count = 0;
for(int j = 0; j < V.length; j++)
{
double num = D[i][j];
double sumTerms = 0;
for(int k = 0; k < V.length; k++)
{
double thisDistance = D[i][k];
sumTerms += Math.pow(num / thisDistance, (1.0 / (m - 1.0)));
}
U[i][j] = (float) (1f / sumTerms);
}
}
通过这种方式,一些优化已经完成,我预先计算了 和 之间所有可能的平方距离,X
并将V
它们存储在一个矩阵D
中,但这还不够,因为我循环遍历元素V
两次,导致两个嵌套循环。查看公式,分数的分子与总和无关,因此我可以独立计算分子和分母,并且每个像素只需计算一次分母。所以我找到了这样的解决方案:
int nClusters = V.length;
double exp = (1.0 / (m - 1.0));
for(int i = 0; i < X.length; i++)
{
int count = 0;
for(int j = 0; j < nClusters; j++)
{
double distance = D[i][j];
double denominator = D[i][nClusters];
double numerator = Math.pow(distance, exp);
U[i][j] = (float) (1f / (numerator * denominator));
}
}
在计算距离时,我将分母预先计算到矩阵的另一列中D
:
for (int i = 0; i < X.length; i++)
{
for (int j = 0; j < V.length; j++)
{
double sum = 0;
for (int k = 0; k < nDims; k++)
{
final double d = X[i][k] - V[j][k];
sum += d * d;
}
D[i][j] = sum;
D[i][B.length] += Math.pow(1 / D[i][j], exp);
}
}
通过这样做,我遇到了“平庸”计算和第二个计算之间的数值差异,导致不同的数值U
(不是在第一次迭代中,但很快)。我想问题在于将非常小的数字取幂到高值( 的元素U
可以从 0.0 到 1.0 并且exp
,对于m = 1.1
,是10
)导致非常小的值,而通过将分子和分母相除然后取幂,结果似乎在数值上更好。问题是它涉及更多的操作。
更新
我得到的一些价值观ITERATION 0
:
D
这是未优化的矩阵的第一行:
384.6632 44482.727 17379.088 1245.4205
这是矩阵的第一行D
优化方式(注意最后一个值是预先计算的分母):
384.6657 44482.7215 17379.0847 1245.4225 1.4098E-26
这是U
未优化的第一行:
0.99999213 2.3382613E-21 2.8218658E-17 7.900302E-6
这是U
优化后的第一行:
0.9999921 2.338395E-21 2.822035E-17 7.900674E-6
ITERATION 1
:
D
这是未优化的矩阵的第一行:
414.3861 44469.39 17300.092 1197.7633
这是矩阵的第一行D
优化方式(注意最后一个值是预先计算的分母):
414.3880 44469.38 17300.090 1197.7657 2.0796E-26
这是U
未优化的第一行:
0.99997544 4.9366603E-21 6.216704E-17 2.4565863E-5
这是U
优化后的第一行:
0.3220644 1.5900239E-21 2.0023086E-17 7.912171E-6
最后一组值表明,由于传播错误(我仍然希望我犯了一些错误),它们非常不同,甚至违反了这些值的总和必须为 1.0 的约束。
难道我做错了什么?是否有可能的解决方案来优化代码和数值稳定?任何建议或批评将不胜感激。