ラベル ディープラーニング の投稿を表示しています。 すべての投稿を表示
ラベル ディープラーニング の投稿を表示しています。 すべての投稿を表示

2025/08/13

AutoML vs Model Builder

先日AutoMLのPreviewがバージョンアップしてLightGBMの4.6.0が使われる様になったって事で、この所、WinUI3での開発の合間にAutoMLでの学習もさせてました。まあ、現在リリースしてるバージョンに添付してる学習モデルも昨年末までのデータでのものでしたし、う~う~言いながらWinUI3での開発で四苦八苦している程度なら裏で学習させていても然程影響もないのでって事で有効活用してた訳です。が!例えば、新馬戦用ではAutoMLで多分30時間以上も学習させて得た最適RMSEは1.9949でした。

45R 1点 芝(38R) ダート(7R) 8頭以下(15R) 9~12頭(15R) 13頭以上(15R) 多点
単勝 17.78%
(53.11%)
21.05%
(62.89%)
0.00%
(0.00%)
40.00%
(113.33%)
6.67%
(14.67%)
6.67%
(31.33%)
53.33%
(97.93%)
複勝 40.00%
(66.44%)
39.47%
(69.21%)
42.86%
(51.43%)
53.33%
(78.00%)
33.33%
(42.00%)
33.33%
(79.33%)
88.89%
(78.22%)
枠連 0.00%
(0.00%)
0.00%
(0.00%)
0.00%
(0.00%)
--
(--)
0.00%
(0.00%)
0.00%
(0.00%)
16.00%
(81.39%)
馬連 2.22%
(5.56%)
2.63%
(6.58%)
0.00%
(0.00%)
6.67%
(16.67%)
0.00%
(0.00%)
0.00%
(0.00%)
15.56%
(36.96%)
ワイド 11.11%
(19.33%)
13.16%
(22.89%)
0.00%
(0.00%)
26.67%
(50.00%)
6.67%
(8.00%)
0.00%
(0.00%)
42.22%
(76.52%)
馬単 2.22%
(8.00%)
2.63%
(9.47%)
0.00%
(0.00%)
6.67%
(24.00%)
0.00%
(0.00%)
0.00%
(0.00%)
15.56%
(30.67%)
三連複 4.44%
(23.11%)
5.26%
(27.37%)
0.00%
(0.00%)
6.67%
(15.33%)
6.67%
(54.00%)
0.00%
(0.00%)
15.56%
(35.72%)
三連単 2.22%
(16.44%)
2.63%
(19.47%)
0.00%
(0.00%)
6.67%
(49.33%)
0.00%
(0.00%)
0.00%
(0.00%)
15.56%
(38.45%)
総合 40.00%
(25.41%)
39.47%
(28.95%)
42.86%
(6.67%)
53.33%
(49.52%)
33.33%
(14.83%)
33.33%
(13.83%)
88.89%
(47.25%)

ああ、これ、さらだでではなく、さらだ64使ってます。

いや、別にサボってないよって事では無いですが(笑)、地道に進めてますって報告兼ねてね。
でも、相変わらずModel Builer自体がLightGBMのどのバージョンを採用しているのかは不明ですが、こちらだと本日5時間程度の学習走らせて得た最適RMSEは1.9632で

45R 1点 芝(38R) ダート(7R) 8頭以下(15R) 9~12頭(15R) 13頭以上(15R) 多点
単勝 15.56%
(50.44%)
18.42%
(59.74%)
0.00%
(0.00%)
20.00%
(65.33%)
20.00%
(54.67%)
6.67%
(31.33%)
53.33%
(87.26%)
複勝 53.33%
(87.11%)
55.26%
(93.68%)
42.86%
(51.43%)
53.33%
(78.67%)
60.00%
(78.00%)
46.67%
(104.67%)
91.11%
(72.67%)
枠連 0.00%
(0.00%)
0.00%
(0.00%)
0.00%
(0.00%)
--
(--)
0.00%
(0.00%)
0.00%
(0.00%)
8.00%
(13.19%)
馬連 4.44%
(26.44%)
5.26%
(31.32%)
0.00%
(0.00%)
13.33%
(79.33%)
0.00%
(0.00%)
0.00%
(0.00%)
11.11%
(38.15%)
ワイド 13.33%
(30.44%)
15.79%
(36.05%)
0.00%
(0.00%)
26.67%
(50.67%)
13.33%
(40.67%)
0.00%
(0.00%)
37.78%
(64.81%)
馬単 4.44%
(47.33%)
5.26%
(56.05%)
0.00%
(0.00%)
13.33%
(142.00%)
0.00%
(0.00%)
0.00%
(0.00%)
11.11%
(30.00%)
三連複 6.67%
(30.22%)
7.89%
(35.79%)
0.00%
(0.00%)
13.33%
(36.67%)
6.67%
(54.00%)
0.00%
(0.00%)
11.11%
(23.78%)
三連単 4.44%
(132.00%)
5.26%
(156.32%)
0.00%
(0.00%)
13.33%
(396.00%)
0.00%
(0.00%)
0.00%
(0.00%)
11.11%
(33.09%)
総合 53.33%
(53.47%)
55.26%
(62.31%)
42.86%
(6.67%)
53.33%
(121.24%)
60.00%
(28.42%)
46.67%
(17.00%)
91.11%
(39.48%)

って感じなんです。ああっ、どちらも学習データは今年の6月末までを使用したので、検証は7月1日~先週末までですので、期間が短く甲乙つけるのは微妙ではありますけどね。

