Concepts / Tensor Shapes and Reshaping

Tensor Shapes and Reshaping

PyTorch supports matrix multiplication through the .matmul method and the @ operator, and the source shows both producing the same result for multiplying a 2D tensor by its transpose.

  • Programming

Predicting a Product Before Running It

Matrix multiplication is a fundamental operation in linear algebra, and PyTorch gives you more than one way to carry it out. This article starts with a short program rather than a definition. Before you read the output, try to work out the answer yourself. The goal is not just to get the right numbers, but to be able to say exactly which numbers produced each value you see.

What do you think happens?

Suppose X is the 2 by 2 matrix with first row 2, 1 and second row 0, 3. What is the product of X and its transpose X-transpose?

  • [[2, 0], [1, 3]]

  • [[5, 3], [3, 9]]

  • [[4, 1], [0, 9]]

  • [[5, 6], [6, 9]]

Reveal answer
Answer:

[[5, 3], [3, 9]]

Each entry pairs one row of X with one row of X (because the columns of the transpose are the rows of X). Row 0 with row 0 gives 2 times 2 plus 1 times 1, which is 5. Row 0 with row 1 gives 2 times 0 plus 1 times 3, which is 3. Row 1 with row 0 also gives 3. Row 1 with row 1 gives 0 times 0 plus 3 times 3, which is 9.

Running the Multiplication Two Ways

The .matmul method is the common way to multiply two matrices in PyTorch. You call it on one tensor and pass the other tensor as the argument. In the program below, the 2D tensor is multiplied by its transpose, written tensor2d.T, which flips rows and columns. The @ operator performs the same multiplication in a more compact form. Both spellings return the matrix product, so for this case you can treat them as interchangeable: one is a method call, the other is an infix operator.

python
Output (expected)
tensor([[14, 32],
        [32, 77]])
tensor([[14, 32],
        [32, 77]])
Notes on the code
  • tensor2d.matmul(tensor2d.T) multiplies the tensor by its transpose using the method form.
  • tensor2d @ tensor2d.T uses the operator form and prints the same tensor.
  • The input values shown here are one choice that produces the printed result 14, 32, 32, 77.

The printed result has four entries: 14, 32, 32, and 77. Notice that the two off-diagonal entries are equal. That is not a coincidence. When you multiply a matrix by its own transpose, the result is always symmetric, so the entry in row i and column j matches the entry in row j and column i. You can check this by looking at the output rather than taking it on trust.

How Each Entry Is Built

Every entry of a matrix product is built from one row of the first matrix and one column of the second matrix. You pair the numbers in that row with the numbers in that column position by position, multiply each pair, and then add the products together. The position of the entry in the output tells you which row and which column were used. In symbols, the entry in row i and column j of a product C = AB is $C_{ij} = \sum_{k} A_{ik} B_{kj}$, which says exactly the same thing: multiply matching pairs and sum them.

When you multiply a matrix by its own transpose, the columns of the second factor are simply the rows of the original matrix. That is why the calculation looks like pairing rows with rows. The top-right entry of the product, for example, comes from the first row of the first matrix paired with the second row of the transpose, which is the second column of the original matrix. Seeing the entry as a row-by-row pairing is the key to reading every result that follows.

Position (0, 0)5 from row 0 and row 0Position (0, 1)3 from row 0 and row 1Position (1, 0)3 from row 1 and row 0Position (1, 1)9 from row 1 and row 1
Which row of X and which row of X-transpose produce each entry of the 2 by 2 result when X is the matrix with rows 2, 1 and 0, 3?

Tracing the Top-Right Entry

Worked example

Computing the top-right entry of the product

Multiply the 2 by 3 tensor with rows 1, 2, 3 and 4, 5, 6 by its transpose, and find the entry in the top-right position, which is row 0 and column 1 of the result.

  1. 1
    Identify the row

    The top-right entry uses row 0 of the first tensor, which is 1, 2, 3.

  2. 2
    Identify the column

    Column 1 of the transpose is the original row 1, which is 4, 5, 6. The transpose turns rows into columns, so the second factor's column is the original second row.

  3. 3
    Pair the values by position

    Pair the first values together, then the second values, then the third: 1 with 4, 2 with 5, and 3 with 6.

  4. 4
    Multiply and add

    The products are 4, 10, and 18. Adding them gives 4 plus 10 plus 18, which equals 32.

