COTOHA音声認識を中の人が仕組みからサンプルまで軽く解説

# COTOHA音声認識を中の人が仕組みからサンプルまで軽く解説

COTOHA APIのラインナップに**音声認識**をリリースしました!!  
しかし，いざAPIを動かそうと思っても，音声データは画像・テキストと比べて<font color="Tomato">**前処理**</font>が面倒．．．  
そこで本記事では，実際に<font color="Tomato">**COTOHA音声認識の中の人**</font>がPythonで<font color="DodgerBlue">音声認識APIを実行する方法</font>を説明していきます!!

# COTOHA APIとは
NTTが長年頑張って得たノウハウを詰め込んだ、NTTコミュニケーションズが提供する自然言語処理のAPIサービスになります!!  
**サービスの詳細** は[こちら](https://api.ce-cotoha.com/contents/index.html)を見て頂くとして、これまでは<font color="Tomato">構文解析</font>や<font color="Tomato">ユーザ属性推定</font>などのテキスト解析のサービスを提供していました．    

- Python初心者 :hand:
- COTOHA API 初心者 :hand:

[自然言語処理を簡単に扱えると噂のCOTOHA APIをPythonで使ってみた](https://qiita.com/gossy5454/items/83072418fb0c5f3e269f)で、テキスト解析で遊んでもらってからこの記事を見て頂くことをオススメします!!
 
さて本題に入りましょう!!

# 今回はテキストではなく... <font color="Tomato">音声認識</font> !!
今までCOTOHAは、テキストしか相手にしてくれませんでした... :disappointed:

しかし、音声認識APIが2019年3月にリリースされCOTOHA君が<font color="Tomato">**音声言語**</font>を理解するようになりました!!しかも、入力フォーマットが

- 音声ファイル形式
- ストリーミング音声形式

の2パターンで提供しているため、様々なユースケースで音声認識をお試し頂けます!

現在，<font color="DodgerBlue">For Enterprise</font>版の提供のみとなっていますが，<font color="Tomato">3ヶ月のお試し期間</font>があるためそちらを使ってお試しいただければと思います!! 

![vr_arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/c3e25b82-346b-5cfd-44e4-f9bfd6c51144.png)


# 音声認識APIってどうなってるの??
<font color="Tomato">**音声認識**</font> → <font color="DodgerBlue">音声データを入れたらテキストが返ってくる</font>  
っていうのは分かりました．  
では，実際に<font color="DodgerBlue">音からどのように言語にしているのか</font>をさらっと解説します．  

## 音声認識API構成図

![vrg_api_ponch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/ea337385-b670-3ecf-ff07-26fc92013eea.png)



COTOHA音声認識APIは，上記のような構成をしております．

1. 区間検出
> 入力された音の波形から，話をしている部分を抽出する部分．
抽出した部分だけが，認識処理の対象になります．

2. 特徴抽出
>音声認識特有の特徴を抽出する部分．
これを行うことで，言語認識の精度を向上させます．

3. 識別
>取得した特徴から，テキストを生成．
3つのモデルの結果を基に，最適なテキストを生成

**3つのモデル**
 > 音響モデル - **音波**を基に予測を行うモデル
 > 発音辞書 - **音素** を基に予測を行うモデル
 > 言語モデル - **文法** を基に予測を行うモデル


# サンプルコード on GitHub
**GitHub** にサンプルコードを

- [Python版](https://github.com/cotoha-api-group/speech_recognition-python-sample)
- [Node.js版](https://github.com/cotoha-api-group/speech_recognition-js-sample)

を公開していますので，そちらを参考にしてください!!

# APIの使い方解説

それでは，どのように音声認識APIを使えば良いのかを，**GitHub**に公開しているサンプル

## ファイル音声認識
API実行プログラムの大まかな処理フローは以下の通りです．

![file_vrg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/f96cfaff-22c1-60d2-7eb9-aeafbc540097.png)


### GitHub
[speech_recognition-python-sample/file.py at master · cotoha-api-group/speech_recognition-python-sample](https://github.com/cotoha-api-group/speech_recognition-python-sample/blob/master/src/file.py)

### ライブラリのインポート & プロパティ情報

<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>
まず，コードに必要なライブラリや環境変数をインポートします．

```py
import requests
import json
import sys
import wave

args = sys.argv
audio_name = args[1]    # 音声ファイルのパス
json_name = args[2]     # 認証情報ファイルパス
oauth_url = 'https://api.ce-cotoha.com/v1/oauth/accesstokens'
model_id = 'ja-gen_tf-16'
hostname = 'https://api.ce-cotoha.com/api/'
url = hostname + 'asr/v1/speech_recognition/' + model_id

with open(json_name) as f:    # 認証情報ファイルの読み込み
    credential = json.load(f)
client_id = credential['client_id']
client_secret = credential['client_secret']
domain_id = credential['domain_id']
Interval = 0.24  # 1メッセージあたり240msの音声データ
wf = wave.open(audio_name, 'rb')
rate = wf.getframerate()
wf.close()
```
#### 認証情報ファイル
認証情報は`json` フォーマットで管理しています．  

```json
{
    "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "client_secret": "YYYYYYYYYYYYYYYYY",
    "domain_id": "ZZZZZZZZ"
}
```

</div>
</details>


### アクセストークンの取得
<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

ここでは，クライアントIDとシークレットから，アクセストークンを発行します．

```py
headers = {"Content-Type": "application/json;charset=UTF-8"}
obj = {'grantType': 'client_credentials', 'clientId': client_id, 'clientSecret': client_secret}
data_json = json.dumps(obj).encode("utf-8")
response = requests.post(url=oauth_url, data=data_json, headers=headers)
access_token = response.json()['access_token']
```
</div>
</details>

### リクエストの作成 & 送信
<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

ファイル音声認識APIのリクエストは，<font color="DodgerBlue">マルチパートエンコード</font>で構成されているため．  
図のようなHTTPリクエスト(<font color="Tomato">ヘッダ</font>， <font color="Tomato">パラメータパート</font>，<font color="Tomato">音声データパート</font>，<font color="Tomato">コマンドパート</font>)を作成します．

```py
### MIME_TYPE設定 & ヘッダ作成
MIMETYPE_JSON = 'application/json'
MIMETYPE_AUDIO = 'application/octet-stream'
headers = {'Authorization': 'Bearer '+access_token}

### パラメータパート作成: 音声認識開始要求パート
request_json = {
        "msg": {
            "msgname": "start"
        },
        "param": {
            "baseParam.samplingRate": rate,
            "recognizeParameter.domainId": domain_id,
            "recognizeParameter.enableContinuous": True
        }
}

### 音声データパート作成: バイナリ音声データを送信パート
wf = wave.open(audio_name, 'rb')
fileDataBinary = wf.readframes(wf.getnframes())
wf.close()

### コマンドパート: 音声認識終了要求パート
command_json = {
        "msg": {
            "msgname": "stop"
        }
}

### マルチパートエンコード → 各パートを1つのリクエストとしてまとめる
files = [
        ('parameter', (None, json.dumps(request_json), MIMETYPE_JSON)),
        ('audio', (None, fileDataBinary, MIMETYPE_AUDIO)),
        ('command', (None, json.dumps(command_json), MIMETYPE_JSON))
]

### POSTリクエスト
response = request.post(url, headers=headers, files=files)
```
</div>
</details>


### 認識結果の出力
<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

```py
# statusが200のときのみresponseにjsonが含まれる
if response.status_code == 200:
    for res in response.json():
        # type=2ではsentenceの中身が空の配列の場合がある
        if res['msg']['msgname'] == 'recognized' and res['result']['sentence'] != []:
            print(res['result']['sentence'][0]['surface'])

else:
    print("STATUS_CODE:", response.status_code)
    print(response.text)
```

</div>
</details>

## ストリーミング音声認識
API実行プログラムの大まかな処理フローは以下の通りです．  

![stream_vrg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467283/bec1122a-894d-b55c-9a4c-b25ea8d95332.png)


[ライブラリのインポート & プロパティ情報](#ライブラリのインポート--プロパティ情報)  
[アクセストークンの取得](#アクセストークンの取得)  

は，ファイル音声認識と同じなので，そちらを参考にしてください．

### GitHub
[speech_recognition-python-sample/streaming_mic.py at master · cotoha-api-group/speech_recognition-python-sample](https://github.com/cotoha-api-group/speech_recognition-python-sample/blob/master/src/streaming_mic.py)

### パラメータ設定
ストリーミング音声認識に必要なパラメータは以下の通りです．

<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

```py
def __init__(self):

    self.channels = 1 # モノラル指定
    self.format = pyaudio.paInt16 #音声データをInt16で扱う
    self.rate = 44100 # 入力のサンプリングレート(Macbookのマイク)
    self.Interval = 0.24 # COTOHA APIの仕様により
    self.nframes = int(self.rate * self.Interval) # 1秒あたりのフレーム数

    """
    ↓↓↓ ファイル音声認識と同様
    """
    self.url = url
    self.client_id = client_id
    self.client_secret = client_secret
    self.param_json = {"param": {
        "baseParam.samplingRate": self.rate,
        "recognizeParameter.domainId": domain_id,
        "recognizeParameter.enableContinuous": 'true'
        }}
```
</div>
</details>

### 音声認識開始要求
まず，COTOHA APIに今から音声認識を開始することを宣言します．

<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

```py
def start(self):  # 開始要求
    obj = self.param_json
    obj['msg'] = {'msgname': 'start'}
    data_json = json.dumps(obj).encode("utf-8")
    headers = {"Content-Type": "application/json;charset=UTF-8", 
                "Authorization": "Bearer " + self.access_token}
    self.requests = requests.Session()
    self.response = self.requests.post(url=self.url, data=data_json, headers=headers)
    self.check_response()
    # uniqueIdはデータ転送、停止要求に必要
    self.unique_id = self.response.json()[0]['msg']['uniqueId']
```

</div>
</details>

### マイクから音声をストリーム形式で音声認識

マイク入力から，ストリーミング音声認識をしていきます．

<details><summary><font color="DodgerBlue"><font color="DodgerBlue">Source Code | クリックしてください</font></summary>
<div>

マイクからの音声入力には，`pyaudio`というライブラリを使います． 

#### ストリーミング音声 → 音声認識
```py
import pyaudio
  
def listen(self):

    # COTOHA APIのデータ送信リクエストヘッダ
    self.headers = {"Content-Type": "application/octet-stream",
                    "Unique-ID": self.unique_id,
                    "Authorization": "Bearer " + self.access_token}

    # Pyaudio インスタンス 作成
    p = pyaudio.PyAudio()

    # デバイス音声をストリーミング入力
    stream = p.open(format=self.format,
                    channels=self.channels,
                    rate=self.rate,
                    input=True,
                    frames_per_buffer=self.nframes
                    )

    print("Now Recording")

    # 音声認識中
    try:
        while True:
            self.data = stream.read(self.nframes, exception_on_overflow=False)
            self._post() #### データ送信リクエスト

    # "Ctrl+C" でプログラム終了
    except KeyboardInterrupt:
        stream.close()
        p.terminate()
        sys.exit("Exit Voice Recognition")
```

#### データ送信リクエスト
```py
def _post(self):
    self.headers = {"Content-Type": "application/octet-stream",
                    "Unique-ID": self.unique_id,
                    "Authorization": "Bearer " + self.access_token
                    }

    self.response = self.requests.post(url=self.url, data=self.data, headers=self.headers)

    self.check_response()  ### 認識レスポンスが正常かチェック
    self.print_result() ### 結果を表示
```

#### 認識レスポンスチェック & 結果表示
```py
    def check_response(self):  # エラー発生時の処理
        if self.response.status_code not in [200, 204]:
            print("STATUS_CODE:", self.response.status_code)
            print(self.response.text)
            exit()

    def print_result(self):  # レスポンスをパースし認識結果を標準出力
        # statusが200のときのみresponseにjsonが含まれる
        if self.response.status_code == 200:
            for res in self.response.json():
                if res['msg']['msgname'] == 'recognized':
                    # type=2ではsentenceの中身が空の配列の場合がある
                    if res['result']['sentence'] != []:
                        print(clean_response(res['result']['sentence'][0]['surface']))
```

</div>
</details>

### 音声認識結果出力例

↓↓↓ Macのターミナルで実行した場合の表示例です．

```bash
Now Recoding
組織論についても聞いたんですよ。
でも組織は問題にフォーカスしてもよくならない。
どういうことですか日本語はわかるけど具体的にあったりとわかんないで教えてくださいって教わったのが。
例えば今は店長がいてスーパーバイザーがいてその上の人だよねよね部長がいいのかそうだね部長の上に部長が
いて傾斜につながってるそう言う組織だよねマクドナルドと同じなんで分かるんですけどその場合例えば自分で
はぴったりと自分のいたとに動いてくれたら理想のエリア作れるようなところが例えば何だけ。
```

# まとめ
COTOHAの音声認識APIをご紹介しました．

- <font color="Tomato">ファイル音声認識API</font>
- <font color="DodgerBlue">ストリーミング音声認識API</font>

と，2つのフォーマットで提供しているので，用途に合わせて使い分けてみてください!