のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

Pythonを使った音楽解析をやってみる

皆さんこんにちは
お元気ですか。私は元気です。

本記事はPythonアドベントカレンダー第6日です。
qiita.com

本日はPythonを使った音楽解析に挑戦します。
偶然にも音楽解析に便利なライブラリを発見したので、試してみたいと思います!

音楽解析

本日の挑戦は特徴量抽出と一部の音楽の加工です。
基本的な音楽ファイルの読み込みや特徴量抽出、音楽の加工分離です。
音楽解析に便利なライブラリ、librosaを紹介します。

librosa

librosaとは

librosaは音楽とオーディオの解析のためのライブラリです。
このライブラリは特徴量抽出、音楽の加工分離等の処理ができます。

github.com

音楽を解析してみた。

音楽を取得する。

librosaを試すための音楽を取得します。
今回の音楽データはhttp://marsyasweb.appspot.com/download/data_sets/:GTZANより、GTZAN Genre Collectionを使います。特徴は次の3つがあります。

  1. 30秒程度
  2. 16bitのオーディオファイル
  3. 10種類のジャンル

データ読み込み

取得したデータを読み込みます。
試しに「blues.00000.au」ファイルを読み込みます。

music, fs = librosa.audio.load(filename)

変数musicは信号、fsがサンプリング周波数を示しています。
この信号を可視化すると、次のグラフになります。

f:id:tereka:20161116231921p:plain

音楽情報を取得する。

音楽の情報の取得ができます。
例えば、音楽の時間、ピッチやビートの位置を計算できます。

時間の計算

サンプリング周波数と動画から取得できるベクトルから
時間を計算することが可能です。

print(librosa.samples_to_time(len(music), fs))
ピッチやテンポの計算

次の関数を使うと、ピッチやビートの位置を計算できます。

tempo, beats = librosa.beat.beat_track(y=music, sr=fs)
# (95.703125,
# array([  13,   40,   66,   94,  119,  146,  173,  201,  229,  257,  283,
#         310,  338,  364,  393,  420,  447,  475,  501,  528,  556,  584,
#         612,  640,  667,  694,  720,  748,  775,  803,  829,  856,  883,
#         911,  939,  966,  993, 1021, 1050, 1078, 1106, 1133, 1161, 1190,
#       1217]))

また、このビートの位置の単位はフレームです。そのため、実際の信号の位置は
別途計算しなければなりません。
それから、実際の信号の位置は次のように計算できます。

librosa.frames_to_samples(beat_frames)

スペクトラム分析をやってみる。

スペクトラム分析、いわゆる、周波数分析にも挑戦できます。
通常のスペクトラム分析で使われるのは離散フーリエ変換です。
しかし、離散フーリエ変換は時間の概念がないため、インパルス信号やノイズに弱い傾向にあります。

そのため、短時間で区切りフーリエ変換をかける短時間フーリエ変換(STFT)を行うことが多いです。
短時間フーリエ変換は次のように計算できます。

D= librosa.stft(music)
print D.shape
# (1025, 1293)

また、短時間フーリエ変換の結果をスペクトログラムで表示できます。
スペクトログラムは信号を計算して周波数スペクトルを計算した結果を示します。
x軸を時間、y軸を周波数、色を強度(db)として示しています。

import numpy as np
librosa.display.specshow(librosa.logamplitude(np.abs(D)**2,ref_power=np.max),y_axis='log', x_axis='time')
plt.title('Power spectrogram')
plt.colorbar(format='%+2.0f dB')
plt.tight_layout()

1.blues.00000.au
f:id:tereka:20161117000402p:plain

2.classical.00000.au
f:id:tereka:20161117000521p:plain

音楽特徴量を取得する。

librosaは音楽で頻繁に使われる特徴量を取得できます。

  1. メルスペクトラムグラム
  2. メル周波数ケプストラム係数(MFCC)
  3. クロマ
  4. それらのデルタを計算する

これらは全てlibrosa.featureパッケージを使用すれば特徴量を取得できます。
例えば、音声解析でよく使われるMFCCを取得したい場合は次のように記述できます。

mfcc_feature = librosa.feature.mfcc(music,n_mfcc=13)
mfcc_feature.shape
# (13, 1293)

音楽を加工する。

これまで音楽の解析を行ってきました。
librosaは音楽に対する加工用のメソッドも準備されています。

