scikit.learn手法徹底比較! K近傍法編

今回はK近傍法を用いて手書き文字データを分類する.

K近傍法は, あるデータのクラスを分類する際に, そのデータから距離が近い順にK個訓練集合からデータを取り出し, それらのラベルの投票によって分類対象のラベルを決定するシンプルなアルゴリズムである.

一見, 学習段階では何もしなくて良さそうだが, 与えられたデータと近いデータを効率的に探索するためのデータ構造を構築する必要がある. また, バリエーションとしてはラベルの投票の際に, 投票の重みを均一にせず, 距離に応じた重みを用いるものなどがある.

scikit.learnで近傍法を扱うクラスは

  • KNeighborsClassifier
  • RadiusNeighborsClassifier

の2つである. 前者は上で説明したK近傍法で, 後者は分類対象から近いものK個の投票ではなく, 分類対象から与えられた距離以内に存在するデータの投票によってラベルを決定する.

KNeighborsClassifier

チューニングするパラメーターは

  • K : いくつのデータ点の投票でラベルを決定するか
  • weights : 投票の重みを均一(uniform)にするか距離に応じた重み(distance)にするか

のみである. 使いやすくて素晴らしい.

パラメーターKは候補range(1,15,2)からクロスバリデーションによって決定した.
このパラメーターを用いて計測した結果は次の通りである.

データ数 正答率(uniform) 正答率(distance) 学習時間(sec) 平均予測時間(msec)
1000 0.831 0.831 0.030 0.753
3000 0.880 0.886 0.159 2.630
5000 0.896 0.899 0.360 4.375
10000 0.910 0.916 1.083 8.985
20000 0.925 0.928 3.563 18.044

学習時間や平均予測時間は, weightsに関わらない. 正答率は投票の重みをdistanceにすることで少し上昇している(あまり上昇しないとも言える). データ数を増やすにつれ, この範囲では安定に正答率が上昇しており, モデルに囚われないノンパラメトリックな手法の強さを感じる.

学習時間に関して議論する前に, K近傍法の探索アルゴリズムについて説明する.
K近傍法ではパラメーターalgorithmによって近傍点の探索方法を切り替えられる

  • ball_tree : BallTreeと呼ばれるデータ構造を用いる. 詳細はFive Balltree Construction Algorithmsを読めばよさそう
  • kd_tree : KD木と呼ばれるデータ構造を用いる. 有名
  • brute : 単純に全ての点との距離を計算する(ブルートフォース)
  • auto : 自動で適切なアルゴリズムを選択する

autoにしたときは次のように用いるアルゴリズムが決定される.

 if self._fit_method == 'auto':
   # BallTree outperforms the others in nearly any circumstance.
   if self.n_neighbors < self._fit_X.shape[0] / 2:
     self._fit_method = 'ball_tree'
   else:
     self._fit_method = 'brute'

要はデータ数が投票に用いる近傍数Kの2倍以上ならBallTreeを用いる(ほぼ常にそうなるだろう). よって上の時間の計測結果はBallTreeに関するものである. そこでアルゴリズムごとにかかる時間を計測した.

学習時間(秒)

データ数 brute kd_tree ball_tree
1000 0.001003027 0.0169811249 0.030
3000 0.00289011 0.0603270531 0.159
5000 0.0047779083 0.1013519764 0.360
10000 0.0138828754 ? 1.083
20000 0.0262858868 ? 3.563

平均予測時間(ミリ秒)

データ数 brute kd_tree ball_tree
1000 0.3043016195 4.178539896 0.753
3000 0.9247073889 14.1015777111 2.630
5000 1.5461462021 21.6120795012 4.375
10000 3.1277508974 たくさん 8.985
20000 6.5119547844 たくさん 18.044

学習時間に関しては, さすが何もしないだけあってbruteの経過時間はあってなきがごとしである. kd_treeの方がball_treeより少し速かった. 平均予測時間に関しては意外なことにbruteが一番速い. kd_treeは多次元データに弱いため非常に遅い. ball_treeの特性はよく知らないので誰か教えてください.

この実験より, autoはそこまで信用できないことがわかる. K近傍法を用いると決めた場合は自分でアルゴリズムを試した方がいいかもしれない.

クロスバリデーションをした印象は, 正答率・必要時間ともにKの値に関してそこまで敏感ではなかった. 特に必要時間はball_treeの場合もbruteの場合もほとんど変化しなかった. 正答率のみ記載しておく.

K\データ数 N=1000 N=3000 N=5000 N=10000 N=20000
1 0.824 0.862 0.886 0.909 0.921
3 0.805 0.863 0.887 0.907 0.924
5 0.791 0.870 0.889 0.907 0.926
7 0.787 0.869 0.888 0.906 0.922
9 0.780 0.863 0.885 0.902 0.919
11 0.777 0.858 0.881 0.904 0.917
13 0.763 0.854 0.877 0.899 0.915

RadiusNeighborsClassifier

データから与えた距離以内の点を元にクラスラベルを決定するクラスである. パラメーターとして距離を指定しなければいけない. クロスバリデーションによって広い範囲から最適な距離を探索したが, 真っ当な性能が出る距離が見つけられなかった. また, 小さすぎる距離(おそらく点と点の間の最短距離以下の距離)を指定した場合, 不親切なエラーメッセージと共に終了してしまう. これらの理由から, このクラスに関しては細かい検証を打ち切った. パラメーターの探索方法が悪かったか, 使い方を間違っていた可能性がある.

まとめ

たくさんデータがある場合は, そこそこ性能が出る印象のあるK近傍法. 今回の実験でもそこそこの正答率を観測できた.

scikit.learn手法徹底比較! ナイーブベイズ編

scikit.learnの分類手法を比較するこの企画. 今回はナイーブベイズを検証する.

ナイーブベイズはi番目の入力ベクトルの各次元が, クラスラベルが与えられると互いに独立, すなわち

となると仮定する分類手法である. ただし, の次元数, はi番目の入力ベクトルのj個目の要素を指す.

学習段階では訓練データから各クラスjごとに確率分布を学習し, 分類する際にはが最大になるようなクラスに分類する.

scikit.learnが提供するナイーブベイズのクラスは

  • Gaussian Naive Bayes
  • Bernoulli Naive Bayes
  • Multinomial Naive Bayes

の3つがある.

