2020년 2월 18일 화요일

Artificial Intelligence - Gradient descent

대부분 딥러닝을 입문할 때 가장 처음 접하는 개념은 경사하강법, Gradient descent다.
오늘은 그 Gradient Descent에 대해서 포스팅을 해보려고 한다.

ML/DL의 기초가 되는 것이기 때문에 한번은 봐둘만 하다.


단일 선형 회귀로서 공부한 시간에 따라 성적이 매겨진다고 가정한다.


이는 공부시간(x), 성적(y)로 두고 1차 방정식 y=ax+b로 표현할 수 있다. 이는 하나의 직선이 그려지고 잘만 그려진다면 공부시간에 따른 성적을 예측할 수 있을 것이다.

그럼 어떻게 방정식의 a와 b를 구할 수 있을까? 결국 반복시키며(학습시키며) 찾아내는 과정이 이번 포스팅의 핵심이다.


직선 하나가 정해져있고 x와 y를 정확하게 안다면 기울기 a를 구하는 공식은 y의 증가량/x의 증가량으로 구할 수 있다. 하지만 우리는 직선이 어떻게 그려져야하는지 모르고 데이터(x, y)만 주어졌기 때문에 대략적인 선을 긋고 이를 조금씩 움직이면서 적당한 방정식을 만들어내가는 것이 목표이다.

평균제곱오차라는 개념을 활용할 것이며 이 오차를 줄이는 방향으로 움직일 것이다.

오차(error)는 (실제 y - 예측 y)이고 이를 제곱하여 평균을 내자. 제곱하는 이유는 데이터의 성격에 따라 값이 +- 왔다갔다 할 수 있어서 제곱을 함으로써 값은 모두 양수가 되고 또한 오차가 클 수록 패널티를 부여한다고 생각할 수 있다.

아무튼 그 오차제곱의 합을 평균내는 것을 평균제곱오차(Mean squared error - mse)라고 하며 그 mse는 다음과 같다.


mse = sum((실제 y - 예측 y)^2) / 데이터의 갯수 n 가 된다.
mse = 1/n * sum((y-ax-b)^2)이다.


이제 mse를 줄이는 방향으로 움직여보자.

a와 b를 변경할 것이기 때문에 각각 어느 방향으로 얼마만큼 이동시킬 것인가 하는 문제가 있다. 위의 수식은 a의 입장에서 보면 a에 대한 2차 방정식이다.(b도 마찬가지이다.)

a와 mes간 관계를 그래프로 그려보면 다음과 같다.



다시 처음으로 돌아와서 error를 최소화 하기 위해서는 그림에서처럼 그래프의 접선의 기울기가 0인 부분, 즉 볼록한 아래지점이 a값이면 좋을 것 같다.

그럼 a의 시작점을 임의로 지정했을 때 어떻게 움직이면 될까? 접선의 기울기가 음수이면 + 방향으로, 양수이면 - 방향으로 움직이면 된다.

그럼 얼마만큼 움직여야하나.
예를 돕기위해 그래프의 가로를 시간, 세로를 위치라고 보면 속도는 위치의 변화량 / 시간의 변화량이 된다. 순간 속도는 위치를 시간으로 미분한 것이다. 그럼 해당 그래프의 한 점을 미분하면(시간이 0으로 수렴하면) 해당 지점에서의 순간 속도가 된다. 우리는 그 순간 속도에 learning rate(LR)라는 미리 정의한 상수를 곱한 만큼의 거리만큼 이동해주도록 하자. LR은 사람이 정하는 것이기 때문에 적당한 값으로 learning rate를 정의해야 한다.

mse = 1/n * sum((y-ax-b)^2)를 a에 대해서 편미분하면 -2/n*sum((y-ax-b)*x)이다.
그리고 y-ax-b는 error이기 때문에 치환하면 -2/n*sum(error*x)로 쓸 수 있다.
즉 a_diff = -2/n*sum(error*x)이다.

