DjangoからAmazon Transcribeを使う

Amazon TranscribeはAmazon Web Serviceの機械学習サービスの1つで、音声をテキスト情報に変換してくれるサービスです。

Amazon Pollyとは逆のことをしてくれます(*´∀`*)

音声から文字起こしをするのって結構大変ですよね。会議の録音を聴きながら議事録を作成しようとすると、会議時間×2倍ぐらいの時間がかかってしまいます。(そんな一言一句、完コピしなくていいじゃんと思いますが)

Amazon Transcribeに音声ファイルを投げるだけで、全部文字にしてくれるんだったら、すごい楽ですよね。もう音声ファイルと、Amazon Transcribeの出力だけ添付して、決定事項だけ書ければいいんじゃないかと妄想しています。

では、さっそくAmazon Transcribeを使っていきましょう!

パッケージのインストール

DjangoからAmazon Transcribeを使うには、3つのパッケージが必要になります。django、boto3、そしてawscliです。

boto3は、AWS SDK for Pythonと呼ばれるもので、要するにAWSにアクセスしたい時に使う、Python用のSDKですね(SDK:Software Development Kit)。名前の由来がよくわからないので、とても覚えにくいです(´;ω;`)

そして、awscliはAWS Command Line Interfaceの略で、AWSにコマンドラインからアクセスできるようにするツールです。

この2つを連携させることで、Amazon Transcribeを使うことができるようになります。

また、今回はAmazon S3も使うので、django-storagesもインストールします。

最後に、Amazon Transcribeからのテキスト出力結果を、ダウンロードする必要があるので、requestsというパッケージもインストールしておきます。

…というわけで、さっそくPythonの仮想環境に入って、これらのパッケージをインストールしていきます。

pip install django

pip install boto3
pip install awscli

pip install django-storages
pip install requests​

インストールはこれだけで完了です!

AWSでIAMユーザーの作成

続いて、Amazon Transcribeで利用するIAMユーザーを作成します。

IAM(Identity and Access Management)は、AWSでユーザー作成やその権限などを設定できるサービスです。AWSアカウントを作成すると、ルートユーザーと呼ばれる、全ての権限をもったユーザーが作成されます。ただ、そのルートユーザーでなんでもできてしまう、というのが問題になることがあります。

ルートユーザーが乗っ取られた場合、悪意を持った人がなんでもできてしまうかもしれません。アカウントを共有していたら、誰が何を変更したのかわからない状態になります。

このような状態を避けるために、それぞれの用途に合わせてIAMユーザーを作成し、各ユーザーにそれぞれ必要な必要最低限の権限を与えることで、もしもの場合に備えておくことができます。

今回はAmazon Transcribeだけを使うので、Amazon Transcribeしか利用できないユーザーを作成するというわけですね!

まずは、AWSマネジメントコンソールでIAMサービスを選択し、左側のメニューから[ユーザー]を選択します。

スクリーンショット 2021-04-03 11.40.28

画面上部にある[ユーザーを追加]ボタンを押して、ユーザーの作成を始めていきます。

スクリーンショット 2021-04-08 16.50.58

作成画面に入ったら、適当に名前をつけてユーザーを作成します。名前はなんでもいいですが、あとでどんなユーザーかわかりやすいようにしておく方が良いでしょう。

下の、[AWSアクセスの種類を選択]に関しては、今回は[AWSマネジメントコンソールへのアクセス]は必要ないので、[プログラムによるアクセス]にだけチェックを入れます。

スクリーンショット 2021-04-08 16.51.14

次に進むと、アクセス許可を設定する画面になります。

[ユーザーをグループに追加]は、同じような権限を持ったユーザーを複数作成するのであれば、グループを作成しておくと便利です。そのグループにユーザーを追加/削除するだけで、そのグループに設定した権限を与えたりなくしたりすることができます。

[アクセス権限を既存のユーザーからコピー]はそのままですね。

今回は新しく作るので、[既存のポリシーを直接アタッチ]を選択します。ポリシーはAWSのサービスの利用権限を細かくわけています。このポリシーの一覧の中から必要な権限を選んで、ユーザーに付与することができます。

Amazon Transcribeを使う場合は、検索バーに"transcribe"と入力して、Amazon Transcribe関連のポリシーをフィルターしましょう。

スクリーンショット 2021-04-08 16.51.27

