🪁

PyTorchを使ったHoudiniノードを作る <C++/HDK>

7 min read

はじめに

PyTorchを使ったHoudiniノードの作成方法に関しては試したことがなかったので、それをテストしたので、そのまとめを書いておく。

開発環境

Windows 10
Houdini 18.5.460
PyTorch 1.7.1 CUDA 11
Visual Studio 2017

サンプルコード

https://github.com/takavfx/MLHoudiniSamples/tree/master/ML0002_PyTorchPlugin

準備

LibTorchをダウンロード

HoudiniのノードとしてC++での実装をするにあたって、LibTorch(PyTorch C++ Distribution)を使用する。

PyTorchのサイトにて、適切なバージョンをダウンロード。
そして任意のディレクトリに展開して配置しておく。

ここで注意すべきなのが、LibTorchはRlease/Debugビルド用のライブラリがそれぞれ別々になっている(NOT ABI-compatible)。なので、ビルドソリューションによって切り替える事。

CMake

基本的なCMakeは次のファイルで行う。

CMakeLists.txt
cmake_minimum_required( VERSION 3.6 )

project( PyTorchTest )

# CMAKE_PREFIX_PATH は、Houdiniインストールパスの
# toolkit/cmakeのサブディレクトリパスを含める。
# 詳しくは、HDKドキュメントの"Compiling with CMake"セクションを参照。
list( APPEND CMAKE_PREFIX_PATH "$ENV{HFS}/toolkit/cmake" )

# HoudiniライブラリとLibTorchライブラリのヘッダーファイルがある場所を指定。
# そして、インポートしたライブラリをHoudiniとTorchとしてターゲットを命名。
find_package( Houdini REQUIRED )
find_package( Torch REQUIRED )

set( library_name SOP_PyTorchTest )
# LibTorch用にフラッグを受け取れる様に追記。
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}" )

# 埋め込み型DSファイルをSOP_PyTorchTest.Cの中に生成.
# この関数は、HoudiniConifg.cmake内に定義。
houdini_generate_proto_headers( FILES SOP_PyTorchTest.C )

# ソースファイルを追加。
add_library( ${library_name} SHARED
    SOP_PyTorchTest.C
    SOP_PyTorchTest.h
)

# HoudiniライブラリとLibTorchへのライブラリをライブラリネームとしてリンク。
# また、必要インクルードディレクトリとコンパイル定義を追加。
target_link_libraries( ${library_name} Houdini "${TORCH_LIBRARIES}")

set_property(TARGET ${library_name} PROPERTY CXX_STANDARD 14)

# Include ${CMAKE_CURRENT_BINARY_DIR} for the generated header.
target_include_directories( ${library_name} PRIVATE
    ${CMAKE_CURRENT_BINARY_DIR}
)

# ライブラリの出力ディレクトリ(デフォルトでは、
# `C:\Users\<username>\Documents\houdini<version>\dso`)など、
# いくつかの共通ターゲットプロパティを設定。
# この関数は、HoudiniConifg.cmake内に定義。
houdini_configure_target( ${library_name} )

# Visual Studioでのビルドの場合に有効
# ポストビルドコマンドとして、リンクされたファイルを
# ビルドターゲットディレクトリにコピーする。
# コピー前にはbinディレクトリを追加し、LibTorch関連はそちらにコピー
if (MSVC)
    set(binary_dir $<TARGET_FILE_DIR:${library_name}>/../bin)
    add_custom_command(TARGET ${library_name}
                        COMMAND ${CMAKE_COMMAND} -E make_directory
                        ${binary_dir})
    
    file(GLOB TORCH_DLLS "${TORCH_INSTALL_PREFIX}/lib/*.dll")
    add_custom_command(TARGET ${library_name}
                        POST_BUILD
                        COMMAND ${CMAKE_COMMAND} -E copy_if_different
                        ${TORCH_DLLS}
                        ${binary_dir})
endif (MSVC)

テスト用のコード編集

今回は、SOP_Starのサンプルソースをもとにテストした。
テストするにあたって、次の項目を書き換えた。

SOP_PyTorchTest.C
#include <torch/torch.h>

// ~ 省略 ~

// ノードがクックされた際の処理
void
SOP_PyTorchTestVerb::cook(const SOP_NodeVerb::CookParms &cookparms) const
{
    // ~ 省略 ~
    torch::Tensor tensor = torch::rand({2, 3});
    std::cout << tensor << std::endl;
    // ~ 省略 ~
}

ビルド

Command Line Toolsを起動して、作業ディレクトリに移動する。
Command Line Toolsを用いなくてもいいが、各種環境設定などなどが面倒なので、おとなしくそれを使った方が吉。

mkdir build
cd build
cmake .. -G "Visual Studio 15 2017 Win64" -DCMAKE_PREFIX_PATH=\path\to\torchlib

そしてビルド。

cmake --buld . --config Release

