ラズパイ3+Juliusで音声認識


日本語音声認識でクラウド・サービスを利用しない(自己完結型の)場合、OSSの界隈では現実的な選択肢はJulius一択のようです。

Juliusとはなんぞや(ビギナー編)

高速・高精度・高効率をうたってますが、「自分の、しかも、それなりの」精度が欲しい場合はチューニングせにゃいかんようです。

(MozillaのDeep Speechに期待しますが、まだラズパイは非力過ぎます)

 


 


準備

インストール

ラズパイ起動時準備

精度アップ-A

精度アップ-B

精度アップ-C

ノイズ対策

認識結果を利用する

マイク・スピーカー

不具合例

OpenJTalk

 


準備

USBマイクがサウンドデバイスとして認識されているか確認

$cat /proc/asound/modules

既存の内蔵オーディオモジュールが優先されるので多分こうなっている

0 snd_bcm2835

1 snd_usb_audio <-ここ

 

USBマイクの優先度を上げる

$sudo leafpad /lib/modprobe.d/aliases.conf

コメントアウト

#options snd-usb-audio index=-2

挿入

options snd slots=snd_usb_audio,snd_bcm2835
options snd_usb_audio index=0
options snd_bcm2835 index=1

 

ラズパイ再起動

 

再確認

$cat /proc/asound/modules

0 snd_usb_audio <-ここ

1 snd_bcm2835

 

↑TOP

 


インストール

GitHubでJuliusの最新版を確認

(2019/01/04現在、最新バージョンは4.5です、が、安定の4.4.2.1を使ってみます)

適当なディレクトリーを作って、ここにダウンロードしてmakeします。

$mkdir julius

$cd ~/julius

$wget https://github.com/julius-speech/julius/archive/v4.4.2.1.tar.gz

$tar zxvf v4.4.2.1.tar.gz
$cd julius-4.4.2.1

 

インストール

$./configure
$make
$sudo make install

 

ossのキャラクター型デバイスの1つの/dev/dspを使えるようにします(juliusはこれを使います)。

2018/12/20現在、最新のカーネル(4.9.80-v7+ )にはsnd-pcm-ossのモジュールはありません(除外されたそうです)。

そこで、直近のカーネル(4.9.80)にダウングレードして使います

v4.9.80のハッシュ値を使います。

$sudo rpi-update 5c80565c5c0c7f820258c792a98b56f22db2dd03

 

再起動

 


juliusフォルダーへ移動

cd ~/julius/julius-4.4.2.1

Juliusディクテーション実行キット
ダウンロード&解凍

wget https://osdn.net/dl/julius/dictation-kit-v4.4.zip

$unzip dictation-kit-v4.4.zip

 

Julius記述文法音声認識実行キット

ダウンロード&解凍

wget https://github.com/julius-speech/grammar-kit/archive/v4.3.1.zip

$unzip v4.3.1.zip

 

/dev/dsp用にossのカーネルモジュールをロードします(ロードは起動時設定にしておく必要があります、systemdに登録しておくとか…..)。

sudo modprobe snd-pcm-oss

$sudo sh -c "echo snd-pcm-oss >> /etc/modules"

 

サウンドカード指定

$arecord -l
$export ALSADEV=hw:<カード番号>

 

$export ALSADEV=hw:0

 

設定しておく場合は以下のファイルに書き込む

 

$sudo leafpad ~/.profile

 

追加

 

export ALSADEV=hw:0

 

 

サンプルファイルを用いてテスト

cd ~/julius/julius-4.4.2.1/dictation-kit-v4.4

julius -C main.jconf -C am-gmm.jconf -demo

 

:マイクの反応がないようなら….

感度チェック

amixer -D hw:0

100%にする場合

amixer -D hw:0 sset Mic 100%


:サンプリングレートが48000hzになっている場合

juliusは16000hzでサンプリングするので、ダウンレートするように….

XXX.jconfに以下を追加

-48

バックアップ環境に作成ファイル一式をコピーして起動した際、認識率が低下したと感じた場合、この項をチェックする必要がある。音響モデルが環境にどう影響されるのかもっと調べてみます。

サンプリングレートについて

 

音声入力について

↑TOP
<\a>


ラズパイ起動時準備

ossモジュールを起動のたびにロードする必要がある。

systemdに登録しておきます。

