初学者向け「Amazon Polly」(AI音声合成サービス)をPythonで利用するにはAWSチートシート

AWS活用における便利な小技を簡潔に紹介する連載「AWSチートシート」。今回は、AWSのAI音声合成サービス「Amazon Polly」をPythonで利用します。

» 2021年03月02日 05時00分 公開
[金晟基東京ITスクール]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

 「Amazon Web Services」(AWS)活用における便利な小技を簡潔に紹介する連載「AWSチートシート」。今回は、AWSのAI音声合成サービス「Amazon Polly」をPythonで利用します。以下、Pollyに用意されているメソッドを概観し、幾つかの使い方を紹介します。

「Amazon Polly」とは

 AWSには、事前トレーニング済みのAIを手軽に利用できる「AIサービス」が多数用意されており、その内容はコンピュータビジョンから言語、レコメンデーション、予測と多岐にわたります。

 今回紹介するAmazon Polly(以下、Polly)は、テキストをリアルな音声スピーチに変換するAIクラウドサービスです。サポート言語は日本語や英語など、2021年2月の本稿執筆時点で28種類が提供されており、アメリカ英語やイギリス英語など、細分化されている言語もあります。

 各言語を話す話者は音声IDで識別され、これは現在、62種類あります。多くのサポート言語では男性、女性の音声が提供されており、日本語では「Takumi」と「Mizuki」がそれに当たります。ちなみに「Polly」とは、英米でオウム(Parrot)によく付けられる名前です。

AWSの「AIサービス」はコンソール画面から利用できますが、開発を念頭に置かなくても、慣れてくれば今回のようにAPIを利用する方がより便利で効率的に感じてくるでしょう。本稿がそのように利用するきっかけになれば幸いです。


利用料金

 Pollyは従量課金制で、処理したテキストの文字数に応じて毎月課金されます。標準音声の料金は音声文字のリクエスト100万字に対して4.00ドル、より高品質なニューラル音声(日本語は未対応)は100万字に対して16.00ドルです。

 なおPollyは、無料利用枠の対象になっており、標準音声は最初のリクエストから12カ月間は、1カ月当たり500万字まで、ニューラル音声は100万字まで、無料で利用できます(ただし、無料利用枠を超えた場合には従量課金が適用されます)。

必要条件

 本稿では読者の環境で下記要件が満たされていることを仮定しています。

  1. AWSアカウントを有しており、「AWS Identity and Access Management」(IAM)ユーザーに必要な権限(今回であればPollyと「Amazon S3」関連)が付与されていること。ホームディレクトリにAPIを利用するための認証情報が保存されていること
  2. AWSが提供するPython用のSDK「Boto3」がインストールされていること

 なおこれは必須ではありませんが、以下のサンプルコードは「Jupyter Notebook」での実行を想定しています。

メソッド一覧

 Pollyには下記のメソッドが用意されています。

メソッド名 機能 引数 戻り値
describe_voice 利用可能な音声一覧を返す 言語コードなど 辞書
synthesize_speech テキストまたはSSMLを音声合成する テキスト、言語コード、音声ID、出力形式など 辞書
start_speech_synthesis_task 非同期音声合成タスクを生成する 上記に加え、書き出し先S3バケット名など 辞書
list_speech_synthesis_tasks 音声合成タスク情報一覧を返す 最大値・タスクの状態 辞書
get_speech_synthesis_task 特定の音声合成タスク情報を取得する タスクID 辞書
put_lexicon レキシコンを保存する レキシコン名とレキシコン 辞書
list_lexicons レキシコン一覧を返す - 辞書
get_lexicon 特定のレキシコンの内容を返す レキシコン名 辞書
delete_lexicon 特定のレキシコンを削除する レキシコン名 辞書
can_paginate 各メソッドのページネーションの有無を調べる メソッド名 真偽値
get_paginator メソッドに関するページネータを生成する メソッド名 ページネータオブジェクト
generate_presigned_url 署名済みURLを返す メソッドとその引数 署名済みURL

 Pollyでは3000文字以内の場合、「synthesize_speech」メソッドを用いて音声合成ができます。また、この戻り値はすぐに取得できます。一方で3000文字を超える場合(最大10万文字まで)は、「start_speech_synthesis_task」という非同期音声合成タスクを実行します。この場合、音声合成の結果は指定したS3バケットに保存されます。

 SSMLとは音声合成マークアップ言語(Speech Synthesis Markup Language)の略称で、これを用いるとナレーションの声量や抑揚などを局所的に細かくコントロールできます。

 レキシコンとは語彙(ごい)集のことです。これを登録することで、略字やPollyが誤読する単語について、利用者がPollyの発音を直接指定できます。

 ここからはPollyのメソッドを用いて音声合成をしてみましょう。