時間の引き伸ばし、短縮

音楽自体を引き伸ばしたり、短縮したりすることができます。

music_fast = librosa.effects.time_stretch(music, 2.0)
music_slow = librosa.effects.time_stretch(music, 0.5)

print(librosa.samples_to_time(len(music_fast), fs))
print(librosa.samples_to_time(len(music_slow), fs))

要素の抽出

打楽器要素とハーモニック要素を抽出するアルゴリズム
librosaには実装されています。
Documentationによれば、この手法は次の論文のアルゴリズムを利用しています。

http://dafx10.iem.at/papers/DerryFitzGerald_DAFx10_P15.pdf

打楽器要素の抽出

次のコードで打楽器要素を抽出できます。

y_percussive = librosa.effects.percussive(music, margin=3.0)
ハーモニック要素の抽出

また、ハーモニック要素は次のコードで抽出できます。

y_harmonic = librosa.effects.harmonic(music, margin=3.0)

音楽の保存

最後に音楽の保存方法です。
ファイル名と保存したい音楽、サンプリング周波数を入力し保存します。

librosa.output.write_wav("music.wav", music, fs)

グレーの画像に色をつけるネットワークについて発表しました。

皆さんこんにちは
お元気ですか。どきどき。

本記事はDeepLearning Advent Calendar 3日目の記事です。

qiita.com

今日は画像に色を付与するネットワークについて発表します。
せっかくなので、そのタイミングを見計らって公開しています。

発表スライド

慌ててあげた。

www.slideshare.net

グレー画像に色をつけること

はじめに、グレー画像から元のRGB画像へ変換する方法はありません。
そこで、グレー画像を元のRGB画像へどう変換するかが本日の内容となります。

参考となる論文は次のとおりです。
https://arxiv.org/pdf/1603.08511.pdf:Colorful Image Colorization

著者のgithub
github.com

なぜ、この論文にしたの?

面白そうだからですね。
一時期グレー画像をカラー画像にする論文をTwitterで見て、
おっ、こんな研究もあるのかと思ったことですね。

会社で話していたら、漫画をカラーにすることができるのではないかと聞いて
この研究の夢が頭で広がってました。(誰か作って)

発表概要

今回の発表では、グレー画像に色を付与するニューラルネットワークを作成・検証したお話です。
発表詳細は発表スライドを見てください。

この論文は、
ニューラルネットワークの誤差関数をクラスリバランスを考慮したCross Entropyを使った方法がみそです。
従来の手法よりもリアリティを出すことができています。

最後に

Chainer実装には時間が足りなかったorz

ChainerのTrainerを使ってみた

皆さんこんにちは
お元気ですか。最近、Chainer便利でびっくりしたような頃合いです。

頻繁に更新することで有名なChainerですが、久々にupgradeすると以前よりも
シンプルなタスクについて、簡単に学習ができます。

Trainer

Chainer version 1.11.0よりTrainerと呼ばれる機能が実装されています。
以前まで学習用バッチ処理を自前で書くようなことが
必要でしたが、これを使うことによってバッチ処理を書く必要がなくなります。

実際の機能としてはある処理をhockしたり、グラフを出力したり
レポートを表示したりと学習中に確認したいグラフは沢山あります。

それらのグラフを可視化したいといったことは往々にしてあります。

Trainerの基本的な使い方

Trainerを使うと、Progress Barやlogを自動的に吐き出すことができます。
通常のモードでは、Trainerを基本的に使うことができます。

Extensionsを使うことにより、Trainerを使えます。
殆どExample通りですが、以下が最低限のコードとなります。

# coding:utf-8
from __future__ import absolute_import
from __future__ import unicode_literals
import chainer
import chainer.datasets
from chainer import training
from chainer.training import extensions
import chainer.links as L
import chainer.functions as F


class MLP(chainer.Chain):
    def __init__(self, n_units, n_out):
        super(MLP, self).__init__(
            l1=L.Linear(None, n_units),
            l2=L.Linear(None, n_units),
            l3=L.Linear(None, n_out),
        )

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)


train, test = chainer.datasets.get_mnist()
train_iter = chainer.iterators.SerialIterator(train, 32)
test_iter = chainer.iterators.SerialIterator(test, 32,
                                             repeat=False, shuffle=False)
