表題のままです
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も
検証目的のための構成です
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
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
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
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',
]);
VoiceId
はLanguageCode
で使用可能なものが決まっている模様
テストを実行するとphp/test
配下にmp3
ファイルが出力されるので、再生して確認
Pollyで再生した音声はキャッシュしてる
追加コストなしで再生できるのは良い