열심히 코딩 하숭!

1. 딥러닝에 꼭 필요한 파이썬의 개념 | 파이토치 딥러닝 프로그래밍 본문

프로그래밍 책/파이토치 딥러닝 프로그래밍

1. 딥러닝에 꼭 필요한 파이썬의 개념 | 파이토치 딥러닝 프로그래밍

채숭이 2024. 4. 15. 17:24

차근차근 실습하며 배우는 파이토치 딥러닝 프로그래밍 | 위키북스

 

 

3월달부터 개발 동아리에 들어가게 되었다! 현재까지 동아리 부원들과 함께 딥러닝 스터디를 진행하고 있다

스터디에서 " 차근차근 실습하며 배우는 파이토치 딥러닝 프로그래밍" 교재를 활용하여 공부하고 있기 때문에

이에 대해 정리한 개념을 티스토리에 남기려고 한다~🙉

 

 

 


 

0. 기본적인 동작 구조

⇒ 딥러닝 기본 구조: 실제값과 예측값의 차이인 오차를 최소로 만들기 위해 손실 함수를 최소화하는 모델 파라미터를 경사 하강법을 토대로 수정해나간다

⇒ 파이토치: 위에서 말한 동작 구조를 다음과 같은 함수를 사용해 구현한다

💡 함수명이 새로워서 그런지, 딥러닝을 처음 공부하는 것이 아닌데 생소하게 느껴짐
어느정도 공부한 후에 해당 그림을 다시 살펴보는 것으로 하자

 

1. 딥러닝에 꼭 필요한 파이썬의 개념

1.2 컨테이너 타입 변수에 주의

1) 컨테이너 타입

  • 실제 데이터에 액세스하기 위해 인덱스를 거쳐야하는 데이터 (→ list, numpy array)

2) 문제점

  • numpy의 array: 변수 설정 시 포인터가 사용되므로, 값 변경 시 두 변수의 array값이 함께 변경되는 것처럼 보일 수 있다
  • torch의 Tensor: 텐서를 array로 변환하여 저장하였을 때, tensor값이 바뀌면 array값도 같이 변경된다
import numpy as np

x = np.array([1,2,3])
y = x
x[1] = 0 # 값 변경
print(x)
print(y)

# 결과 #
[1 0 3]
[1 0 3]
import torch

x1 = torch.ones(5)
x2 = x1.data.numpy()
x1[1] = -2 # 값 변경
print(x1)
print(x2)

# 결과 #
tensor([1., -2., 1., 1., 1.]
[1. -2. 1. 1. 1.]

 

3) 해결 방법

  • x2가 x1을 참조하지 않으려면 copy()함수를 사용하면 된다
x1 = torch.ones(5)
x2 = x1.data.numpy().copy()

 

4) 궁금증

  • tensor끼리도 포인터를 통해 값이 저장되나?
    • 맞음!

 

1.3 ‘합성함수’를 파이썬으로 구현하기

1) 합성함수

  • 어떤 함수의 출력이 다른 함수의 입력이 되는 과정 f( g(x) )

 

1.4 ‘미분’과 파이썬의 ‘수치 미분’ 구현하기

1) 함수의 함수

  • 함수를 인수로 받아서 함수를 반환하는 것

2) 수치 미분

  • h에 매우 작은 값을 넣어 수치 계산을 통해 근사치를 구한다

      ↔ 해석적 미분은 우리가 아는 미분 공식이라고 생각하면 된다

 

3) 코드를 통해 확인

  • fdiff(f) 함수 | sigmoid 함수에 적용
def fdiff(f):
	def diff(x):
		h = 1e-6
		return (f(x+h)-f(x-h)) / (2*h)
	
	return diff

# f의 미분함수인 diff를 얻는다
diff = fdiff(f) 
# x라는 특정 점에서의 미분값(기울기)을 계산한다
y_dash = diff(x) 
print(y_dash)
def g(x):
	return 1 / (1+np.exp(-x))
	
