LoginSignup
8
4

More than 1 year has passed since last update.

LightGBMをg++で使ってみた

Last updated at Posted at 2021-12-22

概要

  • LightGBM CLI のインストール
  • LightGBM CLIをC++で実行

はじめに

今回はLightGBMをマイクロソフトが提供しているLightGBMのCLI版を用いて、c++でコンパイルできる方法を紹介していきます。
処理の流れとしては

  1. LigthGBM CLIを用いてモデルを学習
  2. 学習したモデルファイルを用いて、C++のコードを作成
  3. g++でコンパイル、実行ファイルの作成

という感じになっています。
実験環境は

  • google colaboratory

です。
興味のある方はぜひ途中まででも大丈夫ですので、ご付き合いください(^^)

メイン

LightGBM CLI とは?

まず、「LightGBM CLIとはなんぞや?」となってる方に簡単に説明すると、LightGBM CLIという名の通り、LightGBMのCLI(CUI)バージョンです。通常であればLightGBMはpipscikit-learnでインストールして来て.pyimport lightgbm as gbmとして使ったりしますよね。CLIバージョンでは、./lightgbmとCUIで入力して使います。
とまあ、脇道に逸れた話は置いといて、先に進みましょう。

LightGBM CLIのインストール

それではCLIバージョンのLightGBMをインストールしていきましょう。
と言っても、そんなに難しいことはしなくて公式サイトのインストール手順の通りにインストールしていきます。

公式サイト

今回はcolabで実験したので環境はLinuxで行ってます。実行コードは↓です。

!git clone --recursive https://github.com/microsoft/LightGBM
%cd LightGBM
!mkdir build
%cd build
!cmake ..
!make -j4
!make -j4 install

上記のコードを実行するとLightGBMというディレクリの中に、lightgbmという実行ファイルが作成されます。

LightGBM CLIの使い方

インストールが終わったので、次にCLIでLightGBMを使う方法を見ていきましょう。

今回使うデータは定番のirisデータセットを使っていきます。

Datasetの準備

まずはirisデータセットを準備していきましょう。今回はseabornの方のirisを使っています。

import seaborn as sns
iris = sns.load_dataset('iris')
import numpy as np
import pandas as pd

columns = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
data = iris[columns]

conditions=[(iris["species"] == "setosa"),(iris["species"] == "versicolor"), (iris["species"] == "virginica")]
choice = [0,1,2]

target = pd.Series(np.select(conditions, choice))

データの整形後、trainとtestに分割していきます。
その後、CLIでtrainingするために、CSVの形でtrainとtestデータを保存しておきます。

from sklearn.model_selection import train_test_split

train_data, test_data, train_target, test_target = train_test_split(data, target, test_size=0.2, shuffle=True,random_state=42)

pd.concat([train_target, train_data], axis=1).to_csv("iris.train", header=False, index=False)
pd.concat([test_target, test_data],axis=1).to_csv("iris.test", header=False, index=False)

Train, Testファイルについて

iris.trainiris.testについて軽く説明をしたいと思います。
まずファイル名ですが、エイリアスが設定されておりそれに合わせて名前を決めることができます。

次にtrainの目的変数と説明変数の順番ですが、何も設定をしなければデフォルトで一番最初の列が目的変数になります。
設定するパラメータはlabel_columnで設定することができます。

Train, Convert, Predictのファイルを作成

CLIでは、taskに合わせてconfig fileのようなものを作成する必要があります。

ここでは、主に使用するであろうtrain、predict、convert_model(convert)について説明したいと思います。(refit、save_binaryはよくわからないです)

trainとpredictは名前の通りの役割なのでわかりやすいのかなと思うのですが、convertはわかりにくい感が満載です。
convertで何をするか簡単に説明しますと、

  1. 学習したモデルを.cppの形で保存
  2. buildmakeで学習したタスクの新しいLightGBM実行ファイルを構築
  3. 新しく構築したLightGBMを使ってpredictを行う

という感じです。(下記で参考サイト乗っけてます。)
個人的には、predict機能がCLIで元からできるのになんでconvertという機能があるのかなと感じました。
最初はconvertした.cppを使って、c++でそのモデルを使えるようになるのかなと思ったのですが、これは僕のc++に対する理解不足と相まって、「おそらく使えないだろうけどわからないな」という結論になりました。(もし分かる方がいたらご教授お願いします)

とまあ機能の説明はここまでにして、まずはファイルの作り方を紹介していきます。
ファイルの作り方としては、設定したいパラメータを羅列して、ファイル名をaliasに合わせて保存するだけです。
以下のコードはパラメータの例です。パラメータはpythonでLightGBMを使うときと変わらないので、そんなに違和感なくて設定できると思います。

# train
train_conf = """
task = train
objective = multiclass
metric = multi_logloss
data = iris.train
num_class = 3
output_model = trained_model.txt
num_tree = 5
num_leaves = 3
"""

with open("train.conf", "w") as f:
    f.write(train_conf)

