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



0 件のコメント:

コメントを投稿