[Pytorch] Pytorch 입문 > (1) 미리 보기

Pytorch > Pytorch 입문 > (1) 미리 보기


PyTorch 의 모든것은 PyTorch Tutorials 에서 확인 할 수 있습니다.

  




1. Tensor

  • 넘파이 배열인 ndarray 와 같은 개념으로, 파이토치에서 연산을 수행하기 위한 가장 기본적인 객체 입니다.
  • 파이토치는 텐서를 통해 값을 저장하고 그 값들에 대해 연산을 수행 할 수 있는 함수를 제공 하며 텐서간 연산에 따른 그래프와 경사도를 저장할 수 가 있습니다.
  • 우리는 딥러닝 개발에 있어 Tensor 와 ndarray 를  자유롭게 전환 할 수 있어야 할 것 입니다.


[Code review] 

import torch   

x = torch.Tensor(2,2)   #텐서 구조 선언
print("1. torch.Tensor(2,2) ---- : {0}\n" .format(x))

x = torch.Tensor([[1,2], [3,4]])
print("2-1. torch.Tensor([[1,2], [3,4]])----{0}\n".format(x))
print("2-2. torch.Tensor   x[0][0] ----{0}\n" .format(x[0][0]))

import numpy as np 

x = np.array(x)
print("3.np.array(x) ---- : {0}\n" .format(x))

x = torch.from_numpy(x)
print("4.torch.from_numpy(x) ---- :{0}\n" .format(x))

[결과]

1. torch.Tensor(2,2) ---- : tensor([[0.0.],[0.0.]])

2-1. torch.Tensor([[1,2], [3,4]])----tensor([[1.2.], [3.4.]])

2-2. torch.Tensor   x[0][0] ----1.0

3.np.array(x) ---- : [[1. 2.] [3. 4.]]

4.torch.from_numpy(x) ---- :tensor([[1.2.],[3.4.]])

2. Autograd

  • 파이 토치는 자동으로 미분과 역전파를 수행 하는 Autograd 기능을 가집니다.
  • 파이 토치로 인해 우리는 텐서간의 행열 연산을 크게 신경 쓸 필요 없이 명령어 사용 하면 됩니다.
  • 파이토치는 텐서들 간에 연산을 수행할 때마다 동적으로 Computation graph 를 생성하여 연산의 결과물이 어떤 텐서로 부터 어떤 연산을 통해서 왔는지 추적 합니다.
  • 최종적으로 나온 스칼라에 역전파 알고리즘을 통해 미분을 수행 하도록 했을때, 각 텐서는 자기 자신의 자식노드에 해당 하는 텐서와 연산을 자동으로 찾아 계속해서 역전파 알고 리즘을 수행 할 수 있도록 합니다.

[Code review 1] 

1. 생성된 텐서 x,y를 더함 + 새로운 텐서를 생성 = z 에 할당

2. 이미 생성된 연산 그래프를 따라서 미분 값을 z 에 전달 할 수 있게 됨

import torch

x = torch.FloatTensor(22)
y = torch.FloatTensor(22)

print("1. torch.FloatTensor(2, 2):x = {0} \n y = {1} \n" .format(x , y))

y.requires_grad_(True)
print("2. y.requires_grad_(True)------>  {0} \n  " .format(y))

z = (x + y) + torch.FloatTensor(22)
print("3. torch.FloatTensor(2, 2):\n  x = {0} \n y = {1} \n  z = {2}" .format(x , y , z))

[결과 1]

1. torch.FloatTensor(22):
 x = tensor([[0.0.], [0.0.]])
 y = tensor([[0.0.], [0.0.]])

2. y.requires_grad_(True)------>  tensor([[0.0.],
        [0.0.]], requires_grad=True)

3. torch.FloatTensor(22):
 x = tensor([[0.0.],[0.0.]])
 y = tensor([[0.0.],[0.0.]], requires_grad=True)
 z = tensor([[ 1.4013e-45,  0.0000e+00],
        [-6.2869e-11,  4.5916e-41]], grad_fn=<AddBackward0>)


[Code review 2] 

1. 역전파 알고리즘이 필요 없는 경우, with 문법을 사용하여 연산을 수행, 

2. 기울기를 구하기 위한 사전 작업을 생략 할 수 있으므로 연산 속도 및 메모리 사용 측면에서 이점

import torch

x = torch.FloatTensor(22)
y = torch.FloatTensor(22)

print("1. torch.FloatTensor(2, 2):x = {0} \n y = {1} \n" .format(x , y))


y.requires_grad_(True)
print("2. y.requires_grad_(True)------>  {0} \n  " .format(y))

with torch.no_grad():
    z = (x + y) + torch.FloatTensor(22)
    print("3. torch.FloatTensor(2, 2):\n  x = {0} \n y = {1} \n  z = {2}" .format(x , y , z))

[결과 2]

1. torch.FloatTensor(22):
  x = tensor([[0.0.], [0.0.]])
  y = tensor([[0.0.],[0.0.]])

