텐서는 크게 네 가지 방법을 통해 초기화할 수 있다.
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(x_data.size())
이 경우, 데이터의 타입을 자동으로 유추해서 실행한다.
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
x_ones = torch.ones_like(x_data)
print(f"Ones Tensor: \n {x_ones} \n")
x_rand = torch.rand_like(x_data, dtype=torch.float)
print(f"Random Tensor: \n {x_rand} \n")
torch.ones_like(input)는 input과 동일한 크기를 가지면서 각각의 원소가 1인 텐서를 만들어 반환한다. 이는, torch.ones(input.size(), dtype=input.dtype, layout=input.layout, device=input.device)와 동일하게 동작한다.
rand_tensor = torch.rand(shape) // uniform distribution에서 샘플링
randn_tensor = torch.randn(shape) // standard gaussian에서 샘플링
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
range_tensor = torch.arange(12, dtype=torch.float32)
여기에서 shape는 텐서의 차원을 나타내는 튜플이다. 사실 튜플이 아니라 여러 개의 수를 argument로 줘서 텐서의 차원을 나타낼수도 있다.
텐서의 레이아웃은 텐서의 메모리 구조를 나타내는 개념이다. 밀집도가 높은 텐서(0이 아닌 요소가 대부분인 텐서)의 레이아웃은 tensor.stride() 메소드를 통해 얻을 수 있고 밀집도가 낮은 텐서(0인 요소가 대부분인 텐서)의 레이아웃은 tensor.sparse_coo()을 통해 얻을 수 있다.
stride() 메소드는 튜플을 반환한다. 각 튜플의 k번째 요소는 k번째 축으로 텐서의 한 요소에서 다음 요소로 가기위해 메모리를 얼마나 옮겨야하는지 나타낸다. 이는 텐서의 효율성을 위해 존재한다. 텐서는 ndarray와 동일한 메모리 구조를 갖기에 아마 내부적으로 contiguous한 일차원 배열일 것이다. 하지만 상황에 따라서 non-contiguous한 텐서를 만들수도 있다. 대표적인 경우가 텐서를 만들고 해당 텐서의 transpose를 계산하면 내부적으로 메모리를 재배열하는 것이 비효율적이기에 그대로 내버려둔다. 이러면 non-contiguous한 텐서가 되고 이럴 경우 텐서의 레이아웃이 있어야지만 원하는 요소에 접근할 수 있게된다.
또한 텐서를 반환하는 많은 함수가 optional keyword argument로 layout을 갖는데, 이 argument를 통해 반환될 텐서의 메모리 구조를 정할수도 있다.
torch의 stride 의미
https://stackoverflow.com/questions/56659255/what-does-layout-torch-strided-mean
torch의 ones_like
https://pytorch.org/docs/stable/generated/torch.ones_like.html
contiguous vs non-contiguous 텐서
https://discuss.pytorch.org/t/contigious-vs-non-contigious-tensor/30107