ああっ、TensorFlowは諦めた訳ではないのですが、やはり、簡単ではない様です。既にさらだ64では、TensorFlowの学習モデルをonnx形式に変換したモデルを使った予測も出来る状態にまでは来たのですが、いやね、各epochで出るRMSEもそうなんですが、AutoMLでもModel Builderでも1.4前後なのに0.00??とか異常に小さい数値が出てて、もしかすると学習設定で諸々する方法が何か間違っているかもなので、素人の自分がそれを解決するのには時間が掛かると判断しての作業スケジュールの見直しです。

WinUI3は未だに好きになれないけど、ここまで来てまた最初からってか、Windowsフォームアプリとしてのさらた64なら、まあ、単純に現状のさらだのターゲットをx64に変えて、JV-Linkアクセス周りのみ外部32bitプロセスを作ってって事で行けばってのも選択肢としてはあるのですが、さらだ自体の画面設計やテーマ対応(ダークモードとかね、自分自身のPCはダークモードだし)とかね。実際、少数しかいない、さらだユーザーさんのPCでどんな感じで動いているのかも気になりますし、WinUI3でのさらだ64はその辺りも吸収して解像度に依存しないで行けるアプリになればって期待も含めてね。ただ、UI周りとしては大分作りこみましたが、肝心のJV-Linkアクセス用の外部32bitアプリは全く手つかずorz いや、サボってるのではなく、現行のさらだでDBを最新にしておいて、さらだ64でそのDBを使えば動くようにはしてるので、ちょっと思案中。

仮に現行のさらだにおんぶ的にさらだ64ではJV-Link無でリリースして基本さらだでDB更新してさらだ64で諸々って運用も出来なくはないので...いや、決して使い勝手は良くないのですが、開発期間がまだまだ掛かる事を想定するとってのと、もしJRA-VANがJV-Linkの64bit化をしてくれれば、現行のさらだのコードを利用して最小限の工数でさらだ64に実装も可能なのでってもあるのでね^^;

まっ、そんな訳でWinUI3でのさらだ64の開発進めながら、今現在はModel Builderで通常用の24時間学習をさせている所です。って、あれ?なんかエラーが出まくってるので、その対処しますので、一旦ここまでで報告終わりです。

追記 2025.8.13 18.25
書き忘れました! さらだ64はWindows11のみ対応です。Windows10は残り3カ月程度でMicrosoftのサポートが切れます。有償での延長とかって、基本一般ユーザーはしない可能性高いと思うし、WinUI3ではWindows10は微妙な感じなので切ります<m(__)m>

2025/08/07

ついでな報告として

TensorFlowでの一般用(これまで通常用と言ってきたけど、新馬戦用と一般用にします)の順位予測の学習モデルに取り掛かってましたが、昨夜からいよいよ学習が出来る所まで来た(?)と思うので、ちょっと失敗はいきなり50epochsで行っちゃったので、現時点で31/50だったりしますが、遅くとも土曜日の朝には終わるかと😓 いや、多分10epochsとかにすれば良かったと悔やんでます。

これ、現時点のログ状況なんですが、RMSEが異常な値になってます。先に新馬戦用の学習した時にはここまでのRMSEではなかったと思うので、バグ? それとも...

ただ、一般用はこれまでC#での利用が出来ていません。学習終了後にonnx形式に変換してML.NETでの利用を目指します。順調に行けば、それ元に検証プロセスの実装もして実力を確認したいと思ってます。

追記 2025.8.8 18:07
やらかしました😱 本日、盆前最終勤務後に帰宅してPC確認すると、エラーで止まってました。もう少し修正漏れがあったので、この30数時間が無駄にorz 修正は即座にして、念の為、1epochで行ってます。で、もしかしたら50epochsは必要ないのかも? 正常にセーブまで行くの確認したら取敢えず10epochsで行けば明日朝には終わってる筈なので、順調にコーディング出来れば、その実力を見てみようかと思います。

2025/08/03

苦戦は続いてますが

もう、何カ月苦戦しているか...確かGW辺りには取り掛かってた記憶なので😖

いや、進展がない訳でもないし、沢山学習はって学習モデルの学習ではなく自分自身の学習ですがさせて頂きました。これはCopilotに感謝ですが...まあ、滅茶苦茶遠回りもしてる感じはしますけどね。

そんな過程から、現在までML.NETから始まった機械学習で、ターゲットとして走破予測をしてきたのですが、これは実際には目的ではないのです😞 そもそもの目的は各レースでどの馬が来るかを予測してもらい馬券を当てるのが目的です。なので学習データの出力を走破タイムではなく確定着順に変更して今後は作業進めて行きます。

ML.NET系のAutoMLやLightGBMは現状のままで行きますが、TensorFlowでは順位の直接予測にします。これである程度の無駄が省けると思います。CSV出力からの修正になるので、また少し時間は掛かりますが、ここまでの苦労でモデルの学習や出来たモデルのonnx変換からのML.NET経由でのC#での活用は無駄にはならない筈なので一気に行きます。

ただ、新馬戦用のサイズでは問題はないのですが通常用のサイズで問題解決には至ってないのは気になりますが、この方向で進めて問題解決していく予定。

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/06/29

WinUI3での進捗

頑張って書いてるんですが時間掛かってます。やる事が多過ぎて時短は厳しい😓

見た目的には大分復元出来てきた。それと、これは確信はなかったんですが、学習モデルが大きくなるとロード時にエラーになる問題ですが、32bitではエラーになった196MBのモデルも64bitでは普通に使えました😁 これはなかなか嬉しい副産物です。RMSEが最適なのに大き過ぎて使えなかった過去があるので期待したい。

今後この画面にLightGBMやTensorFlow使った予測も入れ込めれば64bit化した恩恵が更に深まる。この辺りは優先的に進めたいと思ってます。特にTensorFlowの予測に期待しているので早い段階で使える事が確認出来れば学習も同時に進められるので効率も良いかも。

