torch

Leejaegun·2025년 4월 23일

Python & etc

목록 보기
25/27

torch 내부에 있는 함수 끌어쓰기. 특히 torch.view와 size는 많이 쓰므로 알아두는것이 좋음.
unsqueeze같은것도 가끔보임. 하지만 이것들 모두 외울 필요는 없고 느낌(vibe)만 가져다 주면 좋을것임.

1. torch.randperm(n)

  • 정의: 0 ~ n-1 까지의 정수들을 무작위로 섞은 텐서 생성
  • 용도: 인덱스 섞기, 셔플 등
import torch
idx = torch.randperm(5)
print(idx)  # 예: tensor([2, 4, 0, 1, 3])

2. torch.full(size, fill_value)

  • 정의: 지정된 size 모양의 텐서를 만들고 모든 값을 fill_value로 채움
  • 용도: 마스크, 특정값 초기화
x = torch.full((2, 3), 7)
print(x)
# tensor([[7, 7, 7],
#         [7, 7, 7]])

3. torch.clamp(input, min, max)

  • 정의: 입력 텐서의 값을 [min, max] 범위로 잘라줌
  • 용도: 값 제한하기, clipping
x = torch.tensor([1.5, -0.3, 4.2])
x_clamped = torch.clamp(x, min=0, max=1)
print(x_clamped)
# tensor([1.0, 0.0, 1.0])

4. torch.exp(input)

  • 정의: 각 요소에 대해 지수 함수 ( e^x ) 적용
  • 용도: softmax 등 확률 계산에 자주 쓰임
x = torch.tensor([1.0, 2.0, 3.0])
print(torch.exp(x))
# tensor([2.7183, 7.3891, 20.0855])

5. torch.zeros(size)

  • 정의: 0으로 채워진 텐서 생성
  • 용도: 초기화
x = torch.zeros((2, 2))
print(x)
# tensor([[0., 0.],
#         [0., 0.]])

6. torch.tensor(data)

  • 정의: 리스트/넘파이 배열 등을 텐서로 변환
  • 용도: 일반 파이썬 자료를 텐서로 바꿀 때
x = torch.tensor([[1, 2], [3, 4]])
print(x)
# tensor([[1, 2],
#         [3, 4]])

7. x.view(shape)

  • 정의: 텐서의 shape을 변형(reshape) (단, 메모리 공유)
  • 용도: 평탄화(flatten), reshape
x = torch.tensor([[1, 2], [3, 4]])
y = x.view(4)
print(y)  # tensor([1, 2, 3, 4])

⚠️ view()는 contiguous memory여야 함. 그렇지 않으면 .contiguous().view(...) 필요함.


8. x.size()

  • 정의: 텐서의 shape 정보를 반환 (튜플 형태 아님, torch.Size 객체)
  • 용도: 차원 크기 확인
x = torch.tensor([[1, 2], [3, 4]])
print(x.size())  # torch.Size([2, 2])

x.shape 도 같음: x.shape == x.size()

좋지! unsqueeze()squeeze()는 텐서의 차원(dimension) 을 다룰 때 자주 쓰이는 함수야. 특히 batch 처리를 위해 차원을 추가하거나 제거할 때 유용해.


🔹 torch.unsqueeze(tensor, dim)

  • 정의: 지정한 위치에 차원 1인 새 차원 추가
  • 용도: (C, H, W)(1, C, H, W)처럼 배치 차원 추가할 때
  • dim은 어디에 차원을 추가할지 지정 (0이면 맨 앞, -1이면 맨 뒤)
x = torch.tensor([1, 2, 3])
print(x.shape)  # torch.Size([3])

x_unsq = torch.unsqueeze(x, 0)  # 차원 추가 (맨 앞에)
print(x_unsq)       # tensor([[1, 2, 3]])
print(x_unsq.shape) # torch.Size([1, 3])

x_unsq2 = torch.unsqueeze(x, 1)  # 각 원소마다 1차원 추가됨
print(x_unsq2)
# tensor([[1],
#         [2],
#         [3]])
print(x_unsq2.shape)  # torch.Size([3, 1])

x[None, :] == x.unsqueeze(0) 과 동일


🔹 torch.squeeze(tensor, dim=None)

  • 정의: 크기가 1인 차원을 제거
  • dim 지정 시: 해당 위치가 1일 때만 제거함
x = torch.tensor([[[1], [2], [3]]])
print(x.shape)  # torch.Size([1, 3, 1])

x_sq = torch.squeeze(x)
print(x_sq)        # tensor([1, 2, 3])
print(x_sq.shape)  # torch.Size([3])

