2019년 1월 6일 일요일

라즈베리파이 기반 딥러닝 객체 인식 개발 방법

이 글은 어느 분이 블로그로 문의한 내용도 정리할 겸 라즈베리파이 기반 딥러닝 객체 인식 개발 방법에 대한 내용을 간단히 요약해 공유한다.


사실, 이와 관련된 자료는 구글링을 하면 꽤 많이 검색된다. 참고로, OpenCV를 이용해 파이썬 기반에서 객체를 인식하는 방법도 유용하다.

머리말
비전에서 객체 인식이란 다음과 같이 특정 이미지에서 원하는 객체 종류, 위치와 크기를 얻는 것이다.
객체 인식은 다음과 같이 다양한 분야에서 사용된다.

객체 인식에서 유명한 방법인 YOLO는 매우 빠른 방식으로 객체 탐색을 할 수 있다.

YOLO 알고리즘을 간단히 설명하면 다음과 같다.

image = readImage()
cells = 검색 셀 범위 설정
index = 0
for cell in cells
   maxPrediction = 0.0
   for region in cell.region
      cell_image = image.getRegionImage(region)
      prediction[index] = prediction(cell_image)
      if(maxPrediction < prediction[index])
         maxPrediction = prediction[index]
         region = cell.grow(region)
      else
         index++
         break   

의사코드는 다음과 같다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#this is an Image of size 140x140. We will assume it to be black and white (ie only one channel, it would have been 140x140x3 for rgb)
image = readImage()

#We will break the Image into 7 coloumns and 7 rows and process each of the 49 different parts independently
NoOfCells = 7

#we will try and predict if an image is a dog, cat, cow or wolf. Therfore the number of classes is 4
NoOfClasses = 4
threshold = 0.7

#step will be the size of step to take when moving across the image. Since the image has 7 cells step will be 140/7 = 20
step = height(image)/NoOfCells

#stores the class for each of the 49 cells, each cell will have 4 values which correspond to the probability of a cell being 1 of the 4 classes
#prediction_class_array[i,j] is a vector of size 4 which would look like [0.5 #cat, 0.3 #dog, 0.1 #wolf, 0.2 #cow]
prediction_class_array = new_array(size(NoOfCells,NoOfCells,NoOfClasses))

#stores 2 bounding box suggestions for each of the 49 cells, each cell will have 2 bounding boxes, with each bounding box having x, y, w ,h and c predictions. (x,y) are the coordinates of the center of the box, (w,h) are it's height and width and c is it's confidence
predictions_bounding_box_array = new_array(size(NoOfCells,NoOfCells,NoOfCells,NoOfCells))

#it's a blank array in which we will add the final list of predictions
final_predictions = []

#minimum confidence level we require to make a prediction
threshold = 0.7

