機械学習アルゴリズムで実数を保持する場合のfloat型とdouble型の比較
機械学習アルゴリズムをC++等で実装する場合には型をどうするか悩む.きっかけはオープンソースのsvm_lightやLIBSVMはふつうにdouble型だったのに対し,ollではfloat型で実装されていたから.「あれ,別にdoubleほどの精度要らないの?」「floatの方が半分のメモリ量で済むし,キャッシュも効くから高速になるのかな」というようなことを考えていた.
そのまますっかり忘れていたのだけれど,GW中の課題のひとつとして検証してみることにした.
以下の2つについてfloat型とdouble型で比較する.面倒なのでぜんぶfloat型かぜんぶdouble型か.
- 訓練データ事例の特徴量 (素性) の型
- 重みベクトル等,すべてのパラメータの型
それぞれについて以下の3つの値を比較する.
- 実行速度
- モデル精度
- 使用メモリ量
なお,実験に利用したマシン環境は以下のとおり
実験に利用したアルゴリズムはSVM学習法のひとつであるSMO.詳細については明日のPRML勉強会で発表予定 (予定) なので,詳しくはそちらを参照.
実行速度
以下の3ロジックの時間をgettimeofday(2)を利用して測定.LIBSVM datasetのdiabetes_scaleを利用.
- LIBSVM形式のファイルを読み込む
- 訓練事例からパラメータを推定
- モデルファイルを書き出す
10回測定した結果の平均と標準偏差を記録.
- 結果
float: 4.1940 (±0.0032) sec. double: 4.2566 (±0.0026) sec.
わずかにfloatの方が速い.
モデル精度
どの程度モデルに差が生まれるのかどう比較してよいかわからなかったので (SVMの場合はサポートベクタとそれに対する重みの値で比較すればよいのだろうか) & 面倒なので訓練データに対する精度で比較した.
カーネルパラメータ等の条件は書かないが,同じ値を利用している.
- 結果
* Gauss kernel float: 0.7565 double: 0.7539 * Linear kernel float: 0.6927 double: 0.6680
なんとfloat型の方が精度が高くなっている.これはどういうことだろうか.今回の実装では目的関数や停止条件もfloat型になっているため,停止条件による収束判定の差かもしれないが,ちょっと考えづらい.なお,libsvm-3.17でGauss kernelと同条件でモデル構築したところaccuracyが0.7813であったため,SMO実装の誤りがあるのかもしれない.ということでこの結果は棚上げ.
使用メモリ量の比較
実測するまでもなく2倍になるはず.メモリを解放する前に標準入力待ち状態にして/proc/
- float型
Name: smo_train State: S (sleeping) Tgid: 13440 Pid: 13440 PPid: 13356 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 32 Groups: 20 24 25 29 44 46 1000 VmPeak: 5080 kB VmSize: 5080 kB VmLck: 0 kB VmHWM: 3328 kB VmRSS: 3328 kB VmData: 2308 kB VmStk: 88 kB VmExe: 60 kB VmLib: 2568 kB VmPTE: 16 kB Threads: 1 SigQ: 0/32768 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 0000000000000000 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffffffffffff Cpus_allowed: 0f Cpus_allowed_list: 0-3 Mems_allowed: 1 Mems_allowed_list: 0 voluntary_ctxt_switches: 1 nonvoluntary_ctxt_switches: 51
- double型
Name: smo_train State: S (sleeping) Tgid: 13393 Pid: 13393 PPid: 13356 TracerPid: 0 Uid: 1000 1000 1000 1000 Gid: 1000 1000 1000 1000 FDSize: 32 Groups: 20 24 25 29 44 46 1000 VmPeak: 7088 kB VmSize: 7088 kB VmLck: 0 kB VmHWM: 5408 kB VmRSS: 5408 kB VmData: 4316 kB VmStk: 88 kB VmExe: 60 kB VmLib: 2568 kB VmPTE: 20 kB Threads: 1 SigQ: 0/32768 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: 0000000000000000 SigCgt: 0000000000000000 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffffffffffff Cpus_allowed: 0f Cpus_allowed_list: 0-3 Mems_allowed: 1 Mems_allowed_list: 0 voluntary_ctxt_switches: 2 nonvoluntary_ctxt_switches: 70
voluntary_ctxt_switchesは自発的にコンテキストスイッチを発生した数.nonvoluntaryは強制的なコンテキストスイッチ発生回数.コンテキストスイッチの数もここに記述されているのを知らなかった.sleep状態にして待ってしまっているのでcontext switchが発生しているので今回の場合は特に意味はないはず (もういちど実行したらdouble型のvoluntaryが1になったので).