Gaussian Naive Bayesは分布ガウス分布で表現するクラスである. 訓練データからこのガウス分布のパラメーターを学習する.
Bernoulli Naive Bayesは入力ベクトルの要素が[0,1]の2値を取る場合のナイーブベイズである. 分布はベルヌーイ分布で表現される.
Multinomial Naive Bayesでは, 入力ベクトルの要素がi番目のデータにおいてj個目の要素が何回出現するか(例えば, ある記事において単語soupが何回出現しているか)を表す. なんかテキスト分類とかに使われているらしい.

今回は入力ベクトルとして各次元が画素の値(0-255)なので使えそうなのはGaussianとBernoulliである. Bernoulliの場合はオプションの閾値を設定すると, 入力ベクトルの値が閾値以上の場合は1, 以下なら0として扱う.

なお入力データは正規化している.

Gaussian Naive Bayes

コンストラクタに引数がないシンプルなクラスnaive_bayes.GaussianNBを用いる.

簡単に使えるかと思いきや

/usr/lib/pymodules/python2.7/sklearn/naive_bayes.py:174: RuntimeWarning: divide by zero encountered in log n_ij = - 0.5 * np.sum(np.log(np.pi * self.sigma_[i, :]))

というエラーメッセージが発生し, 真っ当に動作しなかった.

エラーメッセージから察するに, おそらく分散の無い要素(特定のクラスで常に一定の値を取る要素)が存在する場合, 0割りが発生しうまく動作しないのだろう.

そこで, とりあえずの解決策として, 全要素である程度の分散を持つために元のデータにガウス分布から生成したノイズを足しあわせた. その結果正常に動作するようになったが, データ数1万のケースでも分類精度が75%程度でしかもバッドノウハウにも程がある感じだったので, 詳細な検証は取りやめた.

Bernoulli Naive Bayes

調整するパラメーターは次の3つである.

  • fit_prior : 事前分布(クラスごとのデータ数の偏り)を考慮するかどうか
  • alpha : 各次元において1を取る確率に関する事前分布を制御する
  • binarize : 元のデータを2値化するときに用いる閾値

alphaに関しては詳細な説明が必要だろう. alphaを用いることで, クラスcに属するデータのj番目の要素が1となる確率は

と学習される. ここでは学習データにおいてクラスがcである要素の数, は学習データにおいてクラスがcでかつj番目の要素が1となる要素の数である. alphaが小さい程, この確率は純粋なデータ内の比率に近づく.

元のデータのラベル比率が均等に近いためか, fit_priorはTrueにしてもFalseにしてもあまり結果が変わらなかったため, 以下fit_priorはTrueと設定する.

クロスバリデーションによってalphaをnumpy.logspace(-5, 0, 6), binarizeをnp.linspace(-0.2,1.2,6)から選んだ. その選んだパラメーターに対して得られた結果は次の通り.

訓練データ数 正答率 学習時間(sec) 平均予測時間(msec)
1000 0.8134 0.0377149582 0.0484575987
3000 0.8166 0.1247229576 0.0482898951
5000 0.8152 0.221326828 0.0484192133
10000 0.8157 0.4449100494 0.047160697
20000 0.8179 0.919823885 0.0482817888

正答率が見事に上がらないw. やはりモデルの仮定に無理があるためか, 2値化の際の情報の損失が激しいのか. 一方, 学習時間はデータ数に対して線形である. また平均予測時間はデータ数に影響を受けない. これらはアルゴリズムを想像すればまあ納得の行く結果である.

クロスバリデーションをして得た感じとしては, 学習時間は全くパラメーターの影響を受けない. また正答率もalphaなら0から1, binarizeなら0から1という常識的な値から選んでいればそこまで変化しなかった.

まとめ

その性能の低さから「論文で負けるためにある分類器」と言われることもあるナイーブベイズさん. この問題でも性能は低かった. だがテキスト分類など一部の分野では使われており, また学習時間も超短いため, 活躍する領域があるんだろう. ナイーブベイズの仮定がうまく効いてくるデータセットでも試してみたい.

scikit.learn手法徹底比較! SVM編

問題設定や細かい実験手法は下のページを参照.
scikit.learn手法徹底比較! イントロダクション

今回は言わずと知れたSVM(サポートベクターマシン)を試す. 訓練データ数を増やしていったときに, 手書き文字の分類性能がどのように推移していくかを調べる.

SVMの詳細な解説は別の文献を引いて欲しい. PRMLを読んでもいいしこのスライドは結構わかりやすい.
概略だけ書くとSVMは2クラス分類のためのアルゴリズムである. データが散らばる多次元空間を超平面で区切り, データを2つに分類する. その超平面をマージン最大化という基準でひくとわりとうまく行くねというアルゴリズムである. そこで元の空間で分類できなくともカーネルで定義された別の空間だとうまく行くことがあるため, 分野によって様々なカーネルが考案されている. カーネルは2つのデータを引数として取る関数でその値はおそらく類似度を意味する.

学習段階では学習データをうまく分離する超平面を求め, 分類段階ではそれを元にデータを分類する.

scikit.learnでは分類に関するSVM

  • SVC
  • LinearSVC
  • NuSVC

の3つである. SVCは標準的なソフトマージン(エラーを許容する)SVMである. 一方, NuSVCはエラーを許容する表現が異なるSVMである. LinearSVCはカーネルが線形カーネルの場合に特化したSVMであり, 計算が高速だったり, 他のSVMにはないオプションが指定できたりする. NuSVCとSVCは数学的に等価らしいので, 今回はSVCとLinearSVCに絞って検証する.

なお, 全ての場合においてデータは正規化している. すなわち平均0, 分散1のデータに変換している. おそらくRBFカーネルでは不要なのだが(カーネル幅で等価な表現が可能), 線形カーネルでは多分重要.

SVCクラス

scikit(0.10)のSVCクラスは結構引数が多いが, 分類のみを必要とする際に重要なのは

  • どのカーネルを使うか
  • ペナルティ項Cをどれぐらいに設定するか

である. カーネルはRBFカーネル(Gaussカーネル), 線形カーネル, 多項式カーネル, シグモイドカーネル, ユーザーが計算済みのカーネル行列を使用, から選択できる. 全部試すのは面倒なので, 王道のRBFカーネルのみ試す. RBFカーネルを用いる場合はカーネル幅も決定する必要がある. ペナルティ項は誤分類にどの程度の罰則を与えるかを決める. 大きいほど誤分類に厳しく複雑な境界面を用いてでもデータを正しく分類しようとする.

