본문 바로가기
AI/딥러닝

01. 파이토치(Pytorch)

by 사라리24 2024. 6. 18.
SMALL

 

1. 파이토치(Pytorch)

* Python을 기반으로 한 오픈 소스 기계 학습 라이브러리
* 텐서플로우(Tensorflow)와 함께 머신러닝, 딥러닝에서 가장 널리 사용되는 프레임워크

* 초기에는 Torch라는 이름으로 Lua언어 기반으로 만들어졌으나, 파이썬 기반으로 변경한 것이 Pytorch
*  뉴욕대학교와 페이스북이 공동으로 개발하였고, 현재 가장 대중적이고 널리 사용됨int32)

주요 특징

  1. 동적 계산 그래프 (Dynamic Computation Graphs):
    • 파이토치는 동적 계산 그래프를 사용하여 계산을 수행합니다. 이는 연산 그래프를 실행 중에 변경할 수 있음을 의미하며, 복잡한 모델을 구현하거나 디버깅하기 쉽게 합니다.
  2. 자동 미분 (Automatic Differentiation):
    • 파이토치의 autograd 모듈은 자동으로 그래디언트를 계산하여 신경망의 역전파를 쉽게 수행할 수 있게 합니다.
  3. 유연한 텐서 연산 (Flexible Tensor Operations):
    • 텐서는 파이토치의 기본 데이터 구조로, 다차원 배열입니다. 파이토치는 다양한 텐서 연산을 지원하며, GPU를 사용한 고속 계산을 지원합니다.
  4. 강력한 커뮤니티와 풍부한 에코시스템:
    • 파이토치는 활발한 커뮤니티와 풍부한 튜토리얼, 예제 코드, 라이브러리를 갖추고 있습니다. 또한, 다양한 딥 러닝 프레임워크 및 도구와의 호환성이 좋습니다.
  5. 모델 배포 용이성:
    • 파이토치는 TorchScript를 통해 모델을 쉽게 배포할 수 있으며, 다양한 플랫폼에서 실행할 수 있습니다.

주요 구성 요소

  1. 텐서 (Tensor):
    • 텐서는 파이토치의 기본 데이터 구조로, 다차원 배열입니다. numpy 배열과 유사하지만, GPU를 사용한 고속 연산을 지원합니다.
  2. 자동 미분 (Autograd):
    • autograd 모듈은 자동으로 그래디언트를 계산하여 역전파를 수행할 수 있게 합니다.
  3. 신경망 (Neural Networks):
    • torch.nn 모듈은 신경망을 구성하는 다양한 레이어와 함수들을 제공합니다.
  4. 데이터 로더 (DataLoader):
    • torch.utils.data 모듈은 데이터셋을 쉽게 로드하고 전처리할 수 있는 도구들을 제공합니다.
  5. 최적화 (Optimization):
    • torch.optim 모듈은 다양한 최적화 알고리즘(SGD, Adam 등)을 제공합니다.

 

  • 파이토치 import
    버전보기

       
        import torch
        print(torch.__version__)


2.3.0+cu121

 

1-1. 스칼라(Scalar)

* 하나의 상수를 의미
* 수학적으로 크기만 있고 방향이 없는 양

 

  • 단일 요소 1차원 텐서 생성

       
      var1 = torch.tensor([1])

      type(var1)


torch.Tensor
---------------------------------------------------------------------
이 경우, [1]이라는 리스트를 텐서로 변환

 

  • 단일 요소 1차원 텐서 생성

       
      var2 = torch.tensor([10.5])
      type(var2)

 
torch.Tensor
------------------------------------------------------------------
이 경우, [10.5]이라는 리스트를 텐서로 변환

 

추가적인 설명

var1 var2 은 torch.Tensor 타입의 객체

: PyTorch에서 텐서는 다차원 배열을 표현하며,
NumPy의 ndarray와 유사한 구조를 가지고 있지만, GPU 가속과 자동 미분 등의 추가적인 기능을 제공

 

 

  • 두 개의 텐서(var1과 var2) 간의 기본 산술 연산

       
        # 두 스칼라의 사칙 연산
        print(var1 + var2)
        print(var1 - var2)
        print(var1 * var2)
        print(var1 / var2)

 