[AmazonTranscribeFullAccess]にチェックを入れます。

また、今回はS3へのアクセスも必要なので、"S3"を検索バーに入力し、[AmazonS3ReadOnlyAccess]も追加しておきます。

スクリーンショット 2021-04-09 20.39.47

Amazon TranscribeからS3への書き込みは必要ないので、ReadOnlyにしています。

スクリーンショット 2021-04-08 16.51.43

タグの追加は任意です。作成しているサービスごとにタグを作っておくと、一覧にしたい時に便利です。

最後に確認画面があります。

スクリーンショット 2021-04-08 16.51.55

確認して問題なければ、下の方にある「ユーザーの作成」ボタンを押します。

スクリーンショット 2021-04-08 16.52.07

成功すると、サインイン用のアドレスや、アクセスキーID、シークレットアクセスキーをダウンロードできる画面になります。(画像ではセキュリティのため表示しません)

これらのキーを記憶するのは困難なので、忘れずに.csvファイルをダウンロードしておきましょう。このファイルは厳重管理しておきます。

AWS CLIのインストール

boto3からAWSサービスにアクセスするためには、利用するIAMユーザーを設定しなければなりません。そのための事前設定として、awscliに先ほど作成したキーを登録する必要があります。

awscliインストール後に、以下のコマンドを入力するとセットアップが開始されます。

aws configure

すると、先ほどIAMユーザーで作成したアクセスキーIDなどを求められますので、それぞれ入力していきます。

今回、S3を使うので、S3のバケットがあるリージョンと同じリージョンに設定しておきましょう。僕の場合は、東京リージョンなので"ap-northeast-1"に設定しました。

出力フォーマットはjsonにします。

Default region name [us-west-2]:ap-northeast-1
Default output format [None]:json

※一応利用しているリージョンでAmazon Transcribeが利用できるかは確認してくださいね。

これでAWS側の準備は完了です。

あとは、DjangoでAmazon Transcribeを使ったモデルを作っていきましょう。

※プロファイルの複数追加について

もし、Amazon Pollyなどで、他のIAMユーザーを同時に利用するのであれば、以下のコマンドで新しいプロファイルを追加してください。"--profile"がない場合は、defaultという名前のプロファイルで設定されます。

aws configure --profile transcribe_prof

最後の要素はプロファイルの名前なので、自由に設定できます。後でAWSに接続する際に使うので、覚えておいてください。

ちなみにプロファイルの一覧は以下のコマンドで確認できます。(linux系の場合)

vi ~/.aws/credentials

Djangoのtranscribeアプリケーションの作成


これからtranscribeというアプリケーションを作成していきますが、大部分は以前に書いたこちらの記事と一緒なので、違うところだけ紹介していきます。

今回は、以下コマンドでtranscribeアプリケーションを作成し、このアプリケーションにCRUDのCreateとRead部分を実装していきます。

python manage.py startapp transcribe

transcribeアプリケーションのモデル

今回はシンプルに作りたいので、モデルにも最低限の情報だけつけていきます。(本当はモデルも作る必要はありませんが、このほうがわかりやすいと思うので…)

# transcribe/models.py

from django.db import models
import uuid
import os

def rename_audio(instance, filename):
 ext = filename.split('.')[-1]
 filename = '{}.{}'.format(str(instance.id), ext)
 return os.path.join('transcribe_audio', filename)

class Transcribe(models.Model):
   id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 
   audio = models.FileField(upload_to=rename_audio, blank=True, null=True)
   text = models.TextField(max_length=400)

まずidにUUIDを設定します。これはアップロードする音声ファイル名にUUIDを設定して被らないようにするためです。実際のファイル名の変更はrename_audio関数で実施します。この関数の詳細は以下の記事で解説しています。

audioは、音声ファイルをアップロードするフィールドです。

textは、音声ファイルを文字起こししたテキストを格納するフィールドになります。

さて、Amazon Pollyでは、MEDIA_URLとMEDIA_ROOTを設定しましたが、Amazon TranscribeではAmazon S3を利用する必要があります。

その理由は、音声ファイルをs3://やhttp(s)://のアドレスで渡さないといけないからです。そのため、ローカル環境に保存しているファイルだとAmazon Transcribeに渡すことができません。

Amazon S3の細かい設定方法は以下の記事を参考にしてください。

ざっくり概要

