このプロジェクトはまず行列型(Matrix)を実装し、そこから層(Layer)・ニューラルネットワーク(NeuralNetwork)を構築していきます。行列演算では高速化のためにOpenBLASを利用するオプションを提供しますが、OpenBLASの利用条件については「OpenBLAS の利用ポリシー」を参照してください。
この方針により、ビルド環境に依存せず安全にフォールバック実装へ切り替えられるようにします。
- 教育・研究用途向けの分かりやすいC++実装。
- 行列演算はOpenBLASを利用可能(オプション)で、無い場合は簡易実装でフォールバック。
- レイヤーや損失関数、最適化(簡易SGD等)を段階的に実装。
- Matrix 型(行列演算、転置、要素ごとの演算、行列積など)
- OpenBLAS を利用するオプション(条件を満たす場合のみ有効)
- OpenBLAS がない環境向けの可読なフォールバック実装
下記はこのREADMEが想定する一般的な開発環境です。プロジェクトに既にビルドシステム(CMake 等)があることを前提としています。
- C++20 以上
- CMake 3.21 以上(
CMakePresets.jsonの要件) - (任意)OpenBLAS(性能を出したい場合) CMakeListsでは初期値でwindows環境かつvcpkgフォルダがCドライブ直下にあり、OPENBLASがインストール済みであることを想定しています。
OpenBLAS を利用する場合、本リポジトリでは次の条件を満たす必要があります:
- CMake オプション
USE_OPENBLASが有効化されていること。 find_package(OpenBLAS REQUIRED)が成功すること。- ビルド時に
USE_OPENBLASマクロが定義されること。
上記3つがすべて満たされる場合に限り、Matrix クラス内部で BLAS 呼び出しを行い、行列積や他の重い演算を OpenBLAS に委譲します。USE_OPENBLAS=ON かつ OpenBLAS が見つからない場合は CMake 構成エラーになります。フォールバック実装を使う場合は USE_OPENBLAS=OFF(例: debug-no-openblas-clang / debug-no-openblas-vs などの no-openblas 系プリセット)でビルドしてください。
この設計により、開発中や CI 環境で OpenBLAS が無くても安全にビルド・テストが可能です。
- BLAS バックエンドは CMake オプション
USE_OPENBLAS/USE_CUBLAS/USE_CLBLASTで切り替えます。 USE_OPENBLAS=ONの場合はfind_package(OpenBLAS REQUIRED)、USE_CUBLAS=ONの場合はfind_package(CUDAToolkit REQUIRED)、USE_CLBLAST=ONの場合はfind_package(OpenCL REQUIRED)とfind_package(CLBlast REQUIRED)が通る必要があります。USE_CUBLASとUSE_CLBLASTを同時に有効化した場合はUSE_CUBLASが優先され、USE_OPENBLASと GPU 系(USE_CUBLAS/USE_CLBLAST)を同時に有効化した場合は GPU 系が優先されます(詳細は CMakeLists.txt を参照)。- Windows で OpenBLAS / CLBlast を使う場合、既定プリセットは
C:/vcpkg/scripts/buildsystems/vcpkg.cmakeを参照します。環境が異なる場合は CMakePresets.json のCMAKE_TOOLCHAIN_FILEを変更してください。
- ビルドする際は OpenBLAS、CuBLAS、CLBlast を有効化・無効化したい場合に応じて CMake プリセットを使い分けます。
- 最適化を有効にする場合は
release-openblas-gccなどのプリセットを使用してください。 - Visual Studio でのビルドでは、{
debug-openblas-vs,debug-cublas-vs,debug-clblast-vs} とdebug-no-blas-vsのプリセットを用意しています。 - clang を使用する場合は、{
debug-openblas-clang,debug-cublas-clang,debug-clblast-clang} とdebug-no-blas-clangのプリセットを用意しています。 - gcc を使用する場合は、{
debug-openblas-gcc,debug-cublas-gcc} とdebug-no-blas-gccのプリセットを用意しています。
# Windows 例(PowerShell)
# OpenBLAS 有効化
cmake --preset=debug-openblas-clang
cmake --build --preset=debug-openblas-clang
# CuBLAS 有効化
cmake --preset=debug-cublas-clang
cmake --build --preset=debug-cublas-clang
# CLBlast 有効化
cmake --preset=debug-clblast-clang
cmake --build --preset=debug-clblast-clang
# BLAS 無効化
cmake --preset=debug-no-blas-clang
cmake --build --preset=debug-no-blas-clang
#include "include/matrix/matrix"
#include "include/neuralnetwork/neuralnetwork.hpp"
#include "include/neuralnetwork/layers/affine.hpp"
#include "include/neuralnetwork/layers/relu.hpp"
#include "include/neuralnetwork/layers/softmaxwithloss.hpp"
int main(){
// レイヤ構成を定義 (Affine -> ReLU -> Affine -> SoftmaxWithLoss)
// * 4層以上で最終レイヤは 損失関数 であり、かつ偶数層である必要があります。
using MyLayers = LayerPack<
Affine<float>,
ReLU<float>,
Affine<float>,
SoftmaxWithLoss<float>
>;
// 高速化バージョン
using MyLayersFaster = LayerPack<
Affine<float, true>, // OpenBLASを使用するAffineレイヤー、デフォルト(std::execution::sequenced_policy)で実行
ReLU<float>,
Affine<float, true, std::execution::parallel_policy>, // OpenBLASを使用し、並列実行ポリシーを指定したAffineレイヤー
SoftmaxWithLoss<float>
>;
// 初期化戦略
using MyLayersHe = LayerPack<
Affine<float, true, std::execution::sequenced_policy, He>, // OpenBLASを使用し、He初期化を指定したAffineレイヤー
ReLU<float>,
Affine<float, true, std::execution::sequenced_policy, He>, // OpenBLASを使用し、He初期化を指定したAffineレイヤー
SoftmaxWithLoss<float>
>;
// オプティマイザにAdamを使用(デフォルトではSGDが使用される)
using MyLayersAdam = LayerPack<
Affine<float, true, std::execution::sequenced_policy, Xavier, Adam<float>>,
ReLU<float>,
Affine<float, true, std::execution::sequenced_policy, Xavier,
Adam<float, true, std::execution::parallel_policy> // OpenBLASを使用し、並列実行ポリシーを指定したAdamオプティマイザを使用するAffineレイヤー
>,
SoftmaxWithLoss<float>
>;
NeuralNetwork<float, MyLayers> nn(2,4,2,0.1f); // 入力サイズ2、隠れ層サイズ4、出力サイズ2、学習率0.1
// ダミーデータ バッチサイズ2、特徴量2、クラス数2
Matrix<float> X = {{0.5f, 0.2f}, {0.9f, 0.7f}}; // 2サンプル、2特徴
Matrix<float> T = {{1.0f, 0.0f}, {0.0f, 1.0f}}; // 2サンプル、2クラスの正解ラベル(one-hot)
double loss = nn.learn<true>(X, T); // 学習実行 (損失計算あり)
nn.learn<false>(X, T); // 学習実行 (損失計算なし)
std::cout << "prediction:\n" << nn.predict(X) << std::endl;
return 0;
}-
Matrix
- コンストラクタ(サイズ指定・初期値コンテナ指定・初期化関数指定・2次元コンテナ/イニシャライザリスト指定)
- 要素アクセス:
operator(),operator[] - ビュー取得:
get_row(),get_col()(参照のみ、const 版対応済み) - ポインタ取得:
get_row_ptr(),get_col_ptr()(レイアウト制約あり) - レイアウト/転置:
convertLayout(),transpose(),transpose_copy() - 関数適用:
apply(),apply_copy(),apply_row(),apply_row_copy() - 四則/要素演算:
add(),sub(),scalar_mul(),scalar_div(),hadamard_mul(),hadamard_div() - 行列積:
matrix_mul() - 集計:
sum_rows() - 補助:
rows(),cols(),data(),is_blas_enabled()
-
Layers
-
Affine
- 初期化スケール戦略:
StandardDeviation(抽象基底),Xavier,He - クラステンプレート:
Affine<ty, use_blas, ExecType, DeviationType, OptimizerType>ty: 要素型use_blas: 行列積でBLASを利用するかどうかExecType: 実行ポリシー(既定:std::execution::sequenced_policy)DeviationType: 重み初期化の標準偏差戦略(既定:Xavier)OptimizerType: 最適化器(既定:SGD<ty, use_blas, ExecType>)
- コンストラクタ:
Affine(size_t input_size, size_t output_size, ty lr = 0.01f, uint32_t seed = std::random_device{}(), DeviationType dev = DeviationType{})_w(in_dim, out_dim),_b(1, out_dim) を正規分布で初期化optimizer(_w, _b, lr)を内部で保持
forward(const Matrix<ty>& in) -> Matrix<ty>out = in * W + bを計算(matrix_mul+apply_row)
backward(const Matrix<ty>& dout) -> Matrix<ty>dx = dout * W^TdW = X^T * doutdb = sum_rows(dout)optimizer.optimize(dW, db)を実行
- 初期化スケール戦略:
-
ReLU
- クラステンプレート:
ReLU<ty, ExecPolicy>ty: 要素型ExecPolicy: 実行ポリシー(既定:std::execution::sequenced_policy)
forward(const Matrix<ty>& in) -> Matrix<ty>- 要素ごとに
max(0, x)を適用 - 逆伝播用に出力を内部保持
- 要素ごとに
backward(const Matrix<ty>& dout) -> Matrix<ty>- 保持した出力からマスク
(x > 0 ? 1 : 0)を生成 doutと要素積(Hadamard積)を取り、入力勾配dxを返す
- 保持した出力からマスク
- クラステンプレート:
-
Sigmoid
- クラステンプレート:
Sigmoid<ty, ExecPolicy>ty: 要素型ExecPolicy: 実行ポリシー(既定:std::execution::sequenced_policy)
forward(const Matrix<ty>& in) -> Matrix<ty>- 要素ごとに
1 / (1 + exp(-x))を適用 - 逆伝播用に出力を内部保持
- 要素ごとに
backward(const Matrix<ty>& dout) -> Matrix<ty>- 保持した出力から勾配を計算:
dx = dout * (out * (1 - out))
- 保持した出力から勾配を計算:
- クラステンプレート:
-
Tanh
- クラステンプレート:
Tanh<ty, ExecPolicy>ty: 要素型ExecPolicy: 実行ポリシー(既定:std::execution::sequenced_policy)
forward(const Matrix<ty>& in) -> Matrix<ty>- 要素ごとに
tanh(x)を適用 - 逆伝播用に出力を内部保持
- 要素ごとに
backward(const Matrix<ty>& dout) -> Matrix<ty>- 保持した出力から勾配を計算:
dx = dout * (1 - out^2)
- 保持した出力から勾配を計算:
- クラステンプレート:
-
IdentityWithLoss
- クラステンプレート:
IdentityWithLoss<ty, ExecPolicy>ty: 要素型ExecPolicy: 実行ポリシー(既定:std::execution::sequenced_policy)
- 静的フラグ:
has_loss = true forward(const Matrix<ty>& in) -> Matrix<ty>- 恒等写像として入力をそのまま出力
- 損失計算/逆伝播用に出力を内部保持
backward(const Matrix<ty>& t) -> Matrix<ty>dx = (out - t) / batch_sizeを計算batch_size == 0の場合は例外を送出
loss(const Matrix<ty>& t) -> double- 二乗誤差:
Σ(out_i - t_i)^2 / (2 * batch_size) batch_size == 0の場合は例外を送出
- 二乗誤差:
- クラステンプレート:
-
SoftmaxWithLoss
- クラステンプレート:
SoftmaxWithLoss<ty, ExecPolicy>ty: 要素型ExecPolicy: 実行ポリシー(既定:std::execution::sequenced_policy)
- 静的フラグ:
has_loss = true forward(const Matrix<ty>& in) -> Matrix<ty>- 行ごとに softmax を適用
- 数値安定化のため各行の最大値を減算してから
expを計算 - 入力が空、または正規化和が非正の場合は例外を送出
- 損失計算/逆伝播用に出力を内部保持
backward(const Matrix<ty>& t) -> Matrix<ty>dx = (out - t) / batch_sizeを計算batch_size == 0の場合は例外を送出
loss(const Matrix<ty>& t) -> double- 交差エントロピー:
-Σ(t_i * log(out_i + ε)) / batch_size batch_size == 0の場合は例外を送出
- 交差エントロピー:
- クラステンプレート:
-
-
NeuralNetwork
- レイヤ構成型:
LayerPack<Layers...>- 制約: レイヤ数は偶数かつ4以上
- 制約: 最終レイヤは
has_loss == trueを持つ型
- クラステンプレート:
NeuralNetwork<ty, LayerPackT>- 実体化:
NeuralNetwork<ty, LayerPack<Layers...>>
- 実体化:
- コンストラクタ:
NeuralNetwork(size_t in_size, size_t hidden_size, size_t out_size, ty learning_rate = 0.01f, uint32_t seed = std::random_device{}())- 先頭/末尾は
Affine前提でサイズを設定 - 中間層は
Affineの場合hidden_size -> hidden_size、それ以外はデフォルト構築
- 先頭/末尾は
learn<use_loss = true>(const Matrix<ty>& in, const Matrix<ty>& t) -> double- 全レイヤで順伝播を実行後、逆順で逆伝播を実行
use_loss == trueの場合は最終レイヤのloss(t)を返すuse_loss == falseの場合は0を返す
predict(const Matrix<ty>& in) -> Matrix<ty>- 学習なしの順伝播のみを実行して推論結果を返す
- レイヤ構成型:
このプロジェクトは MIT ライセンスの下で公開されています。 LICENSE ファイルを参照してください。