S3 へのデータアップロードをトリガーにデータを推論し、推論結果を S3 に保存する AWS Lambda 関数の作成

# はじめに
今回は S3 へのデータアップロードをトリガーに、アップロードされたデータを推論し、結果を S3 に保存する関数を作成しました。
その内容を記載します。

# 前提
- SageMakerでデプロイできるモデルがある。  
※今回作成した Lambda 関数を使う際には、事前にモデルをデプロイしておく必要があります。  
※私は画像検出のモデルを使用します。

# 関数の作成/設定
- まず、AWS のコンソールから Lambda と検索し、 Lambda を選択します。
<img width="558" alt="001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/61a689c0-3c0e-d5da-67fb-804aecb9210f.png">

- 左の一覧から関数を選択し、右上の関数の作成を選択します。
<img width="910" alt="002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/bce8ab4a-affb-d799-4627-71248ab21aee.png">

- 一から作成にチェックをつけ下へスクロール。
<img width="866" alt="003.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/4d2cf418-2789-ccd1-3341-c948089962b6.png">

- 関数名に任意の名前を入力し、ランタイムは Python3.7 を選択してください。
<img width="855" alt="004.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/ece52463-78b4-f51c-543a-712983bd4ae7.png">

- 実行ロールは基本的な Lambda アクセス権限で新しいロールを作成を選択し、関数の作成を選択します。
<img width="848" alt="005.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/f7b23e0f-92aa-bd9c-d9fb-d7e722244f2c.png">

- 作成が完了すると、次のような画面が表示されます。
<img width="882" alt="006.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/f06aef53-ab8c-7d14-b7bc-d5d1b3e2f96c.png">


- 少し下にスクロールし左のトリガーの追加から S3 を選択してください。
<img width="845" alt="007.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/89439f21-ac9b-042f-1a76-993cac7fe6bd.png">

- 選択したら、更に下にスクロールし各種設定を行います。
  - バケット名 : データをアップロードするバケットを選択します。
  - イベントタイプ : PUT を選択します。
  - プレフィックス : データのアップロードを検知するフォルダを指定します。
  - サフィックス : 検知するデータの末尾の形式を指定します。  
  私は画像検出のモデルを使用するので、.jpgと入力しています。  
  - トリガーの有効化 : チェックを付けておきます。
<img width="858" alt="008.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/d53e2bb0-730b-fc1b-2f98-409b21f7b779.png">

- 下にスクロールして、右下の追加を選択します。
<img width="866" alt="009.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/a1c5bb7b-d11a-dcf0-13d0-eddb83bed7d6.png">


- 追加したら、右上の保存ボタンを選択して、設定を保存します。
<img width="875" alt="010.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/59220212-f5c5-fd01-6a72-0cf4fe032917.png">

- 保存したら、少し上にスクロールして、赤枠で囲まれた部分を選択し、下にスクロール。
<img width="884" alt="011.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/927caac2-2546-2c02-89c3-ebe2207b9362.png">


- すると、以下のような画面がでてきます。
赤枠内に、実際のプログラムを記述します。
<img width="822" alt="012.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/7aa2dee0-374e-4acd-17f8-6c8c5751fa1e.png">

- 今回私が作成したプログラムは以下の通りです。

```Python
import json
import boto3

def lambda_handler(event, context):
    s3 = boto3.client("s3")
    sagemaker = boto3.client("sagemaker-runtime", region_name="ap-northeast-1")
    
    #S3にアップロードされた画像データ情報を取得
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']
    input_data = s3.get_object(Bucket=bucket, Key=key)
    
    #画像データをバイナリデータに変換
    body = input_data['Body'].read()
    b = bytearray(body)
    
    #推論
    endpoint_response = sagemaker.invoke_endpoint(
        #エンドポイント名を設定
        EndpointName='auto-endpoint',
        Body=b,
        ContentType='image/jpeg'
    )
    results = endpoint_response['Body'].read()
    print(endpoint_response)
    print(results)
    
    
    split_s3_path = key.split('/')
    for i in split_s3_path:
        if 'jpg' in i:
            full_file_name = i
            file_name = i.split('.')[0]
            
    #output_keyに推論結果の保存パスを設定
    output_key = 'dog-face/lambda/推論結果/'+file_name+'.json'
    #S3に推論結果をjsonファイルで保存
    s3.put_object(Body=results,Bucket=bucket,Key=output_key)
    
    #copy_keyに推論後画像を格納するパスを設定
    copy_key = 'dog-face/lambda/推論済み画像/'+full_file_name
    #S3のアップロードされたデータを別フォルダにコピーして元データは削除
    s3.copy_object(Bucket=bucket,Key=copy_key,CopySource={'Bucket': bucket, 'Key': key})
    s3.delete_object(Bucket=bucket,Key=key)
```