2025/05/24

少し進展した

継続は力とはよく聞きますが、諦めずにやれる事を続けてきてます。そもそもの原因は自分の理解不足なのは承知してます。更にCopilotに振り回されても耐えられる忍耐と多分知力かなぁ。まあ、なんにしてもRMSEが38辺りでって問題は突破出来た。簡単に言えば、層の構築方法に問題があった模様です。

って事で、本来のデータを使った訓練スタートしてます。相変わらずのサイズなので38分/epochで50epochsを試してますので32時間弱かな。明日夜には終わる予定なので、そこまで無難に訓練されていれば良いのですが、数値的に問題があったりしたら中断するかもです。現時点では3epochがRMSE 18辺りなんですが、少なくとも明日朝にはRMSE 2未満になっている事を期待したいかな。

で、まあ、これが順調に終わったと仮定すると、新馬戦用、通常用、順位予測の3本建てのモデルが揃うってか、そもそも新馬戦用と通常用が出来た時点で、これを使った順位予測用学習データ出力にしなければならなくて、それを使って順位予測の訓練をする必要もある。この辺りをまとめて外部プロセスとしてLightGBMでも作った感じのものをTensorFlow用にも作成してアプリに同胞出来る様に仕上げる訳ですが、ダービーには間に合わないorz

それでもそれ仕上げるのとUI変更なんかもこれから徐々に進めてもう少しだけ良いものになればと思ってます。まだまだやる事が山ほど有るな😓

追記 2025.5.25 7:44
あと少しで29epochが終わりますがRMSEはやっと16未満になる辺り。ただ、じりじりと下がってはいるので回数が必要なんだと。順位の時だったか500epochを5日半とか掛けてやりましたが、この通常用でやると13日ちょい掛かるって2週間ぶん回すのはキツイなぁ。電気代もそうだけど、このポンコツPCにトドメ刺しそう。しかもね、これ、そこまでやって本当に良い結果が得られる保証は無い。単純計算では2週間このポンコツ(750W電源使用)回すと7千円以上の電気代になるっぽい。アイドル状態ではなく、機械学習してるとほぼPCのファンがうるさい状態って事はかなり電気は消費してると思われますしね。

2025/05/23

全くの謎

TensorFlowの理解が全く進まない。

これが現時点の問題で、5epoch辺りで収束してそれ以上改善されない。先週はこれに挑んで、まあ、こんなグラフ出力までしても何の改善方法も見つけられない。昨夜久しぶりに新馬戦用の訓練もしてました。20時間弱で500epochsをこなし、見てる限りはRMSE 2.0辺りまで行ってましたが、最終的な表示は8.9とかだったかな? これはなんか計算方法間違えてるのかは疑問だが、それでも500回でもまだモデル自体は収束ってまあ、確かになかなかそれ以上は進まない感じながらもじりじりとで、たった5回程度で収束方向ってのとは違う。

今回改めて新馬戦用の訓練を実行したのは、Anacondaの仮想環境の再構築ってか、そもそも以前ちょっと試してダメだったGPUの力を少し試すってのをね、まあ、うちのPCは今年の夏には丸7年になるポンコツで、NVIDIA GeForce GTX 1050Tiに4GBメモリなZOTAC ZTGTX1050TI-4GD5MINI001/ZT-P10510A-10って18,800円のグラボしか載ってないのでほぼ助けにはならないのは覚悟でCUDAとcuDNNを以前インストールしたらVisual Studioの動きに支障があったのでアンインストールして放置してたんですが、

  • CUDA 12.9.0
  • cuDNN 9.10.1

をインストールして仮想環境を再構築しました。今回は一応NVIDIA公式のCUDA Installation Guide for Microsoft Windowsを参考に

conda install cuda -c nvidia

としてAnaconda側でもCUDAを入れて

python -m pip install --upgrade setuptools pip wheel

とする事で

python -m pip install nvidia-pyindex

これが出来る様になるらしい。で、仮想環境を構築する準備が出来たので仮想環境をPython 3.9で作成して

  • tensorflow
  • pandas
  • scikit-learn

をpip使って入れて、新馬戦用はこれで足りますが、通常版ではグラフ出したりするので更に

  • livelossplot
  • ipython

も入れてます。

でも、ここまでしても流石にグラボの恩恵はほぼ感じられない😞

2025/05/18

特徴量を正規化し、学習率を徐々に減少させる方法

 層毎のユニット数調整では顕著な変化は見られなかったので新たな試みとして

  • 特徴量の正規化
  • 学習率の調整

をCopilotの助言を元に施してみました。

from tensorflow.keras.layers import Activation, BatchNormalization

x = Dense(256)(merged_features)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dropout(0.3)(x)

x = Dense(128)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dropout(0.3)(x)

x = Dense(64)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)

uma_networks.append(x)

これが特徴量の正規化追加した部分で

from tensorflow.keras.optimizers import Adam

# 学習率を指定
initial_learning_rate = 0.001
lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate, decay_steps=10000, decay_rate=0.9, staircase=True)

optimizer = Adam(learning_rate=lr_schedule)

# モデルをコンパイル
model.compile(optimizer=optimizer, loss='mse', metrics=['mae', root_mean_squared_error])

これがExponentialDecayを利用して

  • decay_steps=10000: 1 万ステップごとに減衰
  • decay_rate=0.9: 10% ずつ減衰
  • staircase=True: 段階的に学習率を減らす

としているらしい。50epochsで既に開始して、今までだとRMSE 38辺りが限界値的に得られた程度だったんですが、7epoch辺りで到達してここから更に良くなるかが気になる所です。まあ、CopilotにはDropout(0.3)はちょっと大きいのでDropout(0.2)にしてみてはってアドバイスも頂いてるので、それも試そうとは思ってます。

