【问题标题】:Why does np.array_equal return False for these two sparse arrays that are visually equal?为什么 np.array_equal 对于这两个在视觉上相等的稀疏数组返回 False?
【发布时间】:2018-03-10 07:13:39
【问题描述】:

我有两种计算数组单位向量的方法,这两种方法都处理稀疏数组。其中一个非常“手动”计算,而另一个更“正式”(来自 gensim.matutils 源代码)。

这是手动方法:

def manual_unitvec(vec):
        vec = vec.tocsr()
        if sparse.issparse(vec):
            vec_sum_of_squares = vec.multiply(vec)
            unit = 1. / np.sqrt(vec_sum_of_squares.sum())
            return vec.multiply(unit)
        elif not sparse.issparse(vec):
            sum_vec_squared = np.sum(vec ** 2)
            vec /= np.sqrt(sum_vec_squared)
            return vec

这是修改后的gensim方法,其中显式计算单位向量的方法是unitvec

import numpy as np
from scipy import sparse
from gensim.matutils import ret_normalized_vec, blas
import scipy.sparse

blas_nrm2 = blas('nrm2', np.array([], dtype=float))
blas_scal = blas('scal', np.array([], dtype=float))


def unitvec(vec, norm='l2'):
    """Scale a vector to unit length.
    Parameters
    ----------
    vec : {numpy.ndarray, scipy.sparse, list of (int, float)}
        Input vector in any format
    norm : {'l1', 'l2'}, optional
        Normalization that will be used.
    Returns
    -------
    {numpy.ndarray, scipy.sparse, list of (int, float)}
        Normalized vector in same format as `vec`.
    Notes
    -----
    Zero-vector will be unchanged.
    """
    if norm not in ('l1', 'l2'):
        raise ValueError("'%s' is not a supported norm. Currently supported norms are 'l1' and 'l2'." % norm)

    if scipy.sparse.issparse(vec):
        print("INSIDE SPARSE HANDLING")
        vec = vec.tocsr()
        if norm == 'l1':
            veclen = np.sum(np.abs(vec.data))
        if norm == 'l2':
            veclen = np.sqrt(np.sum(vec.data ** 2))
        if veclen > 0.0:
            if np.issubdtype(vec.dtype, np.int) == True:
                vec = vec.astype(np.float)
                return vec / veclen
            else:
                vec /= veclen
                return vec.astype(vec.dtype)
        else:
            return vec

    if isinstance(vec, np.ndarray):
        print("INSIDE NORMAL VEC HANDLING")
        vec = np.asarray(vec, dtype=vec.dtype)
        if norm == 'l1':
            veclen = np.sum(np.abs(vec))
        if norm == 'l2':
            veclen = blas_nrm2(vec)
        if veclen > 0.0:
            if np.issubdtype(vec.dtype, np.int) == True:
                vec = vec.astype(np.float)
                return blas_scal(1.0 / veclen, vec).astype(vec.dtype)
            else:
                return blas_scal(1.0 / veclen, vec).astype(vec.dtype)
        else:
            return vec

    try:
        first = next(iter(vec))  # is there at least one element?
    except StopIteration:
        return vec

    if isinstance(first, (tuple, list)) and len(first) == 2:  # gensim sparse format
        print("INSIDE GENSIM SPARSE FORMAT HANDLING")
        if norm == 'l1':
            length = float(sum(abs(val) for _, val in vec))
        if norm == 'l2':
            length = 1.0 * math.sqrt(sum(val ** 2 for _, val in vec))
        assert length > 0.0, "sparse documents must not contain any explicit zero entries"
        return ret_normalized_vec(vec, length)
    else:
        raise ValueError("unknown input type")

在运行测试时,我想检查每个方法的输出是否相同。以下是示例代码的 sn-p:

vec = sparse.csr_matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).astype(np.float32)
output1 = manual_unitvec(vec)
output2 = unitvec(vec)
print(output1)
print(' ')
print(output2) 
print(np.array_equal(output1, output2))
print(type(output1) == type(output2))

所以我要检查的是 assertTrue(output1, output2)。你不能这样做,因为数组的真值是不明确的,所以我使用assertTrue(np.array_equal(output1, output2))

现在的问题是 array_equal 不认为 output1 和 output2 是相同的,即使我可以从打印出来的结果中看到它们是相同的。

运行上面的所有代码会得到以下输出:

