IDL/Machine Learning

IDL의 머신러닝(Machine Learning) 기능 사용에 관하여 [3]

이상우_IDL 2018. 10. 31. 21:16
728x90

* 지난 회 내용에서 이어집니다.

 

지난 두차례의 내용에서는 IDL의 머신러닝 기능을 이용하여 분류(Classification), 군집화(또는 집단화, Clustering) 작업을 수행하는 예제들을 살펴보았습니다. 오늘은 회귀(Regression) 작업을 수행하는 예제를 소개하고자 합니다. 앞서 해봤던 분류나 군집화의 경우에서는 결과가 불연속적인 카테고리 형태의 여러 개의 값들이었던 반면, 회귀의 경우에는 결과는 연속적인 하나의 값이 됩니다. 머신러닝 기반으로 이러한 회귀 작업을 IDL에서 하게 될 경우 사용 가능한 모듈들은 다음 세 종류입니다.

 

IDLmlFeedForwardNeuralNetwork

IDLmlSoftmax

IDLmlSupportVectorMachineRegression

 

오늘은 이들 중 Neural Network를 사용하는 예제를 소개할텐데, 여기서는 IDLmlFeedForwardNeuralNetwork 함수를 사용할 것입니다. 이것은 우리가 흔히 인공신경망(Neural Network)이라고 부르는 형태의 모델을 만드는데 사용됩니다. 먼저 예제 데이터를 다음과 같이 생성하고 표출해 봅시다.

 

sz = 100L

x = FINDGEN(sz)/(sz-1)*4-2

y = FLTARR(sz)+1.0

xx = x#y

yy = TRANSPOSE(xx)

zz = 2.0/(EXP((xx-0.5)^2+yy^2))-2.0/(EXP((xx+0.5)^2+yy^2))

HELP, xx, yy, zz

win0 = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)

surf1 = SURFACE(zz, xx, yy, ZRANGE=[-2, 2], $

  COLOR='magenta', STYLE=2, /CURRENT)

tx1 = TEXT(0.5, 0.9, 'Original Data', FONT_SIZE=12, $

  ALIGNMENT=0.5, /NORMAL)

 

여기서 표출된 그림은 원본 데이터를 서피스(Surface)의 형태로 가시화한 것으로 그 모습은 다음과 같습니다.

 

 

 

예제코드의 내용 및 표출된 그림에서 볼 수 있듯이, 이 예제 데이터는 100x100의 구조의 2차원 배열의 형태를 띄고 있습니다. 따라서 데이터 포인트들의 갯수는 10000개입니다. 그리고 xx는 데이터 포인트들의 X 좌표값들, yy는 Y 좌표값들이 되고, zz는 각 데이터 포인트의 값으로서 이 값은 Z축 좌표값의 형태로 반영됩니다. 어쨌든 이것이 원래 데이터 포인트들이라고 할 때, Neural Network를 이용하여 모든 데이터 포인트들이 이 패턴을 학습하게 하면 어떤 결과가 나오는가를 보는 것이 이번 예제가 되겠습니다.

 

이제 모델 객체를 생성하고 데이터를 훈련시킬 준비를 해야 합니다. 지난 회 게시물들에서도 이미 봤지만 IDL의 머신러닝 모듈을 이용하려면 입력 데이터는 NxM 형태의 2차원 배열이 되어야 하고 결과 데이터는 M개의 값들로 구성된 1차원 배열이 되어야 합니다. 여기서 N은 인자들(Attributes)의 갯수이고 M은 데이터 포인트들의 총 갯수입니다. 지금 우리가 다루고 있는 예제 데이터의 경우에는 M은 데이터 포인트들의 총 갯수인 10000이 될 것이고, 인자들의 갯수인데 X, Y 좌표 두 종류이므로 N은 2가 될 것입니다. 입력 데이터 배열을 features라고 정의하고, 결과 데이터 배열을 scores라고 정의하기 위하여 다음과 같은 과정이 필요합니다.

 

features = TRANSPOSE([[REFORM(xx, sz^2)], [REFORM(yy, sz^2)]])

scores = REFORM(zz, sz^2)

HELP, features, scores

 

HELP에 의하여 출력된 내용을 보면, features는 2x10000의 형태를 갖는 2차원 배열이고 scores는 10000개의 값들로 구성된 1차원 배열임을 알 수 있습니다. 이제 다음 과정은 무엇이 되어야 할까요? 원래는 features, scores의 값들을 정규화(Normalize)하는 작업이 필요할 수도 있습니다. 다만 이번 예제의 경우를 보면 데이터의 분포 범위가 X, Y, Z 축 방향으로 0을 중심으로 양쪽으로 대칭된 모습을 띄고 있습니다. 따라서 이 예제에서는 정규화 과정은 생략하고 넘어가겠습니다. 물론 데이터의 성격에 따라서는 정규화 과정이 필요할 수도 있다는 점을 유의해야 합니다. 어쨌든 다음 과정은 데이터 포인트들의 순서를 무작위로 한번 흔들어준 다음, 훈련용과 시험용으로 나누는 작업입니다.훈련용과 시험용의 비율은 80:20으로 하였습니다.

 