2025/05/17

時短には

結局良い結果が得られないままで、時間だけが過ぎていく。そこで、そもそも最初から2004年~2024年という21年分のデータを学習させているのも時間が掛かる一因な訳でこれを2004年~2010年の7年分にして試す事にしてみました。って事で今はCSVデータ出力中でそんな中でもCopilotに質問ぶつけて各層のユニット数調整のアドバイスをもらってみた。

x = Dense(256, activation='relu')(merged_features)
x = Dropout(0.3)(x)  # 過学習防止
x = Dense(128, activation='relu')(x)
x = Dropout(0.3)(x)
x = Dense(64, activation='relu')(x)
uma_networks.append(x)

馬毎はこんな感じとし、

# 結合後のネットワーク全体
x = Dense(512, activation='relu')(final_features)  # 隠れ層
x = Dropout(0.3)(x)  # 過学習防止
x = Dense(256, activation='relu')(x)  # 隠れ層

結合後もちょい調整してみる事にしました。CSVデータ出力し終えたら試します。

追記 2025.5.17 14:56
ちょっとした手違いから時間が余分に掛かってしまいました。まあ、Visual Studio Community2022もVersion 17.14.0がリリースされたので先にアップデートも済ませて訓練開始してみると、10分/epochに短縮されてますのでこれで色々試せればと思います。

2025/05/14

TensorFlowの理解不足

そもそも理解してるとは思っていませんが、取敢えずモデルの訓練が出来る様になったので50epochsで試してました。ああ、これ、35分/epochなんで50epochsだと29時間です😨 初トライではあまり良い結果が出ず、RMSEが確か39とかだった記憶なんですが、Copilotにあまり精度が上がらないけどって相談してみると

from tensorflow.keras.optimizers import Adam

# 学習率を指定
optimizer = Adam(learning_rate=0.01)  # 例えば 0.01 に変更

# モデルをコンパイル
model.compile(optimizer=optimizer, loss='mse', metrics=['mae', root_mean_squared_error])

とする事で学習率が指定出来るとの事で試しました。そもそもデフォルトでは0.001らしいです。でも、結果的にはそれ程改善された訳では無かったです。そこで、まあ、そもそも特徴量が爆発的に増えてますので層毎の指定を大幅に変更し、馬毎の部分を

x = Dense(1024, activation='relu')(merged_features)
x = Dropout(0.3)(x)  # 過学習防止
x = Dense(256, activation='relu')(x)
x = Dropout(0.3)(x)
x = Dense(64, activation='relu')(x)

として、より複雑(?)にして50epochsスタートしてみました。これ、結果わかるの29時間以上後なので...

追記 2025.5.14 21:17
そもそも当初はどうだったのかが分からんと比較出来ないですね😓

x = Dense(64, activation='relu')(merged_features)
x = Dropout(0.3)(x)  # 過学習防止
x = Dense(32, activation='relu')(x)
x = Dropout(0.3)(x)
x = Dense(16, activation='relu')(x)

これ、新馬戦用からコピーしてそのままだったんですが、そもそも新馬戦用ももう少し変更は必要ですね。

ああっ、そいえばWindowsに緊急のUpdateが来てるんだったけど、訓練スタートしちゃったから後日だなorz

追記 2025.5.15 21:23
終わるまで我慢出来なくて中断しました。思えば、上記なんですが、馬毎にしてるので馬毎は確か533特徴量なんてす。なので流石に1024は違うだろうって判断です。まあ、残り10epochsでやはりRMSE 38辺りだったのも中断の決め手です。

x = Dense(128, activation='relu')(merged_features)
x = Dropout(0.3)(x)  # 過学習防止
x = Dense(32, activation='relu')(x)
# x = Dropout(0.3)(x)
# x = Dense(16, activation='relu')(x)

時短になるかもって事で1つの層を削ってみたけど、これは効果無い感じorz いや、まだ1つ目も終わってないが訓練始まるとそのepochの残り時間とかが出るので単純にどの程度掛かるのか分かるんだが、38分とかだったので変わってないなっと。明日朝時点でRMSEがどの程度かで、今回の修正が意味あるのか分かるかも?

2025/04/30

スゲー!のかな? Visual StudioのIntellisense!!

これ、少し前ってかVisual Studio 2022の目玉だった? 正確な記憶が残ってませんが、ここの所も別にサボってた訳じゃなくひたすらコード書いてたのでこちらにはあまり書き込む余裕が無かったんです。でも、登場当初よりも更に磨きかけて助けてくれる様になってきた気がします。

その昔はキーボードマクロなんかで少しでも楽して大量のコードの記述してたんですが、ストラクチャーやクラスのメンバーなんかのあまり覚えてない部分でも候補だしてくれるので、そこから選択すれば可能なのは本当に助かってますが、Copilotなんかのサポートも入れ込んだものになってきたのか入力が格段に効率アップしてます。

PythonのTensorFlowで訓練したモデルのC#での利用って事で進めてますが、また無駄な時間の浪費になるのも覚悟で...いや、結局外部プロセスでの実装を選択したんですが、この際なので...まあ、そもそも順位予測には走破タイムの予測を入れてます。テスト的に行った訓練にはモデルビルダーの学習モデルが吐き出した走破タイム予測を利用してますが、どうせやるなら、その走破タイムの予測もTensorFlowにした方が筋が通るかなって事でね。まずは新馬戦学習用CSV出力のMシリーズでの書き直し。AutoMLにしてもモデルビルダーにしても、走破タイムの予測は個々の馬で予測させて来ましたが、どうなるかは未知ですが、レース毎に出走馬全てを一括で走破タイムの予測を行う事がTensorFlowでは可能なので試してみようと必死にコーディング中。出来ればある程度の形にGW中に仕上げられれば、最悪微調整なんかはその後でもと思って進めてます。