기울기가 음수이면 + 방향으로, 양수이면 - 방향으로 움직이기 위해서 편미분한 결과(a_diff)를 LR만큼 곱해서 빼주면 마침내 a = a - LR*a_diff가 완성되고 이를 통해 a를 업데이트를 해주면 된다.
이를 계속해서 반복해가면서 a를 계속 업데이트해준다면 결국 a가 적당한 지점에 도달할 수 있을 것이다. b역시 똑같이 편미분을 해서 b_diff를 구하자.

b_diff = -2/n*sum((y-ax-b)) = -2/n*sum(error)

정리하면 a와 b는 다음처럼 업데이트를 할 수 있다.
a = a - LR*a_diff, b = b-LR*b_diff 두 식을 얻을 수 있다.

그럼 코드로 보도록 하자.

import numpy as np
import pandas as pd

data_set = [[1,80],[2,85],[4,89],[8,93]]
x = [i[0for i in data_set]
y = [i[1for i in data_set]

origin_x = np.array(x)
origin_y = np.array(y)

a=1 #init a
b=50 #init b
LR = 0.005 #learning rate

for i in range(0,2001):
  predict_y = a * origin_x + b #predicted y
  err = origin_y - predict_y #error

  a_diff = -1*(2/len(x))*sum(origin_x*err)
  b_diff = -1*(2/len(x))*sum(err)
  
  a = a-LR*a_diff
  b = b-LR*b_diff

  if( i % 100 == 0):
    print("i=%.f, a=%.04f, b=%.04f" %(i, a, b))

결과



대략 2000번 시도하니 a는 1.7, b는 80로 수렴하고 있다. 이로써 y=ax+b 수식을 구할 수 있었고 이를 통해 선형회귀(linear regression)를 간단하게 구현해보았다.
마찬가지로 y=ax1+bx2+c의 경우처럼 a, b, c를 구하는 방법도 똑같이 구하면 된다.






2020년 2월 2일 일요일

하둡 - 오류가 발생한 블록 처리하기 (Status : Corrupt)

간혹 장비 노후라던지의 문제로 인해 하둡 블록이 문제가 되는 경우가 있다.
블록에 오류가 발생할 경우 Corrupt라는 메시지가 뜨게 되는데 데이터의 원본이 존재하거나 다른 방법으로 복구가 가능하다면 문제가 되지 않는다.

하지만 어떠한 이유로 든 간에 replication 1로 생성된 파일이 Corrupt가 발생한다면 해당 파일은 더이상 사용이 불가능하기 때문에 지워줘야한다. (과거 Polybase로 HDFS에 내리는 경우 replication 1로 떨궈졌다.)

다음 명령어를 통해 하둡 파일 시스템 상태를 체크한다.
hadoop fsck /


Status : CORRUPT가 확인되고 블록 1개가 문제가 되었다.
(replication이 1이고 테스트 파일이기 때문에 더 이상 필요가 없어서 지우기로 한다.)

hadoop fsck -delete 혹은 -move를 통해 해당 파일을 정리한다.

-move를 하면 /lost+found로 파일이 이동하는데 어차피 필요가 없으니 -delete를 하도록 한다.

hadoop fsck -delete


완료되면 Status가 Healthy로 나와야하는 것 같은데 그게 아닌가.. 뭐 어찌됐든 -delete를 하고 node overview에서 에러가 사라졌고.. 다시 hadoop fsck / 를 날려보았다.


하둡 상태가 정상(HEALTHY)로 돌아왔다.

2022년 회고

 올해는 블로그 포스팅을 열심히 못했다. 개인적으로 지금까지 경험했던 내용들을 리마인드하자는 마인드로 한해를 보낸 것 같다.  대부분의 시간을 MLOps pipeline 구축하고 대부분을 최적화 하는데 시간을 많이 할애했다. 결국에는 MLops도 데이...