AI를 하는데 Slicing을 모른다고?;;

Soeon Park·2025년 9월 20일
post-thumbnail

개요

Slicing은 Python과 Pytorch같은 라이브러리에서 배열이나 텐서의 "특정 부분"을 선택하는 방식임.
따라서, 특히 Logit 계산을 해서 정확한 input이나 loss 등의 입력을 통한 DL 학습에 필수적인 요소이다!!!

헷갈리면 절대 안됨 그러면 그냥 죽음 뿐.
학습을 잘못된 방식으로 하는 것이기 때문이다. ....


기본 슬라이싱 문법

[start:stop:step]
  • start : 슬라이싱을 시작할 인덱스(범위에 포함됨)
  • stop: 슬라이싱을 종료할 인덱스(범위에 포함되지 않음)
  • step: 슬라이싱할 때 건너뛸 간격

위의 값들을 생략하면 다음과 같은 '기본값'으로 들어가게 된다.

  • start: 0
  • stop: 마지막 인덱스
  • step: 1
syntax의미예시
[n:]n부터 끝까지[2:]는 2번 인덱스부터 끝까지
[:n]처음부터 n-1까지[:3]은 0, 1, 2번 인덱스까지
[-n:]뒤에서 n개[:-2]는 뒤에서 2개
[:-n]뒤에서 -n개를 제외한 전부[:-1]는 마지막 하나를 제외한 전부
[::n]n간격으로[::2]는 2칸씩 건너뛰며
[::-1]뒤집기전체를 뒤집음

다차원 배열 슬라이싱

PyTorch같은 DL 라이브러리에서는 다차원 텐서에 대한 슬라이싱이 필요할 때가 있음.
따라서 각 차원마다 별도로 슬라이싱 조건을 지정할 수 있음!

  • [dim1, dim2, ...] 형식으로 각 차원에 대해 슬라이싱 규칙을 지정
  • :: 해당 차원 전체를 선택
  • ...(Ellipsis): 여러 개의 :를 축약한 표현이라 함.
    e.g.,) x[...,-1]은 마지막 차원의 마지막 요소를 의미하되, 앞선 모든 차원 또한 그대로 유지

아래의 예시에서의 모든 전제는 my_tensor.shape(batch_size, sequence_length, feature_dim)이라고 가정하겠다.

슬라이싱의미예시
tensor[:, :, -1]모든 batches, 모든 sequence length에 대해 마지막 feature를 선택
tensor[:, -1, :]모든 batches에 대해 마지막 sequence token의 모든 features를 선택문장 분류에서 마지막 단어의 임베딩 선택
tensor[0, :, :]첫 번째 batch에 대해 모든 데이터를 선택첫 번째 문장 전체를 선택
tensor[:, :5, :]모든 batches에 대해 첫 5번 째 토큰만 선택문장의 앞부분만 사용
tensor[:, -1]my_tensor[:, -1:, :]와 동일
→ 마지막 차원 :가 생략됨

즉, 모든 batches에 대해 마지막 sequence token의 모든 featuers를 선택

슬라이싱과 .view()/.contiguous()

슬라이싱은 원본 tensor의 view를 반환함.
이 '뷰(view)'는 원본 메모리를 공유해서 빠르나, 메모리가 불연속적일 수 있음.

  • contiguous(): 슬라이싱으로 인해 불연속적이게 된 tensor를 메모리 상에 연속적으로 배치된 새로운 tensor로 만듦
    reshapeview를 사용하기 전에 contiguous()를 호출하면 잠재적 오류를 방지할 수 있음.
  • view(): 메모리상 연속적인 tensor에만 사용이 가능함. reshape보다 빠르지만 여전히 제약이 있음.
  • reshape(): tensor의 연속성 여부에 관계없이 작동.
    → 필요한 경우, 메모리를 복사*하므로 view보다 느릴 수 있음

    +) 가끔 성능 최적화를 위해 .contiguous().view()**를 사용할 때도 있음

* 메모리 복사와 속도 관련 문제에 대한 의미
tensor의 경우, reshape()을 사용했을 때 tensor의 데이터가 슬라이싱 후(After)와 같은 상황에서 메모리에서 연속적으로 저장되어있지 않으면, PyTorch는 자동으로 그 데이터를 메모리의 빈 공간에 연속적으로 재배열(contiguous)하며, 복사본을 만듦.
→ 새로운 tensor는 더 이상 원본 tensor와 메모리를 공유하지 않음


** .contiguous().view()를 사용하는 이유
성능과 안정성을 확보하기 위함임.
대개, view()reshape()보다 일반적으로 위의 이유들로 빠르기 때문에,
view()를 사용하고 싶지만 tensor가 연속적인지 확실하지 않은 경우 방어적인 코딩을 하기 좋은 문법

가령 슬라이싱 연산 후, 새로운 tensor가 생성되는게 아닌 원본 tensor의 특정 부분을 가리키는 view를 만들기 때문에 이 과정에서 메모리 주소가 불연속적이게 될 수 있음.

결론적으로, contiguous().view()는 불연속적 tensor을 view() 연산이 가능한 연속적 tensor로 변환하고, 빠르고 효율적 view()연산의 장점을 활용하여 코드의 안정성을 높일 수 있음.

끗 ~.~

profile
베풀기 위해 더 많이 공부하고 성장하기 ᓚᘏᗢ: 공부 정리용

0개의 댓글