ちなみにSVCクラスは多クラス分類を行う際に, one-vs-one戦略を採用している. これは, クラスの組み合わせごとに2クラス分類器を作成し, それらの分類器の投票によってクラスを決定する. この問題では, 10クラス存在するので, 10*9/2=45個の識別器が用いられる.

RBFカーネルは次の式で表される.

クロスバリデーションによって2つのパラメーター

を求める必要がある.

今回は, ペナルティ項の候補としてnumpy.logspace(4, 8 ,8), の候補としてnumpy.logspace(-4, -2, 8)を用いた. もう少し広い範囲と狭い範囲でもクロスバリデーションを行い, この範囲で十分だと判断した. コードでは次のように表現できる(実際は異なるコードを用いているが).

for sigma in np.logspace(-4, -2, 8):
  for C in np.logspace(4, 8, 8):
    clf = svm.SVC(C=C, gamma=sigma, scale_C=True)
    scores = cross_validation.cross_val_score(clf, datas, labels, cv=5, n_jobs=5)
    print "score(mean):",np.mean(scores)

SVCクラスの引数であるscale_Cは, ペナルティ項Cをデータ数で割るかどうかである.

訓練データ数を変化させ, それぞれクロスバリデーションで決定したパラメーターに対して, 正答率, 学習時間, 平均予測時間を求めた.

訓練データ数 正答率 学習時間(sec) 平均予測時間(msec)
1000 0.883 0.49 0.51
3000 0.916 2.63 1.14
5000 0.931 7.13 1.91
10000 0.946 29.47 3.70
20000 0.957 69.30 5.06

正答率は思ったより高かった. 画像の性質を使わず高次元データを直接入れてもこれぐらいは出るのか. また, 正答率はデータ数=10000のときに94.6%となっているが, この記事を読むと98.6%までいけるらしい. うーん, 使ってるデータが違うのか, クロスバリデーションが甘いのか.

学習時間はデータ数の2乗オーダー程度で推移しているかに見える. マニュアルによると, データ数の2乗から3乗程度の速度らしい. 予測時間は訓練データ数に対して線形に近い. これはサポート数と比例関係にあるのではと予想しているが検証していない. なお, が大きな値にするほど計算時間は増加した. 例えばデータ数3000のとき, が10^-4のときに比べ10^-2のときは計算時間が3倍程度に増加している. 一方, 上の範囲ではペナルティ項は計算時間にさほど影響を与えなかった.

クロスバリデーションでは, Cが小さすぎると悲しい程結果がでないが(正答率0.10付近), Cが大きすぎてもそこまでの性能劣化は無かった. また, 上のコードのパラメーター集合では, データ数が変化しても, 最適なパラメーターはあまり変わらなかった. データ数10000のときにクロスバリデーションにおいて得たスコアと, クロスバリデーションにかかった時間をまとめたをリンクしておく.

その他のパラメーター

ここまでで説明していないSVCクラスのパラメーターとして

  • cache_size
  • shrinking

がある. cache_sizeはカーネル行列(カーネル関数の計算結果をまとめたもの)をキャッシュするサイズを指す. カーネル行列はデータ数によっては大きすぎるので, 全てを前もって計算せずに必要なときに計算してキャッシュするようだ. データ数20000のときにデフォルトの200MBから2GBに上げてみたが全く効果がなかった(カーネル行列は1.6GB程あるのだが). もっとデータ数が大きくなったら効いてくるのかもしてない.

shrinkingはBool値で, Trueのときは, 双対問題のパラメーターにおいて最終的に0となるパラメーターを早い段階で検出し, それを問題から取り除くことで計算時間を短縮するテクニックっぽい. データ数20000のときに試しにFalseにしてみた(デフォルトはTrue)が計算時間は変化しなかった. データ数の問題かデータの性質のせいかはわからない.

線形カーネル

線形カーネルでは内積カーネルとして用いる. scikit.learnはSVMの処理を通常LIBSVMというライブラリに丸投げしているようだが, LinearSVCではLIBLINEARが裏では動いている.
そして多クラス分類にはone-vs-all戦略を用いている. これはそれぞれのクラスごとに, そのクラスと残りのクラスに分ける分類器を作成し, それらをまとめたものから最終的に分類結果を決定する手法である. 今回は10クラスなので分類器は10個となる. そしておそらくデータは, 全ての分類器の中で注目したクラスに割り当てられたもののうち, データが分離境界線から最も離れた分類器のクラスに割り当てられる(未確認).

LinearSVCでは以下の項目をチューニングする.

  • 損失関数をL1にするかL2にするか. L1ならヒンジロスになり, L2なら二乗ヒンジロスになる.
  • 正則化項をL1にするかL2にするか. 通常のSVMはL2であるが, L1にすることでパラメーターが疎になることが期待できる
  • C : ペナルティ項
  • intercept_scaling : 入力ベクトルに付加する定数項(超平面の平行移動に該当)

このスライドの19ページに詳しいが, SVMの最小化する目的関数は次のように表せる.

第一項が予測と実際のラベルとの差を表す損失関数, 第二項が大きすぎるパラメーターにペナルティを与える正則化項である. LinearSVCでは, これらをL1にするかL2にするか選べる. 更に, LinearSVCでは多クラス分類用の実装が存在する. その論文は読んでいないので細かい手法はわからない.

これらのことから次の4つのLinearSVCのバリエーションを比較する.

  • Standard : 損失関数L1, 正則化項L2 (通常のSVM)
  • LossL2 : 損失関数L2, 正則化項L2
  • PenaltyL1 : 損失関数L2, 正則化項L1
  • Multi : 多クラス用SVM

それぞれ次のようなコードで生成できる.

Standard = svm.LinearSVC(C=C, intercept_scaling=intercept, multi_class=False, scale_C=True, loss="l1", penalty="l2", dual=True)
LossL2 = svm.LinearSVC(C=C, intercept_scaling=intercept, multi_class=False, scale_C=True, loss="l2", penalty="l2", dual=True)
PenaltyL1 = svm.LinearSVC(C=C, intercept_scaling=intercept, multi_class=False, scale_C=True, loss="l2", penalty="l1", dual=False)
Multi = svm.LinearSVC(C=C, intercept_scaling=intercept, multi_class=True, scale_C=True)