まあ、ズッコケたら日の目を見ずにお蔵入りですけどね😓

2025/04/26

C#でTensorFlowのモデル利用

モデル訓練も2つ程終わらせてるので実際に利用可能か試し始めました。簡単に利用するにはML.NETでロードして利用がと思い調べて試してます。

  • Microsoft.ML
  • Microsoft.ML.TensorFlow
  • SciSharp.TensorFlow.Redist

これらがまず必要なのはわかりインストール。

using Microsoft.ML;
using Microsoft.ML.Data;

namespace TFCheck;

class Program
{
    static void Main(string[] args)
    {
        var mlContext = new MLContext();
        // Load the model
        var modelPath = @"TFMJuniFullCK";
        var tensorFlowModel = mlContext.Model.LoadTensorFlowModel(modelPath);
        // 入力と出力のスキーマを確認
        var schema = tensorFlowModel.GetInputSchema();
        Console.WriteLine("Input Schema:");
        foreach (var column in schema)
        {
            Console.WriteLine($"Name: {column.Name}, Type: {column.Type}");
        }
    }
}

こんな単純なのが最初動かない。少し調べると何のことはないです。この所では珍しくない事でx86がサポートされてない。自力でx86用にビルドすれば可能のようですがそこまでするのは面倒が多過ぎる。が、ちょっと悩むなぁ。苦労してビルドしてうまく行くならアプリにも組み込めます。逆にx64で行くならまた外部プロセスにする事になる。

2025/04/24

TensorFlowモデル訓練で痛恨のミス

モデル訓練が順調に走ってる事は事前に色々Copilotにも相談しながら進めてたので問題無かったんですが、コード自体は更に続いて予測、結果表示、保存と進んでいくんですが、訓練までしかテストしないまま訓練スタートさせて、今回はなんと5.5日の予定だったのが、まあそれよりちょっと早く(?)昨夜終わったんです。が!その後の予測でエラー!となり、保存まで行かなかったのでここまでの苦労は全て無駄にorz いや、全くの無駄かといえば、ここまでやるとかなり訓練されるのは分かったし、ここまでやってもEarlyStoppingにならないのでもっと長くやれば更にって事なのは分かりました。

現在保存まで行ける筈の修正込みでEpochs=50で訓練中。ああ、訓練済みのモデルロードして更に訓練も出来る筈なのでその辺りも試そうとは思います。

しかし、今回の訓練でのPC連続稼働で電気代はザックリな計算で2,700円を無駄にした。750Wの電源つけてるPCだからって本当に750W使ってるかは別として24時間で540円程度。別に訓練とかしてなくても週末なんかだと朝起きて寝るまで18時間程度PCは動かしてるので、それだけで400円程電気代掛かってると再認識すると...いや、でもPCは必要だから😓

2025/04/18

TensorFlowのモデルの訓練

先日書いたPythonでTensorFlow使ってモデルの訓練したってやつは、まあ、さらだでもやった順位予測の一番シンプルなファクター(特徴量)数の31個だったので、まあ、Epochsを50程度で本当にさらっと学習させられるものでした。

でまあ、TensorFlowの基本的な流れは出来たのでCSVデータもRaceJuniFullCKと同等なものを出力させてファクター数も230個と格段に増やしました。

