きっと続かんブログ

勉強したことや人に言いたいことを書く。

【書籍:音声言語処理と自然言語処理】二章 音声言語処理のモデル①

概要

  • "音声の音響分析技術" - 音声とは
  • "音声の認識技術" - speech2text
  • "音声の合成技術" - text2speech

ポイント

4月8日
  • (p16)音声生成は3段階のプロセス(発声、調音、口から放射)からなる。
    • 発声:元となる音源を生成する。母音なら声帯の振動が音源に、子音の場合はそれに加え声道の狭めによる乱流や、閉鎖した声道を開放させて得られる突発的な音などが(場合によっては組み合わさり)音源になる。
      ※母音ア〜オの音源は一緒らしい
    • 調音:音源に対し様々な言語的特徴を付与する。主に声道
  • (p17)音声の心理学的特性4つ
    • 高さ(ピッチ):周波数に対応 / 強さ(デシベル):波形振幅に対応 / 長さ / 音色
  • (p19)「2つの音が同じ高さ、強さ、長さをもつにも関わらずその2音を区別できる場合、それは音色が異なっている。」
    • 同じ高さ強さ長さで「あー」「いー」と声を出した時、この2つの違いは音色なんだ
  • (p22)基準母音:国際基準の母音
    • 母音は国によって違う。そこでIPAは世界中のあらゆる子音を発音できる18種類の母音、基準母音を定義した。
4月9日

※書き方を変えました

2.2 音声認識

音響的難しさと2つの音響モデル

  • 「文字"あ"に変換されるべき発声が無限に存在する」
    • 発声スタイル・話者の性別や年齢や体格・雑音やマイクといった環境 … これらの違いによる音響的変動が原因
  • 不特定話者音響モデル : 大量の話者によって発声された音声コーパスが必要
  • 特定話者音響モデル  : 不特定話者音響モデルを構成し、特定の話者による少数サンプルからパラメータチューニング

言語的難しさ

  1. 未知語の対処
  2. 話し言葉特有の問題(ill-formedness : 前記事参照)

音声認識問題の定式化

  • 音声認識は、発話系列Oが与えられた時に、単語系列Wが意図されたとする確率P(W|O)を最大化するWを求める問題である。
  • つまり、W' = argmax P(W|O)
  • ここで、事後確率P(W|O)やP(O|W)は音響モデル、事前確率P(W)は言語モデルと呼ばれる。言語モデルは実際の発話行為とは独立に、各言語表現がどのくらいの確率で生成されるのかを表す。
  • 音声認識とは、音響モデルと言語モデルの積を最大化するWを探索する問題である。
    • P(W|O)をベイズ定理で変形するとP(O|W)とP(W)の積が現れる!

音響モデル

隠れマルコフモデルとか。次回

【書籍:音声言語処理と自然言語処理】一章 音声と言語の諸相

大まかに

これから学習していく音声学言語学についての全体像を解説。2章以降の準備

ポイント

  • (p1)人間は先天的(生まれながら)に音声言語の識別機構が備わっている説が濃厚。事実、乳幼児は、話者の違いやピッチの違いを聞き分けられることが出来る。
    もちろん、高機能な知覚メカニズムはニューラルネットワークが学習的に形成されることで後天的に獲得される。
  • (p3)構造言語学においても、人間は先天的な能力を持っているとされる。それは言語獲得装置である。獲得装置を生まれながらに持つことで、後天的な学習がスムーズに行える。byチョムスキー
    • 普遍文法という文法の卵を育てることで、母語をスムーズに獲得できる。第二言語以降はそれができないので難しいし時間がかかる。
  • 一方現在の工学(NMTとか言語モデル)では、生得的に備わっている(とされる)文法も大規模データから学習しようとする。
  • (p10)「話し言葉」と「書き言葉」の違い
    話し言葉にはill-formednessが存在する。ポーズ(息継ぎなどによる)、フィラー(えー、あのー)、言い澱み、言い誤り、言い直し、発音の怠け(あたし・あーし)など
    • 音声言語処理特有の難しさでもある。例えば音声翻訳、単にspeech2textしてNMT通すだけじゃん、ではダメ。話し言葉と書き言葉の違いをどうするか(音声認識部分でフィラーを削るとか、翻訳時に発音の怠けを吸収するとか)。
  • (p11)話し言葉だから伝わる言語的情報もある。例えば「白い虎の小屋」という句は、「[白い虎]の小屋」「白い[虎の小屋]」と2つの解釈があるが、「白い」の後にポーズを挟むことで後者だと伝えることが出来る。

感想

音声についても文法構造についても、言語獲得のための能力が生まれながらに備わっていることに驚いた。 そして、今主流の大規模データによる知識獲得で人間の知能を再現するのは難しいんじゃないかと思ってしまった。 だったら赤ちゃんの先天的能力をロボットにも持たせようという研究も当然あるけど、ここ20年大きな進展はないらしい。

全体像を理解するためのすごく分かりやすい説明で読んでいて面白かった。

【python】オプションでパラメータをコマンドラインから受け取る【argparse】

目的

プログラム中のパラメータを、外部ファイル指定があればその内容を見て更新する。無ければデフォルト値を用いる。

