2-1. Gradient descent 알고리즘 구현하기

Gradient descent 알고리즘은 손실 함수(loss function)의 미분값gradient를 이용해 모델에게 맞는 최적의 가중치(weight), 즉 손실 함수의 값을 최소화 하는 가중치를 구할 수 있는 알고리즘입니다.

이번 실습에서는 Gradient descent 알고리즘을 직접 구현한 후, 이를 이용해 데이터를 가장 잘 설명하는 선형 회귀 직선의 기울기와 y절편, 즉 선형 회귀 모델에게 맞는 최적의 가중치를 찾아보겠습니다.

선형 회귀 직선의 수식은 다음과 같은 1차 함수 형태이며, 우리가 Gradient descent 알고리즘을 사용해 찾을 값, 즉 가중치는 w0w0과 w1w1입니다.

f(x)=w0+w1xf(x)=w0+w1x


손실 함수 (loss function)

손실 함수(loss function)는 실제값과 모델이 예측한 값 간의 차이를 계산해주는 함수입니다. 손실 함수의 값은 가중치와 편향을 업데이트하는 데에 사용됩니다. 여기서는 손실 함수로 MSE (Mean Squared Error)를 사용합니다.

MSE는 평균 제곱 오차 함수로, 수식은 다음과 같습니다.

Loss(w0,w1)=1N∑i=1N(yi−f(xi))2=1N∑i=1N(yi−(w0+w1xi))2Loss*(*w*0,*w*1)=*N*1*i*=1∑*N*(*yif(xi*))2=*N*1*i*=1∑*N*(*yi−(w0+w1x**i))2

여기서 yiyi*는 실제값, f(xi)=w0+w1xi*f*(*xi)=w0+w1xi*는 모델 f(x)*f*(*x*)에 xi*xi를 넣어서 나온 예측값입니다.


편미분

gradient에는 편미분이라는 개념이 들어갑니다. 때문에 gradient를 설명하기 전 편미분에 대해 간단하게 짚고 넘어가겠습니다. 편미분이란 2개 이상의 변수를 가진 함수에서 우리가 미분할 하나의 변수를 제외한 나머지 변수들을 상수로 보고, 미분 대상인 그 변수로 미분하는 것입니다.

예를 들어 f(x,y)=2x2+yf(x,y)=2x2+y 라는 수식이 있을 때, xx에 대해서만 편미분한다면 (x,y)=∂f(x,y)∂x=4x(x,y)=∂xf(x,y)​=4x 가 되는 것입니다.


Gradient

gradient는 곧 기울기 벡터를 의미하며, 선형 함수의 각 파라미터들의 편미분으로 구성된 열벡터로 정의합니다.

강의 자료 9페이지를 보면 학습률(learning rate)을 나타내는 αα가 있고, gradient를 나타내는 수식인 ▽Loss(W)▽L**oss(W)가 있습니다. 즉 이를 풀어서 쓰면 다음과 같은 열벡터 형태입니다.

gradient=▽Loss(W)=(∂Loss∂w0∂Loss∂w1∂Loss∂wp)gradient*=▽*Loss(W)=⎝⎛∂w0∂Loss*∂*w*1∂*Loss⋮∂wp*∂*Loss⎠⎞

따라서 우리가 구해야 할 w0w0와 w1w1에 대한 gradient는 다음과 같습니다.

gradient=[∂Loss∂w0,∂Loss∂w1]gradient*=[∂*w*0∂*Loss,∂w1∂L**oss]

w0w0과 w1w1에 대한 gradient를 구하기 위해 LossL**oss를 각각에 대해 편미분하면 다음과 같습니다.

∂Loss∂w0=2N∑i=1N(yi−(w0+w1xi))(−1)∂w0∂Loss*=*N*2*i*=1∑*N*(*yi−(w0+w1xi*))(−1)∂Loss∂w1=2N∑i=1N(yi−(w0+w1xi))(−xi)∂*w*1∂*Loss=N2i=1∑N(yi*−(*w*0+*w*1*xi))(−x**i)


가중치 업데이트

위와 같이 구한 w0w0와 w1w1의 gradient와 학습률 αα를 이용해 가중치를 업데이트하는 공식은 다음과 같습니다.

w0t+1=w0t−α∂Loss∂w0w0t+1=w0tαw0∂Loss*w1t+1=w1t−α∂Loss∂w1*w*1*t*+1=*w*1*t*−*α*∂*w*1∂*Loss