synthesize_speechメソッド

 「synthesize_speech」メソッドは、Pollyの最も基本的なメソッドです。これはプレーンテキストまたはSSMLのUTF-8入力を、音声ファイルに変換します。

import boto3
from contextlib import closing
import os
import sys
import subprocess
 
# Pollyクライアントを作成
polly = boto3.client('polly')
 
# 入力テキスト
input_text='ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。'
 
# 音声合成を実行
response = polly.synthesize_speech(
                    # 言語コード(言語名コード-使用国名コード)
                    LanguageCode='ja-JP',
                    # 使用する登録済みレキシコン名
                    # LexiconNames = [lexicon_name],
                    # 出力形式
                    OutputFormat='mp3',
                    # 音声変換するテキスト
                    Text=input_text,
                    # 音声ID
                    VoiceId='Takumi')
 
# 音声ストリームを保存
output = 'polly_sample.mp3'
with closing(response['AudioStream']) as stream:
    with open(output, 'wb') as file:
        file.write(stream.read())
 
# 各OSのデフォルトプレーヤーでオーディオを再生
if sys.platform == 'win32':  # Windows
    os.startfile(output)
else:
    # macOS ('darwin') またはLinux
    opener = 'open' if sys.platform == 'darwin' else 'xdg-open'
    subprocess.call([opener, output])

 いかがでしょうか。発音や声の抑揚はかなり自然な仕上がりになっていると思います。ただ、今回生成されたナレーションの中で、Takumiは「一人(ひとり)」を「かずと」と読み、「雨やみ(あまやみ)」を「あめやみ」と読んでしまいました。そこで、以下ではレキシコンを登録することで、この誤読を修正します(なお、この挙動は2021年2月の本稿執筆時点であり、今後変わる可能性があります)。

put_lexiconメソッド

 まずはレキシコンを「Pronunciation Lexicon Specification」(PLS)という形式にのっとって作成します。下記コード例のように、ユーザーは追加したい語彙ごとにlexeme(語彙素)タグを用意し、grapheme(書記素)タグとalias(別名)タグを用いて、対象語彙とその発音をマッピングします。

 なお、ここでは便宜上ソースコードに直接レキシコンを書いています。一般的には、同内容をテキストファイルで作成し、拡張子に「.pls」を使用して保存したものを読み込みます。また、音声合成の対象となる文章自体も、長くなればテキストファイルから読み込むのが一般的でしょう。

lexicon_name = 'Rashomon'
lexicon_content = '''\
<?xml version="1.0" encoding="UTF-8"?>
<lexicon version="1.0"
    xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon
    http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"
    alphabet="ipa"
    xml:lang="ja-JP">
    <lexeme>
        <grapheme>一人</grapheme>
        <alias>ひとり</alias>
    </lexeme>
    <lexeme>
        <grapheme>雨やみ</grapheme>
        <alias>あまやみ</alias>
    </lexeme>
</lexicon>
'''

 それでは、put_lexiconメソッドを用いて、AWSリージョンに発音レキシコンを保存しましょう。なお、同名のレキシコンが該当リージョンに既に存在する場合は、新しいレキシコンで内容が上書きされます。

