3

我对 Python 的经验为零。我查看了一些教程材料,但似乎很难理解高级代码。所以我来这里是为了更具体的答案。对我来说,任务是重做我电脑中的代码。

这是场景:

我是一名在关系学习中研究张量分解的研究生。一篇论文[1] 提供了运行该算法的代码,如下:

import logging, time
from numpy import dot, zeros, kron, array, eye, argmax
from numpy.linalg import qr, pinv, norm, inv 
from scipy.linalg import eigh
from numpy.random import rand

__version__ = "0.1" 
__all__ = ['rescal', 'rescal_with_random_restarts']

__DEF_MAXITER = 500
__DEF_INIT = 'nvecs'
__DEF_PROJ = True
__DEF_CONV = 1e-5
__DEF_LMBDA = 0

_log = logging.getLogger('RESCAL') 

def rescal_with_random_restarts(X, rank, restarts=10, **kwargs):
    """
    Restarts RESCAL multiple time from random starting point and 
    returns factorization with best fit.
    """
    models = []
    fits = []
    for i in range(restarts):
        res = rescal(X, rank, init='random', **kwargs)
        models.append(res)
        fits.append(res[2])
    return models[argmax(fits)]

def rescal(X, rank, **kwargs):
    """
    RESCAL 

    Factors a three-way tensor X such that each frontal slice 
    X_k = A * R_k * A.T. The frontal slices of a tensor are 
    N x N matrices that correspond to the adjecency matrices 
    of the relational graph for a particular relation.

    For a full description of the algorithm see: 
      Maximilian Nickel, Volker Tresp, Hans-Peter-Kriegel, 
      "A Three-Way Model for Collective Learning on Multi-Relational Data",
      ICML 2011, Bellevue, WA, USA

    Parameters
    ----------
    X : list
        List of frontal slices X_k of the tensor X. The shape of each X_k is ('N', 'N')
    rank : int 
        Rank of the factorization
    lmbda : float, optional 
        Regularization parameter for A and R_k factor matrices. 0 by default 
    init : string, optional
        Initialization method of the factor matrices. 'nvecs' (default) 
        initializes A based on the eigenvectors of X. 'random' initializes 
        the factor matrices randomly.
    proj : boolean, optional 
        Whether or not to use the QR decomposition when computing R_k.
        True by default 
    maxIter : int, optional 
        Maximium number of iterations of the ALS algorithm. 500 by default. 
    conv : float, optional 
        Stop when residual of factorization is less than conv. 1e-5 by default

    Returns 
    -------
    A : ndarray 
        array of shape ('N', 'rank') corresponding to the factor matrix A
    R : list
        list of 'M' arrays of shape ('rank', 'rank') corresponding to the factor matrices R_k 
    f : float 
        function value of the factorization 
    iter : int 
        number of iterations until convergence 
    exectimes : ndarray 
        execution times to compute the updates in each iteration
    """

    # init options
    ainit = kwargs.pop('init', __DEF_INIT)
    proj = kwargs.pop('proj', __DEF_PROJ)
    maxIter = kwargs.pop('maxIter', __DEF_MAXITER)
    conv = kwargs.pop('conv', __DEF_CONV)
    lmbda = kwargs.pop('lmbda', __DEF_LMBDA)
    if not len(kwargs) == 0:
        raise ValueError( 'Unknown keywords (%s)' % (kwargs.keys()) )

    sz = X[0].shape
    dtype = X[0].dtype 
    n = sz[0]
    k = len(X) 

    _log.debug('[Config] rank: %d | maxIter: %d | conv: %7.1e | lmbda: %7.1e' % (rank, 
        maxIter, conv, lmbda))
    _log.debug('[Config] dtype: %s' % dtype)

    # precompute norms of X 
    normX = [norm(M)**2 for M in X]
    Xflat = [M.flatten() for M in X]
    sumNormX = sum(normX)

    # initialize A
    if ainit == 'random':
        A = array(rand(n, rank), dtype=dtype)
    elif ainit == 'nvecs':
        S = zeros((n, n), dtype=dtype)
        T = zeros((n, n), dtype=dtype)
        for i in range(k):
            T = X[i]
            S = S + T + T.T
        evals, A = eigh(S,eigvals=(n-rank,n-1))
    else :
        raise 'Unknown init option ("%s")' % ainit

    # initialize R
    if proj:
        Q, A2 = qr(A)
        X2 = __projectSlices(X, Q)
        R = __updateR(X2, A2, lmbda)
    else :
        R = __updateR(X, A, lmbda)

    # compute factorization
    fit = fitchange = fitold = f = 0
    exectimes = []
    ARAt = zeros((n,n), dtype=dtype)
    for iter in xrange(maxIter):
        tic = time.clock()
        fitold = fit
        A = __updateA(X, A, R, lmbda)
        if proj:
            Q, A2 = qr(A)
            X2 = __projectSlices(X, Q)
            R = __updateR(X2, A2, lmbda)
        else :
            R = __updateR(X, A, lmbda)

        # compute fit value
        f = lmbda*(norm(A)**2)
        for i in range(k):
            ARAt = dot(A, dot(R[i], A.T))
            f += normX[i] + norm(ARAt)**2 - 2*dot(Xflat[i], ARAt.flatten()) + lmbda*(R[i].flatten()**2).sum()
        f *= 0.5

        fit = 1 - f / sumNormX
        fitchange = abs(fitold - fit)

        toc = time.clock()
        exectimes.append( toc - tic )
        _log.debug('[%3d] fit: %.5f | delta: %7.1e | secs: %.5f' % (iter, 
            fit, fitchange, exectimes[-1]))
        if iter > 1 and fitchange < conv:
            break
    return A, R, f, iter+1, array(exectimes)