Result

The top-right entry is 32, matching the printed output. Written as a sum, the entry is $1 \cdot 4 + 2 \cdot 5 + 3 \cdot 6 = 32$.

The same procedure gives every other entry. The bottom-right entry pairs row 1 with row 1, giving 16 plus 25 plus 36, which equals 77. Because the two entries in the middle both pair row 0 with row 1, they both equal 32. Each position in the output is a self-contained calculation, which is why an error in one entry can be traced back to one specific row pair.

What the Transpose Changes

The transpose is what makes the product of a matrix and its own transpose possible. Transposing flips rows and columns, so a tensor with shape 2 by 3 becomes a tensor with shape 3 by 2, and each value moves to a new position. The value in row 0, column 2 of the original moves to row 2, column 0 of the transpose. Keeping track of where values move is the main way to check that the shapes of your operands are what you think they are.

Original shape2 by 3Transposed shape3 by 2Value 3 at (0, 2)row 0, column 2Value 3 at (2, 0)row 2, column 0
How the shape and the position of a value change when a 2 by 3 tensor is transposed to a 3 by 2 tensor.

Shape Rules for Products

Shapes and order determine whether a product is defined and what shape it has, so you should check the shape of each operand before multiplying. For two matrices, the inner dimensions must match. A tensor with shape m by n can be multiplied by a tensor with shape n by p, and the result has shape m by p. The inner n cancels out in the sense that it is used for pairing, while the outer m and p survive to become the shape of the output.

2 by 3left operand: rows thencolumns@matrix product operator3 by 2inner 3 matches the leftcolumns2 by 2outer dimensions survive
Which dimensions must match for a 2 by 3 tensor times a 3 by 2 tensor, and which dimensions survive into the result?

Mistakes That Hide Shape Errors

  • Common mistake

    Using the * operator when you mean a matrix product

    What it looks like

    tensor2d * tensor2d.T

    Why it fails

    The * operator multiplies matching entries position by position. It is not the row-by-column product that this article describes, so the printed values will not match 14, 32, 32, 77.

    Do this instead

    Use tensor2d @ tensor2d.T or tensor2d.matmul(tensor2d.T) when you want the matrix product.

  • Common mistake

    Forgetting the transpose on the second operand

    What it looks like

    tensor2d @ tensor2d

    Why it fails

    With the tensor of shape 2 by 3, the inner dimensions 3 and 2 do not match, so the product is not defined and the code raises an error.

    Do this instead

    Transpose the second operand so that its rows match the inner dimension, using tensor2d.T.

  • Common mistake

    Assuming an output entry is random when it looks wrong

    Why it fails

    Each entry is a single, repeatable row-by-column calculation, so a wrong value always points to one specific pair of row and column.

    Do this instead

    Find the position of the wrong entry, recompute that row pair by hand, and compare the result to the printed value.

  • Common mistake

    Reading the output shape from the numbers alone instead of from the operand shapes

Predict Before You Run

EASY

Let Y be the 1 by 3 tensor with values 1, 0, 2. Before running any code, predict the shape of Y @ Y.T and the single value it contains. Then check your answer by running the code.

Hints
  • Y.T has shape 3 by 1, so the inner dimensions match and the result has the outer dimensions 1 and 1.
  • There is only one entry, so pair the row of Y with the single column of Y.T position by position and add the products.

Key Takeaways

  • Multiplying a 2D tensor by its transpose with .matmul or the @ operator gives the same matrix product for the same inputs.
  • Each entry of a matrix product comes from pairing one row of the first matrix with one column of the second, multiplying the pairs, and adding the products.
  • The position of an entry in the output tells you which row and which column produced it, so a wrong entry can be traced to one specific calculation.
  • The inner dimensions of two operands must match for a product to be defined, and the outer dimensions become the shape of the result.
  • Predicting the shape and at least one entry before running code gives you a reference point for spotting errors.

Related concepts

More concepts starting with T