텐서플로우와 딥러닝 학습 방법

이재경·2023년 1월 12일
0

인공지능

목록 보기
13/14

Gradient descent 알고리즘 구현하기

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

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

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

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

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

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

예를 들어 f(x,y)=2x2+yf(x,y) = 2x^2+yf(x,y)=2x
2+y 라는 수식이 있을 때, x에 대해서만 편미분한다면

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

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

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

w0과 w1에 대한 gradient를 구하기 위해 Loss를 각각에 대해 편미분하면 다음과 같습니다.

가중치 업데이트

실습
설명 중 ‘손실 함수’ 파트의 수식을 참고해 MSE 손실 함수를 완성하세요.

설명 중 ‘Gradient’ 파트의 마지막 두 수식을 참고해 w0와 w1에 대한 gradient인 gradient0과 gradient1을 반환하는 함수를 완성하세요.

설명 중 ‘가중치 업데이트’ 파트의 두 수식을 참고해 gradient descent를 통한 가중치 업데이트 코드를 작성하세요.

import numpy as np

# 사용할 1차 선형 회귀 모델

def linear_model(w0, w1, X):
    
    f_x = w0 + w1 * X
    
    return f_x
    
'''
1. 설명 중 '손실 함수' 파트의 수식을 참고해
   MSE 손실 함수를 완성하세요. 
'''

def Loss(f_x, y):
    
    ls = np.mean(np.square(y-f_x))
    
    return ls

'''
2. 설명 중 'Gradient' 파트의 마지막 두 수식을 참고해 두 가중치
   w0와 w1에 대한 gradient인 'gradient0'와 'gradient1'을
   반환하는 함수 gradient_descent 함수를 완성하세요.
   
   Step01. w0에 대한 gradient인 'gradient0'를 작성합니다.
   
   Step02. w1에 대한 gradient인 'gradient1'을 작성합니다.
'''

def gradient_descent(w0, w1, X, y):
    
    gradient0 = 2* np.mean((y-(w0+w1*X))*(-1))
    gradient1 = 2* np.mean((y-(w0+w1*X))*(-1*X))
    
    return np.array([gradient0, gradient1])

'''
3. 설명 중 '가중치 업데이트' 파트의 두 수식을 참고해 
   gradient descent를 통한 가중치 업데이트 코드를 작성하세요.
   
   Step01. 앞서 완성한 gradient_descent 함수를 이용해
           w0와 w1에 대한 gradient인 'gd'를 정의하세요.
           
   Step02. 변수 'w0'와 'w1'에 두 가중치 w0와 w1을 
           업데이트하는 코드를 작성합니다. 앞서 정의한
           변수 'gd'와 이미 정의된 변수 'lr'을 사용하세요.
'''

def main():
    
    X = np.array([1,2,3,4]).reshape((-1,1))
    y = np.array([3.1, 4.9, 7.2, 8.9]).reshape((-1,1))
    
    # 파라미터 초기화
    w0 = 0
    w1 = 0
    
    # learning rate 설정
    lr = 0.001
    
    # 반복 횟수 1000으로 설정
    for i in range(1000):
    
        gd = gradient_descent(w0, w1, X, y)
        
        w0 = w0- lr*gd[0]
        w1 = w1- lr*gd[1]
        
        # 100회마다의 해당 loss와 w0, w1 출력
        if (i % 100 == 0):
        
            loss = Loss(linear_model(w0,w1,X),y)
        
            print("{}번째 loss : {}".format(i, loss))
            print("{}번째 w0, w1 : {}, {}".format(i, w0, w1),'\n')

    return w0, w1

if __name__ == '__main__':
    main()
    

역전파(Back propagation)

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

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




출처:엘리스

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

X의 한 원소가 3개이므로 가중치도 3개가 있어야 합니다. 초기 가중치 w를 [1,1,1]로 정의하는 코드를 작성하세요.