실습

  1. 설명 중 ‘손실 함수’ 파트의 수식을 참고해 MSE 손실 함수를 완성하세요.
  2. 설명 중 ‘Gradient’ 파트의 마지막 두 수식을 참고해 w0w1에 대한 gradient인 gradient0gradient1을 반환하는 함수를 완성하세요.
  3. 설명 중 ‘가중치 업데이트’ 파트의 두 수식을 참고해 gradient descent를 통한 가중치 업데이트 코드를 작성하세요.

2-2. 역전파(Back propagation)

역전파(Back propagation)는 다층 퍼셉트론 모델을 이루는 가중치들을 개선하기 위해 개발된 여러 알고리즘들 중 가장 유명하고 널리 쓰이는 방법입니다.

이번 실습에서는 역전파를 간단하게 실습해보기 위해, 퍼셉트론 한 개의 가중치들을 개선시키는 역전파를 구현해 보도록 합니다.


다음 그림은 이번 실습에서 사용되는 퍼셉트론을 나타냅니다. 입력은 x1,x2,x3x1,x2,x3 세 개의 정수로 주어지고, 각각 w1,w2,w3w1,w2,w3의 계수가 곱해져 sigmoid 함수를 통과할 값은 x1w1+x2w2+x3w3x1w1+x2w2+x3w3가 됩니다.

여기서 w1,w2,w3w1,w2,w3가 바로 우리가 이번 실습에서 알아내야 하는 가중치입니다.

image1

x1w1+x2w2+x3w3x1w1+x2w2+x3w3가 sigmoid 함수를 거치고 나면 0 ~ 1 사이의 값으로 변환됩니다. 이는 특정 클래스로 분류될 확률을 나타내며, 0.5보다 작을 경우 0으로, 0.5보다 클 경우 1로 분류된다고 합시다.


이제 이 퍼셉트론을 학습시키려고 합니다. 좀 더 정확히 이야기하면, x1,x2,x3x1,x2,x3와 그 클래스 yy가 여러 개 주어질 때, yy값을 가장 잘 예측하는 w1,w2,w3w1,w2,w3를 찾아야 합니다.

예를 들어, 우리가 갖고 있는 훈련용 데이터가 다음과 같이 3개로 주어진다고 합시다.

그렇다면 w1w1 = 0, w2w2 = 0, w3w3 = 1 이어야 함을 알 수 있습니다.


물론 이와 같은 최적의 w1w1, w2w2, w3w3 값을 처음부터 알 수는 없습니다. 따라서 우선 가중치 w*w*들을 초기화하고, 이를 여러 번의 학습을 거쳐 알아내야 합니다.

즉, 손실 함수(loss function)의 gradient 값을 역전파해서 받은 후, 그 값을 참고하여 손실 함수값을 최소화 하는 방향으로 w1,w2,w3w1,w2,w3를 업데이트 합니다.

이때, w1,w2,w3w1,w2,w3이 잘 개선되서 더 업데이트해도 변화가 거의 없을 때까지 하는 것이 중요합니다.

실습

코드의 주석 설명을 따라서 getParameters(X, y) 함수를 완성하세요. 여기서 Xy는 훈련용 데이터입니다. 필요하다면 설명의 굵은 글씨 부분을 참고하세요.

  1. X의 한 원소가 3개이므로 가중치도 3개가 있어야 합니다. 초기 가중치 w를 [1,1,1]로 정의하는 코드를 작성하세요.
  2. 초기 가중치 w를 모델에 맞게 계속 업데이트 해야합니다. 업데이트를 위해 초기 가중치 w에 더해지는 값들의 리스트 wPrime을 [0,0,0]로 정의하는 코드를 작성하세요.
  3. sigmoid 함수를 통과할 r값과 sigmoid 함수를 통과한 r값인 v를 정의하세요.
  4. 가중치 w가 더이상 업데이트가 안될 때까지 업데이트해주는 코드를 작성하세요. 자세한 내용은 코드 주석을 참고하세요.

더 나아가 여러 예제를 테스트 해 보면서 하나의 퍼셉트론이 100% 예측할 수 없는 훈련용 데이터는 어떤 것이 있는지 생각해보세요.

이는 1장에서 배웠듯이 왜 다층 퍼셉트론 모델이 데이터 분류에서 필요한지 알게 해줍니다.

2-3. 텐서플로우(TensorFlow) 버전 비교하기

텐서플로우

텐서플로우는 데이터 플로우 그래프를 사용하여 수치 연산을 하는 오픈소스 소프트웨어 라이브러리입니다.