# S3のバケットを用意
バケット名:skilla-daikinishimatsu

# settings.pyに以下を追加
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_STORAGE_BUCKET_NAME = 'skilla-daikinishimatsu'
AWS_S3_REGION_NAME = 'ap-northeast-1'
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')

# 環境変数の設定を忘れずに。
export AWS_ACCESS_KEY_ID="あなたのアクセスキーID"​
export AWS_SECRET_ACCESS_KEY="あなたのシークレットアクセスキー"

transcribeアプリケーションのCreateView

さて、ちょっと手間がふえて大変でしたね。ようやくtranscribeアプリケーションの作成に入れます。

処理の流れとしては、フォームに音声ファイルを登録し、その音声ファイルをAmazon Transcribeに渡して、テキスト情報を作成してもらいます。Amazon Pollyと似たような感じですが、異なる部分もありますので、注意しながら進めてください。

まずは、フォームに入力した後に、Djangoが[form_valid]関数を呼ぶので、それを使います。

[form_valid]はフォームに入力された内容に問題がなかった場合に、呼ばれる関数です。ここでaudioに登録されたurlをAmazon Transcribeに渡して、テキスト情報に変換してもらうことにします。

まずはTranscribeCreateViewがどうなっているかみてみましょう。

# transcribe/views.py

from django.views import generic 
from django.urls import reverse_lazy
from .models import Transcribe

class TranscribeCreateView(generic.CreateView):
 model = Transcribe
 template_name = 'transcribe/create.html'
 success_url = reverse_lazy('transcribe:list')
 fields = ['audio']
 
 def form_valid(self, form):
   transcribe = form.save(commit=True)
   response = use_transcribe(transcribe.id,transcribe.audio)

   try:
       transcribe.text = response["results"]["transcripts"][0]["transcript"]
   except:
       pass
   transcribe.save()

   return super().form_valid(form)

フォームで入力するフィールドは、audioだけなので、fieldsにはaudioだけ書きます。textはaudioから作成するフィールドなので、入力されては困ります。

[form_valid]関数では、form.save(commit=True)と呼ぶことによって、フォームで入力した内容から作成されるモデル(transcribe)を取得します。commit=Trueとしているのは、一旦S3に音声ファイルを保存してもらわないと、Amazon Transcribeの処理ができないからです。

Amazon Pollyの時と同じようにuse_transcribe関数から結果を返してもらって、その中に情報があれば、textフィールドに保存しています。この関数については、後述します。

これでモデルのデータが完成しましたので、最後にtranscribe.save()でデータベースに保存して完了です。

Amazon Transcribeへのアクセス方法

最後にuse_transcribe関数の中身を紹介しておきます。最低限の実装として以下のようにしました。

# transcribe/views.py 

def use_transcribe(id,audio):

   # create session
   session = Session(profile_name="transcribe_prof")
   transcribe = session.client("transcribe")

   # set job info
   job_name = str(id)
   job_uri = audio.url.split("?")[0]

   transcribe.start_transcription_job(
       TranscriptionJobName=job_name,
       Media={'MediaFileUri': job_uri},
       MediaFormat='mp3',
       LanguageCode='ja-JP'
   )

   # wait until job ends
   while True:
       status = transcribe.get_transcription_job(TranscriptionJobName=job_name)
       if status['TranscriptionJob']['TranscriptionJobStatus'] in ['COMPLETED', 'FAILED']:
           break
       print("Not ready yet...")
       time.sleep(5)

   try:
       response = requests.get(status['TranscriptionJob']['Transcript']['TranscriptFileUri'], allow_redirects=True)
       return response.json()
   except:
       return None

最初にboto3を利用してAWSと接続するセッションを作成します。profile_name="default"と指定すると、aws configureで設定したアクセスキーIDなどでAWSに接続します。僕は"transcribe_prof"に設定したので、こちらを使います。

そして、次の行で"transcribe"を使うクライアントを作成します。job_nameはジョブの名前なのでIDを使い、job_uriには、音声ファイルの場所を設定します。

audio.urlのファイル名の後ろに余計な情報がたくさんついていて、最初うまくいかなかったので、"?"の後を消して渡しました。

これで事前準備が完了しました。

次のtranscribe.start_transcription_jobで、実際にAmazon Transcribeに接続しに行っています。設定をしている値は見ればわかる感じなので、説明は必要ないかと思います。