초기 가중치 w를 모델에 맞게 계속 업데이트 해야합니다. 업데이트를 위해 초기 가중치 w에 더해지는 값들의 리스트 wPrime을 [0,0,0]로 정의하는 코드를 작성하세요.

sigmoid 함수를 통과할 r값과 sigmoid 함수를 통과한 r값인 v를 정의하세요.

가중치 w가 더이상 업데이트가 안될 때까지 업데이트해주는 코드를 작성하세요. 자세한 내용은 코드 주석을 참고하세요.

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

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

import math

def sigmoid(x) :
    return 1 / (1 + math.exp(-x))

'''
X, y 를 가장 잘 설명하는 parameter (w1, w2, w3)를 반환하는
함수를 작성하세요. 여기서 X는 (x1, x2, x3) 의 list이며, y 는
0 혹은 1로 이루어진 list입니다. 예를 들어, X, y는 다음의 값을
가질 수 있습니다.

    X = [(1, 0, 0), (1, 0, 1), (0, 0, 1)]
    y = [0, 1, 1]
'''

'''
1. 지시 사항을 따라서 getParameters 함수를 완성하세요.

Step01. X의 한 원소가 3개이므로 가중치도 3개가 있어야 합니다.
        초기 가중치 w를 [1,1,1]로 정의하는 코드를 작성하세요.
        
        단순히 f = 3, w = [1,1,1]이라고 하는 것보다 좀 더 
        좋은 표현을 생각해보세요.
        
        
Step02. 초기 가중치 w를 모델에 맞게 계속 업데이트 해야합니다.
            
        업데이트를 위해 초기 가중치 w에 더해지는 값들의 리스트
        wPrime을 [0,0,0]로 정의하는 코드를 작성하세요.  
            
        마찬가지로 단순히 wPrime = [0,0,0]이라고 하는 것보다
        좀 더 좋은 표현을 생각해보세요.
        
        
Step03. sigmoid 함수를 통과할 r값을 정의해야합니다. r은 
        X의 각 값과 그에 해당하는 가중치 w의 곱의 합입니다.
            
        즉, r = X_0_0 * w_0 + X_1_0 * w_0 + ... + X_2_2 * w_2
        가 됩니다.
            
        그리고 sigmoid 함수를 통과한 r값을 v로 정의합시다.
    
    
Step04. 가중치 w가 더이상 업데이트가 안될 때까지 업데이트 해줘야합니다.
        즉, 가중치 w의 업데이트를 위해 더해지는 wPrime의 절댓값이 어느 정도까지
        작아지면 업데이트를 끝내야 합니다. 
            
        그 값을 0.001로 정하고, wPrime이 그 값을 넘지 못하면 가중치 
        업데이트를 끝내도록 합시다. 
        
        다만 wPrime의 절댓값이 0.001보다 작아지기 전까지는 w에 wPrime을 계속
        더하면서 w를 업데이트 합시다.    
'''

def getParameters(X, y) :
    
    # Step01.
    
    f = len(X[0])
    
    w = [1]*f
    
    values = []
    
    while True :
        
        # Step02.
        
        wPrime = [0]*f
        vv=[]
        
        # Step03.
        
        for i in range(len(y)) :
            r = 0
            for j in range(f) :
                r = r+X[i][j]*w[j]
                
            
            v = sigmoid(r)
            vv.append(v)
            # w를 업데이트하기 위한 wPrime을 역전파를 이용해 구하는 식
            for j in range(f) :
                wPrime[j] += -((v - y[i]) * v * (1-v) * X[i][j])
        
        # Step04.
        
        flag = False
        
        for i in range(f) :
            if abs(wPrime[i])>=0.001 :
                flag = True
                break
        
        if flag == False :
            break
        
        for j in range(f) :
            w[j] = w[j]+wPrime[j]
    
    return w

