今回は、AIサービスを触ったことがないのでAWS 初心者向けハンズオンで紹介されている下記をやってみます。
AWS Lambda と AWS AI Services を組み合わせて作る音声文字起こし & 感情分析パイプライン
Terraformを最近触り始めたので、こちらでリソース作成しながら進めてみようと思います!
動作環境
Cloud9
Terraform v0.13.0
- provider registry.terraform.io/hashicorp/archive v2.2.0
- provider registry.terraform.io/hashicorp/aws v3.69.0
Amazon TranscribeとAmazon Comprehendってなに?
Amazon Transcribeとは、音声をテキストに変換してくれる文字起こしサービス
保存された音声ファイルだけでなく、リアルタイム変換も可能
・ユースケースの例
・記者会見等の録音データを文字起こし
・動画コンテンツの字幕作成
・コールセンター業務の通話の文字起こし
Amazon Connect等のサービスと組み合わせれば色々と使えそうですね。
Amazon Comprehendとは、機械学習を使用した自然言語処理サービス
テキストの中の有用な情報を発見・分析やキーフレーズやエンティティ(場所・日付)の取得、感情分析(ポジネガ分析)が可能
・ユースケースの例
・社内ドキュメントのキーフレーズやエンティティを検索エンジンでインデックス
・レビューやアンケート結果、ソーシャルメディアからのFeedback を感情分析
感情分析ができるのは知っていたのですが、キーフレーズやエンティティの取得も可能なのは知らなかったです…
ドキュメントの分類等にも使えて便利そうですね。
構成図
折角なので、cacooを使ってハンズオン構成図を真似ました。
使い慣れてないと難しい…
処理の流れ
- Pollyから音声ファイル(mp3)を作成
- S3(input用)に保存(トリガー)
- Lambda実行(transcribe-function) 音声から文字生成
- 3の結果を、S3(output用)にjsonファイルで保存(トリガー)
- Lambda実行(comprehend-function) 文字から感情分析
- 実行結果がcloudwatchlogsに保存される。(/aws/lambda/comprehend-function)
Terraform環境準備
ちょうど弊社のアドベントカレンダーに環境準備の記事があるので参考にどうぞ
Cloud9 + CodeCommitでサクッとterraform実行環境を作成する
ディレクトリ構成
.
├── build
│ ├── function1
│ └── function2
├── build.sh
├── lambda
│ ├── function1.zip
│ └── function2.zip
│
├── main.tf
├── src1
│ └── transcribe-function.py
├── src2
│ └── comprehend-function.py
├── terraform.tfstate
└── terraform.tfstate.backup
src1 src2 にLambdaで実行するコードを保存しています。
build.shを実行するとアップロード用にbuildされたzipファイルが作成されます。
ディレクトリ作成
mkdir -p handson/lambda handson/src1 handson/src2
まずは、ハンズオン用ディレクトリ、Lambdaにコードをアップロード時に必要となるディレクトリを作成します。
Lambdaアップロード準備
pythonファイル作成
vi /src1/transcribe-function.py
vi /comprehend-function.py
import json
import urllib.parse
import boto3
import datetime
s3 = boto3.client('s3')
transcribe = boto3.client('transcribe')
def lambda_handler(event, context):
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
try:
transcribe.start_transcription_job(
TranscriptionJobName= datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '_Transcription',
LanguageCode='ja-JP',
Media={
'MediaFileUri': 'https://s3.ap-northeast-1.amazonaws.com/' + bucket + '/' + key
},
#OutputBucketName='*Your Output Bucket*'
OutputBucketName='h4b-serverless-t-output'
)
except Exception as e:
print(e)
print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
raise e
import json
import urllib.parse
import boto3
s3 = boto3.client('s3')
comprehend = boto3.client('comprehend')
def lambda_handler(event, context):
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
try:
response = s3.get_object(Bucket=bucket, Key=key)
body = json.load(response['Body'])
transcript = body['results']['transcripts'][0]['transcript']
sentiment_response = comprehend.detect_sentiment(
Text=transcript,
LanguageCode='ja'
)
sentiment_score = sentiment_response.get('SentimentScore')
print(sentiment_score)
except Exception as e:
print(e)
print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
raise e
vi /src1/transcribe-function.py
vi /comprehend-function.py
Lambdaで実行させるコードを準備します。
S3Bucket名はユニークである必要があるため、transcribe-function.pyに記載してるOutputBucketNameは適宜変更してください。
変更した場合は、tfファイルの方でも変更が必要となります。
アップロード準備
vi bulid.sh
#!/bin/bash
if [ -d build ]; then
rm -rf build
fi
# Recreate build directory
mkdir -p build/function1/ build/function2/
# Copy source files
echo "Copy source files"
cp -r src1 build/function1/
cp -r src2 build/function2/
sh build.sh
build用のスクリプトを作成。
pythonファイル作成は終わってるので実行。
tfファイル作成
vi main.tf
# Terraform Setting
terraform {
required_version = "0.13.0"
}
# Role Policy
data "aws_iam_policy" "AmazonS3FullAccess" {
arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
data "aws_iam_policy" "AmazonTranscribeFullAccess" {
arn = "arn:aws:iam::aws:policy/AmazonTranscribeFullAccess"
}
data "aws_iam_policy" "ComprehendFullAccess" {
arn = "arn:aws:iam::aws:policy/ComprehendFullAccess"
}
data "aws_iam_policy" "CloudWatchLogsFullAccess" {
arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}
data "aws_iam_policy_document" "lambda_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "transcribe_function_role" {
name = "transcribe-function-role"
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}
resource "aws_iam_role_policy_attachment" "attache_ts_role1" {
role = aws_iam_role.transcribe_function_role.name
policy_arn = data.aws_iam_policy.AmazonS3FullAccess.arn
}
resource "aws_iam_role_policy_attachment" "attache_ts_role2" {
role = aws_iam_role.transcribe_function_role.name
policy_arn = data.aws_iam_policy.AmazonTranscribeFullAccess.arn
}
resource "aws_iam_role_policy_attachment" "attache_ts_role3" {
role = aws_iam_role.transcribe_function_role.name
policy_arn = data.aws_iam_policy.CloudWatchLogsFullAccess.arn
}
resource "aws_iam_role" "comprehend_function_role" {
name = "comprehend-function-role"
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}
resource "aws_iam_role_policy_attachment" "attache_ch_role1" {
role = aws_iam_role.comprehend_function_role.name
policy_arn = data.aws_iam_policy.AmazonS3FullAccess.arn
}
resource "aws_iam_role_policy_attachment" "attache_ch_role2" {
role = aws_iam_role.comprehend_function_role.name
policy_arn = data.aws_iam_policy.ComprehendFullAccess.arn
}
resource "aws_iam_role_policy_attachment" "attache_ch_role3" {
role = aws_iam_role.comprehend_function_role.name
policy_arn = data.aws_iam_policy.CloudWatchLogsFullAccess.arn
}
# S3 input output
change
resource "aws_s3_bucket" "h4b-serverless-t-input" {
bucket = "h4b-serverless-t-input" #適宜変更
force_destroy = true
}
resource "aws_s3_bucket" "h4b-serverless-t-output" {
bucket = "h4b-serverless-t-output" #適宜変更
force_destroy = true
}
# Lambda transcribe
resource "aws_lambda_permission" "allow_bucket1" {
statement_id = "AllowExecutionFromS3Bucket"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.transcribe-function.arn
principal = "s3.amazonaws.com"
source_arn = aws_s3_bucket.h4b-serverless-t-input.arn
}
resource "aws_s3_bucket_notification" "bucket_notification" {
bucket = aws_s3_bucket.h4b-serverless-t-input.id
lambda_function {
lambda_function_arn = aws_lambda_function.transcribe-function.arn
events = ["s3:ObjectCreated:*"]
}
depends_on = [aws_lambda_permission.allow_bucket1]
}
data "archive_file" "function_zip1" {
type = "zip"
source_dir = "build/function1"
output_path = "lambda/function1.zip"
}
resource "aws_lambda_function" "transcribe-function" {
function_name = "transcribe-function"
handler = "src1/transcribe-function.lambda_handler"
filename = data.archive_file.function_zip1.output_path
runtime = "python3.7"
role = aws_iam_role.transcribe_function_role.arn
}
# Lambda Comprehend
resource "aws_lambda_permission" "allow_bucket2" {
statement_id = "AllowExecutionFromS3Bucket"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.comprehend-function.arn
principal = "s3.amazonaws.com"
source_arn = aws_s3_bucket.h4b-serverless-t-output.arn
}
resource "aws_s3_bucket_notification" "bucket_notification2" {
bucket = aws_s3_bucket.h4b-serverless-t-output.id
lambda_function {
lambda_function_arn = aws_lambda_function.comprehend-function.arn
events = ["s3:ObjectCreated:*"]
filter_suffix = ".json"
}
depends_on = [aws_lambda_permission.allow_bucket2]
}
data "archive_file" "function_zip2" {
type = "zip"
source_dir = "build/function2"
output_path = "lambda/function2.zip"
}
resource "aws_lambda_function" "comprehend-function" {
function_name = "comprehend-function"
handler = "src2/comprehend-function.lambda_handler"
filename = data.archive_file.function_zip2.output_path
runtime = "python3.7"
role = aws_iam_role.comprehend_function_role.arn
}
Bucket名を変更した場合は、こちらに記載しているBucket名も変更してください。
環境構築
terraform init
terraform plan
terraform apply
環境を構築します。
init でワークスペースを初期化後
plan で問題なければ applyします
音声を保存する
コンソールから、Pollyを使用し音声をS3(input)に保存します。
# 分析結果
comprehendから分析結果が返ってきてますね!
おぉ、Negativeが強い文章だとなってます。
Comprehendすごい!!
感想
今回は、AWS Hands-on for BeginnersからAWS Lambda と AWS AI Services を組み合わせて作る音声文字起こし & 感情分析パイプラインをTerraformで構築し触ってみました。
AI関連のサービスを触ったことがなかったのでとても良い経験になりました。
Terraform部分はもっときれいに効率良く書けるように関数やmoduleそしてファイル構成を考えないといけないですね…
AWS Hands-on for Beginnersは要点がきれいにまとまっていてわかりやすく、色んなサービスのハンズオンもあるので触ったことないサービスを触る前に一度見てみることをおすすめします。
参考
AWS Lambda と AWS AI Services を組み合わせて作る音声文字起こし & 感情分析パイプライン
TerraformでLambda[Python]のデプロイするときのプラクティス