실습: 유용한 함수들
이번에는 마지막으로 다양한 기타 함수들을 살펴보려합니다.
Expand 함수
expand 함수는 차원의 크기가 1인 차원을 원하는 크기로 늘려주는 역할을 수행합니다. 이것은 마치 동일한 텐서를 그냥 반복하여 리스트에 넣고, cat 함수를 해당 차원에 대해서 수행하는 것과 같습니다.
>>> x = torch.FloatTensor([[[1, 2]],
... [[3, 4]]])
>>> print(x.size())
torch.Size([2, 1, 2])
앞서 코드와 같이 두 번째 차원의 크기가 1인 텐서가 존재할 때, 다음의 코드처럼 expand를 수행할 수 있습니다. 여기서 우리는 두 번째 차원의 크기를 3으로 늘리고자 합니다.
>>> y = x.expand(2, 3, 2)
>>> print(y)
tensor([[[1., 2.],
[1., 2.],
[1., 2.]],
[[3., 4.],
[3., 4.],
[3., 4.]]])
>>> print(y.size())
torch.Size([2, 3, 2])
이것을 cat 함수를 통해 구현하면 다음의 코드와 같을 것입니다.
>>> y = torch.cat([x] * 3, dim=1)
Random Permutation 함수
randperm 함수는 랜덤 수열을 생성하는 파이토치 함수입니다. 딥러닝은 랜덤성에 의존하는 부분이 많기 때문에, 필요에 따라 이 함수를 활용할 수 있습니다. 다음의 코드에서 볼 수 있듯이, randperm 함수의 인자로 숫자를 넣어주면, 1부터 해당 숫자까지의 정수를 임의의 순서로 텐서에 넣어 반환합니다.
>>> x = torch.randperm(10)
>>> print(x)
tensor([1, 9, 6, 4, 0, 7, 3, 5, 2, 8])
>>> print(x.size())
torch.Size([10])
Argument Max 함수
argmax 함수는 수식에서도 굉장히 많이 활용될텐데요. 예를 들어 다음의 수식에서 argmax가 의미하는 것은 set $\mathcal{X}$ 에서 뽑을 수 있는 $x$ 값 중에서, 함수 $f$ 의 출력 값을 최대로 만드는 입력을 반환하는 함수입니다.
\[\begin{gathered} \hat{x}=\underset{x\in\mathcal{X}}{\text{argmax}}f(x) \end{gathered}\]중요한 점은 $f$ 의 최대값을 반환하는 것이 아닌, $f$ 를 최대로 만드는 $x$ 값을 반환한다는 점입니다. 이 argmax 함수도 파이토치에서 제공합니다. 다음과 같이 $3\times3\times3$ 크기의 텐서를 만들어봅니다.
>>> x = torch.randperm(3**3).reshape(3, 3, -1)
>>> print(x)
tensor([[[18, 9, 25],
[ 0, 16, 8],
[24, 20, 14]],
[[ 1, 4, 17],
[ 2, 22, 7],
[ 5, 10, 12]],
[[15, 13, 23],
[ 3, 21, 19],
[26, 6, 11]]])
>>> print(x.size())
torch.Size([3, 3, 3])
여기서 다음 코드와 같이 argmax 함수를 활용해봅니다.
>>> y = x.argmax(dim=-1)
>>> print(y)
tensor([[2, 1, 0],
[2, 1, 2],
[2, 1, 0]])
>>> print(y.size())
torch.Size([3, 3])
argmax 함수의 인수로 차원의 인덱스를 -1로 지정해주었기 때문에, 다른 차원들이 같은 값들 중에서 가장 큰 값의 인덱스를 반환합니다. 다른 차원들이 같은 값이란 다음의 예제를 통해 이해할 수 있습니다.
\[\begin{gathered} \text{argmax}(x_{1,2,0}=5,x_{1,2,1}=10,x_{1,2,2}=12)=2 \end{gathered}\]첫 번째 차원은 똑같이 1, 두 번째 차원도 똑같이 2인 값들 중에서 가장 큰 값을 갖는 인덱스를 찾는 것입니다. 따라서 세 값 중 가장 큰 값인 12를 갖는 2번 인덱스가 반환됩니다. 결과적으로 텐서 x에 대해 argmax를 수행한 결과는 앞서와 같습니다.
Top-k 함수
이번에는 argmax의 상위호환 버전인 topk를 소개합니다. 앞서 argmax는 가장 큰 한 개의 값의 인덱스를 반환하는 것이었다면, 이 topk 함수는 가장 큰 k개의 값과 인덱스 모두 반환합니다. 다음의 코드는 앞서 선언한 텐서 x에 대해서 topk 함수를 수행한 결과입니다.
>>> values, indices = torch.topk(x, k=1, dim=-1)
>>> print(values.size())
torch.Size([3, 3, 1])
>>> print(indices.size())
torch.Size([3, 3, 1])
topk 함수는 상위 k개 값과 인덱스 모두 반환하기 때문에, 반환 값을 튜플로 받는 것을 볼 수 있습니다. 그리고 현재는 $k=1$ 이므로, values와 indices의 마지막 차원의 크기가 1로 되어 있습니다. 만약 k를 1보다 더 큰 값을 쓸 경우, 반환되는 텐서의 크기는 다음과 같이 바뀝니다.
>>> _, indices = torch.topk(x, k=2, dim=-1)
>>> print(indices.size())
torch.Size([3, 3, 2])
Sort 함수
파이토치는 정렬 함수도 제공합니다. 다음은 앞서 선언한 텐서 x를 원하는 차원 기준으로 정렬 후, k개를 뽑아오는 파이토치 코드입니다. 즉, 결과물은 topk함수와 같을 것입니다.
>>> k = 1
>>> values, indices = torch.sort(x, dim=-1, descending=True)
>>> values, indices = values[:, :, :k], indices[:, :, :k]
>>> print(values.squeeze(-1))
tensor([[25, 16, 24],
[17, 22, 12],
[23, 21, 26]])
>>> print(indices.squeeze(-1))
tensor([[2, 1, 0],
[2, 1, 2],
[2, 1, 0]])
마찬가지로 topk를 통해 sort 함수를 구현할 수 있습니다.
>>> target_dim = -1
>>> values, indices = torch.topk(x,
k=x.size(target_dim),
largest=True)
>>> print(values)
tensor([[[25, 18, 9],
[16, 8, 0],
[24, 20, 14]],
[[17, 4, 1],
[22, 7, 2],
[12, 10, 5]],
[[23, 15, 13],
[21, 19, 3],
[26, 11, 6]]])
결과물을 살펴보면 마지막 차원 내에서만 내림차순으로 정렬되어 있는 것을 볼 수 있습니다.
Masked Fill 함수
이번에는 텐서 내의 원하는 부분만 특정 값으로 바꿔치기하는 함수인 masked_fill 함수를 이야기해보겠습니다. 다음의 코드는 $3\times3$ 크기의 텐서를 내부 값을 0부터 8까지 순서대로 갖도록 생성하는 코드입니다.
>>> x = torch.FloatTensor([i for i in range(3**2)]).reshape(3, -1)
>>> print(x)
tensor([[0., 1., 2.],
[3., 4., 5.],
[6., 7., 8.]])
>>> print(x.size())
torch.Size([3, 3])
그리고 논리 연산자를 통해 불리언boolean 텐서를 만들어봅니다.
>>> mask = x > 4
>>> print(mask)
tensor([[False, False, False],
[False, False, True],
[ True, True, True]])
그럼 이 마스크mask를 통해서 masked_fill 함수를 수행한다면, 4 보다 큰 값을 갖는 요소들을 특정 값으로 치환할 수 있게 될 것입니다. 다음의 코드는 4보다 큰 값을 모두 -1로 한번에 치환하도록 적용한 코드입니다.
>>> y = x.masked_fill(mask, value=-1)
>>> print(y)
tensor([[ 0., 1., 2.],
[ 3., 4., -1.],
[-1., -1., -1.]])
Ones & Zeros 함수
딥러닝을 진행하다보면, 상수 값으로된 텐서가 필요할 때가 있습니다. ones 함수와 zeros 함수를 통해 쉽게 구현할 수 있습니다. 다음의 코드는 1로 가득찬 $2\times3$ 텐서와 0으로 가득찬 같은 크기의 텐서를 구하는 코드입니다.
>>> print(torch.ones(2, 3))
tensor([[1., 1., 1.],
[1., 1., 1.]])
>>> print(torch.zeros(2, 3))
tensor([[0., 0., 0.],
[0., 0., 0.]])
또는 ones_like 와 zeros_like 함수를 통해서 특정 텐서와 같은 크기의 0 또는 1 텐서를 만들 수도 있습니다.
>>> x = torch.FloatTensor([[1, 2, 3],
... [4, 5, 6]])
>>> print(x.size())
torch.Size([2, 3])
>>> print(torch.ones_like(x))
tensor([[1., 1., 1.],
[1., 1., 1.]])
>>> print(torch.zeros_like(x))
tensor([[0., 0., 0.],
[0., 0., 0.]])
이외에도 다양한 함수들이 매우 많이 존재하기 때문에, 파이토치 공식 문서(https://pytorch.org/docs/stable/tensors.html)를 참고하면 굉장히 유용합니다.