def main():
    
    '''
    이 코드는 수정하지 마세요.
    '''
    
    X = [(1, 0, 0), (1, 0, 1), (0, 0, 1)]
    y = [0, 1, 1]
    
    '''
    # 아래의 예제 또한 테스트 해보세요.
    X = [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
    y = [0, 0, 1, 1, 1, 1, 1, 1]

    # 아래의 예제를 perceptron이 100% training할 수 있는지도 확인해봅니다.
    X = [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
    y = [0, 0, 0, 1, 0, 1, 1, 1]
    '''
    
    print(getParameters(X, y))

if __name__ == "__main__":
    main()
    
    

텐서플로우(TensorFlow) 버전 비교하기

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

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

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

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

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

출처:엘리스

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

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

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

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

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

다음은 ‘텐서플로우 1.x‘의 덧셈 연산 코드입니다. 똑같이 작성해보고 결과를 확인해보세요.

계산 정의

add_op = a + b

세션 시작

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

import numpy as np
import logging, os

logging.disable(logging.WARNING)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"


"""""
텐서플로우 1.x 버전
"""""

def tf1():
    
    import tensorflow.compat.v1 as tf
    tf.disable_v2_behavior()
    
    # 상수
    a = tf.constant(5)
    b = tf.constant(3)

    # 계산 정의

    add_op = a + b
 
    # 세션 시작
    sess = tf.Session()
    result_tf1 = sess.run(add_op)
    
   
    
 
    
    return a, b, result_tf1

"""""
텐서플로우 2.0 버전
"""""

def tf2():
    
    import tensorflow as tf
    tf.compat.v1.enable_v2_behavior()
    
    # 상수
    a = tf.constant(5)
    b = tf.constant(3)
    
    # 즉시 실행 연산
    result_tf2 = tf.add(a, b)
    
    return a, b, result_tf2.numpy()

def main():
    
    tf_2, tf_1 = tf2()[2], tf1()[2]
    
    print('result_tf1:', tf_1)
    print('result_tf2:', tf_2)
    
if __name__ == "__main__":
    main()
    

텐서(Tensor) 데이터 생성

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

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

텐서플로우 자료형
tf.float32 : 32-bit float
tf.float64 : 64-bit float
tf.int8 : 8-bit integer
tf.int16 : 16-bit integer
tf.int32 : 32-bit integer
tf.uint8 : 8-bit unsigned integer
tf.string : String
tf.bool : Boolean
이번 실습에서는 텐서플로우의 다양한 함수와 자료형을 사용하여 직접 상수, 시퀀스, 난수, 변수 등을 생성해보도록 하겠습니다.

실습
상수 텐서를 생성하는 constant_tensors 함수를 완성하세요.

시퀀스 텐서를 생성하는 sequence_tensors 함수를 완성하세요.

변수를 생성하는 variable_tensor 함수를 완성하세요.

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

텐서에 .numpy 메소드를 적용하면 numpy 배열로 변환됩니다.

import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

'''
1. 상수 텐서를 생성하는 constant_tensors 함수를 완성하세요.

   Step01. 5의 값을 가지는 (1,1) shape의 8-bit integer 텐서를 만드세요.
   
   Step02. 모든 원소의 값이 0인 (3,5) shape의 16-bit integer 텐서를 만드세요.
   
   Step03. 모든 원소의 값이 1인 (4,3) shape의 8-bit integer 텐서를 만드세요.
'''

def constant_tensors():
    
    t1 = tf.constant(5,shape=(1,1),dtype=tf.int8)
    
    t2 = tf.zeros(shape=(3,5),dtype=tf.int16)
    
    t3 =tf.ones(shape=(4,3),dtype=tf.int8)
    
    return t1, t2, t3

'''
2. 시퀀스 텐서를 생성하는 sequence_tensors 함수를 완성하세요. 

   Step01. 1.5에서 10.5까지 증가하는 3개의 텐서를 만드세요.
   
   Step02. 2.5에서 20.5까지 증가하는 5개의 텐서를 만드세요. 
'''

