본문 바로가기
AI_basic/Pytorch

[Pytorch Part.1] Basic Skill

by hits_gold 2022. 1. 5.
반응형

1. Scalar

 우리가 아는 선형대수에서 배우는 스칼라 즉 상숫값입니다. torch 내 tensor method를 이용해 스칼라 값을 정의할 수 있습니다.

import torch

scalar1 = torch.tensor([1.])
scalar1 = torch.tensor([2.])
print(scalar1)
#tensor([1.])

 

tensor([1.])가 출력됩니다. 이러한 스칼라값들은 사칙연산 기호 또는 내장 메서드를 이용해 계산할 수 있습니다.

 

torch.add(scalar1, scalar2) # 덧셈
# scalar1 + scalar2
torch.sub(scalar1, scalar2) # 뺄셈
# scalar1 - scalar2
torch.mul(scalar1, scalar2) # 곱셈
# scalar1 * scalar2
torch.div(scalar1, scalar2) # 나눗셈
#scalar1 / scalar2

 

2. Vector

 벡터는 하나의 값을 2개 이상의 수치로 표현한 것입니다.

 

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

print(vector1)
# tensor([1., 2., 3.])

 

스칼라와 마찬가지로 사칙연산과 같은 내장 메서드로 벡터끼리의 계산을 해주면 각 요소별로(element-wise) 연산됩니다.

 

torch.add(vector1, vector2) # 덧셈
# scalar1 + scalar2
torch.sub(vector1, vector2) # 뺄셈
# vector1 - vector2
torch.mul(vector1, vector2) # 곱셈
# vector1 * vector2
torch.div(vector1, vector2) # 나눗셈
#vector1 / vector2

 

 

 

3. Matrix

 행렬은 2개 이상의 벡터 값을 통합해 구성된 값으로, 벡터 값 간 연산 속도를 빠르게 진행할 수 있는 선형 대수의 기본 단위입니다.

 

matrix1 = torxh.tensor([[1., 2.], [3., 4.]])
matrix = torxh.tensor([[5., 6.], [7., 8.]])
print(matrix1)
#tensor([[1., 2.], [3., 4.]])

 

 행렬도 스칼라와 벡터와 마찬가지로 사칙연산기호와 이에 대응하는 내장 메서드 두 가지 방법으로 사칙연산을 할 수 있습니다. 이 때 행렬 값 간 사칙연산 역시 벡터 연산처럼 각 요소별로 계산됩니다. 또한 행렬 곱 연산을 torch.matmul을 이용해 실행할 수 있습니다.

 

torch.matmul(matrix1, matrix2)
# tensor([[19., 22.],
#            43., 50.])

 

4. Tensor

 텐서는 2차원 이상의 배열이라 표현할 수 있습니다. 다음은 스칼라부터 텐서를 그림으로 표현한 것입니다.

 텐서는 선형대수학에서 n>2일 때 Rn공간에 존재하는 행렬이라고 볼 수 있습니다(저의 의견입니다) 텐서값도 스칼라 벡터, 행렬과 같이 사칙연산기호와 내장 메서드로 사칙연산수행이 가능하고, 텐서 간 내적 연산이 가능합니다.

tensor1 = torch.tensor([[[1., 2.], [3., 4.]], [[5., 6.], [7., 8.]]])
tensor1 = torch.tensor([[[9., 10.], [11., 12.]], [[13., 14.], [15., 16.]]])

#tensor([[[31., 34.],
#          [71., 78.]],
#          [[155., 166.],
#          [211., 226.]]])

텐서 간 텐서곱 연산은 해당하는 인덱스의 행렬들을 행렬곱한 것들이 뒤로 줄 지어있는 직육면체 블록을 생각하면 쉽습니다.

 

 

5. Broadcasting

 기본적으로 텐서값끼리의 연산은 크기가 동일해야하나 크기가 다를 경우 Broadcasting 자동적으로 shape을 맞춰 계산해준다.

  ex)

m1 = torch.tensor([[1, 2]])
m2 = torch.tensor([3]) # -> [[3, 3]]
print(m1 + m2)
# tensor([4, 5])