# 入力データ(特徴量)
X = data[[
    "JouCD", "KaisaiTuki", "KaisaiKai", "KaisaiNichi",
    "TrackCD", "Course", "Kyori", "Tousu", "Tenko", "Baba",
    "KyosouShubetu", "KyosouJouken", "JuuryoShubetu", "StandardDeviation",
    "Uma1Barei", "Uma1Sex", "Uma1Blinkers", "Uma1Kinryo",
    "Uma1Kyakusitu1", "Uma1Kyakusitu2", "Uma1Kyakusitu3", "Uma1Kyakusitu4",
    "Uma1Kishu", "Uma1Chokyosi", "Uma1Souha", "Uma1DeviationValues",
    "Uma2Barei", "Uma2Sex", "Uma2Blinkers", "Uma2Kinryo",
    "Uma2Kyakusitu1", "Uma2Kyakusitu2", "Uma2Kyakusitu3", "Uma2Kyakusitu4",
    "Uma2Kishu", "Uma2Chokyosi", "Uma2Souha", "Uma2DeviationValues",
    "Uma3Barei", "Uma3Sex", "Uma3Blinkers", "Uma3Kinryo",
    "Uma3Kyakusitu1", "Uma3Kyakusitu2", "Uma3Kyakusitu3", "Uma3Kyakusitu4",
    "Uma3Kishu", "Uma3Chokyosi", "Uma3Souha", "Uma3DeviationValues",
    "Uma4Barei", "Uma4Sex", "Uma4Blinkers", "Uma4Kinryo",
    "Uma4Kyakusitu1", "Uma4Kyakusitu2", "Uma4Kyakusitu3", "Uma4Kyakusitu4",
    "Uma4Kishu", "Uma4Chokyosi", "Uma4Souha", "Uma4DeviationValues",
    "Uma5Barei", "Uma5Sex", "Uma5Blinkers", "Uma5Kinryo",
    "Uma5Kyakusitu1", "Uma5Kyakusitu2", "Uma5Kyakusitu3", "Uma5Kyakusitu4",
    "Uma5Kishu", "Uma5Chokyosi", "Uma5Souha", "Uma5DeviationValues",
    "Uma6Barei", "Uma6Sex", "Uma6Blinkers", "Uma6Kinryo",
    "Uma6Kyakusitu1", "Uma6Kyakusitu2", "Uma6Kyakusitu3", "Uma6Kyakusitu4",
    "Uma6Kishu", "Uma6Chokyosi", "Uma6Souha", "Uma6DeviationValues",
    "Uma7Barei", "Uma7Sex", "Uma7Blinkers", "Uma7Kinryo",
    "Uma7Kyakusitu1", "Uma7Kyakusitu2", "Uma7Kyakusitu3", "Uma7Kyakusitu4",
    "Uma7Kishu", "Uma7Chokyosi", "Uma7Souha", "Uma7DeviationValues",
    "Uma8Barei", "Uma8Sex", "Uma8Blinkers", "Uma8Kinryo",
    "Uma8Kyakusitu1", "Uma8Kyakusitu2", "Uma8Kyakusitu3", "Uma8Kyakusitu4",
    "Uma8Kishu", "Uma8Chokyosi", "Uma8Souha", "Uma8DeviationValues",
    "Uma9Barei", "Uma9Sex", "Uma9Blinkers", "Uma9Kinryo",
    "Uma9Kyakusitu1", "Uma9Kyakusitu2", "Uma9Kyakusitu3", "Uma9Kyakusitu4",
    "Uma9Kishu", "Uma9Chokyosi", "Uma9Souha", "Uma9DeviationValues",
    "Uma10Barei", "Uma10Sex", "Uma10Blinkers", "Uma10Kinryo",
    "Uma10Kyakusitu1", "Uma10Kyakusitu2", "Uma10Kyakusitu3", "Uma10Kyakusitu4",
    "Uma10Kishu", "Uma10Chokyosi", "Uma10Souha", "Uma10DeviationValues",
    "Uma11Barei", "Uma11Sex", "Uma11Blinkers", "Uma11Kinryo",
    "Uma11Kyakusitu1", "Uma11Kyakusitu2", "Uma11Kyakusitu3", "Uma11Kyakusitu4",
    "Uma11Kishu", "Uma11Chokyosi", "Uma11Souha", "Uma11DeviationValues",
    "Uma12Barei", "Uma12Sex", "Uma12Blinkers", "Uma12Kinryo",
    "Uma12Kyakusitu1", "Uma12Kyakusitu2", "Uma12Kyakusitu3", "Uma12Kyakusitu4",
    "Uma12Kishu", "Uma12Chokyosi", "Uma12Souha", "Uma12DeviationValues",
    "Uma13Barei", "Uma13Sex", "Uma13Blinkers", "Uma13Kinryo",
    "Uma13Kyakusitu1", "Uma13Kyakusitu2", "Uma13Kyakusitu3", "Uma13Kyakusitu4",
    "Uma13Kishu", "Uma13Chokyosi", "Uma13Souha", "Uma13DeviationValues",
    "Uma14Barei", "Uma14Sex", "Uma14Blinkers", "Uma14Kinryo",
    "Uma14Kyakusitu1", "Uma14Kyakusitu2", "Uma14Kyakusitu3", "Uma14Kyakusitu4",
    "Uma14Kishu", "Uma14Chokyosi", "Uma14Souha", "Uma14DeviationValues",
    "Uma15Barei", "Uma15Sex", "Uma15Blinkers", "Uma15Kinryo",
    "Uma15Kyakusitu1", "Uma15Kyakusitu2", "Uma15Kyakusitu3", "Uma15Kyakusitu4",
    "Uma15Kishu", "Uma15Chokyosi", "Uma15Souha", "Uma15DeviationValues",
    "Uma16Barei", "Uma16Sex", "Uma16Blinkers", "Uma16Kinryo",
    "Uma16Kyakusitu1", "Uma16Kyakusitu2", "Uma16Kyakusitu3", "Uma16Kyakusitu4",
    "Uma16Kishu", "Uma16Chokyosi", "Uma16Souha", "Uma16DeviationValues",
    "Uma17Barei", "Uma17Sex", "Uma17Blinkers", "Uma17Kinryo",
    "Uma17Kyakusitu1", "Uma17Kyakusitu2", "Uma17Kyakusitu3", "Uma17Kyakusitu4",
    "Uma17Kishu", "Uma17Chokyosi", "Uma17Souha", "Uma17DeviationValues",
    "Uma18Barei", "Uma18Sex", "Uma18Blinkers", "Uma18Kinryo",
    "Uma18Kyakusitu1", "Uma18Kyakusitu2", "Uma18Kyakusitu3", "Uma18Kyakusitu4",
    "Uma18Kishu", "Uma18Chokyosi", "Uma18Souha", "Uma18DeviationValues"
]]

出力データは以前のものと同じです。で、今回は当たり前ですが本格的に取り組みました。Copilotとまあ平日で夜勤なのでちょっと眠い感じではありますが、可能な限り作業を続け3日目となる昨日出勤2時間前程度にどうにか訓練してくれる所までこぎつけました。

# コースは小数点第1位までの実数ですが、整数に変換する必要があります。
X["Course"] = (X["Course"] * 10).astype(int)  # コースデータを10倍して整数化

このコース区分はAutoMLやLightGBM用のCSVデータでもそうだったんですがコース区分を数値にしてて、"A"~"E"だけど、中には"A1"とかもあるって事で"A"は1.0、"A1"は1.1的にしてました。ML.NET系は基本実数なので特に問題なく使えてましたが、TensorFlowでファクター(特徴量)をカテゴリ変数とする場合にはint64である必要があるのでこんな感じでデータを前処理加工。

# レースのカテゴリ変数
X_train_inputs = [
    X_train["JouCD"], X_train["KaisaiTuki"], X_train["TrackCD"], X_train["Course"], X_train["Tenko"], X_train["Baba"],
    X_train["KyosouShubetu"], X_train["KyosouJouken"], X_train["JuuryoShubetu"]
]
# レースの数値データを追加
X_train_inputs.extend([
    X_train["KaisaiKai"], X_train["KaisaiNichi"], X_train["Kyori"], X_train["Tousu"], X_train["StandardDeviation"]
    ])

