🗂
1ファイルでニューラルネットワークを作れる MiniDNN を試してみた。
はじめに
C++ ヘッダオンリーのライブラリが大好きなので、日々 GitHub を散策しています。
今日も探索していたら MiniDNN というライブラリを見つけたので試してみました。
MiniDNN とは
MiniDNN は Eigen を使ったニューラルネットワーク構築ライブラリです。行列の操作は Eigen::Matrix
等を使い、各レイヤの構築からバッチの実行、推論まで行えます。
特徴としては
- ヘッダーのみで移植性が高い
- CPU だけど高速
- モジュール化されていて拡張可能
- 詳細なドキュメントを提供
- DNN がどのように機能するかを理解するのに役立つ
- DNNの良い部分と汚い部分の両方を学んで実践する素晴らしい機会を提供 (笑)
サンプル書いてみた
いつもながら iris のサンプル
#include <iostream>
#include <string>
#include <algorithm>
#include <MiniDNN.h>
static std::vector<std::string>
split(std::string& fname, char delimiter) {
std::istringstream f(fname);
std::string field;
std::vector<std::string> result;
while (getline(f, field, delimiter)) {
result.push_back(field);
}
return result;
}
int
main() {
std::ifstream ifs("iris.csv");
std::string line;
std::getline(ifs, line);
std::vector<double> rows;
std::vector<std::string> names;
while (std::getline(ifs, line)) {
// sepal length, sepal width, petal length, petal width, name
auto cells = split(line, ',');
rows.push_back(std::stof(cells.at(0)));
rows.push_back(std::stof(cells.at(1)));
rows.push_back(std::stof(cells.at(2)));
rows.push_back(std::stof(cells.at(3)));
names.push_back(cells.at(4));
}
Eigen::Map<Eigen::MatrixXd> x(rows.data(), 4, rows.size()/4);
std::map<std::string, std::size_t> labels;
std::vector<double> counts;
std::vector<std::string> tmp;
for (auto& name : names) {
std::size_t &label = labels[name];
if (label == 0) {
label = labels.size();
tmp.push_back(name);
}
counts.push_back((double) (label - 1));
}
names = tmp;
Eigen::Map<Eigen::MatrixXd> y(counts.data(), 1, (int) counts.size());
y /= 3;
MiniDNN::Network net;
net.add_layer(new MiniDNN::FullyConnected<MiniDNN::Identity>(4, 4));
net.add_layer(new MiniDNN::FullyConnected<MiniDNN::ReLU>(4, 4));
net.add_layer(new MiniDNN::FullyConnected<MiniDNN::Identity>(4, 1));
net.set_output(new MiniDNN::RegressionMSE());
MiniDNN::RMSProp opt;
opt.m_lrate = 0.001;
MiniDNN::VerboseCallback callback;
net.set_callback(callback);
net.init(0, 0.01);
net.fit(opt, x.matrix(), y.matrix(), 100, 1000);
Eigen::MatrixXd pred = net.predict(x.matrix());
for (auto v : pred.colwise()) {
std::cout << v[0] << " " << names[(int)(v[0] * 3)] << std::endl;
}
return 0;
}
試してないけど Eigen なので SYCL のセレクタを使えば GPU も行けたりするんかもしれない。
おわりに
インタフェースが keras ぽく分かりやすいので、keras 触った事がある方であれば、すぐ触れると思います。
Discussion