CPU나 GPU를 사용하여 연산을 구동시킬수 있으며, 머신러닝과 딥러닝 연구를 목적으로 구글 브레인 팀이 2015년에 개발했습니다.

2019년, 텐서플로우 2.0 버전이 공개되었습니다. 따라서 최근 코드들은 텐서플로우 2.0으로 작성되어 있습니다. 하지만 당연하게도 텐서플로우 2.0이 나오기 이전 코드들은 모두 텐서플로우 1.x 버전입니다.

따라서 머신러닝 및 딥러닝에 대한 넓고 깊은 이해를 위해선 텐서플로우 2.0뿐만 아니라 텐서플로우 1.x에 대한 지식을 갖고 있어야 합니다.

이번 실습에서는 텐서플로우 1.x와 텐서플로우 2.0으로 간단한 연산을 해보고, 이를 통해 각 버전을 비교해보도록 하겠습니다.


텐서플로우 1.x

tensor

기존 텐서플로우 1.x 버전에서는 위와 같이 그래프 구조를 이용해 연산을 하도록 구성되어 있습니다.

그래프를 사용하기 위해 만들어진 tf.Session()은 상수, 변수, 오퍼레이션을 선언하고 실질적으로 그래프를 실행하기 위한 객체입니다.

1.x 버전에서는 Session에서 모든 학습과 계산을 진행합니다. 따라서 값을 계산하고 출력하기 위해서는 Session을 반드시 실행시켜야합니다.

텐서플로우 2.0

그러나 텐서플로우 2.0에서는 파이썬의 함수처럼 바로바로 계산되어 사용할 수 있습니다. 이를 즉시 실행 (Eager Excution) 모드라고 합니다.

실습

코드를 따라 작성해보면서 각각의 텐서플로우 버전이 어떻게 구성되어 있고 작동하는지 이해해보세요.

  1. 다음은 ‘텐서플로우 1.x‘의 덧셈 연산 코드입니다. 똑같이 작성해보고 결과를 확인해보세요.
# 계산 정의
add_op = a + b
 
# 세션 시작
sess = tf.Session()
result_tf1 = sess.run(add_op)
Copy
  1. 다음은 ‘텐서플로우 2.0‘의 덧셈 연산 코드입니다. 텐서플로우 1.x보다 코드가 간단해진 것이 느껴지시나요? 똑같이 작성해보고 결과를 확인해보세요.
result_tf2 = tf.add(a, b)

2-4. 텐서(Tensor) 데이터 생성

텐서플로우는 상수, 시퀀스, 난수, 변수 등을 텐서(Tensor)형으로 생성하는 연산을 제공합니다. 이러한 연산은 기존 Numpy와 유사하게 사용할 수 있습니다.

또한, 텐서플로우에는 다양한 자료형을 사용할 수 있습니다. 이를 이용하면 어떤 데이터든지 구조화된 형식으로 저장할 수 있습니다.


텐서플로우 자료형

이번 실습에서는 텐서플로우의 다양한 함수와 자료형을 사용하여 직접 상수, 시퀀스, 난수, 변수 등을 생성해보도록 하겠습니다.

실습

  1. 상수 텐서를 생성하는 constant_tensors 함수를 완성하세요.
  2. 시퀀스 텐서를 생성하는 sequence_tensors 함수를 완성하세요.
  3. 변수를 생성하는 variable_tensor 함수를 완성하세요.
  4. 실행 버튼을 눌러 결과값을 확인하고 제출해보세요.
    • 텐서에 .numpy 메소드를 적용하면 numpy 배열로 변환됩니다.

2-6. 텐서(Tensor) 연산

이번 실습에서는 텐서플로우에서 제공하는 이항 연산자들을 활용하여 텐서들의 연산을 수행해보겠습니다.


실습

  1. 이항 연산자를 사용해 사칙 연산을 수행하여 각 변수에 저장하세요.

2-7. 텐서플로우 계산기 구현하기

텐서플로우를 이용해 간단한 실수 사칙연산 계산기를 만들어보겠습니다.

앞서 배운 텐서플로우의 다양한 연산자를 사용하여 직접 여러 수를 입력해보고 결과를 확인해보세요.

입력을 필요로 하는 실습입니다. 실행 버튼을 누른 후 출력되는 메시지에 따라 입력해주세요!