for (i<0; i<NoOfCells; i=i+1):
 for (j<0; j<NoOfCells;j=j+1):
  #we will get each "cell" of size 20x20, 140(image height)/7(no of rows)=20 (step) (size of each cell)"
  #each cell will be of size (step, step)
  cell = image(i:i+step,j:j+step) 

  #we will first make a prediction on each cell as to what is the probability of it being one of cat, dog, cow, wolf
  #prediction_class_array[i,j] is a vector of size 4 which would look like [0.5 #cat, 0.3 #dog, 0.1 #wolf, 0.2 #cow]
  #sum(prediction_class_array[i,j]) = 1
  #this gives us our preidction as to what each of the different 49 cells are
  #class predictor is a neural network that has 9 convolutional layers that make a final prediction
  prediction_class_array[i,j] = class_predictor(cell)

  #predictions_bounding_box_array is an array of 2 bounding boxes made for each cell
  #size(predictions_bounding_box_array[i,j]) is [2,5]
  #predictions_bounding_box_array[i,j,1] is bounding box1, predictions_bounding_box_array[i,j,2] is bounding box 2
  #predictions_bounding_box_array[i,j,1] has 5 values for the bounding box [x,y,w,h,c]
  #the values are x, y (coordinates of the center of the bounding box) which are whithin the bounding box (values ranging between 0-20 in your case)
  #the values are h, w (height and width of the bounding box) they extend outside the cell and are in the range of [0-140]
  #the value is c a confidence of overlap with an acutal bounding box that should be predicted
  predictions_bounding_box_array[i,j] = bounding_box_predictor(cell)

  #predictions_bounding_box_array[i,j,0, 4] is the confidence value for the first bounding box prediction
  best_bounding_box =  [0 if predictions_bounding_box_array[i,j,0, 4] > predictions_bounding_box_array[i,j,1, 4] else 1]

  # we will get the class which has the highest probability, for [0.5 #cat, 0.3 #dog, 0.1 #wolf, 0.2 #cow], 0.5 is the highest probability corresponding to cat which is at position 0. So index_of_max_value will return 0
  predicted_class = index_of_max_value(prediction_class_array[i,j])

  #we will check if the prediction is above a certain threshold (could be something like 0.7)
  if predictions_bounding_box_array[i,j,best_bounding_box, 4] * max_value(prediction_class_array[i,j]) > threshold:

   #the prediction is an array which has the x,y coordinate of the box, the height and the width
   prediction = [predictions_bounding_box_array[i,j,best_bounding_box, 0:4], predicted_class]

   final_predictions.append(prediction)

print final_predictions

prediction함수는 딥러닝으로 훈련된 모델을 사용한다. 훈련 모델을 얻기위해서는 훈련 데이터를 미리 준비해야 한다. 준비 순서는 다음과 같다.
훈련 데이터 획득
이 단계를 수행하기 위해서는 객체 당 100개 정도 수준의 데이터가 필요하다. 가능한 예측해야할 데이터와 비슷한 자료가 필요하다.

라벨링(labeling) - 주석달기
labelImg와 같은 도구를 이용해 이미지에 테두리 상자를 그리고, 라벨을 달아 놓는다. 사실 딥러닝 소스코드는 그리 어렵지 않은 수준이지만, 라벨링 작업은 매우 노동집약적이고 힘든 작업이다. 딥러닝 소스코드는 사실 이런 데이터가 된다. 

모델학습
보통 GPU가 달린 컴퓨터를 이용해 학습을 시킨다. 이미 CNN과 같이 딥러닝 이미지 인식용 모델은 공개된 것이 많다. 예제도 많으니 이런 것들을 사용해 학습시킨다. 
훈련에 필요한 데이터 량을 줄이는 것도 필요하다. 이와 관련된 링크를 참고한다. 미리 훈련된 모델은 여기서 찾을 수 있다.

혹시, 텐서플로우나 케라스를 설치하지 않았고, 설치하는 데 힘들다면, 다음과 같이 이미 만들어진 도커 이미지를 사용할 수도 있다. 사용방법은 여기를 참고한다.

sudo nvidia-docker run -p 8000:8000 -v `pwd`:data docker.nanonets.com/pi_training -m train -a ssd_mobilenet_v1_coco -e ssd_mobilenet_v1_coco_0 -p '{"batch_size":8,"learning_rate":0.003}' 

학습된 모델 설명은 여기서 얻을 수 있다.

라즈베리파이 딥러닝 실행 속도 개선
딥러닝 모델은 32비트 float 실수형 가중치로 구성된 행렬로 볼 수 있다. 딥러닝 모델 중 유명한 AlexNet은 크기만 거의 200MB이다. 이 행렬을 로딩해 실시간으로 계산한다는 것은 라즈베리파이 같은 임베디드 컴퓨터에서는 쉽지 않다. 

이런 이유로 32비트 float을 양자화해 8비트 정수로 변환시켜 사용한다. 이 결과로 딥러닝 모델 크기는 75%가 줄어든다. 
양자화 셀 스크립트는 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
curl -L "https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz" |
  tar -C tensorflow/examples/label_image/data -xz
