OneFlowが静的グラフの演算子調整タスクを実行する方法

記事ディレクトリ

  • 1はじめに
  • 2OneFlowのグラフ演算子の配置の概要
  • 3グラフモードでの自動テストの実装原理
    • 3.1AutoTestプロセスの概要
    • 3.2グラフモードがオペレーターの調整のための熱心なモードにどのように付随するか
    • 3.3グラフモードの自動テストパーソナライズ
  • 4グラフのデバッグサポート
  • 5まとめ
  • 6関連リンク

1はじめに

ディープラーニングフレームワークでモデルを実行する主な方法は、動的グラフと静的グラフの2つです。動的グラフは使いやすく、静的グラフの方がパフォーマンスが優れています。OneFlowは、これらをEagerモードとGraphモードと呼ぶことに慣れています。OneFlowはnn.Graphモジュールを提供します。これにより、ユーザーはEagerモードと同様のプログラミング習慣を使用して静的グラフトレーニングテストを構築できます。したがって、オペレーターの動作の正確性を確保し、EagerモードとGraphモードの両方で結果を得る必要があります。

前回の記事では、ディープラーニングフレームワークがオペレーターの調整タスクをエレガントに実行する方法について説明し、ランダムデータテストケースの生成方法やAutoTestコアコードの実装など、EagerOpsの自動テストプロセスを分析しました。AutoTestフレームワークは次のようになります。他のディープラーニングフレームワークに簡単に移植できます。ただし、Graph Opsの自動テスト方法に焦点が当てられていないため、この記事の主な目的は、OneFlowがグラフモードでオペレーターのテストタスクを完了する方法を紹介することです。これまでのところ、OneFlow v0.7.0には、nn.Graph上おり、自動化された単一テスト機能が完了しています。

記事に含まれるコードの場所:

  • https://github.com/Oneflow-Inc/oneflow/blob/master/python/oneflow/test_utils/automated_test_util/torch_flow_dual_object.py
  • https://github.com/Oneflow-Inc/oneflow/blob/master/python/oneflow/test_utils/automated_test_util/generators.py

2OneFlowのグラフ演算子の配置の概要

OneFlowが提供する熱心なモードで、使用法はPyTorchに合わせて調整されます。したがって、テストでは、AutoTestフレームワークはさまざまな法的パラメーターで構成されるOpsをランダムに生成し、同じ値とタイプ(PyTorchとOneFlow用に1つ)の入力Tensorに基づいてそれぞれPyTorchとOneFlowのコードを実行して操作を完了しますオペレーターの。作業を調整します。

さらに、OneFlowは、オブジェクト指向プログラミングスタイルに基づくグラフモードも提供するため、Eager開発に精通しているユーザーは、わずかなコード変更で静的グラフを効率的に使用できます。熱心なモードと比較して、グラフモードはデバッグが容易ではありませんが、パフォーマンスが向上し、最適化と展開が容易です。次に、グラフモードでOpsを自動的にテストする方法は、注意が必要な重要な問題です。

グラフの片面を詳しく紹介する前に、AutoTestフレームワークのグラフを開く方法を見てみましょう。以下はmatmul演算子のテスト例です。random_pytorch_tensor2つのランダムテンソルがメソッドに基づいて構築され、それらの次元はそれぞれ[n, k]とです[k, m]。これらの次元の値はランダムに生成されます。AutoTestフレームワークパラメーターのランダム性は、generators.pyのgenerator基本。

    @autotest(check_graph = True)
    def test_flow_matmul_with_random_data(test_case):
        device = random_device()
        k = random(1, 6)
        x = random_tensor(ndim=2, dim1=k).to(device)
        y = random_tensor(ndim=2, dim0=k).to(device)
        z = torch.matmul(x, y)
        return z

を呼び出すことによりtorch.matmul(x, y)、自動テストフレームワークはTorchおよびOneFlowのmatmul演算子をそれぞれ実行し、EagerモードでのOneFlowおよびPyTorch演算子の順方向と逆方向の結果が一貫しているかどうかを確認します。コード内の@autotestデコレータのcheck_graph切り替えがであることに注意してください。これはTrue、グラフの単一のテストがこの時点で並行して実行されることを示しています。