2.y.requires_grad_(True)------>  tensor([[0.0.],
        [0.0.]], requires_grad=True)

3. torch.FloatTensor(22):
 x = tensor([[0.0.],[0.0.]])
 y = tensor([[0.0.],[0.0.]], requires_grad=True)
 z = tensor([[2.3694e-380.0000e+00],[0.0000e+000.0000e+00]])

3. Feedfoward 

  • linear layer 나 fully connected layer 에서, M x N 의 입력 행렬 'x' 가 주어 지면, 
  • M x P 의 행열 'W' 를 곱한 후 
  • P 차원의 벡터 'b' 를 편차 값을 더하게 됩니다.


[Code review]

import torch

def linear(xWb):
    y = torch.mm(xW) + b # x 곱하기 w 에 b 편차를 더함

    return y

x = torch.FloatTensor(85) # 8 x 5
W = torch.FloatTensor(52) # 5 x 2
b = torch.FloatTensor(2) # 1 X 2
print("1.Feed-forward  :\n  x = {0} \n w = {1} \n  b = {2}" .format(x , W , b))

#Feed - forward
y = linear(xWb) # 8 x 2 반환 , y 는 <class 'torch.Tensor'> 임
print("2.Feed-forward  torch.mm(x, W) + b :\n  y = {0} " .format(y))

[결과]

1.Feed-forward  :
  x = tensor([[1.2514e-148.9634e-333.0566e+321.8469e+258.7126e-04],
        [1.8889e+314.3973e+217.3773e+282.8936e+127.5338e+28],      
        [1.8037e+283.4740e-121.7743e+289.4701e-011.7338e+25],      
        [3.4732e-121.7743e+289.4701e-012.6907e+201.8910e+23],      
        [7.1443e+311.9046e+311.1128e+272.0700e-193.0263e+29],      
        [1.7704e+316.9767e+221.8037e+283.0313e+327.7783e+31],      
        [4.5453e+307.4392e+285.4275e-141.6109e-191.7743e+28],      
        [5.3831e-141.0194e-384.1328e-395.1429e-391.0286e-38]])     
 w = tensor([[0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.]])
  b = tensor([0.0.])
2.Feed-forward  torch.mm(xW) + b :
  y = tensor([[0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.]])

4. nn.Module

  • 파이 토치는 nn.Module 이라는 클래스를 제공 합니다. 사용자는 해당 클래스를 상속받아 필요한 모델 구조를 구현 할 수 있게 됩니다.
  • 가장 중요한 nn.Module 의 forward() 함수는override 하여 Feedfoward 를 구현 할수 있게 됩니다. (사전 개념 정리 없이 코드만 분석 하는 경우, 디버깅시 forward() 가 자동 실행 되는 것을 보고 무한의 삽질에 빠질 수 가 있습니다.)
  • 이외 nn.Module 의 특징을 이해 하여 한번에 신경망 가중치 파라미터 저장 및 불러오기 수행도 가능 합니다.


[Code review]

import torch
import torch.nn as nn

# nn.Module 을 상속 받아 MyLinear 을 구현 
class MyLinear(nn.Module):

    #네트워크 구성
    def __init__(selfinput_sizeoutput_size):
        super().__init__()
        print("1.__init__(self, input_size, output_size): \n  input_size = {0} \n output_size = {1} \n" .format(input_sizeoutput_size))
        # x * W + b 에서 W , b 텐서 구성 
        self.W = torch.FloatTensor(input_sizeoutput_size)
        self.b = torch.FloatTensor(output_size)
        print("2._torch.FloatTensor \n  self.W  = {0} \n self.b = {1} \n" .format(self.W , self.b ))

    #feed forward 수행
    #return 의 값은 <class 'torch.Tensor'>  
    def forward(selfx):
        y = torch.mm(xself.W) + self.b   # x 곱하기 W 에 b 편차를 더함
        return y
        
##############################
# x 텐서 선언
x = torch.FloatTensor(85)  #  x * W + b 에서 x

# 네트워크 생성 
#  - hidden layer , output size
linear = MyLinear(52)   #  x * W + b 에서  W, b 

#MyLinear 에 입력 텐서 x 를 넣어 x * W + b 를 수행 하게 함
y = linear(x)
print("3.y = linear(x) \n  linear  = {0} \n y = {1} \n " .format(linear  , y))

[결과]

1.__init__(self, input_size, output_size): 
  input_size = 5
 output_size = 2

2._torch.FloatTensor
  self.W  = tensor([[0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.]])
  self.b = tensor([0.0.])

3.y = linear(x)
 linear  = MyLinear()
 y = tensor([[0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.],
        [0.0.]])


(1) Parameters 함수 : 모듈 내에 선언된 학습이 필요한 파라미터들을 반환 하는 Iterator 


params = [p.size() for p in linear.parameters()]
print(params)

