post
update

pytorch의 tensor와 tensor 연산, torch.nn을 활용한 모델 구축을 다룹니다.

Tensor

import torch

t = torch.tensor([2, 3, 4], dtype=torch.int32)
print(t.float().dtype()) # torch.float32

print(t.dim())      # 1
print(t.size())     # or t.shape -> torch.Size([3])
print(t.numel())    # 3 (number of element)

torch.sum(t)     # 모든 요소의 합
torch.prod(t)    # 모든 요소의 곱
torch.mean(t)    # 평균
torch.var(t)     # 표본 분산
torch.std(t)     # 표본 표준편차

  • (참고)표본 표준편차의 경우 표본으로 계산한 값이기 때문에 n이 아닌 n-1로 나누어야한다 - 자유도 개념
\[S^2 = \frac{1}{n-1}\sum_{i=1}^{n}\left(x_i - \bar{x}\right)^2\]
q = torch.empty(5)  # 초기화 되지 않은 텐서 생성(쓰레기 값 들어감)
q.fill_(3.0)        # fill_ 언더바 넣어야함

q.float()           # tensor의 dtype casting 가능

# CPU tensor
x = torch.IntTensor([1,2,3])
x = torch.FloatTensor([1, 2, 3])

x.to(device='cuda')        # cuda, 즉, GPU 메모리로 텐서를 옮기며 gpu 사용
x.cuda()

x.to('cpu')         # 텐서를 다시 cpu로 옮겨옴
x.cpu()

x = torch.tensor([1, 3])
y = x.clone()       # tensor 복사
z = x.detach()      # 마찬가지로 복사하지만 계산 그래프에서 제외된 채 복사됨

t = torch.tensor([[1, 2, 3],
                  [4, 5, 6]])

print(t[1, :])
print(t[1, ...])    # ...으로도 : 과 같은 역할을 수행할 수 있다.

파이썬 리스트는 slicing 시 내부 값을 복사하여 새로운 메모리 공간에 리스트를 생성하지만, numpy의 배열이나 pytorch의 텐서들은 기존과 동일한 메모리 공간을 공유한다(얕은 복사).

이런 경우 slicing으로 인해 tensor가 연속적으로 할당되어 있지 않을 수 있다.

t.is_contiguous()   # 연속적인지 확인 가능

# (2, X) 형태로 변경(-1을 적으면 해당 차원에 적절한 값으로 자동 부여됨)
t.view(2, -1)               # 불연속일 시 view 메서드는 오류 발생
t.contiguous().view(2, -1)  # 연속으로 변경 후 view 적용 가능

t.reshape(2, -1)            # 불연속이라도 사용 가능(이 경우 자동으로 복사가 일어남)
                            # 복사 된 경우 수정 시 원본 tensor에 영향 X

t.flatten(1, 2)     # 1번째 부터 2번째 차원까지 평탄화 수행

t.transpose(1, 2)   # 1, 2 차원의 축을 교환

t.squeeze()         # 채널이 1개인 차원을 없애고 압축
t.unsqueeze(dim=1)  # dim 1에 채널 1인 차원 추가

t = torch.tensor([[1, 2, 3]])   # 1x3
t.expand(4, 3)                  # 4x3 형태로 변경
                                # 기존 t의 메모리를 공유하며 확장된 것도 전부 view이다
                                # 수정 시 반복된 텐서들과 원본 텐서까지 영향이 간다
t.repeat(2, 3)                  # 2x9로 복사
                                # 기존 텐서의 값을 기반으로 새로운 텐서를 생성한다

# in-place 방식의 산술연산
a.add_(b)           # _있는 함수는 a 텐서의 값을 업데이트한다
a.sub_(b)
a.mul_(b)
a.div_(b)

$L_p$ norm 노름

\[\|x\|_p = \left(\sum_{i=1}^{n}\left(|x_i|^p\right)^{\frac{1}{p}} \right)\]

유사도

두개의 1-D Tensor(Vector) 가 얼마나 유사한지에 대한 측도

맨해튼 유사도

\[\begin{aligned} \text{Manhattan Distance} &= \sum_{i=1}^n|x_i-y_i| \\ \text{Manhattan Similarity} &= \frac{1}{1 + \text{Manhattan Distance}} \end{aligned}\]
  • 유사도의 최댓값은 1이다.