실습

  1. 두 정수나 실수 x, y와 연산 종류 문자 cal을 입력받는 insert() 함수를 살펴보세요.
    • 연산의 종류인 cal은 +, -, *, / 중 한개의 문자를 입력 받습니다.
  2. 입력받는 연산의 종류 cal에 따라 연산을 수행하고 결과를 반환하는 calcul() 함수를 완성하세요.
    • 모든 연산은 텐서플로우 내장 함수를 이용해 구현해주세요.
  3. 실행 버튼을 눌러 출력되는 메시지에 따라 두 정수와 연산 종류를 입력하고, 결과를 확인한 후 제출해보세요.

2-8. 텐서플로우를 활용하여 선형 회귀 구현하기

선형 회귀란 데이터를 가장 잘 설명하는 선을 찾아 입력값에 따른 미래 결괏값을 예측하는 알고리즘입니다. 선형 회귀 직선의 수식은 다음과 같습니다.

y=W∗X+by=WX+b

이번 실습에서는 텐서플로우를 활용해 손실 함수와 선형 회귀 직선을 직접 구현한 후, 모델의 학습 과정을 통해 가중치(Weight)와 Bias가 어떻게 변화되는지 살펴보도록 하겠습니다.

여기서는 손실 함수로 MSE를 사용합니다. 손실 함수 및 MSE에 대한 자세한 설명은 [실습1]을 참고하세요.


클래스(Class)

실습 코드에 클래스에 관한 내용이 나오기에 간단히 짚고 넘어가겠습니다. 이미 알고 계시다면 넘어가도 좋습니다.

클래스(Class)는 객체를 만들 수 있는 틀이며, 객체(Object)는 ‘성질’과 ‘할 수 있는 행동’이 담긴 자료입니다.

이때 ‘성질’에 해당하는 것을 필드(Field)라 하고, 이는 객체가 가지고 있는 변수에 해당합니다. 그리고 ‘할 수 있는 행동’에 해당하는 것을 메소드(Method)라 하고, 이는 객체가 동작시킬 수 있는 함수에 해당합니다.

인스턴스(instance)는 객체를 만들 수 있는 틀(클래스)로 찍어낸 객체입니다. 클래스가 붕어빵 틀이라면 인스턴스는 팥 붕어빵, 슈크림 붕어빵 등등이라고 비유할 수 있습니다.

self 는 메소드라면 가져야 할 첫번째 매개변수입니다.

def __init__(self): 는 클래스의 초기 인스턴스를 설정해주는 부분입니다. 클래스에서 사용되는 변수들의 초기값이라고 이해해도 좋습니다.

실습

  1. 지금까지 배운 텐서플로우에서의 변수 텐서 설정법과 이항 연산자 함수를 사용하여 선형 회귀 모델의 클래스를 구현하세요.
  2. MSE 값을 계산해 반환하는 손실 함수 loss를 완성하세요.
  1. train() 함수를 보면서 어떤 방식으로 가중치와 Bias가 업데이트되는지 확인하세요.

실행 버튼을 눌러 모델 학습에 따라 변화되는 loss 값과 가중치, Bias를 확인하고 제출하세요.

2-9. 텐서플로우와 케라스를 활용하여 비선형회귀 구현하기

이번 실습에서는 텐서플로우와 케라스(Keras)를 활용하여 다층 퍼셉트론 모델을 직접 만들어 보고, 이를 활용해 비선형 데이터를 회귀 예측 해보겠습니다.

케라스는 텐서플로우 내의 딥러닝 모델 설계와 훈련을 위한 API 입니다. 케라스는 연속적으로(Sequential) 레이어(Layer)들을 쌓아가며 모델을 생성하고, 사이킷런과 같이 한 줄의 코드로 간단하게 학습 방법 설정, 학습, 평가를 진행할 수 있습니다.


텐서플로우와 케라스를 이용해 다층 퍼셉트론 모델을 만들기 위한 함수/메서드

이 외에도 다양한 인자가 존재하는데 추가적인 인자에 대한 정보는 아래 링크를 통해 확인할 수 있습니다.

tf.keras.layers.Dense 함수의 추가 인자 확인하기

실습

  1. Sequential 모델 안의 내부 인자로 Dense 함수를 이용하여 여러 층의 레이어를 쌓아 다층 퍼셉트론 모델을 만들 수 있습니다. tf.keras.models.Sequential 내에tf.keras.layers.Dense() 함수를 이용하여 아래 다층 퍼셉트론 모델을 만드세요.
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(20, input_dim = 1 ,activation='relu'),
    tf.keras.layers.Dense(20, activation='relu'),
    tf.keras.layers.Dense(1)
])
Copy
  1. 모델을 학습시킬 손실 함수(loss function) 계산 방법과 최적화(optimize) 방법을 다음과 같이 설정합니다.
  1. 생성한 모델을 500 epochs 만큼 학습시킵니다. verbose 인자에는 0,1,2 값을 설정할 수 있으며, 이는 모델 학습 과정 정보를 얼마나 자세하게 출력할지를 설정합니다.

    history = model.fit(x_data, y_data, epochs=500, verbose=2)
    Copy
    
  2. 학습한 모델을 사용하여 x_data에 대한 예측값을 생성하고, 이를 predictions에 저장합니다.

    predictions = model.predict(x_data)
    Copy
    