● /home/pi/julius/julius-4.4.2.1/dictation-kit-v4.4にシェルスクリプト作成

【load_oss.sh】

#!/bin/bash

sudo modprobe snd-pcm-oss

 

 

load_oss.shに実行権限を与えておく

●サービス設定ファイル[load_oss.service]を作成

$sudo leafpad /etc/systemd/system/load_oss.service

[Unit] Description = simple service

[Service] Type=simple ExecStart= /home/pi/julius/julius-4.4.2.1/dictation-kit-v4.4/load_oss.sh

[Install] WantedBy = multi-user.target

 

●登録

〇起動時実行サービスとして登録

$sudo systemctl enable load_oss

 

〇再起動

 

*サービス停止の場合は

$sudo systemctl disable load_oss

 

↑TOP

 


精度アップーA

対象語数を絞って、単語だけを高速に認識できるよう自作の辞書を用意

dictation-kit-v4.4ディレクトリーに以下の3つのファイルを作成

1:sample.yomi(tab区切り)

ミカン みかん
キリン きりん
テレビ てれび
エアコンを消して えあこんをけして
エアコンをつけて えあこんをつけて

2:sample.dic

$iconv -f utf8 -t eucjp sample.yomi | yomi2voca.pl > sample.dic

3:sample.jconf

-w sample.dic #単語辞書ファイル。作成した「sample.dic」を指定
-v model/lang_m/bccwj.60k.htkdic #N-gram、または文法用の単語辞書ファイルを指定
-h model/phone_m/jnas-tri-3k16-gid.binhmm #使用するHMM定義ファイル
-hlist model/phone_m/logicalTri #HMMlistファイルを指定する
-n 3 #n個の文仮説数が見つかるまで検索を行う。3〜5。
-output 1 #見つかったN-best候補のうち、結果として出力する個数
-input mic #マイク使用
-input oss #オープンサウンドシステム使用
-rejectshort 600 #検出された入力が閾値以下なら棄却
-charconv euc-jp utf8 #入出力エンコード指定(内部euc-jp, 出力utf-8)
-lv 1000 #入力の振幅レベルの閾値(0~32767)

実行

julius -C sample.jconf   -input mic

 

↑TOP

 


精度アップーB

Julius記述文法音声認識実行キットを使ってみます。

言語モデルを参考にして記述文法を作成

作成する認識用文法ファイルは2つ(xxx.grammar、xxx.voca)。

ファイル名のxxxは同じにします。

vocaファイルに書き込む音素の記述ルールは特殊なものもあります。

コンパイル時には気づきませんが、Juliusで読み込んだ時に分かります、ご注意ください。

例えば家電を操作するような場合。

これは「〇〇〇を△△△して」というお願い系のコマンド。

kaden.grammar

S : NS_B KADEN_ PLEASE NS_E
KADEN_ : KADEN
KADEN_ : KADEN WO
#
PLEASE : TSUKETE
PLEASE : KESHITE

kaden.voca

% KADEN
エアコン e a k o n
テレビ t e r e b i
% WO
を w o
% TSUKETE
つけて t u k e t e
% KESHITE
消して k e s i t e
% NS_B
<s> silB
% NS_E
</s> silE

grammar ファイルと voca ファイルをオートマトン(dfa ファイル)と単語辞書(dict ファイル)に変換します。

$mkdfa.pl kaden

 

 

kaden.dfa,kaden.dictが新たに作成されます。

 

記述文法は複数つくることができます。

例えば声掛け用をもう一つ。

call.grammar
S : NS_B CALL_ NS_E
CALL_ : CALL
CALL_ : CALL NOISE

call.voca
% CALL
キリン k i r i n
レモン r e m o n
アラレちゃん a  r a r e ch a N
% NOISE
<sp> sp
% NS_B
<s> silB
% NS_E
</s> silE

$mkdfa.pl call

 

 

設定ファイル

sample2.jconf

-gram kaden -gram call #2つの文法を使います
-v ../model/lang_m/bccwj.60k.htkdic
-h ../model/phone_m/jnas-tri-3k16-gid.binhmm
-hlist ../model/phone_m/logicalTri
-n 1 #見つける文仮説数は1
-output 1
-input mic
-rejectshort 600
#入出力エンコード指定は使わない、何故かmultibyteがらみのinternalErrorになる
#-charconv euc-jp utf8
-lv 1000