IDLmlShuffle, features, scores

part = IDLmlPartition({train:80, test:20}, features, scores)

 

이제 훈련시킬 모델 객체를 생성하기 위하여 다음과 같은 과정을 거치게 됩니다. 앞서 언급했던 IDLmlFeedForwardNeuralNetwork 함수를 사용하였습니다.

 

Model = IDLmlFeedForwardNeuralNetwork([2, 7, 7, 1], $

  ACTIVATION_FUNCTIONS=[IDLmlafArcTan(), IDLmlafArcTan(), $

  IDLmlafArcTan()])

 

여기서 먼저 주목해야 할 부분은 인자로 사용된 [2, 7, 7, 1]이라는 배열인데, 이 배열은 우리가 만들고자 하는 인공신경망(Neural Network) 모델의 전체적인 구조를 반영합니다. 첫번째 값인 2는 앞서 언급했던 인자 갯수 N에 해당되며, 입력층(Input Layer)의 노드(Node) 갯수가 됩니다. 그리고 네번째이자 마지막 값인 1은 출력값의 갯수에 해당되는데, 즉 출력층(Output Layer)의 노드 갯수가 됩니다. 지금과 같은 회귀(Regression)형 머신러닝 작업에서는 최종 결과값은 연속적인 하나의 값이 되기 때문에 이와 같이 1이 된다고 보면 됩니다. 그리고 중간에 있는 7, 7은 은닉층(Hidden Layer)의 구성에 해당되는데, 여기서는 두개의 은닉층을 정의하였고 각 층마다 7개의 노드들로 구성되도록 하였습니다. 은닉층의 구성은 프로그래머의 재량껏 임의의 형태로 하면 됩니다. 다만 입력층 및 출력층에 해당되는 값들(여기서는 2, 1)은 훈련시킬 데이터의 형태와 일치하도록 해줘야 합니다. 결국 이 인공신경망 모델은 4개의 층들(Layers)로 구성되고 각 층별 노드 갯수는 순서대로 각각 2, 7, 7, 1이 되는 형태를 갖게 됩니다.

 

그리고 또 주목해야 할 부분은 ACTIVATION_FUNCTIONS 키워드 및 이 키워드에 부여된 배열입니다. 이 배열을 보면 총 3개의 값들로 구성되어 있습니다. 이것은 1번층에서 2번층, 2번층에서 3번층, 3번층에서 4번층으로 갈 때 사용되는 활성함수(Activation Function)가 각각어떤 종류가 되어야 하는가를 정의한 것입니다. 층의 갯수가 네 개이므로 활성함수는 세 개가 됩니다. 이미 지난 회 게시물에서 이미 언급했듯이 IDL에서는 18종의 활성함수들이 지원됩니다. 위 예제코드에서는 세 개의 활성함수들을 모두 IDLmlafArcTan으로 정의하고 있습니다. 만약 활성함수를 위와 같이 따로 명시하지 않을 경우에는 디폴트인 IDLmlafLogistic이 사용됩니다. 물론 어떤 종류의 활성함수를 사용할 것인가를 판단하는 것은 당연히 프로그래머의 몫입니다. 사실 어떤 종류의 활성함수를 사용하느냐에 따라 훈련의 효율이 민감하게 차이가 나기 때문에, 이러한 모델 설계 작업만으로도 그리 쉬운 작업은 아닌 것 같습니다. 설계된 모델의 전체적인 구조는 대략 다음 그림과 같습니다.원래는 각 층간 노드들끼리 이어지는 모든 화살표들을 다 표시해야 하지만 너무 많아서 생략하였습니다.

 

 

 

어쨌든 이제 훈련 과정으로 넘어갑시다. 이 과정은 다음과 같이 실행합니다.

 

Optimizer = IDLmloptAdam(0.1)

Losses = LIST()

win1 = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)

p = PLOT(FLTARR(10), TITLE='Loss', THICK=2, $

  COLOR='blue', FONT_SIZE=11, /CURRENT)

FOR i = 1, 300 DO BEGIN

  Losses.Add, Model.Train(part.train.features, $

    SCORES=part.train.scores, OPTIMIZER=Optimizer)

  p.SetData, Losses.ToArray()

ENDFOR

 