[결과] : linear 에는 학습 가능한 파라미터가 없다고 나옴, 신경망의 학습 파라메터는 파라미터로 등록 필요 

[]    

   [해결 방안] parameter 클래스 (nn.Parameter)사용하여 텐서를 감싸 줘야 함

        # x * W + b 에서 W , b 텐서 구성 
        self.W = nn.Parameter(torch.FloatTensor(input_sizeoutput_size), requires_grad=True)
        self.b = nn.Parameter(torch.FloatTensor(output_size), requires_grad=True)

params = [p.size() for p in linear.parameters()]
print("parameters()  -->" , params) 

[결과]

  parameters()  --> [torch.Size([52]), torch.Size([2])]

5. Back Propagation

  • 역전파 수행, Feedfoward 가 원하는 연산을 통해 값을 앞으로 전달하는 방식이라면,
  • 이렇게 Feedfoward 를 통해 얻은 값에서 실제 정답 값 과의 차이를 계산 하여 오류(손실)를 뒤로 전달하는 Back Propagation 의 이해가 필요 합니다.
  • 이때의 오류 값은 스칼라로 표현 돠어야 하며, 벡터나 행렬의 형태여서는 안됩니다.
  • 구해진 각 파라미터의 기울기에 대해 반복적으로 경사 하강법을 사용하여 에러를 줄여 나갈 수 있습니다.


[Code review]

objective = 100  

x = torch.FloatTensor(85)
linear = MyLinear(52)
y = linear(x)
print("1. y = linear(x)\n  x = {0} \n y = {1} \n " .format(x   , y ))

loss = (objective - y.sum())**2  #차이를 제곱
print("y = linear(x) \n loss = {0} \n  objective - y = {1} \n " .format(loss    , objective - y))

z = loss.backward()   # 차이값을 backward 수행 함
print("loss.backward() \n z = {0} \n  " .format(z))

[결과]

1. y = linear(x
  x = tensor([[0.0000e+000.0000e+002.1019e-440.0000e+000.0000e+00],
        [0.0000e+000.0000e+000.0000e+000.0000e+000.0000e+00],
        [0.0000e+000.0000e+000.0000e+000.0000e+000.0000e+00],
        [0.0000e+000.0000e+000.0000e+001.4013e-450.0000e+00],
        [0.0000e+000.0000e+000.0000e+000.0000e+001.0965e-38],
        [0.0000e+001.2729e+264.5916e-410.0000e+000.0000e+00],
        [2.1019e-440.0000e+000.0000e+000.0000e+000.0000e+00],
        [0.0000e+000.0000e+000.0000e+000.0000e+000.0000e+00]])
  y = tensor([[0.0000e+000.0000e+00],
        [0.0000e+000.0000e+00],
        [0.0000e+000.0000e+00],
        [0.0000e+000.0000e+00],
        [0.0000e+000.0000e+00],
        [1.2742e-121.1339e-12],
        [0.0000e+000.0000e+00],
        [0.0000e+000.0000e+00]], grad_fn=<AddBackward0>)

2. y = linear(x
 loss = 10000.0
  objective - y = tensor([[100.100.],
        [100.100.],
        [100.100.],
        [100.100.],
        [100.100.],
        [100.100.],
        [100.100.],
        [100.100.]], grad_fn=<RsubBackward1>)

3. loss.backward() 
 z = None


6. Trainning 그리고 Evaluation 

  • 파이토치가 제공하는 train() 함수와 eval() 함수를 사용 하면, 우리는 모델에 대해 훈련과 추론 시 요구되는 모드 변환을 아주 쉽게 수행 할 수 있게 됩니다.
  • nn.Module 을 상속 받아 구현 하고 생성한 객체는 기본 적으로 Training 모드 입니다. 이를 eval() 을 사용하여 Evaluation 모드로 전환 하고
  • 추론이 끝나면 train() 을 선언 하여 원래의 훈련 모드로 돌아가면 됩니다.

[Code review]

# Training 모드
linear.eval()  
linear.train()    

8. GPU 

  • GPU 가 장착 되는 않은 CPU 버전에서 일반 적인 파이토치 작업은 가능 합니다.
  • 다만. GPU 사용이 요구될수 있는 문제에서는 'cuda()' 함수를 통해 원하는 텐서르 GPU 메모리에 복사 하거나 이동 시킬수 있습니다. 


[Code review]

x = torch.cuda.FloatTensor(1610)
linear = MyLinear(105)
# .cuda() let module move to GPU memory.
linear.cuda()
y = linear(x)



ref

- 김기현의 자연어 처리 딥러닝 캠프(파이토치 편)


Comments

Popular posts from this blog

[MaritimeCyberTrend] Relationship and prospects between U.S. Chinese maritime operations and maritime cybersecurity

Examining the Reality of Cyber Incidents and the Shortfalls in Compliance Frameworks

Comprehensive List of Shipboard Systems in Commercial Vessels