統計的声質変換 (2) ボイスチェンジャーを作ろう
統計的声質変換 (1) ロードマップ(2015/2/11)の続き。
統計的声質変換の第二回ということでまずは統計的じゃない声質変換の枠組みで簡単なボイスチェンジャーを作ってみたい。いきなり本題とずれているけれどここをしっかり理解できていないと統計的な方はまったく歯が立たないため整理しておきたい。
ソース・フィルタモデル
人間の音声は、ノドの声帯を振動させたブザー音が声道、口、唇を通過することで出てくる仕組みになっている。これを数学的にモデル化したのがソース・フィルタモデル。
このモデルでは、音源にあたるブザー音を作り出し、ブザー音をディジタルフィルタに通すことで音声を作る。音源のパラメータとして声の高さを表すピッチ、声道のパラメータとしてメルケプストラムというのがよく使われる。メルケプストラムは音韻を表すだけでなく、個人の声質の特徴も表している。
簡単にまとめると「人間の音声からピッチとメルケプストラムというパラメータを抽出でき、そのパラメータからフィルタを用いて音声を再合成できる」ってところが非常に重要になる。最初に勉強したときはここがピンとこなくてずいぶん苦労した・・・*1。
ボイスチェンジャーを作ろう
簡単なボイスチェンジャーは、音声からメルケプストラムとピッチを抽出し、それらのパラメータに何らかの変換を施したあとに再合成することで実現できる。
上の図でmcep、pitchなどのコマンドが書いてあるが、これらはSPTKという音声信号処理のツールのコマンドを表している。SPTKを使うとパラメータの抽出や変換がコマンドの組み合わせで簡単にできる。今回はこれを使って簡単なボイスチェンジャーを実装してみた。SPTKの具体的な使い方は前に書いたことがあるので今回は触れない。
マイクから自分の声を入力し、SPTKでパラメータを抽出・変換・再合成し、スピーカーから変換した声が出てくるスクリプトを作ってみる。
このスクリプトを実行するには、PyAudioというPythonの音声ライブラリが必要。音声の録音と再生に使っている。SPTKのコマンドもパスが通ったところに置く必要がある。SPTKのインストール方法は、SPTKの使い方 (1) インストール・波形描画・音声再生(2012/7/1)を参照。あとパソコンに接続したマイクも必要。
スクリプトを起動する(-u
をつけないと出力がバッファリングされて表示されない場合があるので注意)と、*** Now recording ... (10 sec)
と出てくるので適当に10秒しゃべるとしばらくして変換された音声がスピーカーから流れるようにしてみた。
$ python -u easy_vc.py *** Now recording ... (10 sec) *** extract pitch ... *** extract mel cepstrum *** modify parameters ... *** play!
コードの下の部分はどれか1つコメントを外しておくといろいろな声が出せる。
# どれか一つしか有効にできない # modify_pitch(0.3, pitch_file, mcep_file, output_file) # modify_speed(300, pitch_file, mcep_file, output_file) # hoarse_voice(pitch_file, mcep_file, output_file) # robot_voice(100, record_seconds, mcep_file, output_file) # child_voice(pitch_file, mcep_file, output_file) deep_voice(pitch_file, mcep_file, output_file)
管理人の声で試してみた
元の音声は恥ずかしくて投稿できないので変換後の音声だけ・・・
ロボット声
太い声
子供声
えー、管理人は男です(笑)
これから実験していく統計的声質変換は先の図のパラメータ変換のところを統計的な変換モデルにした場合にあたる。具体的に言うと混合ガウスモデル。今回作ったようにピッチを上げたり、αパラメータでメルケプストラムをいじるような単純な変換ではなく、Aさんの声のメルケプストラムをBさんの声のメルケプストラムに変換するモデルを音声データから学習するというのがポイント。というわけで次回から始めたい。
実はもうほとんどできているので次の投稿は早いと思う。
統計的声質変換 (1) ロードマップ
Pythonで音声信号処理(2011/5/14)のつづき。
@r9y9さんの以下のチュートリアル記事をきっかけに興味をもった統計的声質変換の実験をしてみたい。統計的声質変換とはAさんの声を別のBさんの声に変換する技術のこと。
統計的声質変換クッソムズすぎワロタ(チュートリアル編) - LESS IS MORE
「統計的」という名前からわかるように今回対象としているのはデータに基づいた声質変換である。簡単に手順をまとめると、
- 変換元のAさんと変換先のBさんの音声データを用意する
- この音声データを学習データとしてAさんの声をBさんの声に変換する統計モデルを学習する
- Aさんの任意の音声を統計モデルに入力するとBさんの声になって出てくる
という感じ。コナンの声が毛利小五郎の声になって出てくるという例の蝶ネクタイ型マイクの背景技術である。あのマイクの中にはコナンの声を毛利小五郎の声に変換する何らかの統計モデルが含まれてるんじゃないかな?
少し混乱していたのだが、テキストを入力すると合成音が出てくるいわゆるTTS(Text-to-Speech)とは少し違う。TTSで入力するのはテキストだが、声質変換で入力するのは音声になる。そのため、TTSで必要となる自然言語処理は声質変換では行わない。純粋に音声信号処理の問題のようだ。
先のチュートリアル記事ではJulia(2015/1/13)というプログラミング言語を使っている。このプロジェクトはPythonで音声信号処理(2011/5/14)の一環としてやっているため言語はPythonを使っていきたい。
大ざっぱなロードマップを書いておこう。
ロードマップ(これ)- 統計的じゃない声質変換(音声の分析合成について)
- 音声データのダウンロード
- SPTKによるメルケプストラムの抽出と可視化
- Pythonのdtwライブラリの使い方
- パラレルデータの作成
- scikit-learnのGMMライブラリの使い方
- 声質変換モデルの学習
- GMMによるメルケプストラムの変換
- MLSAフィルタによる音声の再合成
- 音声分析合成システムWORLDの使い方
- WORLDを使った声質変換
基本的に先のチュートリアル記事をなぞる形で進める予定。声質変換の統計モデルとしてメジャーな混合ガウスモデル(GMM)を使う方法を実験してみたい。GMMは前に実装した(2010/5/21)が今回は機械学習のPythonライブラリとして有名なscikit-learnを使ってみる予定。
先のチュートリアルではWORLDという音声分析合成システムが使われているが、今回は前に使ったことがあるSPTK(2012/7/1)という音声信号処理ツールを使っていく。WORLDの方ができあがる音声の品質が優れているようだがSPTKのほうが手軽そうだったので。余力があったらSPTKとWORLDで比較してみたい。
追記(2016/2/25)
- Pythonで実装している人を見つけた。すごい情報量!⇛SunPro C89 特設ページ
TheanoをWindowsにインストール
Deep Learningを実装するのによく使われるTheanoというPythonライブラリをWindowsマシンにインストールしたのでそのときの記録。ただ使うだけだったらPythonとnumpy/scipyをインストールした後にpip installs Theano
で普通に使えていた。実際、GPUが貧弱なMacbook Airではこの方法でインストールしていた。今回、PCを買い替えた(2015/1/19)こともあって、NVIDIA社のGPUを使って高速演算できるようにしてみたというわけ。私のマシンは、
OS : Windows 8.1 64bit GPU: NVIDIA GeForce GTX 760 Ti OEM
という環境。Theano TutorialにもWindowsへのインストール方法は載っているけどはっきり言ってよくわからない。いろいろ調べていたところ以下の記事が自信満々で手順が充実していたので参考にした。
python - Installing theano on Windows 8 with GPU enabled - Stack Overflow
Visual Studio Community 2013をインストール
http://www.microsoft.com/ja-jp/dev/products/community.aspx
最新のVisual Studio Community 2013をインストールする。ライセンスは要注意。個人開発者なら無償利用できる。VSを入れて、日本語言語パックをインストールする直前にWindows Updateでアップデートしておくとよいみたい。日本語言語パックを入れただけでは日本語にならずオプションで設定を変える必要がある。この記事が参考になった。システムの環境変数PATHにC:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin;
を追加しておく。
CUDA Toolkitをインストール
NVIDIAのサイト CUDA 7.5 Downloads からCUDA Toolkitをダウンロードする。CUDAに対応しているビデオカードがないと使えない。自分のビデオカードが対応しているかはここで確認できる。GeForce GTX 760は対応しているみたい。Tiがついているのがないのでやや不安だけど・・・私はデスクトップマシンなのでDesktopの64bitをダウンロード。インストーラにしたがってインストールする。Visual Studio 2013のヘルプを見るとCUDAの機能が追加されたのがわかる。
インストールが終わったらコマンドプロンプトを起動し、
cd C:\ProgramData\NVIDIA Corporation\CUDA Samples\v6.5\bin\win64\Release deviceQuery.exe
を実行するとCUDAの情報が表示される。ProgramDataは隠しディレクトリなので注意。最後にResult = PASS
が表示されていればインストール成功!
(注)このときは6.5が最新だったが7.0がリリースされている
(注)CUDA 7.0の場合はdeviceQuery.exeは自動的にできない。1_UtilitiesにVisual Studioのプロジェクトファイルがあるので自分でビルドすると上の場所(6.5でなく7.0)に実行ファイルができる。
Pythonをインストール
Python、numpy、scipyをインストールする。Pythonは2.7.9を選択。まだ3系を使う気にはなれない・・・Windowsなら全部バイナリで入れたほうが楽だね。64bitマシンだけどPythonは32bitを入れた。
最後にC:\Python27;C:\Python27\Scripts;
を環境変数PATHに追加。
余裕があるならnumpyとscipyにatlasを使うとさらに早くなるかも。これはまたあとで試そう。
(2015/7/24追記)64bit版のPythonの場合はこちらの記事が参考になる。公式のnumpy、scipyは32bitにしか対応していないみたい。ここの非公式版を入れる必要がある。
(2015/7/24追記)numpy、scipy、pycuda(後述)、theano(後述)は全部ここからwhlファイルをダウンロードしてpipでインストールすると時間の節約になる。matplotlib、scikit-learn、pandas、pillow(PIL) なんかもそろっているので簡単に入れられる。
(2015/7/24追記)Python2.7.10で試したらimport theano時にlibpython27.aのundefined reference errorが大量に表示された。Python2.7.9だと起きない。謎だ。
MinGWをインストール
いらないかもしれないけどadaとobjc以外をすべてインストールしておく。この記事が参考になった。久しぶりにMinGWを入れたけどGUIのインストーラがついたんだね。最後にC:\MinGW\bin;C:\MinGW\msys\1.0;
を環境変数PATHに追加。
(注)64bit環境でそろえるならmingw-w64を入れる
Cygwinをインストール
もしかしたらいらないかもしれないけど。TheanoはGithubからソースをダウンロードしてインストールするのでgitはインストールしておく。最後にC:\cygwin\bin;
を環境変数PATHに追加。
Cygwinに何かの拍子でpythonがインストールされてしまうことがあり、WindowsのPythonではなく、/usr/bin/python
が使われるといろいろ面倒くさい。.bash_profile
にexport PATH=/cygdrive/c/Python27:$PATH
を追加し、つねにWindowsのPythonが優先されるようにしておく。
Python distutilsの修正
C:\Python27\Lib\distutils\msvc9compiler.py
を開いて641行目あたりを下のように修正。MANIFESTの1行だけ追加する。
ld_args.append ('/IMPLIB:' + implib_file) ld_args.append('/MANIFEST')
これは何のために必要な修正かわからなかったので元ファイルのバックアップを推奨。もしかしたらPyCUDAのコンパイルに必要なのかな?
PyCUDAをインストール
CUDA ToolkitをPythonから動かすPyCUDAライブラリをインストール。元記事ではソースからコンパイルしているけどどうしてもできなかったのでバイナリをインストール。バイナリは
http://www.lfd.uci.edu/~gohlke/pythonlibs/
にある。Python2.7の32bit版をインストールしたのpycuda‑2014.1+cuda6514‑cp27‑none‑win32.whl
をダウンロード。whl形式のファイルをインストールするにはpipを使う。
pip install pycuda‑2014.1+cuda6514‑cp27‑none‑win32.whl
(注)CUDA7.0をインストールした場合はpycuda-2015.1.2を入れる。
以下のサンプルで動作確認。
# from: http://documen.tician.de/pycuda/tutorial.html import pycuda.gpuarray as gpuarray import pycuda.driver as cuda import pycuda.autoinit import numpy a_gpu = gpuarray.to_gpu(numpy.random.randn(4,4).astype(numpy.float32)) a_doubled = (2*a_gpu).get() print a_doubled print a_gpu
こんなのが出力される。乱数なので実行するたびに結果は変わる。
[[-0.06662463 0.63219547 2.9402144 -3.22129011] [-2.22378063 -1.05216527 3.22402525 1.18233967] [ 0.89209527 -1.18591869 0.09169154 -0.02648566] [-1.0123651 -0.06864624 1.37346625 1.98764777]] [[-0.03331231 0.31609774 1.4701072 -1.61064506] [-1.11189032 -0.52608263 1.61201262 0.59116983] [ 0.44604763 -0.59295934 0.04584577 -0.01324283] [-0.50618255 -0.03432312 0.68673313 0.99382389]]
Theanoをインストール
ようやくTheano!Cygwinのターミナルからgitでインストールする。
git clone git://github.com/Theano/Theano.git python setup.py install
何のエラーも出ずにすんなりインストールできた。GPUを使う場合は、$HOME/.theanorc.txt
のファイルを作成し、以下の設定を書く。
#!sh [global] device = gpu floatX = float32 [nvcc] compiler_bindir=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin # flags=-m32 # we have this hard coded for now [blas] ldflags = # ldflags = -lopenblas # placeholder for openblas support
(注)64bit環境でimport theano
したときにlibpython27.a関係のエラーが表示される場合はlibpython27.aを作り直す必要がある (1) C:\WINDOWS\System32\python27.dll
をC:\Python27\libs
にコピー (2) gendef python27.dll
(gendef
と次のdlltool
はともにmingwのコマンド) (3) dlltool --dllname python27.dll --def python27.def --output-lib libpython27.a
Theanoの動作確認
以下のPythonスクリプトを書いて実行。どうもCygwinのターミナルからはエラーが出て実行できないときがある(実行できるときもある)。MinGWのminttyだとOK。なぜかはよくわからない。
from theano import function, config, shared, sandbox import theano.tensor as T import numpy import time vlen = 10 * 30 * 768 # 10 x #cores x # threads per core iters = 1000 rng = numpy.random.RandomState(22) x = shared(numpy.asarray(rng.rand(vlen), config.floatX)) f = function([], T.exp(x)) print f.maker.fgraph.toposort() t0 = time.time() for i in xrange(iters): r = f() t1 = time.time() print 'Looping %d times took' % iters, t1 - t0, 'seconds' print 'Result is', r if numpy.any([isinstance(x.op, T.Elemwise) for x in f.maker.fgraph.toposort()]): print 'Used the cpu' else: print 'Used the gpu'
GPUが使われていると下のようにビデオカードの名前とUsed the gpu
という出力が出る。
$ python theano_sample.py Using gpu device 0: GeForce GTX 760 Ti OEM [GpuElemwise{exp,no_inplace}(<CudaNdarrayType(float32, vector)>), HostFromGpu(GpuElemwise{exp,no_inplace}.0)] Looping 1000 times took 0.811000108719 seconds Result is [ 1.23178029 1.61879349 1.52278066 ..., 2.20771813 2.29967761 1.62323296] Used the gpu
CPUとGPUのパフォーマンス比較
device = cpu
にしてGPUを使わないようにすると
$ python theano_sample.py [Elemwise{exp,no_inplace}(<TensorType(float64, vector)>)] Looping 1000 times took 9.76800012589 seconds Result is [ 1.23178032 1.61879341 1.52278065 ..., 2.20771815 2.29967753 1.62323285] Used the cpu
という出力になる。Used the cpu
になっている。実行時間を比較するとCPUの場合9.768秒に対し、GPUだと0.811秒なのでGPUを使うことで約12倍早くなっている。これはすごい!苦労したかいがあったね。