3グラフモードでの自動テストの実装原理

背景と使用法を理解した後、GraphAutoTestの実装のアイデアを説明しましょう。

3.1AutoTestプロセスの概要

Eagerの自動テストの原則では、ランダムデータの生成方法とautotest()デコレータこの記事の焦点では​​なく、前の記事で明確に紹介されています。AutoTestフレームワークのコアプロセスの実装に関して、最初に注意しなければならないのは、OneFlowとPytorchのオペレーター調整タスクで使用されるGetDualObject関数です。この関数は、渡されたプリミティブとオブジェクトのマジックGetDualObjectオーバーライドし、最後にオブジェクトを返します。このプロセスには、注意を必要としないいくつかの魔法の関数をスキップすること、着信オブジェクトのプロパティが有効かどうかを確認すること、継承されたクラスによって生成されたランダムデータに対する他のAPIデフォルトパラメータに基づくことも含まれます。特定のタイプ(関数で完了);さらに呼び出し(pass )と、のメソッドが異なる(pass )ため、コード内のメソッドには特別な判断があります。PyTorchOneFlow__call__DualObjectnn.Modulegeneratorget_argstensortensorgetattrnn.modulenn.functional__call__

上記のプロセスに基づいて、サンプルコードを実行することにより、torch.matmul(x, y)AutoTestフレームワークGetDualObjectDualObject関数を呼び出すことによってtorchオブジェクトを生成します。この関数はDualObjectオブジェクトとして理解できます。最後に、DualObjectオブジェクトして結果の比較を完了します。自動テストプロセスでの熱心なオペレーターの調整の詳細も、前の記事で明確に紹介されています。

  • GetDualObjectこの関数は、https://github.com/Oneflow-Inc/oneflow/blob/7fe29cb3d24be41fa981c4ad6be3051dacc3b605/python/oneflow/test_utils/automated_test_util/torch_flow_dual_object.py#L600で実装されています。

  • DualObjectクラスオブジェクトは次の場所に実装されています:https://github.com/Oneflow-Inc/oneflow/blob/0826518cc49200dccada0f54d5c83accb9218c83/python/oneflow/test_utils/automated_test_util/torch_flow_dual_object.py#L784

3.2グラフモードがオペレーターの調整のための熱心なモードにどのように付随するか

上記の分析から、AutoTestのプロセスを大まかに要約して、ランダムデータの生成、オブジェクトの生成、DualObjectオブジェクトの実行DualObject、および結果が一致しているかどうかの判断を行うことができます。その中で、AutoTestフレームワークは、DualObjectオブジェクトます。これにより、オペレーターをグラフモードのEagerモードに合わせるタスクが完了します。さらに、このセクションでは、静的(グラフ)実行が必要なオブジェクトをGetDualObject関数。

オペレーター調整タスクには、3つのタイプnn.moduleメソッドnn.functionalがあります。tensorここでは、グラフモードのコードをイーガーモードテストで分析する例としてnn.Moduleタイプますが、他の3つのタイプの処理方法は基本的に同じです。コードの実行順序は以下のとおりです。
ここに画像の説明を挿入

と呼ばれ、それぞれoneflow_eager_run_with_graph_check、グラフモードと熱心モードの2つの結果を取得し、最後にアライメントをチェックしました。つまり、テストケースの場合、AutoTestフレームワークは合計3つのコード、つまりPytorch、OneFlow Eagerモード、OneFlow Graphモードを実行して、3つの結果が一致しているかどうかを確認します。最初にこの。コードは次のように表示されます。get_module_graph_testget_oneflow_eager_resget_module_graph_test