x_sq2 = torch.squeeze(x, dim=0)
print(x_sq2.shape)  # torch.Size([3, 1])

x_sq3 = torch.squeeze(x, dim=2)
print(x_sq3.shape)  # torch.Size([1, 3])

크기가 1이 아닌 차원은 절대 삭제되지 않음!

✅ 정리 표

함수기능예시 입력출력
unsqueeze(x, 0)앞에 차원 추가[3][1, 3]
unsqueeze(x, 1)중간에 추가[3][3, 1]
squeeze(x)모든 1 제거[1, 3, 1][3]
squeeze(x, dim=2)특정 차원만 제거[1, 3, 1][1, 3]

🔍 상황 정리:

예시 텐서:

x = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])  # shape: [2, 3]

즉, x.shape == torch.Size([2, 3])
→ 이건 2차원 텐서고 dim=2는 존재하지 않음

🔥 그래서 torch.squeeze(x, dim=2) 하면?

👉 에러 발생!

torch.squeeze(x, dim=2)
# RuntimeError: Dimension out of range (expected to be in range of [-2, 1], but got 2)

✅ 참고로

squeeze(x, dim=0) 처럼 dim을 줄 때는:

  • 해당 dim이 반드시 존재해야 하고
  • 해당 dim의 크기가 1이어야 squeeze가 작동함

예를 들어:

x = torch.zeros(2, 1, 3)   # shape: [2, 1, 3]
x_squeezed = torch.squeeze(x, dim=1)
print(x_squeezed.shape)    # torch.Size([2, 3])

🧠 요약

조건squeeze(x, dim=2) 결과
x.shape에 dim=2 없음❌ 에러 발생
x.shape[2] != 1❌ 에러 발생
x.shape[2] == 1✅ 해당 차원 제거됨

완전 정확하게 캐치했어!
x.size()np.shape()거의 같은 기능이야 — 텐서나 배열의 shape 정보를 반환한다는 점에서!


x.size() vs x.size(dim)

사용법설명예시
x.size()전체 shape 반환 (ex: [2, 3])torch.Size([2, 3])
x.size(1)특정 차원의 크기 반환3 (예: 두 번째 축)

예시 코드:

x = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])  # shape: [2, 3]

print(x.size())     # torch.Size([2, 3])
print(x.size(0))    # 2  (첫 번째 차원)
print(x.size(1))    # 3  (두 번째 차원)

즉, x.size(dim)x.shape[dim]과 같아!


✅ 비교: torch vs numpy

기능PyTorchNumPy
전체 shapex.size() 또는 x.shapex.shape
특정 dim 크기x.size(0)x.shape[0]
반환 타입torch.Size 객체 (튜플처럼 작동)tuple

예시:

import numpy as np
a = np.array([[1, 2], [3, 4]])
print(a.shape)     # (2, 2)
print(a.shape[1])  # 2

🔔 Tip

  • x.shape는 PyTorch에서도 튜플처럼 바로 접근 가능해서,
  • x.shape[1] 쓰는 것도 완전 가능해!
  • 사실상 x.size() == x.shape 라고 보면 돼 (기능적으론 동일)

좋아, 이번엔 torch.arangetorch.expand에 대해 설명할게!
둘 다 shape 조작이나 텐서 생성에서 엄청 자주 쓰이는 애들이야.


🔹 torch.arange(start, end, step)

  • 정의: start부터 end 미만까지 step 간격으로 텐서를 생성
  • 유사함: 파이썬의 range()나 NumPy의 np.arange() 와 유사

예시:

x = torch.arange(0, 5)
print(x)  # tensor([0, 1, 2, 3, 4])

y = torch.arange(1, 10, 2)
print(y)  # tensor([1, 3, 5, 7, 9])
  • 실수형으로 만들고 싶으면 dtype=torch.float 지정 가능
z = torch.arange(0, 1, 0.1, dtype=torch.float)
print(z)  # tensor([0.0, 0.1, ..., 0.9])

🔹 torch.expand(*sizes)

  • 정의: 크기를 복제(확장) 하되, 실제 메모리는 공유
  • 조건: 기존 텐서의 크기 1인 차원만 다른 크기로 확장 가능

⚠️ 반드시 차원 크기 1인 곳만 늘릴 수 있음!


예시 1: batch 확장

x = torch.tensor([[1, 2, 3]])  # shape: [1, 3]
x_exp = x.expand(5, 3)        # 5개의 같은 row로 확장
print(x_exp)

# 출력:
# tensor([[1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3]])

복사 아님! – 똑같은 메모리를 바라보고 있어 → 효율적임
(단, 쓰기 작업에는 주의 필요)


