TensorFlowのC#バインディングとして、検索するとTensorFlowSharpが見つかるが、更新が止まっているようで.NET Coreには対応していない(ビルドすれば可能そう)。
TensorFlow.NET
他のライブラリを探したところ、TensorFlow.NETというライブラリを見つけた。
最近でも活発にメンテされており、.NET Coreに対応しており、TensorFlowのバージョンも最新に対応している。
情報が少なく(日本語の情報は皆無)で、今後もメンテされ続けるか不安はあるが試してみた。
TensorFlow.NETの導入方法
.NET Coreのアプリに導入する方法を説明する。
プロジェクト作成
.NET Coreのコンソールプロジェクトを作成する。
mkdir example cd example dotnet new console
ライブラリ追加
プロジェクトにTensorFlow.NETを追加する。
dotnet add package TensorFlow.NET
CPUで実行する場合は、プロジェクトにSciSharp.TensorFlow.Redistを追加する。
dotnet add package SciSharp.TensorFlow.Redist
LinuxでGPUを使用する場合は、TensorFlow for Cのビルド済みバイナリが別途必要になる。
GPU版のビルド済みバイナリをダウンロードして、適当な場所に解凍する。
※CUDA 10.0、cuDNN 7.4以上が必要(参考)
wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-1.13.1.tar.gz tar xzf libtensorflow-gpu-linux-x86_64-1.13.1.tar.gz
/usr/local/lib/にコピーして、キャッシュを更新する。
cp lib/libtensorflow_framework.so /usr/local/lib/ cp lib/libtensorflow.so /usr/local/lib/ ldconfig
WindowsでGPUで実行する場合は、プロジェクトにSciSharp.TensorFlow.Redist-Windows-GPUを追加する。
dotnet add package SciSharp.TensorFlow.Redist-Windows-GPU
サンプルコード
以下のように3層パーセプトロンで2値分類するコードを試した。
using System; using NumSharp; using Tensorflow; using static Tensorflow.Binding; namespace tensorflowcs { class Program { static Tensor x, y; static Tensor loss; static Operation optimizer; static private Tensor fc_layer(Tensor x, int num_units, string name, bool use_relu = true) { var in_dim = x.shape[1]; var initer = tf.truncated_normal_initializer(stddev: 0.01f); var W = tf.get_variable("W_" + name, dtype: tf.float32, shape: (in_dim, num_units), initializer: initer); var initial = tf.constant(0f, num_units); var b = tf.get_variable("b_" + name, dtype: tf.float32, initializer: initial); var layer = tf.matmul(x, W) + b; if (use_relu) layer = tf.nn.relu(layer); return layer; } static private Tensor sigmoid_cross_entropy_with_logits(Tensor labels, Tensor logits) { return tf.add(logits - logits * labels, tf.log1p(tf.exp(-logits))); } static private void BuildGraph() { var graph = new Graph().as_default(); x = tf.placeholder(tf.float32, shape: (-1, 1), name: "X"); y = tf.placeholder(tf.float32, shape: (-1, 1), name: "Y"); var fc1 = fc_layer(x, 3, "FC1", use_relu: true); var output_logits = fc_layer(fc1, 1, "OUT", use_relu: false); var entropy = sigmoid_cross_entropy_with_logits(labels: tf.reshape(y, new[] { -1 }), logits: tf.reshape(output_logits, new[] { -1 })); loss = tf.reduce_mean(entropy, name: "loss"); optimizer = tf.train.AdamOptimizer(learning_rate: 0.1f).minimize(loss); } static void Main(string[] args) { BuildGraph(); using (var sess = tf.Session()) { Train(sess); } } static private void Train(Session sess) { var init = tf.global_variables_initializer(); sess.run(init); var x_train = np.array(new float[4, 1] { {0.1f}, {0.2f}, {0.8f}, {0.9f} }, dtype: np.float32); var y_train = np.array(new float[4, 1] { {1}, {1}, {0}, {0} }, dtype: np.float32); foreach (var iteration in range(100)) { var result = sess.run((optimizer, loss), (x, x_train), (y, y_train)); float loss_val = result.Item2; print($"iter {iteration.ToString("000")}: Loss={loss_val.ToString("0.0000")}"); } } } }
実行結果
iter 000: Loss=0.6931 iter 001: Loss=0.6921 iter 002: Loss=0.6869 ... iter 097: Loss=0.0038 iter 098: Loss=0.0038 iter 099: Loss=0.0037
Pythonで同じ処理をした場合と同じ結果になることが確認できた。
tf.nn.sigmoid_cross_entropy_with_logitsは未実装のようで、動かなかったため、独自に実装した。
まだ、ライブラリは実装されていない部分が残っているようだ。