10

我有一个输入文件,其中包含小数点后 4 位的浮点数:

i.e. 13359    0.0000    0.0000    0.0001    0.0001    0.0002`    0.0003    0.0007    ... 

(第一个是id)。我的班级使用将loadVectorsFromFile其乘以 10000 然后是int()这些数字的方法。最重要的是,我还循环遍历每个向量以确保内部没有负值。但是,当我执行时_hclustering,我不断看到错误"LinkageZ contains negative values"

我认真地认为这是一个错误,因为:

  1. 我检查了我的价值观,
  2. 这些值没有足够小或足够大以接近浮点数的限制,并且
  3. 我用来导出文件中值的公式使用绝对值(我的输入绝对正确)。

有人可以让我了解为什么我会看到这个奇怪的错误吗?是什么导致了这个负距离误差?

=====

def loadVectorsFromFile(self, limit, loc, assertAllPositive=True, inflate=True):
    """Inflate to prevent "negative" distance, we use 4 decimal points, so *10000
    """
    vectors = {}
    self.winfo("Each vector is set to have %d limit in length" % limit)
    with open( loc ) as inf:
        for line in filter(None, inf.read().split('\n')):
            l = line.split('\t')
            if limit:
                scores = map(float, l[1:limit+1])
            else:
                scores = map(float, l[1:])

            if inflate:        
                vectors[ l[0]] = map( lambda x: int(x*10000), scores)     #int might save space
            else:
                vectors[ l[0]] = scores                           

    if assertAllPositive:
        #Assert that it has no negative value
        for dirID, l in vectors.iteritems():
            if reduce(operator.or_, map( lambda x: x < 0, l)):
                self.werror( "Vector %s has negative values!" % dirID)
    return vectors

def main( self, inputDir, outputDir, limit=0,
        inFname="data.vectors.all", mappingFname='all.id.features.group.intermediate'):
    """
    Loads vector from a file and start clustering
    INPUT
        vectors is { featureID: tfidfVector (list), }
    """
    IDFeatureDic = loadIdFeatureGroupDicFromIntermediate( pjoin(self.configDir, mappingFname))
    if not os.path.exists(outputDir):
        os.makedirs(outputDir)

    vectors = self.loadVectorsFromFile( limit, pjoin( inputDir, inFname))
    for threshold in map( lambda x:float(x)/30, range(20,30)):
        clusters = self._hclustering(threshold, vectors)
        if clusters:
            outputLoc = pjoin(outputDir, "threshold.%s.result" % str(threshold))
            with open(outputLoc, 'w') as outf:
                for clusterNo, cluster in clusters.iteritems():
                    outf.write('%s\n' % str(clusterNo))
                    for featureID in cluster:
                        feature, group = IDFeatureDic[featureID]
                        outline = "%s\t%s\n" % (feature, group)
                        outf.write(outline.encode('utf-8'))
                    outf.write("\n")
        else:
            continue

def _hclustering(self, threshold, vectors):
    """function which you should call to vary the threshold
    vectors:    { featureID:    [ tfidf scores, tfidf score, .. ]
    """
    clusters = defaultdict(list)
    if len(vectors) > 1:
        try:
            results = hierarchy.fclusterdata( vectors.values(), threshold, metric='cosine')
        except ValueError, e:
            self.werror("_hclustering: %s" % str(e))
            return False

        for i, featureID in enumerate( vectors.keys()):
4

5 回答 5

9

这是因为浮点数不准确,所以向量之间的一些距离,而不是 0,例如 -0.000000000000000002。使用scipy.clip()函数来纠正问题。如果您的距离矩阵是dmatr,请使用numpy.clip(dmatr,0,1,dmatr),您应该没问题。

于 2012-06-05T16:58:17.610 回答
5

我很确定这是因为您在调用 fclusterdata 时使用的是余弦度量。尝试使用欧几里得,看看错误是否消失。

如果集合中两个向量的点积大于 1,余弦度量可能会变为负数。由于您使用非常大的数字并对它们进行归一化,我很确定点积在很多时候都大于 1在您的数据集中。如果要使用余弦度量,则需要对数据进行归一化,以使两个向量的点积永远不会大于 1。请参阅此页面上的公式以了解余弦度量在 Scipy 中的定义。

编辑:

好吧,通过查看源代码,我认为该页面上列出的公式实际上并不是 Scipy 使用的公式(这很好,因为源代码看起来使用的是正常且正确的余弦距离公式)。但是,当它创建链接时,无论出于何种原因,链接中显然存在一些负值。尝试使用 scipy.spatial.distance.pdist() 和 method='cosine' 查找向量之间的距离并检查负值。如果没有,那么它与如何使用距离值形成链接有关。

于 2010-04-07T05:18:54.663 回答
1

“链接 Z 包含负值”。当链接矩阵中的任何链接集群索引分配为-1时,此错误也会发生在 scipy 层次聚类过程中。

根据我的观察,在组合过程中,任何链接集群索引都被分配-1,当所有集群对或要组合的点之间的距离为负无穷大时。因此,即使它们之间的链接距离是无限的,链接函数也会将集群与它们结合起来。并分配一个集群或点负索引

总结所以重点是,如果您使用余弦距离作为度量,并且如果任何数据点的范数或幅度为零,则会发生此错误

于 2015-06-27T14:05:33.580 回答
1

我遇到过同样的问题。你可以做的是重写余弦函数。例如:

from sklearn.metrics.pairwise import cosine_similarity
def mycosine(x1, x2):
    x1 = x1.reshape(1,-1)
    x2 = x2.reshape(1,-1)
    ans = 1 - cosine_similarity(x1, x2)
    return max(ans[0][0], 0)

...

clusters = hierarchy.fclusterdata(data, threshold, criterion='distance', metric=mycosine, method='average')
于 2016-02-23T08:58:48.280 回答
0

我无法改进贾斯汀的答案,但另一点需要注意的是您的数据处理。

你说你做一些类似int( float("0.0003") * 10000 )读取数据的事情。但如果你这样做,你会得到 not 3but 2.9999999999999996。那是因为浮点数的不准确性只会成倍增加。

更好,或者至少更准确。方法是在字符串中进行乘法运算。也就是说,使用字符串操作来获取 from 0.0003to3.0等等。

也许在某个地方甚至有一个 Python 数据类型扩展,它可以在不损失精度的情况下读取这种数据,您可以在转换前执行乘法运算。我不熟悉 SciPy/numerics,所以我不知道。

编辑

贾斯汀评论说,python 中有一个十进制类型。这可以解释字符串,乘以整数并转换为浮点数(我测试过)。在这种情况下,我建议您更新您的逻辑,例如:

factor = 1
if inflate:
  factor = 10000
scores = map(lambda x: float(decimal.Decimal(x) * factor), l[1:])

这会稍微减少你的舍入问题。

于 2010-04-07T06:16:05.900 回答