bazel build tensorflow/tools/graph_transforms:transform_graph
bazel-bin/tensorflow/tools/graph_transforms/transform_graph \
--in_graph=tensorflow/examples/label_image/data/inception_v3_2016_08_28_frozen.pb \
  --out_graph=/tmp/quantized_graph.pb \
  --inputs=input \
  --outputs=InceptionV3/Predictions/Reshape_1 \
  --transforms='add_default_attributes strip_unused_nodes(type=float, shape="1,299,299,3")
    remove_nodes(op=Identity, op=CheckNumerics) fold_constants(ignore_errors=true)
    fold_batch_norms fold_old_batch_norms quantize_weights quantize_nodes
    strip_unused_nodes sort_by_execution_order

라즈베리파이 설정
라즈베리파이에 카메라를 설치한다. 

카메라에서 이미지를 가져오는 코드는 다음과 같이 매우 간단하다.

import picamera, os
from PIL import Image, ImageDraw
camera = picamera.PiCamera()
camera.capture('image1.jpg')
os.system("xdg-open image1.jpg")

딥러닝 모델을 다운로드한다.

sudo nvidia-docker run -v `pwd`:data docker.nanonets.com/pi_training -m export -a ssd_mobilenet_v1_coco -e ssd_mobilenet_v1_coco_0 -c /data/0/model.ckpt-8998

라즈베리파이에 텐서플로우와 모델을 설치한다.

