我有一个theano符号矩阵
x = T.fmatrix('input')
x
稍后将由n
dim d
向量填充(在火车时间)。
我希望theano相当于pdist
(scipy.spatial.distance.pdist
pdist
),类似
D = theano.pdist( x )
我怎样才能做到这一点?
直接在scipy.spatial.distance.pdist
上调用x
无法正常工作,因为x
此阶段仅具有象征意义......
更新:我非常希望能够模仿pdist
“紧凑”行为:也就是说,只计算n
x {的{1/2} {1}}距离矩阵的条目。
答案 0 :(得分:14)
pdist
是一系列不同的函数 - 并没有同时存在所有这些函数的Theano等价物。但是,每个特定的距离,作为封闭形式的数学表达式,都可以在Theano中写下来然后编译。
以minkowski p
范数距离(复制+可粘贴)为例:
import theano
import theano.tensor as T
X = T.fmatrix('X')
Y = T.fmatrix('Y')
P = T.scalar('P')
translation_vectors = X.reshape((X.shape[0], 1, -1)) - Y.reshape((1, Y.shape[0], -1))
minkowski_distances = (abs(translation_vectors) ** P).sum(2) ** (1. / P)
f_minkowski = theano.function([X, Y, P], minkowski_distances)
请注意,abs
会调用内置__abs__
,因此abs
也是一个theano函数。我们现在可以将其与pdist
进行比较:
import numpy as np
from scipy.spatial.distance import pdist
rng = np.random.RandomState(42)
d = 20 # dimension
nX = 10
nY = 30
x = rng.randn(nX, d).astype(np.float32)
y = rng.randn(nY, d).astype(np.float32)
ps = [1., 3., 2.]
for p in ps:
d_theano = f_minkowski(x, x, p)[np.triu_indices(nX, 1)]
d_scipy = pdist(x, p=p, metric='minkowski')
print "Testing p=%1.2f, discrepancy %1.3e" % (p, np.sqrt(((d_theano - d_scipy) ** 2).sum()))
这会产生
Testing p=1.00, discrepancy 1.322e-06
Testing p=3.00, discrepancy 4.277e-07
Testing p=2.00, discrepancy 4.789e-07
正如您所看到的那样,对应关系存在,但函数f_minkowski
略微更为通用,因为它比较了两个可能不同的数组的行。如果两次相同的数组作为输入传递,f_minkowski
返回一个矩阵,而pdist
返回一个没有冗余的列表。如果需要这种行为,它也可以完全动态地实现,但我将坚持这里的一般情况。
应该注意一种专业化的可能性:在p=2
的情况下,通过二项式公式计算变得更简单,这可以用来节省内存中的宝贵空间:而一般的Minkowski距离,如上面实现的,创建一个3D数组(由于避免for循环和累积求和),这取决于维d
(和nX, nY
),p=2
我们可以写
squared_euclidean_distances = (X ** 2).sum(1).reshape((X.shape[0], 1)) + (Y ** 2).sum(1).reshape((1, Y.shape[0])) - 2 * X.dot(Y.T)
f_euclidean = theano.function([X, Y], T.sqrt(squared_euclidean_distances))
仅使用O(nX * nY)
空格而非O(nX * nY * d)
我们检查通信,这次是关于一般问题:
d_eucl = f_euclidean(x, y)
d_minkowski2 = f_minkowski(x, y, 2.)
print "Comparing f_minkowski, p=2 and f_euclidean: l2-discrepancy %1.3e" % ((d_eucl - d_minkowski2) ** 2).sum()
产生
Comparing f_minkowski, p=2 and f_euclidean: l2-discrepancy 1.464e-11
答案 1 :(得分:-2)
我以前没有和Theano一起工作过,但是这里有一个基于纯Numpy功能的解决方案(也许你将它转换为等效的theano功能。注意我使用自动广播在下面的表达式中,所以如果Theano不支持它,你可能必须明确地重写它:)
# X is an m-by-n matrix (rows are examples, columns are dimensions)
# D is an m-by-m symmetric matrix of pairwise Euclidean distances
a = np.sum(X**2, axis=1)
D = np.sqrt((a + a[np.newaxis].T) - 2*np.dot(X, X.T))
这是基于以下事实:||u-v||^2 = ||u||^2 + ||v||^2 - 2*u.v
。 (我在我的previous answers中使用MATLAB显示了这一点
以下是对Scipy现有函数的比较:
import numpy as np
from scipy.spatial.distance import pdist, squareform
def my_pdist(X):
a = np.sum(X**2, axis=1)
D = np.sqrt((a + a[np.newaxis].T) - 2*np.dot(X, X.T))
return D
def scipy_pdist(X):
D = squareform(pdist(X, metric='euclidean'))
return D
X = np.random.rand(5, 3)
D1 = my_pdist(X)
D2 = scipy_pdist(X)
差异应该可以忽略不计,接近机器epsilon(np.spacing(1)
):
>>> np.linalg.norm(D1-D2)
8.5368137554718277e-16
HTH
以下是使用单个循环的另一个实现:
def my_pdist_compact(X):
D = np.empty(shape=[0,0], dtype=X.dtype)
for i in range(X.shape[0]-1):
D = np.append(D, np.sqrt(np.sum((X[i,] - X[i+1:,])**2, axis=1)))
return D
有点等效的MATLAB代码:
function D = my_pdist_compact(X)
n = size(X,1);
D = cell(n-1,1);
for i=1:n-1
D{i} = sqrt(sum(bsxfun(@minus, X(i,:), X(i+1:end,:)).^2, 2));
end
D = vertcat(D{:});
end
以紧凑的形式返回成对距离(对称矩阵的上三角部分)。这与pdist
的输出相同。使用squareform
将其转换为完整矩阵。
>>> d1 = my_pdist_compact(X)
>>> d2 = pdist(X) # from scipy.spatial.distance
>>> (d1 == d2).all()
True
我会留给你看看是否可以使用Theano写出等效的loop(见theano.scan
)!