スキップしてメイン コンテンツに移動

Algorithm::SVM の注意点

要旨

Algorithm::SVM は極めて有用だけど API がなんか変なので注意が必要。

詳説

CPAN に Algorithm::SVM1 というモジュールがあります。これ Support Vector Machine (SVM)2 を提供する LIBSVM3 という有名なライブラリの Perl バインディングなのですが、なんか API に癖があるので注意点を解説します。

まず使い方を簡単に紹介します:

use strict;
use warnings;
use Algorithm::SVM;

my @data_set;
while (<DATA>) {
  chomp;
  my ($label, $vector) = split /:\s+/, $_, 2;
  my @vector = split /,\s+/, $vector;
  my $data = Algorithm::SVM::DataSet->new(
    DataSet => \@vector,
    Label => $label,
  );
  push @data_set, $data;
}

# 本当はパラメータ調整が要るけど省略。全部デフォルトなのでガウスカーネル利用の C-SVC になる。
my $svm = Algorithm::SVM->new;
# 分類器を訓練する。
$svm->train(@data_set);

# ラベル1に分類されるべき未知のデータ。
my $test_data = Algorithm::SVM::DataSet->new(
  DataSet => [ 4.6, 3.2, 1.4, 0.2 ],
  # ラベルは未知なので仮に 0 とする。単に無視されるので -1 でも 65536 でも 42 でも良い。
  Label => 0,
);
# 未知データを分類。1 が返るはず。
my $label = $svm->predict($test_data);
print "$label\n";

# Iris Data Set (http://archive.ics.uci.edu/ml/datasets/Iris) より一部抜粋の上形式を変更。
# <label>: <vector elm1>, <vector elm2>, ...
__DATA__
1: 5.1, 3.5, 1.4, 0.2
1: 4.9, 3.0, 1.4, 0.2
2: 7.0, 3.2, 4.7, 1.4
2: 6.4, 3.2, 4.5, 1.5
3: 6.3, 3.3, 6.0, 2.5
3: 5.8, 2.7, 5.1, 1.9
...

DataSet がデータセットじゃない

SVM で訓練・分類されるべき (正解ラベル付き) ベクトルを表現するために Algorithm::SVM::DataSet というクラスが用意されていますが、このクラスが表現するのは1個のベクトルです。つまりデータセットじゃなくてデータです。Algorithm::SVM->train メソッドで訓練するときにはデータセットとして Algorithm::SVM::DataSet の配列を渡す必要があります。

分類時にもラベルが必要

未知データを分類する Algorithm::SVM->predict メソッドのパラメータは Algorithm::SVM::DataSet オブジェクトです。Algorithm::SVM::DataSet->new は名前付きパラメータとしてベクトル (DataSet) と正解ラベル (Label) を取りますが、正解ラベルは必須パラメータです。 つまりラベルが未知のデータに対してもラベルを与えてやらなければなりません。割と意味不明ですが、predict ではラベルは単に無視されるのでダミーのラベルを与えてやれば良いです。

疎ベクトルの与え方

Algorithm::SVM::DataSet->newDataSet パラメータは ArrayRef を取ります。ところで問題によってはほとんどの成分が 0 である (i.e., 疎である) ようなベクトルを扱う場合があり、このような問題のデータを配列で表現するとメモリの無駄です。 例えば1万次元ベクトルの 1123 番目と 5813 番目の要素だけが 1 のようなベクトルを表現する場合、[ (0) x 1122, 1, (0) x 4689, 1, (0) x 4187 ] という具合になってほとんど 0 です。もし HashRef で表現できるなら +{ 1123 => 1, 5813 => 1 } といった感じになってより簡潔かつ省メモリです。

実際 LIBSVM の内部ではベクトルは連想リストとして表現されていて、ArrayRef でしか受けつけないのはバインディングのコンストラクタの都合です。疎ベクトルのつもりで 0 だらけの ArrayRef をコンストラクタに渡すと、値 0 の無駄なデータで連想リストが伸びて、メモリ使用量だけでなく計算量も増大します。

これを避けるためには成分をコンストラクタから与えず、Algorithm::SVM::DataSet->attribute を使用します。このメソッドはベクトルの成分と値を併せて指定することで非零成分だけを連想リストに追加できます:

