딥러닝 공부

골빈해커의 3분 딥러닝 텐서플로맛_ch.4 기본 신경망 구현 (텐서플로우 2.0)

Woonys 2021. 7. 24. 02:19
반응형

이번에는 챕터 4, 인공 신경망에 대해 쓴다. 자세한 내용은 책을 구입하셔서 보시고! 여기서는 코드 예제 변환 위주 + 공부한 내용 필기에 집중하련다.

 

4.1 인공신경망 작동 원리

 

인공 뉴런의 기본 원리는 아래와 같다.

인공 뉴런 = 활성화함수((입력값 * 가중치)+편향)
  • 입력값(X): 입력 신호
  • 가중치(W, weight)
  • 편향(b, bias)
  • 활성화 함수(activation function): 인공신경망을 통과해온 값을 최종적으로 어떤 값으로 만들지 결정하는 함수
    • 여기서 가장 중요한 게 activation function이라고 해도 과언이 아님.
    • 대표 예시: sigmoid, ReLU, tanh 함수 등
    • 학습 목적에 따라 그에 맞게 함수를 쓴다.

정리하면

  1. 인공뉴런은 가중치와 활성화 함수로 연결된다.
  2. 뉴런을 단순히 많이 연결하는 것만으로도 패턴을 학습한다.
  3. But 수천~수만 개 조합을 만들려면 상당한 computing power가 필요하다 => 옛날에 딥러닝이 잘 되지 않았던 이유

이어서 역전파(Back Propagation)가 나온다. 예전에 <밑바닥부터 시작하는 딥러닝>에서 열심히 공부했던 기억이 난다. 여기서는 정말정말 간략하게 얘기하는데, 그마저도 편미분 내용조차 없다. 그러니 나도 패스.

 

한 문장만 쓰자면: 출력층이 내놓은 결과에 대한 오차를 각 신경망을 따라 입력층까지 역으로 전파하여 계산하는 방식이며, 편미분을 이용한다.

 

이외에도 드롭아웃 등 중요한 개념에 대한 용어가 등장하지만 비중있게 다루지는 않으니 이 책을 마치고 제대로 파봐야겠다.

 

4.2 간단한 분류 모델 구현하기

시작 전에 용어부터 정리하면

  • 분류(Classification): 패턴을 파악해 여러 종류로 구분하는 작업
  • 원-핫 인코딩(One-hot encoding): 데이터가 가질 수 있는 값들을 일렬로 나열한 배열을 만들고 그 중 표현하려는 값을 뜻하는 인덱스 원소만 1로 표기/ 나머지는 모두 0으로 표기하는 기법을 의미.

이 예제에서는 털과 날개가 있느냐를 기준으로 포유류와 조류를 구분하는 신경망 모델을 만드는 작업을 한다. 이미지 대신 간단한 binary 데이터로 해본다.

 

#1. import tf and np
import tensorflow as tf
import numpy as np

#2. 학습에 사용할 데이터 정의 => 털, 날개: 있으면 1 없으면 0
#[털, 날개]
x_data = np.array(
	[[0,0], [1,0], [0,0], [0,0], [0,1]]) #원-핫 인코딩

#판별 개체 종류 => 기타, 포유류, 조류
#기타 = [1,0,0]
#포유류 = [0,1,0]
#조류 = [0,0,1]

#실측값 데이터 정의 => 레이블 데이터 (Ground Truth, GT라고도 함. 정답지라는 뜻)
y_data = np.array([
	[1,0,0], #기타
    [0,1,0], #포유류
    [0,0,1], #조류
    [1,0,0],
    [1,0,0],
    [0,0,1]
])

#x, y 간 상관관계
	[0,0] => [1,0,0] / 털X, 날개X: 기타
    [1,0] => [0,1,0] / 털O, 날개X: 포유류
    [0,1] => [0,1,0] / 털X, 날개O: 조류
    [1,1] => [0,0,1] / 털O, 날개O: 조류

학습 & 정답(레이블) 데이터셋을 만들었으면 다음은 모델 설계다. 앞선 챕터에서 keras를 활용하면 훨씬 간단하게 모델을 짤 수 있음을 배웠다. 여기서는 좀 더 디테일하게 설명을 해보겠다.

 