# NOTE(lixiang): When oneflow is of type nn.Module, build the following Graph for testing.
#   graph_train_oneflow: is a deepcopy of oneflow.
def get_module_graph_test(graph_train_oneflow, oneflow, *args):
    of_sgd = flow.optim.SGD(graph_train_oneflow.parameters(), lr=0.001, momentum=0.9,)
    graph_train_parameters_len = 0
    for param in oneflow._parameters.values():
        if param is not None:
            graph_train_parameters_len += 1

    class TestGraphOfModule(flow.nn.Graph):
        def __init__(self):
            super().__init__()
            self.test_module = graph_train_oneflow
            if global_backward and graph_train_parameters_len:
                self.add_optimizer(of_sgd)

        def build(self, *args):
            res = self.test_module(*args)
            forward_res = res
            if global_backward and graph_train_parameters_len:
                res = res.sum()
                res.backward()
            return forward_res

    return TestGraphOfModule()

その中にoneflowは、ディープコピーの結果であるnn.Moduleオブジェクトがあります。これgraph_train_oneflowは主に、演算子のインプレースバージョンをテストするときに対応するDualObjectオブジェクト、GraphとEagerの入力の不一致によりテスト結果に一貫性がなくなります。まず、グラフの逆方向が正常に実行できることを確認するために、オプティマイザーが構築されます。でイーガーモードでオブジェクトを再利用した__init__、グラフテストの計算プロセスをで説明し、最後にグラフのインスタンスを返します。つまり、すべての演算子に適応する一般的な静的グラフモデルを構築することです。nn.Modulebuild

静的に実行されたコードを作成してグラフの結果を計算する方法について説明した後、静的な実行が必要なオブジェクトを特定することも解決すべき優先事項です。oneflow_eager_run_with_graph_check完全なコードは次のとおりです。

# NOTE(lixiang): Check if the results of eager and graph are equal when oneflow is of type nn.Module or functional.
def oneflow_eager_run_with_graph_check(
    oneflow, oneflow_args, oneflow_kwargs, testing_graph, verbose, *args
):
    if testing_graph:
        graph_args, graph_kwargs = get_args_copy(oneflow_args, oneflow_kwargs)

        if isinstance(oneflow, flow.nn.Module):
            graph_train_oneflow = copy.deepcopy(oneflow)
            if not is_global():
                arg_device_type = "cpu"
                for arg in oneflow_args:
                    if flow.is_tensor(arg):
                        arg_device_type = arg.device.type
                graph_train_oneflow = graph_train_oneflow.to(arg_device_type)

        else:
            graph_functional_oneflow = copy.deepcopy(oneflow)

    oneflow_res = get_oneflow_eager_res(oneflow, oneflow_args, oneflow_kwargs, verbose)
    if testing_graph:
        if verbose:
            print(
                "After running eager module or functional: ", repr(oneflow),
            )
        find_check_module_func = True
        ignore_apis_list = ["tensor", "train"]
        test_g_res = []
        if isinstance(oneflow, flow.nn.Module):
            test_g = get_module_graph_test(graph_train_oneflow, oneflow, *args)
            if verbose:
                print("Run graph of module: ", repr(oneflow))
                test_g.debug(3)
            # When testing module methods, kwargs are not considered.
            test_g_res = test_g(*graph_args)
            if verbose:
                print(
                    "The result after running graph module: ", test_g_res,
                )
        elif oneflow.__name__ in ignore_apis_list:
            find_check_module_func = False
        # 1. "oneflow.nn.modules" not in oneflow.__module__: For avoid run nn.Module branch graph test, like fold op call Fold Module actually.
        # 2. inspect.isfunction(oneflow): Compared with the ordinary flow.xxx, oneflow.nn.modules.math_ops series op exist an extra layer of python wrapper.
        # 3. inspect.ismethod(oneflow) and "oneflow.nn.modules" in oneflow.__module__:  For op that only has Tensor.xxx method, and call oneflow.xxx actually, like masked_fill.
        elif (
            ("oneflow.nn.modules" not in oneflow.__module__)
            or inspect.isfunction(oneflow)
            or (
                inspect.ismethod(oneflow) and "oneflow.nn.modules" in oneflow.__module__
            )
        ):

            test_g_res = get_functional_graph_res(
                graph_functional_oneflow,
                oneflow,
                oneflow_res,
                oneflow_args,
                oneflow_kwargs,
                verbose,
                *graph_args,
                **graph_kwargs,
            )
        if find_check_module_func:
            if isinstance(test_g_res, tuple):
                for _, g_res in enumerate(test_g_res):
                    check_eager_graph_tensor(oneflow_res, g_res)
            else:
                check_eager_graph_tensor(oneflow_res, test_g_res)
    return oneflow_res

