2022年2月1日火曜日

lightGBMでMNISTをする。

おじさん、今月はシステムトレードの計算ができるようにするため、lightGBMの使い方を勉強し始めました。

ネットの情報ではlightGBMでMNISTができるらしい。

決定木でニューラルネットワークのようなことができるのね。

これは面白そう。

ということで、lightGBMの最初のプログラムでMNISTをやってみました。



しかし、Pythonのコードはたくさん落っこちているのですが、c++のサンプルコードはどこにも落ちていません。

マイクロソフトの公式サイトにもC++のサンプルがありません。

これ本当にC++でできんの?


一日中いろいろ試して、どうにか学習と予測のところまでC++でできました。

だけれど、C++だと学習がちょうどいいところで自動で終わってくれない。

このハイパーパラメータを自分で計算しないといけないっぽいです。

どうやって計算するんだ?


でもとりあえずできた。

実際にやってみてわかったのですが、lightGBMは他の機械学習のライブラリと比べてビルド時間や学習にかかる時間が圧倒的に速い。

調べてみると、MNISTもニューラルネットワークと比べて8倍くらい早いらしいです。

しかもMNISTのようにたくさんの次元をいれても高速。

これくらいさくさく学習できると、ストレスなくいろんな特徴量をいれることができますね。

Kaglleとかでみんなが使う理由がよくわかります。



ソースコード

--------------------------


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "LightGBM/c_api.h"


#define TRAIN_IMAGE "train-images-idx3-ubyte"

#define TRAIN_LABEL "train-labels-idx1-ubyte"

#define TEST_IMAGE "t10k-images-idx3-ubyte"

#define TEST_LABEL "t10k-labels-idx1-ubyte"


float data[]={


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,


0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,


0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,


0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,


};




struct tensor {

float* data;

int cols;

int rows;

};


static struct tensor* create_tensor(int rows, int cols) {

struct tensor* ret;

ret = (struct tensor*)malloc(sizeof(struct tensor));

ret->data = (float*)malloc(sizeof(float) * rows * cols);

memset(ret->data, 0, sizeof(float) * rows * cols);

ret->cols = cols; ret->rows = rows;

return ret;

}

static void free_tensor(struct tensor* t)

{

if (t && t->data)free(t->data);

if (t)free(t);

}




static int buf2int(char* buf_) {

int ret;

unsigned char* buf = (unsigned char*)buf_;


ret = buf[0]; ret <<= 8; ret |= buf[1]; ret <<= 8;

ret |= buf[2]; ret <<= 8; ret |= buf[3];

return ret;

}

static struct tensor* load_image_file(const char* fn)

{

struct tensor* ret = NULL;

FILE* fp;

int sz, t, w, h, n, i, j;

char buf[4];


fp = fopen(fn, "rb");

if (fp == NULL)goto end;

fseek(fp, 0, SEEK_END);

sz = ftell(fp);

fseek(fp, 0, SEEK_SET);


fread(buf, 1, 4, fp);

t = buf2int(buf);

if (t != 0x803)goto end;


fread(buf, 1, 4, fp);

n = buf2int(buf);

fread(buf, 1, 4, fp);

w = buf2int(buf);

fread(buf, 1, 4, fp);

h = buf2int(buf);

if (h * w != 784)goto end;


ret = create_tensor(n, 784);

for (i = 0; i < n; i++) {

for (j = 0; j < 784; j++) {

fread(buf, 1, 1, fp);

ret->data[i * 784 + j] = (float)(buf[0] & 255);

}

}

end:

if (fp)fclose(fp);

return ret;

}



static struct tensor* load_label_file(const char* fn)

{

struct tensor* ret = NULL;

FILE* fp;

int sz, t, n, i, j;

char buf[4];


fp = fopen(fn, "rb");

if (fp == NULL)goto end;

fseek(fp, 0, SEEK_END);

sz = ftell(fp);

fseek(fp, 0, SEEK_SET);


fread(buf, 1, 4, fp);

t = buf2int(buf);

if (t != 0x801)goto end;


fread(buf, 1, 4, fp);

n = buf2int(buf);

ret = create_tensor(n, 1);

for (i = 0; i < n; i++) {

fread(buf, 1, 1, fp);

ret->data[i] = (float)buf[0];

}

end:

if (fp)fclose(fp);

return ret;

}



int main(int argc, char* argv[])

