Loading...
10/31/2025
transpose() makes tensors non-contiguous (logical ≠ physical order). view() only works with contiguous tensors → RuntimeError. Solution: use reshape() instead of view().
image = torch.tensor([
[1, 2, 3, 4],
[5, 6, 7, 8]
])
image_rotated = image.transpose(0, 1)
image_flat = image_rotated.view(1, 8)
RuntimeError: view size is not compatible with input tensor's size and stride
(at least one dimension spans across two contiguous subspaces).transpose() makes the tensor "non-contiguous"contiguous() does and when you need it
(4, 1) indicates the following: the first value (4) indicates how many elements you need to jump to the right in memory to reach the next row of the matrix. The second value (1) indicates how many elements you need to jump to the right to reach the next column.
[1, 2] (index starts at 0, so second row, third column = the 7):Memory-Index = Row × Stride[0] + Column × Stride[1]
Memory-Index = 1 × 4 + 2 × 1 = 6transpose() method, one might think that the complete data array in memory is restructured, as if we had deleted the old tensor and created a new one, which would then be stored in memory as follows:
transpose() method. Instead, PyTorch takes the original memory arrangement of the tensor and only adjusts the strides.transpose() to (1, 4), which means that PyTorch interprets them as a 4x2 matrix when reading.[2, 1] (third row, second column = the 7):Memory-Index = Row × Stride[0] + Column × Stride[1]
Memory-Index = 2 × 1 + 1 × 4 = 6
image = torch.tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
print(image.is_contiguous()) # True
image_rotated = image.transpose(0, 1)
print(image_rotated)
# tensor([[1, 5],
# [2, 6],
# [3, 7],
# [4, 8]])
print(image_rotated.is_contiguous()) # False
transpose(), the tensor still points to the same data in memory, but due to the changed strides, we read them in a different order..view() on a tensor that is non-contiguous, as in the example at the very beginning, this RuntimeError occurs. The view() method can only process contiguous tensors because it (like transpose()) only changes the strides and not the actual data.image_rotated = image.transpose(0, 1)
# What view(1, 8) would expect:
# One row with the elements in logical order
# → [1, 5, 2, 6, 3, 7, 4, 8]
# What actually lies in memory:
# → [1, 2, 3, 4, 5, 6, 7, 8]
# view() cannot resolve this discrepancy!
image_rotated.view(1, 8) # RuntimeError! ❌.contiguous():image = torch.tensor([
[1, 2, 3, 4],
[5, 6, 7, 8]
])
image_rotated = image.transpose(0, 1)
# Make tensor contiguous again
image_contiguous = image_rotated.contiguous()
print(image_contiguous.is_contiguous())
# Output: True
image_flat = image_contiguous.view(1, 8)
print(image_flat)
# Output: tensor([[1., 5., 2., 6., 3., 7., 4., 8.]])Memory: [1, 2, 3, 4, 5, 6, 7, 8]
Logical: [1, 5, 2, 6, 3, 7, 4, 8]Memory: [1, 5, 2, 6, 3, 7, 4, 8] ← Rearranged!
Logical: [1, 5, 2, 6, 3, 7, 4, 8]Beware: copying costs performance, especially with large tensors.
reshape() instead of view():image = torch.tensor([
[1, 2, 3, 4],
[5, 6, 7, 8]
])
image_rotated = image.transpose(0, 1)
image_flat = image_rotated.reshape(1, 8)
print(image_flat)
# Output: tensor([[1., 5., 2., 6., 3., 7., 4., 8.]])reshape() is an intelligent wrapper that automatically checks whether the tensor is contiguous:view(), no copy needed (very fast)contiguous() and then creates the new shapeRecommendation for production code: usereshape()instead ofview(), as it's more robust and in most cases just as performant. The difference is only measurable with very large tensors and frequent reshaping operations.
transpose() only changes strides (fast), but makes the tensor non-contiguousview() only works with contiguous tensors → RuntimeError with non-contiguous.contiguous().view() — copies data, then reshapesreshape() — intelligent, automatic, recommended