search
LoginSignup
6
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

AWS Lambda で画像AIを動かす

はじめに

AWS Lambda で Pytorch の画像AIを動かしてみました

SAM CLI で簡単に構築できるので、ちょっとした AIサービスなら低コストで実現できそうです

実行環境

  • macOS Big Sur 11.4
  • SAM CLI 1.24.1
  • Docker 20.10.6

参考にしたもの

2021/2/12 のブログです

こちらは自然言語処理のAIを動かしています

AWS Lambda とは

AWS Lambda はサーバレスコンピューティングサービスです

開発したコードをアップロードすると、呼び出されたときだけマシンが起動し、コードを実行して、自動的に終了してくれます

何が嬉しいかというと

  • サーバーがないので、サーバー構築や保守が必要ない
  • マシンが起動した時間とマシンに割り当てたメモリで課金されるため、使っていない間はコストがかからない
  • 大量のリクエストを受けると必要な数だけマシンが立ち上がる
  • コードをアップロードし直すだけで無停止のシステム更新が可能

つまり、低コストで高可用性のシステムが構築できるわけです

ただし、万能ではありません

  • マシン起動時に時間がかかる

    遅延してもいいようにするか、常時1台はマシンが起動しているようにする

  • 大量アクセスで思わぬ高コストになることもあり得る

    認証、アクセス制限、API Gatewayによる使用量制限などを入れる

SAM とは

SAM = サーバーレスアプリケーションモデル

Lambda や API Gateway など、サーバレスアーキテクチャの構築を支援してくれます

インストール方法はこちら

昔、むかーしむかし、 Lambda の開発には Apex というツールを使っていました

Lambda のデプロイのややこしいところを上手くラッピングしてくれていたからです

しかし、2018年に開発が停止し、リポジトリーもアーカイブされ、インストールするためのリリースも消されました

今では Lambda 以外もまとめて Terraform 管理にしています

なので、実のところ SAM を触るのは初めてでした

Lambda の新機能

少し寄り道して、 Lambda で AI を動かせたのが嬉しかった理由を書いておきます

かつて、 AWS Lambda はコードを Web コンソールから直接入力するか、 Zip に固めてアップロードするしかありませんでした

必要なパッケージがある場合、パッケージも Zip に含める必要があります

しかし、 Pytorch は非常に大きいパッケージで、 Zip によるアップロードの上限を超えます

Zip 圧縮して 50MB が上限では、とても使えません

昔も画像AIを Lambda で動かしたいと思ったことがありましたが、この容量の問題で断念していました

ところが、 2020年12月、とても素晴らしい新機能が追加されたのです

なんと、コンテナイメージが動かせるようになったのです!

もう何でもありじゃないか、、、

自分で好きなパッケージを好き放題入れて動かせます!

AIでも何でもバッチコイです!

プロジェクト作成

何はともあれ、 SAM を動かしてみましょう

まず、以下のコマンドで対話しながらプロジェクトの大枠を作ります

sam init

実行すると、以下のように表示されます

Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location
Choice:

今回は 1 - AWS Quick Start Templates を選択します

続いて、どのようにコードをアップロードするか聞かれます

What package type would you like to use?
    1 - Zip (artifact is a zip uploaded to S3)
    2 - Image (artifact is an image uploaded to an ECR image repository)
Package type:

当然、 2 - Image を選びます

続いてベースになるイメージの選択です

Which base image would you like to use?
    1 - amazon/nodejs14.x-base
    2 - amazon/nodejs12.x-base
    3 - amazon/nodejs10.x-base
    4 - amazon/python3.8-base
    5 - amazon/python3.7-base
    6 - amazon/python3.6-base
    7 - amazon/python2.7-base
    8 - amazon/ruby2.7-base
    9 - amazon/ruby2.5-base
    10 - amazon/go1.x-base
    11 - amazon/java11-base
    12 - amazon/java8.al2-base
    13 - amazon/java8-base
    14 - amazon/dotnet5.0-base
    15 - amazon/dotnetcore3.1-base
    16 - amazon/dotnetcore2.1-base
Base image:

Python で動かしたいので、 4 - amazon/python3.8-base を選びます

プロジェクト名は適当に入れてください

Project name [sam-app]:

今回は py-sam-app とします

次に、テンプレートを選びます

Cloning from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
    1 - Hello World Lambda Image Example
    2 - PyTorch Machine Learning Inference API
    3 - Scikit-learn Machine Learning Inference API
    4 - Tensorflow Machine Learning Inference API
    5 - XGBoost Machine Learning Inference API
Template selection:

2 - PyTorch Machine Learning Inference API を選べば、それだけで AI システムの大枠が作られます

もちろん、用途に応じて Tensorflow 等を選んでも OK です

以下の表示が出れば、必要なコードは揃っています

    -----------------------
    Generating application:
    -----------------------
    Name: py-sam-app
    Base Image: amazon/python3.8-base
    Dependency Manager: pip
    Output Directory: .

    Next steps can be found in the README file at ./py-sam-app/README.md

ビルド

Lambda をコンテナで動かすので、当然コンテナをビルドする必要があります

あらかじめ、自分の IAM ユーザーに以下のサービスを自由に使える権限があることを確認してください

なければ付与してください

  • Lambda
  • API Gateway
  • ECR
  • IAM
  • CloudFormation

まず、 ECR にコンテナ用のリポジトリーを作ります

aws ecr create-repository \
  --repository-name py-sam-app \
  --image-scanning-configuration \
  scanOnPush=true

以下のようなレスポンスが返ってきます

{
    "repository": {
        "repositoryArn": "arn:aws:ecr:<REGION>:<AWS_ACCOUNT_ID>:repository/py-sam-app",
        "registryId": "<AWS_ACCOUNT_ID>",
        "repositoryName": "py-sam-app",
        "repositoryUri": "<AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/py-sam-app",
        "createdAt": 1625620019.0,
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": true
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

続いて、リポジトリーにプッシュするために ECR にログインしておきます

<repositoryUri> は上記 ECR リポジトリー作成のレスポンスに入っています

aws ecr get-login-password | docker login \
  --username AWS \
  --password-stdin <repositoryUri>

Login Succeeded とレスポンスがあれば OK です

続いて、作成したプロジェクトのディレクトリーに移動し、ビルドします

cd py-sam-app \
  && sam build

内部で docker build が実行され、最終的に以下のように結果が表示されます

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

では、ローカルで一度動かしてみましょう

sam local invoke InferenceFunction --event events/event.json

以下のように結果が返ってくれば、 AI モデルが動かせています

Invoking Container created from inferencefunction:python3.8-v1
Building image.........
Skip pulling image and use local one: inferencefunction:rapid-1.24.1.

START RequestId: f034abb7-2e1f-4c44-a754-ee67be8fb419 Version: $LATEST
END RequestId: f034abb7-2e1f-4c44-a754-ee67be8fb419
REPORT RequestId: f034abb7-2e1f-4c44-a754-ee67be8fb419  Init Duration: 2.00 ms  Duration: 1577.01 ms Billed Duration: 1600 ms        Memory Size: 5000 MB    Max Memory Used: 5000 MB
{"statusCode": 200, "body": "{\"predicted_label\": 3}"}%

少し解説します

プロジェクトディレクトリー内の template.yml を開くと、以下のように記述されています

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 50
    MemorySize: 5000
  Api:
    BinaryMediaTypes:
      - image/png
      - image/jpg
      - image/jpeg

Resources:
  InferenceFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      PackageType: Image
      Events:
        Inference:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /classify_digit
            Method: post
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./app
      DockerTag: python3.8-v1
...

ここで、どういう API で、どういう関数で、といったことが定義されているわけです

この中の InferenceFunction を先ほど指定して実行しました

引数に指定した events/event.json を見てみましょう

色々と書かれていますが、一番下に関数に渡す値が定義されています

...
  },
  "body": "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAAAAABXZoBIAAABQGlDQ1BJQ0..."
}

関数の中身を少し見てみます

関数は app/app.py に定義されています

...
def lambda_handler(event, context):
    image_bytes = event['body'].encode('utf-8')
    image = Image.open(BytesIO(base64.b64decode(image_bytes))).convert(mode='L')
    image = image.resize((28, 28))

    probabilities = model.forward(image_transforms(np.array(image)).reshape(-1, 1, 28, 28))
    label = torch.argmax(probabilities).item()
