2025/07/17

tf2onnx

先日もちらっと書きました。現行tf2onnx(1.16.1)はnumpy 1.26.4までにしないとダメです。

 python -m tf2onnx.convert --saved-model .\saved_model_multi_output\ --output Shinba.onnx

自分はAnacondaの仮想環境使ってるので、Anaconda PowerShell Prompt使って仮想環境をアクティブにして上記コマンドでSavedModel形式の

# TensorSpec を生成(shapeは共通なので固定)
input_signature = [
    tf.TensorSpec([None, 1], dtype, name=name)
    for name, dtype in feature_info
]

@tf.function(input_signature=input_signature)
def serve_fn(*features):
    return {"output": model(list(features))}

saved_model_path = "saved_model_multi_output"
tf.saved_model.save(model, saved_model_path, signatures={"serving_default": serve_fn})

とした方を指定して変換。出来上がるShiba.onnxは1.53GBってデカイ! Netron使って開いてみると

って感じです。まあ、こんなのはどうでもいいですが、Graph Properties開くと

と表示されるのが重要で、これを元に

internal class TFShinbaOnnxInput
{
    [ColumnName("baba")]
    [VectorType(1)]
    public int[] baba { get; set; }
    [ColumnName("course")]
    [VectorType(1)]
    public int[] course { get; set; }
    [ColumnName("joucd")]
    [VectorType(1)]
    public int[] joucd { get; set; }
    [ColumnName("kaisaikai")]
    [VectorType(1)]
    public float[] kaisaikai { get; set; }
    [ColumnName("kaisainichi")]
    [VectorType(1)]
    public float[] kaisainichi { get; set; }
    [ColumnName("kaisaituki")]
    [VectorType(1)]
    public int[] kaisaituki { get; set; }
    [ColumnName("kyori")]
    [VectorType(1)]
    public float[] kyori { get; set; }
    [ColumnName("tenko")]
    [VectorType(1)]
    public int[] tenko { get; set; }
    [ColumnName("tousu")]
    [VectorType(1)]
    public float[] tousu { get; set; }
    [ColumnName("trackcd")]
    [VectorType(1)]
    public int[] trackcd { get; set; }

    // uma10
    [ColumnName("uma10bareidays")]
    [VectorType(1)]
    public float[] uma10bareidays { get; set; }
...

って感じの入力クラスを準備して、先程のGraph Propertiesの最後まで行くと確認出来る出力部分も確認して

internal class TFShinbaOnnxOutput
{
    [ColumnName("output")]
    public float[] output { get; set; }
}

てな感じの出力クラスも用意。

            MLContext mLContext = new MLContext();

            var inputType = typeof(TFShinbaOnnxInput);

            // int[]のColumnName一覧
            var intColumnNames = inputType.GetProperties()
                .Where(p => p.PropertyType == typeof(int[]) &&
                            p.GetCustomAttributesData().Any(a => a.AttributeType == typeof(ColumnNameAttribute)))
                .Select(p => p.GetCustomAttributesData()
                    .FirstOrDefault(a => a.AttributeType == typeof(ColumnNameAttribute))
                    ?.ConstructorArguments[0].Value?.ToString() ?? p.Name)
                .ToList();

            // float[]のColumnName一覧
            var floatColumnNames = inputType.GetProperties()
                .Where(p => p.PropertyType == typeof(float[]) &&
                            p.GetCustomAttributesData().Any(a => a.AttributeType == typeof(ColumnNameAttribute)))
                .Select(p => p.GetCustomAttributesData()
                    .FirstOrDefault(a => a.AttributeType == typeof(ColumnNameAttribute))
                    ?.ConstructorArguments[0].Value?.ToString() ?? p.Name)
                .ToList();
            var allInputColumns = intColumnNames.Concat(floatColumnNames).ToArray();

            var pipeline = mLContext.Transforms.ApplyOnnxModel(
                modelFile: Properties.Settings.Default.TF_Shinba_Onnx,
                outputColumnNames: new[] { "output" },  // Netronで確認した出力名
                inputColumnNames: allInputColumns       // 入力名と一致させる
            );

            var emptyData = mLContext.Data.LoadFromEnumerable(new List<TFShinbaOnnxInput>());
            var model = pipeline.Fit(emptyData);
            var predictionEngine = mLContext.Model.CreatePredictionEngine<TFShinbaOnnxInput, TFShinbaOnnxOutput>(model);

            var input = new TFShinbaOnnxInput();
            input.SetValue(_baba, _courseKB, byte.Parse(_jouCD), _kai, _nichi, _tuki, _kyori, _tenko, _tousu, _trackCD, raceDetails);
            var prediction = predictionEngine.Predict(input);

って感じにC#でモデルロードして予測が出来ますね😁

0 件のコメント:

コメントを投稿