ここで、Amazon Pollyと違うのは、この関数から直接結果を返してくれないところです。

処理に意外と時間がかかるので、ステータスを確認して、終わったかどうか随時チェックしています。短い音声ファイルですが、15~20秒ほどかかってしまいます。

最後に、statusから結果を保存しているurlを指定し、getで実際のデータを取得しています。そのデータを扱いやすいようにjson()で変換して結果をreturnしています。

これでAmazon Transcribeを利用して、音声ファイルをテキスト情報に変換して、データベースに保存することができるようになりました。

おまけ

statusの中身の例。

# status

{'TranscriptionJob': 
    {'TranscriptionJobName': '186f54fa-523c-489d-9341-4ae819a0650d',
     'TranscriptionJobStatus': 'COMPLETED',
     'LanguageCode': 'ja-JP',
     'MediaSampleRateHertz': 22050,
     'MediaFormat': 'mp3',
     'Media': {
         'MediaFileUri': 'https://skilla-daikinishimatsu.s3.amazonaws.com/transcribe_audio/fd392708-9633-4232-a04f-b4a41caca865.mp3'},
         'Transcript': {
             'TranscriptFileUri': 'https://s3.ap-northeast-1.amazonaws.com/aws-transcribe-ap-northeast-1-prod/377818748129/186f54fa-523c-489d-9341-4ae819a0650d/d74f0a7e-b0ff-4c2e-8623-b4d219ebeb26/asrOutput.json?X-Amz-Security-Token=IQoJb3JpZ2luX2VjEBsaDmFwLW5vcnRoZWFzdC0xIkcwRQIgb6ZLDgIA%2F62YdFxnc%2BsJhJzxQc9gMJSfan%2FDV6PuM0oCIQDmKYztby5HXGJGGCno7PimVLIqawXPmX8%2FsH2si1i1Ayq%2BAwh0EAEaDDAxNTUzMzUyNzE5NSIMeCaXmFegz%2FdcN87EKpsDgTKKEmQPIIFJ79VUOkOCatOooKK3%2FLrBDton1QubB7ivCsQgbomvdlKtWCP3H990n0T%2B1t%2BhGp7r5iMss7kQz2cZdehKe%2FAZ7qSPJhJ%2BMVpywXXmr9zPfFVNsWUGs34VbSreYiysp7%2FMzDFKGW2GkqkkamiFetV8jMb7oV4hywVCUNFCveMQ2ZNuWZVz7DlGvdLy%2FGtybrYxkBXa0%2Fx6Rn59JuNinsE4ZZeGW5LmqA17VD%2F9Cdqw3PgyT5dmzWVTvtnpqJmZXuu%2B9Sie1Sz0ZYQgpz5B0YmAYt3kGfBTQPPy1W0%2FhogUa%2Fx%2F%2BUQ35TfjKRL7MSiL4jxR4obCw6FJgW1tyIxwtyaoHpXrR%2F1SfS8ECdvaCZTuV8llri4AcS3K2uRrt6sBQGeucUpBn%2FcKcD8cEPpu4VOmpquP45QBhHLV6V8aSgwsUh9FqTNNl5qMi54Xd%2FsnnWyQgjv5G2k7uX1AG2uMIKG91RpW%2FbkfOWc4nvqcdMR%2FnIIW3KQbem7jXWWbnL6Tp%2BOfEH4Kui85RdESO15y05ijop39MJjpwIMGOusBNP1fW0YV%2BukZjdzVubSc9vtxmNyOCql2w45fktUxqho5NZCP6MHL7AyRNeKfNndBBG1Lo44WLlKCl1REn%2Bo9Kwb7RySZGhbwJdDLtb0ZuDYTcMO9KelO%2F%2B9qxDWWcBtdi7bi%2FutH80zh0CqAzj1%2BdrfbSRR2P08%2BeA4e4%2BX5qGfR%2BGBfOYp6AcrJUa37lpKtR9u37nnFoAyiWE1%2FwrMrUcr8%2F9%2BLZS9m9Lz%2FpzE6rwKsHyeF0bZBwNbvpauD8Smr926d4KNwg%2Bd%2FA0elm1npAofNlkrxR4J6mH7DYeTR5CyuOk4PtI6O8T9D%2Bw%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20210409T114642Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&X-Amz-Credential=ASIAQHHO62CN2TIALLRM%2F20210409%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Signature=870eaaaa95760252be04d1d3176028ec998c4605b02e970becbe2f6abddb577c'
             },
         'StartTime': datetime.datetime(2021, 4, 9, 11, 46, 21, 420000, tzinfo=tzlocal()),
         'CreationTime': datetime.datetime(2021, 4, 9, 11, 46, 21, 391000, tzinfo=tzlocal()),
         'CompletionTime': datetime.datetime(2021, 4, 9, 11, 46, 37, 811000, tzinfo=tzlocal()),
         'Settings': {
             'ChannelIdentification': False,
             'ShowAlternatives': False
         }
     }, 
 'ResponseMetadata': {
     'RequestId': 'c77937f1-2c99-4ce8-adf6-f082cc431b33',
     'HTTPStatusCode': 200,
     'HTTPHeaders': {
         'content-type': 'application/x-amz-json-1.1',
         'date': 'Fri, 09 Apr 2021 11:46:41 GMT',
         'x-amzn-requestid': 'c77937f1-2c99-4ce8-adf6-f082cc431b33',
         'content-length': '2147',
         'connection': 'keep-alive'
         },
     'RetryAttempts': 0
 }
}

