使用numpy重塑执行三阶张量展开操作

vas*_*kan 3 python numpy image-processing linear-algebra tensor

我试图在numpy python中使用reshape命令在第3级/模式张量上执行展开操作。我不确定我在做什么是否正确。我在网上找到了此张量分解。还发现以下代码:作者在其中编写的SVD图像压缩

彩色图像在python中表示为3维numpy数组-三维表示颜色值(红色,绿色,蓝色)。但是,svd方法适用于二维矩阵。因此,我们必须找到一种将3维数组转换为2维数组,应用svd并将其重新构造回3维数组的方法。有两种方法可以做到这一点。我们将在下面显示这两种方法。

  • 重塑方法
  • 分层方法

重塑方法以压缩彩色图像:

该方法涉及使用numpy的reshape方法将图像数组的第三维展平为第二维。

image_reshaped = image.reshape((original_shape[0],original_shape[1]*3))

我正在尝试了解重塑方法。在我看来,这就像是对3级/模式张量进行的展开操作。可以说我有一个数组,它是尺寸NxMxP,该模式会如果我用下面蟒指令I被展开沿着:reshape(N, M*P)

这是我测试展开操作的方式:

import cv2
import numpy as np

def m_unfold(thrd_order_tensor,m):
matrix = []
if m == 1:
    matrix = thrd_order_tensor.reshape((thrd_order_tensor.shape[0], thrd_order_tensor.shape[1]*3))
    #matrix = np.hstack([thrd_order_tensor[:, :, i] for i in range(thrd_order_tensor.shape[2])])
if m == 2:
    matrix = thrd_order_tensor.reshape((thrd_order_tensor.shape[1], thrd_order_tensor.shape[0]*3))
    #matrix = np.hstack([thrd_order_tensor[:, :, i].T for i in range(thrd_order_tensor.shape[2])])
if m == 3:
    matrix = thrd_order_tensor.reshape((3, thrd_order_tensor.shape[0]*thrd_order_tensor.shape[1]))
    #matrix = np.vstack([thrd_order_tensor[:, :, i].ravel() for i in range(thrd_order_tensor.shape[2])])
return matrix

def fold(matrix, os):
#os is the original shape
tensor = matrix.reshape(os)
return tensor

im = cv2.imread('target.jpg')
original_shape = im.shape
image_reshaped = m_unfold(im,3)
U, sig, V = LA.svd(image_reshaped, full_matrices=False)
img_restrd = np.dot(U[:,:], np.dot(np.diag(sig[:]), V[:,:]))
img_restrd = fold(img_restrd,original_shape)
img_restrd = img_restrd.astype(np.uint8)
cv2.imshow('image',img_restrd)
cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

小智 5

TL; DR:假设您使用的是元素的默认(C-)排序,则tensor.reshape(N,M * P)对应于根据例如TensorLy中使用的定义的张量沿其第一个模式的展开。


长答案更加微妙。关于展开的定义不止一个。一般而言,n模式展开对应于i)将第n个模式移至开头,以及ii)将结果重塑为矩阵。重塑的完成方式为您提供了不同的展开定义。

首先是一点术语:沿着张量的第n模(即尺寸)的光纤是通过改变张量的第n个折射率而保持所有其他固定不变而获得的。对于矩阵,我们都将纤维称为行(仅改变第一个索引)或列(仅改变第二个索引)。这个概念可以推广到任何阶数的张量(对于三阶张量,沿着第三模式的光纤也称为管)。

张量的n模式展开是通过将纤维沿着张量的n模式堆叠以获得矩阵而获得的。展开的各种定义随这些纤维的顺序而变化。

现在,在存储张量的过程中:将元素作为一个长矢量存储在内存中,从最后到第一维,反之亦然。这些称为行优先(或C)和列优先(或Fortran)排序。使用reshape张量时,通常会读取元素在内存中组织的形式。

最确定的定义在Kolda和Bader关于张量分解的开创性工作中得到了普及。他们在2009年SIAM REVIEW上发表的论文Tensor Decompositions and Applications非常出色。它们对展开的定义reshape与具有元素的Fortran顺序的张量的a 相对应。这是Matlab中实现其方法的默认设置。

既然您提到您使用的是Python,我将假设您使用的是NumPy,以及元素的默认顺序(即C顺序)。您可以使用不同的展开定义来匹配该顺序,也可以使用稍微复杂一些的功能来展开:

import numpy as np

def f_unfold(tensor, mode=0):
    """Unfolds a tensors following the Kolda and Bader definition

        Moves the `mode` axis to the beginning and reshapes in Fortran order
    """
    return np.reshape(np.moveaxis(tensor, mode, 0), 
                      (tensor.shape[mode], -1), order='F')
Run Code Online (Sandbox Code Playgroud)

或者,就像我们在TensorLy中所做的那样,您可以使用与元素的C顺序匹配的定义。

只要您保持一致,使用哪个定义就无关紧要(尽管在某些情况下,各种定义会导致稍微不同的属性)。

最后,回到您的第一个问题,如果您有一个大小为(N,M,P)的张量表示为numpy数组,且元素的顺序为C,那么reshape(N, M*P)您将沿着该张量的第一个模式展开使用展开的“ TensorLy”定义。如果要展开“ Kolda&Bader”版本,可以使用f_unfold上面定义的功能。

请注意,无论使用什么定义,如果要沿第n个模式展开,则首先必须在重塑(例如使用np.moveaxis(tensor, n, 0))之前将此模式放在开头。

如果您对细节感兴趣,我写了一篇关于张量展开定义的博客文章