# レキシコンを登録
polly.put_lexicon(
        # レキシコン名
        Name=lexicon_name,
        # 登録するレキシコンの内容
        Content=lexicon_content)

 登録できたら、先ほどの音声合成サンプルコードを再度実行してみましょう。ただし、今回はsynthesize_speechメソッドの引数の中でコメントアウトされていた、レキシコンに関する行を有効にして実行してください。全て正しく発音されると思います。

その他のメソッド

 最後に、Polly(とS3)のメソッドを幾つかまとめて使います。今回は3000文字以上の音声合成を念頭に置いて、start_speech_synthesis_taskメソッドを利用した非同期音声合成タスクを実行してみます。

 手順は次の通りです。まず、S3に書き出し用のバケットを作成します。そして、start_speech_synthesis_taskメソッドを利用して、非同期音声合成タスクを開始します。非同期合成タスクを開始したら、S3のwaiterを利用して、対象オブジェクトがS3へ書き出されるまで待機します。書き出しが終了したら、オブジェクトをS3からローカルに保存して、最後にS3バケットとレキシコンを削除します。

import uuid
 
# 一意識別子を利用してバケット名を作成
bucket = 'polly-sample-' + str(uuid.uuid1())
 
# S3クライアントを作成
s3 = boto3.client('s3')
 
# S3にバケットを作成
s3.create_bucket(
    Bucket=bucket,
    CreateBucketConfiguration={'LocationConstraint': 'ap-northeast-1'})
 
input_text = 'ある日の暮方の事である。一人の下人が、羅生門の下で雨やみを待っていた。'
 
# 非同期音声合成タスクを開始
response = polly.start_speech_synthesis_task(
                    # 言語コード(言語名コード-使用国名コード)
                    LanguageCode='ja-JP',
                    # 使用する登録済みレキシコン名
                    LexiconNames=[lexicon_name],
                    # 出力形式
                    OutputFormat='mp3',
                    # 出力先S3バケット
                    OutputS3BucketName=bucket,
                    # 音声変換するテキスト
                    Text=input_text,
                    # 音声ID
                    VoiceId='Takumi')
 
# S3に書き出されるオブジェクトのキー
object_key = f"{response['SynthesisTask']['TaskId']}.mp3"
 
# S3への書き出しが終わるまで待機
waiter = s3.get_waiter('object_exists')
waiter.wait(Bucket=bucket, Key=object_key)
 
# S3からオブジェクトを取得
output_file = s3.get_object(Bucket=bucket, Key=object_key)
 
# 音声ストリームを保存
output = 'polly_sample2.mp3'
with closing(output_file['Body']) as stream:
    with open(output, 'wb') as file:
        file.write(stream.read())
 
# 各OSのデフォルトプレーヤーでオーディオを再生
if sys.platform == 'win32':  # Windows
    os.startfile(output)
else:
    # macOS ('darwin') またはLinux
    opener = 'open' if sys.platform == 'darwin' else 'xdg-open'
    subprocess.call([opener, output])
 
# S3バケット内のオブジェクトを削除
s3.delete_object(Bucket=bucket, Key=object_key)
 
# S3バケットを削除
s3.delete_bucket(Bucket=bucket)
 
# レキシコンを削除
polly.delete_lexicon(Name=lexicon_name)

最後に

 いかがだったでしょうか。音声合成は、このような簡単なサンプルでも十分楽しめますが、各種アプリケーションや動画での利用など、実用的なユースケースも数多く考えられます。今回は必要最低限のメソッドのみ紹介しましたが、興味を持った方は公式ドキュメントを参考に、その他のメソッドも試してみてください。

筆者紹介

金 晟基(キム ソンギ)

株式会社システムシェアード

東京ITスクールでJava研修の講師、IT専門学校の教材、カリキュラム開発、一般社団法人とのプログラミング教育を通じた国際貢献事業などを担当。AWS認定資格は「機械学習 - 専門知識」など


Copyright © ITmedia, Inc. All Rights Reserved.

スポンサーからのお知らせPR

注目のテーマ

Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。