引数dualはTrueのとき元の最小化問題の双対問題を解くことでパラメーターを求める. StandardとPenaltyL1はそれぞれTrueとFalseしか選択できない. またMultiではこのパラメーターは無視される. よって実質dualが影響するのはLossL2のみである.

それぞれの分類器において, パラメーターC, intercept_scalingをそれぞれ候補numpy.logspace(-1, 4, 8), numpy.logspace(-2,3,8)から5-foldクロスバリデーションによって選択した. しかし, 選択したパラメーターはSVCの場合と異なり正答率が最大のものではない. LinearSVCでは, わずかながらの正答率の向上と引き換えに, 計算時間が大きく増加(ときには8倍程度)してしまうケースが多い. そのため, 正答率が最大のケースeから0.3%以内でクロスバリデーションが最も短時間で終了したパラメーターを使用する. また, たぶん大丈夫だとは思うがSVC程気合を入れてパラメーター調整をしていないため, 必死でやればもう少し性能が向上するかもしれない.

結果は次のようになった

正答率
データ数 Standard LossL2 PenaltyL1 Multi
1000 0.849 0.849 0.843 0.852
3000 0.880 0.884 0.875 0.886
5000 0.892 0.894 0.886 0.899
10000 0.901 0.903 0.903 0.910
20000 0.910 0.908 0.908 0.914

どの手法もそこまで大きな差はない.

学習時間, 平均予測時間

学習時間(秒)

データ数 Standard LossL2 LossL2Dual PenaltyL1 Multi
1000 0.60 0.47 0.43 1.27 0.57
3000 0.87 1.66 0.87 3.95 1.12
5000 1.13 3.97 2.21 5.98 1.29
10000 1.80 9.39 4.77 32.18 5.43
20000 7.23 19.82 9.88 63.81 7.69

LossL2Dualは双対問題を解いた場合のLossL2である. 一方, LossL2は主問題を解いた場合である.
LinearSVCはパラメーターによって学習時間は大きく影響を受けるため, あまりこの値は正確ではなく桁以上の情報は期待できない. ただ, 傾向として双対問題を解く手法(Standard, LossL2Dual)は主問題を解く手法(LossL2, PenaltyL1)よりも速い. これはデータ数 > 入力データの次元ならば主問題を解いた方がいいという公式の解説と異なる(なんでだろう?). データ数に対する反応もパラメーターが変化しているため不安定だが, データ数に線形っぽい.

平均予測時間(ミリ秒)

データ数 Standard LossL2 PenaltyL1 Multi
1000 0.0090 0.0090 0.0090 0.0090
3000 0.0093 0.0093 0.0093 0.0093
5000 0.0096 0.0096 0.0096 0.0096
10000 0.0097 0.0097 0.0098 0.0098
20000 0.0099 0.0099 0.0099 0.0099

平均予測時間は手法ごとに差はない. また, データ数が増えるにつれて微増している. L1正則化によりスパースなパラメーターベクトルを与えると期待されたPenaltyL1も別に速くない. この程度の次元では意味がないのか, うまく疎な解が求まらなかったのか.

クロスバリデーションで感じたのは, 学習時間がパラメーターに強く影響を受けることである. そのため, 最初に何も考えず広い範囲のパラメーター候補に対してクロスバリデーションを行なってしまうと不当に大きい計算コストを支払わされるかもしれない. また, 先程も述べたように, あるパラメーターに対して正答率がわずかに高いパラメーターが, 学習時間が数倍だったりするため場合によっては最も正答率が高いパラメーターを使う選択はベストではない.

Multiに関してはintercept_scalingによってあまり正答率は左右されないように見えた. 一方, 計算時間はパラメーターに大きく左右される.

参考のため各手法のクロスバリデーション結果の一部をにまとめた.

まとめ

あまりきちんとscikit.learnのSVMを使ったことがなかったので勉強になった. 特にパラメーター変化による計算量の変化はあまりきちんと意識していなかったので, 思ったより大きいことに驚いた(まあそもそもLinearSVCを使ったの初めてだけど).

SVCとLinearSVCの比較は後の記事に譲るつもりだが, やはりSVCは正答率が高い(一般には言えないが). しかし, 予測時間に大きな差があるためLinearSVCを使いたくなる場面も出てくるのかもしれない.

On Comparing Classifiers: Pitfalls to Avoid and a Recommended Approach

分類器の比較に関する注意事項をまとめた論文「On Comparing Classifiers: Pitfalls to Avoid and a Recommended Approach(SALZBERG 1997)」を読んだ. この辺はちゃんと学んで起きたかったが, 面倒で(あんまり面白くないし)放置していた.

概要としては, 「新しい分類器を提案するときに有意差検定をみんなしてるけど, こういう落とし穴があるよー」という論文. 落とし穴として色々挙げられていた.

多重検定の問題

有意水準0.05で実験しても500個のデータセットに対して検定してしまうといくつかは有意差が出てしまう.そのような場合は, Bonferroniの調整などを用いて有意水準を調整する必要がある.

t-testの問題

分類器の比較としてpaired t-testが用いられていたりするが, paired t-testの仮定としてそれぞれのデータセットは独立であるという前提がある. しかし, 元のデータセットからランダムにテスト集合と訓練集合を作る場合などはこの仮定が満たされず, 第一種の過誤が大きくなってしまうことがある. 解決法として異なるテストを使うことが提案されている. Statistical tests for comparing supervised learning algorithmsで詳しく検証されてるっぽいが読んでない.

公開データセットの問題

UCI repositoryなどの公開データセットを用いて手法の比較がなされているが, 例え有意水準0.01で検定しても100人がその検定を行なってしまうと誰かがたまたま有意差を検出してしまう. また, 公開データセットは分類問題の一般的な代表として不十分な面があるため, 結論の過度な一般化は禁物である.

チューニングの問題

パラメーターチューニングをいい加減にしている研究がわりとある. テストデータとは独立にパラメーターを決定すべきだ. 特にパラメーターごとにテスト集合に対してテストを行い最善のものを持ってくるとかはぼーっとしてるとやりそうなので気を付けたい.


だいたいこんな感じだろうか. 推奨する比較手順などもありわかりやすかった. 標準的なpaired t-testとその問題点はこのスライドにまとめられていた.

以前から感じていた疑問の一部を解消できてすっきりしたが, 更に勉強する必要を感じる. あまり興味がないからしらばくやらないけど.