{

int ret;


DatasetHandle hx_train, hx_test;


struct tensor* x_train, * x_test;

struct tensor* y_train, * y_test;


x_train = load_image_file(TRAIN_IMAGE);

y_train = load_label_file(TRAIN_LABEL);

x_test = load_image_file(TEST_IMAGE);

y_test = load_label_file(TEST_LABEL);



ret = LGBM_DatasetCreateFromMat(x_train->data, C_API_DTYPE_FLOAT32, x_train->rows,

x_train->cols, 1, "", nullptr, &hx_train);

if (ret) {

printf("Error: LGBM_DatasetCreateFromMat()\n");

return 1;

}

ret = LGBM_DatasetSetField(hx_train, "label", y_train->data, y_train->rows, C_API_DTYPE_FLOAT32);

if (ret) {

printf("Error: LGBM_DatasetSetField()\n");

return 1;

}


ret = LGBM_DatasetCreateFromMat(x_test->data, C_API_DTYPE_FLOAT32, x_test->rows,

x_test->cols, 1, "", hx_train, &hx_test);

if (ret) {

printf("Error: LGBM_DatasetCreateFromMat()\n");

return 1;

}

ret = LGBM_DatasetSetField(hx_test, "label", y_test->data, y_test->rows, C_API_DTYPE_FLOAT32);

if (ret) {

printf("Error: LGBM_DatasetSetField()\n");

return 1;

}


printf("Create Data OK!!\n");


BoosterHandle bh;

ret = LGBM_BoosterCreate(hx_train, "objective=multiclass "

"num_class=10 num_boost_round=300 "

"early_stopping_round=5", &bh);

if (ret) {

printf("Error: LGBM_BoosterCreate()\n");

return 1;

}


ret = LGBM_BoosterAddValidData(bh, hx_test);

if (ret) {

printf("Error: LGBM_BoosterAddValidData()\n");

return 1;

}


int is_finished = 0;

double d_min = 10;

int ct_min=0;

for (int i = 0; i < 300; i++) {

double d = 0;

ret = LGBM_BoosterUpdateOneIter(bh, &is_finished);

if (ret) {

printf("Error: LGBM_BoosterUpdateOneIter()\n");

return 1;

}

int out_len = 10;

ret = LGBM_BoosterGetEval(bh, 1, &out_len, &d);

if (ret) {

printf("Error: LGBM_BoosterGetEval()\n");

return 1;

}

printf("%d %f\n",i,d);

if (d < d_min) {

d_min = d;

ct_min = 0;

}

else {

ct_min++;

if (ct_min >= 5)break;

}

}


printf("Update OK!!\n");


long long d_len = 10;

double d[10];

for (int j = 0; j < 784; j++) {

data[j] = data[j] * 255;

}

ret = LGBM_BoosterPredictForMat(bh, data, C_API_DTYPE_FLOAT32, 1, 784,

1, C_API_PREDICT_NORMAL, 0, -1, "", &d_len, d);

if (ret) {

printf("Error: LGBM_BoosterPredictForMat()\n");

return 1;

}

for (int i = 0; i < 10; i++) {

printf("d[%d]=%f\n", i, d[i]);

}


LGBM_BoosterFree(bh);

LGBM_DatasetFree(hx_train);

LGBM_DatasetFree(hx_test);

        free_tensor(x_train); free_tensor(y_train);

free_tensor(x_test); free_tensor(y_test);

return 0;

}

--------------------------


結果

--------------------------

[LightGBM] [Info] Load from binary file x_train

Create Data OK!!

[LightGBM] [Warning] Auto-choosing col-wise multi-threading, the overhead of testing was 1.095544 seconds.

You can set `force_col_wise=true` to remove the overhead.

[LightGBM] [Info] Total Bins 109606

[LightGBM] [Info] Number of data points in the train set: 60000, number of used features: 629

[LightGBM] [Info] Start training from score -2.315501

[LightGBM] [Info] Start training from score -2.185988

[LightGBM] [Info] Start training from score -2.309610

[LightGBM] [Info] Start training from score -2.280987

[LightGBM] [Info] Start training from score -2.329271

[LightGBM] [Info] Start training from score -2.404064

[LightGBM] [Info] Start training from score -2.316346

[LightGBM] [Info] Start training from score -2.259366

[LightGBM] [Info] Start training from score -2.327732

[LightGBM] [Info] Start training from score -2.311121

0 1.679374

1 1.373263

2 1.159687

...

...

149 0.000824


Update OK!!


d[0]=0.000818

d[1]=0.000104

d[2]=0.236144

d[3]=0.014695

d[4]=0.573232