1
2
3
4
5
6
7
8
sudo apt-get install libblas-dev liblapack-dev python-dev libatlas-base-dev gfortran python-setuptools libjpeg-dev
sudo pip install Pillow
sudo pip install http://ci.tensorflow.org/view/Nightly/job/nightly-pi-zero/lastSuccessfulBuild/artifact/output-artifacts/tensorflow-1.4.0-cp27-none-any.whl
git clone https://github.com/tensorflow/models.git
sudo apt-get install -y protobuf-compiler
cd models/research/
protoc object_detection/protos/*.proto --python_out=.
export PYTHONPATH=$PYTHONPATH:/home/pi/models/research:/home/pi/models/research/slim

이제 모델을 실행해 이미지에서 객체를 인식해 보자.
python ObjectDetectionPredict.py --model data/0/quantized_graph.pb --labels data/label_map.pbtxt --images /data/image1.jpg /data/image2.jpg

라즈베리파이에서 이미지 인식 성능은 다음과 같다. 예를 들어, raster RCNN은 초당 34.52 예측을 할 수 있다.

NanoNets 기반 예측
나노넷(NanoNets)은 앞의 준비과정보다 좀 더 단순하게 객체 인식을 할 수 있도록 해 준다. 나노넷은 GPU같이 값비싼 하드웨어가 필요없이 라즈베리파이 같은 장치에서도 사용하기 좋다.

다음은 나노넷의 간단한 예제이다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import picamera, json, requests, os, random
from time import sleep
from PIL import Image, ImageDraw

#capture an image
camera = picamera.PiCamera()
camera.capture('image1.jpg')
print('caputred image')

#make a prediction on the image
url = 'https://app.nanonets.com/api/v2/ObjectDetection/LabelFile/'
data = {'file': open('image1.jpg', 'rb'), \
    'modelId': ('', 'YOUR_MODEL_ID')}
response = requests.post(url, auth=requests.auth.HTTPBasicAuth('YOUR_API_KEY', ''), files=data)
print(response.text)

#draw boxes on the image
response = json.loads(response.text)
im = Image.open("image1.jpg")
draw = ImageDraw.Draw(im, mode="RGBA")
prediction = response["result"][0]["prediction"]
for i in prediction:
    draw.rectangle((i["xmin"],i["ymin"], i["xmax"],i["ymax"]), fill=(random.randint(1, 255),random.randint(1, 255),random.randint(1, 255),127))
im.save("image2.jpg")
os.system("xdg-open image2.jpg")

나노넷에 대한 상세한 내용은 다음 링크를 참고한다.



마무리
이 글에서는 라즈베리파이에서 딥러닝 모델을 실행해 객체 인식 탐색하는 방법을 알아보았다.
이 글의 코드는 다음 링크를 참고하길 바란다.


이 글에 대한 좀 더 상세한 내용은 다음 링크를 참고하길 바란다.

레퍼런스
1. Raspberry Pi: Deep learning object detection with OpenCV
2. How to easily Detect Objects with Deep Learning on Raspberry Pi

2018년 12월 27일 목요일

직접 간단히 만들어보는 블록체인과 가상화폐


이 글은 블록체인 동작 메커니즘 이해하고자 하는 싶은 분을 위해 핵심 개념만 간단히 개발하는 과정을 공유한다.

좀 더 자세한 내용은 아래 링크를 참고한다.

우선 요구사항을 정해보자
블록체인 개발전에 블록체인 개념을 내포하는 몇가지 요구사항을 정의해야 한다.
  • 가상화폐: 가치 교환을 위한 디지털 가치 보관 수단
  • 지갑: 개인이나 조직 가상화폐를 보관하는 전자 계정
  • 트랜잭션: 데이터의 변화를 기록한 단위
  • 블록: 트랜잭션을 기록한 단위. 데이터 변조를 막기 위해 해쉬 암호화 사용
  • 마이닝: 블록을 관리하는 컴퓨팅 소모 자원에 대한 대가 지급
  • 스마트 계약: 상호간 거래시 계약 불이행을 방지하기 위한 수단

간단히 만들어 보는 블록체인 
우선 요구사항을 바탕으로 아키텍처를 디자인해보자.

요구사항에 해당하는 객체를 모두 클래스화한다. 그리고, 각 클래스간 관계를 디자인한다. 예를 들어, 코인과 화폐는 서로 관계가 있다.

코인은 가치를 보관하는 개념이다. 트랜잭션은 쌍방간의 계약중 하나이며, 가치의 변경을 담는 역할이다. 블록은 트랜잭션의 변화를 기록한다. 블록은 링크드리스트(linked list) 자료구조로 정의된다. 블록의 위변조는 해쉬값으로 알 수 있다. 블록체인은 블록을 생성하고 관리한다. 스마트계약은 트랜잭션을 발생시킨다.

이를 디자인하면 다음과 같다.
클래스 디자인(UML)

앞의 디자인을 반영해 중요한 부분만 코딩하면 다음과 같다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class coin
{
public:
   double value = 0;
}

class wallet
{
public:
   string accountAddress;
   coin coin;
}

class transaction
{
public:
   string fromAcccount, toAccount;
   coin trasnferValue;
   string calculateHash() {return SHA256(this)};
}

class block
{
public:
   const int MAX_BLOCK_TRANSCTION 40
   int countTransaction = 0;
   transaction[MAX_BLOCK_TRANSACTION];
   block* previousBlock = NULL;
   block* nextBlock = NULL;

   int insertTransaction(transaction& t);
   string calculateHash();
}

class blockChain
{
public:
   block* createGenesisBlock();
   coin* miningBlock(int hash, block& block);
   block* insertBlock(block* b);
}

class smartContract
{
public:
   void setScript(string code);
   bool execute();

private:
   string code;
   python contractScript;
}
style by hilite

마무리
이 글은 블록체인과 스마트 계약 구현 원리를 이용해 보기 위해 간단히 아키텍처를 디자인해보고, 코드로 구현해 보았다. 사실, 이외 여러 기능과 구성요소가 필요하나, 다양한 블록체인 플랫폼의 핵심적인 내용은 비슷하다. 좀 더 상세한 내용은 다음 레퍼런스를 참고하길 바란다. 

레퍼런스

2018년 12월 16일 일요일

Blue Z IoT 오픈소스 프로젝트

이 글은 BlueZ IoT 오픈소스 프로젝트를 간략히 설명한다. 

Bluz는 Arduino처럼 작동하지만 Bluetooth LE (BLE) 통신 기능이 내장된 개발 키트이다. 이 BLE 연결을 통해 장치는 REST API를 통해 전 세계 어느 곳에서나 하드웨어에 액세스할 수있게 해주는 Particle 클라우드와 통신할 수 있다. 
BluZ 개념도

함수를 호출하고, 변수를 가져오고, 이벤트를 게시 또는 구독하고, 웹 훅을 트리거하고, 웹 IDE를 통해 무선으로 프로그래밍 할 수 있다. 

Bluz는 배터리 수명이 긴 무선 애플리케이션에 이상적이다. BLE를 사용하면, bluz는 몇 달 또는 몇 년 동안 코인 셀 배터리로 지속될 수 있으며, 모두 클라우드에 연결되어 액세스 할 수 있다. 

Bluz는 Arduino에서 사용되는 것과 동일한 언어인 Wiring으로 프로그래밍되어 있으므로 많은 호환 가능한 개발 키트가 사용 가능하다.

레퍼런스는 아래 링크를 참고한다.

2018년 12월 10일 월요일

마이크로소프트 딥러닝 인공지능 도구

몇 년사이에 마이크로소프트사의 딥러닝 인공지능 도구가 크게 발전한 것 같다. 물론 오픈소스 기반인데다 무료라 사용하기 어렵지 않다.

이 글은 대표적인 이 회사의 인공지능 도구를 소개한다.

  • Azure Notebooks: 쥬피터 노트북 기반 다양한 프로젝트를 무료로 개발할 수 있음
  • Custom Vision: 딥러닝 모델 관련 설정 및 코딩 없이, 손쉽게 이미지를 업로드하면 딥러닝 모델을 개발할 수 있음
  • Caption Bot: 비전 딥러닝 모델을 이용한 간단한 서비스로 이미지를 올리면, 자동으로 객체과 상호관계를 인식해 설명을 해줌










2018년 11월 18일 일요일

아마존 AWS 딥렌즈(DeepLens) 사용기

이 글은 연구용으로 여름에 구매했던 아마존 AWS 딥렌즈(DeepLens)의 간단한 사용기이다. 연말이 되어서야 아래한글 연구행정 문서질을 어느정도 정리하고, 시간이 되어 딥렌즈를 꺼내놓고 분석하고 사용법을 정리한다.

개요
딥렌즈는 아마존에서 개발한 딥러닝 기반 비전용 카메라이다.


딥렌즈를 이용하면, 미리 훈련된 신경망 모델을 클릭만 해서 딥렌즈에 적용하여, 다양한 비전 프로젝트를 손쉽게 진행할 수 있다. 이미 아마존에는 다양한 최신 신경망 모델이 업로드되어 있어, 적용에 그리 어렵지 않다.

딥렌즈 스펙
딥렌즈 스펙은 다음과 같다.

Hardware
Intel Atom® processor
Gen9 graphics
Ubuntu 16.04 LTS
106 GFLOPS performance
Dual band Wi-Fi
4 MP camera with MJPEG
H.264 encoding at 1080p resolution
Storage
8 GB RAM
16 GB memory
32 GB SD card
Inputs / Outputs
2 USB ports
Micro HDMI
Audio out
Software
AWS Greengrass pre-configured
Cl-DNN (compute library for deep neural networks)

셋업
딥렌즈를 구입하고, 다음 영상을 참고해 셋업한다.


딥러닝 모델 설치
이 예제에서는 객체를 탐지하는 CNN 모델을 적용할 것이다. 이 모델은 이미 훈련되어 있으며, 딥렌즈에 설치하면, 바로 실행할 수 있다. 다음 영상을 참고하자.

딥러닝 모델 설치
지금까지 딥렌즈 사용법을 간단히 살펴보았다. 딥러닝도 점차 사용하기 쉬워지고 있다. 지금은 딥러닝이 특별한 사람들만 사용하는 기술처럼 보이지만, 아마존, 마이크로소프트와 같이 대기업 소프트웨어가 손쉽게 딥러닝 기술을 사용할 수 있는 도구를 출시하고 있어, 조만간 대중화되리라 생각한다. 그때는 누구나 드래그 드롭으로 딥러닝을 개발하고, 응용 프로그램을 만들 수 있을 것이다.



2018년 11월 3일 토요일

간단한 BIM 기반 스마트 계약 개발하기

이 글에서는 BIM 기반 스마트 계약을 개발할 때, 어떤 방식으로 진행하는 지를 간략히 설명한다.

스마트 계약 개발 절차
개발은 보통 요구사항 정의, 분석, 설계, 개발 및 테스트로 진행된다.  요구사항 정의 시 사용되는 방법 중 하나가 유스케이스 분석이다. 유스케이스 분석을 통해, 실제 사용 사례와 시나리오를 정리한다.

유스케이스 정의
BIM의 경우 제일 큰 이슈 중 하나가 디지털 모델 변경이 다수 관계자간에 진행되어, 모델의 신뢰성 보장이 어렵다는 것이다. 신뢰성 보장을 위해서는 모델에 대한 이슈 발생과 처리 시 과정을 추적할 수 있어야 한다. 이슈 관리 이력을 분산원장(블록체인)에 저장해, 모델 변경 이력을 추적할 수 있도록 하고, 모델에 대한 신뢰성을 확보한다. 그래서, 유스케이스를 BIM 협업 상황에서 모델 변경 관련된 모델링 이슈 이력 관리로 한다. 모델 변경 시 관련 트랜잭션 정보는 다음과 같이 가정한다.

1. BIM file URL = resource_type://address+directory
2. Issue No = IS###
3. Issue reason = enum {element_collision, element_missing, invalid_data, etc}
4. Sender = email
5. Receiver = email
6. Date = YYYY/MM/DD
7. State = enum {open, progress, solved, close}

구현해 보기
앞의 유스케이스를 스마트 계약으로 구현해 보면 다음과 같다.

pragma solidity ^0.4.0;
contract BIM_modeling_issue {
    struct Issue {
        uint no;
        string url;
        uint reason;
        string sender, receiver;
        string date;
        uint state;
    };
    Issue[] issues;

    /// Create a issue
    function BIM_modeling_issue(uint8 num) public {
        issues.length = num;
    }

    function findIssueBySender(string sender) public Issue returns (Issue _is) {
        for(uint8 i = 0; i < issues.length; i++)
        {
            if(issues[i].sender == sender) { 
               _is = issues[i];
            }
        }
    }
}



BIM 모델링 이슈 데이터를 관리하는 Issue 구조체를 정의하고, issues 배열을 만든다. sender가 생성한 issue를 찾는 findIssueBySender라는 간단한 함수를 만들어 보았다. 이 스크립트는 Solidity 계약 개발을 지원하는 REMIX등에서 실행할 수 있다.


레퍼런스
Welcome to Remix documentation!
Create Your First Smart Contract In Ethereum With Ganache And Remix IDE
Build Your First Ethereum Smart Contract with Solidity — Tutorial


2018년 10월 31일 수요일

Google 에서 제안하는 어린이 코딩 도구

집에서도 할 수 있는 코딩 방법 중 구글에서 제안한 유용한 도구를 설명한 사이트가 있어 잠깐 소개한다. 코딩에 흥미를 갖게 하는 도구들이 소개되어 있는 데, 어린이들도 쉽게 할 수 있는 것들이라 부모와 충분히 함께 할 수 있다.

코드짐(Code Gym)
구글에서 제공하는 오픈소스 코딩 도구로 그리기, 음악, 그래픽 패턴등을 코딩을 통해 만들고 즐길 수 있다.

블로키(Blockly)
코딩으로 재미있는 다양한 그림을 만들 수 있는 도구이다.

Made with Code
코딩으로 다양한 게임과 스토리를 만드는 도구이다. 설명이 잘 되어 있어, 따라하는 데 그리 어렵지 않다.

MIT 앱 인벤터(app inventor)
스마트폰에 실행되는 앱을 블록 코딩 방식으로 만들 수 있다. MIT에서 개발되었으며, 오픈소스이다.