def __updateA(X, A, R, lmbda):
    n, rank = A.shape
    F = zeros((n, rank), dtype=X[0].dtype)
    E = zeros((rank, rank), dtype=X[0].dtype)

    AtA = dot(A.T,A)
    for i in range(len(X)):
        F += dot(X[i], dot(A, R[i].T)) + dot(X[i].T, dot(A, R[i]))
        E += dot(R[i], dot(AtA, R[i].T)) + dot(R[i].T, dot(AtA, R[i]))
    A = dot(F, inv(lmbda * eye(rank) + E))
    return A

def __updateR(X, A, lmbda):
    r = A.shape[1]
    R = []
    At = A.T    
    if lmbda == 0:
        ainv = dot(pinv(dot(At, A)), At)
        for i in range(len(X)):
            R.append( dot(ainv, dot(X[i], ainv.T)) )
    else :
        AtA = dot(At, A)
        tmp = inv(kron(AtA, AtA) + lmbda * eye(r**2))
        for i in range(len(X)):
            AtXA = dot(At, dot(X[i], A)) 
            R.append( dot(AtXA.flatten(), tmp).reshape(r, r) )
    return R

def __projectSlices(X, Q):
    q = Q.shape[1]
    X2 = []
    for i in range(len(X)):
        X2.append( dot(Q.T, dot(X[i], Q)) )
    return X2

粘贴这么长的代码很无聊,但没有其他方法可以找出我的问题。我很抱歉这件事。

我根据作者的网站导入这个模块并传递参数:

import pickle, sys
from rescal import rescal

rank = sys.argv[1]
X = pickle.load('us-presidents.pickle')
A, R, f, iter, exectimes = rescal(X, rank, lmbda=1.0)

数据集 us-presidents.rdf 可以在这里找到。

我的问题是:

  1. 根据代码说明,张量 X 是一个列表。我不太明白这一点,如何将列表与 Python 中的张量相关联?我可以理解 Python 中的 tensor = list 吗?
  2. 我应该先将 RDF 格式转换为三元组(主语、谓语、宾语)格式吗?我不确定 X 的数据结构。如何手动为 X 赋值?
  3. 那么,如何运行呢?

未经作者授权粘贴代码,是否侵权?如果是这样,我很抱歉,我会尽快删除它。

这些问题可能有点无聊,但这些对我来说很重要。任何帮助将不胜感激。

[1] Maximilian Nickel, Volker Tresp, Hans-Peter Kriegel, A Three-Way Model for Collective Learning on Multi-Relational Data, 第 28 届机器学习国际会议论文集,2011 年,美国华盛顿州贝尔维尤

4

2 回答 2

1

要回答问题 2:您需要先转换 RDF 并保存它,然后才能从文件“us-presidents.pickle”加载它。该代码的作者可能曾经这样做过,因为 Python 本机 pickle 格式加载速度更快。由于 pickle 格式包含数据的数据类型,因此可能X是一些 numpy 类实例,您需要此代码使用的示例 pickle 文件,或者一些执行 pickle.dump 的代码来确定如何从RDF 到这个特定的 pickle 文件,正如rescal预期的那样。

所以这可能会回答 Q1:张量由元素列表组成。从代码中您可以看到X要重新缩放的参数有一个长度 ( k = len(X)) 并且可以被索引 ( T = X[i])。所以它的元素被用作一个列表(即使它可能是其他一些数据类型,也就是这样。

顺便说一句:如果您不熟悉 Python,并且只对计算结果感兴趣,您可能会获得更多帮助,请联系该软件的作者。

于 2012-06-20T06:50:39.380 回答
1
  1. 根据代码说明,张量 X 是一个列表。我不太明白这一点,如何将列表与 Python 中的张量相关联?我可以理解 Python 中的 tensor = list 吗?

不一定,但代码的作者已决定将张量数据表示为列表数据结构。如评论所示,列表 X 包含:

张量 X 的正面切片 X_k 的列表。每个 X_k 的形状为 ('N', 'N')

这意味着张量被表示为元组列表:[(N, N), ..., (N, N)]

  1. 我不确定 X 的数据结构。如何手动为 X 赋值?

现在我们有了 X 的数据结构,我们可以使用赋值给它赋值。下面将把元组分配给(1, 3)列表 X 中的第一个位置(因为第一个位置在索引 0 处,第二个在位置 1 处,等等):

X[0] = (1, 3)

同样,以下将元组分配(2, 4)到第二个位置:

X[1] = (2, 4)
于 2012-06-20T06:54:50.323 回答