import argparse

GLOBAL_PARAM = 1
GLOBAL_PARAM2 = 2

def update_params(param_path):
    # ファイルからパラメータを読み込み更新

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument('--param', dest='param_path', help='option : assign parameter file', type=str)
    args = parser.parse_args()

    if args.param_path!=None:
        update_params(args.param_path)

オプション引数は無ければNoneを返すので、それによって分岐する。

【python】リストや辞書を外部ファイルに保存

pickleを使用する。

モジュール

import pickle

def pickle_dump(obj, path):
    with open(path, mode='wb') as f:
        pickle.dump(obj,f)

def pickle_load(path):
    with open(path, mode='rb') as f:
        data = pickle.load(f)
        return data

使用方法

mylist = [1,2,3,4,5]
mydict = {'a':1, 'b':2, 'c':3}

# ダンプ
pickle_dump(mylist, './mylist.pickle')
pickle_dump(mydict, './mydict.pickle')

# ロード
print(pickle_load('./mylist.pickle')) # [1,2,3,4,5]
print(pickle_load('./mydict.pickle')) # {'a':1, 'b':2, 'c':3}

大きなデータを毎度毎度読み込むと時間がかかるので、一度読み込んで外部に保存しておくような時に便利。

pythonでコマンドライン引数を受け取る方法

import argparse

...

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-dir', dest='input_dir', type=str) 
    parser.add_argument('-num', dest='number', type=int)
    parser.add_argument('--myoption', dest='opt', action='store_true', help='use myoption or not') # "--myoption"を付けるとTrue
    args = parser.parse_args()

    print(args.input_dir)
    print(args.number)
    print(args.opt) # True of False

TensorFlowでモデルの保存・読み込み

例示モデルの設計

input : 任意のデータ数×3次元ベクトル
w : 3×1次元の空行列
output = input × w

任意の個数の3次元ベクトルをゼロベクトルに変形する。

保存

import tensorflow as tf
import numpy as np

input = tf.placeholder(tf.int32, shape=(None,3))
w = tf.Variable(np.array([[0,0,0]]).T, dtype=tf.int32, name="v1")
output = tf.matmul(input,w)

feed_dict = dict()
var_in = [[1,1,1],[2,2,2],[3,3,3]]
feed_dict = {input : var_in}

sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess.run(output, feed_dict=feed_dict)

saver = tf.train.Saver()
saver.save(sess, "./hoge.ckpt")

読み込みと実行

import tensorflow as tf
import numpy as np

input = tf.placeholder(tf.int32, shape=(None,3))
w = tf.Variable(np.array([[3,3,3]]).T, dtype=tf.int32, name="v1")
output = tf.matmul(input,w)

feed_dict = dict()
var_in = [[1,1,1],[2,2,2],[3,3,3]]
feed_dict = {input : var_in}

saver = tf.train.Saver()

sess = tf.Session()
saver.restore(sess, "hoge.ckpt")

sess.run(output, feed_dict=feed_dict)

結果にwの変更np.array([[3,3,3]]).Tが反映されない(=保存時のパラメータが読み込まれている)ことに注目しよう。

読み込むときの注意

・モデル作成時と同じplaceholderとVariableを宣言しなければならない。Variableの値は適当で構わない。
・Variableを初期化tf.global_variables_initializer()すると読み込んだパラメータが消去されるので注意。
Saverの宣言tf.train.Saver()はセッションオブジェクトの作成tf.Session()の前に行う。 順番は逆でも構わない

SVMで2値分類を行うテスト

scikit-learnのSVMを使って、数値を2つに分類してみた。データとラベルは次のように。

train_text = np.array([[0],[1.2],[0],[0.8],[1.0],[0.1]])
train_label = np.array(['a','b','a','b','b','b'])  

つまり、0がラベル"a"に、それ以外の数値が"b"に分類されるのが望ましい。
このタスクが簡単なのかどうなのか、データ数少ないけどいけるのか、わからないけど動かしてみる。

プログラム

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split,GridSearchCV

#学習データ
train_text = np.array([[0],[1.2],[0],[0.8],[1.0],[0.1]])
train_label = np.array(['a','b','a','b','b','b'])

#テストデータ
test_text = ([[0],[0.11],[0.3],[1.5],[2.3],[0.5],[0.9]])
test_label = np.array(['a','b','b','b','b','b','b'])

#学習
clf = SVC(verbose=True)
clf.fit(train_text, train_label)

#結果を表示
predicted_labels = clf.predict(test_text)
print('\ntest data: ',test_text)
print('predict  : ',predicted_labels)
print(f'正答率: {clf.score(test_text, test_label)}')

こんな短く書けるなんてちょっとすごい。

結果

*
optimization finished, #iter = 2
obj = -3.422058, rho = 0.386035
nSV = 4, nBSV = 4
Total nSV = 4
[LibSVM]
test data:  [[0], [0.11], [0.3], [1.5], [2.3], [0.5], [0.9]]
predict  :  ['a' 'b' 'b' 'b' 'b' 'b' 'b']
正答率: 1.0

精度も上々。テストデータでは100%だ。
精度が低い場合、グリッドサーチを使ってパラメータを簡単にチューニングできるらしい。