scikit.learn手法徹底比較! イントロダクション

MNIST手書き文字データセットを利用してscikit.learnのsupervisedな分類アルゴリズムを比較する. パラメーターチューニングや計算時間の感覚が掴みたくて, 1回やってみたかった.

MNIST手書き文字データセットとは, 機械学習初学者が何故か必ず与えられると言われている0から9の手書き文字が描かれた画像のデータセットである.

データ概要

次元: 28*28の784次元
値の範囲: 0-255の整数
クラス数: 0-9の10クラス

評価環境:

scikit.learn(sklearn): 0.10
CPU: Xeon E5-2667 (6コア 2.9 GHz)
メモリ: 8GB

この条件でscikit.learnが提供しているアルゴリズムを適用していく. 訓練データ数は, 1000から20000まで変化させ分類精度, 学習時間, 分類時間の推移を観察する. 最終的には全手法の結果をまとめるがしばらくは手法ごとに結果や考察を記事にしていく.

実験詳細

実験の詳しい手続きなど. 細かいことが気になる人以外は読まなくても大丈夫.

実験の主目的

scikit.learnの各分類手法について使い方をまとめる. 使い方というのはパラメーターとしてどのような選択肢があるか, そのパラメーターがどのように性能に影響を与えるかである.

性能への影響の与え方を見るためにMNISTデータセットの分類結果を用いる. しかし分類性能はあくまでこのデータセットに対するものであり, また上下関係はたまたま起こったものである可能性を考慮して欲しい.

実験手順

訓練集合のデータ数を[1000 3000 5000 10000 20000]と変化させて,
それぞれで以下の実験を行う.

1.訓練集合を用いて分類器のパラメーターを決定する
訓練集合を5つに分割し, そのうちの4つで学習, 1つでテストを行い, 分類精度を求める. これを5回繰り返し(5-fold cross validation), 特に注釈のない限り, 平均精度が最も高かった際のパラメーターを以後用いる.

2.テスト集合を用いて分類器の性能を計測する
テスト集合は訓練集合と異なる10000個のデータを使用する. 分類器は1で決定したパラメーターで訓練集合全体を用いて訓練を行う.

計測対象は
・分類精度(正答率)
・学習時間
・平均分類時間
の3つである.

実験手法に対する考察

なぜこのような実験手法を選んだかとバリエーションに関する考察.

有意差検定はしないの?

今回の企画の主旨は, 各分類器の使い方のチュートリアルであって最強の分類器を決めたいわけではない(手書きデータセットだけでやっても...). だから有意差検定はやらなくいいかなー. あと多群の有意差検定で, どれを使うべきかの調査がめんどくさい...

性能計測において正答率の分散を求めないのか

テスト集合のデータ数1万でテストすれば, そこまで大きな誤差は生まれ無いと判断した. 以下詳細.

k個目のデータが分類に成功した際に1, 失敗した際に0を取る確率変数をX_kとする.
今, 分類するデータ数をnとすると中心極限定理より
\frac{1}{n} \sum_{k=1}^n X_k \sim N(\mu, \frac{\sigma^2}{n})
となる. ただしμとσ^2はそれぞれX_kの平均と分散であり, Nは正規分布を表す. この式の左辺は分類精度に相当する.

σ^2は平均0.5のときに1/4となり最大となる. よってデータ数1万のとき最悪でも分類精度の標準偏差は1/200となる. これより分類精度は±1%の範囲に95.5%程度の確率で収まる.
この実験なら1%程度の誤差ならどうでもいいかなー, ということで分散は与えていない.

訓練集合の取り方は1種類でいいのか?

上の手順では, 訓練集合を全て用いて訓練した分類器によって性能を計測している. しかし訓練集合によって性能が変化することを考えると, 用いる訓練集合を変更して何度か性能を計測し平均などを求めるべきではないか?

うーん...確かにそうなんだけど, 手元にある手描き文字データセットのデータ数が4万ちょっとなので, 最初にテスト集合を定めて, 残りから訓練集合を決定する場合, 訓練データ数2万のとき, オーバーラップしない訓練集合は一つしか取れない. オーバーラップするように訓練集合を取れば, 何種類か訓練集合を取れるけど果たしてそれにどの程度意味があるのか... (クロスバリデーションではオーバーラップするように取ってるけど)

ある実験では訓練集合だったデータを, 次の実験ではテスト集合として用いる操作を許容するともう少し使えるデータは増えるが, その場合, 全実験でクロスバリデーションによるパラメーター決定をしないと「テスト集合を観測する前に全てのパラメーターを決定する」という原則に反してしまう. これはちょっとめんどくさい.

これらの理由から今回は上記のように訓練集合を取ることにした.

raw data(バイナリ列や数値列)を画像として表示する

Pythonでraw data(バイナリ列や数値列)を画像として表示する. 今まではOpenCVを用いていたが, インストールされていない環境も多そうなので, 今回はPIL(Python Imaging Library)とPyGTKを用いた.

PILのみ用いる場合

数値のリストからnumpyのarrayを経由してPILのImage型を作成した.
arrayとImageの相互変換についてはNumPyのarrayとPILの変換を読めば詳しい.
とりあえずソースコード.

import Image
import numpy as np

data = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,17,17,17,81,180,180,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,253,253,253,253,253,253,253,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,253,253,253,253,253,253,253,207,197,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,213,253,253,253,253,253,253,253,253,253,253,223,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,231,253,253,253,108,40,40,115,244,253,253,134,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,114,114,114,37,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,99,96,0,0,45,224,253,253,195,10,0,0,0,0,0,0,0,0,0,0,0,11,25,105,83,189,189,228,253,251,189,189,218,253,253,210,27,0,0,0,0,0,0,0,0,0,0,42,116,173,253,253,253,253,253,253,253,253,253,253,253,253,253,221,116,7,0,0,0,0,0,0,0,0,0,118,253,253,253,253,245,212,222,253,253,253,253,253,253,253,253,253,253,160,15,0,0,0,0,0,0,0,0,254,253,253,253,189,99,0,32,202,253,253,253,240,122,122,190,253,253,253,174,0,0,0,0,0,0,0,0,255,253,253,253,238,222,222,222,241,253,253,230,70,0,0,17,175,229,253,253,0,0,0,0,0,0,0,0,158,253,253,253,253,253,253,253,253,205,106,65,0,0,0,0,0,62,244,157,0,0,0,0,0,0,0,0,6,26,179,179,179,179,179,30,15,10,0,0,0,0,0,0,0,0,14,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