oneflow_eager_run_with_graph_checkでは、静的にテストする必要があるオブジェクトを決定する必要があります。たとえば、コードのOneFlow設定部分は静的である必要があるため、Eagerモードの一部のメソッドはグラフモードで定義されていません。上記のコードでは、if testing_graph:最初にグラフスイッチがオンになっているかどうか、つまりグラフ単体テストを並行して実行するかどうかを判断し、次にオブジェクトのタイプを判断oneflowします。オンになっている場合は、静的に実行して呼び出す必要がありますそれ以外の場合は、呼び出しやその他の処理を行います。同様の判断が必要なテストフレームワークの他の場所についても同じことが言えます。isinstancenn.Moduleget_module_graph_testget_functional_graph_res

    if testing_graph:
        ···
        ···
        if isinstance(oneflow, flow.nn.Module):
            ···
            test_g = get_module_graph_test(graph_train_oneflow, oneflow, *args)
            ···
        elif:
            ···
            ···

3.3グラフモードの自動テストパーソナライズ

グラフが3.2でイーガーモードとのオペレーター調整のタスクをどのように行うかを紹介した後、このセクションでは主にグラフモードでの自動テストのパーソナライズされたコンテンツを分析します。

グラフモードでは、3つのカテゴリのメソッドを処理する必要がありnn.module、 AutoTestフレームワークは、最初にグラフを判断してから作成する方法を採用しています。まず関数には、関連するインターフェイスが含まます、、、、、、、、、および次の表に示すように、各インターフェイスの機能を確認してください。nn.functionaltensorGetDualObjectget_pytorch_oneflow_resget_pytorch_oneflow_tensor_resoneflow_eager_run_with_graph_checkoneflow_tensor_eager_run_with_graph_checkget_oneflow_eager_resget_tensor_graph_resget_functional_graph_resget_module_graph_test

働き 情報
get_module_graph_test nn.moduleモジュール内の演算子のGraphインスタンスを返します。
get_functional_graph_res nn.functionalモジュール内の演算子のグラフ計算結果を返します。
get_tensor_graph_res tensorモジュール内の演算子のグラフ計算結果を返します。
get_pytorch_oneflow_res それぞれOneFlowとTorchの計算結果を取得します。
get_pytorch_oneflow_tensor_res テンソルOpsの専門分野、同上。
oneflow_eager_run_with_graph_check グラフチェックを伴って、熱心なパターンが整列しているかどうかをテストします。
oneflow_tensor_eager_run_with_graph_check テンソルOpsの専門分野、同上。
get_oneflow_eager_res OneFlowEagerモード演算子の計算結果を取得します。

各関数の機能を大まかに理解した後、コールチェーンを見てみましょう。次のフローチャートに示すように、この図には、AutoTestフレームワークでのグラフモードの存在とnn.module、対応する3つnn.functionalのカテゴリのメソッドの処理方法が含まれています。図の3つの灰色のボックスにtensor
ここに画像の説明を挿入

3つのカテゴリーnn.moduleの処理方法を分析した後、その中、グラフを自動テストする場合の逆勾配テストもありますが、テンソルに対応する勾配は取り出されません。つまり、逆実行が保証されます。正常です。グラデーション値はチェックされません。使用方法については、オンにすると(デフォルトでオンになります)、Eagerの後方テスト(勾配の結果がここで比較されます)を実行するだけでなく、対応するグラフの後方テストも実行します。 (ここではグラデーションの比較は行われません)。nn.functionaltensor@autotest()auto_backward=True

上記の説明に対応するコードは、記事のセクション3.2のコードにあります。

if (
  global_backward
  and graph_train_parameters_len
):
  self.add_optimizer(of_sgd)
···
···
···
if (
  global_backward
  and graph_train_parameters_len
):
  res = res.sum()
  res.backward()

