GithubHelp home page GithubHelp logo

kyopark2014 / image-classification-via-iot-greengrass Goto Github PK

View Code? Open in Web Editor NEW
7.0 3.0 0.0 151.05 MB

It shows how to create custom component for image classification in IoT Device.

Python 79.21% Dockerfile 0.77% JavaScript 2.32% TypeScript 16.73% Shell 0.97%
aws-cdk deep-learning greengrassv2 image-classification inference iot machine-learning

image-classification-via-iot-greengrass's Introduction

IoT 디바이스에서 머신러닝(ML)을 이용한 이미지 분류하기

IoT 디바이스에서 이미지 분류를 하기 위해서는 효과적인 머신러닝 모델이 필요합니다. AWS에서 무료로 제공하는 DLR 이미지 분류 모델 스토어를 이용하면, Greengrass가 설치된 디바이스에서 ResNet-50 모델을 사용할 수 있습니다. DLR (Deep Learning Runtime) 이미지 분류 모델은 Built-in Component인 variant.DLR.ImageClassification.ModelStore로 제공되며, 이것을 IoT device에서 활용하기 위해서는 또 다른 Built-in Component인 aws.greengrass.DLRImageClassification을 이용하여 이미지 분류를 요청하거나, 별도의 Custom Component를 구현하여야 합니다.

Built-in Component를 이용한 이미지 분류 및 문제점

Greengrass에서 Built-in으로 제공하는 public component인 aws.greengrass.DLRImageClassification으로 IoT 디바이스에서 이미지 분류를 손쉽게 구현할 수 있습니다. aws.greengrass.DLRImageClassification을 설치하고, 활용하는 방법에 대한 상세한 설명은 [Built-in Component를 이용하여 IoT 디바이스에서 이미지 분류하기]를 참조합니다.

aws.greengrass.DLRImageClassification에서는 아래와 같이 이미지 분류를 처리하는 주기(InferenceInterval), 이미지 로드하는 폴더(ImageDirectory), 이미지 이름(ImageName)을 변경할 수 있지만, Public component이므로 사용자의 목적에 따라 자유롭게 수정하여 사용할 수 없습니다. 또한, RESTful API처럼 Greengrass의 다른 Component에서 aws.greengrass.DLRImageClassification에 직접 요청을 보내고 결과를 얻는 방식이 아니라, IoT Core를 통해 결과를 확인하여야 합니다. 따라서, off-line 같은 네트워크 상황도 고려하여 IoT 디바이스에서 이미지 분류를 구현하고자 한다면 Custom Component에서 직접 variant.DLR.ImageClassification.ModelStore의 DLR model을 로딩하여 활용할 수 있어야 합니다.

{
  "InferenceInterval": "60",
  "ImageDirectory": "/tmp/images/",
  "ImageName": "image.jpg"
}

아래에서는 Custom component를 이용하여 variant.DLR.ImageClassification.ModelStore의 DLR model을 로드하고, 이미지 분류(Image Classification)를 쉽고 편리하게 수행하는것을 설명합니다.

Custom Component를 이용한 이미지 분류

Edge에 있는 IoT 디바이스에서 이미지 분류를 수행하는 과정을 아래 Architecture에서 설명하고 있습니다. AWS Cloud의 Greengrass를 이용하여 디바이스에 Component를 배포하거나 관리할 수 있습니다. IoT 디바이스에는 이미지 분류를 요청하는 component인 Requester(com.custom.requester), 추론을 수행하는 component인 Classifier(com.custom.ImageClassifier), DLR model(variant.DLR.ImageClassification.ModelStore)이 있습니다. 이미지 분류 추론을 수행하는 Inference은 아래와 같이 Classifier와 DLR model로 구성됩니다. Requester와 Classifier는 Nucleus를 통해 IPC 통신을 수행합니다. Requester는 "local/inference" topic으로 추론을 요청하고, "local/result" topic으로 결과를 얻습니다. 마찬가지로 Classifier는 "local/inference" topic으로 추론 요청을 확인하여, DLR model로 추론을 수행하고, "local/result" topic으로 결과를 Requester에 전달합니다.

Custom component를 개발하는 과정은 Local 환경에서 이미지 분류 추론 개발하기Script를 이용해 Component를 설치하여 이미지 분류 추론 개발하기에서 상세하게 설명하고 있습니다.