#文字列から作成する場合
#string_data = "".join([chr(c) for c in data])
#data = np.fromstring(string_data, dtype=np.uint8)
data = np.array(data, dtype=np.uint8)
data = data.reshape(28,28)
image = Image.fromarray(data)
image.show(command='display -quiet')

今回はグレースケール画像を扱っている. 注意点としては最後のshow関数のcommand引数だろう. これは画像をどのコマンドで表示するかを与えている. Linux環境だとデフォルトはxvを想定しているらしく, それがインストールされていないとshow関数は動作しない. 今, commandにdisplayコマンドを指定しているがdisplayコマンドをインストールしないとこれも動かない. このことはPILのshow()に詳しい.

文字列から作成する場合はnumpy.fromstring関数を使うようにコメントを残している. ただ, Imageクラスの方にfromstring関数があるのでそちらを使うべきだろう. バイナリ列のフォーマットは見ての通り, ただ各画素の値を並べているだけである.

カラー画像を数値列から作成するにはNumPyのarrayとPILの変換を読むに, おそらく3次元arrayを用いて一番上の次元をR,G,Bに対応させるのだろうが未検証.

PyGTKとの連携

もうちょっと豊かなインターフェイスで使いたいよねということで生データをPyGTKの画像として表示する方法を記載する.
今度は 数値列 -> array -> PILのImage -> str -> PixBuf -> GTKのImage と面倒な変換順序を辿っている. 一度, PILのImageを経由しているのは, PixBufがカラー画像のバイナリしか受け付けないためである. たぶん, もっとマシなやり方がある.

#!/usr/bin/python

import csv
import numpy as np
import Image

import pygtk
pygtk.require("2.0")
import gtk

src = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,17,17,17,81,180,180,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,139,253,253,253,253,253,253,253,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,228,253,253,253,253,253,253,253,207,197,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,213,253,253,253,253,253,253,253,253,253,253,223,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,66,231,253,253,253,108,40,40,115,244,253,253,134,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,114,114,114,37,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,57,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,253,253,253,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,99,96,0,0,45,224,253,253,195,10,0,0,0,0,0,0,0,0,0,0,0,11,25,105,83,189,189,228,253,251,189,189,218,253,253,210,27,0,0,0,0,0,0,0,0,0,0,42,116,173,253,253,253,253,253,253,253,253,253,253,253,253,253,221,116,7,0,0,0,0,0,0,0,0,0,118,253,253,253,253,245,212,222,253,253,253,253,253,253,253,253,253,253,160,15,0,0,0,0,0,0,0,0,254,253,253,253,189,99,0,32,202,253,253,253,240,122,122,190,253,253,253,174,0,0,0,0,0,0,0,0,255,253,253,253,238,222,222,222,241,253,253,230,70,0,0,17,175,229,253,253,0,0,0,0,0,0,0,0,158,253,253,253,253,253,253,253,253,205,106,65,0,0,0,0,0,62,244,157,0,0,0,0,0,0,0,0,6,26,179,179,179,179,179,30,15,10,0,0,0,0,0,0,0,0,14,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]


class ImageWindow:
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        
        data = np.array(src, dtype=np.uint8)
        data = data.reshape(28,28)
        image = Image.fromarray(data)
        image = image.convert("RGB")

        buf = gtk.gdk.pixbuf_new_from_data(image.tostring(),
                                           colorspace=gtk.gdk.COLORSPACE_RGB,
                                           has_alpha=False,
                                           bits_per_sample=8,
                                           width=28,
                                           height=28,
                                           rowstride=84)
        self.window.add(gtk.image_new_from_pixbuf(buf))
        self.window.show_all()

    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False

    
if __name__ == "__main__":
    window = ImageWindow()
    gtk.main()

PyGTKの使い方は他で調べてもらうとして変換についてのみ書く.
重要なのはpixbuf_new_from_dataがcolorspace引数にCOLORSPACE_RGBしか許さないため, 色空間を変換しなければならない点である. そのためPILのImage上で一度色空間の変換を行なっている. PILのImageに関する操作はこちらによくまとまっている. rowstride引数には画像の行と行の間のバイト数を指定する必要がある(画像処理ではお馴染み). ここでは3バイト * 28画素なので84としている.

声優ラジオ 感想まとめ

現在, 継続して視聴しているラジオについて感想をまとめた. 好みの点数一覧はこっち. そんなに量は聞いてないのでお勧めがあったら教えて欲しい.
★5は敬意を払っている, ★4は放送が待ち遠しい, ★3は楽しく聞いてる, ★2はたまに面白い, ぐらいの雰囲気. 僕はどうしても同じ番組を聴き続けると飽きてしまうので, 新しめのラジオの方が評価が甘い. あと継続して視聴している時点で★1でもそれなりに好き.

杉田智和のアニゲラ!ディドゥーーン ★★★★★

涼宮ハルヒキョン役や今期だとジョジョ役をやっている杉田さんが, ゲームライターのマフィア梶田さんやゲストと共に好きなゲームや漫画について語ったり大喜利したりする番組.

特に大喜利が面白いわけでもないし, 冒頭の茶番も意味がわからないことも多いんだけど, 杉田さんがゲームや漫画について語るのを聞くのが楽しい. 自分に近い趣味の人がその趣味について話すのを聞く楽しさがある. 杉田さんは, 今でもゲーセンに結構行ってそうだし, ラブプラスはやってるし, モバマスのかな子好きだしで, おお, 年取ってもやっぱりゲーム遊べる! という気持ちになれる.

あと, 漏れでてくる友人関係がいい. 梶田さんのゴミのような生活を心配する杉田さんだったり, ことあるごとに杉田さんが
「中村とこの前」
と語りだす様は薄い本が出るな・・・というレベル.

杉田さんのネガティブな面とか優しい面とかが見られて, 杉田さんが好きになれる番組.

アクセルワールド 加速するラジオ ★★★★★

アクセルワールドを宣伝するための番組. 黒雪姫役の三澤さんと, 司会業?ミュージシャン?の鷲崎さんがやっている.

