제가 얼마전에 관련 게시물을 올리면서 언급을 했듯이 최근에 IDL 8.7.1 버전에서 머신러닝(Machine Learning) 관련 기능이 추가되었습니다. 머신러닝이 IT 분야에서 이제는 핫이슈를 넘어서 거의 대세가 된 분위기라는 점을 감안하면 늦은 감이 있는 것은 사실입니다. 더구나 머신러닝 기반의 작업을 하는 프로그래머들은 아마도 파이썬(Python) 등과 같은 타 언어에서 작업을 하는 경우가 훨씬 많을 것입니다. 엔비디아나 구글 등에서 제공하는 관련 라이브러리들이 파이썬 언어로 제공되고 있는 것이 하나의 좋은 예입니다. 그래도 IDL 올드유저인 제 입장에서는 지금이나마 IDL에서 머신러닝 기능이 지원되기 시작했다는 것 자체에 일단 큰 의미를 두고 싶습니다.
그래서 이번에 도입된 머신러닝 관련 기능에 대한 대략적인 내용을 이 블로그를 통하여 소개해보고자 합니다. 이 내용은 IDL 8.7.1 도움말에 있는 머신러닝 관련 모듈들에 대한 내용을 바탕으로 하였습니다. 특히 도움말의 What's New에서 "IDL Machine Learning Framework"라는 섹션이 따로 마련되어 있는데 여기 소개된 내용을 그대로 따라갑니다. 물론 이 내용을 직접 보셔도 됩니다. 다만 간단하게나마 중간중간 제 나름대로 코멘트들을 좀 달면서 내용을 소개해보고자 이 게시물을 기획해보았습니다. 사실 저는 머신러닝에 대해서는 인터넷을 통하여 이런저런 자료들을 본 것이 전부이기 때문에 그리 깊은 지식을 갖고 있지는 못합니다. 그래서 이번 게시물에서 설명하는 내용은 IDL 도움말에 있는 원본의 내용을 가급적 그대로 따라갈 예정입니다. 중간중간 나오는 머신러닝과 관련된 다소 전문적인 내용에 관해서는 그리 깊이 파고들지는 못한다는 점 미리 이해를 부탁드립니다. 그럼 지금부터 시작하겠습니다.
IDL의 머신러닝 기능은 머신러닝의 기본적인 목적을 그대로 따라갑니다. 즉 데이터가 있을 때 이를 훈련(Training)시켜서 모델로 만들며, 이러한 모델은 목적에 따라 분류(Classification), 집단화(Clustering), 회귀(Regression) 중 하나의 범주에 속하게 됩니다. 즉 훈련된 모델이 결과를 어떤 형태로 산출하도록 할 것이냐에 따라 이와 같이 구분됩니다. 그래서 지금부터는 IDL에서 할 수 있는 머신러닝 관련 작업을 다음과 같이 구분하여 관련된 전형적인 작업흐름의 예를 소개하게 됩니다.
1. 데이터 준비 : 머신러닝을 위하여 데이터를 어떻게 준비할 것인가에 대하여 알아봅니다.
2. 분류(Classification) : 한정된 갯수의 불연속적인 값들로 구성된 결과를 산출하는 분류 작업이 가능하도록 모델을 훈련시키는 방법을 알아봅니다.
3. 집단화(Clustering) : 데이터를 일정 갯수의 집단들로 나누는 집단화 작업이 가능하도록 모델을 훈련시키는 방법을 알아봅니다.
4. 회귀(Regression) : 연속적인 값의 형태로 결과를 산출하는 회귀 작업이 가능하도록 모델을 훈련시키는 방법을 알아봅니다.
1. 데이터 준비
머신러닝에서 데이터를 훈련시키려면 훈련에 맞도록 데이터를 먼저 가공하는 것이 필수입니다. 이를 위해서는 당연히 현재 내가 어떤 데이터를 다루게 되는지에 관하여 명확히 확인해두는 것이 필요합니다. 데이터는 입력(Input) 데이터와 출력(Output) 데이터로 구분됩니다. 훈련시킬 데이터 역시 이와 같이 입력 데이터와 출력 데이터를 구분해서 준비해야 합니다. 데이터를 준비하는데 있어서는 다음 두가지를 반드시 유의해야 합니다.
데이터의 정규화(Data Normalization)
머신러닝 훈련이 제대로 진행되기 위해서는 데이터의 모든 값들이 정규화되도록 하는 것이 바람직합니다. 즉 값들의 범위가 0에서 1까지 또는 -1에서 +1까지 이런 식으로 조정되도록 해야 합니다. 물론 원본 데이터는 값 범위가 이렇지 않은 경우가 대부분일 것입니다. 예를 들어 기온이면 -30도부터 +50도와 같은 범위일텐데, 그래도 일부터 정규화된 범위로 따로 조정을 해줘야 합니다. 불연속적인 값들로 구성되는 데이터일 경우도 마찬가지입니다. 예를 들어 혈액형 데이터가 있는데 A, B, AB, O형 등 4개의 경우로 한정되어 있다면, A는 0.1, B는 0.3, AB는 0.6, O는 0.8과 같은 식으로 정규화된 값 하나에 대응되도록 맞춰줘야 합니다. 이러한 범위 재조정 작업은 프로그래밍을 직접 하여 해결할 수도 있지만, IDLmlLinearNormalizer, IDLmlRangeNormalizer, IDLmlTanHNormalizer, IDLmlUnitNormalizer, IDLmlVarianceNormalizer 등의 내장루틴들을 사용해서 해결할 수도 있습니다. 데이터 정규화는 입력 데이터에 대해서는 필수적으로 진행되어야 하며, 출력 데이터의 경우는 모델의 범주에 따라서는 정규화가 필요할 수도 있고 그렇지 않을 수도 있습니다.
데이터의 분리(Data Separation)
전체 데이터를 훈련용(Training)과 시험용(Testing) 두 종류로 분리해야 합니다. 훈련용 데이터는 말 그대로 모델 훈련을 위하여 직접 투입되는 데이터를 뜻하며, 시험용 데이터는 훈련이 다 끝난 모델의 정확도를 측정하기 위한 데이터입니다. 두 종류의 데이터는 서로 겹치지 않아야 합니다. 통상적으로는 훈련용과 시험용의 비율은 8:2 또는 7:3 정도로 하는 경우가 많은데, 물론 작업자가 모델 및 데이터의 특성에 맞게 판단해야 합니다. 전체 데이터에서 훈련용과 시험용을 선별하는 방법은 랜덤하게 샘플링하는 방법도 있고 다른 방법을 사용하기도 합니다. IDL에서는 IDLmlShuffle 또는 IDLmlPartition 등의 내장루틴을 사용하여 이러한 선별 작업을 할 수도 있습니다.
데이터를 준비하는 작업에 대한 구체적인 예제로서 IDL 8.7.1에서는 관련 예제 데이터를 제공하고 있습니다. IDL 명령 프롬프트에서 또는 프로그램 내에서 다음과 같은 명령을 실행하면 예제 데이터를 불러옵니다. 이는 IDL 8.7.1 설치시 함께 제공되는 CSV 형식의 파일로부터 값들을 읽어서 features 및 labels라는 배열을 생성하도록 하는 내장 프로그램을 실행한 것입니다. 이 CSV 파일은 IDL 설치 디렉토리 내에서 examples/data라는 하위 디렉토리에 있는 seeds_dataset.csv입니다.
IDL> read_seeds_example_data, features, labels
이 프로그램을 실행한 후 HELP 명령을 사용하여 각 배열에 관한 정보를 확인해보면 다음과 같습니다.
IDL> help, features
FEATURES DOUBLE = Array[7, 210]
IDL> help, labels
LABELS STRING = Array[210]
즉 features는 7x210의 형태를 갖는 2차원 배열이고 labels는 210개의 값들로 구성된 1차원 배열임을 알 수 있습니다. 데이터 값들의 갯수 자체는 210개인데, 입력 데이터는 총 7종의 인자들로 구성되어 있고 출력 데이터는 한 종류입니다. 원본 데이터 파일에 수록된 내용은 다음과 같은 형태입니다.
입력 데이터를 구성하는 7종의 인자들은 각각 area, perimeter, compactness, length, width, asymmetry coefficient, length of kernel groove입니다. 위의 테이블에서는 전반부 10개의 라인들만 보여주고 있습니다. 실제로 이 데이터는 씨앗의 종류(labels) 및 각 종류의 씨앗의 특성을 나타내는 7종의 인자들(features)에 해당됩니다. 배열 features의 전반부 10줄만 출력해보면 다음과 같습니다.
IDL> print, features
15.260000 14.840000 0.87100000 5.7630000 3.3120000 2.2210000 5.2200000
14.880000 14.570000 0.88110000 5.5540000 3.3330000 1.0180000 4.9560000
14.290000 14.090000 0.90500000 5.2910000 3.3370000 2.6990000 4.8250000
13.840000 13.940000 0.89550000 5.3240000 3.3790000 2.2590000 4.8050000
16.140000 14.990000 0.90340000 5.6580000 3.5620000 1.3550000 5.1750000
14.380000 14.210000 0.89510000 5.3860000 3.3120000 2.4620000 4.9560000
14.690000 14.490000 0.87990000 5.5630000 3.2590000 3.5860000 5.2190000
14.110000 14.100000 0.89110000 5.4200000 3.3020000 2.7000000 5.0000000
16.630000 15.460000 0.87470000 6.0530000 3.4650000 2.0400000 5.8770000
16.440000 15.250000 0.88800000 5.8840000 3.5050000 1.9690000 5.5330000
15.260000 14.850000 0.86960000 5.7140000 3.2420000 4.5430000 5.3140000
그리고 배열 labels의 값들은 Kama, Rosa, Canadian 등 세가지 문자값들 중 하나가 됩니다. 결국 이러한 데이터를 훈련시키면, 어떤 씨앗에 대하여 측정된 7종의 인자값들에 대응되는 씨앗 종류가 세가지 중 어느 것인지를 판단할 수 있는 모델을 얻게 됩니다. 이러한 모델은 결국 분류(Classification)의 범주에 속한다고 볼 수 있습니다. 어쨌든 여기까지는 원본 데이터 그대로인 상태입니다. 하지만 머신러닝 훈련을 위해서는 이대로 두면 안되고 몇가지 처리 과정이 선행되어야 합니다. 가장 먼저 입력 데이터 값들을 정규화하는 과정입니다. 여기서는 다음과같이 IDLmlVarianceNormalizer라는 내장함수를 사용하였습니다.
IDL> Normalizer = IDLmlVarianceNormalizer(features)
IDL> Normalizer.Normalize, features
이렇게 처리하면 배열 features의 값들은 정규화된 값들로 대체됩니다. 여기서 배열 features의 전반부 10줄을 출력해보면 다음과 같습니다.
IDL> print, features
0.14175904 0.21494882 6.0457330e-05 0.30349301 0.14136404 -0.98380096 -0.38266305
0.011161356 0.0082041534 0.42749378 -0.16822270 0.19696159 -1.7839036 -0.91981560
-0.19160873 -0.35934192 1.4389449 -0.76181710 0.20755160 -0.66588820 -1.1863572
-0.34626388 -0.47420007 1.0369037 -0.68733567 0.31874671 -0.95852756 -1.2270506
0.44419577 0.32980697 1.3712327 0.066506648 0.80323970 -1.5597684 -0.47422315
-0.16067770 -0.26745540 1.0199756 -0.54740087 0.14136404 -0.82351440 -0.91981560
-0.054137485 -0.053053525 0.37670962 -0.14790958 0.0010463939 -0.075953850 -0.38469772
-0.25347079 -0.35168471 0.85069509 -0.47066243 0.11488901 -0.66522311 -0.83029017
0.61259805 0.68969583 0.15664494 0.95802676 0.54643194 -1.1041822 0.95411430
0.54729921 0.52889442 0.71950268 0.57659157 0.65233205 -1.1514035 0.25418826
출력된 값들을 보면 아까와는 달라져있음을 확인할 수 있습니다. 앞서 데이터의 정규화에 관한 내용에서 정규화를 담당하는 내장루틴들이 여러개 있었는데, 여기서는 이 중에서 IDLmlVarianceNormalizer라는 것을 사용하였습니다. 이 루틴을 사용하면 평균이 0이고 표준편차가1이 되는 분포 형태로 범위를 갖는 값들로 조정됩니다. 그래서 위와 같이 + 또는 -인 값들이 보이는 것입니다. 물론 다른 루틴을 사용하면 다른 정규화 알고리즘이 적용된 결과를 얻을 수 있으므로, IDL 도움말에서 각 루틴별 자세한 내용을 참조하시기 바랍니다.
이제 다음 단계는 훈련용과 시험용으로 데이터를 분리하는 과정입니다. 이 작업은 다음과 같이 IDLmlShuffle 및 IDLmlPartition 루틴을 연달아 사용하여 해봅시다.
IDL> IDLmlShuffle, features, labels
IDL> part = IDLmlPartition({train:80, test:20}, features, labels)
여기서는 먼저 IDLmlShuffle 루틴을 사용하여 데이터 내 값들을 무작위 순서로 말 그대로 흔들어준 다음, IDLmlPartition 루틴을 사용하여 훈련용과 시험용의 비율을 각각 80%와 20%로 설정하였습니다. 이렇게 하면 features를 80:20으로 분리하고 labels도 동일한 비율로 분리해줍니다. 앞서 봤듯이 데이터의 갯수 자체는 210개입니다. 그러면 80:20의 비율로 나누면 168:42가 됩니다. 실제로 확인해보면 다음과같습니다.
IDL> help, part.train.features
<expression> DOUBLE = Array[7, 168]
IDL> help, part.test.features
<expression> DOUBLE = Array[7, 42]
이와 같이 배열 features에 대하여 80:20으로 분리된 데이터가 각각 part.train.features 및 part.test.features 두 배열로 저장되었음을 알수 있습니다. 배열 labels 역시 동일한 방식으로 분리되었는데, 다음과 같이 확인해볼 수 있습니다.
IDL> help, part.train.labels
<expression> STRING = Array[168]
IDL> help, part.test.labels
<expression> STRING = Array[42]
2. 분류형(Classification) 모델 생성
그래서 이렇게 하면 머신러닝 훈련을 위한 데이터 가공 작업이 일단 완료된 것입니다. 이제는 본격적인 훈련 및 모델 생성 작업으로 들어갑니다. 데이터의 특성상 분류(Classification) 모델이 되어야 하는데, IDL의 머신러닝에서는 분류 모델 훈련에 사용될 수 있는 세부 기능이 다음과 같이 세 종류입니다.
IDLmlFeedForwardNeuralNetwork
IDLmlSoftmax
IDLmlSupportVectorMachineClassification
즉 통상적인 인공신경망(Neural Network), 소프트맥스(Softmax), SVM(Support Vector Machine) 등 세 종류의 기법들이 지원됩니다. 여기서는 이들 중 세번째인 SVM 기법을 사용하여 분류 모델을 훈련시켜보겠습니다. 다음과 같이 이름이 매우 긴 관련 내장함수를 이용하여 모델을 먼저 정의합니다.
IDL> Classifier = IDLmlSupportVectorMachineClassification(7, ['Kama', 'Rosa', 'Canadian'])
여기서 첫번째 인자인 7은 입력 데이터의 인자 종류의 갯수 7에 해당됩니다. 그리고 두번째 인자로 주어진 문자값 배열을 보면 결과로 산출되어야 할 씨앗 종류 3개에 해당되는 문자값들로 구성되어 있습니다. 이렇게 하면 Classifier라는 이름의 모델이 객체의 형태로 생성됩니다. 물론 아직은 모델의 초기 형태만 생성된 상태이고 훈련은 아직 되지 않은 상태입니다. 다음 단계가 바로 실제로 모델을 훈련시키는 과정이 되어야 하는데, 다음과 같이 하면 됩니다.
IDL> loss = Classifier.Train(part.train.features, LABELS=part.train.labels)
이와 같이 앞서 생성된 Classifier 객체에 대하여 Train이란 메서드를 실행하는 방식입니다. 이 때 투입된 인자들이 모두 훈련용 데이터로 분리했던 배열들임을 주목할 필요가 있습니다. 어쨌든 이 과정을 거치면 훈련 작업은 끝납니다. 이 때 loss라는 변수의 값을 주목할 필요가 있는데요. 여기서는 이 값이 93.45로 얻어집니다. IDL 도움말에 의하면 이 값은 모델과 훈련 데이터 사이의 유사성이 어느 정도인가를 나타내는 단위가 따로 없는 값이라고 되어 있습니다. 그래서 이 값이 어느 정도면 훈련이 잘 된 것인지에 대해서는 저도 아직은 모르겠습니다. 얼핏보면 93.45%인 것처럼 느껴지기도 하는데, 정확하지는 않습니다. 어쨌듯 훈련이 끝났단 얘기는 바로 사용이 가능하단 얘기입니다. 다음과 같이 시험용으로 분리해놨던 42개의 데이터들 중 하나를 사용하여 결과를 산출해볼 수 있습니다.
IDL> print, Classifier.Classify(part.test.features[*, 0])
Canadian
이외에도 나머지 시험용 데이터들을 사용해보면 다음과 같이 분류 결과들을 얻을 수 있습니다.
IDL> print, Classifier.Classify(part.test.features[*, 1])
Canadian
IDL> print, Classifier.Classify(part.test.features[*, 10])
Canadian
IDL> print, Classifier.Classify(part.test.features[*, 20])
Kama
IDL> print, Classifier.Classify(part.test.features[*, 30])
Rosa
물론 시험용으로 쟁여놨던 데이터가 아니더라도 우리가 직접 값들을 입력하여 결과를 얻을 수도 있습니다.
IDL> print, Classifier.Classify([0.2, -0.4, 0.8, 0.3, -0.1, 0.5, 0.7])
Kama
그런데 이와 같이 바로 실전에 투입하기에 앞서, 과연 이 모델이 어느 정도의 정확도를 보이는가를 확인해볼 필요가 있습니다. 정확도의 측정은 시험용 데이터를 이용하여 진행합니다. 즉 우리가 20%만큼 따로 분리해놨던 시험용 데이터에 이미 존재하는 결과와 모델로 예측된 결과가 맞아 떨어지는 비율이 어느 정도인가를 측정하는 일입니다. 이러한 시험 작업을 위한 IDLmlTestClassifier라는 루틴도 따로 마련되어 있으므로 다음과 같은 방식으로 이용하면 됩니다. 모델 객체 및 시험용 데이터들이 인자로 투입되었음을 주목해야 합니다.
IDL> confMatrix = IDLmlTestClassifier(Classifier, part.test.features, part.test.labels, ACCURACY=accuracy)
IDL> print, accuracy
0.976190
여기서는 정확도의 값을 accuracy라는 변수에 담도록 했는데, 이 변수의 값을 출력해보니 정확도가 97.62%로 확인되었습니다. 이 정도면 나쁘지 않은 성적입니다. 그런데 이 정확도 값이 꼭 이렇게 나오지 않을 수도 있습니다. 왜냐하면 데이터를 훈련용과 시험용으로 나누는 과정에서 랜덤 샘플링이 적용되었기 때문에, 매번 똑같은 결과를 주지 않기 때문입니다. 여러분들이 해보신 결과는 다를 수도 있습니다. 또한 제가 동일한 작업을 또 해보면 다른 결과가 나올 수도 있습니다. 그래서 정확도가 높은 모델로 훈련되었을 때 이 결과를 그대로 저장하여 나중에 다시 재사용할 수도 있습니다. 즉 다음과 같이 Save 메서드를 사용하여 데이터 정규화 및 모델 객체를 .sav 형식의 파일로 저장하면 됩니다.
IDL> Classifier.Save, 'myclassifier.sav'
IDL> Normalizer.Save, 'mynormalizer.sav'
그러면 나중에라도 다음과 같이 IDL에서 복원해서 재사용하는 것이 가능합니다.
IDL> Classifier = IDLmlModel.Restore('myclassifier.sav')
IDL> Normalizer = IDLmlNormalizer.Restore('mynormalizer.sav')
지금까지는 분류형 모델을 만드는데 있어서 SVM 기법을 사용하는 예제를 봤는데요. 사실 SVM 기법 자체의 특성상 훈련 과정이 반복적이지 않고 단 한번으로 끝납니다. 그런데 어떤 기법들은 훈련 과정이 반복적인 과정(Iteration)인 경우도 있습니다. 소프트맥스(Softmax) 기법이 그러한 경우입니다. 다음은 이 기법을 사용하여 모델을 훈련시킨 예제입니다.
SoftmaxClassifier = IDLmlSoftmax(7, ['Kama', 'Rosa', 'Canadian'])
win = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)
p = PLOT(Fltarr(2), title='Loss', THICK=2, COLOR='blue', FONT_SIZE=11, /CURRENT)
SoftmaxLoss = List()
for i=1, 200 do begin
SoftmaxLoss.Add, SoftmaxClassifier.Train(part.train.features, $
LABELS=part.train.labels)
p.SetData, SoftmaxLoss.ToArray()
endfor
confMatrix = IDLmlTestClassifier(SoftmaxClassifier, $
part.test.features, part.test.labels, $
ACCURACY=accuracy)
print, accuracy
여기서는 IDLmlSoftmax라는 루틴을 사용하여 모델 객체를 정의하였습니다. 그런데 눈에 띄는 것은 반복문을 사용하여 훈련이 여러 차례 반복의 과정을 거쳐 행해진다는 점입니다. 여기서는 총 200회의 반복을 거치도록 했으며, 반복을 하면서 모델과 훈련용 데이터 사이의 유사성이 어떻게 변해가는가를 나타내는 그림도 그리도록 하였습니다. 이 그림은 대략 다음과 같습니다. 이 그림에 대해서는 머신러닝에서 흔히 얘기하는 Cost Function의 개념과 연관지어 생각하면 되지 않을까 합니다.
출력되는 정확도의 값은 앞서 언급했듯이 유동적입니다. 아마 90%를 넘나드는 값으로 산출될 것입니다. 여기서는 반복의 횟수를 200으로설정하였지만 다른 숫자가 되어야 할 수도 있습니다. 물론 반복횟수가 많다고 해서 반드시 좋은 것은 아닙니다. 반복을 많이 해도 정확도가 별로 나아지지 않는 경우도 있습니다. 몇번 반복하는 것이 가장 좋은가를 알아내기 위해서는 어느 정도의 시행/착오를 거치는 것이 필요할 수도 있습니다. 사실 머신러닝이란 작업에서는 꽤 많은 시행/착오를 거쳐야 최적의 결과를 얻게 되는 경우가 일반적이기도 합니다.
그 외에도 Neural Network 기법을 사용하는 IDLmlFeedForwardNeuralNetwork라는 루틴을 사용할 수도 있습니다. 사용 방법은 IDL 도움말에서 이 루틴에 관한 내용에서 예제코드로 나와 있으므로 이 내용을 참조하시면 됩니다. 오늘은 일단 여기까지 하겠습니다. 나머지 내용은 다음 회에 이어집니다.
'IDL > Machine Learning' 카테고리의 다른 글
IDL의 머신러닝 기능을 이용한 MNIST 필기숫자 인식 모델 테스트 (0) | 2020.09.09 |
---|---|
IDL의 머신러닝(Machine Learning) 기능 사용에 관하여 [3] (0) | 2018.10.31 |
IDL의 머신러닝(Machine Learning) 기능 사용에 관하여 [2] (0) | 2018.10.24 |