d[5]=0.003431

d[6]=0.016848

d[7]=0.002404

d[8]=0.142658

d[9]=0.009667

--------------------------


おー、なんかあってる。
でもCPPでlightGBMつかうの大変ね。


2022年1月27日木曜日

システムトレードのWebサイトを作ってみた。

おじさん、オミクロン株の流行で外へ出かけられないので、毎日家に引きこもりです。

家でやることがないので、システムトレードのシミュレーターを作ってみました。

前回Protra言語のインタープリタを作ったことをブログに書いたのですが、

こういうのをブログに書くと、「副業で儲ける方法」とか「株で1億円儲けられる」とか「仮想通貨で夢の生活」とか怪しいメールがたくさん来るようになってしまいました。

世の中ほんと人の欲に付け入る悪い人ばかりだなぁ。

そんな簡単にもうかるんだったらこんなツールつくらないっつーの。


システムトレードのソフトウェアって、スクリプト言語の実装、株価情報の取得更新、損益計算、グラフ表示、データ変換、Webサーバ構築、セキュリティー対策、クライアントUXページ作成、JavaScriptr作成、ソースコード公開などいろんなことをやらないといけないので、結構大変。毎日深夜まで、仕事以外の空き時間を1か月全力投入でやっとここまで完成。

これは会社とかグループで作るもので、ひとりで作るものじゃないな。


日本の株価のデータも1Gバイトくらいあり、結構でかい。

これを高速で処理しないとシミュレーション結果がすぐにでないので、C++で実装しないとだめ。

最初、年末年始の一週間くらいでできないかなぁと思っていたのですが、一か月くらいかかってしました。


やっと動くようになっただけでまだまだ機能が貧弱ですが、どうにかWebでシステムトレードのシミュレーションが動くところまでできた。

 http://one.agolamusic.com/htdocs_protra/main.html



こんな感じでグラフも表示できます。

ソースコードはこちら、

https://github.com/yomei-o/Protra_cpp


おじさん、いつもスマートフォンやタブレットでもきちんと動くようにWebサイトを作るので、このシステムを使えば通勤電車のなかとかいつでもどこでもトレードのシミュレーションを行うこともできます。

コアの計算部分以外のグラフ表示とかは全部クライアントのブラウザで処理をしているのですが、最近のスマホのブラウザって本当にすごいねぇ。なんでもできちゃうのね。


来週からは、lightGBMのディープラーニングエンジンを追加しようと思っていますが、

これも結構時間かかりそう。

外に出れない間の暇つぶしにはちょうどいいかなぁ。


2022年1月12日水曜日

Protra言語のインタープリタを実装してみた。

 おじさん、毎年年末年始は、なにかお金儲けでがきそうなライブラリを作ることにしています。

今年は何を作ろうか考えた結果、Protra言語のインタープリタを作ることにしました。

https://ja.osdn.net/projects/protra/wiki/FrontPage



ここに書かれているとおり、Protraとはシステムトレードをするソフトです。

コンピュータのプログラムが儲かりそうな株を分析してくれるのがシステムトレードソフト。そのプログラムの分析の通り株を買うと株で儲かるはず・・・・。

https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%88%E3%83%AC%E3%83%BC%E3%83%89

このシステムトレードをするソフトのなかでオープンソースのものがProtraなのです。

このProtoraを改造して、ディープライニングのLightGBMなどと連携させると、最新のシステムトレードソフトになるらしい。

https://ja.wikipedia.org/wiki/LightGBM


LightGBMのプログラムを書ける人がシステムトレードの上位にはいれるのか!

https://nehori.com/nikki/2021/11/27/post-33741/


これは、お金儲けできそう。

しかし、なぜかオリジナルのProtoraはC#で書かれているため、他のライブラリと連携できないため、おじさんのMacやPythonからも使えないのです。


なるほど、ここにProtraの問題点があるんですね。

これはなんとかしなければ。

ということで、まずはProtraインタープリタをC++で実装してみました。

https://github.com/yomei-o/Protra_cpp


年末年始に実装できたのはProtra言語のインタープリタ部分だけなのですが、とりあえずこれでProtra言語がLinuxやMacでも動くし、LightGBMとも連携させられそうだし、簡単にPythonからもProtoraを使うことができそうです。

あと株価系のビルトイン関数とPythonとのインタフェースを作れば、システムトレードのシミュレーションをすることができるはず。


今年中にある程度完成できればいいなぁ。

ソフトが完成したら誰かお金投資してくれないかなぁ。