#모델 설계
model = tf.keras.models.Sequential([
	tf.keras.layers.Dense(3, activation = 'relu', kernel_initializer='random_uniform', bias_initializer='zeros'),
    tf.keras.layers.Dense(3, activation = 'softmax')
])
  • tf.keras.models.Sequential(): 케라스를 활용한 신경망을 구축할 때는 두 종류 - 순차적(sequential)/함수적(functional) 신경망이 있다. 함수적 신경망은 아직 제대로 배우지 못했으니 순차적 신경망만 말하자면, 말 그대로 x_data가 들어가는 왼쪽에서부터 layer를 순차적으로 쌓는다는 말이다. 가장 왼쪽 라인 레이어를 1번 레이어, 2번, ... 총 n번째 레이어까지 내려간다.
  • tf.keras.layers.Dense(): Sequential 신경망을 짜겠다고 선언했으면 각 레이어에 해당하는 코드를 짜야 한다. 이때 Dense()는 해당 레이어의 노드 수(=Dense)를 결정한다. 여기서 3은 노드가 3개라는 뜻이다. 신경망이 1개 층이고 예측해야 할 값이 셋(기타, 조류, 포유류) 중 하나를 맞춰야 하는 상황이니 그에 맞게 노드 역시 3개가 있어야 한다. 신경망의 마지막 레이어의 노드 수는 정답지 내 원소 수과 같아야 한다.
    • activation: 활성화 함수 종류를 선택한다. 여기서 첫번째 신경망은 ReLU, 두번째는 softmax를 썼다.
    • kernel_initializer = 가중치(W)를 어떻게 설계할지 결정한다. 여기서는 random.uniform을 골랐다.
    • bias_initializer = 편향(b)을 선택한다. 여기서는 0으로 했다.

잠깐! 여기서 뭔가 이상함을 느껴야 한다. 원래 W, b는 행렬인데 왜 행렬 shape를 정하지 않지?!?!

 

그것은 케라스 모델 함수가 학습 데이터(X)의 열 수를 W와 b의 행 값으로 알아서 받아오기 때문이다. X, W, b의 shape를 각각 (x1, x2), (w1, w2), (b1, b2)라고 하면 x2 =w1=b1이어야 행렬 계산이 가능하다. 어차피 Dense에서 설정하는 노드 수가 w2 값이 되고 b2는 차피 다 1이니까 상관없다. 그래서 shape를 정할 필요가 없다.

 

이어서 모델 컴파일 및 학습 실행이다. 모델 컴파일에서는 loss function 및 학습에 쓸 optimizer의 종류 고르기, 학습에서 어떤 메트릭으로 측정할 것인지 등을 고른다. 그 다음은 model.fit()으로 실제 학습을 돌린다.

#모델 컴파일
model.compile(optimizer = 'SGD',
	loss = 'categorical_crossentropy',
    metrics=['accuracy'])

#모델 학습
model.fit(x_data, y_data, epochs=1000)

추가로 cross entropy(교차 엔트로피)만 조금 설명하자. 자세한 설명은 구글링으로 확인할 수 있으니, 간단하게만 설명한다.

 

Cross Entropy는 예측값과 실제값 사이 확률 분포 차이를 계산한 값이다. 주로 원-핫 인코딩을 이용한 대부분 모델에서 사용한다. 왜냐? 원-핫 인코딩에서 출력값은 0, 1의 binary이다. 어디서 많이 본 것 같다? 그렇다, 확률값의 범위 역시 0과 1 사이다. 위에서 softmax를 사용한 것 역시 확률값을 뽑아내기에 좋은 비선형 활성화 함수기 때문이다.

 

Cross entropy를 계산하는 방식은 다음과 같다.

cost = tf.reduce_mean(-tf.reduce_sum(Y*tf.log(model), axis=1))

이를 해석해보자. 가장 안쪽 괄호부터 천천히 살펴본다.

 

1. Y*tf.log(model): 먼저, Y와 tf.log(model)이 보인다. 쫄지 말자. Y는 실측값(위의 실측값 데이터(행렬 형태)), model은 신경망을 통해 나온 예측값이다. 이 둘을 행렬곱이 아니라 동일 행, 동일 열에 있는 원소끼리 곱한다.

 

2. -tf.reduce.sum(1): tf.reduce_sum은 텐서 내의 모든 원소 값을 각 행별로 다 더하는 연산자다. 예를 들어 A=[[1, 2, 3], [4, 5, 6]]이라는 2*3 행렬이 있다고 하자. -tf.reduce_sum(A) = [[1+2+3], [4+5+6]] = [-6, -15]가 된다.

 

3. tf.reduce_mean(2): 여기서도 역시 텐서 내 모든 원소를 행별로 평균을 내는 것이다. 위의 경우로 보면 (-6-15)/2 = -10.5이다. 이것이 최종 loss값이 된다.

 

다음 절에서는 심층 신경망을 짜는데 차피 위의 내용만 잘 숙지하고 있다면 뒤는 같은 내용이니 여기서는 생략하도록 하겠다.(필요하시면 댓글주세요!)

반응형