この時点でエラーが出る場合、以下のトラブルシュートの欄を参照。
場合によっては、Visual Studioを起動してから、各種問題を解決してからビルドすると良い。

Houdini上でテスト

始めにbinディレクトリに対し、set PATH=\path\to\bin(デフォルトでは、C:\Users\<username>\Documents\houdini18.5以下)と環境変数をセットする。

Houdiniを起動し、/obj/geo1と作成し、ノードを作成してみる。
次の様に出力されればビルド成功。

トラブルシュート

以下、ビルドしていた際に起きたトラブルで対処したもの。

C2061: Syntax Error: identifier 'IValue'

原因

libtorch\include\ATen/core/ivalue.hの中のIValueの前の行にあるDoxygen用の部分がうまく機能しておらず、それがVisual Studioのコンパイラに引っかかっている。

https://github.com/pytorch/pytorch/issues/24308#issuecomment-578306016

解決方法

エラーが起きてる以下の箇所を削除。
使う側としては、Doxygenでドキュメント化するわけでもないので、削除対応を選択した。

/// \cond DOXYGEN_CANNOT_HANDLE_CONSTRUCTORS_WITH_MACROS_SO_EXCLUDE_THIS_LINE_FROM_DOXYGEN
C10_DEPRECATED_MESSAGE("IValues based on std::unordered_map<K, V> are slow and deprecated. Please use c10::Dict<K, V> instead.")
/// \endcond

原因

ビルドソリューション用のライブラリを選択ミスしている。
このエラーの場合は、Releaseビルド用のライブラリをダウンロードしてきて参照しているが、このtorch-NOTFOUND.objファイルが含まれるのはDebugビルド用のもの。

https://github.com/pytorch/pytorch/issues/24305

解決方法

適切(Releaseビルド用ライブラリ)なビルド用ライブラリをダウンロードしてきて使用する。


C2872: "std":ambiguous symbolエラー (Release)

原因

Conformance modeYesになっている。
Visual Studio 2017以降で追加されたもので、非標準のC++構文は認めないというものとのこと。LibTorch側がこれに引っかかるので、ここはどうしようもないので切り替える事で対応。強制的にNoにする方法ない…??

https://github.com/pytorch/pytorch/issues/18607

解決方法

Conformance modeNoに設定する。


C2872: "std":ambiguous symbolエラー (Debug)

Debugでビルドしようとすると、上記解決策を試してみても修正されずにエラーが出続ける状態が続いく。

原因

各コード中のstdが指定ビルドバージョンの構文に会っていないため。

解決方法

stdとなっているところをすべて::stdと書き換える。


LibTorchのdllが、Houdiniのバージョン認識にひっかかる

デフォルトの起動だとエラーも何も出力されず、ノードをTab検索しても出てこなかったりして、この問題に気づかないことが多い。そういった原因がわからない場合、とりあえず、set HOUIDNI_DSO_ERROR=1と環境変数をセットして、DSO関連のエラーをすべて表示するようにする。

具体的には次のエラーが起きていた。

原因

dsoディレクトリ以下には、HoudiniがHoudiniのプラグインとして認識するもののみを置くべきで、そこにサードパーティのものがあるがゆえに、Houdiniのバージョン識別の処理が走った際に、その情報が取得できずに弾かれるという問題。

解決方法

サードパーティのdll等はdsoディレクトリ以下に置かず、binなどをのディレクトリを別途つくってそこに配置。set PATH=\path\to\binとして設定することで解決できる。

こちらは、CMake側で、ディレクトリを作成。そちらにLibTorch関連のバイナリをコピーする仕組みにすることで対応。


binディレクトリが作成できない

LibTorch関連のファイルを独自のbinディレクトリにコピーしたいが、binディレクトリが存在しないとエラーで止まる。

原因

存在しないディレクトリにコピー処理をしようとしても、ディレクトリ自体を作る機能がコピーコマンドに存在しないので、予め作る必要がある。

解決方法

ビルド時のコピー前にフォルダを作る処理を入れる。

CMakeLists.txt
if (MSVC)
    set(binary_dir $<TARGET_FILE_DIR:${library_name}>/../bin)
    add_custom_command(TARGET ${library_name}
                        COMMAND ${CMAKE_COMMAND} -E make_directory
                        ${binary_dir})
    
    file(GLOB TORCH_DLLS "${TORCH_INSTALL_PREFIX}/lib/*.dll")
    add_custom_command(TARGET ${library_name}
                        POST_BUILD
                        COMMAND ${CMAKE_COMMAND} -E copy_if_different
                        ${TORCH_DLLS}
                        ${binary_dir})
endif (MSVC)

最後に

こうして、とりあえず外部ライブラリのLibTorchを取り込むことができるので、これをベースに色々HoudiniのPyTorchを使った機械学習ノードを作っていけると思う。

Discussion

ログインするとコメントできます