kakakakakku blog

Weekly Tech Blog: Keep on Learning!

LocalStack を使って Amazon Transcribe をローカル環境で操作する

Amazon Transcribe で Speech-to-Text を実現するときに,LocalStack を使えば Amazon Transcribe を「AWS アカウントを使わずにローカル環境で」動かせる👌LocalStack 自体は仕事でもプライベートでも使ってるけど,Amazon Transcribe は今まで試したことがなくて,今回試してみた❗️

Amazon Transcribe API のサポート状況

もちろん LocalStack がすべての Amazon Transcribe API をサポートしているわけではないけど,カバレッジは以下で確認できる.StartTranscriptionJob / ListTranscriptionJobs / GetTranscriptionJob はサポートされてて基本的な操作はできそう.

docs.localstack.cloud

Vosk

そもそも Amazon Transcribe の Speech-to-Text の仕組みは公開されていないのでは?と疑問に感じるけど,LocalStack の内部では Vosk が使われているとドキュメントに載っていた💡英語・日本語・ドイツ語など,多くの言語をサポートしている.もちろん Vosk の精度に依存せず,LocalStack をインタフェース確認として使うのが良いと思う👌

alphacephei.com

注意点

最初に Amazon Transcribe を試したところ,TranscriptionJob(文字起こしジョブ)を実行すると以下のエラーが出てしまって困ったけど,何やら関連する issue もあって,プラットフォームが関係するようだった.今回は Apple M3 を使っているため localstack:latest-amd64 イメージで LocalStack を起動したら問題なく動くようになった👌試すときに注意が必要かなと🚨

cannot load library '/var/lib/localstack/lib/python-packages/lib/python3.11/site-packages/vosk/libvosk.so': libatomic.so.1: cannot open shared object file: No such file or directory

試す

まずは AWS CloudFormation で Amazon S3 バケットを作る.LocalStack は AWS CloudFormation / Amazon S3 もサポートしている❗️

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: transcribe-sandbox

awslocal コマンドで AWS CloudFormation スタックをデプロイして,transcribe-sandbox バケットを確認した👌

$ awslocal cloudformation deploy --stack-name transcribe-sandbox --template-file templates/template.yaml

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - transcribe-sandbox

$ awslocal s3api list-buckets | jq -r '.Buckets[].Name'
transcribe-sandbox

次に AWS CLI で Speech-to-Text をする mp3 を transcribe-sandbox バケットにアップロードする.今回はサンプルとして効果音ラボ「よろしくお願いします(冷静な魔術師)」を使う.準備完了〜 \( 'ω')/

$ awslocal s3 cp ./mp3/wizard-greeting1.mp3 s3://transcribe-sandbox/wizard-greeting1.mp3
upload: mp3/wizard-greeting1.mp3 to s3://transcribe-sandbox/wizard-greeting1.mp3

そして今度は Python (boto3) スクリプトで Amazon Transcribe の TranscriptionJob(文字起こしジョブ)を実行する❗️

import uuid

import boto3

transcribe = boto3.client('transcribe', endpoint_url='http://localhost:4566')

response = transcribe.start_transcription_job(
    TranscriptionJobName=f'job-{str(uuid.uuid4())}',
    Media={
        'MediaFileUri': 's3://transcribe-sandbox/wizard-greeting1.mp3',
    },
    LanguageCode='ja-JP',
)

print('TranscriptionJobName: ' + response['TranscriptionJob']['TranscriptionJobName'])
print('TranscriptionJobStatus: ' + response['TranscriptionJob']['TranscriptionJobStatus'])

実行すると TranscriptionJobName などのレスポンスを確認できる.

TranscriptionJobName: job-b9b2d46a-6621-4890-b09f-08fb3d5f059e
TranscriptionJobStatus: IN_PROGRESS

少し待つと IN_PROGRESS から COMPLETED になる.LocalStack の Resource Browser(マネジメントコンソール)で TranscriptionJob(文字起こしジョブ)の詳細を確認できる.

もちろん AWS CLI の transcribe get-transcription-job コマンドでも確認できる👌

$ awslocal transcribe get-transcription-job --transcription-job job-b9b2d46a-6621-4890-b09f-08fb3d5f059e | jq .
{
  "TranscriptionJob": {
    "TranscriptionJobName": "job-b9b2d46a-6621-4890-b09f-08fb3d5f059e",
    "TranscriptionJobStatus": "COMPLETED",
    "LanguageCode": "ja-JP",
    "MediaSampleRateHertz": 44100,
    "MediaFormat": "wav",
    "Media": {
      "MediaFileUri": "s3://transcribe-sandbox/wizard-greeting1.mp3"
    },
    "Transcript": {
      "TranscriptFileUri": "http://s3.localhost.localstack.cloud:4566/transcribe-sandbox/job-b9b2d46a-6621-4890-b09f-08fb3d5f059e.json?AWSAccessKeyId=__internal_call__&Signature=pihlxHmUVQOeBuoHSjUCy1Yz0Gs%3D&Expires=1718111742"
    },
    "StartTime": "2024-06-11T21:38:34.119979+09:00",
    "CreationTime": "2024-06-11T21:38:34.119496+09:00",
    "CompletionTime": "2024-06-11T21:40:32.940129+09:00"
  }
}

最終的に transcribe-sandbox バケットにアップロードされた job-b9b2d46a-6621-4890-b09f-08fb3d5f059e.json を確認すると,期待通りに「よろしくお願いします」を Speech-to-Text できていた👏

{
  "jobName": "job-b9b2d46a-6621-4890-b09f-08fb3d5f059e",
  "status": "COMPLETED",
  "results": {
    "transcripts": [
      {
        "transcript": "よろしく お 願い し ます"
      }
    ],
    "items": [
      {
        "start_time": 0.0,
        "end_time": 0.48,
        "type": "pronunciation",
        "alternatives": [
          {
            "confidence": 0.816937,
            "content": "よろしく"
          }
        ]
      },
      {
        "start_time": 0.48,
        "end_time": 0.57,
        "type": "pronunciation",
        "alternatives": [
          {
            "confidence": 0.984702,
            "content": ""
          }
        ]
      },
      {
        "start_time": 0.57,
        "end_time": 0.87,
        "type": "pronunciation",
        "alternatives": [
          {
            "confidence": 0.984702,
            "content": "願い"
          }
        ]
      },
      {
        "start_time": 0.87,
        "end_time": 0.99,
        "type": "pronunciation",
        "alternatives": [
          {
            "confidence": 1.0,
            "content": ""
          }
        ]
      },
      {
        "start_time": 0.99,
        "end_time": 1.23,
        "type": "pronunciation",
        "alternatives": [
          {
            "confidence": 1.0,
            "content": "ます"
          }
        ]
      }
    ]
  }
}

関連記事

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com