こんにちは。SCSK 産業事業グループの樋口です。
本ハンズオンでは、有名人の画像をアップロードすると名前をレスポンスしてくれる、簡単な API サービスを AWS上にて構築します。
本ハンズオンを通して、AWSのサーバレスアーキテクチャについての理解が深まれば幸いです。
本記事で作成するもの:有名人識別サービス
- 「ファイルを選択」ボタンから、有名人の画像(例えば、日本が世界に誇る有名俳優「渡辺謙」さんの画像)を選択します。
- ファイルを選択後、「送信」ボタンを押すと…
“He/She is Ken Watanabe with 100% confidence.”
と表示されます!
はじめに
概要説明
- Amazon Rekognition(画像解析AI) を用いて、有名人識別APIを作成します。
- システム構成図は以下のようになります。
- 明示的なサーバが存在しない「サーバレスアーキテクチャ」のため構築が簡単です。
- また運用管理の手間やコストも軽減します。
APIの動作説明
- クライアントPCから API Gateway 経由で有名人の画像をアップロードします。
- 画像ファイルを受信すると、Lambda 関数が起動します。
- Lambda 関数内で Amazon Rekognition の recognize_celebrities 機能に画像ファイルを送り、画像内の有名人を検出します。
- 検出した有名人の情報を出力用に整形して、API Gateway を通して返します。
- 呼び出し元のブラウザ上で、識別結果が表示されます。
AWS サービスについて
使用する AWS サービスの紹介
AWS 用語解説:「サーバレス」とは
- 利用者がサーバを意識する必要のないサービスやアーキテクチャのことです。
- 注意:サーバが全く存在しないわけではありません。サーバはAWSが管理しています。
- プログラムコードを「置くだけ」で動作します。そのため運用管理が楽になります。
AWS 用語解説:「マネージドサービス」とは
- AWSが運用管理(の一部)を担ってくれるサービスのことです。
- 担ってくれる運用管理の内容や範囲はサービスによって多様ですが、「バックアップを定期的にとる」などだけではなく、「アクセスが増えたら自動でスケールする」なども提供していることが多いです。
- 運用管理の範囲が広く、利用者の手間がほとんどかからない場合は、「フルマネージドサービス」と言われることが多いです。
- 「マネージド」であっても、常に「サーバレス」とは限りません。
開発手順
AWSへのサインイン
- AWSマネジメントコンソールへアクセスし、IAM ユーザー情報でサインインをしてください。
https://console.aws.amazon.com/
- サインインの補足情報
- 今回のハンズオンでは、ハンズオンを実施するIAMユーザーに”AdministratorAccess”のIAMポリシーが付与されていることを前提にして進めています。“AdministratorAccess” の権限が付与されていなかった場合、以下の手順の中でリソースが作れない等のエラーが発生する可能性があります。
- その場合は、適切なポリシーをアタッチした上で再度実行をしてください。
- なお、以下のサイトに記載されていたIAMの設定内容は大変参考になりました。
- 社外の開発メンバーをAWSアカウントに入れるときのIAM設定を考えている – kmiya_bbmのブログ
※ ハンズオンの手順がエラーなく実行できることを確認済みです。
- 社外の開発メンバーをAWSアカウントに入れるときのIAM設定を考えている – kmiya_bbmのブログ
リージョンと言語の確認
[補足] リージョンの変更方法
[補足] 言語の変更方法
Lambda 関数の作成
Lambda 関数の新規作成
- 最初にLambda 関数を作成し、基本的な使い方を学習しましょう。
- マネジメントコンソールの画面にて、検索窓に「Lambda」と入力し、表示された「Lambda」を選択してください。
- Lambdaの画面が開きましたら、「関数の作成」ボタンを押してください。
- 関数の作成画面にて、以下を選択・入力し、最後に画面右下の「関数の作成」ボタンを押下します。
- Lambda関数が作成されます。
- 今から動くコードを記述していきます。
- 下にスクロールし、「コード」のタブを表示します。
lambda_function.py
をダブルクリックし、エディタを開きます。
Lambda 関数の使い方説明
- まずは、シンプルなPythonコードを書いて Lambda の動きを学習しましょう。
- 例えば、文字列を結合して出力する、以下のコードを記述します。
- 変数に文字列を設定し、結合をしたものを return する内容となっています。
- 具体的には、’Pen’ + ’Pineapple’ + ’Apple’ + ’Pen’ → ‘PenPineappleApplePen’ になります。
def lambda_handler(event, context):
a = 'Pen'
b = 'Pineapple'
c = 'Apple'
x = a + b + c + a
return {
'statusCode': 200,
'body': x
}
- 入力後、「Deploy」 ボタンを押します。
- Deploy が完了した後、上部の「Test」を押下します。
- 「テストイベントの設定」画面が表示されます。以下の設定をした後、「作成」ボタンを押下します。
- 再度「Test」ボタンを押下します。
- Lambda 関数がテスト実行されました。
デバッグ方法(ログの確認方法)
- Lambda 実行時の詳細なログは、”CloudWatch Logs” で確認できます。
- 「ログストリーム」画面にて「20xx/xx/xx[$LATEST]xxxxxx」を選択するとログが確認できます。デバック時にご活用ください。
- Logger などを設定すると、ここにログが出力されるようになります。
Lambda 関数の編集
- ここから、Lambda関数を、画像解析AI (Rekognition)と紐づける作業をしていきます。
- Lambda関数からRekognitionを呼び出すには、適切な権限が必要です。
- 具体的には、Lambdaに付与されているIAMロールに、Rekognitionへのアクセスを許可するIAMポリシーをアタッチする必要があります。
- まずは、権限付与から行っていきましょう。
IAM ロール(権限)の変更
- Lambda画面で「設定」タブ -> 「一般設定」項目 -> 「編集」を選択します。
- 「基本設定を編集」画面の下部まで行き、「IAMコンソールでxxxロールを表示します。」をクリックします。
- 別タブで、IAMロールの画面が開きます。
- 「ポリシーをアタッチします」ボタンを押下します。
- 検索窓に「Rekognition」と入力し、「AmazonRekognitionReadOnlyAccess」ポリシーにチェックを入れて、「ポリシーのアタッチ」ボタンを押下します。
- IAMのコンソール画面で、「AmazonRekognitionReadOnlyAccess」がアタッチされていることを確認します。
- これで、Lambda に Rekognition を呼び出せる権限を付与できました。
- Lambda の「基本設定を編集」画面に戻ります。
- タイムアウトを「10秒」に変更します。
- 上記設定後、「保存」ボタンを押下します。
Lambda 関数の更新
貼り付けるソースコード
import boto3
import base64
import io
import cgi
import logging
import traceback
# logger設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# rekognition インスタンスの作成
rekognition = boto3.client('rekognition')
# lambda_handler 関数の定義(メインロジック)
def lambda_handler(event, context):
logger.info(f'Received event = {event}')
# HTMLフォームからアップロードされた部分はBase64形式でエンコードされた状態で受信するため、bytes型にデコードする(正確にはbytes-like object)
received_body = base64.b64decode(event['body-json'])
# cgi.FieldStorageでHTMLフォームを解析できるように、BytesIO オブジェクトを生成する
body_bytes = io.BytesIO(received_body)
# cgi.FieldStorageクラスを用いて、フォームの内容を解析する
environ = {'REQUEST_METHOD': 'POST'}
headers = {'content-type': event['params']['header']['content-type']}
form = cgi.FieldStorage(fp=body_bytes, environ=environ, headers=headers)
# 画像ファイルをbytes型で取得('uploadfile'は送信時に設定した当該ファイルのnameの値)
image = form.getvalue('uploadfile')
# 取得した画像ファイルを、Rekognitionのrecognize_celebritiesメソッドに渡して有名人の検出をする
response = rekognition.recognize_celebrities(
Image={'Bytes': image}
)
logger.info(f'Rekognition response = {response}')
try:
# Rekognition のレスポンスから有名人の名前と信頼度を取り出し、APIのコール元へレスポンスする
label = response['CelebrityFaces'][0]
name = label['Name']
conf = round(label['Face']['Confidence'])
output = f'He/She is {name} with {conf}% confidence.'
logger.info(f'API response = {output}')
return output
except IndexError as e:
# Rekognition のレスポンスから有名人情報を取得出来なかった場合、他の写真にするように伝える。
logger.info(f"Coudn't detect celebrities in the Photo. Exception = {e}")
logger.info(traceback.format_exc())
return "Couldn't detect celebrities in the uploaded photo. Please upload another photo."
コードの説明( #コメントに書ききれなかった部分)
- 1行目
import boto3
- AWS サービスを扱う上で必要なモジュールのインポートをしています。
- boto3はPythonでAWSリソースを操作する際に用いるSDKです。
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/index.html
- 33~35行目
response = rekognition.recognize_celebrities(Image={'Bytes': image})
- rekognitionの使い方は、boto3のドキュメントに記載されています。
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rekognition.html#Rekognition.Client.recognize_celebrities
- 45行目
return文
- これは、実はアンチパターンの return 文です。
- 実際には、Lambdaのプロキシ統合のフォーマットに合わせたレスポンス形式にすると良いでしょう。
- 今回は、ブラウザ上での動作確認を行うため、明示的にプロキシ統合を使わず、また出力のフォーマットも意図的に無視をした記載にしています。
- 詳しくは「API Gateway 統合レスポンス」で検索してください。
- https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-integration-settings-integration-response.html
API Gateway の追加
- ここから、API Gateway と Lambda を紐づけていきます。
- 画面上部「サービス」から検索窓に”API”と入力し、候補にあがる「API Gateway」 を選択します。
- API Gateway の画面に遷移します。
- 画面下部の REST API の「構築」ボタンを押下します。
- 確認画面が表示された際は、「OK」ボタンを押下します。
- 「新しいAPIの作成」画面にて以下の設定を行い、最後に画面右下の「APIの作成」ボタンを押下します。
- API は「リソース」×「メソッド」で開発をしていきます。
- まず、リソースを作成します。「アクション」から「リソースの作成」を選択します。
- リソース名に「Rekognition」と入力し「リソースの作成」ボタンを押下します。
- 次にメソッドの作成をします。リソースを選択した上で、「アクション」->「メソッドの作成」を選択します。
- プルダウンから「POST」を選んで、「チェックボタン」を押下します。
- 統合タイプにて「Lambda関数」を選択し、Lambda関数にて先ほど作成した「《お名前》-rekognition-handson」を選択後、「保存」ボタンを押下します。
- 「Lambda 関数に権限を追加する」の確認画面が表示されるので、「OK」ボタンを押下します。
- 「統合リクエスト」を選択します。
- 画面下部「マッピングテンプレート」を展開し、「リクエスト本文のパススルー」の項目で「テンプレートが定義されていない場合(推奨)」を選択します。
- 「Content-Type」の項目にて、「マッピングテンプレートの追加」を押下します。
- 「Content-Type」の項目にて、
multipart/form-data
と入力し、チェックボタンを押します。
- 画面下部にテンプレートの生成画面が追加されます。
- テンプレートの生成のプルダウンにて、「メソッドリクエストのパススルー」を選択し「保存」ボタンを押します。
- 画面左「設定」を選択し、「設定」画面の下部「バイナリメディアタイプ」の項目にて「バイナリメディアタイプの追加」を押下します。
multipart/form-data
と入力し、「変更の保存」ボタンを押下します。
- 画面左ペイン「リソース」を選択後、画面上部「アクション」より「APIのデプロイ」を選択します。
- APIのデプロイ画面にて、以下の設定をし、「デプロイ」ボタンを押下します。
- 画面左ペインから、「ステージ」を選択し、「dev」-「rekognition」-「POST」を選択します。
- 画面右に、「URLの呼び出し」として作成された API の URL が表示されるので、これをコピーします。
HTML ファイルの作成
- 以下のHTMLの
****API Gateway URL 貼り付け****
の部分に、先ほど作成した API Gateway の URL を貼り付け、”index.html”のファイル名でパソコン上に保存します。- フォームを用いて、選択されたファイルを送信するだけの、単純な HTML ファイルです。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>有名人認識AIハンズオン</title>
</head>
<body>
<p>画像識別AIである Amazon Rekognition を用いて、有名人の認識をします!</p>
<form action="****API Gateway URL 貼り付け****" enctype="multipart/form-data" method="POST">
<input type="file" name="uploadfile" />
<input type="submit"/>
</form>
</body>
</html>
動作確認
- 例えば、日本が世界に誇るお笑い芸人(?)「世界のワタベ」で試してみましょう。
- ファイルを選択し、「送信」ボタンを押すと…
"He/She is Ken Watabe with 100% confidence."
と表示されました!- 有名人識別サービスの完成です!
お片付け
API Gateway の削除
- API Gateway の画面から、作成した「《お名前》-api-handson」を選択します。
- 画面左ペインで「リソース」が選択されている状態で、アクションから「APIの削除」を選択します。
- 削除前の確認画面が表示されるので、API名を入力後、「APIの削除」を押下します。
CloudWatch ログの削除
- Lambda のコンソール画面へ遷移するために、画面上部の「サービス」から「Lambda」を検索し選択します。
- 一覧画面にて、「《お名前》-rekognition-handson」を選択します。
- 画面上部「モニタリング」を選択し、「CloudWatchのログを表示」ボタンを押下します。
- CloudWatch Logs の画面に遷移します。「アクション」から「ロググループの削除」を選択します。
- 確認画面が表示されるので、「削除」ボタンを押します。
IAM ロールの削除
- Lambda の画面に戻り、「設定」タブ ->「一般設定」項目 -> 「編集」ボタンを選択します。
- 「基本設定を編集」画面の下部まで行き、「IAMコンソールでxxxロールを表示します。」を選択します。
- IAMロールの画面に遷移します。画面右上「ロールの削除」を選択します。
- 確認画面が出るので、「はい、削除します」を押下します。
Lambda 関数の削除
- Lambda の画面に戻り、画面上部「アクション」から「関数の削除」を選択します。
- 確認画面が表示されるので、「削除」を選択します。
- 「正常に削除されました。」と表示がでます。
- お片付けは以上で終了です。お疲れ様でした。
補足事項
料金
- AWSの各種サービスには、一定の無料利用枠があります。
- 今回の構成は、APIを大量にコールしない限り、全てAWSの無料利用枠に収まる想定です。
- 参考までに、無料枠を超えた際に発生する料金の目安を記載しておきます。
- (2021年7月27日時点 東京リージョン 月単位)
サービス分類 | 区分 | 料金 | 補足 |
---|---|---|---|
データ通信料 | AWSへのイン | 0.000USD/GB | |
〃 | AWSからのアウト | 0.114USD/GB | 最初の1GB~10TB |
CloudWatch | ログ収集 | 0.760USD/GB | |
〃 | ログ保存 | 0.033USD/GB | |
Lambda | リクエスト課金 | 0.20USD/100万件 | |
〃 | 実行時間課金 | 0.0000000021USD /128MB,1ミリ秒 |
|
API Gateway | REST API | 4.25USD/100万件 | 最初の3億3,300万コール受信数 |
Rekognition | Image | 0.0013USD/1画像 | 最初の100万枚 |
参考資料・ドキュメント
- AWS Hands-on for Beginners Serverless #1サーバーレスアーキテクチャで翻訳 Web API を構築する| AWS
- AWS API Gateway + Lambda から multipart/form-dataを用いてバイナリデータ(wav)をS3にputする – Qiita
- 【AWS-Chalice】multipart/form-data の形式でアップロードされたバイナリファイルの扱い方 – Qiita
- Rekognition — Boto3 Docs 1.15.11 documentation
- API Gateway で統合レスポンスを設定する – Amazon API Gateway
- その他、AWS 公式ドキュメント