Basic use of Tensorlab

Tags: tensor construction, tensor visualization, cpd, lmlra, mlsvd, initialization, compression

Tensorlab is a Matlab package for complex optimization and tensor computations. This demo will discuss the basics of Tensorlab. It consists of three consecutive parts. A first section Tensor construction and visualization will explain how a tensor can be defined and visualized. The second section CPD for beginners handles the elementary use of the cpd command for the computation of the canonical polyadic decomposition, while the third section CPD for pros discusses more advanced items such as customized initialization methods and algorithms.

A Matlab implementation of this demo is given in the demo_basic.m file, which can be found in the demo folder of Tensorlab or downloaded here.

Tensor construction and visualization

Construction of a tensor with random entries

A random tensor can be constructed by using the basic randn and rand commands in Matlab. For example, we can construct a tensor \(\ten{T}\in\R^{10\times 20\times 30}\) with entries randomly sampled from a Gaussian distribution as follows:

size_tens = [10 20 30];
T = randn(size_tens);

Construction of a tensor as a sum of R rank-1 terms

To construct a third-order tensor \(\ten{T}\in\R^{I\times J \times K}\) which can be written as a sum of \(R\) rank-1 terms, we start with the construction of the three factor matrices \(\mat{U}^{(1)}\in\R^{I\times R}\), \(\mat{U}^{(2)}\in\R^{J\times R}\) and \(\mat{U}^{(3)}\in\R^{K\times R}\), corresponding to the three different modes (see Canonical polyadic decomposition). This can be done with the command cpd_rnd, returning a cell which collects these matrices:

size_tens = [10 20 30];
R = 4;
U = cpd_rnd(size_tens,R); % U is a cell containing the factor matrices

The tensor can then be obtained from the factor matrices using the command cpdgen:

T = cpdgen(U);

For a graphical representation, we refer to Figure 53.

cpd

Figure 53: Visualization of a polyadic decomposition.

Note that it is possible to construct complex factor matrices and tensors too, through the options field of the cpd_rnd command:

size_tens = [10 20 30];
R = 4;
options = struct;
options.Real = @randn;
options.Imag = @randn;
U = cpd_rnd(size_tens,R,options);
T = cpdgen(U);

Alternatively, instead of constructing a structure array, the options can be passed as follows:

U = cpd_rnd(size,tens,R,'Real',@randn,'Imag',@randn);

We will use this method in this demo when the number of options to be passed is small. Note that the option names are case insensitive.

Construction of a tensor obtained by a multilinear transformation of a core tensor

We start with the construction of the core tensor and the transformation matrices using the lmlra_rnd command. The tensor can be obtained through the tensor-matrix product of the core tensor with the different matrices, for which one can use the lmlragen command.

size_tens = [10 20 30];
size_core = [4 5 6];
[F,S] = lmlra_rnd(size_tens,size_core);
% F is a cell collecting the transformation matrices U, V and W
Tlmlra = lmlragen(F,S);

For a graphical representation of the multilinear product, we refer to Figure 54.

lmlra

Figure 54: Visualization of the multilinear product of a core tensor \(\ten{S}\) with factor matrices \(\mat{U}\), \(\mat{V}\) and \(\mat{W}\).

Perturbation of a tensor by noise

A tensor can be perturbed by Gaussian noise with a user-defined signal-to-noise-ratio using the command noisy:

SNR = 20;
Tnoisy = noisy(T,SNR);

Visualization

Tensorlab offers four commands for tensor visualization: slice3, surf3, voxel3 and visualize . The plots of the first two commands contain sliders to go through the different slices. The voxel3 command plots the elements of a tensor as voxels whose color and opacity are proportial to the value of the elements. The plot has two sliders for the threshold parameter and the degree parameter. Figures 55, 56 and 57 visualize the tensor constructed in the following block of code:

t = linspace(0,1,60).';
T = cpdgen({sin(2*pi*t+pi/4),sin(4*pi*t+pi/4),sin(6*pi*t+pi/4)});
figure(); slice3(T);
figure(); surf3(T);
figure(); voxel3(T);
_images/slice3demo.png

Figure 55: Visualization of a tensor using slice3.

_images/surf3demo.png

Figure 56: Visualization of a tensor using surf3.

_images/voxel3demo.png

Figure 57: Visualization of a tensor using voxel3.

Finally, the visualize method can be used to ‘walk through’ the data and to compare tensor a decomposed tensor with its decomposition. For example:

t = linspace(0,1,60).';
U = {sin(2*pi*t+pi/4),sin(4*pi*t+pi/4),sin(6*pi*t+pi/4)};
T = noisy(cpdgen(U), 30);
visualize(U, 'original', T);

The result can be seen in Figure 58. The check boxes can be used to select the dimensions to plot. The sliders or the text fields allow a user to select which slices are plotted. More examples involving the visualize method can be found in the user guide.

_images/visualize.png

Figure 58: Visualization of a tensor using visualize.


CPD for beginners

Let us consider a tensor \(\ten{T}\) that approximately has low rank, constructed for example using one of the techniques from the previous section. We discuss the estimation of the rank, the use of the basic cpd command and the calculation of two different error measures.

Rank estimation

If the rank of a tensor is not known or cannot be obtained from prior knowledge, the command rankest from Tensorlab may be useful. It estimates the corner of the L-curve obtained by sweeping over a number of rank-1 terms. This corner is taken as an estimate of the rank of the tensor. If there is no output argument, an L-curve as in Figure 59 is plotted.

figure();
U = cpd_rnd([10 20 30],4);
T = cpdgen(U);
rankest(T);

Figure 59: The plot generated by the rankest command.

For noisy tensors, it can be worthwhile to edit the relative error tolerance MinRelErr in the options structure of the rankest command.

The basic CPD command

The basic cpd algorithm requires only a tensor and a value for the rank:

R = 4;
Uest = cpd(T,R)

An extra output argument can be provided with [U,output] = cpd(T,R) to obtain information about the convergence of the algorithm, among other things.

Error measures

The relative error on the estimates of the factor matrices can be calculated with the command cpderr. It takes into account the permutation and scaling indeterminacies. The command returns the relative errors (for each factor matrix), the permutation matrix, the scaling matrices and the permuted and scaled factor matrices:

[relerrU,P,D,Uest] = cpderr(U,Uest)
% D is a cell collecting the different scaling matrices

The relative error on the estimate of the tensor can be determined using frob and cpdres, with the latter calculating the residuals:

relerrT = frob(cpdres(T,Uest))/frob(T)

CPD for pros

In general, a cpd computation consists of a compression step, a step in which the CPD of the compressed tensor is computed, and a step in which the estimates are refined using the uncompressed tensor. The user has control over what happens in each step. In this section, we discuss the different steps in more detail.

Note that an overview of the output of each step can be obtained if the Display option is set to true:

Uest = cpd(T,R,'Display',true);

Compression

In the compression step, the tensor is compressed to a smaller tensor using the mlsvd_rsi command by default, which applies a randomized SVD algorithm in combination with randomized subspace iterations. Compression is only performed if it is expected to lead to a significant reduction in computational complexity. The user can alter the compression method with the Compression option by providing a compression technique:

Uest = cpd(T,R,'Compression',@lmlra_aca);

In this example, a low multilinear rank approximation with adaptive cross-approximation is used. The user can also disable the compression step ( note that there will not be a refinement step then):

Uest = cpd(T,R,'Compression',false);

Initialization

To initialize the computation of the CPD of the compressed tensor, cpd uses a method based on the generalized eigenvalue decomposition (implemented in cpd_gevd) if the rank does not exceed the second largest dimension of the tensor. Otherwise, a random initialization is used. The initialization method can be altered with the Initialization option:

Uest = cpd(T,R,'Initialization',@cpd_rnd);

In this example, a random initialization is chosen. The user can also provide particular factor matrices Uinit for the initialization:

Uest = cpd(T,Uinit);

When specific factor matrices are provided, the compression step is skipped.

Core algorithms

Tensorlab provides several core algorithms for the computation of a CPD. First, there are optimization-based methods such as cpd_als (alternating least squares), cpd_minf (unconstrained nonlinear optimization) and cpd_nls (nonlinear least squares). Second, there are (semi-)algebraic methods such as cpd3_sd and cpd3_sgsd. The former reduces the computation to the simultaneous diagonalization of symmetric matrices, also in cases where only one factor matrix has full column rank, while the latter reduces the computation to the estimation of orthogonal factors in a simultaneous generalized Schur decomposition.

By default, cpd_nls is used for the CPD of the compressed tensor and for the refinement. The user can alter the choice of algorithm with the Algorithm and Refinement options. By default, Refinement is equal to Algorithm. The following example uses an alternating least squares approach for the CPD of the compressed tensor and for the refinement:

Uest = cpd(T,R,'Algorithm',@cpd_als);