こんな感じで指定しました。馬毎のファクターも同様にカテゴリ変数と数値データを指定。

# ウマごとのカテゴリ変数を追加
for i in range(18):
    X_train_inputs.extend([
        X_train[f"Uma{i+1}Sex"], X_train[f"Uma{i+1}Blinkers"], X_train[f"Uma{i+1}Kishu"], X_train[f"Uma{i+1}Chokyosi"]
    ])

# ウマごとの数値データを追加
X_train_inputs.extend([
    X_train[f"Uma{i+1}{feature}"]
    for i in range(18)
    for feature in ["Barei", "Kinryo", "Kyakusitu1", "Kyakusitu2", "Kyakusitu3", "Kyakusitu4", "Souha", "DeviationValues"]
])

で、TensorFlowではお決まりの層を定義していく為に

# レースのカテゴリ変数を定義
input_joucd = Input(shape=(1,), name="JouCD")  # 場コード
embedding_joucd = Embedding(input_dim=11, output_dim=4)(input_joucd)  # 場コードの埋め込み
flatten_joucd = Flatten()(embedding_joucd)
input_kaisaituki = Input(shape=(1,), name="KaisaiTuki")  # 開催月
embedding_kaisaituki = Embedding(input_dim=13, output_dim=4)(input_kaisaituki)  # 開催月の埋め込み
flatten_kaisaituki = Flatten()(embedding_kaisaituki)
input_trackcd = Input(shape=(1,), name="TrackCD")  # トラックコード
embedding_trackcd = Embedding(input_dim=60, output_dim=4)(input_trackcd)  # トラックコードの埋め込み
flatten_trackcd = Flatten()(embedding_trackcd)
input_course = Input(shape=(1,), name="Course")  # コース
embedding_course = Embedding(input_dim=100, output_dim=4)(input_course)  # コースの埋め込み
flatten_course = Flatten()(embedding_course)
input_tenko = Input(shape=(1,), name="Tenko")  # 天候
embedding_tenko = Embedding(input_dim=7, output_dim=2)(input_tenko)  # 天候の埋め込み
flatten_tenko = Flatten()(embedding_tenko)
input_baba = Input(shape=(1,), name="Baba")  # 馬場状態
embedding_baba = Embedding(input_dim=5, output_dim=2)(input_baba)  # 馬場状態の埋め込み
flatten_baba = Flatten()(embedding_baba)
input_kyosoushubetu = Input(shape=(1,), name="KyosouShubetu")  # 競走種別
embedding_kyosoushubetu = Embedding(input_dim=25, output_dim=2)(input_kyosoushubetu)  # 競走種別の埋め込み
flatten_kyosoushubetu = Flatten()(embedding_kyosoushubetu)
input_kyosoujouken = Input(shape=(1,), name="KyosouJouken")  # 競走条件
embedding_kyosoujouken = Embedding(input_dim=1000, output_dim=8)(input_kyosoujouken)  # 競走条件の埋め込み
flatten_kyosoujouken = Flatten()(embedding_kyosoujouken)
input_juuryoushubetu = Input(shape=(1,), name="JuuryoShubetu")  # 斤量種別
embedding_juuryoushubetu = Embedding(input_dim=5, output_dim=2)(input_juuryoushubetu)  # 斤量種別の埋め込み
flatten_juuryoushubetu = Flatten()(embedding_juuryoushubetu)

# レースの数値変数を定義
input_kaisai_kai = Input(shape=(1,), name="KaisaiKai")  # 開催回
input_kaisai_nichi = Input(shape=(1,), name="KaisaiNichi")  # 開催日
input_kyori = Input(shape=(1,), name="Kyori")  # 距離
input_tousu = Input(shape=(1,), name="Tousu")  # 頭数
input_standard_deviation = Input(shape=(1,), name="StandardDeviation")  # 標準偏差

てな感じでして

# レースの特徴量を結合
race_features = Concatenate()([
    flatten_joucd, flatten_kaisaituki, flatten_trackcd, flatten_course, flatten_tenko, flatten_baba,
    flatten_kyosoushubetu, flatten_kyosoujouken, flatten_juuryoushubetu,
    input_kaisai_kai, input_kaisai_nichi, input_kyori, input_tousu, input_standard_deviation
])
race_dense = Dense(64, activation='relu')(race_features)  # 隠れ層の学習
race_dense = Dense(32, activation='relu')(race_dense)     # 更に学習層を追加

この後、当然馬毎の入力層も定義し

# 馬ごとのネットワーク
merged_horses = Concatenate()(uma_networks)

# レース特徴量と馬特徴量を結合
final_features = Concatenate()([race_dense, merged_horses])

# 結合後のネットワーク全体
x = Dense(128, activation='relu')(final_features)  # 隠れ層
output = Dense(18, activation='linear', name="Output")(x)  # 出力層

これで、層は出来たので

# モデルを構築
model_inputs = [input_joucd, input_kaisaituki, input_trackcd, input_course, input_tenko, input_baba,
                input_kyosoushubetu, input_kyosoujouken, input_juuryoushubetu,
                input_kaisai_kai, input_kaisai_nichi, input_kyori, input_tousu, input_standard_deviation] + uma_inputs
model = Model(inputs=model_inputs, outputs=output)

後は入力データを↑の様に整えるのに