実行

julius -C sample2.jconf   -input mic

 

書き方のサンプル

 

 

:このやり方がAより精度が高いというわけではないです。

↑TOP

 


精度アップ-C

Juliusはver4.3からDNN-HMM音響モデルが使われています。

音声認識のための深層学習

それ以前のバージョンでは誤認識対策でSVM(サポートベクターマシン)を使おうとされた方もいらっしゃるようです。

参考までにJulius + SVM

オンライン学習にチャレンジ その結果

どこにどんな学習モデルなどが適用できるか、少し探してみましょう。

また、パラメータ調節でどの程度認識率が上がるのかも調査してみます。

Juliusとはなんぞや(ビギナー編)に書かれているように、付録 B. オプション一覧の、第1パスパラメータ、第2パスパラメータ、単語ラティス / confusion network 出力の項をいじってカスタマイズしてみます。

認識パラメータの調節は以下の3項目でやってみなはれ…だそうです(どれかではなく1から順番に)。

1. 第一パスのビーム幅を設定する!
2. 言語スコア重みと、挿入ペナルティを設定する!
3. 文仮説数や出力形式を設定する!

くわしくはここ参照

 

で、達成目標

環境からのノイズと「私の」声掛けの言葉を高い確率で区別できること!

「私の」短い命令形の語句を正しく認識できること!

「私の」にとことんこだわる場合ここや ここ参照

 

工事中

 

 

↑TOP

 


ノイズ対策

Juliusは入力された音をとにかく辞書から検索してとにかくマッピングしようとするクセ(?)があります。

(どうしようもない音の場合は空を返してくる場合もありますが)

対策としては以下を参照。

http://julius.osdn.jp/index.php?q=doc/gmm.html

http://winnie.kuis.kyoto-u.ac.jp/members/katumaru/gmm/index.html

まだ、試していませんけど……。

で、とりあえずの対策としては、ノイズを間違って正解文にマッピングしないようにしてみます。

気休めです。

上記の辞書にキリンやレモンがあるのは、これをダミーに使うためです。

咳やクシャミなどの「’%’&%%’△65◇….」という入力音を「アラレちゃん」と解釈しないようにJuliusにダミーをマッピングさせます。どうもJuliusは困った時には似た(?)言葉を採用してしまうようです。

こういう無駄なダミーをいくつか用意しておくと、Juliusの誤認識というか誤適合を回避できるかもしれません。管理人はダミーのことを「捨て台詞」と呼んでます(^^)。

例えば、「バイバイ」という単語のみ登録している場合、”ほいほい”という呼びかけ(無意味な呼びかけもJuliusにとってはノイズと等価)に対してJuliusは高確率で”バイバイ”を採用します。でもダミーとして「ホイホイ」も登録しておけばJuliusは”ホイホイ”も選択肢にいれるので”バイバイ”を採用する確率は下がります。

まぁ、誤適合を防ぐためにノイズに「正解」を与えておこうということです。

 

 

また、モジュールモードでの出力ではクライアントへ第2パスの認識結果とともに単語信頼度も出力されます。

この単語信頼度(CM)を指標にしてもいいかもしれません。CMは単語辞書なら1個、記述文法なら区切り(?)単位で返してくるので、どう扱うか思案します。

単純に、単語辞書のみ使う場合、CMは1個だけ返ってきますが、このCMの値が0.990以上の場合のみ採用するというだけで、例えばマイクのそばで柏手を打ったり咳をするだけで反応するというアホな誤認識は相当に軽減できるようです。

ただ、ウェイクワードとして使う言葉の場合は閾値を下げておかないと、なっかなか返事をしてくれないシステムになってしまうのっで注意が必要です。

 

やり方は、例えばこんな感じ。

単語信頼度はCM=”0.898″というようなフォーマットで返ってくるので、下記のraspi-julius.pyでWORDを取得しているようにindex2 = line.find(‘CM=”‘)という感じで値を取得すればいいです。

 

cm_list = []

index2 = line.find('CM="')
if index2 != -1:
    cm = line[index2 + 4:line.find('"', index2 + 4)]
    cm_list.append(cm)

で、

if(len(cm_list) == 1):
    if(float(cm_list[0]) >= 0.990):
        process_query(sentence)

 

 

これでいいのかは不明です、もう少し学習してまたレポートします。

