モデル統合データセットバックモデル追加のライブラリに対応する

注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

追加のライブラリに対応する

Foundry の標準的な Model 機能が特定のユースケースに対応できない場合や、特定のクラスやライブラリがサポートされていない場合、Stage インターフェースに必要な関数をいくつか上書きしたり登録したりすることができます。例えば、Stage のカスタム transform 関数を作成し、Foundry にシリアライズすることができます。

サードパーティのライブラリをサポートしたい場合は、Stage の独自の実装を作成することができます。

例として、事前学習済みの spaCy モデルを活用する方法のチュートリアルを参照してください。

要件

これらは、共有 Transforms Python ライブラリに記述されている必要があり、Code Repository または Code Workbook 環境に追加することができます。インポートされると、カスタム Stage 実装は自動的に foundry_ml に統合されます。

カスタム実装オプションは以下を説明しています:

  • model.transform() が呼ばれたときにモデルが実行すべき操作。
  • モデルが Foundry でシリアライズされ、保存される方法。
  • モデルがデシリアライズされ、下流の変換で使用できるようになる方法。

Stage クラスはデシリアライズ時に利用可能である必要があるため、Python 環境内でモジュールとして 利用可能である必要があります

トランスフォームの登録

モデルクラスを使用するには、クラスに登録された変換関数とシリアライズ形式が必要です。例えば、CustomModel Stage を含むモデルがある場合、model.transform() が呼び出されたときに適用される関数を定義する必要があります。

定義された transform 関数は、Spark または Pandas DataFrame のいずれかで動作する必要があります。

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from foundry_ml.stage.flexible import stage_transform, register_stage_transform_for_class class CustomModel(object): def __init__(self, name): # ここでモデル名を初期化します ... def custom_transform(self, df): # ここでデータフレームを変換します ... return df # ステージ間でモデルとデータを包み込む関数に注釈を付けます @stage_transform() def _transform(model, df): # これは上で定義したモデルの変換関数を呼び出します return model.custom_transform(df) # これを呼び出してFoundry ML Stage Registryに送信します、force=Trueで既存の登録済み変換を上書きします register_stage_transform_for_class(CustomModel, _transform, force=True)

シリアライザとデシリアライザの登録

クラスに対する変換関数を登録したので、次に、Foundryにモデルコードのシリアライズとデシリアライズ方法を伝える必要があります。カスタム書き込みモデルステージを使用する場合、ステージは共有Pythonライブラリに書き込まれ、依存関係としてインポートされるようにすることが重要です。

これは、Stageクラスがデシリアライズ時に利用可能である必要があるためです。そうでなければ、StageクラスをCode Workbookに書き込み、その後で保存されたモデルを別のCode Workbookからロードしようとすると、モデルをロードできなくなる可能性があります。

以下の例では、CustomModeldillを使用してピクル化できると仮定しています。下の例では、モデルをファイルシステムから安全に読み書きするための2つのFoundryのヘルパー関数 load_datasafe_write_dataを利用しています。spaCyの例では、異なる実装を示しています。

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import dill from foundry_object.utils import safe_write_data, load_data from foundry_ml.stage.serialization import deserializer, serializer, register_serializer_for_class # デシリアライザデコレータ @deserializer("custom_model.dill", force=True) def _deserializer(filesystem, path): # ピクル化されたファイルの読み込み return dill.loads(load_data(filesystem, path, True), encoding='latin1') # シリアライザデコレータ @serializer(_deserializer) def _serializer(filesystem, value): path = 'custom_model.dill' # データをセーフに書き込む safe_write_data(filesystem, path, dill.dumps(value), base64_encode=True) return path # シリアライザをクラスに登録する register_serializer_for_class(CustomModel, _serializer, force=True)

CustomModelを適切に登録したので、他のStageと同様に model = Model(Stage(CustomModel(...))) という構文で使用し、model.transform(dataframe)で実行できます。

モデル内の関数のシリアライズ

一部のモデルステージ(特にシミュレーションラッパー)には、メインの変換関数と並んでユーザー作成の関数が含まれています。モデルを実行可能にするためには、ユーザー作成の関数もモデルの状態にシリアライズする必要があります。内部的には、Pythonはpickleパッケージを使用して関数を保存します。pickleパッケージは、関数を適切にシリアライズするための追加的な考慮事項を必要とします。

ステージをロードする際に、ModuleNotFoundError: No module named '...'などのエラーが発生することがあります。これは、ユーザー作成の関数が値ではなく参照によってシリアライズされたときに発生する可能性があります。これは、Pythonのバイトコードを直接シリアライズするのではなく、pickleが関数の名前をシリアライズしたことを意味します。

値によるシリアライズを強制するためには、コードを直接トランスフォーム関数に移動させることができます。

例を以下に示します:

class SimModel(SimulationWrapper):
    def run(self, data, parameters):
        ...
        # SimModelクラスは、SimulationWrapperクラスを継承します。
        # runメソッドは、データとパラメータを引数に取ります。

@transform(...)
def my_model(...):
  return Model(Stage(
        SimModel(parameters)
    ))
  # my_model関数は、transformデコレータを使用します。
  # この関数は、SimModelのインスタンスをStageクラスに渡して、新しいModelオブジェクトを返します。

