ENGINEER BLOG

ENGINEER BLOG

音声認識APIたたいてみた

こんにちは。
Amazon Echo doteRemote miniで若干のスマートホーム感を楽しんでいる笹野です。

布団の中から「アレクサー ◯◯つけてー(けしてー)」と唱えれば、
照明、エアコン、テレビ(赤外線リモコンの物ならなんでも)をON(OFF)できるのがこの季節は地味に便利です。

今回は、音声認識(音声→テキスト変換)APIを試してみました。

はじめに

「音声認識API?なにそれおいしいの?」的な人向けです。雰囲気を味わっていただければと思います。

とりあえずAPIたたいて、認識結果を得られるようにするところまで行っています。

「へえ〜」「たのしいね〜」って思っていただければ幸いです。

概要

【試したAPI】

  1. Cloud Speech API (Google)
  2. Bing Speech API (Azure)
  3. Speech to Text (IBM)

以下の環境で各APIに音声ファイルを投げて、認識結果(テキスト)を取得しています。

【環境】

【その他】

  • 音声ファイルを Sound of Text で作成、音声認識させました。(PCのマイクから録音するのがめんd…)
    宮沢賢治の「雨ニモマケズ」の冒頭を使用しました。

1. Cloud Speech API (Google)

公式ドキュメントが日本語かつ分かりやすいので、こちらを参考に進めました。
GCPとPCの準備はドキュメントに沿って行ったので省略します。

Google Cloud Speech API ドキュメント

実行するスクリプトを作成します。
(今回はこちらを使用させていただきました → googleapis/nodejs-speech/quickstart.js(GitHub)

  • Project ID(GCPコンソール画面で確認する)
  • 音声ファイルのパス
  • 音声ファイルの形式
  • 言語設定

を自分の環境に合わせて修正します。

// Imports the Google Cloud client library
const speech = require('@google-cloud/speech');
const fs = require('fs');

// Your Google Cloud Platform project ID
const projectId = '<<< project ID >>>';        //Project IDを指定

// Creates a client
const client = new speech.SpeechClient({
  projectId: projectId,
});

// The name of the audio file to transcribe
const fileName = '<<< audiofile path >>>';      //audiofileのパスを指定

// Reads a local audio file and converts it to base64
const file = fs.readFileSync(fileName);
const audioBytes = file.toString('base64');

// The audio file's encoding, sample rate in hertz, and BCP-47 language code
const audio = {
  content: audioBytes,
};
const config = {
  encoding: 'FLAC',             //audiofileの形式に合わせる
  sampleRateHertz: 16000,
  // languageCode: 'en-US',        //言語指定
  languageCode: 'ja-JP',
};
const request = {
  audio: audio,
  config: config,
};

// Detects speech in the audio file
client
  .recognize(request)
  .then(data => {
    const response = data[0];
    const transcription = response.results
      .map(result => result.alternatives[0].transcript);
    console.log(`Transcription: ${transcription}\n`);
  })
  .catch(err => {
    console.error('ERROR:', err);
  });

上記で作成したスクリプトを実行して、音声認識してみます。

$ node ~/quickstart.js

実行すると以下のように音声ファイルを解析した結果がテキストで返ってきます。
何故かカタカナ多めですが、内容はほぼ正しく認識されました。

Transcription: アメニモマケズカゼニモマケズ雪にも夏の暑さニモマケズ

quickstart.jsを以下のように修正すると、

// Detects speech in the audio file
client
  .recognize(request)
  .then(data => {
    const response = data[0];
    const transcription = response.results
      .map(result => result.alternatives[0].transcript);
    const confidence = response.results
      .map(result => result.alternatives[0].confidence);
      // .join('\n');
    console.log(`Transcription: ${transcription} / Confidence: ${confidence} \n`);
  })
  .catch(err => {
    console.error('ERROR:', err);
  });

文章の認識率も表示できます。

Transcription: アメニモマケズカゼニモマケズ雪にも夏の暑さニモマケズ / Confidence: 0.9848828911781311

簡単ですね。
信頼度は 最大 1 のところ 0.98 ととても高いです。
有名な小説の冒頭部分とあってかなり自信があるようですね?(カタカナ多めで、最後「ズ」になってますが…)

というわけで、Cloud Speech APIを用いて、音声ファイルからテキストに変換することができました。

ちなみに quickstart.js では物足りないときは、多機能版の recognize.js があります。
使い方は README.md を見てよしなにお願いします。

recognize.jsの場合、私の環境では足りないパッケージがあったので、以下のコマンドでインストールをしました。

$ brew install sox
$ npm install yargs
$ npm install node-record-lpcm16 --save

また、stream を使うと引数で音声ファイルを指定できるのがいいですね。

$ node recognize.js stream <filename>

2. Bing Speech API (Azure)

次に Bing Speech API です。

公式ドキュメントは英語のみの提供になっています。

Microsoft Speech API

こちらも公式ドキュメントを読んで頑張ります。詳細な手順は省略します。
JavaScriptのサンプルが 404 ERROR で確認できなかったので、今回は Curl で行いました。
(現在は、各種サンプル確認できるようになっています!)

毎回ながーーーーいコマンドを打つのがだr…
雑ですが簡単にシェルスクリプトを作りました。これでだいぶ楽にAPIたたけます。
(FILE/LANG/MODE/FORMATを入力・選択できるようにしています。)

#!/bin/bash

echo "FILE NAME ?"
read FILE
if [ -e ${FILE} ]; then
    echo "> ${FILE} found."
else
    echo "!!! ERROR!!!"
    echo "> ${FILE} NOT found."
    exit
fi

echo "MODE ? (1.interactive / 2.conversation / 3.dictation)"
read MODE
case "${MODE}" in
    "interactive" ) ;;
    "conversation" ) ;;
    "dictation" ) ;;
    "1" ) MODE="interactive" ;;
    "2" ) MODE="conversation" ;;
    "3" ) MODE="dictation" ;;
    *   ) MODE="interactive" ;;
