import numpy as np
a=np.arange(5) # array([0,1,2,3,4])
b=a.copy()
b[0]=100
b[0]의 값을 바꾼 경우 a, b의 값은 어떻게 나올까?print(a) → [0,1,2,3,4]print(b) → [100,1,2,3,4]import numpy as np
a=np.arange(5) # array([0,1,2,3,4])
b=a.view()
b[0]=100
print(a) → [1000,1,2,3,4]print(b) → [100,1,2,3,4]b=a와 같이 썼을 떄 디폴트로 view() 함수처럼 메모리를 공유하도록 설정되어 있다.import numpy as np
import torch
t = np.zeros((4,4,3)) #0으로 채워진 4x4x3 numpy array 생성
ft = torch.FloatTensor(t) #텐서로 변환
print(ft.shape) #torch.Size([4, 4, 3])

## Case 1
print(ft.view([-1, 3])) # ft라는 텐서를 (?, 3)의 크기로 변경
print(ft.view([-1, 3]).shape)
#원소의 개수(4x4x3 = 48 개는) 유치한 채 3차원으로 맞추다보니까 결과적으론 16x3 이 됨.
## Case 2
print(ft.view([-1, 2, 3])) # ft라는 텐서를 (?, 2, 3)의 크기로 변경
print(ft.view([-1, 2, 3]).shape)
import numpy as np
a=np.random.randint(0,10,(2,3))
print("변경 전 a:\n", a)
b[0] = -10
print("변경 후 a:\n", a)
print("b:\n", b)
변경 전 a:
[[7 2 2]
[0 5 8]]
변경 후 a:
[[7 2 2]
[0 5 8]]
b:
[-10 2 2 0 5 8]
In a CNN, the .view() function is preferred over .flatten() for reshaping tensors, especially when moving from convolutional layers to fully connected layers, because .view() offers more control over the reshaping process and can prevent unnecessary memory copies.
- .view() vs. .flatten()
.flatten()
This function collapses all dimensions of a tensor into a single dimension, essentially creating a 1D vector. It's a simple and straightforward way to reshape a tensor, but it might not always be the most efficient way, especially when dealing with complex tensor shapes..view()
This function allows for more fine-grained control over the reshaping process. It can reshape the tensor into any desired shape, as long as the total number of elements remains the same. Importantly, .view() can sometimes avoid creating a new memory copy of the tensor, which can be beneficial for performance, especially with large tensors. If the new shape is compatible with the original tensor's memory layout, .view() will simply provide a different view of the same data, without actually copying it.- Why .view() is preferred in CNNs
- Control over Reshaping:
When transitioning from convolutional layers (which typically output 3D or 4D tensors) to fully connected (dense) layers (which require 1D input), .view() allows you to specify the exact shape you need for the dense layer. You can reshape the tensor to be a single long vector or even a matrix if needed.- Potential Memory Efficiency:
If the new shape you're requesting with .view() is compatible with the original tensor's memory layout, it can avoid creating a new memory copy. This can lead to performance improvements, especially in larger models where memory usage is a concern.- Flexibility:
.view() can handle more complex reshaping scenarios than .flatten(). For instance, you might need to reshape a tensor into a matrix with specific dimensions rather than just a 1D vector.- Example
Let's say you have a 2D convolutional layer output with shape (batch_size, channels, height, width) = (32, 64, 7, 7). To feed this into a fully connected layer, you'd typically reshape it to (batch_size, features) = (32, 64 7 7).
- Using .flatten() would simply flatten the tensor into a vector of size (32, 3136) without any regard for the original structure.
- Using .view(batch_size, -1) (where -1 infers the correct dimension) reshapes it into (32, 3136) as well, but it might have performed a copy.
- Using .view(batch_size, channels height width) would be clearer and potentially more efficient if the memory layout allows.
- In essence, while both functions can achieve the same reshaping, .view() provides greater control and potentially better performance, especially in the context of CNNs where memory management and flexibility are important.
print("변경 전 a:\n", a)
b[0] = -10
print("변경 후 a:\n", a)
print("b:\n", b)
변경 전 a:
[[7 2 2]
[0 5 8]]
변경 후 a:
[[-10 2 2]
[0 5 8]]
b:
[-10 2 2 0 5 8]
.ravel()은 .flatten()과는 다르게 같은 메모리를 공유함.ravel()을 쓰고 싶은데 메모리는 따로 쓰고 싶다면 .copy()를 사용하면 됨 → a.ravel().copy()b.base is a: b의 근간이 a에 있는지 확인torch.view와 torch.reshape의 차이점
contiguous의 의미 (view, reshape, transpose, permute 이해하기)
import torch
x = torch.arange(12)
>>> x
tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
>>> x.reshape(3,4)
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> x.view(2,6)
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.data들이 메모리상에서 실제로 인접해 있는지를 의미

contiguous 여부는 .is_contiguous() 함수로 확인 가능(True/False)
tensor 뒤에 .contiguous()를 사용하여 강제로 contiguous하게 바꿔줄 수 있음
변형하려는 tensor의 상태가 확실하지 않은 경우 reshape 함수를 사용하거나, contiguous 함수를 이용해 tensor의 속성을 바꿔준 뒤 view 함수를 사용하는 것이 좋음
실제 우리가 다루는 array는 메모리에 저장될 때 주소 기반으로 저장됨


첫 번째 방법이든 두 번째 방법이든 우리가 코드 레벨에서 다루는 array는 전혀 변하지 않았지만(똑같은 index를 사용하면 똑같은 값을 얻을 수 있음) 분명 memory level에서는 저장된 순서의 방향이 바뀌었음 → contiguous 개념의 발생
따라서 array level의 순서와 memory level의 순서가 나란히 정렬되어 있을 때는 contiguous, 그렇지 않을 때를 not contiguous라고 정의하고 이를 구분하기 시작함
torch gradient 측면에서는 이러나 저러나 graph가 연결되어 있으니 걱정 안해도 된다.