2025/07/27

tf2onnxの--large_model

散々苦労した対応なんですが、ダイエットは止めて現状で進める事に。ってのもtf2onnxでモデルが2GB超えるとって話ですが、これ、そもそも

python -m tf2onnx.convert --saved-model .\saved_model_multi_output\ --output Souha.onnx --large_model --opset 17

としてたんですが、--large_modelを付けた時点で出力されるモデルはzip圧縮されたものらしい。つまり、

python -m tf2onnx.convert --saved-model .\saved_model_multi_output\ --output Souha.zip --large_model --opset 17

として、利用するには適当なフォルダに解凍して(今回の場合は583個のファイルが出てくる)、その中に含まれる__MODEL_PROTO.onnxを指定してやれば

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

でも利用可能らしい。ただ、今回は色々やったおかげで諸々元に戻す作業と再学習時間が掛かってます。更に新馬戦用の学習中なんですが、学習中はPCが重い。他のデバッグ作業にも影響が出るし、ブラウザーの反応すら悪い。

更に非常に気になっているのはレース選択後に途方もない時間が掛かってます。まあ、レースの出走馬のデータ収集後にまずはML.NET用のデータ準備して予測し、その後にTensorFlowのデータ準備して更に予測。この際、最初のデータ収集でDBからの取得は済ませて置き、後はそこからそれぞれのクラスに適用して予測。このそれぞれのクラスに適用が思いのほか時間が掛かってます。単純にクラス→クラスでスマートにいけば良いのですが、以前まだまだ続く辛い道のりでも書いたクラスのメンバーにプログラムでアクセスする的に

