TadaoYamaokaの日記

山岡忠夫Homeで公開しているプログラムの開発ネタを中心に書いていきます。

C#からPythonを呼び出す

以前にC#からPythonディープラーニングフレームワークを呼び出すいくつかの方法を記事にした。

最後のlibTorchは、Pythonではないが、速度を出したい場合には選択肢になる。
しかし、モデルの定義をC++で実装する必要があり、メンテナンス性を考慮するとPythonからPyTorchを利用したい。

上記の他の方法は、機能が不足していたり、プロセス間通信のオーバーヘッドが大きすぎて速度がでなかったりで難がある。


最近、ハースストーンのAIの論文を読んでいたら、PythonNet(Python for .NET)で、C#からPythonを呼び出せることを知った。
てっきりPythonから.Netを利用するためだけのライブラリかと思ってスルーしていた。
公式ページの説明の下の方の「Embedding Python」を読めば、.NetからPythonを利用する方法について書かれていた。

メリット

PythonNetは、.NetによるPython実装ではなく、インストールされているPythonそのものが.Netのプロセスで実行される。
そのため、インストールされているPythonで動くものはすべて動くので互換性や機能不足で問題が起きない。

また、プロセス間通信がないため、速度面のボトルネックが少ない。
データのマーシャリングを自動で行ってくれるため、C#の変数をそのままPythonに渡せて、Pythonから結果をC#のオブジェクトとして受け取ることができる。

ということで、とても扱いやすいライブラリであることが分かった。


いろいろ試してみて、WindowsでもLinuxでも.Net Core 2.1から利用することができたので、インストール方法と使い方を記しておく。

インストール方法は、公式のWikiに記載があるが記述が少なくわかりにくかった。

Python環境構築

Python環境はすでに利用しているものが利用できる。
対応しているPythonのバージョンは、3.7なので3.8の場合は新たにPython環境の構築が必要になる。

Anacondaの場合は、アーカイブからAnaconda3-2019.10をインストールする。

PythonNetインストール

Windowsの場合

Windowsの場合、ビルド済みパッケージが利用できる。

.Net CoreのC#プロジェクトのディレクトリで、

dotnet add package Python.Runtime.NETStandard

を実行するだけでよい。

環境変数PATHにPythonをインストールしたディレクトリ(python37.dllのあるディレクトリ)を追加する。
Anaconda3のインストール時にPATHを設定している場合は不要。

Linuxの場合

Linuxの場合、ビルドが必要になる。
GitHubからリポジトリをクローンする。

git clone https://github.com/pythonnet/pythonnet

clangが必要なため、インストールされてない場合はインストールする。

sudo apt install clang

setup.pyでwheelを作成するとビルドされる。

python setup.py bdist_wheel --xplat

※「--xplat」オプションは.Net Coreから利用する場合に必要

Pythonから.Netを利用する場合は、ビルドされたwheelをインストールするが、.NetからPythonを利用する場合は必要ない。

build/lib.linux-x86_64-3.7/netcoreapp2.0
にビルドされたPython.Runtime.dllを使用する。

Python.Runtime.dllをC#プロジェクトのディレクトリにコピーして、.csprojに以下を追記する。

  <ItemGroup>
          <Reference Include="Python.Runtime"><HintPath>Python.Runtime.dll</HintPath></Reference>
  </ItemGroup>

環境変数LD_LIBRARY_PATHを設定する。

export LD_LIBRARY_PATH=/home/xxx/anaconda3/lib:$LD_LIBRARY_PATH

Pythonのインストール場所に合わせる。

サンプルコード

以下は、Python環境にインストールされたPyTorchをインポートしてバージョンを表示するC#コードの例である。
WindowsでもLinuxsでも同じ。

using System;
using System.IO;
using Python.Runtime;

namespace pythonnettest
{
    class Program
    {
        static void Main(string[] args)
        {
            var PYTHON_HOME = Environment.ExpandEnvironmentVariables(@"C:\Anaconda3");
            PythonEngine.PythonHome = PYTHON_HOME;

            using (Py.GIL())
            {
                dynamic torch = Py.Import("torch");
                dynamic version = torch.__version__;
                Console.WriteLine(version);
            }
        }
    }
}

PYTHON_HOMEは、Pythonをインストールしたディレクトリに合わせる。

実行結果:

1.3.1

まとめ

PythonNetを使うと、C#からPython経由でディープラーニングフレームワークが簡単に利用できる。
インストールしているPython環境がそのまま利用でき、プロセス間通信のボトルネックがないので、これ以上よい方法はないのではないかと思う。

今回.Net Coreから利用したが、.Net Frameworkやmonoにも対応しているので、試していないがUnityから利用とかも可能だと思う。