def sequence_tensors():
    
    seq_t1 = tf.range(1.5,11,4.5)
    
    seq_t2 = tf.range(2.5,21,4.5)
    
    return seq_t1, seq_t2

'''
3. 변수를 생성하는 variable_tensor 함수를 완성하세요.

   Step01. 값이 100인 변수 텐서를 만드세요.
   
   Step02. 모든 원소의 값이 1인 (2,2) shape의 변수 텐서를 만드세요.
           이름도 'W'로 지정합니다.
   
   Step03. 모든 원소의 값이 0인 (2,) shape의 변수 텐서를 만드세요.
           이름도 'b'로 지정합니다.
'''

def variable_tensor():
    
    var_tensor = tf.Variable(initial_value=100)
    
    W = tf.Variable(tf.ones(shape=(2,2),name='W'))
    
    b = tf.Variable(tf.zeros(shape=(2,),name='b'))
    
    return var_tensor, W, b

def main():
    
    t1, t2, t3 = constant_tensors()
    
    seq_t1,seq_t2 = sequence_tensors()
    
    var_tensor, W, b = variable_tensor()
    
    constant_dict = {'t1':t1, 't2':t2, 't3':t3}
    
    sequence_dict = {'seq_t1':seq_t1, 'seq_t2':seq_t2}
    
    variable_dict = {'var_tensor':var_tensor, 'W':W, 'b':b}
    
    for key, value in constant_dict.items():
        print(key, ' :', value.numpy())
    
    print()
    
    for key, value in sequence_dict.items():
        print(key, ' :', value.numpy())
        
    print()
    
    for key, value in variable_dict.items():
        print(key, ' :', value.numpy())

if __name__ == "__main__":
    main()
    

텐서(Tensor) 연산

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

이항 연산자

tf.add(x, y) : x 텐서와 y 텐서를 더합니다.
tf.subtract(x, y) : x 텐서에서 y 텐서를 뺍니다.
tf.multiply(x, y) : x 텐서와 y 텐서를 곱합니다.
tf.truediv(x, y) : x 텐서를 y 텐서로 나눕니다.

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

import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

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

   Step01. 텐서 'a'와 'b'를 더해 'add'에 저장하세요.
   
   Step02. 텐서 'a'에서 'b'를 빼 'sub'에 저장하세요.
   
   Step03. 텐서 'a'와 'b'를 곱해 'mul'에 저장하세요.
   
   Step04. 텐서 'a'에서 'b'를 나눠 'div'에 저장하세요.
'''

def main():
    
    a = tf.constant(10, dtype = tf.int32)
    b = tf.constant(3, dtype = tf.int32)
    
    add = tf.add(a, b)
    sub = tf.subtract(a, b)
    mul = tf.multiply(a, b)
    div = tf.truediv(a, b)
    
    tensor_dict = {'add':add, 'sub':sub, 'mul':mul, 'div':div}
    
    for key, value in tensor_dict.items():
        print(key, ' :', value.numpy(), '\n')
    
    return add, sub, mul, div

if __name__ == "__main__":
    main()
    

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

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

y=W∗X+by = W * X + by=W∗X+b

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

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

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

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

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

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

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

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

실습
지금까지 배운 텐서플로우에서의 변수 텐서 설정법과 이항 연산자 함수를 사용하여 선형 회귀 모델의 클래스를 구현하세요.

MSE 값을 계산해 반환하는 손실 함수 loss를 완성하세요.

텐서플로우에서 제곱은 tf.square(), 평균은 tf.reduce_mean()을 사용해서 구현할 수 있습니다.
train() 함수를 보면서 어떤 방식으로 가중치와 Bias가 업데이트되는지 확인하세요.
실행 버튼을 눌러 모델 학습에 따라 변화되는 loss 값과 가중치, Bias를 확인하고 제출하세요.

import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

from elice_utils import EliceUtils
elice_utils = EliceUtils()

np.random.seed(100)

'''
1. 선형 회귀 모델의 클래스를 구현합니다.

   Step01. 가중치 초기값을 1.5의 값을 가진 변수 텐서로 설정하세요.
   
   Step02. Bias 초기값을 1.5의 값을 가진 변수 텐서로 설정하세요.
   
   Step03. W, X, b를 사용해 선형 모델을 구현하세요.