유클리드 유사도

\[\begin{aligned} \text{Euclidean Distance} &= \sqrt{\sum_{i=1}^n|x_i-y_i|^2} \\ \text{Euclidean Similarity} &= \frac{1}{1 + \text{Euclidean Distance}} \end{aligned}\]

코사인 유사도

두 벡터가 이루는 각도에 cosine 값을 취해 유사도를 평가한다.

벡터가 평행이면 유사도가 1, 수직이면 0의 유사도를 갖게 된다.

\[\begin{aligned} \text{Cosine Similarity} &= cos(\theta) \\ &= \frac{\mathbf{x} \cdot \mathbf{y}}{\|x\|_2\cdot\|y\|_2} \end{aligned}\]

상관 관계 분석 수식

\[r_{xt} = \frac{\sum_{i=1}^n{(x_i-\bar{x})(t_i-\bar{t})}}{\sqrt{\left(\sum_{i=1}^n{(x_i-\bar{x})^2}\sum_i^n{(t_i - \bar{t})^2} \right)}}\]

x와 t가 함께 변하는 정도를 각각 변하는 정도에 나누는 것

np.corrcoef(x, t)

최소 0.5 이상일때부터 상관관계가 존재한다고 볼수 있음

torch.nn 으로 신경망 모델 구축

데이터 전처리

from sklearn.model_selection import train_test_split  # sklearn을 활용한 데이터 분할

x_train, x_test, t_train, t_test = train_test_split(x, t, test_size=0.2, random_state=42)


from sklearn.preprocessing import StandardScaler    # sklearn을 활용한 데이터 표준화

scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)

from torch.utils.data import Dataset, DataLoader

class IrisDataset(Dataset):                     # CustomDataset 클래스 생성 가능
    def __init__(self, features, labels):
        self.features = features
        self.labels = labels

    def __len__(self):                          # 데이터 길이 반환
        return len(self.features)

    def __getitem__(self, idx):                 # 특정 idx 데이터 반환 메서드까지 오버라이딩 필수
        return self.features[idx], self.labels[idx]

train_dataset = IrisDataset(x_train, t_train)
test_dataset = IrisDataset(x_test, t_test)

batch_size = 4  # 배치 크기를 4로 설정
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

nn.Module

nn.Module을 상속받는 모듈이나 모델을 만들 수 있다. forward 순전파 함수를 구현해주어야한다.

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(3, 5)
        # TODO: 생성자 만들기

    def forward(self, x):
        y = self.linear(x)
        # TODO: 순전파 구현하기
        return y

model = Model()

# GPU 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
x = x.to(device)
y = y.to(device)

# 손실함수 및 optimizer 생성
loss_function = nn.MSELoss()    # Mean Squared Error
                                # nn.BCELoss()는 binary cross entropy loss
optimizer = optim.SGD(model.parameters(), lr = 0.01)


for epoch in range(num_epochs):
    for batch_features, batch_labels in train_loader:
        outputs = model(batch_features)
        loss = loss_function(outputs, batch_labels) # input으로 손실값까지 계산

        optimizer.zero_grad()           # 이전 단계에 계산된 값 초기화
        loss.backward()                 # 기울기 계산
        optimizer.step()                # parameter 업데이트

예측(테스트)

예측 시에는 gradient 계산이 불필요하며, 정규화를 했다면 예측 후 정규화 해제 작업이 필요하다.

# 표준화시 fit_transform으로 훈련 데이터에 따라 정규화를 하며 scaler_x에 정규화 파라미터를 저장하였다.
# 이를 test에도 적용하기 위해 transform 함수를 사용한다.
test_scaled = scaler_x.transform(test_data.reshape(-1, 1))
test_tensor = torch.tensor(test_scaled, dtype=torch.float32).view(-1, 1).to(device)
model.eval()            # dropout, batch norm 같은 걸 평가모드로 전환
with torch.no_grad():   # grad 계산 방지
    predictions_scaled = model(test_tensor)

predictions = scaler_t.inverse_transform(predictions_scaled.cpu().numpy())

댓글남기기