tensor([11.5000])
tensor([-9.5000])
tensor([10.5000])
tensor([0.0952])

 

1-2. 벡터(Vector)

* 상수가 두 개 이상 나열되었을 경우

 

  • 세개의 요소를 가진 1차원 벡터 생성

       
        vec1 = torch.tensor([1, 2, 3])
        vec1


tensor([1, 2, 3])
-----------------------------------------------------------
[1, 2, 3]이라는 리스트를 텐서로 변환

 

 

  • vec1의 데이터 타입

       
      type(vec1)

 
torch.Tensor
--------------------------------
vec1은 1차원 PyTorch 텐서

 

 

  • 부동 소수점 숫자를 포함하는 1차원 벡터를 생성

       
      vec2 = torch.tensor([1.5, 2.4, 3.3])
      vec2


tensor([1.5000, 2.4000, 3.3000])

 

 

  • 벡터(vec1과 vec2) 간의 기본 산술 연산

     
      # 두 벡터의 사칙 연산
      print(vec1 + vec2)
      print(vec1 - vec2)
      print(vec1 * vec2)
      print(vec1 / vec2)


tensor([2.5000, 4.4000, 6.3000])
tensor([-0.5000, -0.4000, -0.3000])
tensor([1.5000, 4.8000, 9.9000])
tensor([0.6667, 0.8333, 0.9091])

 

 

 

  • 네개의 요소를 가진 1차원 벡터 생성

       
        vec3 = torch.tensor([5, 10, 15,20])
        vec3


tensor([ 5, 10, 15, 20])
--------------------------------
[5, 10, 15, 20]이라는 리스트를 텐서로 변환

 

 

  • vec1 와 vec3 덧셈 연산

       
         
vec1 + vec3 # 오류 : cannot assign to expression

 
vec1과 vec3이 서로 다른 길이의 벡터

vec1은 길이가 3인 벡터이고, vec3은 길이가 4인 벡터

 



1-3. 행렬(Matrix)

* 2차원 배열
* 2개 이상의 벡터 값을 가지고 만들어진 값으로 행과 열의 개념을 가진 숫자의 나열

 

  • 'mat1', 'mat2' 두개의 2차원 행렬을 생성하고 사칙 연산을 수행

       
      mat1 = torch.tensor([[1,2], [3,4]])
      print(mat1)


      mat2 = torch.tensor([[7,8], [9,10]])
      print(mat2)

      # 두 행렬의 사칙 연산
      print(mat1 + mat2)
      print(mat1 - mat2)
      print(mat1 * mat2)
      print(mat1 / mat2)


tensor([[1, 2],
        [3, 4]])

--------------------------------

tensor([[ 7,  8],
        [ 9, 10]])

--------------------------------


tensor([[ 8, 10],
        [12, 14]])
tensor([[-6, -6],
        [-6, -6]])
tensor([[ 7, 16],
        [27, 40]])
tensor([[0.1429, 0.2500],
        [0.3333, 0.4000]])

 



1-4. 텐서(Tensor)

* 다수의 행렬이 모이면 텐서라고 부름
* 배열이나 행렬과 매우 유사한 특수한 자료구조
* 파이토치는 텐서를 사용하여 모델의 입력과 출력, 모델의 매개변수들을 처리 사용됨

 

 

 

  • 'tensor1' 'tensor2' 3차원 텐서를 생성하고 출력

       
        tensor1 = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
        print(tensor1)

        tensor2 = torch.tensor([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])
        print(tensor2)


tensor([[[1, 2],
         [3, 4]],

        [[5, 6],
         [7, 8]]])

--------------------------

tensor([[[ 9, 10],
         [11, 12]],

        [[13, 14],
         [15, 16]]])

 

 

  • 'tensor1' 'tensor2'  사칙연산


       