실행 버튼을 눌러 결괏값을 확인하고 제출하세요.

2-10. 텐서플로우로 XOR 문제 해결하기

이전 단원 ‘퍼셉트론’에선 And_gate, OR_gate, NAND_gate를 활용해서 비선형 회귀 문제였던 XOR_gate를 구현해보았습니다.

이번 시간에는 앞선 실습에서 배운 텐서플로우와 케라스를 활용해서 다층 퍼셉트론 모델을 자유롭게 만들어보고, 이를 활용해 XOR 문제를 100% 해결해보겠습니다.

XOR gate 입출력 표

Input (x1) Input (x2) XOR Output(y)
0 0 0
0 1 1
1 0 1
1 1 0

실습

  1. 다층 퍼셉트론 모델을 생성합니다. 이번엔 tf.keras.models.Sequential()model이라는 변수로 선언하고, add메소드를 사용해서 Sequential 모델을 만들어 봅시다.

    레이어의 개수나 레이어 내부의 노드 수를 자유롭게 설정해서 최종 정확도 100%가 되도록 해봅시다.

    단, 중간층(히든층)의 활성화 함수는 relu, 출력층의 활성화 함수는 sigmoid로 고정하고 모델을 만들도록 합니다.

    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(16, input_dim=2, activation='relu'))
    model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
    Copy
    
  2. 모델을 학습시킬 손실 함수(loss function) 계산 방법과 최적화(optimize) 방법, 성능 측정 방법(metrics)을 다음과 같이 설정합니다.

  1. 생성한 모델을 학습시킵니다. 우리의 목표를 위해 epochs도 자유롭게 설정해봅니다.

2-10. Fashion-MNIST 데이터 분류하기

Fashion-MNIST 데이터란 의류, 가방, 신발 등의 패션 이미지들의 데이터셋으로 60,000개의 학습용 데이터 셋과 10,000개의 테스트 데이터 셋으로 이루어져 있습니다.

각 이미지들은 28x28 크기의 흑백 이미지로, 총 10개의 클래스로 분류되어 있습니다.

이번 실습에서 사용하는 데이터는 모델 학습을 위해 28x28 크기의 다차원 데이터를 1차원 배열로 전처리한 데이터로, 60,000개의 학습 데이터 중 4,000개의 학습 데이터와 10,000개의 테스트 데이터 중 1,000개의 데이터를 랜덤으로 추출하였습니다.

이번 실습에서는 이러한 Fashion-MNIST 데이터를 각 이미지의 레이블에 맞게 분류하는 다층 퍼셉트론 모델을 생성해보고, Test 데이터에 대한 정확도, 즉 모델의 성능을 90% 이상으로 높여보도록 하겠습니다.

image

실습

  1. Fashion-MNIST 데이터 분류를 위한 다층 퍼셉트론 모델을 생성하고, 학습 방법을 설정해 학습시킨 모델을 반환하는 MLP 함수를 구현하세요.

    1-1. 다층 퍼셉트론 분류 모델을 생성합니다. 여러 층의 레이어를 쌓아 모델을 구성해보세요.

    • 첫 번째 레이어 (Input layer) 이번 실습에서 사용하는 데이터는 이미 전처리 되어 있기 때문에 input_dim 인자를 통해 데이터의 크기를 맞춰주지 않아도 됩니다.

      tf.keras.layers.Dense(64, activation='relu')
      Copy
      
    • 마지막 레이어 (Output layer) 10개 클래스에 대한 확률을 출력합니다.

      tf.keras.layers.Dense(10, activation='softmax')
      Copy
      

1-2. 모델을 학습시킬 손실 함수(loss function)와 최적화(optimize) 방법, 평가 방법(metrics)을 다음과 같이 설정합니다.

1-3. 모델을 학습시킵니다. 우리의 목표를 위해 epochs도 자유롭게 설정해봅니다.

실행 버튼을 눌러 테스트 데이터에 대한 모델의 성능 즉, 테스트 정확도를 확인하고 90% 이상으로 높여 제출해보세요.