さらに、一部のオペレーターインプレースバージョンのグラフチェックでは、グラフとイーガーの入力が常に一貫していることを確認するために、入力のディープコピーを作成する必要があります。次のコードでは、get_args_copy(torch_flow_dual_object.py内で)共通パラメーターとキーワードパラメーターをそれぞれディープコピーします。同様に、グラフの片側oneflowではgraph_train_oneflow、主に一部の演算子をテストするときにEager InplaceによってEagerの値が変更されないようにするために、ディープコピーの動作があります。その結果、GraphとEagerの入力に不整合が生じ、テストが発生します。エラー。

# NOTE(lixiang): Deepcopy the input parameters in order to correctly test the inplace version of the op.
def get_args_copy(args, kwargs):
    copy_args = []
    for arg in args:
        if flow.is_tensor(arg):
            copy_arg = arg.clone().detach()
        else:
            copy_arg = copy.deepcopy(arg)
        copy_args.append(copy_arg)
    copy_kwargs = {
    
    }
    for key, value in kwargs.items():
        if flow.is_tensor(value):
            copy_kwargs[key] = value.clone().detach()
        else:
            copy_kwargs[key] = copy.deepcopy(value)
    return copy_args, copy_kwargs

最後に、テンソルディープコピーの正確性を確保するために、OneFlowでは、copy.deepcopyテンソルのメソッドgetStatesetStateテンソルの状態には、データ、dtype、およびデバイス情報を同時に含める必要があります。これらはすべて必須です。特定のコードを参照してください:https://github.com/Oneflow-Inc/oneflow/blob/e00ba51364ff87e39edc409be395e5ed493a4ac0/python/oneflow/framework/check_point_v2.py#L159。

4グラフのデバッグサポート

3.2のコードif verbose:は、存在する判断を見つけることができます。verbose = Trueがの場合、グラフのデバッグ情報(オペレーターがグラフモードを実行した後の計算結果など)が出力されます。もちろん、その他の必要なデバッグも出力されます。熱心な情報。テストに問題がある場合は、この関数を使用してエラーサンプルを取得し、最小限の再現コードを作成できます。開く方法は、環境変数によって制御されますONEFLOW_TEST_VERBOSE = 1AutoTestフレームワークのこの関数は、開発者を対象としており、OneFlowのグラフはユーザーにデバッグ関数も提供します。

グラフモードは学習率のデバッグ出力に対応しており、オープニング方法はイーガーと同じです。

optimizer = flow.optim.SGD(model.parameters(), lr=1e-3)
# Set verbose=True
scheduler = flow.optim.lr_scheduler.CosineDecayLR(optimizer, decay_steps=100, alpha=0.98, verbose=True)

さらに、Graphオブジェクトのdebugメソッドを呼び出すと、Graphのデバッグモードが有効になります。

graph.debug(v_level = 1) # 可以简写为:graph.debug(1)
  • v_level=0、最も基本的な警告と作曲時間などの作曲ステージ情報のみが出力されます。
  • v_level=1、それぞれnn.Moduleのます。
  • v_level=2、作曲段階では、名前、入力内容、機器、SBP情報など、各Opの作成情報が追加で印刷されます。
  • v_level=3、コードの位置に関連する情報など、各Opのより詳細な情報が追加で印刷されます。これは、コードの問題を特定するのに便利です。

この部分の詳細については、https://docs.oneflow.org/master/basics/08_nn_graph.html#graph_3をご覧ください。

5まとめ

AutoTestフレームワークの柔軟性と使いやすさは比較的強力です。この記事では、主に、グラフモードがオペレーターの調整とグラフのパーソナライズされたコンテンツの自動テストのためのEagerモードにどのように付随するかを紹介します。EagerからGraphまでのローカルops実行テストカバレッジもOneFlowv0.7.0で完了しており、0.8バージョンはGraphGlobalopsユニットテストの正確さを保証します。さらに、静的グラフのデバッグおよびその他の機能がより完全になります。どなたでもご利用いただけます。

6関連リンク

  • https://github.com/Oneflow-Inc/oneflow
  • https://github.com/pytorch/pytorch

おすすめ

転載: blog.csdn.net/weixin_43838785/article/details/124298222