2019년 1월 30일 수요일
IBM POWER8/9 (ppc64le) 환경에서의 Darknet 설치 및 test
Darknet은 C로 구현된 open source neural network famework입니다. IBM POWER8 또는 POWER9, 즉 ppc64le 환경에서도 당연히 잘 설치됩니다. 여기서는 CentOS 7 on POWER8에서 구현했습니다.
설치는 source를 받아서 다음과 같이 compile 하기만 하면 됩니다. Source를 수정할 필요 전혀 없습니다.
[bsyu@centos01 files]$ git clone https://github.com/pjreddie/darknet.git
[bsyu@centos01 files]$ cd darknet
[bsyu@centos01 darknet]$ make -j8
[bsyu@centos01 darknet]$ ./darknet
usage: ./darknet <function>
다만 혹시 GPU를 사용하고자 할 때는 아래와 같이 CUDA로 build되도록 Makefile을 살짝 수정해주시면 됩니다. 이 또한 ppc64le 환경에서도 build는 잘 됩니다만, 제가 가난하여 GPU가 없기 때문에 테스트는 이것으로 못했습니다.
[bsyu@centos01 darknet]$ vi Makefile
GPU=1 # default 0
CUDNN=1 # default 0
...
[bsyu@centos01 darknet]$ make -j8
[bsyu@centos01 darknet]$ ./darknet
usage: ./darknet <function>
CUDA 없이, 처음에 build한 CPU-only 버전으로도 darknet을 통해 YOLO를 테스트해볼 수 있습니다. 이 darknet 속에는 이미 YOLO를 위한 cfg 파일과 샘플용 멍멍이 사진이 들어있거든요.
[bsyu@centos01 darknet]$ ls -l cfg/yolov3.cfg data/dog.jpg
-rw-r--r-- 1 bsyu bsyu 8342 Jan 30 15:19 cfg/yolov3.cfg
-rw-r--r-- 1 bsyu bsyu 163759 Jan 30 15:19 data/dog.jpg
이 dog.jpg를 download 받아보면 아래와 같습니다.
이제 테스트를 위해, 미리 멍멍이를 감지하도록 YOLO v3로 pre-train된 weight file을 download 합니다.
[bsyu@centos01 darknet]$ wget https://pjreddie.com/media/files/yolov3.weights
이것으로 data/dog.jpg 사진 속의 object를 'detect' 하는 명령을 내려보겠습니다.
[bsyu@centos01 darknet]$ ./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
layer filters size input output
0 conv 32 3 x 3 / 1 608 x 608 x 3 -> 608 x 608 x 32 0.639 BFLOPs
1 conv 64 3 x 3 / 2 608 x 608 x 32 -> 304 x 304 x 64 3.407 BFLOPs
2 conv 32 1 x 1 / 1 304 x 304 x 64 -> 304 x 304 x 32 0.379 BFLOPs
3 conv 64 3 x 3 / 1 304 x 304 x 32 -> 304 x 304 x 64 3.407 BFLOPs
4 res 1 304 x 304 x 64 -> 304 x 304 x 64
5 conv 128 3 x 3 / 2 304 x 304 x 64 -> 152 x 152 x 128 3.407 BFLOPs
6 conv 64 1 x 1 / 1 152 x 152 x 128 -> 152 x 152 x 64 0.379 BFLOPs
...
104 conv 256 3 x 3 / 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BFLOPs
105 conv 255 1 x 1 / 1 76 x 76 x 256 -> 76 x 76 x 255 0.754 BFLOPs
106 yolo
Loading weights from yolov3.weights...Done!
data/dog.jpg: Predicted in 27.192135 seconds.
dog: 100%
truck: 92%
bicycle: 99%
우리가 OpenCV로 darknet을 build하지 않았기 때문에 직접 사진이 나오지는 않지만 darknet은 predictions.jpg로 그 결과를 저장합니다.
[bsyu@centos01 darknet]$ ls -ltr | tail -n 3
-rwxr-xr-x 1 bsyu bsyu 719872 Jan 30 15:29 libdarknet.so
-rwxr-xr-x 1 bsyu bsyu 841976 Jan 30 15:29 darknet
-rw-r--r-- 1 bsyu bsyu 119208 Jan 30 15:41 predictions.jpg
이 predictions.jpg을 PC로 download 받아보면 그 결과는 아래와 같습니다.
2019년 1월 25일 금요일
이공계 연구를 위한 H2O Driverless의 활용 - 분자 에너지 값의 예측
이번에는 화학이나 제조 공정 연구 등에 H2O DriverlessAI를 활용하는 가능성에 대해서 보도록 하겠습니다. 신물질 개발이나 기계적 특성 연구 등에는 다양한 성분 또는 온도, airflow 등의 다양한 조건들의 결합과 그에 따른 결과값 예측이 필요합니다. 그러나 비용과 시간의 문제 때문에 그 엄청난 수의 조합에 대해 모두 다 일일이 테스트를 해볼 수는 없지요. 공장이나 연구실의 각종 계측기를 통해 수집한 data가 어느 정도 축적되어 있다면, machine learning을 통해 기존 data를 분석하여 가장 좋은 결과값을 낼 성분 및 조건 등에 대한 조합을 미리 예측할 수 있습니다. 그를 통해 실제 테스트 회수를 크게 줄일 수 있으므로 비용 절감은 물론이고 더 빠른 개발도 가능합니다. 이때 그 machine learning이 빨리 이루어질 수록, 그리고 그 accuracy가 정확할 수록 그 효과는 커질 것입니다.
Kaggle에 올라온 public dataset 중에는 분자 및 그 내부의 원자 구조, 그리고 그에 따른 분자의 에너지값을 담은 json file들이 있습니다.
https://www.kaggle.com/burakhmmtgl/predict-molecular-properties/home
이 dataset은 다음과 같은 zip 형태로 download 받을 수 있는데, 이걸 unzip 해보면 10개의 json file들이 들어 있습니다.
[u0017649@sys-96775 files]$ unzip ./predict-molecular-properties.zip
Archive: ./predict-molecular-properties.zip
inflating: pubChem_p_00000001_00025000.json
inflating: pubChem_p_00025001_00050000.json
inflating: pubChem_p_00050001_00075000.json
inflating: pubChem_p_00075001_00100000.json
inflating: pubChem_p_00100001_00125000.json
inflating: pubChem_p_00125001_00150000.json
inflating: pubChem_p_00150001_00175000.json
inflating: pubChem_p_00175001_00200000.json
inflating: pubChem_p_00200001_00225000.json
inflating: pubChem_p_00225001_00250000.json
각 json file들의 크기와 row 수는 아래와 같습니다.
[u0017649@sys-96775 files]$ ls -l *.json
-rw-rw-r--. 1 u0017649 u0017649 111430473 Aug 14 2017 pubChem_p_00000001_00025000.json
-rw-rw-r--. 1 u0017649 u0017649 115752953 Aug 14 2017 pubChem_p_00025001_00050000.json
-rw-rw-r--. 1 u0017649 u0017649 119400902 Aug 14 2017 pubChem_p_00050001_00075000.json
-rw-rw-r--. 1 u0017649 u0017649 116769374 Aug 14 2017 pubChem_p_00075001_00100000.json
-rw-rw-r--. 1 u0017649 u0017649 116383795 Aug 14 2017 pubChem_p_00100001_00125000.json
-rw-rw-r--. 1 u0017649 u0017649 122537630 Aug 14 2017 pubChem_p_00125001_00150000.json
-rw-rw-r--. 1 u0017649 u0017649 96286512 Aug 14 2017 pubChem_p_00150001_00175000.json
-rw-rw-r--. 1 u0017649 u0017649 126743707 Aug 14 2017 pubChem_p_00175001_00200000.json
-rw-rw-r--. 1 u0017649 u0017649 129597062 Aug 14 2017 pubChem_p_00200001_00225000.json
-rw-rw-r--. 1 u0017649 u0017649 147174708 Aug 14 2017 pubChem_p_00225001_00250000.json
[u0017649@sys-96775 files]$ wc -l *.json
4924343 pubChem_p_00000001_00025000.json
5096119 pubChem_p_00025001_00050000.json
5258731 pubChem_p_00050001_00075000.json
5159241 pubChem_p_00075001_00100000.json
5120705 pubChem_p_00100001_00125000.json
5401395 pubChem_p_00125001_00150000.json
4234059 pubChem_p_00150001_00175000.json
5570529 pubChem_p_00175001_00200000.json
5698247 pubChem_p_00200001_00225000.json
6485207 pubChem_p_00225001_00250000.json
52948576 total
각각의 json 파일에는 En(에너지 값), atoms(원자 종류와 구조), id(일련번호), shapeM(shape multipole)의 4개 값이 들어있습니다.
{
'En': 37.801,
'atoms': [
{'type': 'O', 'xyz': [0.3387, 0.9262, 0.46]},
{'type': 'O', 'xyz': [3.4786, -1.7069, -0.3119]},
{'type': 'O', 'xyz': [1.8428, -1.4073, 1.2523]},
{'type': 'O', 'xyz': [0.4166, 2.5213, -1.2091]},
{'type': 'N', 'xyz': [-2.2359, -0.7251, 0.027]},
{'type': 'C', 'xyz': [-0.7783, -1.1579, 0.0914]},
{'type': 'C', 'xyz': [0.1368, -0.0961, -0.5161]},
...
{'type': 'H', 'xyz': [1.5832, 2.901, 1.6404]}
],
'id': 1,
'shapeM': [259.66, 4.28, 3.04, 1.21, 1.75, 2.55, 0.16, -3.13, -0.22, -2.18, -0.56, 0.21, 0.17, 0.09]
}
이 json 파일 하나하나에는 다음과 같이 18205개의 atoms 항목이 들어있습니다.
[u0017649@sys-96775 files]$ grep atoms pubChem_p_00000001_00025000.json | wc -l
18205
json 파일 하나의 row 수가 4924343이니까 4924343/18205 = 270 즉, 하나의 atoms 항목 안에 270줄의 data가 들어있는 셈이지요.
[u0017649@sys-96775 files]$ grep type pubChem_p_00000001_00025000.json | wc -l
565479
또 json 파일 하나에는 type이라는 단어가 565479 줄이 나옵니다.
[u0017649@sys-96775 files]$ echo "565479/18205" | bc -l
31.06174127986816808569
즉 하나의 atoms 당 평균 31개의 type가 존재한다는 것인데, 그나마 모든 atoms 항목에 균등한 개수의 type이 들어있는 것도 아니라는 뜻입니다. 하긴 각 분자마다 들어있는 원자의 종류와 개수가 각기 다를 수 밖에 없지요.
특히 atoms와 shapeM라는 항목들은 그 하나하나가 비정형 array로 되어 있습니다. 즉 분자마다 들어있는 원자 개수도 다르고 그에 따라 shape multipole 값도 다릅니다. 이런 비정형 array로 되어 있는 string 값을 분석하여 일정한 pattern을 모델링한다는 것은 매우 어려운 일이 될 것입니다.
이를 수치적으로 분석하려고 해도, atoms 항목 내의 저 많은 값들을 어떻게 분리하고 어떤 이름의 column으로 재정비해야 할지 골치가 아플 수 밖에 없습니다. 원래 그런 고민스러운 작업을 feature engineering이라고 하지요. 이 feature engineering을 어떻게 하느냐에 따라 machine learning으로 만들어낸 model의 성능과 accuracy가 크게 좌우됩니다. 이런 숙제는 data scientist들에게 돌아가는데, 숙련된 data scientist에게도 이는 크게 골치 아픈 일이며 또 과중한 업무 부담으로 이어지게 됩니다.
하지만 이 모든 것을 간단하게 해결해주는 것이 바로 H2O DriverlessAI입니다 ! H2O DriverlessAI의 가장 큰 혜택 중 하나가 자동화된 feature engineering 아니겠습니까 ?
하지만 전에 H2O DriverlessAI는 comma(,)나 pipe(|) 등으로 분리된 CSV 파일이나 Excel(xls, xlsx) 파일만 다룰 수 있다고 하지 않았던 가요 ? 저런 json 파일도 가공없이 통째로 분석할 수 있나요 ? 불행히도 그렇게는 안됩니다.
하지만 json 파일을 csv 파일로 가공하는 것은 매우 쉽습니다. 저는 개발자가 아니며 python program이라고는 Hello World 조차 제대로 할 줄 모르는 젬병이지만, 구글링해보면 구할 수 있는 아래의 샘플 코드 하나로 금방 이걸 전환할 수 있었습니다.
https://gist.github.com/pbindustries/803464d20f48a0a23d5934e3d11dadd6
위의 github에 올려진 sample을 이용하여 아래와 같이 j2c.py라는 이름의 매우 간단한 python code를 짰습니다. 짠 것이 아니라 그대로 copy & paste 했습니다.
[u0017649@sys-96775 files]$ vi j2c.py
import csv, json, sys
#check if you pass the input file and output file
if sys.argv[1] is not None and sys.argv[2] is not None:
fileInput = sys.argv[1]
fileOutput = sys.argv[2]
inputFile = open(fileInput) #open json file
outputFile = open(fileOutput, 'w') #load csv file
data = json.load(inputFile) #load json content
inputFile.close() #close the input file
output = csv.writer(outputFile) #create a csv.write
output.writerow(data[0].keys()) # header row
for row in data:
output.writerow(row.values()) #values row
이제 이를 이용하여 json 파일들을 csv 파일 형태로 변환하겠습니다.
[u0017649@sys-96775 files]$ for i in `ls *.json`
> do
> python j2c.py ./$i ./${i}.csv
> done
거의 날로 먹기지요 ? 아래와 같이 순식간에 csv 파일들이 새로 생성되었습니다.
[u0017649@sys-96775 files]$ ls *.csv
pubChem_p_00000001_00025000.json.csv pubChem_p_00125001_00150000.json.csv
pubChem_p_00025001_00050000.json.csv pubChem_p_00150001_00175000.json.csv
pubChem_p_00050001_00075000.json.csv pubChem_p_00175001_00200000.json.csv
pubChem_p_00075001_00100000.json.csv pubChem_p_00200001_00225000.json.csv
pubChem_p_00100001_00125000.json.csv pubChem_p_00225001_00250000.json.csv
csv 파일 속의 Row 수는 header까지 포함하여 18206, 즉 atoms 개수대로 만들어졌습니다.
[u0017649@sys-96775 files]$ wc -l pubChem_p_00000001_00025000.json.csv
18206 pubChem_p_00000001_00025000.json.csv
각 csv의 형태는 아래와 같습니다. En, id, shapeM, atoms의 4개 column으로 되어있는데, shapeM과 atoms는 여전히 무지막지한 형태의 비정형 string으로 되어 있습니다. id column은 분석에는 사실상 무의미한 column이지요. (보시기 편하도록 제가 shapeM에는 빨간색, atoms에는 파란색으로 글자색을 바꿨습니다.)
[u0017649@sys-96775 files]$ head -n 2 pubChem_p_00000001_00025000.json.csv
En,id,shapeM,atoms
37.801,1,"[259.66, 4.28, 3.04, 1.21, 1.75, 2.55, 0.16, -3.13, -0.22, -2.18, -0.56, 0.21, 0.17, 0.09]","[{u'xyz': [0.3387, 0.9262, 0.46], u'type': u'O'}, {u'xyz': [3.4786, -1.7069, -0.3119], u'type': u'O'}, {u'xyz': [1.8428, -1.4073, 1.2523], u'type': u'O'}, {u'xyz': [0.4166, 2.5213, -1.2091], u'type': u'O'}, {u'xyz': [-2.2359, -0.7251, 0.027], u'type': u'N'}, {u'xyz': [-0.7783, -1.1579, 0.0914], u'type': u'C'}, {u'xyz': [0.1368, -0.0961, -0.5161], u'type': u'C'}, {u'xyz': [-3.1119, -1.7972, 0.659], u'type': u'C'}, {u'xyz': [-2.4103, 0.5837, 0.784], u'type': u'C'}, {u'xyz': [-2.6433, -0.5289, -1.426], u'type': u'C'}, {u'xyz': [1.4879, -0.6438, -0.9795], u'type': u'C'}, {u'xyz': [2.3478, -1.3163, 0.1002], u'type': u'C'}, {u'xyz': [0.4627, 2.1935, -0.0312], u'type': u'C'}, {u'xyz': [0.6678, 3.1549, 1.1001], u'type': u'C'}, {u'xyz': [-0.7073, -2.1051, -0.4563], u'type': u'H'}, {u'xyz': [-0.5669, -1.3392, 1.1503], u'type': u'H'}, {u'xyz': [-0.3089, 0.3239, -1.4193], u'type': u'H'}, {u'xyz': [-2.9705, -2.7295, 0.1044], u'type': u'H'}, {u'xyz': [-2.8083, -1.921, 1.7028], u'type': u'H'}, {u'xyz': [-4.1563, -1.4762, 0.6031], u'type': u'H'}, {u'xyz': [-2.0398, 1.417, 0.1863], u'type': u'H'}, {u'xyz': [-3.4837, 0.7378, 0.9384], u'type': u'H'}, {u'xyz': [-1.9129, 0.5071, 1.7551], u'type': u'H'}, {u'xyz': [-2.245, 0.4089, -1.819], u'type': u'H'}, {u'xyz': [-2.3, -1.3879, -2.01], u'type': u'H'}, {u'xyz': [-3.7365, -0.4723, -1.463], u'type': u'H'}, {u'xyz': [1.3299, -1.3744, -1.7823], u'type': u'H'}, {u'xyz': [2.09, 0.1756, -1.3923], u'type': u'H'}, {u'xyz': [-0.1953, 3.128, 1.7699], u'type': u'H'}, {u'xyz': [0.7681, 4.1684, 0.7012], u'type': u'H'}, {u'xyz': [1.5832, 2.901, 1.6404], u'type': u'H'}]"
이 10개의 파일들로부터 마지막 5줄씩을 미리 잘라내어 총 50줄 (column까지 합하면 51줄)의 pubChem_test1.xlsx라는 test용 dataset을 만들어두겠습니다.
그리고나서 5줄씩 줄어든 이 10개의 파일들을 upload 하기 편하도록 하나의 pubChem1.zip 파일로 zip으로 압축하겠습니다.
[u0017649@sys-96775 files]$ zip pubChem1.zip *.csv
adding: pubChem_p_00000001_00025000.json.csv (deflated 78%)
adding: pubChem_p_00025001_00050000.json.csv (deflated 78%)
adding: pubChem_p_00050001_00075000.json.csv (deflated 78%)
adding: pubChem_p_00075001_00100000.json.csv (deflated 78%)
adding: pubChem_p_00100001_00125000.json.csv (deflated 77%)
adding: pubChem_p_00125001_00150000.json.csv (deflated 78%)
adding: pubChem_p_00150001_00175000.json.csv (deflated 77%)
adding: pubChem_p_00175001_00200000.json.csv (deflated 78%)
adding: pubChem_p_00200001_00225000.json.csv (deflated 78%)
adding: pubChem_p_00225001_00250000.json.csv (deflated 78%)
이제 H2O DAI의 web interface에 접속합니다. Dataset 메뉴에서 이 pubChem1.zip을 H2O DAI 서버로 upload하고 'Details' 항목을 보겠습니다.
보시는 바와 같이 En과 id는 각각 real과 integer로 인식되는데, shapeM과 atoms는 무지막지한 길이와 형태의 string으로 인식됩니다.
하지만 그냥 H2O DAI가 알아서 제대로 해줄 것이라고 믿고 그냥 그대로 prediction (training)으로 들어가겠습니다. 우리가 예측하려는 분자의 energy 값인 En을 Target column으로 정하고 Accuracy와 Time, Interpretability는 각각 10, 7, 7 정도로 세팅해서 돌리겠습니다.
참고로 이렇게 10-7-7로 맞출 경우의 algorithm과 iteration 회수, 그리고 model 및 feature 개수 등은 아래와 같이 설정됩니다. 위 사진의 왼쪽 상세 부분인데 글자가 너무 작아 잘 안 보이실 것 같아서 확대해서 캡춰했습니다.
이제 'Launch experiment'를 클릭하여 training을 시작합니다. 제가 가난하여 GPU가 없는 관계로, 이 training은 모두 2-core짜리 POWER8 가상 머신에서 수행했습니다. (SMT8 때문에 H2O는 이를 2 * 8 = 16-core 짜리 장비라고 인식합니다.) 그래서 꽤 오랜 시간이 걸렸습니다.
Training 중간 과정을 보면 중앙 하단에 'Variable Importance'라는 항목이 보입니다. 이는 dataset 내부의 여러 column 중 어느 column이 En 값 예측에 가장 중요한지 중요도 순으로 보여주는 것인데, 이 값들은 training이 진행됨에 따라 변하기도 하고 새로 나타나기도 합니다. 보시면 우리가 우겨넣은 column은 분명히 En, id, shapeM과 atoms 4개 밖에 없었는데, 이 메뉴에 보여지는 column 이름들은 atoms_0, atoms_18 등 새로운 column 이름들이 많이 나온 것을 보실 수 있습니다. 즉, H2O DAI가 내부에서 자동으로 feature engineering을 수행한 것이지요.
이제 experiment, 즉 training이 끝났습니다. Train된 model을 이용하여 prediction을 해보도록 하겠습니다.
'Score on Another Dataset'이라는 항목을 클릭한 뒤, 아까 따로 잘라놓았던 50줄짜리 pubChem_test1.xlsx를 선택합니다. 그러면 이 excel 표의 shapeM과 atoms column을 분석하고 아까 만들어진 model에 대입한 뒤, En 값이 어떨지 예측을 하여 그 결과를 csv 파일로 download 시켜줍니다.
실제 En 값과 H2O DAI가 예측한 En 값을 비교하여 그래프로 만들면 아래와 같습니다. 나름 꽤 그럴싸한 예측을 했다는 것을 보실 수 있습니다.
이렇게 H2O DAI는 data scientist의 업무 부담을 크게 줄여주고 더 빠른 feature engineering과 modeling을 통해 화학, 생명, 제조 등의 연구실에서도 유용하게 사용하실 수 있습니다.
2019년 1월 16일 수요일
IBM PowerAI Vision 사용법 demo : Image classification/object detection
IBM의 이미지 및 동영상을 위한 classification/object detection용 솔루션인 PowerAI Vision의 사용법을 정리했습니다.
먼저, image dataset에 대해 labeling을 해야 합니다. 이는 인터넷 상에서 쉽게 구할 수 있는 무료 tool인 labelimg, imageio 등을 이용하셔도 되고, 아예 처음부터 PowerAI Vision (이하 PAIV) 속에서 수행하셔도 됩니다. 아래의 예는 labelimg를 이용하여 사진 속의 object에 사각형을 그리고 labeling을 하는 장면입니다.
이런 식으로 labeling을 하면 그 결과가 해당 파일 하나마다 한개씩 xml 파일이 생성되어 저장됩니다. 이렇게 사진마다 labeling xml 파일이 생성되면 파일들을 PAIV에 upload할 준비가 된 것입니다. 이 사진 및 xml 파일들을 PAIV에 upload할 때 더 간편하게 할 수 있도록 아예 zip 파일로 묶어놓으면 더 좋습니다.
참고로 이 labeling xml 파일의 내용은 아래와 같습니다.
이제 web browser를 통해 PAIV에 접속하겠습니다. 기본 userid/passwd는 admin/passw0rd입니다. 접속하고나면 맨 먼저 'Data sets' 항목으로 갑니다. 기존에 다른 분들이 upload하신 dataset들이 있고 수지씨의 아름다운 사진도 보입니다만 한눈 팔지 마시고 우리는 'Create New Data Set'을 클릭합니다.
'Data Set Name'에 적절한 이름을 붙이십시요.
그러고나면 우리가 만든 fighter라는 dataset이 생성된 것을 보실 수 있습니다. 그러나 여기에는 아직 사진이 한장도 들어있지 않습니다. 이제 여기에 사진을 넣어야 합니다.
우리가 만든 fighter라는 dataset을 클릭하면 '파일을 여기에 끌어놓으라'는 메시지와 'Import files'라는 사각버튼이 보입니다. 여기서는 이 사각버튼을 눌러 보겠습니다.
이건 평범하게, 우리가 쓰고 있는 PC의 directory로부터 파일을 upload할 수 있는 메뉴를 불러옵니다. 여기서는 아까 zip으로 압축해놓았던 fighter.zip을 선택합니다.
이제 보면 fighter.zip 속에 들어있는 사진들과 xml 파일을 PAIV가 읽어들여 dataset으로 포맷한 것으로 보실 수 있습니다. 다만 사진들을 보면 작은 글씨로 f16, f15 등이 아까 우리가 labeling해놓은 대로 표시되어 있으나, 사진 위에는 큰 글씨로 'Uncategorized' 라고 분류된 것을 보실 수 있습니다. 이는 위에서 우리가 했던 label은 사진 속의 object에 붙여놓은 것이고, 사진 전체에 대해 f15, f16 등으로 분류하는 label을 붙여놓은 것은 아니기 때문입니다. 즉, 이 사진들은 classification을 위해서 label이 된 것이 아니라 object detection을 위해서 label이 된 것입니다.
여기서 잠깐 classification과 object detection을 구분하자면 아래와 같습니다.
Classification : 사진 1장 전체를 미리 지정한 category 중 어느 하나로 분류
Object detection : 사진 내에 미리 지정된 object에 해당하는 것이 있는지 감지하여 사각형으로 표시
우리의 dataset에는 크기 f15, f16, f14의 3가지 object들이 들어있습니다. 이것들을 모두 택하시고, 이제 'Train'이라는 사각버튼을 눌러 training에 들어가겠습니다.
Train 메뉴에서는 먼저 'Type of training'을 'Image classification'과 'Object detection' 둘 중 하나에서 선택해야 합니다. 물론 이 dataset labeling 특성에 맞게 우리는 'Object detection'을 택해야 합니다. ('Image classification'을 택하려면 먼저 사진의 labeling을 그에 맞게 해야 하는데, 그건 뒤에서 다시 보시겠습니다.) 이어서 그 아래의 'Model selection'을 해야 하는데, 여기서는 'Optimized for accuracy (faster R-CNN)'을 택했습니다.
그 아래의 'Advanced options' 메뉴는 max interation이나 learning rate 같은 hyperparameter를 지정하는 곳입니다. 여기서는 아무 수정 없이 그냥 train 하겠습니다. 아래의 'Train' 사각버튼을 누르면 train이 시작됩니다.
Train이 시작되면 이 과정이 얼마나 더 걸리는지가 대략 계산되어 display 됩니다.
그 아래 화면에서는 iteration이 진행됨에 따라 감소하는 loss graph가 display 됩니다.
Training이 완료되면 'Model details'와 'Deploy'의 2가지 사각버튼이 보입니다. 이 중 먼저 'Model details'를 클릭하겠습니다.
여기서는 train된 model에 대한 간단한 정보들이 display됩니다. Accuracy와 Precision, mAP 등이 무엇을 뜻하는지는 아래 URL을 참조하시기 바랍니다.
https://www.ibm.com/support/knowledgecenter/SSRU69_1.1.2/base/vision_metrics.html#vision_metrics__detection_gpu
이제 맨 위의 'Deploy model' 사각버튼을 눌러보겠습니다. 아래와 같이 model의 이름을 정하고 엔터를 누르면 'Deployed models'에 방금 생성한 model의 이름이 나타나게 됩니다.
우리가 방금 생성한 model 이름을 클릭해 봅니다. 그러면 이 모델이 Image classification을 위한 것인지 Object detection을 위한 것인지가 표시되면서, 외부에서 이 model을 불러쓸 수 있도록 API endpoint도 display됩니다. 여기서는 그 밑에 있는 'Tes Model' 메뉴에 별도의 사진을 넣어 제대로 object를 탐지하는지 시험해보겠습니다. 시험 방법은 간단합니다. 그냥 PC의 파일관리자에서 원하는 사진을 끌어다 'Drop files here'라고 쓰인 흰색 상자에 넣기만 하면 됩니다.
보셨다시피 training dataset에 사진이 몇장 없었기 때문에, 이 모델의 실제 정확도는 매우 낮습니다. 그래서 F14 전투기를 F16으로 분류하기도 합니다만, 겉으로 꽤 비슷해 보이는 F14와 F15를 훌륭하게 제대로 구분해서 보여주기도 합니다.
이번에는 처음에 만들었던 'fighter' dataset으로 되돌아가보겠습니다. 메뉴 화면 중에 'Augment data'라는 것이 보일 것입니다. 그걸 클릭해보면, blur/color/sharpen/rotate/crop/noise 등과 같은 기법을 통해 1장의 사진을 여러장으로 가공하여 사진 수를 늘리는 메뉴임을 아실 수 있습니다. 여기서는 blur와 color 빼고 모든 기법을 다 택하고 'Continue'를 눌러보겠습니다.
아래와 같이 새로 추가될 증강 dataset에 'fighter_augmented'라고 이름을 붙여주고나면, 그 이름으로 새로운 dataset이 추가된 것을 보실 수 있습니다. 그리고 그 속의 사진 개수는 원래 24장이던 것이 무려 528장으로 늘어나 있습니다. 이제 그 'fighter_augmented' dataset을 클릭해보겠습니다.
실제로 하나의 사진이 blur라든가 rotate 등의 기법으로 여러 장으로 복제된 것을 보실 수 있습니다. 이 528장의 사진을 가지고 이번에는 object detection이 아닌 classification을 수행해보도록 하겠습니다.
위의 사진들 중 f16에 해당하는 사진들을 click한 뒤, 사진 위의 메뉴 중 'Assign category'를 클릭하십시요. 그리고 거기에 category 이름을 f14, f15, f16 등으로 지정하면 됩니다.
이제 아래 dataset과 같이 각 사진마다 f14, f15, f16 중 하나의 category가 지정되었습니다. 이제 'Train model' 사각버튼을 눌러 train을 시작합니다.
'Type of training' 항목에서는 아까와는 달리 'Object detection' 대신 'Image Classification'을 선택합니다. 그 아래의 'Model selection'에서는 시스템에서 제공하는 기본 neural network인 'GoogleNet'을 선택합니다. 아직은 Caffe에서의 GoogleNet만 기본 지원되지만, 그 밑의 'Custom Model'을 선택하면 외부에서 가져온 tensorflow의 다른 neural network도 import 하여 사용할 수 있습니다.
그 밑의 'Advanced options'에서는 'Base model'을 선택하게 되어 있습니다. 대부분의 경우엔 'General'을 택하겠습니다만, 혹시 우리가 지금 train하려는 dataset이 PAIV에서 기본으로 제공되는 trained model인 '풍경, 얼굴, 자동차' 등에 해당하는 것이라면, 이 기본 model에 대해 추가 dataset으로 추가 train 하는 형태로 진행할 수 있습니다. 그 경우 더 높은 정확도를 얻을 수 있겠지요. 불행히도 우리는 여기서 전투기 사진을 사용하고 있으므로 해당 사항이 없고, 따라서 'General'을 택해야 합니다.
나머지 부분은 learning rate 등의 hyper parameter setting입니다. 여기서는 변경하지 않고 'Train' 사각버튼을 눌러 train을 시작합니다.
그 다음 부분은 위에서 한번 해본 object detection과 동일합니다. Model training이 끝나면 해당 model을 적당한 이름으로 save하고, 이어서 deploy하면 그 이름이 Deployed models 목록에 나타나는 것을 보실 수 있습니다.
방금 deploy한 'fighter_augmented_model_deployed'를 클릭하여 들어가보면, 아까의 object detection 모델과 같이 'API endpoint' 정보 등과 함께, 'Test Model' 메뉴가 나옵니다. 여기에 별도로 준비한 전투기 사진을 drag & drop으로 넣어서 제대로 classification을 하는지 보겠습니다.
이 classification model은 아까의 object detection model에 비해 (비록 실제로 다양한 사진을 넣은 것이 아니라 augmented dataset에 불과하지만) 훨씬 많은 dataset을 사용해서 train했기 때문인지, 아까의 object detection보다는 훨씬 더 나은 정확도를 보여줍니다. 비록 도중에 F14를 F16이라고 분류한 것이 있긴 합니다만, 전투기에 별 관심 없는 사람이라면 구별하기 힘든 F14와 F15를 매우 잘 분류하고 있습니다.
이번에는 PAIV가 자랑하는 특수 기능인 'Auto labeling'을 시도해보겠습니다. 이는 기존에 train해둔 model을 이용하여, 새로 추가된 dataset에 대해 object label을 자동으로 수행해주는 기능입니다. 많은 일손과 시간이 들어가는 label 수작업을 크게 줄여주는 고마운 기능입니다. 다만 이 기능에 대해서는 2가지를 염두에 두셔야 합니다.
1) Classification은 지원하지 않으며, Object detection을 위한 labeling만 지원합니다.
2) Auto labeling에 사용한 기존 model의 accuracy가 좋지 않으면, 당연히 auto labeling의 품질도 떨어집니다.
Auto labeling을 수행하기 위해, 먼저 추가의 전투기 사진들로 새로운 dataset 'more_fighters'를 만듭니다.
이제 메뉴 바 중의 'Auto label'을 클릭합니다. 그러면 아래와 같이 기존 deployed model 중에서 auto label에 사용할 model을 선택하게 되어 있습니다. 우리는 아까 만들어두었던 fighter_model1_deployed를 선택합니다.
이어서 'Auto label' 사각버튼을 누르면 auto label이 시작됩니다.
사진이 34장 밖에 안 되므로 auto label도 순식간에 끝납니다. 그러나, 기억하시다시피 우리가 사용한 'fighter_model1_deployed' model은 정확도가 별로 높지 않았습니다. 그래서인지, 34장의 사진 중 고작 10개의 object만 label했습니다.
그나마 아래와 같이 F14에 F16이라는 label을 붙여놓았습니다.
원래 어떤 model이라도 100% 정확할 수는 없으며, 따라서 auto label된 dataset은 결국 사람이 눈으로 검토하고 잘못된 label은 손으로 수정해야 합니다. 이제 위의 잘못 label된 것을 수정하겠습니다. 해당 사진을 클릭하면 아래와 같이 pop up 되는데, 여기서 나온 'Label objects' 사각버튼을 클릭합니다.
사진 오른쪽 위에 f16(1) X 라는 표시가 보입니다. 그 X 표시에 클릭하면 f16이라는 사각형으로 둘러싸인 label이 삭제됩니다.
이제 사진 오른쪽 위에 있는 f14(0) 이라는 표시를 선택한 뒤, 마우스를 전투기 사진 옆에서 click & drag 하여 사각형을 그려주면 이 전투기가 f14로 label됩니다.
이제 위쪽의 하얀 사각버튼 'Done editing'을 클릭하면 label 수정이 완료됩니다.
기존에 없던 object label을 이 메뉴에서 새로 추가하는 것도 가능합니다. 가령 아래 사진에는 F15와 F16 외에도, 머스탱(P51D) 프로펠러 전투기와 F22 스텔스 전투기도 보입니다. 이것들에 대해서는 아래와 같이 사진 오른쪽 위의 '+ Add New'를 클릭하여 즉석에서 새로운 label을 입력해주면 됩니다.
PowerAI Vision의 좀더 자세한 정식 manual은 아래 URL을 참조하세요.
https://www.ibm.com/support/knowledgecenter/en/SSRU69_1.1.2/navigation/welcome.html
피드 구독하기:
글 (Atom)