* 지난 회 내용에서 이어집니다.
지난 회에서는 IDL의 머신러닝 기능들 중 분류(Classification)의 기법에 대한 기본적인 내용 및 관련 예제들을 살펴보았습니다. 오늘은 두번째 순서로서 집단화(Clustering)의 기법에 관하여 살펴보고자 합니다. 분류와 집단화는 서로 개념적으로는 비슷하게 보일 수도 있지만, 사실 분명한 차이점이 있습니다. IDL의 머신러닝에서 분류는 그 성격이 기본적으로 감독 분류(Supervised Classification)에 해당된다고 볼 수 있습니다. 즉 훈련용 데이터에는 분류된 카테고리 값들도 함께 포함됩니다. 지난 회 예제를 잠시 돌아보면 씨앗의 종류를 분류하는데 있어서 그 종류는 'Kama', 'Rosa', 'Canadian' 세가지였는데, 처음에 모델 객체를 생성할 때 그리고 모델을 훈련시킬 때 그 데이터가 반영됩니다. 잠시 관련 코드 내용을 봅시다.
Classifier = IDLmlSupportVectorMachineClassification(7, ['Kama', 'Rosa', 'Canadian'])
loss = Classifier.Train(part.train.features, LABELS=part.train.labels)
여기를 보면 분류 카테고리에 해당되는 배열(['Kama', 'Rosa', 'Canadian'])이 모델 객체의 생성시 명시되어 있고, 훈련 과정에서도 LABELS키워드에 해당되는 카테고리 데이터(part.train.labels)가 반영이 되고 있습니다. 분류를 하는 기준이 훈련용 데이터 내에 이미 어느 정도 들어가 있다고 볼 수 있습니다.
그런데 IDL의 머신러닝에서 집단화(Clustering)의 개념은 기본적으로 무감독 분류(Unsupervised Classification)에 가깝습니다. 이 경우에는 카테고리의 갯수만 프로그래머가 정해둘 뿐 구체적인 분류 기준 설정 및 분류 작업 등은 훈련 과정에서 알아서 진행되도록 맡기는 방식입니다. 훈련용으로 투입될 데이터에는 카테고리 분류 결과들은 포함되지 않습니다. 집단화 기법이 반영된 머신러닝 기능을 수행하는 IDL 함수는 다음 두 종류입니다.
IDLmlAutoEncoder
IDLmlKMeans
구체적인 관련 예제를 보면서 좀 더 자세히 알아봅시다. 예제로 해볼 작업은 어떤 RGB 이미지에 대하여 색상별로 카테고리가 구분된 결과를 얻는 것인데, 다음과 같이 IDL의 설치시 예제 디렉토리에 제공되는 JPG 파일을 데이터로 사용합니다.
file = FILEPATH('rose.jpg', SUBDIRECTORY=['examples', 'data'])
data = READ_IMAGE(file)
HELP, data
그러면 RGB 이미지 데이터는 data라는 배열로 인식되는데, 이 배열은 3x227x149의 형태를 갖습니다. 그런데 IDL의 머신러닝에서 훈련용으로 사용될 데이터는 지난 회에서 봤던 것과 마찬가지로 반드시 NxM의 2차원 형태가 되어야 합니다. 여기서 N은 인자들(Attributes)의 갯수이고, M은 데이터의 총 갯수에 해당됩니다. 이에 따라 다음과 같이 입력 데이터로 사용될 features라는 배열을 정의하였습니다.
features = REFORM(data, 3, (data.dim)[1]*(data.dim)[2])
HELP, features
이렇게 정의된 배열 features는 3x33823의 2차원 구조를 갖는 것으로 확인됩니다. 여기서 3은 인자들의 갯수에 해당되는데, 각 화소별로 존재하는 3종(Red, Green, Blue) 채널별 색상값이 여기에 해당됩니다. 그리고 33823은 227x149 즉 화소들의 총 갯수에 해당됩니다. 이런 방식으로 features라는 배열을 생성하였습니다. 그 다음에는 데이터 값들을 정규화(Normalize)하기 위하여 다음과 같이 관련 내장함수를 사용하였습니다. 이렇게 값들을 정규화하는 것도 머신러닝에서 꼭 필요하다는 것은 이미 지난 회에서 언급한 바 있습니다.
Normalizer = IDLmlVarianceNormalizer(features)
Normalizer.Normalize, features
이제 다음 단계는 모델 정의 및 훈련입니다. 여기서는 AutoEncoder 기법을 이용하는 IDLmlAutoEncoder 함수를 다음과 같이 사용하였습니다.
Classifier = IDLmlAutoEncoder([3, 5, 3])
그런데 내용이 너무 횡하고 간단한 느낌이 들 수도 있습니다. 기법 자체의 특성상 이렇게 하면 됩니다. 물론 규칙이 있습니다. 인자로 주어진배열을 보면 첫번째 값과 세번째 값이 모두 3으로 되어 있습니다. 이 숫자는 훈련에 투입될 데이터의 인자갯수(N=3)에 해당됩니다. 그리고 두번째 값은 5라고 되어 있는데, 이것은 총 몇가지 카테고리로 분류할 것인가에 해당됩니다. 그래서 위의 내용은 결국, 각 화소별로 주어져 있는 RGB 3종의 값들을 인자로 하여 총 5종의 카테고리로 분류된 분류 결과를 얻겠다는 의미가 됩니다. 어쨌든 이러한 기준에 맞춰 배열 내 값들을 적어주면 됩니다. 이 배열에 의하면 이 모델은 3개의 층(Layer)들로 구성되어 있으며, 각 층별 노드(Node) 갯수는 3, 5, 3이라는 뜻이 됩니다.
이제 다음 단계는 훈련입니다. 이 과정은 여러 차례의 반복을 거치는 반복훈련(Iteration)이 되어야 합니다. 따라서 지난 회에도 비슷한 예제가 있었는데, 그 예제에서와 유사한 방식으로 훈련의 과정을 거치면서 어떤 식으로 결과가 수렴되는가를 나타내는 그림도 함께 표출하도록 하였습니다. 그 내용은 다음과 같습니다.
Optimizer = IDLmloptGradientDescent(0.01)
win1 = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)
p = PLOT(FLTARR(2), TITLE='Loss', THICK=2, COLOR='blue', FONT_SIZE=11, /CURRENT)
Loss = LIST()
FOR i=1, 300 DO BEGIN
Loss.Add, Classifier.Train(features, OPTIMIZER=Optimizer)
p.SetData, Loss.ToArray()
ENDFOR
이 내용에서 먼저 주목할 부분은 IDLmlOptGradientDescent 함수로 정의된 Optimizer라는 항목입니다. 이와 관련된 기본 개념에 관해서는 제가 여기서 자세히 언급하긴 힘들지만, 대략적으로만 본다면 인공신경망(Neural Network)의 훈련에서 비용함수(Cost Function)가 어떤 형태이며 그 값을 최소화하는 과정을 얼마나 빠른 속도로 수행하도록 할 것인가를 나타낸다고 보면 됩니다. 즉 훈련의 과정을 최적화하기 위하여 어떤 기법을 사용할 것인가를 나타내는 것입니다. 그 기법에는 원래 여러 종류가 있지만 IDL에서는 IDLmloptAdam, IDLmloptGradientDescent, IDLmloptMomentum, IDLmloptQuickProp, IDLmloptRMSProp 등의 관련 함수들이 지원되고 있습니다. 여기서는 IDLmloptGradientDescent 함수를 사용하여 Gradient Descent 기법으로 그 과정을 수행하도록 하고 있는데, 이 함수를 사용하려면 인자값이 필요합니다. 이 값은 learning rate에 해당됩니다. 즉 훈련의 속도 정도로 이해하면 되는 값입니다. 이 값이 너무 작으면 훈련의 과정이 너무 촘촘하여 최적점까지 미처 도달하기 힘들 수 있으며, 반면 너무 크면 훈련의 과정이 너무 성큼성큼이어서 최적점을 지나칠 수도 있습니다. 따라서 적정한 값을 알아내기 위해서는 여러번의 시행/착오가 필요할 수 있습니다. 그러나 여기서는 일단 0.01이라는 값을 넣어보았습니다. 그리고 반복 횟수는 300회로 정의하였습니다. 이 내용을 실행하면 대략 다음과 같은 그림을 얻게 될 것입니다.
이 그림을 보면 회차가 반복될수록 loss의 값이 감소하고 있습니다. 이 loss라는 값은 모델과 훈련 데이터 사이의 차이들에 대한 제곱 평균 에러(Mean Squared Error)에 해당됩니다. 따라서 이 값이 작아질수록 모델에 수렴해간다는 의미가 됩니다. 그런데 그림을 보면 처음 시작할 때에는 급격하게 값이 감소하지만, 일정 횟수가 지나면 전혀 감소하지 않는 것을 볼 수 있습니다. 물론 loss의 값이 어디까지 감소해야 훈련이 잘 된 것인가를 판단하기는 쉽진 않습니다. 일단은 최종 결과가 어떻게 나오는지를 보는 것도 중요합니다. 이 예제에 있어서 최종 결과는 결국 집단화 작업이 끝난 이미지가 될 것입니다. 그래서 다음과 같이 원래 이미지와 작업이 끝난 이미지를 함께 표출하여 서로 비교가 가능하도록 하였습니다.
result = Classifier.Classify(features)
win2 = WINDOW(DIMENSIONS=[900, 360], /NO_TOOLBAR)
im1 = IMAGE(data, title='Original Image', LAYOUT=[2, 1, 1], $
MARGIN=0.05, /CURRENT)
im2 = IMAGE(REFORM(result, (data.dim)[1], (data.dim)[2]), $
RGB_TABLE=34, TITLE='Clustered Image', LAYOUT=[2, 1, 2], $
MARGIN=0.05, /CURRENT)
여기서는 훈련의 결과를 산출하기 위하여, 앞서 정의한 모델 객체인 Classifier의 Classify 메서드를 사용하여 result라는 결과 배열을 얻었습니다. 다만 이 배열은 33823개의 값들로 구성된 1차원 배열의 형태로 얻어지기 때문에, 이미지 형태로 표출하기 위하여 REFORM 함수가사용되었습니다. 결과 그림은 다음과 같이 나왔습니다.
여기서 왼쪽이 원본 이미지이고 오른쪽은 집단화(Clustering) 작업이 끝난 결과 이미지입니다. 즉 원본 이미지상의 RGB 색상값들을 기반으로 각 화소를 총 5가지의 카테고리로 분류하고, 각 카테고리 값에 따라 화소를 서로 다른 색상으로 구분하여 표시하고 있습니다. 유의해야 할 것은 오른쪽 이미지의 색상은 실제 색상이 아니라 단지 구분을 위하여 인위적으로 부여된 색상이라는 점입니다. 그리고 여러분들이 직접 이 작업을 해보시면 그 결과 이미지는 위의 그림과 틀릴 가능성이 큽니다. 저도 다시 해보면 위 그림과는 틀리게 나옵니다. 머신러닝 작업이 원래 그렇습니다. 그래서 최적의 결과를 저장하기 위하여 다음과 같이 모델 및 정규화 객체 각각에 대하여 Save 메서드를 이용하여 저장하는 내용도 함께 실행하는 것이 좋습니다.
Classifier.Save, 'myclassifier.sav'
Normalizer.Save, 'mynormalizer.sav'
이렇게 저장된 결과는 다음과 같이 Restore 메서드를 사용하여 필요할 때 다시 불러올 수 있습니다.
Classifier = IDLmlModel.Restore('myclassifier.sav')
Normalizer = IDLmlNormalizer.Restore('mynormalizer.sav')
일단 이와 같은 방법으로 훈련을 시키고 결과도 얻어보았습니다. 사실 이 정도로 만족할 수도 있겠지만, 더 나은 결과에 대한 욕심도 생길 수있습니다. 더 나은 결과를 얻을 방법은 없을까요? 이를 위해서는 조금씩 다른 시도들을 해볼 수도 있습니다. 그래서 이와 관련하여 두가지 방법을 더 살펴보고 마치고자 합니다. 우선 첫번째 방법으로서 앞선 과정에서 언급되었던 learing rate라는 값을 조정해보는 것입니다. 원래는 0.01이란 값을 사용했었는데, 이번엔 다음과 같이 0.001로 바꿔봅시다.
Optimizer = IDLmloptGradientDescent(0.001)
이렇게 한 다음 똑같이 실행을 해보면 결과는 다음 그림들과 같습니다.
이 그림들을 보면 훈련 과정 그래프에서 loss의 값이 아까보다 더 작은 값으로 수렴하는 것을 볼 수 있고, 결과도 뭔가 좀 더 깔끔해진 느낌도 듭니다. 그래서 아마도 이 learing rate를 조정하는 것으로도 결과가 개선될 여지가 어느 정도 있어 보입니다.
그러면 이번에는 두번째 방법을 봅시다. 앞선 과정에서 IDLmlAutoEncoder 함수를 사용하여 모델 객체를 생성하는 부분이 있었습니다. 다시 한번 보면 다음과 같습니다.
Classifier = IDLmlAutoEncoder([3, 5, 3])
그런데 머신러닝에서 활성함수(Activation Function)라는 개념이 있습니다. 이것은 하나의 층(Layer)에서 다음 층으로 넘어갈 값들을 계산하는데 사용되는 함수라고 생각하면 되는데, 통상적으로 Logistic Function(또는 Sigmoid Function)을 많이 씁니다. 하지만 활성함수에는 꽤 많은 종류가 있습니다. IDL에서는 8.7.1 버전 현재 총 18종이 함수의 형태로 지원되고 있습니다. 각 함수 이름 및 함수의 패턴은 다음 그림과 같습니다. 참고로 이 그림은 IDL 도움말에서 가져온 것입니다.
활성함수의 개념에 관해서는 제가 여기서 자세히 다루기는 힘듭니다. 검색을 좀 해보면 관련 자료를 쉽게 찾아볼 수 있으므로 이 내용을 참조해보시면 됩니다. 사실 비용함수(Cost Function), 활성함수(Activation Function) 등은 머신러닝을 처음 배울 때 항상 등장하는 개념이기도 합니다. 어쨌든 다시 본론으로 돌아오면, 우리가 정의한 AutoEncoder 모델에서는 층이 세개이므로 활성함수는 두개(1->2, 2->3)가 존재하는데, 둘 다 디폴트인 Logistic 함수로 정의되어 있습니다. 따라서 다른 함수를 사용할 경우에는 결과가 달라질 수도 있습니다. 그래서앞의 내용에서 모델 객체 생성 부분을 다음과 같이 변경해봅시다.
Classifier = IDLmlAutoEncoder([3, 5, 3], $
ACTIVATION_FUNCTIONS=[IDLmlafLogistic(), IDLmlafArcTan()])
여기서는 활성함수를 Logistic 및 ArcTan 두 종류로 따로 정의하였습니다. 그리고 learing rate의 값은 0.001로 설정한 상태로 전체적으로 다시 작업을 해보면 그 결과는 다음 그림들과 같습니다.
물론 이 결과가 앞선 결과들에 비하여 향상된 것이냐에 대해서는 작업자의 판단이 필요할 것 같습니다. 어쨌든 이러한 방법들을 사용하여 결과를 다듬어나가는 것도 가능합니다. 물론 이외에 여러가지 방법들이 가능할 것이므로, 최적의 결과를 얻기 위한 작업자의 시행/착오 횟수도 더 많아져야 할 수도 있습니다.
오늘은 여기서 마치겠습니다. 아직 남아있는 내용이 더 있으므로 다음 회차에서 계속 이어나가도록 하겠습니다.
'IDL > Machine Learning' 카테고리의 다른 글
IDL의 머신러닝 기능을 이용한 MNIST 필기숫자 인식 모델 테스트 (0) | 2020.09.09 |
---|---|
IDL의 머신러닝(Machine Learning) 기능 사용에 관하여 [3] (0) | 2018.10.31 |
IDL의 머신러닝(Machine Learning) 기능 사용에 관하여 [1] (0) | 2018.10.15 |