今, 非コミュやぼっちをアピールする女性声優は結構いる. 「高校時代は友達いなくてー」とか「人に話しかけられなくて」とか. その激戦区で, 今一番面白いのは三澤さんだと僕は思っている.
「服屋で『お困りの点はありませんか?』と聞かれると『今、あなたに話しかけられていることがお困りです』と言いたくなる」(意訳)
が僕の中で象徴的. 闇レギオンオーディションという, リスナーが自分のネガティブな面を投稿し, それが三澤さんが認めるレベルかを判定するコーナーでは三澤さんが闇使いとしての格を見せつけてくれる. 「○○できる時点でネガティブではない」

他にも
「お父さんにことあるごとに声優をやめて田舎に帰って来いと言われる」
「楽屋で忙しいふりをしてやりすごす」
といった三澤さんの生活感溢れるネガティブ発言がいちいち面白く, それをちゃんと明るい雰囲気にしてる鷲崎さんすげぇとなる.

初期から追っていくと段々と鷲崎さんの影響を受けて成長していく三澤さんを見れる. ちなみにアクセルワールドの話はもうほとんどしないw.

ひだまりラジオ ★★★★★

ひだまりスケッチを紹介するラジオ. ゆの役の阿澄佳奈さんと週替わりのゲストがやってる.

構成脚本が阿澄さんを作ったのか, 阿澄さんに引きづられてこの脚本になったのか. 阿澄さんの癒し系ポイズンな部分をフィーチャーした素晴らしい番組. 構成が面白いし, パーソナリティーも面白いという最強のラジオ. 阿澄さんだけでも面白いのにゲストが個性豊かでみんな面白いのが凄い.

構成が素晴らしくコーナーも面白いし, たまに変な趣向もこらしてくれる. ある回では全編にわたって「後の○○である」っていう謎ナレーションがついたw. 名物である冒頭の茶番劇もクオリティが高くて, 「ひだまり仕分け」とか「一日一万回感謝のタイトルコール!」など非常に笑えるものばかり. ついには「松来さんが結婚する世界線にいたら・・・」という茶番が本編にまで侵食する始末.

この作品はラジオ以外にも, 阿澄さんがキャラと本人で交互に話した狂気の企画「見えるひだまりラジオ」や感動の「ひだまり王決定戦」など面白い企画が多く, いい宣伝しるなーと思う.

阿澄さんラジオは他にも「あすみさん@がんばらない」や「星空ひなたぼっこ」を聞いてる. 前者はゲスト次第だけど楽しみにしている. 後者は時たまアイマスの話をしてくれるのがちょっとうれしい.

ちょっとお時間よろしいですか(旧Radio Cross) ★★★★★

ビビッドレッドオペレーションズのあかね役の佐倉綾音さんとToLoveるの春菜役の矢作紗友里さんが, 混沌とした企画をこなすラジオ(映像付き).

声優ラジオを広く聞いている人間でこれを知らない人はモグリといっても最早過言ではない. ギャグ系で今一番面白い番組. この組み合わせを産み出したという敬意だけで思わずQuad CrossのドラマCDを買ってしまったぐらい.

この二人の互いへの遠慮無いトークがいい.
「佐倉さんのお腹がぼよよーんって」
とか普通言えねーだろw. 佐倉さんは佐倉さんで, 矢作さんが恋愛ゲームにはまってるのに対し
矢作「でもハルは私のこと好きだって言ってくれてる」
佐倉「それは妄想なんですよ〜」
と言って先輩を撃沈したりと容赦無い.

コーナーも映像をいかした斬新?なコーナーがしょっちゅう出来ては消えていく. どんなぼっち飯をしたかを送るコーナーとか秀逸だった. コーナーの優秀さもあってリスナーが送ってくるネタも面白い. 最近良かったのは
「友達3人でテニプリを見ていて, お気に入りのキャラが出てくるシーンをカメラに収めようとするのを邪魔されたときの話」
がクレイジーで笑えた.

このラジオは本当に1回聞いてみて欲しい.

花澤香菜のひとりでできるかな (ひとかな) ★★★★

化物語の撫子やPSYCHO-PASSのあかねちゃん役の花澤さんがお仕事の近況報告をしたり, ムチャぶりに答えてくれる番組.

この番組を聞くと, 花澤さん声可愛いのに歌も歌えるし, その上面白いのかよ・・・と思える. 一人喋りの声優ラジオってどうしても真面目になりがちなんだけど, ひとかなは結構ギャグを重視してくれてて, 可愛さはあんまり重視してないw.

コーナーが結構良く出来てて, 「リスナーが新曲CDの30秒CMを考えてくるコーナー」は安定して面白いし, 「リスナーが昔私に励まされたりした(妄想)エピソードを送ってくる」コーナーでは, リスナーがいいパスを投げてくるのもあるが, 色んなムチャぶりを見事にこなしたりこなせなかったりする花澤さんの大喜利スキルに感心させられる.

最近は小倉唯ちゃんが非常にプッシュされていて唯ちゃんとの妄想デートが何故か番組のジングルになっている.
「ダメです, 唯ちゃん. ストッキングなんぞ履いちゃあ」

ラジオの婚約者と幼なじみが修羅場すぎる ★★★★

俺の彼女と幼なじみが修羅場すぎるを宣伝するラジオ. 千和役の赤崎千夏さんと愛衣役の茅野愛衣さんがやってる.

赤崎さんのトークをこのラジオで初めてちゃんと聞いたんだけど, サービス精神に溢れてるし声芸やってくれるし面白い. この人のトークはもうちょっと色んな番組で聞きたい.

茅野さんはあの花でメンマをやってた人で, デビューからそこまで経ってないのにかなりラジオの仕事をしている. 何より声が可愛い. わりと落ち着いた大人っぽいトークをするイメージ. そして声が可愛い.

超面白い!ってわけではないんだけど, 赤崎さんの面白さと茅野さんの可愛さを楽しめるラジオ. アニメを見てなくても楽しめます.

THE IDOLM@STER STATION!!! ★★★★

アイドルマスターを宣伝するための番組. 原由美さん(貴音), 沼倉愛美さん(響), 浅倉杏美さん(雪歩)がやってる.

対等な仲良し三人のバランスの取れたやり取りがいい. 後先考えずムチャぶりしちゃう原さんと, しっかりした沼倉さんと, あざとい浅倉さんが凄く面白いわけじゃないんだけど, ちょっと楽しい気持ちになれるトークをしてくれる. てきとーな原さんが「私たち女子力鍛えないと〜」と言うのを沼倉さんが「また由美は・・・」みたいなテンションであしらうのが好き.