そしたら以下のようなお金のむしり取り合いに参加できるんだけどなぁ。

https://nehori.com/nikki/2021/12/16/post-11240/



2021年9月27日月曜日

C言語の"Hello, World!"をGoogle Colaboratoryでやってみた。

おじさん、ふと思ったのですが、

コロナ渦での大学とかのプログラミング演習ってどうやってやってんだろ?

だって大学に行かないとコンピュータの端末とかないじゃん!


このように思って調べてみました。

なんと、今どきの理系の若者は、Google Colaboratoryを使って、スマホやタブレットでプログラムを書くらしい。

そしてGoogleドライブでそのまま提出!

なにそれ?おじさんついていけない・・・・。

おじさんが大学生の時は、情報センターのワークステーションを何時間も占領してプログラムを書いていたのに。

いまはスマホやタブレットで家や電車の中などどこでもプログラムができるんですね。

時代の違いを感じる・・・。





ということで、Google Colaboratoryを使ってC言語の"Hello, World!"をやってみました。


1. Google Colaboratoryで新しいノートブックを作成


2. コードのセルを追加して以下のコードを入力

-------------------------

%%file hello.c


#include  <stdio.h>

int main(void) {

   printf("Hello, World!\n");

   return 0;

}


-------------------------


3. コードのセルをもう一つ追加して以下のコードを入力

-------------------------

!gcc hello.c -o hello && ./hello

-------------------------


4. 上記の2つのセルを実行。

-------------------------

Hello, World!

-------------------------



おーすげー、コンパイラをインストールとかしなくていいんだ。

ボダンを押すだけでコンパイルして実行してくれます。


文法とか間違うとどうなるんだろ?

やってみました。

-------------------------

%%file hello.c

#include  <stdio.h>

int main(void) {

    hogehoge

   printf("Hello, World!\n");

   return 0;

}

-------------------------


-------------------------

!gcc hello.c -o hello && ./hello

-------------------------


-------------------------

hello.c: In function ‘main’:

hello.c:3:5: error: unknown type name ‘hogehoge’

     hogehoge

     ^~~~~~~~

hello.c:4:11: error: expected declaration specifiers or ‘...’ before string constant

    printf("Hello, World!\n");

           ^~~~~~~~~~~~~~~~~

-------------------------


すげー。ちゃんとコンパイルエラーも出る。




C言語のGPUとかも使えるのかな???

-------------------------

!nvcc hello.c -o hello && ./hello

-------------------------


-------------------------

Hello, World!

-------------------------


おーなんかGPUも使えるっぽい。

スマホではコーディングをやりにくい人もFireHD10のタブレットを1万円で買えば、

ほとんど初期投資ゼロ円で簡単なディープラーニングまで学べるのね。

Google Colaboratoryすごすぎる。


コンピュータ将棋ソフトを動かす。

 先日のブログでも触れたように、おじさんonnxruntimeを自由自在に使えるようになりました。

いつもMNISTばっかりだとつまらないので、ディープラーニングのふかうら王をビルドしてみました。


https://yaneuraou.yaneu.com/

https://github.com/yaneurao/YaneuraOu


このソースコード本当によく整備されています。

ディープラーニング系も非ディープラーニング系も誰でも簡単にいろいろなビルドオプションでビルドができます。


囲碁とか将棋のディープラーニングのソフトは、モンテカルロツリー検索 (MCTS)をしてゲームプレイの手を決定するんですね。

ソースコードを見ているだけで本当にいろいろ勉強になります。

ソースコード少ないし、簡単にビルドできるし、将棋ソフトってディープラーニングや強化学習の勉強にとっても良い?


いろんな条件で学習させたデータで将棋ソフト同士を対戦させるのも面白いね。

elmo囲い強いなぁとか将棋の勉強にもなります。


BERTを使った将棋ソフトもあるようです。

https://github.com/nyoki-mtl/bert-mcts-youtube

いままで全然気づかなかったけれど、たしかに、NLPの文章の単語入力と将棋の盤面データ入力はデータ構造的には似てるもんね。


実際に将棋ソフトをカスタマイズしてビルドして動かしてみてわかったのですが、

これ学習結果を利用してコンピュータ将棋ソフトを動かすだけでも相当スペックの高いPCがいるね。

人間よりめちゃくちゃ強いけど人間よりめちゃくちゃ遅い。

それで藤井聡太さんがRyzenThreadripperを買うのね。

さらに、学習結果を利用するだけでこれこんだけ遅いから、将棋を学習させるのに1000万円くらいのGPUがいる。