プログラムは下記の流れになっています。
1. S3にアップロードされたデータを推論。
2. 推論結果を保存。
3. アップロードされたデータを、別フォルダにコピーし、アップロードされたデータを削除。  
 
- プログラムを書き終えたら、もう一度右上の保存を選択します。  
<img width="880" alt="013.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/df4e01f0-5abe-b1d8-3c28-0b9afe60f6ba.png">



#### 実行ロール の設定
さきほど何もいじらずに作成した、実行ロールに、S3とSageMakerのフルアクセスをアタッチします。
※今回はテストのため、フルアクセスをアタッチしますが、本番で利用する際は適切なIAMロールを作成してください。

- IAMを開き、左の欄からロールを選択し、検索欄にLambda関数を作成した時に付けた名前を入力してください。
<img width="828" alt="015.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/ff856b58-3efb-9253-6d07-4573b0dcc44a.png">

- 入力し、出てきたロールを選択します。
<img width="925" alt="016.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/e71b0cef-b05b-233c-37e3-a61f84405b87.png">


- ポリシーのアタッチを選択します。  
<img width="937" alt="017.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/74206177-65f7-a035-9443-fe23716f3484.png">


- 検索バーに S3 と入力し、S3 のフルアクセスにチェックを付けて、ポリシーのアタッチを選択します。
<img width="862" alt="018.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/2fe45da9-bc1a-7c5d-eb4e-f31ef101b7c6.png">


- 同様に、検索バーに SageMaker と入力し、SageMaker のフルアクセスにチェックを付けて、ポリシーのアタッチを選択します。
<img width="899" alt="019.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/a4a2adb9-32e0-4ef3-1752-7f12773a71cf.png">


- すると、2つのポリシーがアタッチされたことが確認できます。
<img width="913" alt="020.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/6f6d617a-2a4a-9168-cd0a-6cb0bf8d0b61.png">



# 実際にS3にデータをアップロードし、Lambda 関数を起動してみる。
この手順に入る前に使用するモデルをデプロイしてください。

- Lambda関数作成時に設定した画像アップロードフォルダに画像をアップロードします。
<img width="798" alt="021.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/943e4035-c81d-c74f-452f-5e667e0a92ba.png">
<img width="875" alt="022.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/89c4b4c0-b2a0-6f77-8212-4e0936932d68.png">


- アップロードしたら、作成したLambda関数を開いて、モニタリングタブを選択し、CloudWatch のログ表示を選択します。
<img width="866" alt="023.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/e0d2753b-10f7-0e71-faf8-dd3382393b6a.png">


- するとこのような形で、Lambda 関数の起動ごとにログが記録されています。
ログを開くと Lambda 関数が正常に動作しているか確認できます。
<img width="937" alt="024.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/b717b4ee-5a46-e65f-241f-9a4713db5cb1.png">


- 正常に動作している場合、S3に、推論結果が保存され、アップロードしたデータは別フォルダに格納され、削除されます。
<img width="881" alt="025.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/27a0f357-d641-a105-a364-f2be5c1a8b87.png">
<img width="865" alt="026.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/d6bca1cd-065d-d418-5577-f88e27e54eaf.png">
<img width="884" alt="027.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/4f9822ae-e57a-d173-f72b-eb71c153e89a.png">

- ちなみに推論結果を可視化した画像はこちらになります。
![028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390035/4d2b6b40-4256-8cda-bc12-68fa0502484a.png)





# 最後に
今回は、S3へのデータアップロードをトリガーに、推論を行いました。
トリガーを変更することで、他にもいろいろな処理ができますので、ぜひ試してみてください。
![undefined]()