冒頭のキャラクターでやる茶番劇も出来が良くてアイマスがより好きになれるラジオ. 公式の響ではこの冒頭劇の響が一番頭悪そうw.

ブレイブルー公式WEBラジオ ★★★

格闘ゲーム, ブレイブルーを宣伝するラジオ. ラグナ役の杉田さん, ツバキ役の今井さん, ノエル役の近藤さんがやってる.

ニコニコで公開されておりラジオカテゴリで一番再生数がある. ラジオにあわせて絵が動くという凄い手間をかけた番組. 結構ちゃんとゲームの宣伝をするので, ゲームをやっておいた方が楽しめる.

上坂すみれのLady Go! ★★★

中二病でも恋がしたい!の凸森役やガルパンのノンナ役の上坂すみれさんが企画をやっていくラジオ.

今最も個性的な声優と言えば上坂さんの名前をあげる人も多いんじゃないだろうか. ロシア文化に傾倒し, ゴスロリを日頃から着用し, お嬢様でミリタリーオタクでしかも成績優秀という冗談みたいなキャラクター. ラジオで紹介する曲も「りんごの唄(赤いりんごに唇寄せて)」だったり描く絵がどこか楳図かずおを彷彿とさせたり, 通常の声優とは一線を画する.

一風変わったトークも魅力なんだけど, 意外と常識人でトークに安定感がある. しかもそこそこ面白い. コーナーはそんなに特徴的ではないので, 面白さは上坂さんを好きになれるかどうか次第.

最近は毎週のように三澤さんの話をしてくれるのがちょっとうれしい.

さくら荘のペットなラジオ ★★★

さくら荘のペットな彼女を宣伝するラジオ. ましろ役の茅野愛衣さんと美咲役の高森奈津美さんがやってる.

映像付きの茅野さんが見られる. 落ち着いたトークをする二人なので, まったり系番組になっている・・・かな? 一番新しい回で, 高森さんが「どうして?」と「それで」だけで会話する罰ゲームは茅野さんのあざとさが遺憾なく発揮されていてお勧め.

ラジオ シンデレラガールズ(デレラジ) ★★★

ソーシャルゲーム, シンデレラガールズを宣伝するラジオ. 島村さん役の大橋彩香さん, 凛役の福原綾香さん, 美嘉役の佳村はるかさんがやってる.

特に面白いわけではないw. ただ理屈じゃない感じで聞きたくなるので欠かさず聞いている

小松未可子のLady Go! ★★

さあ海賊の時間だ! でお馴染みの小松未可子さんが宣伝やら企画やらをやってくれるラジオ.

ビジュアル, ボーカルともにハイレベルの小松さん. KのED「冷たい部屋ひとり」はかなり好き. ラジオはそのビジュアルを活かしており(Lady Goという企画がわりとビジュアル強調するイメージ), 通常の動画ラジオではありえないようなアップとかをしてくれてうひょーってなる.

トークはまあまあ面白いぐらい. コンビでのトークがそこまでうまくないイメージなので, そこを改善してくれれば嬉しい.

竹達沼倉の初めてでもいいですか? ★★

俺妹の桐乃役の竹達さんと, アイマスの響役の沼倉さんの変態ラジオ.

コーナーが女性声優二人のラジオとは思えぬ変態っぷり. どんな匂いが好きかを送ってくるコーナーでは, これ他のラジオだと弾かれるんじゃないのwっていうレベルのメールが紹介されたりする. また竹達さんの素晴らしい英語力もフィーチャーされており, 英語コーナーでは「ぺおぷれ! ぺおぷれ!」(People)と連呼してくれる.

竹達さんと沼倉さんのオタクトークとかも聞ける.

ゆい・かおりの実デジタル ★★

ゆいかおりという声優ユニットの映像付き宣伝番組. ロウきゅーぶのひなた役の小倉唯ちゃんとあの夏の青い子役の石原夏織ちゃんがいちゃいちゃしてる.

声優界の天使と名高い唯ちゃんと, 芸人スキルを上げつつある夏織さん. 残念ながら二人のトーク力はそこまで高くないし, コーナーもそんなに面白いわけではない. だけど, そのまとまりの無さが逆に女の子のキャッキャ感を出してて, そういうのが好きな人にはいいと思う.
最近あった石原さんが声グラ表紙の唯ちゃんの真似をする回とか素晴らしいいちゃいちゃ感.

難点を上げるなら唯ちゃんのトークがあまりに女子高生過ぎて, 自分が犯罪者のような気持ちになってくるところか.

雑感

男性声優ラジオを開拓できてないなぁ. あと作品ラジオもあんまり聞けてない.

ラジオの面白さはパーソナリティの能力によるんだけど, それ以上に組み合わせの要素が大きいように感じる. 「とあるラジオのレールガン」とか凄く面白かったんだけど, 豊崎さんと伊藤さんの一人喋りはそこまででもないんだよなー. 逆に小松さんとか花澤さんは一人のときの方が面白かったかなーと思うことがそこそこある. 目上の人と組んで萎縮みたいなパターンも感じるときあるし.

人数が二人以内というのも重要だと思っていて, 三人以上になると面白さを維持するのが難しい. ラジオは一度に一人しかターンが取れないので, 二人だと会話のラリーみたいになるんだけど, 三人以上だと誰が主導権を握るかが難しそう. 「○○はどう思うの?」みたいに順番にふっていく方法もあるけど, それだとテンポが悪くなってしまう.

あとアニメと連動系のラジオはあんまり聞いていないことに気づいた. どうしても作品紹介に時間を割かなければいけない分, 面白さの面では不利ということかもしれない. アニメ連動系では「とあるラジオのレールガン」とか「Solty Radio」とか凄く面白かったけど, 作品紹介はろくにしてなかったしなー. 面白いと話題になる前に終了してしまって見つけられないという可能性もある.

ここで言及していないラジオでは「田村ゆかり 黒うさぎの小部屋」とか「水樹奈々のスマイルギャング」とかは, 現在も放送していて面白いんだけど, 聞きすぎて少し飽きてしまった. 飽きづらいラジオとは何かという問題は考えてみると面白いかも知れない.

色々ごちゃごちゃ書いてしまったが, アニメをよく見てる人は暇があったらラジオを聞いて声優を覚えるとちょっと楽しくなるのでお勧めです.