esac


echo "LANGUAGE ? (1.en-US or 2.ja-JP)"
read LANG
case "${LANG}" in
    "en-US" ) ;;
    "ja-JP" ) ;;
    "1" ) LANG="en-US" ;;
    "2" ) LANG="ja-JP" ;;
    *   ) LANG="en-US" ;;  
esac


echo "FORMAT ? (1.simple or 2.detailed)"
read FORM
case "${FORM}" in
    "simple" ) ;;
    "detailed" ) ;;
    "1" ) FORM="simple" ;;
    "2" ) FORM="detailed" ;;
    *   ) FORM="detailed" ;;
esac

echo "==========================="
echo "Settings"
echo "==========================="
echo "FILE = ${FILE}"
echo "MODE = ${MODE}"
echo "LANGUAGE = ${LANG}"
echo "FORMAT = ${FORM}"
echo "==========================="


curl -X POST "https://speech.platform.bing.com/speech/recognition/${MODE}/cognitiveservices/v1?language=${LANG}&format=${FORM}" \
-H "Transfer-Encoding: chunked" \
-H "Ocp-Apim-Subscription-Key:<<< your key >>>" \
-H "Content-type: audio/wav; codec=audio/pcm; samplerate=16000" \
--data-binary @${FILE} | jq

MODEは以下のような違いがあります。試してはいませんが入力する音声に適したモードを選択するのが良さそうですね。

  • interactive:機械に話していると分かって話している短文
  • dictation:機械に話していると分かって話している長文
  • conversation:人間同士の会話

また、認識結果はJSON形式で返ってきますが、FORMATの指定によってボリュームが大きく変わります。
(参考)Basic concepts – Output format

「simple」では整形されたテキストのみの表示ですが、
「detailed」では複数の認識結果を

  • “Lexical”:認識したまま
  • “ITN”:標準的な表示形式に変換
  • “Masked ITN”:冒涜的な言葉をマスキング
  • “Display”:simpleと同様な整形されたテキスト

の4つの形式で取得することができます。

マスキングする単語を指定することも可能なので、
子供向けのサービスに使用する場合に子供がに見せたくない単語を表示させないようにするのに利用できますね。

【認識結果(simple)】

{
  "RecognitionStatus": "Success",
  "DisplayText": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けね",
  "Offset": 5600000,
  "Duration": 63100000
}

【認識結果(detailed)】