이미지 분류를 수행하는 과정은 아래와 같습니다.

  1. Requester는 Greengrass의 local component로서 자신이 관리하는 이미지의 Path 및 파일명을 가지고 이미지 분류를 요청합니다.

  2. Nucleus의 local PubSub Service를 이용하여, Requester는 "local/inference" topic으로 publish를 수행합니다.

  3. Classifier는 "local/inference"를 Subscribe하고 있다가, Requester가 보낸 이미지 분류 추론 요청을 받습니다.

  4. Classifier는 variant.DLR.ImageClassification.ModelStore의 DLR model을 이용하여 추론(inference)을 수행합니다.

  5. DLR model은 이미지 분류 추론 결과를 Classifier에 리턴합니다.

  6. Classifier는 "local/result" topic으로 결과를 publish합니다.

  7. Requester는 "local/result" topic으로 부터 이미지 분류 결과를 확인합니다.

Requester (com.custom.requester)

com.custom.requesterGreengrass IPC Client V2를 이용해 아래와 같이 com.custom.ImageClassifier로 추론 요청을 수행합니다.

요청은 JSON 포맷으로된 메시지를 바이너리로 변환하여 publish 합니다.

from awsiot.greengrasscoreipc.clientv2 import GreengrassCoreIPCClientV2

ipc_client = GreengrassCoreIPCClientV2()

message = {
  'image_dir': BASE_DIR,
  'fname': 'pelican.jpeg'
}
publish_binary_message_to_topic(ipc_client, topic,  json.dumps(message))

def publish_binary_message_to_topic(ipc_client, topic, message):
    binary_message = BinaryMessage(message=bytes(message, 'utf-8'))
    publish_message = PublishMessage(binary_message=binary_message)
    ipc_client.publish_to_topic(topic=topic, publish_message=publish_message)

com.custom.ImageClassifier를 통해 추론을 수행한 결과는 "local/result" topic을 이용하여 아래처럼 확인합니다.

_, operation = ipc_client.subscribe_to_topic(topic="local/result", on_stream_event=on_stream_event,
  on_stream_error=on_stream_error, on_stream_closed=on_stream_closed)
            
def on_stream_event(event: SubscriptionResponseMessage) -> None:
    try:
        message = str(event.binary_message.message, 'utf-8')
        print('result: %s' % (message))

    except:
        traceback.print_exc()

Pub/Sub IPC를 이용해 edge에 설치된 component들 사이에 메시지를 교환하기 위해서는 recipe을 아래와 같이 설정합니다. aws.greengrass.ipc.pubsub은 디바이스의 local component들 사이에 메시지를 교환하기 위한 IPC 서비스 식별자입니다.

"ComponentConfiguration": {
    "DefaultConfiguration": {
      "accessControl": {
        "aws.greengrass.ipc.pubsub": {
          "com.custom.requester:pubsub:1": {
            "policyDescription": "Allows access to publish/subscribe to the topics.",
            "operations": [
              "aws.greengrass#PublishToTopic",
              "aws.greengrass#SubscribeToTopic"                  
            ],
            "resources": [
              "local/inference",
              "local/result"
            ]
          }
        }
      }
    }
  }

Classifier (com.custom.ImageClassifier)

interface.py에서는 "local/inference" topic으로 메시지를 받으면, classifier()로 요청합니다.

from classifier import classifier  

ipc_client = GreengrassCoreIPCClientV2()
topic = 'local/inference'

def on_stream_event(event: SubscriptionResponseMessage) -> None:
    try:
        message = str(event.binary_message.message, 'utf-8')
        event_topic = event.binary_message.context.topic
        logger.info("Received new message on topic %s: %s", topic, message)      
        
        # Inference
        if event_topic == topic:
            json_data = json.loads(message) # json decoding        
            result = classifier(json_data)       

            publish_binary_message_to_topic(ipc_client, "local/result",  result)            

classifier.py는 아래와 같이 이미지를 로드하여 실제 추론을 수행하는 "inference"의 handler()를 호출하고 결과가 오면, 가장 확률이 높은 결과를 리턴합니다.

from inference import handler  

def classifier(data):
    image_data = load_image(os.path.join(data['image_dir'], data['fname']))
    
    event = {
        'body': image_data
    }

    try:
        result = handler(event,"")          
        
        return result['body'][0]['Label']

