3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

クラウドAI by ナレコムAdvent Calendar 2021

Day 20

AWS初心者向けハンズオンでAIサービスを触ってみる

Last updated at Posted at 2021-12-19

今回は、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 を感情分析

感情分析ができるのは知っていたのですが、キーフレーズやエンティティの取得も可能なのは知らなかったです…
ドキュメントの分類等にも使えて便利そうですね。

構成図

AWS Design 2 (2).png

折角なので、cacooを使ってハンズオン構成図を真似ました。
使い慣れてないと難しい…

処理の流れ

  1. Pollyから音声ファイル(mp3)を作成
  2. S3(input用)に保存(トリガー)
  3. Lambda実行(transcribe-function) 音声から文字生成
  4. 3の結果を、S3(output用)にjsonファイルで保存(トリガー)
  5. Lambda実行(comprehend-function) 文字から感情分析
  6. 実行結果が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
transcribe-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
comprehend-function.py
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
build.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
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します

音声を保存する

polly1.png
2021-12-17_20h10_29.png
コンソールから、Pollyを使用し音声をS3(input)に保存します。

# 分析結果
2021-12-17_20h13_12.png
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]のデプロイするときのプラクティス

3
0
0

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
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?