I am trying to use the reshape command in numpy python to perform the unfold operation on a 3rd-rank/mode tensor. I'm not sure whether what I'm doing is correct. I found this paper online Tensor Decomposition. Also found this code: SVD Image Compression in which the author writes:
Color images are represented in python as 3 dimensional numpy arrays — the third dimension to represent the color values (red,green blue). However, svd method is applicable to two dimensional matrices. So we have to find a way to convert the 3 dimensional array to 2 dimensional arrays, apply svd and reconstruct it back as a 3 dimensional array. There are two ways to do it. We will show both these methods below.
- reshape method
- Layer method
Reshape method to compress a color image:
This method involves flattening the third dimension of the image array into the second dimension using numpy’s reshape method .
image_reshaped = image.reshape((original_shape[0],original_shape[1]*3))
I am trying to understand the reshape method. This looks to me like an unfold operation on a 3-rank/mode tensor. Lets say I have an array that is of size NxMxP, which mode would I be unfolding along if I used the following python command: reshape(N, M*P)
?
Here is the way I test the unfold operation:
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()
TL;DR: assuming you are using the default (C-)ordering of elements, then tensor.reshape(N, M*P) corresponds to the unfolding of tensor along its first mode according to the definition used in, for instance, TensorLy.
The long answer is more subtle. There are more than one definition of the unfolding. Generally speaking, an n-mode unfolding corresponds to i) moving the n-th mode to the beginning and ii) reshaping the result into a matrix. The way in which this reshaping is done gives you different definition of unfolding.
First a little bit of terminology: the fiber along the n-th mode (i.e. dimension) of a tensor is obtained by varying the n-th index of the tensor while keeping all other fixed. For matrices, we all know the fibers as rows (vary only the first index) or columns (vary only the second index). This notion generalises to tensors of any order (for third order tensors, the fibers along the third mode are also called tubes).
The n-mode unfolding of a tensor is obtained by stacking the fibers along the n-th mode of the tensor so as to obtain a matrix. The various definitions of the unfolding vary by the ordering of these fibers.
Now on the way tensors are stored: the elements are stored in memory as one long vector, either from the last to the first dimension, or vice-versa. These are known as row-major (or C) and column-major (or Fortran) ordering. When you use reshape
on a tensor you typically read the elements as they are organised in memory.
The most established definition was popularised by Kolda and Bader in their seminal work on tensor decomposition. Their paper Tensor Decompositions and Applications in SIAM REVIEW, 2009, is an excellent introduction. Their definition of the unfolding corresponds to a reshape
of the tensor with a Fortran ordering of the elements. This is the default in Matlab, in which they implemented their method.
Since you mention you are using Python, I'll assume you are using NumPy, and the default ordering of the elements (that is, C-ordering). You can either use a different definition of the unfolding to match that ordering, or use a slightly more complicated function to unfold:
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')
Or, as we did in TensorLy, you can use a definition that matches the C-ordering of the elements.
It doesn't really matter which definition you use as long as you are consistent (though in some cases, the various definitions induces slightly different properties).
Finally, to come back to your first question, if you have an tensor of size (N, M, P) represented as a numpy array, with C-ordering of the elements, then reshape(N, M*P)
gives you the unfolding along the first mode of that tensor using the "TensorLy" definition of unfolding. If you want the "Kolda & Bader" version of unfolding, you can use the f_unfold
function defined above.
Note that, regardless of the definition you use, if you want to unfold along the n-th mode, you first have to put this mode at the beginning before reshaping (for instance using np.moveaxis(tensor, n, 0)
).
I wrote a blog post about the definitions of tensor unfolding if you are interested in the details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With