後は、命令文は、最も誤認識されにくい言葉で代替する?

「〇〇〇を△△△して」を分かってもらえない場合、認識されやすい単語で命令する…..とか。

 

↑TOP

 


認識結果を利用する

Juliusをモジュールとして起動し、結果をPythonで利用してみます(socketを使ったプロセス間通信)。

ラズパイのローカルIPが192.168.0.31だとして、10500番ポートを使っています。

Juliusが認識した結果をsentenceで取得しています。その中に所定の命令文や声掛け文(XXXX)があれば、何かします。

何かする….のところは、例えば「声かけ」で赤外線リモコン制御とか、カメラとサーボモーターと顔認識AIがあれば、あなたの方を向いてくれます(^^)。

 

raspi-julius.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from tkinter import messagebox
import socket
import subprocess
import time

julius_host = '192.168.0.31'
julius_port = 10500

has_recongized = False

def process_query(sentence):
    global has_recongized
    if "ハロー" in sentence:
        messagegbox('message box',"hello")
        has_recongized = True
    else:
        if has_recongized == True:
            if "XXXX" in sentence:
                print('"' + sentence + '"')
                #何かする
                has_recongized = False

            elif "キャンセル" in sentence or "中止" in sentence:
                #何もしない
                has_recongized = False
                messagegbox('message box',"ok")

def main():
    # julius起動
    process = subprocess.Popen(["./start-julius.sh"], stdout=subprocess.PIPE, shell=True)
    #プロセスIDを取得
    pid = str(process.stdout.read().decode('utf-8'))

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((julius_host, julius_port))

    print("●なんか言って!")

    try:
        data = ''
        sentence =''
        while True:
            if '</RECOGOUT>\n.' in data:
                word = ""
                for line in data.split('\n'):
                    index = line.find('WORD="')
                    if index != -1:
                        line = line[index + 6:line.find('"', index + 6)]
                        word = str(line)
                    if word != '[s]':
                        sentence += word
                print('"' + sentence + '"')
                process_query(sentence)
                sentence = ''
                data = ''
                print("●なんか言って!")
            else:
                data += str(client.recv(1024).decode('utf-8'))
    except KeyboardInterrupt:
        #Ctrl+ Cなどで起動スクリプトのプロセスを終了
        process.kill()
        subprocess.call(["kill "+pid],shell=True)
        client.close()

if __name__ == "__main__":
    main()

******************************************************

start-julius.sh

#!/bin/sh

julius -C /home/pi/julius/julius-4.4.2.1/dictation-kit-v4.4/sample.jconf -input mic -module > /dev/null &

#プロセスIDを出力
echo $!

sleep 2

 

↑TOP

 


マイク・スピーカー

セルフパワーUSBハブを使います。

スピーカー

ダイソーの300円USBスピーカーで必要十分

アンプ内蔵なので音量調整できます。この商品はコスパの高さが評判で、いろいろな方がグレードアップの方法などもアップしていおられますね。

マイク

会議用マイクロフォン

スペックどおりの範囲で使う場合は、ラズパイのUSBコネクターに直差しではなくセルフパワーUSBハブを通して給電した方がいいです(直差しだと場合によっては10ft≒3mの集音範囲が30cmくらいになります)。

全指向性なので、そばに置いといて、あさっての方に向かって喋ってもJuliusは認識してくれます。

↑TOP

 


音声出力時の不具合例

aplay実行時に「そのようなファイルはありません」と出る場合

サウンドデバイスを明示的に指定しなければならないかもしれません。

$aplay -l

で、カード番号とデバイス番号をチェック

カード番号=1、デバイス番号 = 0の場合

$ aplay  -D hw:1,0  /usr/share/sounds/alsa/Front_Center.wav

 


セルフパワーUSBハブに接続時の不具合例

接続したまま起動した場合、なぜか認識されない不具合がありました。

イレギュラーなので原因はつかみにくいです。

この場合は、単に抜き差しすればいいだけですが、こういうこともあるってことですね。

 


戻る

音素列の記述ルールは、”登録したい単語+半角スペース+ローマ字読み”となります。

ローマ字読み(音素記号)の記述ルールは以下の通りです。

・基本は小文字のアルファベットを使う。

・母音と子音の間に半角スペースを入れる。

・「ん」は大文字の「N」。

・小さい「っ」は「q」で表す。