여기서는 먼저 Optimizer로서 IDLmloptAdam을 사용하였습니다. 이 Optimizer에 관해서도 지난 회 게시물에서 언급을 한 바 있습니다. 다만 지난 회에서는 IDLmloptGradientDescent라는 것을 사용하였는데, 지금의 예제에서는 다른 것을 사용하고 있습니다. 이 부분 역시 프로그래머의 판단의 몫입니다. 그리고 여기서 인자로 사용된 0.1이란 값은 역시 지난 회 게시물에서 언급했던 learning rate입니다. 이 값의 최적의 수치는 원래는 여러번의 시행/착오를 거쳐 찾아야 하지만, 여기서는 일단 0.1로 정의하였습니다. 여기서는 총 300회의 반복을 거쳐 훈련을 진행하고, 각 단계마다 산출되는 loss의 값들을 역시 지난 회 게시물에서 했던 것과 유사한 방식으로 플롯으로 표출하였습니다. 이 값들이 점점 0에 가까와져야 제대로 훈련이 되는 것이라고 볼 수 있습니다. 제가 실행해본 결과 그림은 다음과 같습니다.

 

 

 

이 그림은 작업을 할 때마다 그리고 작업자에 따라 얼마든지 달라질 수 있지만, 전반적인 경향은 아마 비슷할 것입니다. 이 그림을 보면 loss의 값이 대략 0에 가깝게 수렴해가는 것으로 보입니다. 그러면 이렇게 훈련된 결과는 구체적으로 어떻게 나올 것인가를 보기 위하여, 다음과같은 내용을 실행해 봅시다.

 

data = TRANSPOSE([[REFORM(xx, sz^2)], [REFORM(yy, sz^2)]])

HELP, data

result = Model.Evaluate(data)

result2D = REFORM(result, sz, sz)

win2 = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)

surf2 = SURFACE(result2D, xx, yy, ZRANGE=[-2, 2], $

  COLOR='magenta', STYLE=2, /CURRENT)

tx1 = TEXT(0.5, 0.9, 'Trained Model', FONT_SIZE=12, $

  ALIGNMENT=0.5, /NORMAL)

 

여기서는 원래의 입력용 데이터에 해당되는 data라는 배열을 하나 정의하고, 이를 훈련된 모델에 직접 투입하여 결과를 얻어서 가시화를 해보았습니다. 맨 처음에 우리가 표출했던 서피스(Surface) 형태의 그림은 원본 데이터였고, 지금 표출한 서피스 형태의 그림은 훈련된 모델에의하여 산출된 결과에 해당됩니다. 이 결과는 다음 그림과 같습니다.

 

 

 

두 그림을 서로 비교해보면 서로 완벽하게 동일하지는 않습니다. 사실 당연합니다. 원래 제공된 데이터를 기반으로 훈련된 모델은 원래의 데이터를 비슷하게 따라가겠지만, 훈련의 과정에서 적용된 세부적인 사항들에 따라 그 유사성이 높을 수도 있고 낮을 수도 있습니다. 앞서 원본 데이터를 훈련용 및 시험용으로 8:2의 비율로 나눴었는데, 이 때 남겨둔 시험용 데이터를 모델에 투입하여 원래의 데이터와 차이를 다음과 같이 산출해볼 수 있습니다.

 

!null = Model.Evaluate(part.test.features, $

  SCORES=part.test.scores, LOSS=loss)

PRINT, loss

 

이렇게 산출된 값을 보면 약 0.01 정도의 값이 나옵니다. 수치상으로 보면 꽤 잘 수렴된 모델이란 생각도 듭니다. 물론 원래의 데이터를 모두모델이 투입하여 얻은 결과(위 그림)를 보면 과연 유사성이 높은 것인가에 대해 한번 더 생각해볼 여지가 있기도 합니다. 사실 이번 예제에서 훈련된 모델이 최적의 결과인가에 대해서는 100% 확신하기는 어렵습니다. 만약 좀 더 작업을 해본다면, 은닉층의 구성, 활성함수의 종류, Optimizer의 종류, learning rate의 값 등등을 여러가지로 테스트해보면서 여러번의 시행/착오를 더 거쳐야 할 것입니다.

 

만약 훈련의 결과에 만족하고 모델을 저장하고자 한다면 다음과 같이 저장해두면 됩니다.

 

Model.Save, 'model.sav'

 

그리고 이 모델을 나중에 재활용하게 될 경우에는 다음과 같이 회복시키면 됩니다.

 

Model.Restore'model.sav'

 

오늘은 IDL의 머신러닝 기법을 이용하여 회귀(Regression) 작업을 통하여 인공신경망(Neural Network) 모델을 훈련시키는 예제를 살펴보았습니다. 그래서 이번 IDL 8.7.1에서 새로 도입된 머신러닝 기능에 대한 대략적인 소개는 이 정도로 마쳐야 할 것 같습니다. IDL에서 머신러닝 기법을 이용한 다른 좋은 예제가 생기면 다시 또 여기 소개해보도록 하겠습니다.

LIST