# 두 텐서의 사칙연산
        print(tensor1 + tensor2)
        print(tensor1 - tensor2)
        print(tensor1 * tensor2)
        print(tensor1 / tensor2)
       
 
tensor([[[10, 12],
         [14, 16]],

        [[18, 20],
         [22, 24]]])
tensor([[[-8, -8],
         [-8, -8]],

        [[-8, -8],
         [-8, -8]]])
tensor([[[  9,  20],
         [ 33,  48]],

        [[ 65,  84],
         [105, 128]]])
tensor([[[0.1111, 0.2000],
         [0.2727, 0.3333]],

        [[0.3846, 0.4286],
         [0.4667, 0.5000]]])

 

  • PyTorch를 사용하여 다양한 텐서 연산을 수행

       
          print(torch.add(tensor1,tensor2))
          print(torch.subtract(tensor1,tensor2))
          print(torch.multiply(tensor1,tensor2))
          print(torch.divide(tensor1,tensor2))
          print(torch.matmul(tensor1,tensor2)) # 행렬곱 연산


tensor([[[10, 12],
         [14, 16]],

        [[18, 20],
         [22, 24]]])
tensor([[[-8, -8],
         [-8, -8]],

        [[-8, -8],
         [-8, -8]]])
tensor([[[  9,  20],
         [ 33,  48]],

        [[ 65,  84],
         [105, 128]]])
tensor([[[0.1111, 0.2000],
         [0.2727, 0.3333]],

        [[0.3846, 0.4286],
         [0.4667, 0.5000]]])
tensor([[[ 31,  34],
         [ 71,  78]],

        [[155, 166],
         [211, 226]]])

 

 

  • 연산을 수행하고, 결과를 원래 텐서에 바로 저장

       
        # replace 연산
        print(tensor1.add_(tensor2)) # tensor1에 결과를 다시 저장, 모든 사칙 연산자에 _ 를 붙여 사용할 수 있음
        print(tensor1)

        print(tensor1.subtract_(tensor2)) # 모든 사칙연산자에 _를 붙이면 inplace=True가 됨


tensor([[[10, 12],
         [14, 16]],

        [[18, 20],
         [22, 24]]])
tensor([[[10, 12],
         [14, 16]],

        [[18, 20],
         [22, 24]]])
tensor([[[1, 2],
         [3, 4]],

        [[5, 6],
         [7, 8]]])

 

 

 

2.  텐서의 변환

* 다수의 행렬이 모이면 텐서라고 부름
* 배열이나 행렬과 매우 유사한 특수한 자료구조
* 파이토치는 텐서를 사용하여 모델의 입력과 출력, 모델의 매개변수들을 처리 사용됨

 

 

  • 파이썬 리스트 x_data를 정의하고 출력

       
      x_data = [[1,2], [3,4]]
      print(x_data)


[[1, 2], [3, 4]]

 

 

  • 파이썬 리스트(x_data)를 numpy 배열(np_array)로 변환

       
        import numpy as np

        np_array = np.array(x_data)
        np_array


array([[1, 2],
       [3, 4]])

 

 

  • numpy 배열(np_array)을 PyTorch 텐서(x_np_1)로 변환

       
      x_np_1 = torch.tensor(np_array)
      x_np_1


tensor([[1, 2],
             [3, 4]])

 

 

  •  텐서(x_np_1)  첫 번째 원소를 100으로 변경

       

      x_np_1[0, 0] = 100
      print(x_np_1)
      print(np_array)


tensor([[100,   2],
               [  3,   4]])

          [[1 2]
          [3 4]]
'x_np_1' 텐서의 첫 번째 원소를 100으로 수정
 NumPy 배열  'np_array'는 그대로 유지

* PyTorch 텐서는 NumPy 배열과 유사한 방식으로 인덱싱 및 값 할당이 가능하다.

 

  • torch.as_tensor()는 PyTorch 텐서로 변환하며, 원본 데이터를 공유

       
      # torch.as_tensor(): ndarray와 동일한 메모리 주소를 가리키는 뷰를 만드는 함수
      x_np_2 = torch.as_tensor(np_array)
      print(x_np_2)
      x_np_2[0, 0] = 200
      print(x_np_2)
      print(np_array)


