LoginSignup
4
5

More than 5 years have passed since last update.

適当な日本語文章を形態素解析し、英語に翻訳して音声読み上げさせる

Posted at

表題のままです

Elasticsearchで日本語文章をバラバラの単語に分解して、そいつを AWS Translate で翻訳。
翻訳した単語を今度は AWS Polly で読み上げさせる(音声ファイル出力)サンプルです。

環境・コードは以下に置いてます

軽くリポジトリ内の説明を

elasticsearch-test
├── README.md
├── docker
│   └── elasticsearch      # Elasticsearchコンテナイメージ定義
│        ├── Dockerfile
│        └── config
├── docker-compose.yml     # dockerコンテナ定義
└── php
    ├── composer.json
    ├── composer.lock
    ├── composer.phar
    └── test               # テストコードディレクトリ

ついでにdocker-compose.ymlも

検証目的のための構成です

docker-compose.yml
version: '3'
services:
  elasticsearch:
    build: ./docker/elasticsearch
    volumes:
    - ./docker/elasticsearch/config:/usr/share/elasticsearch/config
    ports:
    - 9200:9200
    expose:
    - 9300
    environment:
    - discovery.type=single-node
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
  kibana:
    image: docker.elastic.co/kibana/kibana:6.4.2
    depends_on:
    - elasticsearch
    ports:
    - 5601:5601

コンテナ立てた後はcomposerインストールを実行して、 .env ファイルも作成しておいてください
.env にはご自身のAWSユーザーの access_key と secret_access_key を設定してください。

Elasticsearchのアナライザの設定

日本語の形態素解析にはkuromojiを利用しますが、辞書の更新が止まっているので今回はneologdを使用します。

プラグインは docker/elasticsearch/Dockerfile 内ですでにインストールしてますので、後は設定だけです。
今回は以下のように設定しました。

curl -H 'Content-Type: application/json' -XPUT 'http://localhost:9200/neologd/?pretty' -d'
{
    "settings": {
        "index":{
            "analysis":{
                "analyzer" : {
                    "default" : {
                        "tokenizer" : "kuromoji_neologd_tokenizer"
                    }
                }
            }
        }
    }
}'

※設定されているかの確認はkibanaで行いました

動作確認

PHPUnitでテストコードを書いてます

php/test/ElasticsearchTest.php
<?php

require dirname(__DIR__) . '/vendor/autoload.php';

class ElasticsearchTest extends PHPUnit\Framework\TestCase
{
    public function test_neologdA()
    {
        $client = new \GuzzleHttp\Client([
            'base_uri' => 'http://localhost:9200'
        ]);
        $response = $client->request('GET', '/neologd/_analyze', [
            'json' => ['text' => 'こんにちは世界'],
        ]);

        self::assertEquals(200, $response->getStatusCode());
        $expected = '{"tokens":[{"token":"こんにちは","start_offset":0,"end_offset":5,"type":"word","position":0},{"token":"世界","start_offset":5,"end_offset":7,"type":"word","position":1}]}';
        self::assertJsonStringEqualsJsonString($expected, $response->getBody()->getContents());
    }

... 以下略

『こんにちは世界』が 『こんにちは』『世界』 で別々の単語で返ってたらテストOK

で、テスト実行...

$ ./php/vendor/bin/phpunit php/test/ElasticsearchTest.php
PHPUnit 7.4.0 by Sebastian Bergmann and contributors.

....                                                                4 / 4 (100%)

Time: 84 ms, Memory: 4.00MB

OK (4 tests, 16 assertions)

\(^o^)/トオタ

AWS Translate の確認

ドキュメント: Class Aws\Translate\TranslateClient | AWS SDK for PHP 3.x

php/test/TranslateTest.php
<?php

require dirname(__DIR__) . '/vendor/autoload.php';

class TranslateTest extends PHPUnit\Framework\TestCase
{
    public static function setUpBeforeClass()
    {
        parent::setUpBeforeClass();

        (new Dotenv\Dotenv(dirname(__DIR__)))->load();
    }

...  ...