var properties = new (string target, string src)[]
{
    ("barei", "Barei"),
    ("blinkers", "Blinkers"),
    ("cdchaku1", "CDChaku1"),
    ("cdchaku2", "CDChaku2"),
    ("cdchaku3", "CDChaku3"),
    ("cdchaku4", "CDChaku4"),
    ("cdchaku5", "CDChaku5"),
    ("cdchakux", "CDChakuX"),
⋮
};
for (int i = 0; i < umas.Count; i++)
{
    foreach (var (target, src) in properties)
    {
        string targetProperty = $"uma{i + 1}{target}";
        PropertyInfo targetProp = typeof(TFShinbaOnnxInput).GetProperty(targetProperty, BindingFlags.Public | BindingFlags.Instance);
        string srcProperty = $"{src}";
        PropertyInfo srcProp = typeof(RaceDetailInfo).GetProperty(src, BindingFlags.Public | BindingFlags.Instance);

        if (srcProp != null)
        {
            if (srcProp.CanRead)
            {
                if (targetProp != null && targetProp.CanWrite)
                {
                    if (targetProp.PropertyType == typeof(float[]))
                    {
                        if (srcProp.PropertyType != typeof(float))
                        {
                            Debug.WriteLine($"{targetProperty}({targetProp.PropertyType})に{srcProperty}({srcProp.PropertyType})をセットする為に?→float");
                            var value = Convert.ToSingle(srcProp.GetValue(umas[i]));    // ?をfloatに変換
                            targetProp.SetValue(this, new float[] { value });
                        }
                        else
                        {
                            var value = (float)srcProp.GetValue(umas[i]);
                            targetProp.SetValue(this, new float[] { value });
                        }
                    }
⋮

まあ、こんな感じにしてるので単純な代入ではない為に滅茶苦茶遅い。リリースビルドになれば少しは改善されるかもだけど、多分劇的に早くなる訳ではなさそうだから何か考える必要があるかも。

2025/07/26

ML.NETでonnx利用時の制限

Visual Studio Community2022のC#でML.NET使ってTensorFlowのモデルを利用する為にonnx形式に変換して利用する為にこれまで随分時間掛けて挑んで来ました。新馬戦用ではそれでも1.53GBと大きく、通常用では2GB超えるので、先日もちらっと書きましたが、tf2onnxで"--large_model"を付けて変換し2.07GBです。しかし、

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

として新馬戦用と同様にロードしようとしてもエラーになり、Copilotに相談すると2GB超えるlarge_modelには対応してない可能性が高いという事でTensorFlowでのモデルのダイエットが必要になりました。これまでは、

# レースのカテゴリ変数を定義
input_joucd = Input(shape=(1,), name="JouCD", dtype=tf.int32)  # 場コード
embedding_joucd = Embedding(input_dim=11, output_dim=4)(input_joucd)  # 場コードの埋め込み
flatten_joucd = Flatten()(embedding_joucd)
input_kaisaituki = Input(shape=(1,), name="KaisaiTuki", dtype=tf.int32)  # 開催月
embedding_kaisaituki = Embedding(input_dim=13, output_dim=4)(input_kaisaituki)  # 開催月の埋め込み
flatten_kaisaituki = Flatten()(embedding_kaisaituki)
input_trackcd = Input(shape=(1,), name="TrackCD", dtype=tf.int32)  # トラックコード
embedding_trackcd = Embedding(input_dim=60, output_dim=4)(input_trackcd)  # トラックコードの埋め込み
flatten_trackcd = Flatten()(embedding_trackcd)
⋮

としてましたが

# レースのカテゴリ変数を定義
input_joucd = Input(shape=(1,), name="JouCD", dtype=tf.int16)  # 場コード
embedding_joucd = Embedding(input_dim=11, output_dim=4)(input_joucd)  # 場コードの埋め込み
flatten_joucd = Flatten()(embedding_joucd)
input_kaisaituki = Input(shape=(1,), name="KaisaiTuki", dtype=tf.int16)  # 開催月
embedding_kaisaituki = Embedding(input_dim=13, output_dim=4)(input_kaisaituki)  # 開催月の埋め込み
flatten_kaisaituki = Flatten()(embedding_kaisaituki)
input_trackcd = Input(shape=(1,), name="TrackCD", dtype=tf.int16)  # トラックコード
embedding_trackcd = Embedding(input_dim=60, output_dim=4)(input_trackcd)  # トラックコードの埋め込み
flatten_trackcd = Flatten()(embedding_trackcd)
⋮

ってまあ、tf.int32→tf.int16に変更。当然だけどC#側でも

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; }
⋮

だった所を

internal class TFShinbaOnnxInput
{
    [ColumnName("baba")]
    [VectorType(1)]
    public short[] baba { get; set; }
    [ColumnName("course")]
    [VectorType(1)]
    public short[] course { get; set; }
    [ColumnName("joucd")]
    [VectorType(1)]
    public short[] joucd { get; set; }
⋮

にする必要があるんだけど、万が一元に戻すのも箇所が多いので面倒になる。って事で

// namespaceの外側で
using category_type = System.Int16[];

namespace SaraD64;

internal class TFShinbaOnnxInput
{
    [ColumnName("baba")]
    [VectorType(1)]
    public category_type baba { get; set; }
    [ColumnName("course")]
    [VectorType(1)]
    public category_type course { get; set; }
    [ColumnName("joucd")]
    [VectorType(1)]
    public category_type joucd { get; set; }
⋮

としておけば、category_type宣言部を修正するだけで済む。ただ、問題は本当にこれでダイエットになるのかは、また途方もない修正作業と学習時間が必要になるので、まずは新馬戦用で試して確認してます。

2025/07/24

まっ、TensorFlowで保存後の再学習は断念

Copilotに振り回され、Geminiでも微妙なので時間の節約で一旦、保存後の再学習は諦めて、先程50epochsをスタートしたので金曜日には終わるかな😅 ちょっとML.NETでのonnx形式での予測実装用の入力クラス定義でNetronでモデル開いて確認しながらクラス定義記述してると明らかな違和感があり、Pythonでのコード確認すると...酷いコーディングミスを発見😖

その部分を含めて一応一連を再確認しての学習スタートなので今週末には通常用含めた予測が可能になると期待して、それを元に順位予測用CSVデータ出力して、その学習させて、そのモデルのML.NETでの予測実装ってステップになるかと思ってます。

最悪の想定として秋のGIスタートには間に合うかなぁって最近は諦めてみます。でも、実装や学習等を並列実行して時短し、更に各モデルの検証も実装する予定です。で!この検証が重要で、これが楽しみでもあります。AutoMLやLightGBMでは多変量多出力回帰モデルは対応しておらず、ざっくりな話として、予測対象馬のデータのみ学習させて予測してました。

多変量多出力回帰モデルに対応しているTensorFlowでは、予測対象のレースに出走する全ての馬のデータを学習させて、全ての馬の走破予測や順位予測をさせます。ここで、ざっくりな話として対象馬のみのデータでは同一レースで走る馬、騎手、その他諸々の情報加味しての予測になる筈。新馬戦用で1,018個の特徴量だったかと思いますが、通常走破予測では9,589個の特徴量です。なので新馬戦用とは桁違いに学習時間が必要です。

余談ですが、これまでも何度かやらかしてるというか、Microsoftの無能な対応で人の苦労を勝手に無にしてます。長時間学習ではWindows Updateの自動は無効にしないとってのは何度もやられてます。ちょっと今回は33時間程度の予定でそれ程と思ったのが間違いなのと、夜間だと思ったのに真昼間にやられた事。このUpdateってそもそもMicrosoft自体のチョンボの尻拭いであって、こちらのミスではない。更にPCがアイドル状態とかならまだしも、Visual Studioが2つも起動し、一つは編集途中、一つはデバッグでの実行中なのはタクス確認すれば分かるってか、詳細は別としてCPU使用率は確実に高い状態が維持されている。AI学習されていれば当然CPUはぶん回ってます。そんな事もチェックせずに勝手に再起動させて、こちらの努力を無駄にしてくれる。悲しいかな、こんな無能集団のMicrosoftから逃れられない呪縛。ああっ、ちょっと愚痴りました。

だからではないけど、TensorFlowにはぜひともAutoMLとかLightGBMなんかを遥かに超えた予測してくれる事を期待してひたすらコーディングしてます。

2025/07/21

苦悩の日々

先週末も競馬そっちのけでひたすらCopilotとの格闘。プラス自分のコーディングミスなんかの処置で予想する気力とか時間とかね。でも、先が見えたかも!

そもそもの問題はってか、通常の走破予測の学習モデルはTensorFlowで1epochで40分弱なんです。最低でも500epochs程度は学習しようとは思ってますが、50epochsで学習終了後にセーブ時にエラーで全てが無駄にorz それを踏まえて小刻みに学習をさせるべく、その術をCopilotに問い合わせ、その実現に向けて先週末はほぼ終わりました。

ここまでは

# SavedModel形式で保存
model.export('TFMShinbaCK')

# 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})

この2本立て。いやね、当初諸々してた時にたどり着いたmodel.exportなんですが、これ、多分無用ですね。先日も書いてますが、その次のがonnx形式には重要ってか使います。

一旦学習したモデルを保存して、後日読込して再学習が必要と思い、その実現に先週末は時間を費やしました。最初Copilotが提案したのが

model.save("Shinba.keras")

なんですが、これはNG! 詳細既に忘れてますが、そもそも".keras"では保存がエラーになる。これって別に自分がやろうとしたのではなく、単にCopilotのアドバイスからね。で、次は

model.save("Shinba.h5")

これは、Geminiも案内した方法なんですが、これをこれからちょっと色々とやってみるかなぁ。ってのも、それ以前に新馬戦用のonnx形式は実装出来たので、走破予測のonnx形式も確認して実装作業しながら学習も進めていこうってのと、それ出来たら二つ合わせた走破予測の検証して、それ使った順位予測の学習用CSVの出力とその学習。

自分の年齢的にものんびりする予定は無いです! ただ、想定より遥かに時間を必要としているので、ここは慌てずに着実に進めていく予定です

追記 2025.7.21
ちょっとビビった😅 実は昨晩どうにか走破予測モデルをたった1epochで完了してまして、これをonnx形式にしてNetronで確認してML.NETで予測する部分の実装進めようかと思っていて、早速いつものってかAnacondaの仮想環境でお約束のコマンド使ってみた所、そのままでは2GBの壁か何かでNG! --large_model付ければってCopilotがいうので試した所、無事にonnx形式に出来た。参考までに1epochしかしてないモデルですが、2.07GBでした。

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#でモデルロードして予測が出来ますね😁

2025/07/13

TensorFlow→ML.NET

もうどの位の期間取り組んできたのか忘れた。相当時間掛かってます。Netronでモデルの情報確認してあれこれ試し、

model.export(save_path)

として保存してたのを

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

にしても結局何かがダメって言われる。この何かが理解不能でCopilotやGeminiで解決図ったが行き詰った。

public class TFShinbaModelInput
{
    [VectorType(1)]
    [ColumnName("serving_default_Baba")]
    public int[] Baba { get; set; }
    [VectorType(1)]
    [ColumnName("serving_default_Course")]
    public int[] Course { get; set; }
    [VectorType(1)]
    [ColumnName("serving_default_JouCD")]
    public int[] JouCD { get; set; }
...

新馬戦用でも1,018個の特徴量があったりで

var mlContext = new MLContext();

var inputType = typeof(TFShinbaModelInput);

var intColumns = inputType.GetProperties()
    .Where(p => p.PropertyType == typeof(int[]) &&
                p.GetCustomAttributesData().Any(a => a.AttributeType == typeof(ColumnNameAttribute)))
    .Select(p =>
    {
        var attr = p.GetCustomAttributesData()
            .FirstOrDefault(a => a.AttributeType == typeof(ColumnNameAttribute));
        return attr?.ConstructorArguments[0].Value?.ToString() ?? p.Name;
    })
    .ToArray();

var floatColumns = inputType.GetProperties()
    .Where(p => p.PropertyType == typeof(float[]) &&
                p.GetCustomAttributesData().Any(a => a.AttributeType == typeof(ColumnNameAttribute)))
    .Select(p =>
    {
        var attr = p.GetCustomAttributesData()
            .FirstOrDefault(a => a.AttributeType == typeof(ColumnNameAttribute));
        return attr?.ConstructorArguments[0].Value?.ToString() ?? p.Name;
    })
    .ToArray();

var allFeatureColumns = intColumns.Concat(floatColumns).ToArray();

var pipeline = mlContext.Model.LoadTensorFlowModel(Properties.Settings.Default.TF_Shinba)
    .ScoreTensorFlowModel(
        outputColumnNames: new[] { "StatefulPartitionedCall" },
        inputColumnNames: allFeatureColumns,
        addBatchDimensionInput: true);

こんな感じでしっかり準備したうえでロードしてるんですが結局ダメでした。

以前確か試した記憶があるんですが、TensorFlowモデルをonnx形式で保存してML.NETでonnx形式を利用する方法を試すべく新たな仮想環境を準備。ってのもtf2onnxはnumpy 2.0以降で削られたnp.castとかの部分がNGでnumpy 1.26.4にダウングレードして試す必要があるようなので、今その環境で新馬戦用モデルの学習してます。それが済んだらonnx形式で保存してML.NETでロードしてまた苦戦する予定😣

2025.7.14 23:02
ひゃ~! onnx形式で行けた😂 これでちょっと先が見えたので、一気に進めます😉

2025/07/12

今回の芋焼酎

3カ月ぶりに新たな芋焼酎探しの買い物をしました。

楽天の買い回り利用しての購入でしたが

  • 甕伊佐錦三年貯蔵 2,800円
  • 伊佐舞 2,120円
  • 小鹿本にごり 2,050円
  • 黒伊佐錦 2,050円
それと島乙女 1,896円で2本と合わせて12,812円で送料無料に。このショップは1万円以上なら送料無料だったので選択しましたが、P箱での発送は処理に困るのでもう利用しない。送料無料がなかなか少ないので今回も島乙女が本来の値段ではないけど我慢して購入。今回の買い回りは14倍程度まで行ったので単純計算なら12,812×0.86だとすると実質11,018円程度。普段は先月辺りに購入した米焼酎が2ケースで13,530円なのでちょっと贅沢だ(笑)

取敢えず甕から試しましたが、まあ、値段的にもですが、美味い。表現難しいけど、角が無くまろやかな甘みがある芋ですね。ただ、値段的にもリピートは微妙。お気に入りのだいやめは12,650円/ケースの送料無料だから。

2025/07/03

VMware Workstation Pro Version 17.6.3

久しぶりにWMware入れてみようとダウンロードしました。ここ最近はPlayerが個人が無料で利用出来るって事で使ってたんですが、今はProも個人は無料😁 ちょっとビックリでしたが、まあ、ダウンロードしてインストール。

いきなり見た目が全く違うものになっていて驚きました。取敢えずUbuntu 24.04を入れてみました。
なんか随分スムーズになった感じがします。ちょっと遊んでみようかと思いますが、やる事もあるので程々に😉

2025/07/01

Microsoft Rewards 21回目

先日からちょいょいやらかしてます。夜勤明けに忘れたり、先週末はひたすらコーディングが気になり、すっかり忘れました。そんな訳で今のストリークが3だったりします😓 それでも継続は力? 本日Amazonギフト600円分頂きました。前回5月14日から49日での達成です。前回が40日での達成だったので大幅に伸びたのはやらかしてるからですね。でも、継続します。ああっ、楽天はもっと貯めないと交換出来ないので今回は諦めた。でも、今後は検討してみようかと思います。しかし、現時点で確認しても楽天は無くなってる。どうも不安定ですね。