예시 2: 오류 나는 경우

x = torch.tensor([[1, 2, 3], [4, 5, 6]])  # shape: [2, 3]
x.expand(5, 3)  # ❌ RuntimeError 발생!
# 이유: 첫 번째 차원이 2이므로, 확장 불가능 (1이어야 가능)

expand() vs repeat() 비교

기능expandrepeat
메모리 공유✅ O❌ X (복사됨)
속도더 빠름느림
제한 조건1인 차원만 확장모든 차원 가능
쓰기 가능 여부❌ 불가 (읽기 전용)✅ 가능

🔔 Tip:

  • expandbroadcasting처럼 작동하지만, 메모리 효율적임
  • repeat진짜 복사, 데이터 조작 시 필요

완벽하게 비교해볼게! torch.repeat()torch.expand()는 모양은 비슷하게 만들 수 있지만 원리, 메모리 사용 방식, 속도, 제약조건이 완전히 다름!


🔍 핵심 비교: torch.expand() vs torch.repeat()

항목torch.expandtorch.repeat
💡 목적크기 1인 차원을 메모리 공유 방식으로 확장데이터 복제로 새 텐서 생성
🧠 메모리💾 공유 (No 복사)💾 복사 발생 (메모리 증가)
🔁 동작 방식broadcasting처럼 보이게 만듦실제로 값을 복제함
⚠️ 제약 조건확장되는 차원의 크기가 반드시 1이어야 함아무 차원이든 반복 가능
✍️ 쓰기 가능 여부❌ 원본 수정 불가 (읽기 전용)✅ 수정 가능
🚀 속도더 빠름 (메모리 효율적)느림 (복사 오버헤드 있음)

📘 예제 비교

📌 expand 예시:

x = torch.tensor([[1, 2, 3]])     # shape: [1, 3]
x_exp = x.expand(4, 3)            # 1st dim을 4로 확장 (원본의 dim=1이므로 가능)
print(x_exp)
# tensor([[1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3]])

메모리 공유! 값은 반복되지만, 실제 복사된 건 아님.


📌 repeat 예시:

x = torch.tensor([[1, 2, 3]])     # shape: [1, 3]
x_rep = x.repeat(4, 1)            # 각 행을 4번 복제
print(x_rep)
# tensor([[1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3]])

모양은 expand와 같지만, 진짜 데이터가 4배 복제됨 → 메모리 낭비

📌 중요한 차이점 확인

x = torch.tensor([[1, 2, 3]])
x_rep = x.repeat(4, 1)
x_rep[0][0] = 999

x_exp = x.expand(4, 3)
# x_exp[0][0] = 999  # ❌ RuntimeError: cannot write to expanded tensor

print(x_rep)  # ✅ 수정됨
# tensor([[999,   2,   3],
#         [  1,   2,   3],
#         [  1,   2,   3],
#         [  1,   2,   3]])

🎯 요약

언제 expand()?언제 repeat()?
차원 크기가 1이고, 메모리 효율이 중요할 때실제 복제된 데이터가 필요할 때
모델 입력 텐서 조정 (ex. [1, C, H, W])데이터 증강처럼 완전한 복제 필요할 때
연산 속도가 중요하고 읽기 전용이면 좋을 때수정하거나 저장하는 용도면 repeat 사용

네! 이번엔 더 자세하게, 그리고 예시까지 들어서 설명해드릴게요.


is_eos = completion_ids == self.processing_class.eos_token_id
        device = self.accelerator.device
        eos_idx = torch.full((is_eos.size(0),), is_eos.size(1), dtype=torch.long, device=device)
        # 각 배치(문장)별로 EOS토큰이 하나라고 있으면 True 없으면 False
        # 각 배치별로 처음 등장하는 EOS토큰의 인덱스 반환 , EOS토큰이 있는 문장에 대해서만 인덱스를 실제 EOS위치로 바꿔줌
        eos_idx[is_eos.any(dim=1)] = is_eos.int().argmax(dim=1)[is_eos.any(dim=1)]
        #시퀀으 인덱스를 만들어줌 [0,1,2,....L-1]을 배치 크기만큼 복제해서 (B,L)텐서로 만듦
        sequence_indices = torch.arange(is_eos.size(1), device=device).expand(is_eos.size(0), -1)
        completion_mask = (sequence_indices <= eos_idx.unsqueeze(1)).int()

1. 전체 코드의 목적

이 코드는 생성된 토큰 시퀀스(completion_ids)에서
처음 등장하는 EOS(End Of Sequence, 시퀀스 끝) 토큰 이후의 모든 토큰을 마스킹(무시)하기 위한 마스크(completion_mask)를 만드는 과정입니다.

