블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

텐서보드를 이용하여 학습 과정을 시각화 해보자


조대협 (http://bcho.tistory.com)


텐서플로우로 머신러닝 모델을 만들어서 학습해보면, 각 인자에 어떤 값들이 학습이 진행되면서 어떻게 변화하는지 모니터링 하기가 어렵다. 앞의 예제들에서는 보통 콘솔에 텍스트로 loss 값이나, accuracy 값을 찍어서, 학습 상황을 봤는데, 텐서보다는 학습에 사용되는 각종 지표들이 어떻게 변화하는지 손쉽게 시각화를 해준다.


예를 들어 보면 다음 그림은 학습을 할때 마다 loss 값이 어떻게 변하는지를 보여주는 그래프이다.

가로축은 학습 횟수를 세로축은 모델의 loss 값을 나타낸다.





잘 보면 두개의 그래프가 그려져 있는 것을 볼 수 있는데, 1st 그래프는 첫번째 학습, 2nd 는 두번째 학습에서  추출한 loss 값이다.

Visualize Learning

그러면 어떻게 학습 과정을 시각화할 수 있는지를 알아보자

학습 과정을 시각화 하려면 학습중에 시각화 하려는 데이타를 tf.summary 모듈을 이용해서 중간중간에 파일로 기록해놨다가, 학습이 끝난 후에 이 파일을 텐서 보드를 통해서 읽어서 시각화 한다. 이를 위해서 다음과 같이 크게 4가지 메서드가 주로 사용이 된다.

  • tf.summary.merge_all
    Summary를 사용하기 위해서 초기화 한다.

  • tf.summary.scalar(name,value)
    Summary에 추가할 텐서를 정의 한다. name에는 이름, vallue에는 텐서를 정의한다. Scalar 형 텐서로 (즉 다차원 행렬이 아닌, 단일 값을 가지는 텐서형만 사용이 가능하다.) 주로 accuracy나 loss와 같은 스칼라형 텐서에 사용한다.

  • tf.summary.histogram(name,value)
    값(value) 에 대한 분포도를 보고자 할때 사용한다. .scalar와는 다르게 다차원 텐서를 사용할 수 있다. 입력 데이타에 대한 분포도나, Weight, Bias값의 변화를 모니터링할 수 있다.

  • tf.train.SummaryWriter
    파일에 summary 데이타를 쓸때 사용한다.


예제는 https://www.tensorflow.org/tutorials/mnist/tf/ 를 참고하면 된다.


mnist.py에서 아래와 같이 loss 값을 모니터링 하기 위해서 tf.summary.scalar를 이용하여 ‘loss’라는 이름으로 loss 텐서를 모니터링하기 위해서 추가하였다.


다음 fully_connected_feed.py에서

Summary를 초기화 하고, 세션이 시작된 후에, summary_writer를 아래와 같이 초기화 하였다.


이때, 파일 경로 (FLAGS.log_dir)을 설정하고, 텐서 플로우의 세션 그래프(sess.graph)를 인자로 넘긴다.




다음 트레이닝 과정에서, 100번마다, summary 값을 문자열로 변환하여, summary_writer를 이용하여 파일에 저장하였다.


트레이닝이 끝나면 위에서 지정된 디렉토리에 아래와 같이 summary 데이타 파일이 생성 된다.



이를 시각화 하려면 콘솔에서 tensorboard --logdir=”Summary 파일 디렉토리 경로" 를 지정해주면 6060 포트로 텐서보드 웹 사이트가 준비된다.



웹 브라우져를 열어서 localhost:6060에 접속해보면 다음과 같은 그림이 나온다.


Loss 값이 트레이닝이 수행됨에 따라 작아 지는 것을 볼 수 있다. (총 2000번 트레이닝을 하였다.)

세로축은 loss 값, 가로축은 학습 스텝이 된다.


만약에 여러번 학습을 하면서 모델을 튜닝했다면, 각 학습 별로 loss 값이나 accuracy 값이 어떻게 변하는지 그래프를 중첩하여 비교하고 싶을 수 있는데, 이 경우에는


% tensorboard --logdir=이름1:로그경로2,이름2:로그경로2,....


이런식으로 “이름:로그경로"를 ,로 구분하여 여러개를 써주면 그래프를 중첩하여 볼 수 있다.

아래는 1st, 2nd 두개의 이름으로 두개의 summary 로그를 중첩하여 시각화하여 각 학습 별로 loss 값이 어떻게 변화 하는지를 보여주는 그래프 이다.



Histogram

히스토 그램은 다차원 텐서에 대한 분포를 볼 수 있는 방법인데,

https://github.com/llSourcell/Tensorboard_demo 에 히스토그램을 텐서보드로 모니터링할 수 있는 좋은 샘플이 있다. 이 코드는 세개의 히든레이어를 갖는 뉴럴네트워크인데, (사실 좀 코드는 이상하다. Bias 값도 더하지 않았고, 일반 레이어 없이 dropout 레이어만 엮었다. 모델 자체가 맞는지 틀리는지는 따지지 말고 어떻게 Histogram을 모니터링 하는지를 살펴보자)


모델 그래프는 다음과 같다.




다음, 각 레이어에서 사용된 weight 값인 w_h,w_h2,w_o를 모니터링 하기 위해서 이 텐서들을 tf.historgram_summary를 이용하여 summary에 저장 한다.



이렇게 저장된 데이타를 텐서 보드로 시각화 해보면


Distribution 탭에서는 다음과 같은 값을 볼 수 있다.



w_h_summ 값의 분포인데, 세로 축은 w의 값, 가로축은 학습 횟수 이다.

학습이 시작되는 초기에는 w값이 0을 중심으로 좌우 대칭으로 모여 있는 것을 볼 수 있다. 잘 보면, 선이 있는 것을 볼 수 있는데, 색이 진할 수 록, 값이 많이 모여 있는 것이고 흐릴 수 록 값이 적게 있는 것이다.


다른 뷰로는 Histogram View를 보면, 다음과 같은 그래프를 볼 수 있는데,



세로축이 학습 횟수, 가로축이 Weight의 값이다.

그래프가 여러개가 중첩 되어 있는 것을 볼 수 있는데, 각각의 그래프는 각 학습시에 나온 Weight의 값으로, 위의 그래프에서 보면 중앙에 값이 집중되어 있다가, 아래 그래프를 보면 값이 점차적으로 옆으로 퍼지는 것을 볼 수 있다.


사실 개인적인 의견이지만 Weight 값의 분포를 보는 것이 무슨 의미를 가지는지는 잘 모르겠다. CNN에서 필터링 된 피쳐의 분포나, 또는 원본 데이타의 분포에는 의미가 있을듯하다.


수포자를 위한 딥러닝

#4 - 로지스틱 회귀를 이용한 이항 분류 문제의 해결

조대협 (http://bcho.tistory.com)


1장에서 머신러닝의 종류는 결과값의 타입이 연속형인 Regression (회귀) 문제와, 몇가지 정해진 분류로 결과(이산형)가 나오는 Classification(분류) 문제가 있다고 하였다. 2,3장에 걸쳐서 회귀 문제에 대해서 알아보았고, 이번장에서는 로지스틱 회귀를 이용한 분류 문제에 대해서 알아보자.

이 글의 내용은 Sung.Kim 교수님의 “모두를 위한 딥러닝”(http://hunkim.github.io/ml/) 을 참고하였다. 여러 자료들을 찾아봤는데, 이 강의 처럼 쉽게 설명해놓은 강의는 없는것 같다.

분류 문제(Classification)의 정의

분류 문제란 학습된 모델을 가지고, 입력된 값을 미리 정해진 결과로 분류해주는 모델을 이야기 한다.

분류 결과가 참/거짓과 같이 두개만 있을때 이항 분류 분석, 두개 이상일때는 다항 분류 분석이라고 하는데, 이번장에서 살펴볼 로지스틱 회귀 분석은 분류된 결과가 두 가지만 있는 이항 분류 모델이다. (다항 분류 모델은 로지스틱 회귀에 이어서 소프트맥스 회귀 분석에서 설명하도록 하겠다.)


이항 분류의 대표적인 예는 다음과 같다.

  • 이메일 스팸과 정상 이메일 검출

  • 신용카드 거래에서 정상 거래와 이상 거래 검출

  • 게임에서 어뷰징 사용자와 정상 사용자 검출


등이 이항 분류의 예가 될 수 있다.


예를 들어 아래와 같은 데이타가 있다고 가정하자



붉은 동그라미로 표시된 데이타와, 녹색 세모로 표시된 데이타를 분류하고 싶을때, 아래와 같이 이항 분류 문제는 이를 분류할 수 있는 이상적인 직선 그래프를 찾는 것이다.


로지스틱스 회귀 분석 (Logistics Regression)

선형 회귀 분석 (Linear regression) 으로 분류 문제 접근하기

이항 분류 모델에 대한 예를 들어보자. 종양의 크기에 따라서 양성 종양(암)인지 음성 종양인지를 판별하는 문제가 있다고 하자. 아래 그림은 종양의 크기에 대한 양성과 음성 여부를 그래프로 나타낸 것인데, X축은 종양의 크기, Y축은 종양의 양성과 음성 여부를 나타낸다. 1이면 양성 0이면 음성이다.


이 문제를 선형 회귀 모델로 정의해서 그래프를 그려보면 다음과 같다.

y=W*x

와 같은 그래프가 그려지고 대략 아래 그림에서 보는것과 같이 y > 0.5 보다 크면 양성 암, y <0.5 보다 작으면 음성암으로 판단할 수 있다.


그런데, 만약에 새로운 트레이닝 데이타에서, 종양의 크기가 큰 데이타가 들어오면 어떻게 될까?

아래 그림을 보자, 예를 들어 새로운 트레이닝 데이타에 종양의 크기가 5인 경우 양성 암이라는 데이타가 새로 들어왔다고 하자


이 경우 앞에서 선형 회귀로 만든 그래프의 기울기가 새로 들어온 데이타를 포함하면 맞지 않기 때문에 선형 회귀로 재학습을 시키게 되면 다음과 같은 기울기(점선 그래프)로 변하게 된다.


이 경우에는 앞에서 암이 양성인 여부를 판단할때 사용한 y가 0.5라는 기준은 더이상 사용할 수 없게 되고, y가 0.2 일때를, 새 기준으로 잡아서, 암의 양성/음성 여부를 판단해야 한다.

그러면 새로운 데이타가 들어올때 마다 기준점을 다시 잡아야 하는것인가? 또한 그렇게 만든 모델로 예측을 한다면, 학습에 사용되지 않은 큰 데이타가 들어온다면 오류가 발생할 수 도 있다.

그래서, 선형 회귀 분석 모델(Linear regression) 은 이항 분류에 적절하지 않다. 그렇다면 어떤 모델이 적절할까 ?

참고



시그모이드(sigmoid) 함수

이런 형태의 이항 분류 분석에 적절한 함수로 시그모이드(sigmoid)함수라는 것이 있다. 그래프의 모양은 다음과 같다. S 자 형태의 그래프 모양으로 중심축 (x=0)을 중심으로 좌측은 0으로 수렴하고 우측은 1로 수렴한다.



이 시그모이드 함수에 앞의 데이타를 적용해보면 다음과 같은 형태가 된다.


그림과 같이 y축을 0.5를 기준으로 판단할때 y가 0.5 일때 x가 2 인데, x<2 인 부분은 y=0으로 음성, x>2인 부분은 y=1로 양성이 된다.

큰 데이타 (x=100)가 추가된다하더라도 시그모이드 함수는 그 값이 1로 수렴되기 때문에, 앞의 선형 회귀 분석의 경우 처럼 암의 양성/음성인 경우를 결정하는 y와 x값이 변화하지 않는다.

가설 (Hyphothesis)

그래서, 이 시그모이드 함수를 사용하여 가설을 정의할 수 있다.

가설은 아래와 같다

y = sigmoid(Wx + b)


결과 값 y 는 0 또는 1의 값을 갖는다. 시그모이드 함수를 수학 공식으로 표현하면, 아래와 같다. (그렇다는 것만 알아두고 외워서 쓰자)
1 / (1+ math.exp(-(W*x+b) )


디시전 바운드리(Decision boundary)


y = sigmoid (W*x + b) 을 가설 함수로 그려진 위의 그래프에서 W는 1, b는 -2로 그래프로 우측으로 두칸을 이동하였다.

W에 1,  b에 -2 를 대입해보면 y = sigmoid(1*x-2) 의 형태의 그래프인데, “x가 2를 기준으로 좌,우측이 양성암이냐 아니냐” 를 결정하기 때문에 때문에,

1*x-2 <0 이면,  y=0이 되고

1*x+2 >0 이면, y=1이 된다.

이를 일반화 해보면, 시그모이드(sigmoid) 함수 내에 들어가는

W*x+b < 0 이면,  y=0이 되고,

W*x+b > 0 이면,  y=1이 된다.

즉 로지스틱 회귀 분석은 위의 조건을 만족하는 W와 b의 값을 찾는 문제이다.

그리고  시그모이드 함수내의

z=W*x+b

그래프를 기준으로나눠서 y가 0또는 1이 되는 기준을 삼는데, 이 그래프를 기준으로 결정을 하기 때문에, 이를 디시전 바운드리 (Decision boundary) 라고 한다.


변수가 x하나가 아니라, x1,x2가 있는 문제를 살펴보자


이 문제에서 가설 함수 y = sigmoid (W1*x1 + W2*x2 + b)가 될것이고,

z=W1*x1+W2*x2+b 가 디시전 바운드리 함수가 되며, 위의 그래프상에서는 붉은선과 초록선을 나누는 직선이 되고 이것이 바로 디시전 바운드리가 된다.

코스트 함수 (비용함수/Cost function)

자 그러면 가설 함수를 정의 했으니 적정 W와 b값을 찾기 위해서 코스트 함수를 정의해보자.

다시 한번 앞에서 코스트 함수의 개념을 되집어 보면, 코스트 함수의 개념은 가설 함수에 의해서 예측된 값과 트레이닝을 위해서 입력된 값(실제값) 사이의 차이를 계산해주는 함수로, 예측된 값과 입력된값 들의 차이에 대한 평균 값을 구한다.

로지스틱 회귀에서 사용되는 코스트 함수는 다음과 같다.

cost_function =(1/n) * Sum(
                              -y_origin*log(sigmoid(Wx+b)) - (1-y_origin)*log(1-(sigmoid(Wx+b)))

                        )

  • n 의 트레이닝 데이타의 수

  • Y_origin  는 트레이닝에 사용된 x에 대한 입력값


의미를 설명하겠지만, 머리가 아프면 넘어가도 좋다. 그냥 가져다 쓰면 된다.


그러면 어떻게 저런 코스트 함수가 사용되었는지를 알아보자.

선형회귀분석(Linear regression)의 코스트 함수를 다시 한번 살펴보자

코스트 함수는 측정값과 가설에 의해서 예측된 값의 차이의 제곱 평균을 나타내는 함수였다.

선형 회귀 분석에서의 코스트 함수는 다음과 같았다.

Cost =  Sum( (y_data_n - y_origin_n) ^ 2) / n

그리고 이 함수를 그래프로 그려보면 다음과 같이 매끈한 그래프가 나왔다.


그래프의 모양이 매끈한 골짜기 모양이였기 때문에 경사 하강법(Gradient descent)을 사용할 수 있었다.

그러면 로지스틱 회귀 분석에도 기존의 코스트 함수를 이용하여 경사하강법을 적용할 수 있는지 보자

코스트 함수에서 y_data_n에 가설 함수를 대입 시켜 보면

Cost = Sum(  ( sigmoid(Wx + b) - y_origin) ^ 2) /n

Sigmoid 함수를 풀어서표현하면


Cost = Sum(  1 / (1+ math.exp(-(W*x+b) )- y_origin) ^ 2) /n

가 되는데, exp( -(W*x+b) ) (즉 e^(-Wx+b) ) 형태로 표현되기 때문에,e 가 들어간 코스트 함수의 그래프는 다음과 같은 형태를 띄게 된다.

경사 하강법은 그래프를 타고 내려가면서 가장 작은 값을 찾는 알고리즘인데, (물이 골짜기를 따라서 내려가듯이), 이 코스트 함수의 그래프는 작은 골짜기들이 모여서 큰 골짜기 형태를 만든 모양이 된다. 그래서 경사 하강법을 적용할 경우 아래 그림과 같이 코스트 함수의 최소값으로 수렴하지 않고, 중간에 작은 골짜기에서 수렴해 버린다.


그래서 로지스틱 회귀 분석에서는 경사 하강법을 사용하기 위해서 이 코스트 함수를 메끈한 형태로 만들 필요가 있고, 새로운 코스트 함수를 사용한다. “e” 때문에 이런형태의 그래프가 그려지는 건데, e를 상쇄할 수 있는 역치함수는 log 함수가 있다. 그래서 log 함수를 적용하여 메끈한 형태의 코스트 함수를 정의해보자.코스트 함수는 y=1일때와 y=0일때 나눠서 계산해야 한다. 각각의 함수를 보면 다음과 같다.


y=1
cost = (1/n) * sum (-log(가설) )
cost = (1/n) * sum (-log(sigmoid(Wx+b)) )


y=0
cost = (1/n) * sum( -log(1-가설) )
cost = (1/n) * sum( -log(1-sigmoid(Wx+b)) )


코스트 함수는 측정한 값과, 가설에 의해 예측된 값의 차이를 나타내는 함수로, 개별값이 작을 수록 적절한 모델이 된다. 그래서 측정값과 가설값이 같거나 유사할 수 록 코스트 함수의 결과값이 작게 나와야 한다.

먼저 y=1 (측정한 값이)일때를 보자,  

코스트 함수는 cost = (1/n) * sum (-log(sigmoid(Wx+b)) ) 이다.

전체 코스트 평균 말고, 개발 값에 대한 측정값과 예측값에 대한 차이는 이 함수에서 -log(sigmoid(Wx+b)) 이다. 시그모이드 함수 특성상 sigmoid(Wx+b)는 0~1까지의 범위를 가지기 때문에 -log(0~1)을 그래프로 그려보면 다음과 같다.


측정값이 1이기 때문에, 가설함수 (시그모이드 함수  sigmoid(Wx+b) )에 의한 결과가 1이면 예측이 잘된것이고, 1에서 멀어져서 0으로 갈수록 예측된 값과 측정된 값의 차이가 크다고 할 수 있는데, 위의 그래프에서 보면, 가설에 의해 계산한 결과(x축)가 1에 가까울 수록 코스트(y축)은 0으로 수렴하고, 가설에 의해 계산한 결과가 0에 수렴할 수 록, 코스트는 높아지는 것을 볼 수 있다. 즉 y=1에서는 가설이 1에 수렴해야 하기 때문에, 1에 가까워질 수록 코스트가 낮아지는 그래프를 띄게 된다.


y가 0일때도 마찬가지 원리인데 측정값이 0이기 때문에, 가설에 의한 결과값이 0이 되어야 한다. 0에서 멀어질 경우 코스트가 늘어나고 0에서 가까워질 경우 코스트가 줄어드는 형태의 비용 함수를 정의해야 한다.

마찬가지로 코스트 함수의 평균이 아닌 개별값을 보면, -log(1-sigmoid(Wx+b)) 이 되고 sigmoid(Wx+b)는 0..1의 범위이기 때문에, -log(1- (0..1) ) 이 된다. 단 1에서 0..1을 뺄셈을 했기 때문에, 그래프는 -log(1.0, 0.99,0.98,....) 형태로 y=1인 경우와 반대 모양의 그래프가 된다.



자 이제, y=1인 경우와 y=0인 경우에 대한 각각의 코스트 함수를 정의하였다. 이를 코딩으로 옮기려면 y=1인 경우와 0인 경우에 대해서 각각 다른 코스트 함수로 처리하기 위해서 if  문을 사용해야 하지만, 그러면 코딩이 복잡해지기 때문에 이를 하나의 식으로 간단히 할 수 있는 방법을 찾아보자

y=1
cost_y1 = (1/n) * sum (-log(sigmoid(Wx+b)) )


y=0

cost_y0 = (1/n) * sum( -log(1-sigmoid(Wx+b)) )


y=1일때는 y=0인 코스트 함수 cost_y0 가 0이 되고, y=0일때는 y=1의 코스트 함수 cost_y1가 0이 되면 된다.

즉 cost = y*cost_y1 + (1-y)cost_y0 형태가 되면,


y=1 이면, cost = 1*cost_y1 + (1-1)*cost_y0 = cost_y1이 되고

y=0 이면, cost = 0*cost_y1 +  (1-0)*cost_y0 = cost_y0 이 된다.


그래서 y=1인 코스트 함수와 y=0인 코스트 함수를 위의 식 cost_y1과  cost_y=0에 각각 대입해보면


cost = y*[ (1/n) * sum (-log(sigmoid(Wx+b)) )]  + (1-y)[(1/n) * sum( -log(1-sigmoid(Wx+b)) )]


으로 평균 함수인 (1/n)* sum을 앞으로 빼면

cost = (1/n)*Sum(

y*(-log(sigmoid(Wx+b)))

        + (1-y)(-log(1-sigmoid(Wx+b) ))

     )


가 된다.

수식을 따라가면서 이해하면 제일 좋겠지만, 코스트 함수가 저런 원리로 생성이 되는 구나 정도 이해하고, 그냥 가져도 쓰자. (왠지 주입식 교육 같은 느낌이 들기는 하지만)


옵티마이져 (Optimizer)

코스트 함수가 정해졌으면, 코스트를 최소화할 옵티마이져로 어떤 옵티마이져를 사용해야 할지 결정해야 한다.

앞에서 경사 하강법에 적절하도록 코스트 함수를 수정했기 때문에, 경사 하강법(Gradient descent) 알고리즘을 사용한다. 이 경사하강법은 텐서플로우 코딩에서는 간단하게

optimizer = tf.train.GradientDecentOptimizer(learningRate)

train = optimizer.minimize(cost_function)


정의하여 옵티마이져로 사용할 수 있다.

예측 (Prediction)

자 이렇게 해서, 로지스틱 회귀에 대한 학습을 끝내고, W와 b 값을 구했다고 하자. 그렇다면 이 학습된 모델을 가지고, 들어오는 데이타에 대한 분류는 어떻게 할것인가? (예측은 어떻게 할것인가)


가설 함수를 다시 생각해보면 가설함수는 sigmoid(Wx + b) 이다.

그래서 예측이 필요한 값 x가 들어왔을때, 가설함수에 의한 결과값이 y’ 이라고 하면, y’은 0..1 까지의 실수가 되고, y’은 0.75일 경우, x에 대한 결과가 1일 확률은 75%, 0일 확률은 (100-75%인) 25%가 된다.


앞에서 종양의 크기에 대한 양성암 문제를 다시 예를 들어보면, 종양의 크기 x=5이면, sigmoid(Wx+b)에 의해서 계산된 결과가 0.95라고 하면, 이 경우 양성 종양인 확률은 95%, 음성인 확률은 5% 이다.


정리

분류 문제는 회귀 분석과 함께 머신러닝에서 가장 대표적인 문제이고, 로지스틱 회귀 분석은 분류문제에 있어서 기본적이고 대표가 되는 모델이다. 수식이 많이 나와서 다소 복잡할 수 있겠지만 정리를 해보면 다음과 같다.


먼저 가설 함수 (Wx+b) 를 정의하고, 이 가설 함수에 사용된 변수 W와 b에 대한 적정값을 계산하기 위해서 코스트 함수를 정의한후, 이 코스트 함수를 이용한 W와 b를 구하기 위해서 옵티마이저를 정의해서 학습을 통해서 W와 b값을 구한다.


이 과정에서 복잡한 수학적인 설명이 있었지만, 다음과 같은 접근 방법이 좋지 않을까 한다.

  1. 로지스틱 회귀 분석은 결과가 참/거짓인 이항 분석 문제에 사용된다.

  2. 비용 함수가 있다는 것을 알고 로지스틱 회귀 분석용 비용 함수를 가져다 쓴다. 단 비용 함수가 낮을 수 록 학습이 정확하다는 의미 정도는 알아야 학습 도중에 비용 함수의 결과를 보고 학습의 정확도를 파악할 수 있다.

  3. 옵티마이져는 그레디언트 디센트 알고리즘을 사용한다. 그냥 가져다 쓴다.
    단 여기서 학습 속도(Learning Rate, 2장 선형 회귀 분석을 구현하는 문서에서도 설명하였음)의 의미를 파악하고,학습 프로그램을 돌릴때 이 패러미터를 조정하면서 사용한다.

  4. 나온 결과값 (W와 b) 값을 이용하여 예측을 수행한다.


참고