以下のように記述する必要があります:

Copied!
1 2 3 4 5 6 7 8 9 @transform(...) # @transform デコレーターを使用します。このデコレーターは関数を変換します。 def my_model(...): # my_model 関数を定義します。 class SimModel(SimulationWrapper): # SimModelという名前のクラスを定義し、SimulationWrapperクラスを継承します。 def run(self, data, parameters): # runメソッドを定義します。このメソッドは、データとパラメータを引数に取ります。 ... return Model(Stage( # Modelクラスのインスタンスを作成し、Stageクラスのインスタンスを引数に取ります。 SimModel(parameters) # SimModelクラスのインスタンスを作成し、パラメータを引数に取ります。 ))

このルールは、カスタムコードが呼び出す他の関数にも適用されます。シリアライズされた関数やクラスが、値でシリアライズされる多くの依存関係を持っている場合、依存関係を Python ライブラリに抽出し、それをモデルに依存関係として追加することが推奨されます。

共有ライブラリの設定

上記のすべてのコードが model.py に配置されていると仮定すると、リポジトリは以下の構造になります:

├── README.md  # プロジェクトの説明やセットアップ方法など、重要な情報が書かれているマークダウンファイル
├── build.gradle  # Gradleビルドスクリプト。プロジェクトのビルドプロセスを定義
├── ci.yml  # GitHub Actionsのための設定ファイル。CI/CDパイプラインの設定
├── conda_recipe
│   └── meta.yaml  # Condaパッケージのメタデータが含まれる。パッケージ名、バージョン、依存関係など
├── gradle.properties  # Gradleの設定値が含まれる。例えば、GradleのバージョンやJVMのオプションなど
├── gradlew  # Gradle Wrapperスクリプト。これを使ってGradleタスクを実行
├── gradleww  # タイポかもしれない。本来であればgradlewが正しい
├── settings.gradle  # Gradleプロジェクトの設定ファイル。プロジェクトの構造やサブプロジェクトの定義など
├── src  # ソースコードが格納されるディレクトリ
│   ├──custom_plugin
│   │   ├── __init__.py  # Pythonパッケージとして認識させるためのファイル
│   │   └── model.py  # モデルの定義が含まれるPythonファイル
│   ├── setup.cfg  # Pythonパッケージの設定情報。パッケージ名、バージョン、依存関係など
│   └── setup.py  # Pythonパッケージのインストールスクリプト
└── templateConfig.json  # JSON形式の設定ファイル。具体的な内容はファイル内部を参照する必要がある

Foundry がプラグインを検出できるようにするために、まず __init__.py を変更して、model.py の内容をパッケージのトップレベルにインポートする必要があります:

Copied!
1 2 # .modelからすべてをインポート from .model import *

さらに、Model プラグインレジストリが新しいプラグインを検出するために、次の内容を setup.py に追加する必要があります:

Copied!
1 entry_points={'foundry_ml.plugins': ['plugin = custom_plugin']}, # エントリーポイント: {'foundry_ml.plugins': ['プラグイン = カスタムプラグイン']}

コミット、ビルド、そしてリリースをタグ付けすると、新しいモデルクラスが Code Workbook やコードリポジトリで利用可能になります。

ライブラリクラスの transform 関数をオーバーライドする

Foundry の標準関数が特定のユースケースに対応しきれない場合、既存の Model クラスの transform 関数をオーバーライドすることができます。手順は、上記のカスタムクラスに対して transform を登録する セクションと同じです。

ただし、レジストリは クラス レベルであることに注意してください。これは、特定のライブラリ関数(たとえば、sklearn の LogisticRegression)の transform() 関数をオーバーライドすると、そのライブラリ関数のすべてのインスタンスがオーバーライドした transform 関数を使用することを意味します。この関数は、オーバーライドを含むライブラリをインポートするたびに使用されます。

この振る舞いが望ましくない場合、以下の方法で解決できます:

  • ライブラリ関数をラップするラッパークラス(例:LogisticRegressionCustom)を作成する
  • 上記の手順に従って、それを所望の transform 関数で登録する

これにより、ライブラリ関数への呼び出しの振る舞いを変更することなく、この新しいクラスを使用できます。

トラブルシューティング

stage に対して stage_transform が登録されていない

シリアライズされたモデルでカスタムステージを使用しようとすると、エラー foundry_ml_core.stage.flexible._flexible_stage.FlexibleStageException: No stage_transform registered for stage type: <class 'NoneType'> が発生することがあります。

このエラーは、以下の手順で解決できることが多いです:

  • カスタムステージを含むプラグインパッケージが環境の一部であることを確認する。
  • パッケージが正しく設定されていることを確認する。特に、パッケージがプラグインとして登録されていて、上記のように __init__.py ファイルがクラスをインポートしていることを確認してください。
  • たまに、Conda はパッケージの古いバージョンに解決してしまいます。パッケージの正しいバージョンが使用されることを確実にするため、必要な特定のプラグインバージョンに Conda 環境をピン留めすることを推奨します。

PyPI パッケージ

現在、Foundry モデルでは PyPI パッケージをサポートしていません。Conda から依存関係を解決する必要があります。