Core algorithm parameters

Parameters can be passed to the core algorithm with the AlgorithmOptions option for cpd. Tensorlab provides default choices for all parameters.

A first parameter is Display, which determines after how many iterations intermediate results are printed. The first column of the printed results shows the objective function values. The second and third column give the relative change in the objective function values and the step sizes, respectively.

There are three parameters that affect the stopping criterion. The parameter Maxiter determines the maximum number of iterations. The parameters TolX and TolFun are tolerances for the step size and for the relative change in objective function value, respectively. For the NLS algorithm, the CGMaxIter option determines the maximum number of conjugate gradient iterations in each NLS iteration. The following piece of code illustrates how these parameters can be changed for the cpd_nls and cpd_als algorithms:

R = 4;
T = cpdgen(cpd_rnd([10 20 30],R));
Uinit = cpd_rnd([10 20 30],R);

options = struct;
options.Compression = false;
options.Algorithm = @cpd_nls;
options.AlgorithmOptions.Display = 1;
options.AlgorithmOptions.MaxIter = 100;      % Default 200
options.AlgorithmOptions.TolFun = eps^2;     % Default 1e-12
options.AlgorithmOptions.TolX = eps;         % Default 1e-6
options.AlgorithmOptions.CGMaxIter = 20;     % Default 15
[Uest_nls,output_nls] = cpd(T,Uinit,options);

options = struct;
options.Compression = false;
options.Algorithm = @cpd_als;
options.AlgorithmOptions.Display = 1;
options.AlgorithmOptions.MaxIter = 100;      % Default 200
options.AlgorithmOptions.TolFun = eps^2;     % Default 1e-12
options.AlgorithmOptions.TolX = eps;         % Default 1e-6
[Uest_als,output_als] = cpd(T,Uinit,options);

A convergence plot can be obtained from the link in the command terminal output, or by plotting the objective function values from output.Algorithm (and output.Refinement):

figure();
semilogy(output_nls.Algorithm.fval); hold all;
semilogy(output_als.Algorithm.fval);
ylabel('Objective function'); xlabel('Iteration');
title('Convergence plot'); legend('cpd\_nls','cpd\_als')

Figure 60 shows the output of the code block above and illustrates first-order convergence for the ALS algorithm and second-order convergence for the NLS algorithm.

Figure 60: Convergence plot for the cpd_nls and cpd_als algorithm.

Multilinear singular value decomposition and low multilinear rank approximation

Let us consider a tensor \(\ten{T}\in\R^{10\times 20 \times 30}\). The multilinear singular value decomposition (MLSVD) of \(\ten{T}\) is a decomposition of the form

\[\ten{T} = \ten{S}\cdot_1\mat{U}^{(1)}\cdot_2\mat{U}^{(2)}\cdot_3\mat{U}^{(3)},\]

with \(\ten{S}\in\R^{10\times 20 \times 30}\) an all-orthogonal and ordered tensor. The factor matrices \(\mat{U}^{(1)}\in\R^{10\times 10}\), \(\mat{U}^{(2)}\in\R^{20\times 20}\) and \(\mat{U}^{(3)}\in\R^{30\times 30}\) have orthonormal columns. This decomposition can be calculated using the mlsvd command. In the following example, a tensor with random entries is decomposed:

size_tens = [10 20 30];
T = randn(size_tens);
[U,S,sv] = mlsvd(T);

The mode-\(n\) singular values are stored in sv{n}. A truncated MLSVD can be obtained by providing the desired core size as second input argument:

size_core = [4 5 6];
[U,S,sv] = mlsvd(T,size_core);

Note that, due to the truncation, the core tensor may no longer be all-orthogonal and ordered. The truncated MLSVD is just one, not necessarily optimal, way of obtaining a low multilinear rank approximation (LMLRA). A better approximation with the same multilinear rank can be obtained with the lmlra command:

[U,S] = lmlra(T,size_core);

Similar to the cpd command as explained above, the initialization and core algorithm can be altered using the Initialization and Algorithm options. Various LMLRA core algorithms are available; we refer to the user guide and the documentation of the lmlra command for more information. Factor matrices and a core tensor to initialize the algorithm can be provided with lmlra(T,U0,S0). By default, lmlra provides orthogonal factor matrices and an all-orthogonal and ordered core tensor. This step can be skipped by setting the Normalize option to false.

For a more in-depth discussion of MLSVD and LMLRA, we refer to the demo Multilinear singular value decomposition and low multilinear rank approximation.