要旨
かなり前になりますが、Algorithm::LibLinear という Perl モジュールを書きました。
これを使うと線形分類器などが高速に学習できます。テキストや画像の分類が応用として期待されます。
LIBLINEAR について
LIBLINEAR は LIBSVM と同じ台湾国立大学の Chih-Jen Lin 教授のチームが公開しているオープンソースの機械学習パッケージです。 関数のロジスティック回帰、サポートベクター回帰及び線形 SVM による多クラス分類を行うことができます。LIBSVM と違ってカーネル関数を使うことはできませんが、はるかに高速に動作します。
Algorithm::LibLinear について
LIBLINEAR には C++ で書かれたライブラリと、その機能を使って機械学習と分類・関数回帰を行うコマンドラインユーティリティが含まれています。 Algorithm::LibLinear はライブラリの機能を Perl からオブジェクト指向的に利用できるようにした上で、コマンドラインユーティリティの一部機能をライブラリ化して Perl で再実装したものです。
使い方
分類問題を解くときは、
- 訓練データセットの読み込み・スケーリング
- 学習器パラメータの設定
- 分類器の訓練
- 実データの分類
という手順で行います。
訓練データセットの読み込み
正解ラベルのついたデータを大量に用意して学習させます。
LIBSVM 形式のデータを読み込むか:
my $data_set = Algorithm::LibLinear::DataSet->load(string => <<'EOD');
1 1:0.1 2:0.1 4:0.1
-1 1:0.1 2:-0.1 3:0.1
...
EOD
HashRef として表現されたデータを使います:
my $data_set = Algorithm::LibLinear::DataSet->new(data_set => [
+{ feature => +{ 1 => 0.1, 2 => 0.1, 4 => 0.1, }, label => 1 },
+{ feature => +{ 1 => 0.1, 2 => -0.1, 3 => 0.1, }, label => -1 },
...
]);
データのスケーリング
線形分類器は与えられた入力ベクトルと訓練データから生成した重みベクトルの内積を取ってクラスを判定します。 入力ベクトルの特定の成分が大きな値域を持っていると、その成分が結果に大きな影響を与えることになります。これを避けるため、入力ベクトルの各成分の値域を揃えるスケーリング処理を行います。
my $scaling_parameter = Algorithm::LibLinear::ScalingParameter->new(
data_set => $data_set,
lower_bound => 0,
upper_bound => 1,
);
my $scaled_data_set = $data_set->scale(parameter => $scaling_parameter);
なお疎なベクトルに対して lower_bound
を非零にするとデータサイズが馬鹿みたいに増えるので注意してください。
学習パラメータの設定
データの用意ができたら次は学習パラメータの設定です。色々ありますが詳細は perldoc
参照。
my $learner = Algorithm::LibLinear->new(
cost => 1.0, # ペナルティコスト
epsilon => 0.1, # 収束判定
solver => 'L2R_L2LOSS_SVC_DUAL', # 分類器の学習に使うソルバ
);
全パラメータにデフォルト値があるので、そのままでも一応動きます:
my $learner = Algorithm::LibLinear->new;
分類器の訓練
Algorithm::LibLinaer の train
メソッドを呼ぶと Algorithm::LibLinear::Model のインスタンスが返ってきます。これが分類器です。
my $classifier = $learner->train(data_set => $scaled_data_set);
実データの分類
訓練データセットに含まれない未知のデータに対しても、一番それっぽいラベルが返ってきます。
# 入力も訓練データと同じパラメータでスケーリングする
my $feature = Algorihtm::LibLinear::DataSet->new(data_set => [
+{ feature => +{ 1 => 0.1, 2 => 0.1 }, label => 0 },
])->scale(parameter => $scaling_parameter)->as_arrayref->[0]{feature};
my $label = $classifier->predict(feature => $feature);
交差検定
学習パラメータの調整には精度評価が必要です。訓練データセットと精度評価用のテストデータセットを別々に用意するのは面倒なので、訓練データを n 個に分割し、n-1 個のブロックを訓練データ、1 個のブロックをテストデータとして精度評価を n 回行った平均を出す交差検定がよく使われます。
my $accuracy = $learner->cross_validation(
data_set => $data_set,
num_folds => 5,
);
say $accuracy;
例
界隈では有名な Iris Data Set を使った多値分類:
#!/usr/bin/env perl
use v5.14;
use Algorithm::LibLinear;
use Algorithm::LibLinear::ScalingParameter;
my $label_id = 0;
my %label_ids;
my @data_set = map {
chomp;
my @columns = split /,/;
# Algorithm::LibLinear は整数値のラベルしか取らないので ラベル → 整数 の辞書を作る
my $label = pop @columns;
$label_ids{$label} //= ++$label_id;
# 素性は疎ベクトルとして HashRef で表現する
my $feature_index = 0;
my %feature = map { (++$feature_index => $_) } @columns;
+{ feature => \%feature, label => $label_ids{$label} };
} <DATA>;
my $data_set = Algorithm::LibLinear::DataSet->new(data_set => \@data_set);
# ベクトルの各成分の値域を [-10, 10] にスケール
my $scaling_parameter = Algorithm::LibLinear::ScalingParameter->new(
data_set => $data_set,
lower_bound => -10,
upper_bound => 10,
);
$data_set = $data_set->scale(parameter => $scaling_parameter);
# 識別器を訓練する
my $learner = Algorithm::LibLinear->new;
my $classifier = $learner->train(data_set => $data_set);
my %labels = reverse %label_ids;
my $test_feature = +{ 1 => 6.3, 2 => 2.5, 3 => 5.0, 4 => 1.9 };
$test_feature = Algorithm::LibLinear::DataSet->new(
data_set => [ +{ feature => $test_feature, label => 0 } ],
)->scale(parameter => $scaling_parameter)->as_arrayref->[0]{feature};
my $predicted_label_id = $classifier->predict(feature => $test_feature);
say $labels{$predicted_label_id}; # 'Iris-virginica'
# Data source: Iris Data Set [http://archive.ics.uci.edu/ml/datasets/Iris].
__DATA__
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
4.6,3.4,1.4,0.3,Iris-setosa
5.0,3.4,1.5,0.2,Iris-setosa
4.4,2.9,1.4,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
5.4,3.7,1.5,0.2,Iris-setosa
4.8,3.4,1.6,0.2,Iris-setosa
4.8,3.0,1.4,0.1,Iris-setosa
4.3,3.0,1.1,0.1,Iris-setosa
5.8,4.0,1.2,0.2,Iris-setosa
5.7,4.4,1.5,0.4,Iris-setosa
5.4,3.9,1.3,0.4,Iris-setosa
5.1,3.5,1.4,0.3,Iris-setosa
5.7,3.8,1.7,0.3,Iris-setosa
5.1,3.8,1.5,0.3,Iris-setosa
5.4,3.4,1.7,0.2,Iris-setosa
5.1,3.7,1.5,0.4,Iris-setosa
4.6,3.6,1.0,0.2,Iris-setosa
5.1,3.3,1.7,0.5,Iris-setosa
4.8,3.4,1.9,0.2,Iris-setosa
5.0,3.0,1.6,0.2,Iris-setosa
5.0,3.4,1.6,0.4,Iris-setosa
5.2,3.5,1.5,0.2,Iris-setosa
5.2,3.4,1.4,0.2,Iris-setosa
4.7,3.2,1.6,0.2,Iris-setosa
4.8,3.1,1.6,0.2,Iris-setosa
5.4,3.4,1.5,0.4,Iris-setosa
5.2,4.1,1.5,0.1,Iris-setosa
5.5,4.2,1.4,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
5.0,3.2,1.2,0.2,Iris-setosa
5.5,3.5,1.3,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
4.4,3.0,1.3,0.2,Iris-setosa
5.1,3.4,1.5,0.2,Iris-setosa
5.0,3.5,1.3,0.3,Iris-setosa
4.5,2.3,1.3,0.3,Iris-setosa
4.4,3.2,1.3,0.2,Iris-setosa
5.0,3.5,1.6,0.6,Iris-setosa
5.1,3.8,1.9,0.4,Iris-setosa
4.8,3.0,1.4,0.3,Iris-setosa
5.1,3.8,1.6,0.2,Iris-setosa
4.6,3.2,1.4,0.2,Iris-setosa
5.3,3.7,1.5,0.2,Iris-setosa
5.0,3.3,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
6.5,2.8,4.6,1.5,Iris-versicolor
5.7,2.8,4.5,1.3,Iris-versicolor
6.3,3.3,4.7,1.6,Iris-versicolor
4.9,2.4,3.3,1.0,Iris-versicolor
6.6,2.9,4.6,1.3,Iris-versicolor
5.2,2.7,3.9,1.4,Iris-versicolor
5.0,2.0,3.5,1.0,Iris-versicolor
5.9,3.0,4.2,1.5,Iris-versicolor
6.0,2.2,4.0,1.0,Iris-versicolor
6.1,2.9,4.7,1.4,Iris-versicolor
5.6,2.9,3.6,1.3,Iris-versicolor
6.7,3.1,4.4,1.4,Iris-versicolor
5.6,3.0,4.5,1.5,Iris-versicolor
5.8,2.7,4.1,1.0,Iris-versicolor
6.2,2.2,4.5,1.5,Iris-versicolor
5.6,2.5,3.9,1.1,Iris-versicolor
5.9,3.2,4.8,1.8,Iris-versicolor
6.1,2.8,4.0,1.3,Iris-versicolor
6.3,2.5,4.9,1.5,Iris-versicolor
6.1,2.8,4.7,1.2,Iris-versicolor
6.4,2.9,4.3,1.3,Iris-versicolor
6.6,3.0,4.4,1.4,Iris-versicolor
6.8,2.8,4.8,1.4,Iris-versicolor
6.7,3.0,5.0,1.7,Iris-versicolor
6.0,2.9,4.5,1.5,Iris-versicolor
5.7,2.6,3.5,1.0,Iris-versicolor
5.5,2.4,3.8,1.1,Iris-versicolor
5.5,2.4,3.7,1.0,Iris-versicolor
5.8,2.7,3.9,1.2,Iris-versicolor
6.0,2.7,5.1,1.6,Iris-versicolor
5.4,3.0,4.5,1.5,Iris-versicolor
6.0,3.4,4.5,1.6,Iris-versicolor
6.7,3.1,4.7,1.5,Iris-versicolor
6.3,2.3,4.4,1.3,Iris-versicolor
5.6,3.0,4.1,1.3,Iris-versicolor
5.5,2.5,4.0,1.3,Iris-versicolor
5.5,2.6,4.4,1.2,Iris-versicolor
6.1,3.0,4.6,1.4,Iris-versicolor
5.8,2.6,4.0,1.2,Iris-versicolor
5.0,2.3,3.3,1.0,Iris-versicolor
5.6,2.7,4.2,1.3,Iris-versicolor
5.7,3.0,4.2,1.2,Iris-versicolor
5.7,2.9,4.2,1.3,Iris-versicolor
6.2,2.9,4.3,1.3,Iris-versicolor
5.1,2.5,3.0,1.1,Iris-versicolor
5.7,2.8,4.1,1.3,Iris-versicolor
6.3,3.3,6.0,2.5,Iris-virginica
5.8,2.7,5.1,1.9,Iris-virginica
7.1,3.0,5.9,2.1,Iris-virginica
6.3,2.9,5.6,1.8,Iris-virginica
6.5,3.0,5.8,2.2,Iris-virginica
7.6,3.0,6.6,2.1,Iris-virginica
4.9,2.5,4.5,1.7,Iris-virginica
7.3,2.9,6.3,1.8,Iris-virginica
6.7,2.5,5.8,1.8,Iris-virginica
7.2,3.6,6.1,2.5,Iris-virginica
6.5,3.2,5.1,2.0,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
6.4,2.8,5.6,2.1,Iris-virginica
7.2,3.0,5.8,1.6,Iris-virginica
7.4,2.8,6.1,1.9,Iris-virginica
7.9,3.8,6.4,2.0,Iris-virginica
6.4,2.8,5.6,2.2,Iris-virginica
6.3,2.8,5.1,1.5,Iris-virginica
6.1,2.6,5.6,1.4,Iris-virginica
7.7,3.0,6.1,2.3,Iris-virginica
6.3,3.4,5.6,2.4,Iris-virginica
6.4,3.1,5.5,1.8,Iris-virginica
6.0,3.0,4.8,1.8,Iris-virginica
6.9,3.1,5.4,2.1,Iris-virginica
6.7,3.1,5.6,2.4,Iris-virginica
6.9,3.1,5.1,2.3,Iris-virginica
5.8,2.7,5.1,1.9,Iris-virginica
6.8,3.2,5.9,2.3,Iris-virginica
6.7,3.3,5.7,2.5,Iris-virginica
6.7,3.0,5.2,2.3,Iris-virginica
6.3,2.5,5.0,1.9,Iris-virginica
6.5,3.0,5.2,2.0,Iris-virginica
6.2,3.4,5.4,2.3,Iris-virginica
5.9,3.0,5.1,1.8,Iris-virginica
参考資料
SVM による多クラス分類を真面目に行う場合は A Practical Guide to Support Vector Classification (PDF) を一読することをお勧めします。LIBSVM を前提として書かれていますが、カーネル関数の選定法 (要約:「ガウスカーネルを使え」) 以外のノウハウは LIBLINEAR でも同様です。
MLSS 2012 の Lin 教授による講義スライド冒頭の40ページくらいにも同様の内容があります。
終わりに
XS が難しかったです (小並感)。業務の都合でテキストの分類器が必要になって作ったので、引数の名前が label
と feature
だったりして回帰問題を解くときには少しややこしいかも知れません。
学術っぽいツールは公式バインディングが Python やら R やら MATLAB やらしかない場合も多いので、CPAN にモジュール上げてみたいけどネタがないなんて時は狙い目です。 ちなみにテキストの分類をやるときは Python で書かれた LibShortText があるみたいなので、無理して Perl を使う理由がなければこっちの方が楽だと思います。俺はどっちでもいいけど。
コメント
コメントを投稿