17.2 PyTorch 텐서와 autograd

실습 · 연습문제 2개

stride로 view와 contiguous 판정하기 strides-and-views

텐서는 1차원 메모리 위에 shape과 stride(각 축으로 한 칸 갈 때 건너뛰는 원소 수)를 얹은 것이다. transpose·permute는 메모리를 옮기지 않고 stride만 바꾼다. 세 함수를 완성한다. stride는 바이트가 아니라 원소 수 단위다.

  1. row_major_strides(shape) — 새로 만든 행 우선(C 순서) 텐서의 stride. 마지막 축의 stride는 1이고, 앞 축의 stride는 뒤 축들의 길이를 곱한 값이다. 예: (2, 3, 4) → (12, 4, 1), () → ().
  2. permute_strides(shape, strides, dims) — permute(dims) 결과의 (shape, strides). 축의 순서만 바꾼다. 예: shape (2, 3), strides (3, 1), dims (1, 0) → ((3, 2), (1, 3)).
  3. is_contiguous(shape, strides) — 이 shape·stride가 행 우선으로 빈틈없이 놓인 배치와 같은지. 길이가 1인 축의 stride는 무시한다(그 축으로는 움직일 일이 없기 때문이다). PyTorch의 is_contiguous(), NumPy의 flags["C_CONTIGUOUS"]와 같은 판정이다.

각 함수는 tuple을 반환한다(is_contiguous는 bool). 테스트는 NumPy 배열의 실제 stride(arr.strides를 원소 크기로 나눈 값)와 비교한다. view는 contiguous가 아닌 텐서에서 실패할 수 있고, 그때 reshape는 복사를 한다는 본문의 내용을 이 판정으로 설명할 수 있다.

broadcasting의 gradient 되돌리기 unbroadcast-grad

y = x + b에서 b의 shape이 (1, 3)이고 x가 (4, 3)이면, b는 4개 행에 복제되어 더해진다. 같은 b가 여러 번 쓰였으므로 chain rule에 따라 각 사용처의 gradient를 더해야 한다(8.4장의 +=). autograd는 이 일을 자동으로 하지만, 직접 역전파를 쓰거나 custom 연산을 만들 때는 손으로 해야 한다.

함수 unbroadcast(grad, shape)를 완성한다.

  • grad: broadcast된 결과(출력) 쪽 gradient. shape은 출력의 shape이다.
  • shape: 원래 입력의 shape(tuple).
  • 반환: shape 모양의 gradient. 출력에서 broadcast로 늘어난 축을 모두 합해 줄인다.

NumPy broadcasting 규칙(5.3장)을 떠올린다.

  1. 입력의 차원 수가 적으면 앞쪽에 길이 1인 축이 붙은 것으로 본다. 이렇게 앞에 붙은 축은 합한 뒤 없앤다.
  2. 입력에서 길이가 1이었던 축이 출력에서 늘어났다면, 그 축을 합하되 길이 1로 남긴다(keepdims).
grad.shape shape 반환 shape
(4, 3) (1, 3) (1, 3)
(4, 3) (3,) (3,)
(2, 4, 3) (4, 1) (4, 1)
(5,) () (스칼라) ()

테스트는 위 표의 경우들과 무작위 shape에서 수치미분과 비교한다.