将棋に限らず、ディープラーニング系のソフトを動かすといつも思うのですが、このブログで書いているようなことを学習させるのに、だいたい100万円位のGPUが8枚くらいいります。

腕時計に例えると、

ロレックスサブマリーナ、ロレックスサブマリーナ、

ロレックスサブマリーナ、ロレックスサブマリーナ、

ロレックスサブマリーナ、ロレックスサブマリーナ、

ロレックスサブマリーナ、ロレックスサブマリーナ、


ロレックスをじゃんじゃん買えるような人でないと最先端のディープラーニングの学習ってできないのです。


おじさんの持ってるGPUは腕時計でいうとSeiko5

見た目は似てるよねー。

おじさんも日本語のBERTやりたかったのですが、ディープラーニングって一部のお金持ちしか学習させることができないのでしょうか?

一般の人が買えるGPUで学習させられるのはMNISTとRESNET50くらいだよね。

みんなどうやって学習させてるの???


2021年9月13日月曜日

SIMDライブラリ、libsimdppを使う

 最近、カメラの動画で画像処理やディープラーニングなどの計算をやることが増えています。

その時に常に課題にあがるのが、処理速度の高速化。



ディープラーニングのネットワークってそもそも計算の量を減らしながら良い結果が得られるように設計されているし、ディープラーニングのフレームワークも相当高速化されているから、そう簡単にこれ以上早くならないっつーの。


最近はいろいろな機械学習フレームワークが、学習の計算はGPUでやって、その結果を利用するのはSIMD命令でやって高速化するようになっているようです。

SIMD命令ってコンパイラやCPUに依存するからあんまり使いたくないんだよねぇと思っていたら、SIMD命令を抽象化するライブラリを見つけました。

いろいろなプロジェクトがSIMD命令のところをどう書くのか、いろいろ試行錯誤しているようですが、なかなか読みやすくて、移植性があるように書くのが難しいようです。

いろいろ調べていたらついに便利なライブラリを見つけました。

https://qiita.com/engineer/items/f47a54bffe2691f9784a


なんだ、ちょー簡単にSIMD命令を抽象化できる、libsimdppライブラリがあるじゃないか!


https://github.com/p12tic/libsimdpp


これ本当によくできています。

SIMD命令を使わない設定やSIMD命令がないコンパイラのときはSIMD命令をエミュレーションしてくれるので、PCの高度なIDE環境で開発を行って、実際には組み込みで動かすというようなことも簡単にできます。

簡単にSIMDを使う場合と使わない場合を切り替えられるので、実行速度を比較しながらパフォーマンスチューニングを行うこともできます。


こんだけ便利なライブラリなのになぜか使い方のサンプルがないので、サンプルを作ってみました。


------------------------------------------------------

#define SIMDPP_ARCH_X86_SSE4_1 true

//#define SIMDPP_ARCH_POWER_ALTIVEC true

//#define SIMDPP_ARCH_NULL true


#include <iostream>

#include <chrono>

#include <simdpp/simd.h>

//#include "simdpp_simd.h"


//example where i got this from

//https://github.com/p12tic/libsimdpp/tree/2e5c0464a8069310d7eb3048e1afa0e96e08f344


// Initializes vector to store values

void init_vector(float* a, float* b, size_t size) {

    for (int i=0; i<size; i++) {

        a[i] = i * 1.0;

        b[i] = (size * 1.0) - i - 1;

    }

}




using namespace simdpp;

int main() {

    //1048576

    const unsigned long SIZE = 4 * 1500000;


    float* vec_a=new float[SIZE];

    float* vec_b= new float[SIZE];

    float* result= new float[SIZE];


///////////////////////////*/

//LibSIMDpp

    //*

    auto t1 = std::chrono::high_resolution_clock::now();


    init_vector(vec_a, vec_b, SIZE);

    for (int i=0; i<SIZE; i+=4) {

        float32<4> xmmA = load(vec_a + i);  //loads 4 floats into xmmA

        float32<4> xmmB = load(vec_b + i);  //loads 4 floats into xmmB

        float32<4> xmmC = add(xmmA, xmmB);  //Vector add of xmmA and xmmB

        //float32<4> xmmD = div(xmmA, xmmB);  //Vector add of xmmA and xmmB

        store(result + i, xmmC);            //Store result into the vector

    }


    auto t2 = std::chrono::high_resolution_clock::now();


    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count()

              << " milliseconds\n";

    //*/



///////////////////////////*/

//standard

    //*

    init_vector(vec_a, vec_b, SIZE);

    t1 = std::chrono::high_resolution_clock::now();


    for (auto i = 0; i < SIZE; i++) {

        result[i] = vec_a[i]  + vec_b[i];

    }


    t2 = std::chrono::high_resolution_clock::now();


    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count()

              << " milliseconds\n";

    //*/



    delete[] vec_a;

    delete[] vec_b;

    delete[] result;

    return 0;

}