MacBook-Air:matutils.unitvec Olly$ python try.py
INSIDE SPARSE HANDLING
try.py:80: FutureWarning: Conversion of the second argument of issubdtype from `int` to `np.signedinteger` is deprecated. In future, it will be treated as `np.int64 == np.dtype(int).type`.
  if np.issubdtype(vec.dtype, np.int) == True:
  (0, 0)    0.059234887
  (0, 1)    0.118469775
  (0, 2)    0.17770466
  (1, 0)    0.23693955
  (1, 1)    0.29617444
  (1, 2)    0.35540932
  (2, 0)    0.4146442
  (2, 1)    0.4738791
  (2, 2)    0.53311396

  (0, 0)    0.059234887
  (0, 1)    0.118469775
  (0, 2)    0.17770466
  (1, 0)    0.23693955
  (1, 1)    0.29617444
  (1, 2)    0.35540932
  (2, 0)    0.4146442
  (2, 1)    0.4738791
  (2, 2)    0.53311396
/Users/Olly/anaconda2/lib/python2.7/site-packages/scipy/sparse/compressed.py:226: SparseEfficiencyWarning: Comparing sparse matrices using == is inefficient, try using != instead.
  " != instead.", SparseEfficiencyWarning)
False
True

我认为问题可能来自稀疏数组类型,但正如您所见,它们是相等的。您还可以直观地看到元素完全相同。

那么为什么 array_equal 返回 false?怎么改?

【问题讨论】:

  • 您可能需要注意一些警告。另外,请改用np.allclose(output1, output2)
  • 看来np.array_equal 不是为处理稀疏数组而设计的。 this answer 会完成你想做的事情吗? np.allclose(output1, output2)
  • 作为一般规则np. 函数不适用于稀疏矩阵。您必须使用稀疏函数或方法(或委托给稀疏方法的 numpy 函数)。稀疏矩阵不是ndarray 的子类。

标签: python numpy sparse-matrix


【解决方案1】:

在你的第一个函数中你这样做:

    vec = vec.tocsr()
    if sparse.issparse(vec):

我认为issparse 测试对您没有任何帮助。如果输入参数是一个稀疏矩阵,它有一个tocsr 方法,结果是稀疏矩阵。如果vecndarray 则没有tocsr 方法,第一行会抛出错误。

在该函数的其余部分,稀疏矩阵有一个multiply 方法和一个sum 方法。 sum 的结果很密集,所以 np.sqrt 可以正常工作。实际上np.sqrt(M) 也适用于稀疏矩阵,因为M.sqrt 存在。

在第二个函数中,您使用data 属性,它是一维ndarray

np.sum(np.abs(vec.data))

没关系。但请注意,稀疏矩阵的M.__abs__

self._with_data(abs(self._deduped_data()))

在稍微更圆一点的方式中,abssqrt 等功能/方法也可以与 .data 属性一起使用。只有它们返回一个新的稀疏矩阵。

至于测试看np.array_equal

return bool(asarray(a1 == a2).all())

如果我尝试在output1 上使用它(我不会尝试您的gensim 解决方案

In [106]: np.array_equal(output1, output1)
/usr/local/lib/python3.5/dist-packages/scipy/sparse/compressed.py:226: SparseEfficiencyWarning: Comparing sparse matrices using == is inefficient, try using != instead.
  " != instead.", SparseEfficiencyWarning)
Out[106]: False

它不喜欢在稀疏矩阵上使用==。通常这些很大,有很多 0。这意味着所有这些结果将是True,因此不再稀疏。

您的output1 是一个稀疏矩阵,但是,至少对于这些输入,不是稀疏的:

In [107]: output1.A
Out[107]: 
array([[0.05923489, 0.11846977, 0.17770466],
       [0.23693955, 0.29617444, 0.35540932],
       [0.4146442 , 0.4738791 , 0.53311396]], dtype=float32)

但即使你绕过稀疏位,np.array_equal(output1.A, output2.A) 也可能由于浮点数的比较而失败

密集版本上的allclose 可能是最简单的测试:

In [113]: np.allclose(output1.A, output1.A)
Out[113]: True

您还可以比较data(假设稀疏度相同):

In [114]: np.allclose(output1.data, output1.data)
Out[114]: True

更全面的稀疏测试需要检查shapennzindices 属性。

实际上,我不确定np.array_equal 的失败之处。请注意,它以a1=asarray(a1) 开头,它生成一个 0d 对象 dtype 数组。这是坚持将其输入视为数组的numpy 函数之一。它不是稀疏感知的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 2016-04-21
    • 2017-09-21
    • 1970-01-01
    • 2015-07-08
    相关资源
    最近更新 更多