inference.py에서는 기학습된 모델을 로딩하고 전달받은 이미지 데이터를 resize 한 후 추론을 수행합니다. 아래에서처럼 load_model()은 "variant.DLR.ImageClassification.ModelStore"로 DLR model을 로딩하고, handler()는 event을 받아서 body를 추출한후 모델에 맞는 크기로 변환합니다. 마지막으로 predict_from_image()은 로딩한 모델로 추론을 수행하고 결과를 리턴합니다.

SCORE_THRESHOLD = 0.3
MAX_NO_OF_RESULTS = 5
SHAPE = (224, 224)

MODEL_DIR = '/greengrass/v2/packages/artifacts-unarchived/variant.DLR.ImageClassification.ModelStore/2.1.9/DLR-resnet50-x86_64-cpu-ImageClassification'

def load_model(model_dir):
    model = DLRModel(model_dir, dev_type='cpu', use_default_dlr=False)
    return model

model = load_model(MODEL_DIR)

def handler(event, context):
    image_data = event['body']
    cvimage = resize(image_data, SHAPE)

    if cvimage is not None:
        result = predict_from_image(model, cvimage)
        return {
            'statusCode': 200,
            'body': result
        }  

def predict_from_image(model, image_data):
    result = []
    try:
        model_output = model.run(image_data)

        probabilities = model_output[0][0]
        sort_classes_by_probability = argsort(probabilities)[::-1]
        for i in sort_classes_by_probability[: MAX_NO_OF_RESULTS]:
            if probabilities[i] >= SCORE_THRESHOLD:
                result.append({"Label": str(synset[i]), "Score": str(probabilities[i])})
        
        return result

recipes와 같이 libgl1, opencv-python등과 같은 라이브러리를 설치하고, interface.py를 실행합니다.

"Manifests": [{        
  "Lifecycle": {
    "Install": {
        "RequiresPrivilege": "true",
          "Script": "apt-get install libgl1 -y\n pip3 install --upgrade pip \n pip3 install scikit-build wheel opencv-python==4.6.0.66 dlr\n python -m pip install dlr\n pip3 install awsiotsdk"
    },
  "Run": {
    "RequiresPrivilege": "true",
    "Script": "python3 -u {artifacts:path}/interface.py"
  }
}

또한, DLR model을 가진 Built-in component를 설치하기 위하여, ComponentDependencies에 아래와 같이 "variant.DLR.ImageClassification.ModelStore"를 기술합니다.

      "ComponentDependencies": {
        "variant.DLR.ImageClassification.ModelStore": {
          "VersionRequirement": ">=2.1.0 <2.2.0",
          "DependencyType": "HARD"
        }
      }

AWS CDK를 이용한 Component 배포 준비

AWS CDK를 이용하여 Component를 배포합니다.

cdk-greengrass-stack.ts에서는 s3 bucket을 생성하고, artifact를 복사한후 deployment를 이용해 배포합니다.

const s3Bucket = new s3.Bucket(this, "gg-depolyment-storage",{
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
  removalPolicy: cdk.RemovalPolicy.DESTROY,
  autoDeleteObjects: true,
  publicReadAccess: false,
});

// copy artifact into s3 bucket
new s3Deploy.BucketDeployment(this, "UploadArtifact", {
  sources: [s3Deploy.Source.asset("../src")],
  destinationBucket: s3Bucket,
}); 

new greengrassv2.CfnDeployment(this, 'MyCfnDeployment', {
  targetArn: `arn:aws:iot:ap-northeast-2:`+accountId+`:thing/`+deviceName,    
  components: {
    "com.custom.requester": {
      componentVersion: version_requester
    },
    "com.custom.ImageClassifier": {
      componentVersion: version_ImageClassifier
    },
    "aws.greengrass.Cli": {
      componentVersion: "2.9.2"
    }
  }

배포하기

[CDK로 Cloud9에서 이미지 분류 추론하기]에서는 DLR model을 이용하여 Component에서 추론(Inference)를 수행하는것을 설명합니다.

Greengrass Commands와 Memo

유용한 Greengrass 명령어와 중요한 메모들를 정리하였습니다.

Reference

DLR image classification model store

Github - DLR

Image Classification via Greengrass

DLR: Compact Runtime for Machine Learning Models

End-to-end AIoT w/ SageMaker and Greengrass 2.0 on NVIDIA Jetson Nano

image-classification-via-iot-greengrass's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.