sub sparse_data {
  my ($sparse_vector) = @_;
  my $data = Algorithm::SVM::DataSet->new(Label => 0);
  # 番号が若い成分を追加すると挿入ソートが走るので若い順に追加していく方が速い。
  for my $index (sort { $a <=> $b } keys %$sparse_vector) {
    $data->attribute($index => $sparse_vector->{$index});
  }
  return $data;
}
my $sparse_data = sparse_data(+{ 1123 => 1, 5813 => 1 });

宣伝

カーネル関数を使わない線形 SVM を利用したい場合、LIBLINEAR ベースの拙作 Algorithm::LibLinear4 の方が高速です。API もこっちの方が明解です (当社比)


  1. https://metacpan.org/pod/Algorithm::SVM ↩

  2. http://ja.wikipedia.org/wiki/%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%83%99%E3%82%AF%E3%82%BF%E3%83%BC%E3%83%9E%E3%82%B7%E3%83%B3 ↩

  3. http://www.csie.ntu.edu.tw/~cjlin/libsvm/ ↩

  4. https://metacpan.org/pod/Algorithm::LibLinear ↩

コメント

このブログの人気の投稿

Perl 5 to 6 - コンテキスト

2011-02-27: コメント欄で既に改訂された仕様の指摘がありました ので一部補足しました。 id:uasi に感謝します。 これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 06 - Contexts の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 06 - コンテキスト SYNOPSIS my @a = <a b c> my $x = @a; say $x[2]; # c say (~2).WHAT # Str() say +@a; # 3 if @a < 10 { say "short array"; } DESCRIPTION 次のように書いたとき、 $x = @a Perl5では $x は @a より少ない情報—— @a の要素数だけ——しか持ちません。 すべての情報を保存しておくためには明示的にリファレンスを取る必要があります: $x = \@a Perl6ではこれらは反対になります: デフォルトでは何も失うことなく、スカラ変数は配列を単に格納します。 これは一般要素コンテキスト(Perl5で scalar と呼ばれていたもの)及びより特化された数値、整数、文字列コンテキストの導入によって可能となりました。無効コンテキストとリストコンテキストは変更されていません。 特別な構文でコンテキストを強制できます。 構文 コンテキスト ~stuff 文字列 ?stuff 真理値 +stuff ...

多分週刊チラシの裏 (Sep 28 - Oct 04, 2020)

Chrome Web Store が有料 Chrome 拡張の取扱を終了 Chrome Web Store で提供されている有料 Chrome 拡張及びアプリ内課金 API の両方が 2021 年 1 月いっぱいで廃止される。 開発者はそれまでに代替となるサードパーティの課金 API に移行し、購入済ライセンスの移行手段も用意する必要がある。 この決定の発表時点で新規の有料ないしアプリ内課金のある Chrome 拡張の新規登録は終了している。実際のところ 2020 年 3 月時点で既に「一時的に」停止されており、その措置が恒久化されただけとの由。 シェルスクリプティングには長いオプションを使え 「短いオプション (e.g., -x ) はコマンドライン上での略記である。スクリプトにおいては自分や将来の同僚のためにも長いオプション (e.g., ---do-something ) を与える方が理解が容易だろう」という主張。 異論の余地なく正論である。 CobWeb - COBOL to WebAssembly Compiler COBOL から WebAssembly へのコンパイラ。いやマジで。 Cloudflare が何を思ったか同社のサーバレス環境である Workers に COBOL 対応を追加した際 の成果物である。 COBOL から C へのトランスレータである GNU COBOL と C コードをコンパイルして WebAssembly を出力する Emscripten から成っており、他の言語に比べて軽量なバイナリを生成するとのこと。 「ウチではそんな風にはやらないんだ (“We don’t do that here”)」 昨今ソフトウェア開発のコミュニティでも Code of Conduct を用意するところが増えてきたが、コミュニティの文化を明文化するのは難しい。 長大な「べからず集」は息苦しいし、肯定的なガイドラインは時に抽象的で実効的に使えない。問題となるようなふるまいの動機が善意であった場合は特にそうだ。 仮に優れたガイドラインがあっても、それに基いて人を実際に咎めるのは骨が折れることである。初中やればコミュニティ内でも疎まれる。 話の分かる相手ならそれでもまだ説得する意義もあるが、Web 上の対話で当事者双方が納得し合っ...

多分週刊チラシの裏 (Oct 19, 2020 - Feb 26, 2021)