m3 = torch.tensor([[1, 2]])
m4 = torch.tensor([[3], [4]]) # -> [[3, 3], [4, 4]]
print(m3 + m4)
# tensor([[4, 5], [5, 6]])

6. Multiplication VS Matrix Multiplication

 torch의 내장메서드에서 mul은 element-wise 곱셈을, matmul은 행렬곱을 나타내는데, 여기서도 broadcasting이 적용된다.

m1 = torch.tensor([[1, 2], [3, 4]])
m2 = torch.tensor([[1], [2]])
# broadcasting으로 둘 사이 연산 시 m2 -> tensor([[1, 1], [2, 2]])

print(m1.mul(m2)) # m1 * m2
# tensor([[1, 2], [6, 8]])

print(m1.matmul(m2))
# tensor([[5], [11]])

 

7. Autograd

 Autograd를 통해 오차역전파를 이용해 파라미터를 업데이트하는 방법을 쉽게 구현할 수 있습니다.

 

if torch.cuda.is_available(): # GPU를 이용해 계산할 수 있는지 파악(True, False 반환)
    DEVICE = torch.device('cuda') # cuda 장비 사용
else:
    DEVICE = torch.device('cpu') # cpu 장비 사용
    
BATCH_SIZE = 64 # 파라미터를 업데이트할 계산되는 데이터 개수
INPUT_SIZE = 1000 # 입력층 노드의 수
HIDDEN_SIZE = 100 # 은닉층 노드 수
OUTPUT_SIZE = 10 # 최종으로  출력되는 값의 벡터 크기
                
                
                
x = torch.randn(BATCH_SIZE, INPUT_SIZE, # 크기가 input_size인 데이터 batch_size만큼 생성
               device = DEVICE, 
               dtype = torch.float, 
               requires_grad = False) # 학습을 통해 조절될 매개변수가 아님
               
y = torch.randn(BATCH_SIZE, OUTPUT_SIZE, # 크기가 output_size인 데이터 batch_size개 생성
               device = DEVICE,
               dtype = torch.float,
               requires_grad = False) # 
               
w1 = torch.randn(INPUT_SIZE, HIDDEN_SIZE, # x와 행렬곱을 통해 (hidden_size, batch_size)를 만들어야 하므로 다음과 같은 형상
               device = DEVICE,
               dtype = torch.float,
               requires_grad = True) # 학습을 통한 매개변수 조절 필요함
               
w2 = torch.randn(HIDDEN_SIZE, OUTPUT_SIZE, 
               device = DEVICE,
               dtype = torch.float,
               requires_grad = True) # 학습을 통한 매개변수 조절 필요함
               
learning_rate = 1e-6 # 학습률(gradient에 따른 학습 정도) 결정

 

torch.randn은 평균이 0 표준편차가 1인 정규분포에서 샘플링한 값입니다. 

 

for t in range(1, 501): # 500번 학습(epochs=500)
    y_pred = x.mm(w1).clamp(min=0).mm(w2) # torch.mm()을 사용해 가중치와 행렬곱 적용 후, clamp(min=0)로 활성화 함수 적용(ReLU와 같은 역할)
    
    loss = (y_pred - y).pow(2).sum() # 오차 계산
    if t % 100 == 0: # 100번째 반복마다
        print(f"Iteration: {t}\t Loss: {loss.item()}") # 반복 횟수 및 오차 표시
    loss.backward() # 오차역전파법 수행
    
    with torch.no_grad(): # gradient 계산 결과 이용해 파라미터 업데이트할 때는 gradient값 고정
        w1 -= learning_rate * w1.grad # 가중치 업데이트
        w2 -= learning_rate * w2.grad # 가중치 업데이트
        
        w1.grad.zero_() # gradient를 통해 파라미터 업데이트 했으므로 다시 학습을 위해 graident 0으로 초기화
        w2.grad.zero_()

 

데이터를 랜덤하게 샘플링했음에도 반복이 더해질수록 오차가 줄어드는 것을 알 수 있습니다!

 

# Auto grad구현 https://colab.research.google.com/drive/1oTpmnggPJZGHGCUG7zTicRslNGf6FWy3

반응형