Rubyで深層学習を使った音声合成Amazon Pollyを使ってWebサイトの読み上げ音声合成してみた

今日のre:InventでDeep Learningを使った音声合成サービスのAmazon Pollyが発表されました。 正直、DLを使ったの音声合成が話題になったのなんて今年に入ってからだと思っていたのに、もう商用化したんか!という気持ちでいっぱいです。
[2016/12/02追記]
PollyがDLベースという話は https://aws.amazon.com/jp/polly/ に、“Polly is an Amazon AI service that uses advanced deep learning technologies to synthesize speech that sounds like a human voice. “と書かれていますが、DNNを一部のモジュールとして使っている波形接続型じゃないの?とのことです。なので、「DLを使った」という表現に修正しました。
Amazon Polly sounds like concatenative TTS, neither parametric nor WaveNet. I can hear concatenation glitches in its samples.
— Heiga Zen (全 炳河) (@heiga_zen) December 1, 2016
多分DNNをどこかのモジュールで使ってる単位選択型です。Appleもそうだったりします。
— Heiga Zen (全 炳河) (@heiga_zen) December 2, 2016
なので、DeepMindのWaveNetのように音声を直接生成しているわけではなさそうです。
[/追記]
Amazon PollyはAmazon AIのサービスとして展開していますが、画像認識(Amazon Rekognition)、音声合成(Amazon Polly)、音声認識と自然言語理解という対話用のコンポーネント(Amazon Lex)が使えるようになりました。 画像認識についてはGoogleのVision APIで話題になりましたが、その他の2つはちょっと変わっていると思いますがこれはおそらくSiriのような対話エージェントであるAmazon Alexaのバックエンドを切り売りしているんだと思います。
Pollyが凄いのは、AWS CLIで簡単に音声合成がされるということです。しかも、お値段は月500万字までは無料で、その後も$0.000004/文字と非常に安く、本一冊で$2.4くらいという驚異的な安さです。なので、ちょっと前にrebuild.fmでスクリプト書くだけで音声合成でpodcastできるんじゃないみたいな話題がありましたが、おもったより速くその現実が来るのかもしれません。
AWS CLIを使ったbashのサンプルコードはこんな感じです。かんたんでしょ?
$ aws polly synthesize-speech \
--output-format mp3 --voice-id Joanna \
--text "Hello my name is Joanna." \
joanna.mp3
また、サポートしている言語数も2016/12/01現在で、ヨーロッパ言語を中心に以下の24言語をサポートしています。
- アイスランド語
- イタリア語
- ウェールズ語
- オランダ語
- スウェーデン語
- スペイン語 (カスティリヤ)
- スペイン語 (米国)
- デンマーク語
- トルコ語
- ドイツ語
- ノルウェー語
- フランス語
- フランス語 (カナダ)
- ポルトガル語
- ポルトガル語 (ブラジル)
- ポーランド語
- ルーマニア語
- ロシア語
- 日本語
- 英語 (インド)
- 英語 (ウェールズ)
- 英語 (オーストラリア)
- 英語 (米国)
- 英語 (英国)
日本語も聞いていて結構自然に聞こえており、ちょいちょい単語の認識に失敗するときは変なアクセントになりますが、レキシコンで単語を登録すれば自分で改善もできそうです。 サンプル音声はこんな感じです。
http://chezou.tumblr.com/post/153883804175/amazon
chezou.tumblr.com[2016/12/02追記] Google翻訳に手伝ってもらって英語版をMediumに書きました。それの生成した音声も貼っておきます。
soundcloud.com[/追記]
で、Mediumなんかの記事の中に面白い記事をよく見るのですが、英文だとなかなか途中で挫折するので、音声にすれば聞くんじゃないかと思い、記事を音声に変換するコードをRubyで書いてみました。 以下にコードがあります。
source 'https://rubygems.org' | |
gem 'nokogiri', '~>1.6' | |
gem 'aws-sdk', '~> 2' |
require 'aws-sdk' | |
require 'nokogiri' | |
require 'open-uri' | |
class Synthesizer | |
def initialize(region='us-east-1') | |
@polly = Aws::Polly::Client.new(region: region) | |
end | |
def synthesize(text, file_name="./tmp.mp3", voice_id="Joanna") | |
@polly.synthesize_speech( | |
response_target: file_name, | |
text: text, | |
output_format: "mp3", | |
# You can use voice IDs http://docs.aws.amazon.com/polly/latest/dg/API_Voice.html | |
# If you want to synthesize Japanese voice, you can use "Mizuki" | |
voice_id: voice_id | |
) | |
end | |
end | |
module TextFetcher | |
def self.fetch_text_from(url, xpath) | |
charset = nil | |
html = open(url) do |f| | |
charset = f.charset | |
f.read | |
end | |
doc = Nokogiri::HTML.parse(html, nil, charset) | |
node_texts = doc.xpath(xpath).map(&:text) | |
combined_texts = [] | |
tmp_string = "" | |
node_texts.each do |text| | |
if tmp_string.size + text.size > 1500 | |
combined_texts << tmp_string | |
tmp_string = text | |
else | |
tmp_string << " #{text}" | |
end | |
end | |
combined_texts << tmp_string | |
end | |
end | |
if __FILE__ == $0 | |
synthesizer = Synthesizer.new | |
url = "https://medium.com/@chezou/building-predictive-model-with-ibis-impala-and-scikit-learn-356b41f404e0#.xeiwrmhhb" | |
# This XPath assumes medium contents | |
xpath = '//section//text()' | |
input_texts = TextFetcher.fetch_text_from(url, xpath) | |
input_texts.each.with_index do |text, i| | |
synthesizer.synthesize(text, "./tmp_#{i}.mp3") | |
sleep(1) | |
end | |
# You can combine mp3 with cat on Linux based system | |
`cat ./tmp_*.mp3 > ./combined.mp3` | |
end |
ポイントとしては、幾つか重要な制約があります。
- API一回あたりの文字数が1500字1(なので、catでmp3を結合している)
- 長い音声は、5分以降が切り捨てられる
詳細は以下を参考にしてください。docs.aws.amazon.com
実際には、ちょうどHckr newsで見つけた以下の記事の音声を聞いてみました。意外と聞けます。
How the Circle Line rogue train was caught with data
もうちょっと頑張ってRSSを取得すれば、特定のサイトの最新の記事の音声を生成して、Dropboxに保存した音声をモバイルから再生するということもできそうですね。
正直、安くて多言語でそれなりに自然で何よりAPIが使いやすいということで、既存の日本の音声合成を頑張ってきた企業は大変だなぁという気持ちになりますが、いろいろな使い方ができそうで楽しみです。
厳密には、“1500 billed characters (3000 total characters)“と書いてあるけど"billed characters"がわからない ↩︎