{
  "RecognitionStatus": "Success",
  "Offset": 5600000,
  "Duration": 63100000,
  "NBest": [
    {
      "Confidence": 0.8847888,
      "Lexical": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けね",
      "ITN": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けね",
      "MaskedITN": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けね",
      "Display": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けね"
    },
    {
      "Confidence": 0.8789037,
      "Lexical": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けぬ",
      "ITN": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けぬ",
      "MaskedITN": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けぬ",
      "Display": "雨にも負けず風ニモマケズ雪にも夏の暑さにも負けぬ"
    },
    {
      "Confidence": 0.857748866,
      "Lexical": "雨にも負けず風にも負けず雪にも夏の暑さにも負けね",
      "ITN": "雨にも負けず風にも負けず雪にも夏の暑さにも負けね",
      "MaskedITN": "雨にも負けず風にも負けず雪にも夏の暑さにも負けね",
      "Display": "雨にも負けず風にも負けず雪にも夏の暑さにも負けね"
    },
    {
      "Confidence": 0.857748866,
      "Lexical": "雨にも負けず風ニモマケズ雪ニモ夏の暑さにも負けね",
      "ITN": "雨にも負けず風ニモマケズ雪ニモ夏の暑さにも負けね",
      "MaskedITN": "雨にも負けず風ニモマケズ雪ニモ夏の暑さにも負けね",
      "Display": "雨にも負けず風ニモマケズ雪ニモ夏の暑さにも負けね"
    },
    {
      "Confidence": 0.851863444,
      "Lexical": "雨にも負けず風にも負けず雪にも夏の暑さにも負けぬ",
      "ITN": "雨にも負けず風にも負けず雪にも夏の暑さにも負けぬ",
      "MaskedITN": "雨にも負けず風にも負けず雪にも夏の暑さにも負けぬ",
      "Display": "雨にも負けず風にも負けず雪にも夏の暑さにも負けぬ"
    }
  ]
}

detailedで結果表示させたものは、
第1候補は “雨にも負けず風ニモマケズ雪にも夏の暑さにも負けね” になっていますが、
第2候補は “雨にも負けず風ニモマケズ雪にも夏の暑さにも負けぬ” と正しい結果が返ってきています!やったね!
(合成した音声を確認するとなかなか微妙な発音です…)

3. Speech to Text (IBM)

さいごに、IBM の Speech to Text です。
こちらは IBM Cloudライト・アカウントで無料で使うことができます。
制限事項がありますが、クレジットカードの登録無しで無料で使えるのは嬉しいです!

例によって、公式ドキュメントはこちら。英語です。頑張ります。
IBM Cloud Docs – Speech to Text » Getting started tutorial
speech-to-text – API Reference
» Watson Developer Cloud · GitHub

スクリプト(Javascript)と認識結果は以下のような感じです。
まずはシンプルに「文章」と「結果全体の信頼度」だけを取得してみました。

【スクリプト①】

var SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
var fs = require('fs');

var speech_to_text = new SpeechToTextV1({
  username: '<<< username >>>',
  password: '<<< password >>>'
});

var params = {
  model: 'ja-JP_BroadbandModel',

// From file
  audio: fs.createReadStream('<<< audio file path >>>'),
  content_type: 'audio/l16; rate=16000'
};

speech_to_text.recognize(params, function(err, res) {
  if (err)
    console.log(err);
  else
    console.log(JSON.stringify(res, null, 2));
});

【認識結果①】

{
  "results": [
    {
      "alternatives": [
        {
          "confidence": 0.964,
          "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 ニモ 夏 の 暑さ にも 負け ね "
        }
      ],
      "final": true
    }
  ],
  "result_index": 0
}

前半は「漢字+カタカナ」、後半は「漢字+ひらがな」になってます…
そして、Azure同様、最後の「ぬ」が「ね」になってしまっていますが、おおむね合っている認識結果が返ってきています。

また、IBM の Text to Speech では “param” を指定することで、
更に詳細な情報を取得することもできるので以下の3つを指定してみました。

  • max_alternatives:認識結果候補の表示数を指定
  • word_confidence:単語毎の信頼度を表示する
  • timestamps:単語が発話されている時間(start/end)を表示する

次のスクリプト②では、最大候補数を3 で単語毎の信頼度と時間を表示させています。

【スクリプト②】

'use strict';

var SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
var fs = require('fs');

var speech_to_text = new SpeechToTextV1({
  username: '<<< username >>>',
  password: '<<< password >>>'
});

var params = {
  model: 'ja-JP_BroadbandModel',
  audio: fs.createReadStream('<<< audio file path >>>'),
  content_type: 'audio/flac',
  'max_alternatives': 3,
  'word_confidence': true,
  timestamps: true

};