tensor([[200,   2],
        [  3,   4]])

tensor([[200,   2],
        [  3,   4]])

[[200   2]
 [  3   4]]

torch.as_tensor np_array와 동일한 메모리 주소를 가리키는 텐서를 생성하기 때문에 발생합니다.
따라서 텐서의 값을 변경하면 원본 NumPy 배열의 값도 함께 변경

 

 

  • torch.from_numpy()를 사용하여 NumPy 배열을 PyTorch 텐서로 변환,
    변환된 텐서의 값을 변경하여 원본 NumPy 배열의 값도 변경

       
        # from_numpy() : ndarray와 동일한 메모리 주소를 가리키는 뷰를 만듬
        x_np_3 = torch.from_numpy(np_array)
        print(x_np_3)
        x_np_3[0, 0] = 400 # 기존 메모리 주소의 ndarray 값을 변경하게 됨
        print(x_np_3)
        print(np_array)

 
tensor([[200,   2],
        [  3,   4]])
tensor([[400,   2],
        [  3,   4]])
[[400   2]
 [  3   4]]

 


torch.as_tensor() / from_numpy() 의 차이

  • torch.as_tensor: 입력 데이터가 이미 텐서인 경우 새로운 텐서를 만들지 않고 입력 텐서를 그대로 반환합니다. 또한, NumPy 배열 외에도 다양한 데이터 타입(리스트, 튜플, 다른 텐서 등)을 입력으로 받을 수 있습니다.
  • torch.from_numpy: 입력 데이터로 NumPy 배열만 받을 수 있습니다. NumPy 배열 이외의 데이터 타입을 입력으로 받을 수 없습니다.

 

  • 텐서 x_np_3를 NumPy 배열로 변환한 np_again과 그 데이터 타입을 출력

       
        np_again = x_np_3.numpy()
        print(np_again, type(np_again))


[[400   2]
 [  3   4]] <class 'numpy.ndarray'>

 

3.  파이토치 주요함수

* 다수의 행렬이 모이면 텐서라고 부름
* 배열이나 행렬과 매우 유사한 특수한 자료구조
* 파이토치는 텐서를 사용하여 모델의 입력과 출력, 모델의 매개변수들을 처리 사용됨

 

 

  •  텐서 a _ 모든 값이 1로 채워짐

       
        a = torch.ones(2, 3)
        print(a)


tensor([[1., 1., 1.],
        [1., 1., 1.]])

 

 

  • 텐서 b _ 모든 요소가 0으로 채워짐
       
 
        b = torch.zeros(2, 3)
        print(b)


tensor([[0., 0., 0.],
        [0., 0., 0.]])

 

 

  • 텐서 c  _ 내가 원하는 값으로 채우기

     
      c = torch.full((2,3), 10)
      print(c)

 
tensor([[10, 10, 10],
        [10, 10, 10]])

 

 

 

  • 텐서 d  _ 무작위 값 넣기

       
        d = torch.empty(2,3) # 실수 무작위값을 넣어줌
        print(d)


tensor([[2.1085e-07, 6.7703e+22, 3.3356e-09],
        [2.1707e-18, 7.1450e+31, 6.1970e-04]])

 

 

  • 단위 행렬(identity matrix)  e _ 대각선을 1로 채우기

      
      # 대각선을 1로 채우기
      e = torch.eye(5)
      print(e)


tensor([[1., 0., 0., 0., 0.],
        [0., 1., 0., 0., 0.],
        [0., 0., 1., 0., 0.],
        [0., 0., 0., 1., 0.],
        [0., 0., 0., 0., 1.]])

 

 

 

  • 텐서 f _  0부터 9까지의 정수

       
      f = torch.arange(10)
      print(f)


tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

 

  • 텐서 g _0과 1 사이의 무작위 실수

       
      g = torch.rand(2, 3) # 랜덤한 숫자를 나열해줌, 0~1 사이의 양수만 출력
      print(g)

 
tensor([[0.6799, 0.8935, 0.9098],
        [0.9568, 0.1631, 0.3808]])

 

 

  • 텐서 h _평균이 0이고 표준편차가 1인 정규 분포에서 추출한 무작위 수로 구성

       
        h = torch.randn(2, 3) # 평균이 0이고 표준편차가 1인 정규 분포에서 난수로 이루어진 텐서
        print(h)


tensor([[ 1.0582, -0.1366, -2.2529],
        [ 0.6362, -0.2156,  0.6600]])

 

 

  • 텐서 i _ 0부터 15까지의 정수

       
        i = torch.arange(16).reshape(2, 2, 4) # 2행 4열 2개
        print(i, i.shape)


 
tensor([[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7]],

        [[ 8,  9, 10, 11],
         [12, 13, 14, 15]]]) torch.Size([2, 2, 4])
차원 바꾸기
.reshape(2, 2, 4)는 이를 2x2x4 크기의 텐서로 재구성합니다. 즉, 2개의 2x4 행렬로 구성됩니다.

 

 

  • 텐서 i의 차원을 변경

       
      # 차원을 인덱스로 변환
      # i = {2, 2, 4} 2행 4열 두개

      k = i.permute((2, 0, 1))
      # 2, 2, 4 -> 4, 2, 2 : 2행 2열짜리 네개

      print(k, k.shape)
 
tensor([[[ 0,  4],
         [ 8, 12]],

        [[ 1,  5],
         [ 9, 13]],

        [[ 2,  6],
         [10, 14]],

        [[ 3,  7],
         [11, 15]]]) torch.Size([4, 2, 2])
  • i.permute((2, 0, 1))은 텐서 i의 차원을 변경합니다. 
  • 원래 텐서 i의 차원은 (2, 2, 4)입니다.
    이를 (2, 0, 1) 순서로 변경하면 각 차원의 위치가 바뀌어서,
    첫 번째 차원이 2번째 위치로, 두 번째 차원이 0번째 위치로, 세 번째 차원이 1번째 위치로 이동합니다.
  • 따라서 k는 4x2x2 크기의 텐서가 됩니다.
  • print(k, k.shape)는 텐서 k의 값을 출력하고, 그 크기(shape)를 출력합니다.

 

 

4.  텐서의 인덱싱과 슬라이싱

* 다수의 행렬이 모이면 텐서라고 부름
* 배열이나 행렬과 매우 유사한 특수한 자료구조
* 파이토치는 텐서를 사용하여 모델의 입력과 출력, 모델의 매개변수들을 처리 사용됨

 

 

 

  • 1부터 12까지의 정수로 구성된 1차원 텐서_ 3x4 크기의 텐서로 재구성

     
      a = torch.arange(1, 13).reshape(3, 4)
      print(a)


tensor([[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]])

 

  • 텐서 a의 특정 부분을 인덱싱하여 출력
    

      print(a[1])
      print(a[0, -1])
      print(a[1:-1])
      print(a[:2, 2:])


tensor([5, 6, 7, 8])
tensor(4)
tensor([[5, 6, 7, 8]])
tensor([[3, 4],
        [7, 8]])
  • print(a[1]): 인덱스 1에 해당하는 행을 출력합니다. 인덱스는 0부터 시작하므로 두 번째 행이 됩니다.
  • print(a[0, -1]): 첫 번째 행의 마지막 열의 값을 출력합니다. 인덱스 -1은 마지막 요소를 나타냅니다.
  • print(a[1:-1]): 인덱스 1부터 인덱스 -1 전(마지막에서 두 번째)까지의 행을 출력합니다.
  • print(a[:2, 2:]): 처음부터 인덱스 2 전(세 번째 열)까지의 행과, 인덱스 2부터 끝까지의 열을 출력합니다.

 

@.  코렙에서 GPU 사용하기