Amazon Transcribeでの出力結果の例。

# asrOutput.json

{"jobName":"186f54fa-523c-489d-9341-4ae819a0650d",
 "accountId":"377818748129",
 "results":{
     "transcripts":
         [{"transcript":"吾輩 は 猫 で、 ある 名前 は まだ ない"}],
         "items":[
             {"start_time":"0.04","end_time":"0.58","alternatives":[{"confidence":"0.9493","content":"吾輩"}],"type":"pronunciation"},
             {"start_time":"0.58","end_time":"0.73","alternatives":[{"confidence":"1.0","content":"は"}],"type":"pronunciation"},
             {"start_time":"0.73","end_time":"1.04","alternatives":[{"confidence":"1.0","content":"猫"}],"type":"pronunciation"},
             {"start_time":"1.04","end_time":"1.18","alternatives":[{"confidence":"1.0","content":"で"}],"type":"pronunciation"},
             {"alternatives":[{"confidence":"0.0","content":"、"}],"type":"punctuation"},
             {"start_time":"1.18","end_time":"1.44","alternatives":[{"confidence":"1.0","content":"ある"}],"type":"pronunciation"},
             {"start_time":"1.81","end_time":"2.28","alternatives":[{"confidence":"1.0","content":"名前"}],"type":"pronunciation"},
             {"start_time":"2.28","end_time":"2.5","alternatives":[{"confidence":"1.0","content":"は"}],"type":"pronunciation"},
             {"start_time":"2.72","end_time":"3.03","alternatives":[{"confidence":"1.0","content":"まだ"}],"type":"pronunciation"},
             {"start_time":"3.03","end_time":"3.31","alternatives":[{"confidence":"0.9916","content":"ない"}],"type":"pronunciation"}
            ]
      },
"status":"COMPLETED"
}

あとは、いろいろ遊んでみてください!(*´꒳`*)

サンプルサイト

repl.itにここまでに作成した内容を残しておきます。左側にあるCodeタブを選択すると、ファイル構成やファイルの内容を確認できます。

今回は、repl.itだとAmazon S3へのアクセス権がないので、実行しても詳細ページを表示できません…。

なので実行結果の画像を貼っておきます。

スクリーンショット 2021-04-09 22.19.12

スクリーンショット 2021-04-09 22.19.47

※repl.itからAmazon Transcribeに接続できないので、データの追加/変更はできません。

もし試したい方は、自分の環境にサンプルサイトをダウンロードして、awscliや環境変数にあなたのアクセスキーIDやシークレットアクセスキーを設定してください。

最後に

最初の料金は1 秒あたり 0.0004USDなので、1分で0.024USD、60分で1.44USDです。約150円ぐらいでしょうか。普通に議事録を作るよりはずっと安そうですね。

精度は…うーん、音声や内容にもよるんだろうけど、まだまだなのかな?精度を上げる設定もあるので、これだけじゃまだわかりませんが…。

今回はたぶん邪智暴虐が悪さをしているんだと思います(笑)


ここまで読んでいただけたなら、”スキ”ボタンを押していただけると励みになります!(*´ー`*)ワクワク







この記事が気に入ったらサポートをしてみませんか?