週刊とは言ったが毎週刊とは言ってないという言い訳。 C++ のコンパイルを高速化する小技 ビルドシステムやツールを変更せずともコーディングだけで改善できるコンパイル時間短縮テクニック。 #include を減らす インライン化を明示的に避ける 関数オーバーロードの可視性を制限する 公開シンボルを減らす の 4 本。 歯医者で歯を治したら記憶能力を失った話 歯医者で簡単な治療を受けた日から後、記憶が 90 分しか保持できなくなった英国の軍人の話。まるで「博士の愛した数式」だが実話である。 DRPK で売られていた Sim City っぽいゲームのリバースエンジニアリング 平壌市内のアプリストア (物理) で売られていた Sim City 風ゲームがインストールに失敗してライセンス認証で止まってしまったのでなんとか動かせないものかとリバースエンジニアリングしてみた話。 日本にあっては DPRK のデジタル事情というと 3G セルラーが現役とか国内 Web サイトのリストがポスター一枚に収まるとか何故かコンピュータ将棋の古豪とかの断片的な情報が伝え聞かれる程度だが、近頃は Android タブレットでゲームなどもできるらしい。 国内のインフラ及びエコシステム事情に合わせて元々フリーミアム + アプリ内課金モデルだったものが買い切り 5,000 KPW (< 1 USD) になっているなど、我々が失った自由が我々よりも不自由な (はずだと我々が信じている) 国に残存しているのは皮肉だろうか。 typosquatting は単なる typo じゃ済まない typo を狙って人気のあるドメインやソフトウェアに類似した名前をつける手法 (typosquatting) は人を辟易させるのみならずセキュリティの脅威である。 IQT が 2017 年から 2020 年にかけて Python ライブラリの中央リポジトリである PyPI において行った調査で、メジャーなライブラリに名前を似せたマルウェアが 40 個確認されたとのこと。 その内 16 個が単純なスペルミス狙い (e.g., “urlib3” vs. “urllib3”) で、26 個は正当なパッケージと混同するような名前 (e.g., “nmap-python” vs. “pytho...

Mac から iPhone のカメラを起動して写真を直接取り込める

Via: The Verge ID セルフィーや (物理) 書籍のページスキャンなど携帯電話のカメラを使って写真を取り込むことは日常的な所作になっているが、写真の使い途が何かの申し込み用 Web フォームなどで iPhone より Mac の方が操作し易いときなどは億劫だ。Mac 組込の FaceTime カメラは 720p とか 1080p しかなくて非力すぎ、かといって iPhone で一旦撮影したものを Photos から探して AirDrop するのも面倒である。 実は macOS Mojave / iOS 12 以降には Continuity Camera という機能がある。これを使うと Apple 製の Mac アプリケーションから iPhone / iPad のカメラを起動して、余計な中間コピーを残すことなく写真を Mac に転送できる。 使い方は簡単で、対応している Mac アプリケーションのコンテキストメニューに “Import (or Insert) from iPhone (or iPad)” という項目がある。“Take Photo” だと一枚、“Scan Documents” だと複数の写真を (歪み補正しつつ) 連続で撮影して転送できる。 対応 Mac アプリケーションは Finder のほか iWork (Keynote, Numbers, Pages), Mail, Messages, Notes, TextEdit となっている、のだが実は Preview でも使える。同様にコンテキストメニューあるいは “File” メニューから起動できる。

Perl 5 to 6 - クォートと構文解析

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 23 - Quoting and Parsing の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 23 - クォートと構文解析 SYNOPSIS my @animals = <dog cat tiger> # or my @animals = qw/dog cat tiger/; # or my $interface = q{eth0}; my $ips = q :s :x /ifconfig $interface/; # ----------- sub if { warn "if() calls a sub\n"; } if(); DESCRIPTION クォート Perl6には強力な文字列クォート機構があり、文字列のあらゆる機能を完全に制御できます。 Perl5にはシングルクォート、ダブルクォートそして qw(...) (空白で分割するシングルクォート文字列リスト)があり、さらに q(...) と qq(...) がそれぞれシングルクォートとダブルクォートの同義語になっていました。 一方のPerl6には Q というクォート演算子が定義されていて、様々な修飾子を取ります。 :b (バックスラッシュ)修飾子はバックスラッシュによる \n のようなエスケープシーケンスの展開を許し、 :s 修飾子はスカラ変数の展開を許し、 :c はクロージャ( "1 + 2 = { 1 + 2 }" )の展開を許す、などなど。また :w は q...