    public function test_translateTextC()
    {
        $client = new \GuzzleHttp\Client([
            'base_uri' => 'http://localhost:9200'
        ]);
        $response = $client->request('GET', '/neologd/_analyze', [
            'json' => ['text' => 'こんにちは世界'],
        ]);
        self::assertEquals(200, $response->getStatusCode());
        $json = json_decode($response->getBody()->getContents());

        $credentials = new Aws\Credentials\Credentials(
            getenv('aws_access_key'),
            getenv('aws_secret_access_key')
        );
        $client = new Aws\Translate\TranslateClient([
            'region' => 'us-east-1',
            'version' => 'latest',
            'credentials' => $credentials,
        ]);

        foreach ($json->tokens as $token) {
            $result = $client->translateText([
                'SourceLanguageCode' => 'ja',
                'TargetLanguageCode' => 'en',
                'Text' => $token->token,
            ]);
            self::assertNotEmpty($result->get('TranslatedText'));
            echo sprintf("\n%s => %s", $token->token, $result->get('TranslatedText'));
        }
    }
}

↓で対象の言語に翻訳

$result = $client->translateText([
    'SourceLanguageCode' => 'ja',
    'TargetLanguageCode' => 'en',
    'Text' => $token->token,
]);

テストを実行すると

こんにちは => Hello
世界 => World

が表示されるはず

AWS Polly の確認

ドキュメント: Class Aws\Polly\PollyClient | AWS SDK for PHP 3.x

php/test/PollyTest.php
<?php

require dirname(__DIR__) . '/vendor/autoload.php';

class PollyTest extends PHPUnit\Framework\TestCase
{
    public static function setUpBeforeClass()
    {
        parent::setUpBeforeClass();

        (new Dotenv\Dotenv(dirname(__DIR__)))->load();
    }

...  ...

    public function test_pollyB()
    {
        $client = new \GuzzleHttp\Client([
            'base_uri' => 'http://localhost:9200'
        ]);
        $response = $client->request('GET', '/neologd/_analyze', [
            'json' => ['text' => 'こんにちは世界'],
        ]);
        self::assertEquals(200, $response->getStatusCode());
        $json = json_decode($response->getBody()->getContents());

        $credentials = new Aws\Credentials\Credentials(
            getenv('aws_access_key'),
            getenv('aws_secret_access_key')
        );
        $translateClient = new Aws\Translate\TranslateClient([
            'region' => 'us-east-1',
            'version' => 'latest',
            'credentials' => $credentials,
        ]);
        $pollyClient = new Aws\Polly\PollyClient([
            'region' => 'us-east-1',
            'version' => 'latest',
            'credentials' => $credentials,
        ]);

        foreach ($json->tokens as $token) {
            $text = $translateClient->translateText([
                'SourceLanguageCode' => 'ja',
                'TargetLanguageCode' => 'en',
                'Text' => $token->token,
            ]);
            self::assertNotEmpty($text->get('TranslatedText'));

            $speech = $pollyClient->synthesizeSpeech([
                'LanguageCode' => 'en-US',
                'OutputFormat' => 'mp3',
                'Text' => $text->get('TranslatedText'),
                'TextType' => 'text',
                'VoiceId' => 'Salli',
            ]);

            /** @var \GuzzleHttp\Psr7\Stream $stream */
            $stream = $speech->get('AudioStream');
            self::assertTrue($stream instanceof \GuzzleHttp\Psr7\Stream);
            self::assertNotFalse(file_put_contents(sprintf("%s/test_pollyB_%s.mp3", __DIR__, $text->get('TranslatedText')), $stream));
        }
    }
}

↓で音声に変換

$speech = $pollyClient->synthesizeSpeech([
    'LanguageCode' => 'en-US',
    'OutputFormat' => 'mp3',
    'Text' => $text->get('TranslatedText'),
    'TextType' => 'text',
    'VoiceId' => 'Salli',
]);

VoiceIdLanguageCodeで使用可能なものが決まっている模様

テストを実行するとphp/test配下にmp3ファイルが出力されるので、再生して確認


Pollyで再生した音声はキャッシュしてる
追加コストなしで再生できるのは良い

4
5
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
4
5