...

event から body を取り出し、 BASE64 デコードしていますね

つまり、入力となるデータは BASE64 エンコードされた画像データということがわかります

試しに以下のサイトで body の中の文字列をデコードしてみましょう

デコード結果は数字の 3 の手書き文字でした

image.png

MNIST dataset のようですね

つまり、このアプリは手書き数字の 0 から 9 を判別する画像 AI を動かしているわけです

デプロイ

では、ビルドしたものをクラウド上にデプロイしましょう

init のときと同じく、対話型で進めます

sam deploy --guided

まず、スタック名を入力します

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: 

SAM は内部で AWS CloudFormation を動かしています

スタックとは、一連の関連するリソースをまとめたものです

今回で言うと、 Lambda 関数や API Gateway 、 IAM ロールなどです

これらをスタックとしてまとめて実行するため、名前をつけます

今回はここも py-sam-app としておきます

続いてリージョンを指定します(デフォルトで問題ありません)

        AWS Region [ap-northeast-1]:

次に ECR のリポジトリーを聞かれるので、作ったリポジトリーのURL(前出の repositoryUri )を入力します

        Image Repository for InferenceFunction: 

デプロイ前に確認するかと聞かれますが、今回は n で良いでしょう

        #Shows you resources changes to be deployed and require a 'y' to initiate deploy
        Confirm changes before deploy [y/N]: 

SAM が IAM ロールを作るのを許可するかと聞かれるので、もちろん y と答えます

        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: 

API に認証がかかってないけど大丈夫?と心配してくれます

業務で使う場合はもちろん認証が必要ですが、今回はお試しなので y です

        InferenceFunction may not have authorization defined, Is this okay? [y/N]: 

設定を保存するか聞かれます

保存しておけば、次回以降、今回の内容がデフォルト値になるため、 y です

        Save arguments to configuration file [Y/n]: 

設定ファイル名はデフォルトのままで良いでしょう

        SAM configuration file [samconfig.toml]: 

環境もデフォルトにします

        SAM configuration environment [default]: 

これで ECR へのプッシュが始まり、続けて各リソースが作られていきます

最後に成功のメッセージが表示されます

Successfully created/updated stack - py-sam-app in ap-northeast-1

実行

上記成功メッセージの上に、 API Gateway の作成結果が表示されています

Key                 InferenceApi                                                                
Description         API Gateway endpoint URL for Prod stage for Inference function              
Value               https://xxx.execute-api.ap-                                          
northeast-1.amazonaws.com/Prod/classify_digit/

この Value のところが作成された API のエンドポイントなので、ここに向けて BASE64 の文字列(event/event.json の body の値)を投げてみましょう

curl -XPOST https://xxx.execute-api.ap-northeast-1.amazonaws.com/Prod/classify_digit/ \
  -d "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAAAAABXZoBIAAABQGlDQ1BJQ0..."

最初はすごく時間が掛かって、もしかしたらタイムアウトするかもしれません

{"message": "Endpoint request timed out"}% 

これは、 初回呼び出し時にマシンを起動しているのと、モデルの読み込みを行っているためです

本番ではタイムアウト時間を調節するなどしてください

もう一回呼んでみましょう

すると、正しい結果が返ってきます

{"predicted_label": 3}% 

ちゃんと 3 が返ってきましたね!

注意

お試しで作った関数、API Gateway、ECRリポジトリー、IAMユーザーは削除しておきましょう

API を攻撃されたら高額請求が来るかも

最後に

あとは、 Dockerfile や app.py を編集して、自分の使いたいモデルを組み込めば何でも動かせます

私は物体検出を動かしてみましたが、無事動作できました

ただし、 template.ymlMemorySize に書いてあるように、メモリを 5,000 MB 割り当てています

実際、物体検出を動かした時は 3,000 MB 以上使われていました

これは結構高いので、多数アクセスがある場合、結局割高になりそうです

GPU を積んでいないため、処理に時間がかかる点も考慮しましょう

アクセスが多く、速度を求められるような場合、やはり Lambda は向いていないでしょう

とはいえ、これを活かせる場面もあると思うので、今後の選択肢に入れておきます

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6
Help us understand the problem. What are the problem?