しろかい!

アプリ開発や機械学習などの開発Tips.

【LIBLINEAR】Pythonから使う方法と確率値の取得方法

LIBLINEAR を Python から使う方法を解説します.
基本的な学習と予測の方法はもちろんですが,LIBLINEAR がサポートするロジスティック回帰では予測値の確率を得ることもできます.これを Python のコードから取得する方法も紹介します.

インストール

LIBLINEAR をインストールしたディレクトリ (例:liblinear-2.01/) 上で,以下のコマンドを実行するだけです.

$ make lib

これにより liblinear.so.3 というファイルが作成されます.
これで Python から LIBLINEAR を使う準備ができました.

使い方

詳しい使い方は liblinear-2.01/python/README に書かれています.
以下では基本的な使い方に絞って説明します.

予めコードの冒頭で LIBLINEAR の Python用のモジュールをインポートしておきます.

$ python
>>> import sys
>>> sys.path.append('liblinear-2.01/python までの絶対パス')
>>> from liblinearutil import *
>>> from liblinear import *

学習と予測

最も基本的な使い方です.

# LIBLINEAR形式のファイルを読み込む.
# labels にラベルのリスト, features に対応する素性値のリストが格納される.
>>> labels, features = svm_read_problem('heart_scale')
>>> labels
>>> [1.0, -1.0, 1.0, -1.0, -1.0, ...]
>>> features
>>> [{1: 0.708333, 2: 1.0, 3: 1.0, 4: -0.320755, 5: -0.105023, 6: -1.0, 7: 1.0, 8: -0.419847, 9: -1.0, 10: -0.225806, 12: 1.0, 13: -1.0}, {1: 0.583333, 2: -1.0, 3: 0.333333, 4: -0.603774, 5: 1.0, 6: -1.0, 7: 1.0, 8: 0.358779, 9: -1.0, 10: -0.483871, 12: -1.0, 13: 1.0}, ...]

# 学習.第3引数に ./train コマンドのオプションを指定できる.
>>> model = train(labels[:200], features[:200], '-c 4')

# 予測 (テスト).
# テストデータの正解ラベルと素性値のリスト,学習済モデルを渡す.
# ここでは省略されているが第4引数に ./predict コマンドのオプションを渡すことも可能.
# p_label に入力したリストに対応したラベルの予測値のリストが返ってくる.
>>> p_label, p_acc, p_val = predict(labels[200:], features[200:], model)

学習時に -v n オプションを渡せばもちろん cross validation できますが,上述した方法で評価尺度を変更していたとしても,accuracy が出力されるようです.

データはコード内でリストと辞書を使って生成することもできます.

>>> labels, features = [1,-1], [{1:1, 3:1}, {1:-1,3:-1}]
>>> prob  = problem(labels, features)
>>> param = parameter('-s 0 -c 4 -B 1')
>>> m = train(prob, param)

モデルの保存と読み込み

# 生成したモデルをファイルに保存
>>> save_model('heart_scale.model', model)

# 生成済モデルのファイルを読み込む
>>> model = load_model('heart_scale.model')

確率の取得

ロジスティック回帰では通常予測値の確率を得ることができます.
LIBLINEAR でももちろん取得できるのですが,方法がちょいと複雑です.

LIBLINEAR では liblinear.predict_probability() 関数を使うことで,予測値の確率を得ることができます.
この関数は以下の3つの引数が必要です.

  • model: 学習済みのモデル
  • feature_node: 予測したいデータの素性値 (feature_node_Array という特殊な型)
  • probabilities: 確率を格納する受け皿になる配列.

なのでこれらをまず用意します.

1) model

# 学習済みモデルを読み込む
>>> model = load_model('heart_scale.model')

2) feature_node

# 予測したいサンプル (データ) の素性値
>>> feature_dict = {1:1, 3:1, 5:-2}
# feature_node を生成.
>>> feature_node, max_idx = gen_feature_nodearray(feature_dict)

ちなみに,max_idx には入力した feature_dict の最大のインデックスが格納されます (上の例では 5 が格納される).

3) probabilities

# 各ラベル毎の確率を格納するリストを生成.
>>> from ctypes import c_double
>>> probabilities = (c_double * model.get_nr_class())()

かなり特殊な書き方ですが,これは c_double という C言語 の double型 の値を model.get_nr_class() 個格納できるリストを生成します.
model.get_nr_class()model によって予測されうるラベルの数です (heart_scale の場合は 1 と -1 の2値予測なので,model.get_nr_class() = 2 となる).
各ラベルとなり得る確率がこのリストに格納されることになります.

3つの引数が用意出来たので,実際に予測値の確率を取得します.

# 予測.返り値は予測したラベルで,確率は probabilities に格納される.
>>> label = liblinear.predict_probability(model, feature_node, probabilities)

予測値と予測値の確率は以下のようにして取り出せます.

# 予測値
>>> label
1.0
# 予測値の確率
>>> probabilities[model.get_labels().index(label)]
0.802037613535

予測値の確率は probabilities 内にリストとして格納されています.
並び方は model.get_labels() で得られる予測されうるラベルのリストと同じなので,そこから index を取得してやります.
ちなみに以下のようにすれば,ラベルが -1 となる確率を得られます.

>>> probabilities[model.get_labels().index(-1)]
0.197962386465

以上をまとめると以下のようになります.
細かいことはよく分からないという方は,とりあえず以下の通りにやればいいと思います.

# 必要なモジュールのimport
>>> import sys
>>> sys.path.append('liblinear-2.01/python までの絶対パス')
>>> from liblinearutil import *
>>> from liblinear import *

# 学習済みモデルを読み込む
>>> model = load_model('heart_scale.model')
# 予測したいサンプル (データ) の素性値から feature_node を生成.
>>> feature_dict = {1:1, 3:1, 5:-2}
>>> feature_node, max_idx = gen_feature_nodearray(feature_dict)
# 各ラベル毎の確率を格納するリストを生成.
>>> from ctypes import c_double
>>> probabilities = (c_double * model.get_nr_class())()

# 予測.返り値は予測したラベルで,確率は probabilities に格納される.
>>> label = liblinear.predict_probability(model, feature_node, probabilities)

# 予測値
>>> label
1.0
# 予測値の確率
>>> probabilities[model.get_labels().index(label)]
0.802037613535

まとめ

LIBLINEAR を Python から使う方法を解説しました.
特に確率の取得方法を解説しているWeb上の記事は(調べた限りでは)ないので,参考になれば幸いです.