# convert
convert_conf = """
task = convert_model
input_model= trained_model.txt
convert_model_language = cpp
"""

with open("convert.conf", "w") as f:
    f.write(convert_conf)

# predict
predict_conf = """
task = predict
input_model = trained_model.txt
data = iris.test
"""

with open("predict.conf", "w") as f:
  f.write(predict_conf)

上記のコードを実行すると、train.conf,convert.conf,predict.confの3つのファイルが作成できます。
それぞれの使い方は公式サイトの通りです。

Train

! ./lightgbm config=train.conf

oupput_modelのパラメータで設定した.txtファイルが作成されます。
今回であれば、trained_model.txtという名前でモデルのファイルが作成されます。
ちなみになんですが、このモデルファイルはpythonで作成したモデルファイルと中身は一緒です。
このコマンドですね → model.save_model('model.txt')

Convert

! ./lightgbm config=convert.conf

ここではパラメータを設定してないので、デフォルトのgbdt_prediction.cppが作成されます。
convert_modelのパラメータで設定することができます。
ちなみにConvertで生成されたファイルの使い方はGithubのIssueのところにあったので参考にしてもらえればと思います。

一応該当コードも添付しときます。

cd $BUILD_DIRECTORY/tests/cpp_test && ../../lightgbm config=train.conf convert_model_language=cpp convert_model=../../src/boosting/gbdt_prediction.cpp && ../../lightgbm config=predict.conf output_result=origin.pred || exit -1 
cd $BUILD_DIRECTORY/build && make lightgbm -j4 || exit -1 
cd $BUILD_DIRECTORY/tests/cpp_test && ../../lightgbm config=predict.conf output_result=ifelse.pred && python test.py || exit -1 

Predict

! ./lightgbm config=predict.conf

ここでもパラメータを設定してないので、デフォルトのLightGBM_predict_result.txtが作成されます。
output_resultのパラメータで設定することができます。

C++で使う

それではいよいよc++でコンパイルしていきます。ここで必要になってくるファイルはTrainで生成された.txtファイルと、testデータセットです。
そしてLightGBMのC APIというものを使用します。

c++に関してはあまり知識がないので、誰か海外ニキが試みてないのかなーと探してたら、あるにはあったんですがそれだけでは実行ができませんでした。ただ参考にはとてもなったので、そのサイトを以下に載せています。

コード

まずc++のコードです。colabで実行しているので、%%writefile skeleton_test.cppが記載してあります。

%%writefile skeleton_test.cpp

#include "LightGBM/c_api.h"
#include <stdio.h>
#include <iostream>
#include <vector>

void predict()
{
    int temp;
    int p = 1;
    BoosterHandle handle;  

    // load model 
    temp = LGBM_BoosterCreateFromModelfile("trained_model.txt", &p, &handle);    
    std::cout <<"load result value is "<<temp <<std::endl;

    // predict file data
    const char* para = "None";
    int res = LGBM_BoosterPredictForFile(handle, "iris.test", 0, C_API_PREDICT_NORMAL, 0, 0, para, "result");
    std::cout << "file predict result is " << res << std::endl;

    // row data
    std::vector<float> row={6.1,2.8,4.7,1.2};
    void* in_p = static_cast<void*>(row.data());

    std::vector<double> out = (3, 0); //result用
    double* out_result = static_cast<double*>(out.data());

    int64_t *out_len = new int64_t();
    res = LGBM_BoosterPredictForMat(handle, in_p, C_API_DTYPE_FLOAT32, 1, 4, 1, C_API_PREDICT_NORMAL, 0, 2, "None", out_len, out_result);
    std::cout << "row predict return is " << res << std::endl;
    std::cout << "row predict result size is " << int(out.size()) << " value is " << out_result[0] << " "<< out_result[1] << " " << out_result[2] << std::endl;

    delete out_len;

}

int main(void) {
  predict(); 

  std::cout << "Ok complete!"<< std::endl;
  return 0;
}

このコードの主な点は、

  • モデルをロード
  • ファイルデータからpredictする
  • 配列などのrowデータから予測する

といった感じです。
使ったC APIとしては

の3つです。

最後に以下のコマンドを実行することで完了です。

!g++ -fpermissive  -o a.out -v skeleton_test.cpp lib_lightgbm.so -Wl,-rpath,/content/LightGBM
./a.out

実行結果としては、まずファイルデータからpredictした場合はパラメータで指定してるresultという結果ファイルが作成されます。
そしてrowデータから予測してる場合は、コマンドラインに結果が表示されます。
↓こんな感じです。

load result value is 0
file predict result is 0
row predict return is 0
row predict result size is 3 value is 0.274069 0.45598 0.269951
Ok complete!

これにて完了!!

最後に

今回はLightGBMをC++で使えるようにしてみました。エッジデバイスとかで使うときに役に立つかもなくらいには考えています。C++も使えようになりたいなと思った今日このごろでした。

8
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4