model = L.Classifier(MLP(784, 10))
optimizer = chainer.optimizers.SGD()
optimizer.setup(model)
updater = training.StandardUpdater(train_iter, optimizer, device=-1)
trainer = training.Trainer(updater, (10, 'epoch'), out="result")

trainer.extend(extensions.Evaluator(test_iter, model, device=10))
trainer.extend(extensions.dump_graph('main/loss'))
trainer.extend(extensions.snapshot(), trigger=(10, 'epoch'))
trainer.extend(extensions.LogReport())
trainer.extend(extensions.PrintReport(
    ['epoch', 'main/loss', 'validation/main/loss',
     'main/accuracy', 'validation/main/accuracy']))
trainer.extend(extensions.ProgressBar())
trainer.run()

Trainerにはupdate方法を宣言します。
Trainer#extendを使うことで、一定の条件の元で起動します。

Extension 概要
Evaluator 一定の期間で評価する。(validation)
dump_graph グラフを表示する。
snapshot 一定の間隔(ユーザ指定)でモデルを保存する
LogReport ログとして出力する。
PrintReport print文を使って現状をprintする。(以下に例あり)
ProgressBar Progress Barを表示する。

上記の場合の出力例は次のとおりです。

epoch       main/loss   validation/main/loss  main/accuracy  validation/main/accuracy
1           0.624464    0.306581              0.850083       0.913538
2           0.282575    0.240019              0.919283       0.932608
     total [##########........................................] 21.87%
this epoch [#########.........................................] 18.67%
      4100 iter, 2 epoch / 10 epochs
    61.825 iters/sec. Estimated time to finish: 0:03:56.958209.

また、結果として、出力されるresult配下のディレクトリは次のとおりです。

-rw-r--r--  1 Tereka  staff     2250 10 24 23:33 cg.dot
-rw-------  1 Tereka  staff     2590 10 24 23:39 log
-rw-------  1 Tereka  staff  4775680 10 24 23:39 snapshot_iter_18750

DatasetMixinを使った拡張

ImageNetのサンプルにありますが、Real Time Augmentationを行うことができます。
これを応用すると様々な用途で使うことができて非常に便利です。

例えば、ファイルを順次読み出したい場合に
ファイルをデータとして渡しておき、それを処理するタイミングで順次読み出すことができます。
また、データを加工することも自由にできるため、自由にデータに変換を加えることができます。

chainer.dataset.DatasetMixinを使って以下のような拡張が可能です。
以下の拡張はシンプルです。chainer.dataset.DatasetMixin#get_exampleを使うと実現できます。
このメソッド内部にファイルを読み込む処理を作ると、
実際にファイルを読み込むことが可能となります。
例は次の通りです。

import skimage.io
class DatasetExampleMixin(chainer.dataset.DatasetMixin):
    def __init__(self,X,y):
        self.X = X
        self.y = y

    def __len__(self):
        return len(X)

    def get_example(self,i):
        """
        Fileを読み出す処理
        """
        return skimage.io.imread(X[i]), y[i]

最後に

Trainer凄く便利!これを使いこなしてTrainerを使えるChainer使いになりましょう。

Keras.jsを使って、JavascriptでDeepLearningを動かしてみる

皆さんこんにちは
お元気ですか?一人で箱根温泉りらっくすできました。

Keras.jsが出て、遂にKerasをjavascriptを扱えるようになりました。
(これ公式なのかどうかが非常に不安で、きっと違う)
ということで実際に動かしてみようと思います。

Kerasについて

Keras(Python

KerasはDeepLearningのライブラリです。
Theano、TensorFlowをバックエンドとして動作し、
切り替えることが可能です。

github.com

Keras-js

今回扱うのは、Keras-jsです。
github.com

ブラウザ上で、学習したKerasのモデルを走らせ、GPUWebGLを使って
扱うことができます。Kerasのjsonファイルとhdf5から読み出すことができます。
(backendはTheano,TensorFlowおそらくどちらも可)

ディレクトリ構成

デモのディレクトリ構成を確認します。
treeコマンドを実行すると次のような出力(一部省略)となります。

├── assets
│   ├── imdb-bidirectional-lstm.png
│   ├── inception-v3.png
│   ├── mnist-cnn.png
│   ├── mnist-vae.png
│   └── resnet50.png
├── base.css
├── data
│   ├── imdb_bidirectional_lstm
│   │   ├── imdb_bidirectional_lstm.json
│   │   ├── imdb_bidirectional_lstm_metadata.json
│   │   ├── imdb_bidirectional_lstm_weights.buf
│   │   ├── imdb_dataset_test.json
│   │   ├── imdb_dataset_word_dict_top20000.json
│   │   └── imdb_dataset_word_index_top20000.json
│   ├── inception_v3
│   │   ├── inception_v3.json
│   │   ├── inception_v3_metadata.json
│   │   └── inception_v3_weights.buf
│   ├── mnist_cnn
│   │   ├── mnist_cnn.json
│   │   ├── mnist_cnn_metadata.json
│   │   └── mnist_cnn_weights.buf
│   ├── mnist_vae
│   │   ├── mnist_vae.json
│   │   ├── mnist_vae_metadata.json
│   │   └── mnist_vae_weights.buf
│   └── resnet50
│       ├── resnet50.json
│       ├── resnet50_metadata.json
│       └── resnet50_weights.buf
├── dist
│   └── bundle.js
├── notebooks
├── src
├── webpack.dev.config.js
└── webpack.prod.config.js
  1. assets・・出力例の.png
  2. data・・keras.jsonで必要なNeuralNetworkのデータ、*.json, *_metadata.json, *weights.bufの組み合わせ
  3. notebooks・・Jupyter Notebookで、学習ソースコードが入っている。
  4. src・・ソースコード(html,css)が含まれている 。

Demoを動かしてみよう

既に殆どのものは準備されているので、
実は非常に簡単です。

Clone

まずは、リポジトリをクローンします。

git clone https://github.com/transcranial/keras-js

サーバの起動

npmを使ってinstallとサーバの起動を行います。

npm install
npm run server

http://127.0.0.1:3000にアクセスすると以下の画面を表示することができます。

f:id:tereka:20161016190946p:plain

ホームには様々な画面があり、サンプルを動かすことができます。
試しにMNISTを動かしてみましょう。

f:id:tereka:20161016191158p:plain

MNISTのDemoでは、四角の枠に絵(数字)を自分で書いて、どのように認識をさせることができるのかを
見ることができます。これを応用すると自分のWebサービスを構築した場合に、
Kerasをブラウザで動作させることができます。

カスタマイズポイント

Demoでは、既に準備されている
モデルを使って、表示をしてみました。TOPのREADME.mdには
他のモデルはどう扱うかなど、カスタマイズのポイントが紹介されているので
それを紹介したいと思います。

Model もしくは、Sequentialの構成の出力

次のようにするとモデルを出力することができます。

model = Sequential()
model.add(...)
...
model.save_weights('model.hdf5')
with open('model.json', 'w') as f:
  f.write(model.to_json())

hdf5として出力したファイルを変換する。

encoder.pyはkeras-js/encoder.pyに含まれています。

python encoder.py /path/to/model.hdf5

上記を実行すると、model_weights.bufとmodel_metadata.jsonが生成されます。

javascript上でのkerasのNeural Networkの宣言

今まではPython上でしたが、javascriptでは次のように宣言します。
model, weights, metadataの3種類のファイルのパスを宣言すれば良いです。
また、gpuのtrue or falseの宣言が必要となります。

const model = new KerasJS.Model({
  filepaths: {
    model: 'url/path/to/model.json',
    weights: 'url/path/to/model_weights.buf',
    metadata: 'url/path/to/model_metadata.json'
  }
  gpu: true
})

データ形式

javascriptの形式ですが、WebGLのFloat32Arrayで扱います。
MNISTの場合は次の変数宣言がありました。784次元の配列の宣言です。

this.input = new Float32Array(784)

予測

modelにはPython同様predictメソッドがあり、Float32Arrayを入力とすることで動作します。

model.ready().then(() => {
  model.predict(inputData).then(outputData => {
     //ここにoutputを書く。
  })
})

感想

個人的には結構扱いやすいKerasをベースとした構築のため
使ってみたいが、、、実のところ私がWebサービスを作ることが少ないという。。

PyConJPで触発されてPythonのType Hints(型ヒント)について書いてみた

皆さんこんにちは
お元気ですか。ラーメン食べたくなってきた。

私自身は自他共に認めそうなPython2.7.x userのつもりなのですが、
PyConJP2016でAndrey Vlasovskikh氏の講演でType Hints(型ヒント)の話があって
Python3.5すごいとか思いながら聞いてました。

ぶっちゃけ型ヒント何か知りません。

ということで、今回はPythonにおけるType Hintsを調べてみました。

Type Hintsについて

そもそもType Hintsってなに?

型に対するアノテーションです。具体的には実例を見るとおそらくわかりやすいです。

PEP484に規定されたType Hintsを提供するらしく、Python3.5で導入されています。
何のことやらわからないので、早速読んでみようと思ったら意外に長かったので読めておりません。
www.python.org

何が嬉しいのか

調べるとどうやらこんなメリットがあるそうです。

  1. Type Hintsを使うことで静的解析を容易にする。
  2. 容易なリファクタリングドキュメンテーションの作成
  3. アノテーションの標準的な書き方の定義

調べていたのですが、このあたりであまり見慣れない名前なので、何を言っているんだろうとおもってきてました・・・
ということで、そろそろ実例を

実例

よくあるチュートリアルの例

さて、既に調べるのに疲れたのでPython3.5に書いてある例に触ってみましょう。

def greeting(name: str) -> str:
    return 'Hello ' + name

name:strが、name変数がstr型であること、 -> strが返り値がstrを期待するといったことを示しています。

変数の別名

変数は前の紹介のstrだけではなく、複数の型を定義することができます。
次にその例を掲載します。
因みに、Tuple等を使って、定義することができます。

from typing import TypeVar, Tuple

T = TypeVar('T', int, float, complex)
def typetest(var: Tuple[T, T]) -> Tuple[T, T]:
    return var

Collections

Collectionsを使う場合はtypingのListやSetを使うことができます。
以下には例を記載します。

# coding:utf-8
from typing import TypeVar, List

T = TypeVar('T', int, float, complex)

def collection_test(list_var: List[T]) -> T:
    return list_var[0]

Generics

Genericsを使うと、ユーザ定義のGenericsを書くことができます。
ここまで来るとC++のtemplate構文を思い出してきますが、
そんなものを書くこともできます。

# coding:utf-8
from typing import TypeVar, Generic

T = TypeVar('T', int, float, complex)

class GenericTest(Generic[T]):
    def __init__(self,init: T):
        self.init = init

    def get_init(self) -> T:
        return self.init

    def set_init(self, init: T):
        self.init = init

書いてみたけど、どんな効果があるのでしょう

ここまでいくつか試しに書いてみましたが、
実際にどんな効果があるのかを確認したいので、見てみましょう。

PyCharm

PyCharmのVersion5.0以降でType Hintsがあることによって、
警告を出すようになります。

f:id:tereka:20160924004700p:plain

書いている時に、実際に自分が期待していない型を入力していることがわかるので、
その地点で警告を出してくれます。超便利
C++とか書いている人だと、結構ありがたいような感じではないでしょうか。

Mypy

Mypyは経験的な静的チェックのツールで、動的、静的のタイピングの良いところ取りする為に開発したようです。
Pythonの強力かつ、便利な型チェックで、基本的なPythonプログラムをサポートしているそうです。

Mypyが先に作成されて、Type Hintsとして導入されたようです。
インストールは次のコマンドです。私は知りませんが、mypyと呼ばれるpipで取得できるソフトウェアは別の内容とのことです。

pip install mypy-lang

さて、試しに次のコードに対してmypyを書けてみようと思います。
型が合わない、greetingに42のintを使っています。

# coding:utf-8

def greeting(name:str) -> str:
    return 'Hello, {}'.format(name)

greeting(42)

次のコメントが出てきます。

mypy TypeHints/typehint.py
TypeHints/typehint.py:6: error: Argument 1 to "greeting" has incompatible type "int"; expected "str"

つまり、greeting関数に対して、第一引数はstrを期待しているが、
intを与えていると言われています。
ここで期待していない値を投入していることがわかります。

最後に

Type Hintsは面白いけれどもversion2,3適用が難しそうだから少なくとも後数年は使わないかな
どうやらAndrey Vlasovskikh氏曰く、Python2のほうがPyCharm的には利用者が多いそうなので、
Python3がそれをうわまわる人数になったときにどうなるかによってType Hintsの扱いをどうするかとか、
コーディング規約が変わっていくのかなと思ったりもします。

参考にType Hintsに触れている規約があったら見てみたいとも思っています。