我试图设计一个测试,以验证tensorly中的partial_tucker
函数是否按我预期的方式工作。换句话说,我想为partial_tucker
函数设计一个输入及其相关的预期输出。
所以,我试图做的是取一个初始随机张量A
(4阶(,用手计算它的"低秩"塔克分解,然后重建与初始张量形状相同的张量,比如A_tilde
。我认为A_tilde
张量就是初始张量A
的"低阶近似"。我说得对吗?
然后我想给我们A_tilde
张量上的partial_tucker
函数,我希望结果与我手工计算的塔克分解相同。事实并非如此,所以我想我手工制作的塔克分解是错误的。如果是,为什么?
import tensorly
import numpy as np
h, w, c, f = 3, 3, 64, 128
c_prim, f_prim = 16, 32
base_tensor = np.random.rand(h, w, c, f)
# compute tucker decomposition by hand using higher order svd describred here: https://www.alexejgossmann.com/tensor_decomposition_tucker/.
lst_fac = []
for k in [2, 3]:
mod_k_unfold = tensorly.base.unfold(base_tensor, k)
U, _, _ = np.linalg.svd(mod_k_unfold)
lst_fac.append(U)
real_in_fac, real_out_fac = lst_fac[0], lst_fac[1]
real_core = multi_mode_dot(base_tensor, [real_in_fac.T, real_out_fac.T], modes=(2,3))
del base_tensor # no need of it anymore
# what i call the "low rank tucker decomposition"
real_core = real_core[:,:,:c_prim,:f_prim]
real_in_fac = real_in_fac[:, :c_prim]
real_out_fac = real_out_fac[:, :f_prim]
# low rank approximation
base_tensor_low_rank = multi_mode_dot(real_core, [real_in_fac, real_out_fac], modes=(2,3))
in_rank, out_rank = c_prim, f_prim
core_tilde, (in_fac_tilde, out_fac_tilde) = partial_tucker(base_tensor_low_rank, modes=(2, 3), ranks=(in_rank, out_rank), init='svd')
base_tensor_tilde = multi_mode_dot(core_tilde, [in_fac_tilde, out_fac_tilde], modes=(2,3))
assert np.allclose(base_tensor_tilde, base_tensor_low_rank) # this is OK
assert np.allclose(in_fac_tilde, real_in_fac) # this fails
注意,我试图计算in_fac_tilde.T @ real_in_fac
,看看它是否是恒等式或类似的东西,我注意到只有第一列在两个矩阵中共线,并且与所有其他矩阵正交。
你在这里隐含地做了很多假设:例如,你假设你可以修剪一个rank-R分解来得到rank-(R-1(分解。这通常不是真的。此外,请注意,您使用的Tucker分解不仅仅是高阶SVD(HO-SVD(。相反,HO-SVD用于初始化,然后进行高阶正交迭代(HOOI(。
您还假设,对于任何给定的秩,低秩分解都是唯一的,这将允许您直接比较分解的因子。事实也并非如此(即使在矩阵的情况下,并且具有强约束(如正交性(,仍然会存在符号不确定性(。
相反,您可以例如检查相对重建误差。我建议你看看TensorLy的测试。如果你从张量开始,这里有很多很好的参考资料。例如,科尔达和巴德的开创性工作;特别是对于Tucker,De Lathauwer等人的工作(例如张量的最佳低秩近似(等。