즉,

  • 각 문장(배치)별로 EOS 토큰이 처음 등장한 위치까지는 1(유효),
  • 그 이후는 0(무시)로 마스킹하는 역할입니다.

2. 코드 한 줄씩 설명

(1) EOS 토큰 위치 찾기

is_eos = completion_ids == self.processing_class.eos_token_id
  • completion_ids: (B, L) — 배치 크기 B, 시퀀스 길이 L
  • self.processing_class.eos_token_id: EOS 토큰의 id
  • 결과: is_eos는 (B, L) 크기의 bool 텐서
    (각 토큰이 EOS면 True, 아니면 False)

(2) 디바이스 정보 가져오기

device = self.accelerator.device
  • 텐서를 올릴 디바이스(GPU/CPU)를 가져옴

(3) eos_idx 초기화

eos_idx = torch.full((is_eos.size(0),), is_eos.size(1), dtype=torch.long, device=device)
  • (B,) 크기의 1차원 텐서를 만들고, 모든 값을 L(시퀀스 길이)로 채움
  • 의미: "EOS 토큰이 없으면 끝까지 유효"라는 뜻으로 L로 초기화

(4) .any(dim=1)와 argmax

eos_idx[is_eos.any(dim=1)] = is_eos.int().argmax(dim=1)[is_eos.any(dim=1)]
  • is_eos.any(dim=1):
    • 각 배치(문장)별로 EOS 토큰이 하나라도 있으면 True, 없으면 False
    • shape: (B,)
  • is_eos.int().argmax(dim=1):
    • 각 배치별로 처음 등장하는 EOS 토큰의 인덱스 반환
    • shape: (B,)
  • [is_eos.any(dim=1)]:
    • EOS 토큰이 있는 문장에 대해서만 인덱스를 실제 EOS 위치로 바꿔줌

(5) 시퀀스 인덱스 만들기

sequence_indices = torch.arange(is_eos.size(1), device=device).expand(is_eos.size(0), -1)
  • [0, 1, 2, ..., L-1]을 배치 크기만큼 복제해서 (B, L) 텐서로 만듦

(6) 마스크 만들기

completion_mask = (sequence_indices <= eos_idx.unsqueeze(1)).int()
  • 각 문장별로 EOS 인덱스 이하(=EOS까지)는 1, 그 이후는 0
  • .unsqueeze(1)로 (B, 1)로 만들어 브로드캐스팅
  • 결과: (B, L) 크기의 0/1 마스크

3. 예시

예시 상황

  • 배치 크기 B = 2
  • 시퀀스 길이 L = 6
  • EOS 토큰 id = 99

예시 completion_ids

completion_ids = [
    [10, 20, 99, 30, 40, 50],   # 3번째(인덱스2)에 EOS
    [11, 22, 33, 44, 55, 66]    # EOS 없음
]

1) is_eos

is_eos =
[
    [False, False, True, False, False, False],
    [False, False, False, False, False, False]
]

2) eos_idx 초기화

eos_idx = [6, 6]  # (B,)  모두 6으로 시작

3) eos_idx[is_eos.any(dim=1)] = ...

  • 첫 번째 문장: EOS 있음 → True

  • 두 번째 문장: EOS 없음 → False

  • is_eos.int().argmax(dim=1)
    → [2, 0] (첫 번째 문장은 2번째 위치에 EOS, 두 번째 문장은 없음→0)

  • is_eos.any(dim=1)
    → [True, False]

  • 적용 결과:

    • 첫 번째 문장: eos_idx[0] = 2
    • 두 번째 문장: eos_idx[1] = 6 (변경 없음)
eos_idx = [2, 6]

4) sequence_indices

sequence_indices =
[
    [0, 1, 2, 3, 4, 5],
    [0, 1, 2, 3, 4, 5]
]

5) completion_mask = (sequence_indices <= eos_idx.unsqueeze(1)).int()

  • 첫 번째 문장: eos_idx=2 → 0,1,2까지 1, 그 이후 0
  • 두 번째 문장: eos_idx=6 → 0~5까지 모두 1
completion_mask =
[
    [1, 1, 1, 0, 0, 0],
    [1, 1, 1, 1, 1, 1]
]

4. 요약

  • 각 문장별로 EOS 토큰이 처음 등장한 위치까지 1, 그 이후는 0으로 마스킹
  • EOS가 없으면 전체가 1
  • 이 마스크는 loss 계산 등에서 "실제 유효한 토큰만 고려"할 때 사용
profile
Lee_AA

0개의 댓글