diff = fdiff(g)
y_dash = diff(x)
print(y_dash)
  • 근사값을 활용하여 fdiff함수를 작성하니, 복잡한 합성함수의 미분값을 간편하게 구할 수 있다

4) 딥러닝에서

  • 딥러닝의 학습 알고리즘은 ‘손실’이라고 하는 방대한 파라미터를 가진 복잡한 합성 함수의 미분계산을 수행하고, 그 결과를 이용해서 최적의 파라미터 값을 구하는 구조다
  • 때문에 파이토치수치미분을 사용한 미분계산을 간단히 처리하는 구조를 내장하고 있다

 

1.5 커스텀 클래스 정의하기

  • 파이토치로 머신러닝 또는 딥러닝 모델을 구축할 경우, 커스텀 클래스의 정의가 필요하다

1) 클래스 기본 형태

import matplotlib.pyplot as plt
import matplotlib.patches as patches

class Point:

	def __init__(self, x, y): # 생성자
		self.x = x
		self.y = y
		
	def draw(self): # 클래스의 함수
		plt.plot(self.x, self.y, marker='o', markersize=10, c='k')
		
p1 = Point(2, 3)
print(p1.x, p1.y)
p1.draw()
  • self의 개념
    • Class 타입의 어떤 객체가 method를 호출하면, Python은 그 객체를 self 매개변수를 통해 메서드에 전달한다
    • 이를 통해 메서드 내에서는 self를 사용하여 해당 인스턴스의 다른 속성이나 메서드에 접근할 수 있다
    • 실제로 함수를 호출할 때는 self인수는 지정하지 않는다 (python이 자동으로 처리해준다!)

2) 클래스 상속 기본

class Circle1(Point): # Point라는 클래스를 상속받음

	def __init__(self, x, y, r):
		super().__init__(x,y) # x, y는 부모 클래스의 __init__함수를 호출하여 설정한다
		self.r = r
		
c1_1 = Circle1(1, 0, 2)
c1_1.draw() # <= 부모 클래스의 draw 함수가 출력된다

 

3) 오버라이드

class Circle2(Point):

	def __init__(self, x, y, r):
		super().__init__(x,y)
		self.r = r
		
	def draw(self): # 부모 클래스에 있던 함수를 오버라이딩하여 재정의한다
		c = patches.Circle(xy=(self.x,self.y), radius = self.r, fc='b', ec='k')
		ax.add_patch(c)
		
c2_1 = Circle2(1, 0, 2)
c2_1.draw() 
  • Circle2 클래스에서 정의한 draw() 함수가 실행된다
  • 오버라이드: 부모 클래스와 같은 이름의 함수를 자식 클래스에서 역할을 달리 정의하는 것

4) 오버라이드 - 부모 클래스의 함수를 불러오기

class Circle3(Point):

	def __init__(self, x, y, r):
		super().__init__(x,y)
		self.r = r
		
	def draw(self): # 부모 클래스에 있던 함수를 오버라이딩하여 재정의한다
		super().draw() # 부모 클래스의 함수를 불러온다
		c = patches.Circle(xy=(self.x,self.y), radius = self.r, fc='b', ec='k')
		ax.add_patch(c)
		
c3_1 = Circle2(1, 0, 2)
c3_1.draw()
  • super().함수()를 사용하여 부모 클래스의 함수를 불러온다

1.6 인스턴스를 함수로 사용하는 방법

1) call - 객체를 함수처럼 호출

class H:

	def __call__(self, x):
		return 2*x**2 + 2

x = np.arange(-2, 2.1, 0.25)

h = H()
y = h(x)
  • 클래스로 생성한 인스턴스함수로 사용할 수 있다
  • pytorch 라이브러리 내부의 다양한 부분에서 이 방법을 사용하고 있다. 알아두기!
💡 나중에 pytorch 코드 리뷰할 때 헷갈리지 않도록 잘 숙지하기!