・伸ばす音は母音に「:(コロン)」を付ける。
例 大阪 o: s a k a

・発音に近い表記をする。
例 「昨日」は「きのー」と表現し「k i n o:」と表記する。

・助詞の「は」「へ」は,それぞれ「w a」「e」と表記する。

・記述の例
チェンジ ch e N j i
終了 sh u: ry o:
ウェーブファイル w e: b u f a i r u
ガンバ g a N b a

あ a
い i
う u
え e
お o
か k a
き k i
く k u
け k e
こ k o
さ s a
し sh i
す s u
せ s e
そ s o
た t a
ち ch i
つ ts u
て t e
と t o
な n a
に n i
ぬ n u
ね n e
の n o
は h a
ひ h i
ふ f u
へ h e
ほ h o
ま m a
み m i
む m u
め m e
も m o
や y a
ゆ y u
よ y o
ら r a
り r i
る r u
れ r e
ろ r o
わ w a
が g a
ぎ g i
ぐ g u
げ g e
ご g o
ざ z a
じ j i
ず z u
ぜ z e
ぞ z o
だ d a
で d e
ど d o
ば b a
び b i
ぶ b u
べ b e
ぼ b o
ぱ p a
ぴ p i
ぷ p u
ぺ p e
ぽ p o
きゃ ky a
きゅ ky u
きょ ky o
しゃ sh a
しゅ sh u
しょ sh o
ちゃ ch a
ちゅ ch u
ちょ ch o
にゃ ny a
にゅ ny u
にょ ny o
ひゃ hy a
ひゅ hy u
ひょ hy o
みゃ my a
みゅ my u
みょ my o
りゃ ry a
りゅ ry u
りょ ry o
ぎゃ gy a
ぎゅ gy u
ぎょ gy o
じゃ j a
じゅ j u
じょ j o
びゃ by a
びゅ by u
びょ by o
ぴゃ py a
ぴゅ py u
ぴょ py o
てぃ t i
でぃ d i
でゅ dy u
とぅ t u
どぅ d u
ふぁ f a
ふぃ f i
ふぇ f e
ふぉ f o
ちぇ ch e
しぇ sh e
じぇ j e
あー a:
いー i:
うー u:
えー e:
おー o:
かー k a:
きー k i:
くー k u:
けー k e:
こー k o:
さー s a:
しー sh i:
すー s u:
せー s e:
そー s o:
たー t a:
ちー ch i:
つー ts u:
てー t e:
とー t o:
なー n a:
にー n i:
ぬー n u:
ねー n e:
のー n o:
はー h a:
ひー h i:
ふー f u:
へー h e:
ほー h o:
まー m a:
みー m i:
むー m u:
めー m e:
もー m o:
やー y a:
ゆー y u:
よー y o:
らー r a:
りー r i:
るー r u:
れー r e:
ろー r o:
わー w a:
がー g a:
ぎー g i:
ぐー g u:
げー g e:
ごー g o:
ざー z a:
じー j i:
ずー z u:
ぜー z e:
ぞー z o:
だー d a:
でー d e:
どー d o:
ばー b a:
びー b i:
ぶー b u:
べー b e:
ぼー b o:
ぱー p a:
ぴー p i:
ぷー p u:
ぺー p e:
ぽー p o:
きゃー ky a:
きゅー ky u:
きょー ky o:
しゃー sh a:
しゅー sh u:
しょー sh o:
ちゃー ch a:
ちゅー ch u:
ちょー ch o:
にゃー ny a:
にゅー ny u:
にょー ny o:
ひゃー hy a:
ひゅー hy u:
ひょー hy o:
みゃー my a:
みゅー my u:
みょー my o:
りゃー ry a:
りゅー ry u:
りょー ry o:
ぎゃー gy a:
ぎゅー gy u:
ぎょー gy o:
じゃー j a:
じゅー j u:
じょー j o:
びゃー by a:
びゅー by u:
びょー by o:
ぴゃー py a:
ぴゅー py u:
ぴょー py o:
てぃー t i:
でぃー d i:
でゅー dy u:
とぅー t u:
どぅー d u:
ふぁー f a:
ふぃー f i:
ふぇー f e:
ふぉー f o:
ちぇー ch e:
しぇー sh e:
じぇー j e:

ん N

Be the first to comment

Leave a Reply

Your email address will not be published.


*