'''

class LinearModel:
    
    def __init__(self):
        
        self.W = tf.Variable(1.5)
        
        self.b =tf.Variable(1.5)
        
    def __call__(self, X, Y):
        
        return tf.add(tf.multiply(X,self.W),self.b)

'''
2. MSE 값을 계산해 반환하는 손실 함수를 완성합니다. 
'''

def loss(y, pred):
    
    return tf.reduce_mean(tf.square(y-pred))

'''
3. gradient descent 방식으로 학습하는 train 함수입니다.
   코드를 보면서 어떤 방식으로 W(가중치)와 b(Bias)이
   업데이트 되는지 확인해 보세요.
'''

def train(linear_model, x, y):
    
    with tf.GradientTape() as t:
        current_loss = loss(y, linear_model(x, y))
    
    # learning_rate 값 선언
    learning_rate = 0.001
    
    # gradient 값 계산
    delta_W, delta_b = t.gradient(current_loss, [linear_model.W, linear_model.b])
    
    # learning rate와 계산한 gradient 값을 이용하여 업데이트할 파라미터 변화 값 계산 
    W_update = (learning_rate * delta_W)
    b_update = (learning_rate * delta_b)
    
    return W_update,b_update
 
def main():
    
    # 데이터 생성
    x_data = np.linspace(0, 10, 50)
    y_data = 4 * x_data + np.random.randn(*x_data.shape)*4 + 3
    
    # 데이터 출력
    plt.scatter(x_data,y_data)
    plt.savefig('data.png')
    elice_utils.send_image('data.png')
    
    # 선형 함수 적용
    linear_model = LinearModel()
    
    # epochs 값 선언
    epochs = 100
    
    # epoch 값만큼 모델 학습
    for epoch_count in range(epochs):
        
        # 선형 모델의 예측 값 저장
        y_pred_data=linear_model(x_data, y_data)
        
        # 예측 값과 실제 데이터 값과의 loss 함수 값 저장
        real_loss = loss(y_data, linear_model(x_data, y_data))
        
        # 현재의 선형 모델을 사용하여  loss 값을 줄이는 새로운 파라미터로 갱신할 파라미터 변화 값을 계산
        update_W, update_b = train(linear_model, x_data, y_data)
        
        # 선형 모델의 가중치와 Bias를 업데이트합니다. 
        linear_model.W.assign_sub(update_W)
        linear_model.b.assign_sub(update_b)
        
        # 20번 마다 출력 (조건문 변경 가능)
        if (epoch_count%20==0):
            print(f"Epoch count {epoch_count}: Loss value: {real_loss.numpy()}")
            print('W: {}, b: {}'.format(linear_model.W.numpy(), linear_model.b.numpy()))
            
            fig = plt.figure()
            ax1 = fig.add_subplot(111)
            ax1.scatter(x_data,y_data)
            ax1.plot(x_data,y_pred_data, color='red')
            plt.savefig('prediction.png')
            elice_utils.send_image('prediction.png')

if __name__ == "__main__":
    main()

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

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

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

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

tf.keras.models.Sequential()
: 연속적으로 층을 쌓아 만드는 Sequential 모델을 위한 함수

model.complie() : 학습 방법 설정

model.fit() : 모델 학습

model.predict() : 학습된 모델로 예측값 생성

tf.keras.layers.Dense(units, activation)
: 신경망 모델의 레이어를 구성하는데 필요한 keras 함수

units: 레이어 안의 노드 수
activation: 적용할 activation function
이 외에도 다양한 인자가 존재하는데 추가적인 인자에 대한 정보는 아래 링크를 통해 확인할 수 있습니다.

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

실습
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
입력층 (Input layer):
입력되는 데이터의 차원에 따라 input_dim에 적절한 값을 설정해주어야 합니다. 여기서 dim이란 dimension, 즉 차원을 의미합니다.

출력층 (Output layer):
출력해야 하는 결괏값의 개수만큼 노드의 개수를 설정해주어야 합니다.

모델을 학습시킬 손실 함수(loss function) 계산 방법과 최적화(optimize) 방법을 다음과 같이 설정합니다.
손실 함수(loss) : ‘mean_squared_error’

최적화 방법(optimizer) : ‘adam’

model.compile(loss='mean_squared_error', optimizer='adam')
Copy
생성한 모델을 500 epochs 만큼 학습시킵니다. verbose 인자에는 0,1,2 값을 설정할 수 있으며, 이는 모델 학습 과정 정보를 얼마나 자세하게 출력할지를 설정합니다.

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

predictions = model.predict(x_data)
Copy
실행 버튼을 눌러 결괏값을 확인하고 제출하세요.

import tensorflow as tf
import numpy as np
from visual import *

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

np.random.seed(100)
tf.random.set_seed(100)

def main():
    
    # 비선형 데이터 생성
    
    x_data = np.linspace(0, 10, 100)
    y_data = 1.5 * x_data**2 -12 * x_data + np.random.randn(*x_data.shape)*2 + 0.5
    
    '''
    1. 다층 퍼셉트론 모델을 만듭니다.
    '''
    
    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)
    ])
    
    '''
    2. 모델 학습 방법을 설정합니다.
    '''
    
    model.compile(loss='mean_squared_error', optimizer='adam')
    
    '''
    3. 모델을 학습시킵니다.
    ''' 
    
    history = model.fit(x_data, y_data, epochs=500, verbose=2)
    
    '''
    4. 학습된 모델을 사용하여 예측값 생성 및 저장
    '''
    
    predictions = model.predict(x_data)
    
    Visualize(x_data, y_data, predictions)
    
    return history, model

if __name__ == '__main__':
    main()
    

텐서플로우로 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

실습
다층 퍼셉트론 모델을 생성합니다. 이번엔 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
모델을 학습시킬 손실 함수(loss function) 계산 방법과 최적화(optimize) 방법, 성능 측정 방법(metrics)을 다음과 같이 설정합니다.

손실함수(loss) : ‘mse’ (‘mean_squared_error’와 같습니다)

최적화 방법(optimizer) : ‘adam’

평가 방법(metrics): [‘binary_accuracy’] (이진 분류 모델의 정확도를 위한 평가 방법입니다)

model.compile(loss='mse', optimizer='adam', metrics=['binary_accuracy'])
Copy
생성한 모델을 학습시킵니다. 우리의 목표를 위해 epochs도 자유롭게 설정해봅니다.

import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

def main():
    
    # XOR 문제를 위한 데이터 생성
    
    training_data = np.array([[0,0],[0,1],[1,0],[1,1]], "float32")
    target_data = np.array([[0],[1],[1],[0]], "float32")
    
    '''
    1. 다층 퍼셉트론 모델을 생성합니다.
    '''
    
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(32, input_dim=2, activation='relu'))
    model.add(tf.keras.layers.Dense(32,  activation='relu'))
    model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
    
    '''
    2. 모델의 손실 함수, 최적화 방법, 평가 방법을 설정합니다.
    '''
    
    model.compile(loss='mse',  optimizer='adam', metrics=['binary_accuracy'])
    
    '''
    3. 모델을 학습시킵니다. epochs를 자유롭게 설정해보세요.
    ''' 
    
    hist = model.fit(training_data,target_data,epochs=30)
    
    score = hist.history['binary_accuracy'][-1]
    
    print('최종 정확도: ', score*100, '%')
    
    return hist

if __name__ == "__main__":
    main()
profile
코딩으로 빛나게

0개의 댓글