Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hasktorchで学ぶ関数型ディープラーニング__型安全なニューラルネットワークと...

Avatar for junji hashimoto junji hashimoto
June 13, 2025
280

 Hasktorchで学ぶ関数型ディープラーニング__型安全なニューラルネットワークとその実践.pdf

Avatar for junji hashimoto

junji hashimoto

June 13, 2025
Tweet

Transcript

  1. 自己紹介 • Junji Hashimoto • グリー、12年 • インフラエンジニア ◦ シニアリードエンジニア

    ◦ KVS Unitのチームリーダー • 業務 ◦ KVSの運用、パフォーマンス分析(Memcached, PHP, Datadog APM, eBPF) ◦ Web3のバリデータ運用(DB=LevelDB、運用自動化=LLM) • 社外活動 ◦ Hasktorch(Haskel)やgpu.cpp(WebGPU)の機械学習のフレームワーク開発 ◦ AIの安全性の分析と修正(画像系) 2
  2. 目次 • Hasktorchの概要 ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化とFFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 3
  3. 目次 • Hasktorchの概要 ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化とFFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 4
  4. Hasktorchの概要 • Hasktorch(2017〜) ◦ Pytorchと同じ基盤(libtorch)を使ったHaskellのライブラリ ◦ CPUやGPUをサポートした自動微分・ディープラーニング ◦ テンソルが計算の中心 •

    プロジェクトの目的 ◦ 関数型によるディープラーニング ▪ 状態を持たないで、入力で出力が決定 ◦ コンパイル時にテンソルの計算に関連するシェイプやデバイスの型のエラーを検知 ◦ 使いやすく実用的なライブラリ • 開発者 ◦ Hasktorch Team ◦ Austin Huang ◦ Sam Stites ◦ Torsten Scholak ◦ Adam Paszke ◦ Junji Hashimoto ◦ など 5
  5. Hasktorchを構成しているパッケージ • libtorch(C++) ◦ pytorchのC++バックエンド • libtorch-ffi ◦ pytorchのバックエンドであるlibtorchをhaskellから使うためのライブラリ ◦

    リソース管理しているレイヤー • hasktorch ◦ テンソルのシェイプについて型あり( Typed)、型なし(Untyped)の両方のライブラリを提供 ◦ 自動微分に関して、状態に依存しないライブラリ 6
  6. 目次 • Hasktorchの概要 ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化とFFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 7
  7. 最適化(Untyped) 12 class Optimizer optimizer where runStep :: Parameterized model

    => model -> optimizer -> Loss -> LearningRate -> IO (model, optimizer) 入力:モデルと最適化のパラメータとロス 出力:更新したモデルと最適化のパラメータ
  8. 目次 • Hasktorchの概要 ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化とFFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 19
  9. シェイプの検証 (Typed):依存型 • テンソルのシェイプに利用 ◦ DataKinds拡張で型に値を保持 ▪ [Nat]とか。 ▪ Nat

    は Natural (Word) ▪ Nat はペアノ数(data Peano = Zero | Succ Peano)ではないが。 24
  10. シェイプの検証 (Typed):依存型 • ghc-typelits-natnormaliseプラグイン ◦ Natレベルの式と式の等価性 ◦ 例:(x + 2)^(y

    + 2) == 4*x*(2 + x)^y + 4*(2 + x)^y + (2 + x)^y*x^2 ◦ ghc-typelits-natnormaliseプラグインを使って解決。 28
  11. シェイプの検証 (Typed):依存型 • typelevel-rewrite-rulesプラグイン ◦ 条件レベルの等価性 ◦ a or (

    a or b) == a or bだが、GHCは簡略化してくれないので、推論できない。 29
  12. Hasktorchの主要なメンバー • 自動微分を使いやすくするためのライブラリ ◦ 2つのレイヤーを提供 ▪ Torch : シェイプの型なし ▪

    Torch.Typed: シェイプの型あり • データ ◦ Tensor : テンソルのデータ ◦ IndependentTensor / Parameter : 自動微分するテンソルのデータ • 型クラス ◦ TensorLike : Tensorとスカラ(例:Float)やリスト(例:[Float])のデータとの相互変換 ◦ TensorIndex : スライス型 ◦ Optimizer : ロスの値を元に微分を計算 ◦ Randomizable : データの初期化(シェイプを決定) ◦ Parameterized : データを自動微分可能にするもの。( Genericsで自動生成) ◦ HasTypes : データのCPU/GPUデバイスの切り替え、DTypeの変更をサポートするLens ◦ HasForward : データと推論の関数を紐付けるもの。 ◦ Castable: 型と値の変換のサポート 30
  13. Hasktorchの概要とエコシステム(まとめ) • Hasktorch ◦ 関数型の特徴(値が変わらない) ◦ 自動微分をサポートしたディープラーニングのフレームワーク ◦ 純粋関数で計算を記述 ◦

    自動微分はgrad関数で計算 (backward関数がない) ▪ grad関数も純粋関数 ◦ API ▪ テンソルの型にシェイプがないAPI ▪ テンソルの型にシェイプがあるAPI • 依存型:GHCプラグイン ▪ HackageでAPIの検索 31
  14. Hasktorchの概要とエコシステム(関連リソース) • 関連プロジェクト ◦ hasktorch-yolo (テンソルの型なし ) ◦ Transformer (テンソルの型あり

    ) ◦ GPT2-Hasktorch (テンソルの型なし ) ◦ gpt2-haskell (テンソルの型あり ) ◦ BERT / RoBERTa / T5 (漸進的型付け) ◦ SpiderSQL (漸進的型付け) ◦ 様々なhasktorchに付随の例 • ドキュメント ◦ ホームページ ◦ Tutorial ◦ APIドキュメント ◦ Hasktorch-tutorial (Cmdv) ◦ Neural Networks(Bogdan Penkovsky) 32
  15. 目次 • Hasktorchの概要とエコシステム ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化と FFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 33
  16. Hasktorchのセットアップ • サポートしている環境 ◦ OS ▪ Ubuntu(Linux) ▪ NixOS(Linux) ▪

    Windows(WSL) ▪ MacOS ▪ Docker(linux amd64) ◦ ビルドツール ▪ stack ▪ cabal ▪ nix ◦ ghc(コンパイラ) ▪ 9.6と9.8 ◦ libtorch(ライブラリ) ▪ 2.3,2.4,2.5,2.7 ◦ 他 ▪ Jupyter Notebook(Nix, Docker) 34
  17. Hasktorchを構成しているパッケージ • libtorch(C++) ◦ pytorchのC++バックエンド ◦ セットアップでハマるところ • libtorch-ffi ◦

    pytorchのバックエンドであるlibtorchをhaskellから使うためのライブラリ ◦ リソース管理のレイヤー • hasktorch ◦ テンソルのシェイプについて型あり、型なしの両方のライブラリを提供 ◦ 自動微分に関して、状態に依存しないライブラリ 35
  18. HasktorchのCabalでの開発 1. ghcupでghcとcabalをインストール 2. hasktorchをgit clone 3. libtorchをインストール a. hasktorch/depsのget-deps.shを実行

    4. libtorchの共有ライブラリへのパスを設定 a. source setenvを実行 5. StackageのLTSのセットアップ a. ./setup-cabal.shを実行 b. cabal freezeファイルをセットアップ 6. ビルド a. cabal build allを実行 36
  19. HasktorchのStackでの開発 1. ghcupでstackをインストール。 2. hasktorchをgit cloneする。 3. libtorchをインストール a. hasktorch/depsのget-deps.shを実行

    4. libtorchの共有ライブラリへのパスを設定 a. source setenvを実行 5. ビルド a. stack build allを実行 37
  20. stackで新規プロジェクト開発 1. ghcupでstackをインストール 2. hasktorch-stack-skeletonをgit clone 3. libtorchのインストール a. makeを実行

    4. ビルド a. stack build allを実行 注意:大筋は大丈夫なはずだが、バージョンとかのアップデートが必要かも。 38
  21. Cabal(Nix)で新規プロジェクト開発 1. Nixをインストール 2. hasktorch-skeletonをgit clone 3. hasktorchのnixのキャッシュをセットアップ(option) a. nix-env

    -i cachix とかを実行してcachixをインストール b. cachix use hasktorchを実行 4. 開発のシェルに入る a. nix developを実行 5. ビルド a. cabal build allを実行 39
  22. Nixのインストール • curl -L https://nixos.org/nix/install | sh • . ~/.nix-profile/etc/profile.d/nix.sh

    • mkdir -p ~/.config/nix • echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf 40
  23. 目次 • Hasktorchの概要とエコシステム ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化と FFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 41
  24. Notebook(Nix)の起動手順 1. Nixをインストール 2. hasktorch-skeletonをgit clone 3. fullのブランチをcheckout 4. パッケージを追加(option)

    a. flake.nixのconfig.ihaskell.packagesに必要なパッケージを追加 5. hasktorchのnixのキャッシュをセットアップ( option) a. nix-env -i cachix とかを実行してcachixをインストール b. cachix use hasktorchを実行 6. 開発シェルの起動 a. nix developを実行 7. jupyter notebookの起動 a. ihaskell-notebookを実行 8. Haskell Kernelを選択。 43
  25. Notebook(Nix)の起動手順 :Nixのインストール • curl -L https://nixos.org/nix/install | sh • .

    ~/.nix-profile/etc/profile.d/nix.sh • mkdir -p ~/.config/nix • echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf 44
  26. Notebook(Nix)の起動手順 : hasktorch-skeletonのセットアップ 1. Nixをインストール 2. hasktorch-skeletonをgit clone 3. fullのブランチをcheckout

    4. 開発シェルの起動 a. nix developを実行 5. jupyter notebookの起動 a. ihaskell-notebookを実行 6. Haskell Kernelを選択。 7. ihaskell-hvegaでグラフを書く。 46
  27. Notebook(Nix)の起動手順 : hasktorch-skeletonのセットアップ 1. Nixをインストール 2. hasktorch-skeletonをgit clone 3. fullのブランチをcheckout

    4. 開発シェルの起動 a. nix developを実行 5. jupyter notebookの起動 a. ihaskell-notebookを実行 6. Haskell Kernelを選択。 7. ihaskell-hvegaでグラフを書く。 47
  28. 目次 • Hasktorchの概要とエコシステム ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化と FFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 48
  29. Hasktorchを構成しているパッケージ • libtorch(C++) ◦ pytorchのC++バックエンド • libtorch-ffi ◦ pytorchのバックエンドである libtorchをhaskellから使うためのライブラリ

    ◦ リソース管理しているレイヤー(パフォーマンスと関係するレイヤー) • hasktorch ◦ テンソルのシェイプについて型あり、型なしの両方のライブラリを提供 ◦ 自動微分に関して、状態に依存しないライブラリ 49 なぜこのアーキテクチャなのか? • GPU(CUDA)の高性能なカーネル関数の開発は大変 ◦ HaskellのGPUバックエンドの性能が上がらない。 ◦ AccelerateやFutharkといったコンパイラではユーザーが共有メモリが扱えない。 ◦ 逆伝搬のカーネルを書くのはもっと大変 • よくデバッグされている Pytorchのカーネルを利用
  30. libtorch-ffiパッケージの構成 • pytorchのバックエンド(libtorch)をhaskellから使うためのライブラリ • コード生成(codegenパッケージ) ◦ libtorchの提供する関数の一覧 (Declarations.yaml)のファイルからコード生成 ▪ yamlファイルからAST(関数の宣言)を生成

    ▪ 関数のパース:megaparsec ▪ コード生成:shakespeare ◦ C++のオーバーロードに対応した Haskellの関数を生成 ◦ IOベースにした関数を生成。( pureでない) • HaskellからC++の関数を呼び出し ◦ FFI(Foreign Function Interface) ▪ HaskellからCの関数を呼ぶ場合 • foreign import ccall "exp" c_exp :: Double -> Double – 宣言 ◦ Template Haskell ▪ inline-c / inline-c-cpp パッケージ (C++のコードをHaskellのコードに埋め込む) ◦ ForeignPtr ▪ C++のリソースをForeignPtrでラップ ▪ GCでC++のリソースを解放するために利用 ◦ Cast(Castable 型クラス) ▪ Raw pointerをForeignPtrにラップするために利用 50
  31. パフォーマンス最適化と FFIを用いたメモリ管理 • FFIはunsafe callを利用。(呼び出しのオーバーヘッド削減) • ForeignPtrはc finailizerを使ってdelete • データの読み出しには注意

    ◦ ForeignPtrのオブジェクト(destructor付き)をたくさん作るとGCが遅くなるので、時にはC++でアクセス。 • メモリ不足の時にはGCを起動 51
  32. FFIはunsafe callを利用 • FFI(Foreign Function Interface) ◦ HaskellからC/C++の関数を呼び出すインターフェース ◦ unsafe

    call ▪ C++の関数を呼び出し元の Haskellのスレッドで実行 ▪ 元のHaskellのスレッドはブロックされる。 ◦ safe call ▪ C++の関数を別のスレッドを作成し、実行 ▪ 元のHaskellのスレッドはブロックされない。 ▪ 別のスレッドを作成。作成に数十 usくらいかかる。 • 1GHzなら10us = 10000cycle • Hasktorchではunsafe callを利用 ◦ 数十us以下の処理時間なら、 unsafe call が速い 52 CPU CPU HEC HEC Thread Thread L L L L L Thread unsafe call ブロック L safe call 移動 ブロックなし
  33. ForeignPtrはc finailizer • ForeignPtr ◦ FFIで利用するメモリの管理用のポインタ ▪ destructorをつけることができる。 ▪ destructorはWeakPtrで管理。

    ▪ c finalizerとhaskell finalizerの二つが選択できる。 ◦ c finalizer ▪ c言語で書かれたdestructor ▪ GCの中で実行される。 ▪ すぐにオブジェクトが解放される。( GCが終わった時には解放されている。) ▪ ただし、GCをブロックしている。 ◦ haskell finalizer ▪ haskellで書かれたdestructor ▪ GCの処理が終わってから実行( GCが終わった時には解放されてない。) ▪ コンテキストスイッチが発生して遅い。 • GCのプロセスと別に処理する必要がある。 • hasktorchではc finalizerでオブジェクトを解放 53
  34. ForeignPtrはc finailizer • ForeignPtr ◦ FFIで利用するメモリの管理用のポインタ ▪ destructorをつけることができる。 ▪ destructorはWeakPtrで管理。

    ▪ c finalizerとhaskell finalizerの二つが選択できる。 ◦ c finalizer ▪ c言語で書かれたdestructor ▪ GCの中で実行される。 ▪ すぐにオブジェクトが解放される。( GCが終わった時には解放されている。) ▪ ただし、GCをブロックしている。 ◦ haskell finalizer ▪ haskellで書かれたdestructor ▪ GCの処理が終わってから実行( GCが終わった時には解放されてない。) ▪ コンテキストスイッチが発生して遅い。 • GCのプロセスと別に処理する必要がある。 • hasktorchではc finalizerでオブジェクトを解放 54
  35. データの読み出しには注意 • ForeignPtrのオブジェクト(destructor付き)をたくさん作るとGCが遅くなる。 ◦ ForeignPtrは頻繁に生成 ▪ 例 • a =

    ones’ [], b = 3 * a • ForeignPtrは3回作られる。 ◦ ForeignPtrでC++のオブジェクトをdelete ▪ GCで使用の有無を毎回全数チェック ▪ オブジェクトはWeak Pointerで管理 ◦ 不要なForeignPtrの削除は容易ではない。 ▪ LinearTypeはGCと関係がない。(参照がなくなるところで destructorを入れてくれたりしない。) ▪ GHC Pluginでの最適化なら技術的にできるが、最適化する ASTのパターンの定義がネストが深く て簡単ではない。 ◦ 小さなテンソルを結合して大きなテンソルを作成しない(対処療法) ▪ 例 • 画像を読み込む時はByteStringで読み出し、memcpyでテンソルのメモリにコピー 55
  36. メモリ不足の時には GCを起動 • GCを呼び出すタイミング ◦ FFIのC++のメモリ不足の例外発生時 ◦ hasktorchのOptimizer型クラスのrunStep関数 ▪ 1epochが終わるたびにGC

    • メモリのフラグメンテーション対応 ◦ glibcのmallocはフラグメンテーションを起こす。 ▪ rubyで顕著 ◦ malloc_trim関数をGCと一緒に呼び出し。 56
  37. 目次 • Hasktorchの概要とエコシステム ◦ Untyped API ▪ 基本的な使い方 ▪ 実際のニューラルネットワーク構築

    ◦ Typed API ▪ 基本的な使い方 ▪ 依存型 • Hasktorchのセットアップ ◦ CabalやStackでの開発 ◦ Jupyter NotebookでのHasktorch活用 • Hasktorchの内部 ◦ パッケージ構成 ◦ パフォーマンス最適化と FFIを用いたメモリ管理 • FAQ • Hasktorchの今後と活用の可能性 57
  38. よくある質問 • 欲しい関数がない。 ◦ パターン1:hasktorchパッケージにはないが、 libtorch-ffiにはある ▪ hasktorchの関数をみて、libtorch-ffiの関数をラップ ▪ 例

    • 平均を計算する関数の例 • mean t = unsafePerformIO $ cast1 Libtorch.mean_t t ◦ パターン2:hasktorchにもlibtorch-ffiにもない。 ▪ 自作してください 58
  39. よくある質問 • includeしているファイルがないと言われる。 ◦ 原因 ▪ libtorchがインストールされてない。 ▪ libtorchへのパスが設定されてない。 ▪

    libtorchのバージョンが違う。 ◦ 背景 ▪ haskellに精通しているからといってC++に精通しているわけではない。 ▪ 加えて、haskellでC++のライブラリの設定に精通しているわけではない。 ▪ ghcupしか知らないユーザーもいる。 ◦ 対策 ▪ nixを使う。(nixの設定方法、デバッグ、心理的安全性がないとかの問題がある。) ▪ homebrewやaptのパッケージでlibtorchを入れる。 • パッケージシステムに精通しているわけではない。 60
  40. よくある質問 • ビルドできたけどライブラリがないと言われる。 ◦ 原因 ▪ libtorchをパッケージ管理ソフトで入れてない。(グローバルに入れてない。) ▪ libtorchの共有ライブラリへの参照がない。 •

    LD_LIBRARAY_PATHやDYLD_LIBRARY_PATHの設定がない。 ◦ 背景 ▪ haskellに精通しているからといって共有ライブラリに精通しているわけではない。 ◦ 対策 ▪ nixを使う。(nixの設定方法、デバッグ、心理的安全性がないとかの問題がある。) ▪ homebrewやaptのパッケージでlibtorchを入れる。 • パッケージシステムに精通しているわけではない。 61
  41. よくある質問 • pytorchのライブラリにリンクするとエラーが出る。 ◦ 原因 ▪ C++のABIのフォーマットが違う ◦ 背景 ▪

    pytorchが互換性のために古いgccのABIを使っている ▪ haskellに精通しているからといって共有ライブラリに精通しているわけではない。 ◦ 対策 ▪ -D_GLIBCXX_USE_CXX11_ABI=0をつけてビルドする。 • https://github.com/hasktorch/hasktorch/blob/master/libtorch-ffi/libtorch-ffi.cabal#L171 62
  42. よくある質問 • Hasktorchに書き換えたけど遅い。GPUだともっと遅い。 ◦ 原因 ▪ バッチ処理してない。 ▪ 関数のディスパッチ処理が遅い。 ◦

    背景 ▪ テンソルでバッチ処理をしてない小さなデータの処理はディスパッチ処理などの呼び出しのオー バーヘッドの時間が無視できないので遅い。 ▪ 4つのFloatの足し算に1usくらいかかる。(おおよそ1000クロックかかる。) ◦ 対策 ▪ カーネル関数やスライスで一括して処理 ▪ C++でカーネル関数を書く。 ▪ 分析:linuxならperfでどこの関数が遅いのか確認 63
  43. まとめ • Hasktorch ◦ 関数型の特徴をもった自動微分をサポートしたディープラーニングのフレームワーク ◦ 純粋関数で計算を記述 ◦ 自動微分はgrad関数で計算 (backward関数がない)

    ◦ 使いやすく利便性を考慮して開発!! • 各種OS・ツールでの開発をサポート ◦ Linux、MacOS、Docker、Cabal、Stack、Nix • パッケージ構成 ◦ libtorch-ffi(FFI) • FAQ 64
  44. 今後 • 合成可能なモジュール ◦ SpecとDataと推論関数のそれぞれは合成可能だったが、モデルレベルでは合成可能でない ◦ 型なしのAPIでは構築済み ◦ 型ありのAPIで構築(ToDo) •

    FFIのFusion ◦ Hasktorchの計算はFFIを使っている。FFIの呼び出し回数を減らしたい。 ▪ 一時的なForeignPtrが生成され、GCに負荷をかけているが、それも緩和 ◦ GHC Pluginで構築(ToDo) • Cudaでカーネル関数を生成 ◦ inline-c-cppでhaskellコード上でC++を使ってカーネル関数を生成できるようにした。 ◦ c++でカーネルを書いてHasktorchから呼べるようにした。 ◦ inline-c-cudaを使って構築(ToDo) 65