* 코랩에서 device 변경하는 방법
  * 상단 메뉴 -> 런타임 -> 런타임 유형변경 -> 하드웨어 가속기를 GPU로 변경 -> 저장 -> 다시 시작 및 모두 실행

 

 

 

 

  • PyTorch 텐서 생성 및 속성 출력

       
        tensor = torch.rand(3, 4)
        print(tensor)
        print(f'shape: {tensor.shape}')
        print(f'type: {type(tensor)}')
        print(f'dtype: {tensor.dtype}')
        print(f'device: {tensor.device}'


tensor([[0.7024, 0.5153, 0.5479, 0.8518],
        [0.9010, 0.1850, 0.0758, 0.7326],
        [0.7646, 0.0624, 0.5765, 0.8787]])
shape: torch.Size([3, 4])
type: <class 'torch.Tensor'>
dtype: torch.float32
device: cpu
  • torch.rand(3, 4): 3x4 크기의 텐서를 생성하여 무작위로 초기화합니다. 이 텐서는 0과 1 사이의 값을 갖습니다.
  • print(tensor): 생성된 텐서 tensor를 출력합니다.
  • print(f'shape: {tensor.shape}'): 텐서의 모양(shape)을 출력합니다. 여기서는 3행 4열을 나타냅니다.
  • print(f'type: {type(tensor)}'): 텐서의 타입을 출력합니다. Torch 텐서는 torch.Tensor 타입입니다.
  • print(f'dtype: {tensor.dtype}'): 텐서의 데이터 타입을 출력합니다. torch.rand()는 기본적으로 torch.float32 타입의 값을 생성합니다.
  • print(f'device: {tensor.device}'): 텐서가 저장된 장치(device)를 출력합니다. 여기서는 CPU에 저장되어 있습니다.

 

 

  • PyTorch 텐서 속성 및 GPU 사용 확인
    CPU 의 경우 

       
 
      # is_available() : gpu를 사용할 수 있는지 여부
      tensor = tensor.reshape(4, 3)
      tensor = tensor.int()
      print(f'shape: {tensor.shape}')
      print(f'type: {type(tensor)}')
      print(f'dtype: {tensor.dtype}')

      if torch.cuda.is_available():
        print('GPU를 사용할 수 있음')
        tensor = tensor.to('cuda')

      print(f'device: {tensor.device}')


tensor([[0.1793, 0.0093, 0.8758, 0.2619],
        [0.8945, 0.8254, 0.6933, 0.6634],
        [0.2428, 0.1457, 0.1283, 0.5310]])
shape: torch.Size([3, 4])
type: <class 'torch.Tensor'>
dtype: torch.float32
device: cpu

 

 

  • PyTorch 텐서 속성 및 GPU 사용 확인
    GPU로 설정

       
        # is_available() : gpu를 사용할 수 있는지 여부
        tensor = tensor.reshape(4, 3)
        tensor = tensor.int()
        print(f'shape: {tensor.shape}')
        print(f'type: {type(tensor)}')
        print(f'dtype: {tensor.dtype}')

        if torch.cuda.is_available():
          print('GPU를 사용할 수 있음')
          tensor = tensor.to('cuda')

        print(f'device: {tensor.device}')


shape: torch.Size([4, 3])
type: <class 'torch.Tensor'>
dtype: torch.int32
GPU를 사용할 수 있음
device: cuda:0

 

  • if torch.cuda.is_available(): GPU가 사용 가능한지 확인합니다.
    tensor = tensor.to('cuda'): GPU가 사용 가능하면 텐서를 GPU 메모리로 이동시킵니다.
  • print(f'device: {tensor.device}'): 텐서가 저장된 장치(device)를 출력합니다.
    GPU가 사용 가능할 경우 'cuda'로 출력됩니다
만약 GPU가 사용 가능하면, 텐서는 CPU에서 GPU로 이동될 것이고
만약 GPU가 사용 불가능하면, 텐서는 그대로 CPU에 남아있을 것입니다.