X_train_inputs = [
    X_train["JouCD"],  # JouCD
    X_train["KaisaiTuki"],  # KaisaiTuki
    X_train["TrackCD"],  # TrackCD
    X_train["Course"],  # Course
    X_train["Tenko"],  # Tenko
    X_train["Baba"],  # Baba
    X_train["KyosouShubetu"],  # KyosouShubetu
    X_train["KyosouJouken"],  # KyosouJouken
    X_train["JuuryoShubetu"],  # JuuryoShubetu
    X_train["KaisaiKai"],  # KaisaiKai
    X_train["KaisaiNichi"], # KaisaiNichi
    X_train["Kyori"], # Kyori
    X_train["Tousu"], # Tousu
    X_train["StandardDeviation"], # StandardDeviation
]
for i in range(18):
    X_train_inputs.extend([
        X_train[f"Uma{i+1}Sex"],   # Uma{i+1}Sex
        X_train[f"Uma{i+1}Blinkers"], # Uma{i+1}Blinkers
        X_train[f"Uma{i+1}Kishu"], # Uma{i+1}Kishu
        X_train[f"Uma{i+1}Chokyosi"], # Uma{i+1}Chokyosi
        X_train[f"Uma{i+1}Barei"], # Uma{i+1}Barei
        X_train[f"Uma{i+1}Kinryo"], # Uma{i+1}Kinryo
        X_train[f"Uma{i+1}Kyakusitu1"], # Uma{i+1}Kyakusitu1
        X_train[f"Uma{i+1}Kyakusitu2"], # Uma{i+1}Kyakusitu2
        X_train[f"Uma{i+1}Kyakusitu3"], # Uma{i+1}Kyakusitu3
        X_train[f"Uma{i+1}Kyakusitu4"], # Uma{i+1}Kyakusitu4
        X_train[f"Uma{i+1}Souha"], # Uma{i+1}Souha
        X_train[f"Uma{i+1}DeviationValues"], # Uma{i+1}DeviationValues
    ])

として、モデルの訓練は以前のものほぼ変わらずですが、

# モデルの訓練
from tensorflow.keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(monitor='val_rmse', patience=5, mode='min')
history = model.fit(
    X_train_inputs,  # 入力データをリスト形式で渡す
    Y_train,         # 出力データ(ターゲット)
    epochs=2000,
    batch_size=32,
    validation_split=0.2,
    callbacks=[early_stopping]
)

流石に複雑なのでってのと、何度もやるのは辛いと想像して最初からepochsを2,000にしてます。ただ、これ、ちょっとザックリと指定してしまいましたが、昨日スタートすると明らかに各epochがこれまでと違い時間が掛かります。特にプログラム的に時間表示させたりしてないので目測的に測ると4分とかです。たった今205回目が終わってました。4×205=820÷60=13.6時間です。もし、過学習でEarlyStoppingにならないと4×2,000=8,000÷60=133.3÷24=5.5日です😰

今晩も夜勤で出勤しますが、訓練が終わらない事には予測させるコーディングも進められないので今週末には間に合わない。まっ、GW中には形に出来れば...

追記 2025.4.19 18:30
少し前に675個目のepochが終わりました。TensorFlowってテキストベースでログが出て各epochの終わりに

って感じで出るんですが、地味に進んでるのでEarlyStoppingにはならないのかな? 勝手にそろそろ終わるんじゃね?っと思ったんですが地味に続くのが良いのか悪いのか😓 MicrosoftをGoogleが超えるのをマジ期待してたりもします😉
いや、AutoMLやML.NET、LightGBMはMicrosoftで、TensorFlowはGoogleって話なら...

いやね、そろそろ、いつEarlyStoppingになってもって思ってますが地味に進んでるのがepochs=2000って指定が良かったのかが...ここまで36時間以上なのでやり直しは簡単ではないので期待してる😉

追記 2025.4.19
↑の追記は間違いが多いかな。多分675/2000って出てるEpochですが、これ次に始まった奴だったと思うので、厳密には674個目の終わりだった。更にそろそろ36時間以上ではなく48時間以上だな。いや、一昨日の出勤2時間前にコーディング終えてスタートしたんでね😉 先程750個目がスタートした。

追記 2025.4.20
何が失敗っていきなり2,000epochsで行った事ですね😭 いや、実際問題としてはその位必要な感じで進んではいます。だからこそ中断という選択は無いんです。ただ、今回ってbatch_size=32で行ったんですが、こちらのPCには64GB積んでるんでbatch_size=64でも行けたかも? これ指定してれば半分で済んだかも? その辺りも後から分かったけど今更中断出来ないのが辛いです。開始してからこれに要する時間が5.5日程度と分かった時点で一旦止める判断しなかったのが悔やまれますが、これも自分の知識不足が原因として。先程929回目がスタートしました。まだ半分に届いてない。でもloss: 6.1598と確実に進んでるのは分かります。進まなければEarlyStoppingが効くはずなのでね。まいったなぁ、終わるのはこのまま行くと3.4日後って木曜日になるのかなぁorz

2025/04/15

PythonのTensorFlow

確かにこれまでの苦労がうその様に楽に動きます。C#に固執しなければ良さげですが、そうもいかないのはこれまでの蓄積したアプリの形。ここにどうにかTensorFlowのモデルを読み込んで利用出来るまでもって行きたい。

主に順位予測に対してなんですが、ここまでModel BuilderでRMSE 3.0880とか、LightGBMではRMSE 3.6735程度。これがまあ検証は出来ていませんが、RMSE 2.77とか2.78辺りが出てます。Epochsがやり始めは50だったけど、Copilotに相談しながらLightGBMでも採用している過学習させない工夫入れたり、特徴量のグループ化等取り入れて何度か500まで試してるんですが、それでもEarly Stoppingされないって事は回数足りてないので明日もう少し増やしてどこまでになるか確認します。

しかし、ここまで苦労しててもC#でモデル読み込んで予測可能にならなかったら...