2026/04/18

ひたすらコーディング

連日コーディングに明け暮れてます。最近はCopilotだけではなくGeminiも以前と比べやり取りが増えてます。そもそもどちらもAIなんですが、それぞれの企業の味が出てるって事なのかは分かりませんが、どちらも同じネット上の情報を元にしているにしては結構違いがある。

ああっ、前にGeminiに移行を考えてChromeへの乗り換えも検討した時に垂直タブ未対応で断念したChromeに確か最近垂直タブが実装されたようだけど、まだ試してないな。今はそんな余裕が無いってのもありますけどね。

モデル学習が壁にぶち当たってる感が半端なくて、まあそりゃ昨年8月辺りからだから、下手すれば1年経ってしまいそうな危機感で色々とやってきてました。で、たどり着いたのが今の作業です。ここまで基本3つの学習モデルに予想を託してきました。

  • 新馬戦用走破タイム予測
  • 通常用走破タイム予測
  • 順位予測(上記走破タイムを使用)

しかし、ここ最近ってか昨年8月から行っている作業ではRMSE 1.4の壁どころか1.45辺りにも壁が出来た感じがしてたんですよ。そこでまあ色々と試してきました。ここ最近はUbuntuでの学習とかね。でも、それも良い結果は得られず初心に帰った訳じゃないけど、そもそものAutoMLの使い方の改善。いや、実際にはML.NETのバージョンアップ関連での改善なのか改悪なのかは不明だけどRMSE 1.38とかは幻と化した。CopilotにもAutoMLの使い方の改善を相談したり、Geminiにも同様に相談した。

そんな中でGeminiがそもそも走破タイムの予測自体がAI的(AutoML)に限界まで改善はしての結果だから予測を変えた方が良いのでは?って提案をしてきたんです。それが相対タイム。その実装でひたすらコーディングしてるんですが、ここ数日、厳密には水曜日辺りからその学習用CSV出力をまず取り掛かって一応これまで同様にCKの有無で2種類。で、その過程でこれまでの走破タイム予測モデルの学習用CSV出力を参考にちょっと変えれば出来る話なのでと思いながら、でも、これってこれまで2~3時間掛かる処理だったけど、これも最適化しておいてからマネして作る方が無難と判断。巨大化した機械学習用のクラスを分割して

internal class MLTime : IDisposable

としてたクラスを

public partial class MLTime : IDisposable

として、追加するクラスを

public partial class MLTime

としながらプロパティやメソッドをそちらに分けて書く。なんとなく、自分的に分かりやすくする為に元々"MLTime.cs"ってファイル名だったので、追加のクラスのファイル名は"MLTime.SouhaCKCsv.cs"とする事で同じクラスだと認識しやすくしてみた。

で、Copilotに振り回され木曜夜は0時過ぎまで、昨夜はってか今朝方3時までやっても上手く動かず、今朝も朝からやって、いや、ほぼマジでCopilotに切れて文句言いながら修正させてたどり着いたのが15分ちょっとで2004年から2024年までの21年分出力が可能になった^^ まあ、先日もフルセットアップが改善して1時間掛からなくなったりしてたので期待はしてたけど、今朝やっとコンパイルエラーとか諸々なくなり走らせてみると終了予想が2,000分以上とかで、Copilotに「やっと走ってるっていうより、老人が歩いてるよ」って言いながらたどり着けた結果です。

自分自身はあまり理解してないけど、新たなC#の技法(いや、単に自分が知らないだけなんだが)は

// CK 用:列インデックス → SCSrc の PropertyInfo
private bool _ckMapInitialized;
private Action<SCSrc, int>[] _ckSetters;

を定義しておいて

    private string ColumnToProperty(string col)
    {
        // 例: "D2428C1" → "D2428Chaku1"
        //     "J01T3"   → "J01TChaku3"
        //     "KT5"     → "KTChaku5"

        // 数字部分を抽出(末尾の数字が着順)
        int len = col.Length;
        int pos = len - 1;

        // 末尾の数字を探す
        while (pos >= 0 && char.IsDigit(col[pos]))
            pos--;

        // col = prefix + number
        string prefix = col.Substring(0, pos + 1);   // 例: "D2428C"
        string numStr = col.Substring(pos + 1);      // 例: "1"

        // prefix の末尾が 'C' なら削除(D2428C → D2428)
        if (prefix.EndsWith("C"))
            prefix = prefix.Substring(0, prefix.Length - 1);

        // プロパティ名は prefix + "Chaku" + number
        return prefix + "Chaku" + numStr;
    }

これはまあ特になんじゃなく単に今回のASでの定義してる名前を処理しやすくする為とかでCopilotに言われて入れたけど、実際の使い方は

    private void ReadCkStats(DateTime kaisaibi, string jouCD, byte rno, long ketto, ref SCSrc scSrc)
    {
        _cmdSTCK.Parameters["@KAISAI"].Value = kaisaibi;
        _cmdSTCK.Parameters["@JOUCODE"].Value = jouCD;
        _cmdSTCK.Parameters["@RACENO"].Value = rno;
        _cmdSTCK.Parameters["@KETTO"].Value = ketto;

        using var rd = _cmdSTCK.ExecuteReader();
        if (!rd.Read())
            return;

        // ★ 最初の1回だけ setter delegate を作る
        if (!_ckMapInitialized)
        {
            int count = rd.FieldCount;
            _ckSetters = new Action<SCSrc, int>[count];

            var scType = typeof(SCSrc);

            for (int i = 4; i < count; i++)
            {
                string col = rd.GetName(i);
                string propName = ColumnToProperty(col);
                var prop = scType.GetProperty(propName);
                if (prop == null)
                    continue;

                // setter メソッドを取得
                var setMethod = prop.GetSetMethod();
                if (setMethod == null)
                    continue;

                // ★ setter をコンパイル済み delegate に変換
                var setter = (Action<SCSrc, int>)Delegate.CreateDelegate(
                    typeof(Action<SCSrc, int>),
                    null,
                    setMethod,
                    throwOnBindFailure: false
                );

                if (setter != null)
                {
                    _ckSetters[i] = setter;
                }
                else
                {
                    // short プロパティ用のラッパー
                    if (prop.PropertyType == typeof(short))
                    {
                        _ckSetters[i] = (sc, v) => prop.SetValue(sc, (short)v);
                    }
                    else
                    {
                        _ckSetters[i] = (sc, v) => prop.SetValue(sc, v);
                    }
                }
            }

            _ckMapInitialized = true;
        }

        // ★ 以降は delegate 呼び出しだけ(超高速)
        for (int i = 4; i < rd.FieldCount; i++)
        {
            var setter = _ckSetters[i];
            if (setter == null)
                continue;

            int value =
                rd.IsDBNull(i) ? 0 :
                rd.GetFieldType(i) == typeof(int) ? rd.GetInt32(i) :
                rd.GetInt16(i);

            setter(scSrc, value);
        }
    }

こんな感じらしい。これ見てもあまり理解してない自分が情けないけど^^;

0 件のコメント:

コメントを投稿