------------------------------------------------------


2021年9月10日金曜日

ONNX RuntimeでMNISTをする。

 少し前、将棋の藤井颯太さんが、70万円もするパソコンでコンピュータ将棋ソフトを動かしているらしいと話題になりました。


これ、どのように動いているのだろう?


調べてみると、オープンソースの将棋ソフトは、「やねうら王」ベースのソフトと「dlsyogi」ベースの二つのものが主流らしい。

そして、onnxruntimeを使えば、「やねうら王」でも「dlsyogi」のonnxフォーマットのデータが読めるらしい。


すごいねー。

機械学習系のソフトってだいたいバージョンがちょっとでも合わないと動かないのに、将棋ソフトはonnxruntimeを使っているので、onnxruntimeとonnxのデータのバージョンが違ってもきちんと動きます。


onnxruntimeソースを見てみると、onnxのバージョンごとに使う関数を変えて互換性を保っているのですね。




このように、最近onnxruntimeを使った面白いソフトがたくさん出てきているので、onnxruntimeの使い方を勉強がてら、いつものようにMNISTを作ってみました。


ONNX Runtime

https://github.com/microsoft/onnxruntime


ONNX MNIST

https://a-kawashiro.hatenablog.com/entry/2019/03/07/201304


onnxruntimeってMLASというMicrosoftBLAS?みたいなライブラリを使っているのですが、IntelのCPUとVisualStudioのコンパイラーでしか動かなかったいものを、無理やり他のプラットフォームやコンパイラーでも動くようにした感が満載です。

一部アセンブラがないとビルドできないとか、SIMD命令の書き方がきちんと分離できてないとか。なんなんだこのへたくそライブラリ。


おじさん、onnxruntimeをC/C++言語だけで動くように改造してみました。

本当はSIMD命令もきちんと分離したかったのですが、これはめんどくさくて途中で断念。

でもC++のMNISTできた!


----------------------

#include <onnxruntime_cxx_api.h>

#include <algorithm>

#include <iostream>


struct MNIST {

MNIST() {

auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);

input_tensor_ = Ort::Value::CreateTensor<float>(memory_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size());

output_tensor_ = Ort::Value::CreateTensor<float>(memory_info, results_.data(), results_.size(), output_shape_.data(), output_shape_.size());

}


std::ptrdiff_t Run() {

const char* input_names[] = {"Input3"};

const char* output_names[] = {"Plus214_Output_0"};


session_.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor_, 1, output_names, &output_tensor_, 1);


result_ = std::distance(results_.begin(), std::max_element(results_.begin(), results_.end()));

return result_;

}


static constexpr const int width_ = 28;

static constexpr const int height_ = 28;


std::array<float, width_ * height_> input_image_{};

std::array<float, 10> results_{};

int64_t result_{0};


private:

Ort::Env env;

Ort::Session session_{env, ORT_TSTR("model.onnx"), Ort::SessionOptions{nullptr}};


Ort::Value input_tensor_{nullptr};

std::array<int64_t, 4> input_shape_{1, 1, width_, height_};


Ort::Value output_tensor_{nullptr};

std::array<int64_t, 2> output_shape_{1, 10};

};


std::unique_ptr<MNIST> mnist_;


int main()

{

try {

mnist_ = std::make_unique<MNIST>();

} catch (const Ort::Exception& exception) {

std::cerr << exception.what() << std::endl;

return 1;

}


mnist_->input_image_ = {

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,

0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

};

mnist_->Run();

std::cout << mnist_->result_ << std::endl;


std::cout << std::endl;

std::cout <<"detail" <<std::endl;


std::array<float, 10>::iterator ite;

int ct = 0;

for (ite = mnist_->results_.begin(); ite != mnist_->results_.end(); ite++) {

std::cout << ct++<<"     "<<(*ite) << std::endl;

}

return 0;

}

----------------------


全ソースはこちら。

https://drive.google.com/file/d/1-96nTJBsDRhdvFz09C4d17vS8r50YntS/view?usp=sharing