speech_to_text.recognize(params, function (error, transcript) {
  if (error)
      console.log('Error:', error);
  else
      console.log(JSON.stringify(transcript, null, 2));
});

【認識結果②】

{
  "results": [
    {
      "alternatives": [
        {
          "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 ニモ 夏 の 暑さ にも 負け ね ",
          "confidence": 0.953,
          "word_confidence": [
            [
              "雨",
              1
            ],
            [
              "ニモマケズ",
              1
            ],
            [
              "風",
              1
            ],
            [
              "ニモマケズ",
              0.997
            ],
            [
              "雪",
              0.941
            ],
            [
              "ニモ",
              0.828
            ],
            [
              "夏",
              1
            ],
            [
              "の",
              1
            ],
            [
              "暑さ",
              1
            ],
            [
              "にも",
              1
            ],
            [
              "負け",
              1
            ],
            [
              "ね",
              0.511
            ]
          ],
          "timestamps": [
            [
              "雨",
              0.51,
              0.82
            ],
            [
              "ニモマケズ",
              0.82,
              1.89
            ],
            [
              "風",
              2.08,
              2.44
            ],
            [
              "ニモマケズ",
              2.44,
              3.5
            ],
            [
              "雪",
              3.54,
              4.08
            ],
            [
              "ニモ",
              4.08,
              4.57
            ],
            [
              "夏",
              4.78,
              5.15
            ],
            [
              "の",
              5.15,
              5.3
            ],
            [
              "暑さ",
              5.3,
              5.77
            ],
            [
              "にも",
              5.77,
              6.09
            ],
            [
              "負け",
              6.09,
              6.45
            ],
            [
              "ね",
              6.45,
              6.76
            ]
          ]
        },
        {
          "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 にも 夏 の 暑さ にも 負け ね "
        },
        {
          "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 ニモ 夏 の 暑さ にも 負け ねえ "
        }
      ],
      "final": true
    }
  ],
  "result_index": 0
}

文章全体の信頼度だけでなく、単語毎の信頼度と発話時間も表示されるようになりました。
単語の信頼度は全体的に高い値が出ていますが、最後の「ね」は低い値となっています。

また、文章全体の認識結果が3つ表示されています。

(認識結果から抜粋)

  "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 ニモ 夏 の 暑さ にも 負け ね "
  "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 にも 夏 の 暑さ にも 負け ね "
  "transcript": "雨 ニモマケズ 風 ニモマケズ 雪 ニモ 夏 の 暑さ にも 負け ねえ "

今回は全体的に認識率が高かったのもあってか、ひらがなorカタカナでも1候補となっています。
「ぬ」にはたどり着けなかったようです…残念…

単語ごとに信頼度を得られることで、どの単語の認識精度が甘いかが分かります。
さらには、単語毎の発話時間から、単語毎に元の音声ファイルを分割したりできると思うので、夢が広がりそうです!

まとめ

  1. Cloud Speech API (Google)

    • 認識結果は1パターンのみ。
    • 認識結果全体の信頼度が取得できる。
    • ドキュメントが日本語で分かりやすい。
    • C#/Go/Java/Node.js/PHP/Python/Rubyと多くの言語のライブラリを提供。
  2. Bing Speech API (Azure)

    • 認識結果はsimple(1パターン)とdetailed(複数パターン)で取得できる。
    • 認識結果全体の信頼度が取得できる。
    • 機械に話している/人間同士が話しているなどのMODEを指定できる。
    • C#/JavaScript/Java for Android/Objective-C for iOSのライブラリを提供。
  3. Speech to Text (IBM)

    • 認識結果はparamの指定によって複数パターンの表示が可能。
    • 認識結果全体のだけでなく、単語単位での信頼度も取得可能。
    • 単語単位でどのタイミングで話されているかの時間(start/end)が取得可能。
    • Curl/Node.js/Javaのライブラリを提供。

とても簡単ですがまとめると上記のような感じになります。

それぞれのAPIで「そんなに違いは無いかな〜」と思っていましたが、
実際に使ってみると、認識結果が1つだけだったり複数にできたり、単語ごとに信頼度が取得できたりと違いがありました。

今回紹介した内容はほんの一部なので、是非実際に触っていろいろやってみることをおすすめします!