まだタイトルない

アウトプット用です

AHC025 ふりかえり!

こんにちは、AtCoder Heuristic Contest 025 が 2023-10-14(土) 12:00 ~ 2023-10-22(日) 19:00 で開催されていたので参加してきました

暫定ですが結果はこんな感じ。AHC024についで2回目の参加となり、初めての長期コンテストでしたがそれなりにやれたのではないでしょうか・・・?

考えたことや考察を時系列に書いていきます。考察があってない可能性もありますがご了承ください。

課題の確認

詳細はcontest pageで確認ください。

  • 与えられたN個のアイテムをD個のグループに分ける
    • Dこのグループの重さが均等になるように分割する
  • それぞれのアイテムの重さはわからないが、天秤があるので、どっちが重いかの情報を得られる
    • 天秤を使える回数はQ回まで
  • アイテム数Nは30から100で一様ランダムに生成される
  • 分割数Dはrand(2,floor(N/4))で一様ランダムに生成。30なら2~7、00なら2~25
  • クエリ数Qは2N~32Nで一様ランダムに生成
  • 生成されるアイテムの重さはλ=10^-5の指数分布
    • ほぼ一様ランダムと言える(多分)
    • 上限は決まってる

考察

※あくまでcontest2回目の私がポイントだと思った要素

  • アイテム1が10gの時、アイテム2が11gでも100gでも天秤の出す答えは変わらない
  • N個のアイテムの中でどれが一番重いか、どれが一番軽いかはそれぞれ(N-1)回比較すればわかる
    • クエリ数の条件(2N以上)から一番重いものと一番軽いものを探すことができる
      • ただし、それを求められていないしクエリ数の無駄
  • 評価指標がMSE(機械学習コンペでよく見るやつだ!!!)なので大きく平均を外すと悪化する

結構ドヤ顔で「ああ、大きく外さないようにする方針でアプローチするのね」となってました。

とりあえずランダムにグループ分けする

「うげーインタラクティブ形式じゃん、えぇっとABCのインタラクティブ用のコードを持ってきてと・・・」

ぐぬぬ・・・・・洗礼を受けました。

なんだかんだして通します。

N, D, Q = map(int, input().split())
ans = [i%D for i in range(N)] # random split
for i in range(Q):
    # Interactive
    print(1, 1, 1, 2, flush=True)
    ret = input()
print(*ans)

クエリーは絶対Q回送らなければいけないので、無駄に消費します。

LB2427m,Local2602m

seed21でこんな感じになります。

ローカルでテストできるようにする

エラーでたりしても30分提出できなくなるので、ローカルでテストできるようにしました。

参考サイト

seed100個分回せるようにしました

ランダムにグループ間の移動をする

code

  • ランダムに2グループ選ぶ
    • ここで一回秤を使う
  • 大きい方のグループからランダムに一つ小さいグループに移動させる
    • これを繰り返す
  • 大きいグループが入れ替わったら操作をやめる

これでLB2142m Local2100mくらいまで微改善しました

このシードに関しては改善されてないような・・・・?

移動した時大小関係が変わったら戻す

移動したときに2つのグループの大小関係が変わらない場合は明らかにスコアは改善されます

ただし、移動後に大小関係が変わった時に関してはスコアが悪化している可能性があります。それを考慮漏れしていたので、大小関係が入れ替わったらその移動を無効にするようにしました。

    # G1からG2に移動させる
    if len(dict_idx[g1]) == 1: continue
    while remainQ:
        item = random.choice(dict_idx[g1])
        dict_idx[g1].remove(item)
        dict_idx[g2].append(item)
        ret = compare(g1, g2)
        if ret == ">":
            ans[item] = g2
            if len(dict_idx[g1]) == 1:
                break
        elif ret == "=":
            ans[item] = g2
            break
        else:
            dict_idx[g1].append(item)
            dict_idx[g2].remove(item)
            break

この変更でscore503m, local400mにかなりの改善が得られました

だいぶ見た目も良くなりましたが、左から2列目とかに課題を感じますね

何回か実験に失敗

  • 残りのquery回数が300回以上の時50%の確率で大小が切り替わった移動を戻さない
    • 焼きなまし的に、たまに悪化も許容したらどう?という狙い
    • 300は適当
    • 改善なし
  • 指定回数を使ってお土産の重さを降順に取得して、残りの数で4回目までの入れ替え動作を行う

初期配列をスイスドローでやる

↑で初期配列を多少の大小関係考慮したものにして失敗したんですけど、「もう少し少ない回数で大体の大小関係理解るとうまく言ったりするんじゃない?」ってことでスイスドローしてみることにしました。

シャドバやってた経験が生きましたね。

スイスドローをすると

  • N個に対して、高々ceil(math.log2(N))ラウンドで1番重いものが分かる≒大体の重さ順が分かる
  • 100人だと7回戦、350試合、3.5Nなので、クエリ数の条件によって足りないこともあるが50%以上の確率で7回戦出来る

余談ですが

  • スイスドローの文脈で色々調べてたらサイクリック計画とスイスドローのいいとこどりをした手法の論文とか見つけたけどやり方を読み取れなかったです(link)
  • サイクリック計画自体もはN人の順序付けをN回で実現させるらしいんですけど、計算方法がわからんかったです

閑話休題

スイスドロー組み込んだcode

def init_swiss_groups(round:int):
    rank = [[0,i] for i in range(N)]# [-勝利数, 選手の番号]のリスト
    for _ in range(round):
        for i in range(N//2):
            p1 = rank[2*i][1]
            p2 = rank[2*i+1][1]
            ret = compare_single(p1, p2)
            if ret == "<":
                rank[2*i+1][0] -= 1            
            else:
                rank[2*i][0] -= 1
        # 奇数対策
        if N%2 == 1:
            rank[-1][0] -= 0.5
        rank.sort()

    omiyage = [i for _, i in rank]
    swiss_score = dict()
    for item in rank:
        swiss_score[item[1]] = item[0]
    ans = [-1]*N
    tmp = (list(range(D)) + list(range(D-1,-1,-1)))*50#最低でも100にする
    tmp = tmp[:N]

    for i, idx in enumerate(omiyage):
        # ans[idx] = i%D
        ans[idx] = tmp[i]
    return ans

LB 423m local388m で実際はあまり変化なかったけどなんとかく気持ちいいので採用

移動させる条件の改善

  • 大きい方からランダムで一個取り除いく
    • 大小が変わらなければ小さい方に移動させる(明らかに部分最適

つまり、この場合これまで使ってた条件だと一番上にある茶色の移動は成功しないのですが、移動させたほうが得点は高くなるのでそれが実現できるような条件に変えました。必要な秤の使用回数に変化はありません

code

LB 200m local 176m まで改善できました

飛び出た2列分は明らかに過大なんですけど解決方法はわかりません・・・

スイスドローしたときの結果を使ってswapさせる

  • これまでは1個を何処かに移動させる方法で均等なグループを作ることを目指してましたが、ちゃんと交換できるなら強いはずです。
  • n:mで交換できればいいですがコストがかかりそうなので1:1での交換を指せる方法を考えていきます。
    • 移動と同じようにランダムに2グループ選んで条件をしっかり決めて交換をしましたがあまり良くなかったです
      • (後から実装見たらバグがあったので多分これでも少し良くなる)
  • ランダムにグループを選ぶのでなく、すでに比較した結果を使って交換間できないか考えます。
  • これはスイスドローの結果をメモを活用できます

  • 図において、(はっきり視認できませんが)a>bであり、a,bを除いたそれぞれのグループG1,G2の関係がG1>G2なら、交換するとスコアは改善されます。
  • 直感ですが、移動させるほうが優先してするべきな気がしたので、移動をする→swapを探して最終調整みたいな順序にしました。
def swap(ans:list, comp_result:list, dont_move_idx:set)->list:
    idx = 0
    mod = len(comp_result)
    dict_idx = defaultdict(list)
    for i, v in enumerate(ans):
        dict_idx[v].append(i)

    while remainQ:
        a, b=comp_result[idx]#ここで a>b を満たす
        idx = (idx+1)%mod
        if (a in dont_move_idx) or (b in dont_move_idx):
            continue
        g1, g2 = ans[a], ans[b]
        if g1 == g2:continue
        if len(dict_idx[g1]) == 1 or len(dict_idx[g2]) == 1:
            continue
        dict_idx[g1].remove(a)
        dict_idx[g2].remove(b)
        if compare_group(g1, g2, dict_idx) == ">":
            # 交換確定
            dict_idx[g1].append(b)
            dict_idx[g2].append(a)
            ans[a] = g2
            ans[b] = g1
        else:
            # 戻す
            dict_idx[g1].append(a)
            dict_idx[g2].append(b)
    return ans

code全体

これをするとLB143m local 120m くらいまでいけました(思っていた以上に効いた

ビジュアルでも良くなってますね

最終

Qの使い道をどの処理にどれくらいという微調整して最終的にこれくらいになりました

提出Code

n:mの交換の実装とかまだできることはありそうですが今回はここまで!

お疲れ様でした!

【AHC始めました】AHC024参加レポート

こんにちは、そろそろAHCの色も欲しいなーって思ったので参加してきました。

基本的に答えがある方が好きなのでAtCoderではこれまでアルゴリズムの方をやってました。

今回は参加レポートということで時系列で思ってたこと考えてたことをつらつらと

問題の理解

隣接関係を崩さないようになるべく少ないマス数で表現する

  • 明らかに削っていいマスはわかるけどどうやってプログラムでやればいいかわわからない
  • ダウンロードしたツールもよくわからない
    • 昨日Introduction to Heuristics Contestで少し予習したときはtester.pyがあったけどそれがない
      • →「あーこれ自分で評価関数的なやつを作らなきゃいけないのか、隣接情報とかよくわからないし作るの無理やな!」

行を間引いてみる

  • 上から一行ずつ見て、下の行と全く同じ行は削除する。[code]

え???

ビジュアライザを見てみたらエラーが出てました。よく見たら出力行が1行足りなかったみたいです。

直して提出してみる

まだだめ

行単位で消すなら下の行が同じなら消していいは間違いで、上下の行が同じなら消していいが正しいです。

直して提出[code]

150点になりました!

ただこれはぬか喜びで、ルールに"作成した地図に含まれる色 0 のマスの総数を E としたとき、E+1 の得点が得られる。"とあるので、つまり0点です。

ならば、初見で感じた明らかに消して良いマスを消す実装をするしか無いです。

凸を消してみる

といっても、腰が重いので、凸部分を消せば、行単位の削除できるんじゃね?

この真中部分を青に揃えるコードを実装してみる→150点から変わらず

消していい場所を消す実装をする

code

  • 外周に0の番兵を置く
  • 自信含む周囲9マスの色の数が2色以下かつ0があれば消していい

(´・ω・`)

手元のサンプルでダメなやつを探してみる

左下が途切れてます。

さてどうしよう?

BFSを使う

code

  • 組み込むのに40分くらいかかりました
  • 事前にdefaultdictで色番号ごとの連結数を記録
  • 消した後、周囲の消した色のマスの座標を取得し、連結数をBFSで数える
    • bfsは計算量高々50**4(ほんと?)なので動くと判断
  • 非連結になってたら戻す

良さそう

スコアが出た!

これに凸を消すロジックを入れたら微改善しました。

地図の形状ごと変えに行く

  • 後なにできるかを考えると、もう中の方の色を圧縮するしか無いんですよね・・・・
  • でも何らかのプログラムでできる気がしない
  • とりあえず色をできるでけ右に伸ばせば、これまでの手法で削れるマスが増えるかも知れない
# 右に進撃するコード
for _ in range(2):
    for i in range(1,N+1):
        for j in (range(1, N)):
            color1, color2 = C[i][j], C[i][j+1]
            if color1 == color2:
                continue
            _, s1 = bfs(C,(i,j))
            _, s2 = bfs(C,(i,j+1))
            C[i][j+1] = color1
            d[color1] += 1
            d[color2] -= 1
            _, s1_2 = bfs(C,(i,j))
            i2, j2 = get_around_color(C,i,j+1,color2)
            n, s2_2= bfs(C,(i2,j2))
            if (s1 != s1_2) or (s2 != s2_2) or (n != d[color2]):
                C[i][j+1] = color2
                d[color1] -= 1
                d[color2] += 1
  • こんな感じに隣接情報が崩れないようにbfsを酷使しながら色を右に伸ばしていきます[code]

もう私としては上出来です

長くのびてる部分を消す処理を追加

愚直に無駄な部分を消してみます。

これで61606 から 99053に改善

右に進撃するコードを繰り返す

見た感じ何回か繰り返すことに意味はありそうなので、時間にも余裕があるということで処理を増やします

めちゃくちゃ潰せたww

これで最終スコアは166260まで伸ばせました!

時間も着てここで終了

結果

水パフォで入茶しました!とりあえず水色目指してみようかなと思います!

おわりに

  • 予習として何個か資料を読んで焼きなましだったりビームサーチというアプローチがあることは知ってました
  • が、本問題を見て適用方法が全く分からず、貪欲?思いつくままのアプローチで攻めました
  • アルゴリズムやってたおかげでBFSを使ってかなりスコア改善ができたと思います。
  • 今後水に向けて振り返りをしたり勉強をしたりしていくわけですが、どこまで振り返ればいいかがアルゴリズムと違って難しいように感じてます
  • コンテスト中の取り組み方も色々改善できるところは多そうなので引き続き頑張ります!

Stable Audio で吹奏楽の楽器をいろいろ生成して遊んだ!

こんにちは 音声生成モデル来ましたね。

ja.stability.ai

www.stableaudio.com

ユーザーガイドをみるとPromptと生成された音声が公開されています。

雰囲気や使用楽器、テンポやジャンルが指定できそうな雰囲気です。

既にギターやドラム、ベースなどはサンプルであるので、今回は吹奏楽で使われる主要な楽器をpromptに入れてどれくらいそれっぽく作れるのか試してみようと思います

Promptは trumpet solo, happy, 156 BPM こんな感じに指定した楽器のソロにhappyを添えてテンポも指定する感じで行きます。

1. トランペット

流石に誰でも知ってるだろうラッパといえばこれという楽器ですね。吹けるようになりたい楽器の一つです。

↓の出だしです。
アニメ『響け!ユーフォニアム』洗足イベント(夜) 松田彬人 / 三日月の舞 - YouTube

それっぽいと言えるのではないでしょうか

2. トロンボーン

これも有名だと思います。吹奏楽だと大体トランペットが左側に対して右側にいます。

コレジャナイ感

3. ホルン

あまり認知度は高くないかも知れないですが大事な金管楽器です。宝島のホルンのグリッサンドは本当にかっこいい

挑戦してみたい楽器の一つです。

↓これの後ろの方で響いてる音です(ツタワレ
和泉宏隆 / 宝島 (指揮:真島 俊夫) - YouTube

さて、生成されるでしょうか?hornだと伝わらなそうなので french hornとしました

ホルンもだめですか・・・

4. ユーフォニアム

響け!ユーフォニアムのそれです。

知識不足でこれが有名とかあまり言えないです。。。

絶対知らないですよね?????

5. チューバ

金管楽器で低音を務めるでかいやつです

ちょっと変だけど音はトランペットとかより近いのではないでしょうか


ここまでは金管パートでした。

金管八重奏の下記楽曲では上記5つの楽器で構成されていて、ユーフォもチューバもどんなことやってるかわかると思います!
文明開化の鐘 - YouTube

木管にいきましょう


6. サックス

ジャズとかでよくメロディ弾いてると思います。見た目も音もかっこよくて高校の吹奏楽部に入部したときに第1希望にしました(だめだった

↑の宝島の4:00くらいからソロをしてます。

知ってる感は出てるけど許せない人はいそう

7. フルート

フルート実は木管なんですよね。意外と単独での音を聞いたことあってどんな音か知ってる人は多いのではないでしょうか?

結構いいと思います! (高音頑張れって感じの音ですね

8. クラリネット

吹奏楽だと前の方に座ってるリコーダーみたいな楽器です。バスクラとか色んな種類がありますがどのクラリネットも温かい音で私は大好きです。

やってみたいけどリードミスの目立ち方がやばいので演奏会で吹くのは怖いです。

↓最近だとルイージマンションのBGMのメロディーがクラリネットだった気がします。
ルイージマンション2 HD [Nintendo Direct 2023.9.14] - YouTube

クラリネットの雰囲気は知ってそうな雰囲気ですね。余裕があればバスクラとか試したい

9. オーボエ

クラリネットっぽい見た目だけどリードの形が違います。値段が高いし難しいというイメージで、私の所属してた部活は所有してませんでした。

このオーボエファゴットはよくジブリのBGMでメロディやってます。

ユーフォのスピンオフのリズと青い鳥とか、のだめカンタービレオーボエ協奏曲とかあるので意外と馴染み深いのではないでしょうか

しらなそう・・・

10. ファゴット

知名度的には一番悲しいかも知れませんが上にある通り絶対ジブリで聞いてる音なんです。

ユーフォでもクローズアップされてない・・・

英語だとBassoonです。

全く知らないことはなさそう!!!

11. コントラバス

吹奏楽では珍しい弦楽器。オーケストラに入っても一番大きい弦楽器です。

私は学生時代コントラバスをやってたので当たり前に知ってますが、人に伝えるときはいつもチェロだと思われます。

ところでユーフォの緑ちゃん、中学時代全国金のコンバス奏者だし、作中でも技術的な壁にぶつかってないので隠れ実力者なんですよ(早口

いいんじゃない?学習データにジャズでもたくさんあったのでしょうか?双方もピチカートの方ですし

12. マリンバ

パーカスからも何かということで。独断と偏見で音が好きなマリンバを選びました。

↓参考
【マリンバ演奏】リトルマーメイドより「アンダザシー」The Little Mermaid〜Under the Sea〜 - YouTube

いい感じに表現できてて嬉しい気持ちになりました!

おわりに

みんな響け!ユーフォニアムを見よう!!!!

最後に最近ハマってる楽曲2つおいて終わります

イーストコーストの風景
youtu.be

ユーフォ原作のコンクールで演奏した楽曲

オーメンズ・オブ・ラブ
youtu.be
名前は知ってたけど先日の映画のクレジットまで気づかなかったハズカシイ

※各楽器の説明は個人の認識に基づいて記載したためなにか間違いがありましたらご指摘ください。

最近いろいろアニメを接種した話

最近、たくさん(当社比)のアニメを視聴しました。都度、思ったことをツイートしていますが、これほどの視聴量は珍しいので、まとめてシェアしていきたいと思います。

結論とかは無いです。

ぼっち・ざ・ろっく

バンドアニメはけいおん至上主義 + キャラデザに惹かれない = 視聴しない
だったのですが、
たまたま暇だった + 途中まで友人と見てた = まぁ、見るか
ということで見ました。結論としては比べるものじゃないしどっちもそれぞれいい!
最初のライブハウスライブで感極まってないた。これを見てから結束バンドのアルバムを1ヶ月以上ヘビロテした。

五等分の花嫁

Huluにrecommendされたので見た。

以前感想書いたので改めて下記はしませんが、原作、アニメ1期、2期、∽を2周しました。映画は切ないのでなかなか2回目に手を付けれていません。四葉風太郎のテストを渡しに行ったシーンの四葉視点は泣ける

誰かを選ぶなら3ですがまんまと箱で推す人間になりました。特に∽の5の「隠し撮りですよねぇ」、「本当に何してたんですか」、「終わった」あたりが良かったです。

響け!ユーフォニアム

2015年春、2016年秋とやってましたね。響けシリーズは一貫して人に薦めてるので今に始まったことではないけど、最近新作映画がやってて気付いたら2回見に行ってました。
これきっかけで原作を今読みすすめてます。なんと原作は関西弁。結構比喩表現豊かな作品です。
アニメでは大体コンクールのシーンで泣く。

原作とアニメでコンクールで演る曲が違うのですが、原作読みながらその時やってる曲を聞いてたらその曲にハマりました。吹奏楽はいいぞ

3期が楽しみです!

やがて君になる

たまたま薦められたので見ました。2018年秋にやってたみたいです。このクールのラインナップを見ると『色づく世界の明日から』、『青春ブタ野郎はバニーガール先輩の夢を見ない』は見てたけど『ソードアート・オンライン アリシゼーション』、『転生したらスライムだった件』あたりは離脱したりとアニメ熱が冷えてた時期な気がします。あとPVみて自分から見に行くようなジャンルではないんですよね。

アニメ見終わって我慢できなくて5-8巻を買って2周しました。漫画の絵が可愛かった。結構セリフが少なめで、絵で伝える形を取ってるシーンが多いことが印象的でした。アニメ化された部分の漫画は読んでないですが、丁寧に肉付けしてアニメ化されてたんじゃないかなって想像できました。尊い作品でした!

あ、死を連想させてくるのやめてほしい()

四月は君の嘘

2014年冬クールのアニメ。リアタイで見てた作品です。
当時確か原作の完結に合わせてアニメも終わらせるようなやつでしたよね?(ハガレン2回目のアニメ化もそうだった気がする)
名作だった記憶は残ってるけど内容はあまり覚えてなかったです。今回オンデマンドで配信されたということで見ました。(物語の結末を覚えるのが結構苦手です・・・

原作もそうかわからないですが回想が長かったりとこっちとしては理想的でないテンポだったような気も・・・?17-18話くらいの相座凪が頑張ってるあたりで一回泣きました。最終回、涙枯れちゃったかなーと思いながら見てたら最後に泣きました。王道とも言えますが後から知る系のものは泣けますね。

あと、

バトル系は特に語るものはないですが最近だとBLEACHとか呪術みてます

雑談

私のアニメへの興味は、おそらくドラえもんちびまる子ちゃんといった国民的アニメから始まったと思います。それらのアニメから少年漫画のアニメにも段々と興味を広げていきました。ただし、テレビ愛知が受信できなかったため、視聴できる作品には制限がありました。『うえきの法則』、『シャーマンキング』、『BLEACH』などは視聴できませんでした。

そして、深夜アニメの世界に足を踏み入れたきっかけは何だったのか、と考えると、夜更かししてテレビつけていたとき、アニメが流れてきてそれを見た。だと思います。おそらく私の深夜アニメのルーツは『のだめカンタービレ』、『おおきく振りかぶって』などだったと思います。

そこからさまざまなアニメを見るようになり、一番熱中していた時期では、1クールで10本ほどのアニメを同時に見てたと思います。京アニとか特定のアニメ制作会社の作品なら絶対見るとかしてました。ニコ生のらき☆すた一挙とかなつかしい。

ここ数年その熱も冷えてきてあまり見てなかったという体感で、視聴履歴を見たらたしかに少なかった。

物語シリーズとか、SAO大好きだったんですけど途中から終えてないです。

ところで

こんなにアニメにハマっちゃうのって本能的になにかから逃避してるのでしょうか・・・・?

ICRの上位解法を眺めた

1st

  • score
    • public 0.14445
    • private 0.30626

「本当に予想外でした。上位10%に入ることを望んでいましたが、それ以上のことは夢にも思っていませんでした。」

  • DNNを使用
    • GBDTは明らかに過学習だったので非採用
  • greeksは未使用
  • FEは過学習に繋がった
  • DNN based on Variable Selection Network(論文)
  • 正規化はMinMaxScalerやStandartScalerでなく、8つのニューロンを持つ線形射影
  • 最終的に確率を重み付けし直すと、とてもうまくいった。
  • cvは機能していた
    • 10foldで10-30回学習し、cvを元に各foldで裁量のモデルを2つ選ぶ
    • cvは0.25-0.05まで変動するほど不安定だった
  • 予測しにくさを表現する新しいラベルを作成
    • y_true = 1かつy_pred < 0.2、y_true = 0かつy_pred > 0.8 を1
    • それ以外はラベル0
    • このラベルはいいCVを切るために使用、scoreは0.02向上

2nd

  • score
    • public 0.18941
    • private 0.32586
    • CV 0.21996

「控えめに言って、私は完全にショックを受けています。今日朝起きてチームメイトにおめでとうと言われましたが、何位になったのか興味が湧きました。 それが2位だったのを見たとき、私は大笑いしました。」

  • model
    • Catboost, XGBoost, TabPFN
    • lightgbmはよくなかった
  • 前処理
    • greeksを使ってtimeを特徴量に加える。testにはgreeksがないのでmax(time)+1を使用
    • timeが存在しないtrain行は削除して学習(全体の23%らしい)
      • umapで他のデータから離れたデータにtimeが存在しなかったから
    • umapで次元を減らし、kmeansでクラスタにラベリングする
    • feature permutationで特徴量選択
    • nanは-100で補完
  • 4fold

3rd

  • score
    • public 0.21545
    • private 0.33974
    • CV 0.24217

「このような結果を残せたことに本当に驚きました。実は、このコンペティションに参加し、ベースライン・コードを実装しただけで、普段は残業が多い会社だったので、それからこのコンペティションに取り組むことはほとんどありませんでした😅。」

  • model
    • catboost
    • lightGBMはよくなかった
  • 前処理
    • 全特徴量ペアの比率を特徴量を作成 約1500個

4th

  • score
    • public 0.16087
    • private 0.34077
    • CV 0.03346?

「リーダーボードで大Shakeを目の当たりにし、その結果、私がLBの一番上に行くことになり、とても驚いている。」

  • model
    • CatBoost(ハイパラチューニングなし
  • 処理
    • 特徴量のギャップを再帰的に埋める
      r = CatBoostRegressor()
      is_null_c_train = train.columns[train.isnull().any()]
      is_null_c_test = test.columns[test.isnull().any()]
      nulls = list(is_null_c_train.append(is_null_c_test))
      for c in nulls:
          null_c_train = train[c].isnull()
          null_c_test = test[c].isnull()
          except_c = [x for x in train.columns[1:-1] if x not in [c]]
          r.fit(train.loc[~null_c_train, except_c], train.loc[~null_c_train, c], verbose = False)
          if len(train[null_c_train]) > 0:
              train.loc[null_c_train, c] = r.predict(train.loc[null_c_train, except_c])
          if len(test[null_c_test]) > 0:
              predicted = r.predict(test.loc[null_c_test, except_c])
              if c == 'EJ':
                  predicted = np.round(predicted)
              test.loc[null_c_test, c] = predicted
    
    • greeks['Epsilon'] Unknown は greeks['Epsilon'].min() で埋める
    • 'Alpha'、'Beta'、'Gamma'、'Delta'の各値に対するCatBoostClassifierの予測値を特徴量として使用
    • イプシロンでソートしたときの行番号を使用
      • 特徴量重要度が4番目に高くなった
      • public,privateともに0.03ほど改善
  • 5fold

5th

  • score
    • public 0.20333
    • private 0.34286
    • CV 0.21979

「私は数ヶ月前に基本的なソリューションのsubmitを提出し、コンペティションのことは忘れていた。朝、友人たちが祝福してくれたときには驚いた。」

  • 各α、β、γ、δのモデルを訓練し、これらの確率を積み重ねて特徴量として使用。
  • 各特徴量について LightGBMで imputer model を作成
    • これがなくてもそこまでスコアに影響はない
  • RepeatedStratifiedKFold(n_splits=5, n_repeats=5)と基本的なcatboostモデルを使用。

6th

  • score
    • public 0.206555
    • private 0.34696
  • model
    • XGBoost
      • 複数のパラメータで学習し、アンサンブル
  • pandasの'interpolate’で欠損値補完
  • ランダムフォレスト、gini-importance'を使用してデータセットで最も重要な特徴を探す

7th

  • score
    • public 0.18113
    • private 0.34752
    • CV 0.18034?

コンペティションの序盤にいくつかサブミッションをした後、大きなShakeが予想できたのでそれ以上取り組まなかった。」

  • Fill Nan data with 0
  • 5 fold Multi Label Stratified using Greeks Values
  • EJ was categorical so used Label Encoding
  • Also Label Encoding for Beta, Gamma and Delta
  • sed MultiClass CatBoost Classifier for all the models
  • ベータ、ガンマ、デルタを予測するモデルを作り、特徴量として使用

8th

  • score
    • public 0.19
    • private 0.34

「率直に言って、この結果には多少ショックを受けた。大混戦が予想されるとは思っていたが......トップ10に入るとは思っていなかった。」

  • model
    • XGBoostとTabPFN
  • ある人がクラスよりも特定の年齢関連状態にあるかどうかを予測することに重点を置いた
    • クラスだけを予測するよりも効果的だった
  • 欠損値埋めはinputeモデルを作成して埋めた (public +0.04 private+-0
  • 特徴量は特に作成しなかった
    • 特徴量選択はimportanceをもとに実施
    • ※すべての特徴量を使ったもののほうがprivateは良かった
  • alpha(class), beta, delta, gammaを予測するモデルを作ってアンサンブル

9th

「これは本当に予想外だった。銅メダルすら考えてもいなかったのに、9位でここにいる!」

part1

  • model
    • XGBoostとTabPFNのアンサンブル
  • データ分布のバランスをとるためにオーバーサンプラーを活用
  • テストセットにEpsilon.max() + 1のギリシャイプシロンを使用
  • SimpleImputer with the strategy set to 'constant'.
  • 5-fold

part2

  • model
    • XGBClassifier, LGBMClassifier
  • イプシロン不使用
  • 特徴量のスケーリングを実施
  • 特徴量選択を実施
  • 15-fold

おわりに

Light GBMは微妙。foldも20まで行かないで5-10, greeksのΒ,γ,δを予測して特徴量にする方針は多い気がします。(が、その高いソリューションでもみんなやってる可能性はあります・・・

皆さんover fittingに気を使ってはいますが、その上での運ゲーだったのかは私にはよくわかりません。

また、私が英語に弱いため、情報が正しくなくなっているかもしれません。その場合はリンク先のkaggle discussionをご参照ください。

yukiCup2023Summerに参加しました

内輪で集まって(オンライン)データ分析コンペで戦いましょう的なものに参加してきました。

企画ありがとうございました!

結果は15/42でした悔しい。

使ったコードはコチラ

いつもちゃんとデータを見て仮説を立てて実験して結果を見て改善させていきたいっていう理想は持ってるのですが、 今回も結果的には思考停止でやっちゃったことが多かったというところが個人的な反省です。

考えて取り組んだポイント

  • 目的変数の裾が長かったから対数変換する
    • しないほうがよかった
      • +500したら効いたらしい
  • trainデータを価格順に並べて bike_title の1単語目が特徴になりそうだったので抽出した
  • 学習データを増やしたかったのでfold数を増やした

作業的にとりあえずやった部分

#15 atmaCup 振り返り

この記事は五等分の花嫁のネタバレを含みますので未見の方はご注意ください。

こんにちは

atmaCup15がありましたね。

結果は82位でした。

atmaCupは課題内容の評判がよく、今回もきっとしっかり課題を理解して有効なアプローチを取った人がスコアを伸ばしてるんじゃないでしょうか?上位を見るとkaggleGMもちゃんといます。

それだけにこの結果はシンプルに凹むものがあります。悔しいです・・・

自分の上の81個のソリューションが出てくるため、簡単に振り返ります。

簡単に振り返り

時系列でいきます。

  • 最初に基本的なラベルエンコーディングやワンホットエンコーディングをしました。CV1.3999 LV 1.4006
  • useridを使ったターゲットエンコーディング CV1.2564 LB: 1.2781
    • 実装はfold情報を見てfold0のデータの値はfold0以外で計算するような実装です
    • やはりターゲットエンコーディングは正義ですね。(ただあんまり好きな手法じゃないんですけど気持ちわかる人います?
  • ユーザー単位、ジャンル別アニメ視聴数特徴量追加 CV 1.22183 LB:1.2397
    • 結構伸びました
    • このあたりからseen userとunseen userのCVをチェックするようにしました(有効活用できたとは言ってない
  • アニメの特徴量内で演算
    • DataRobotおじさんによる分析にあった割り算の特徴量で伸びました
  • scikit-learnのTarget Encoding totalCV 1.1677 seen 1.1594 unseen 1.2030 LB 1.2300
    • scikit-learnで内部でfoldきってくれるターゲットエンコーディングが実装されてることを見つけたので使用
    • CVが改善したものの明らかにリークしてそうなので今後のテーブルコンペでどう付き合っていくか結構悩みます
    • CVとLBのギャップは大きくなったけど相関は割と取れてたのでどうなんでしょう?
  • implict w2v explict w2v
    • これで256次元増えます
    • 重要度ランキングには入ってきますがCVもLBもあまり変わりませんでした
  • optunaで調整
    • 寝てる間に調整
  • transformerに挑戦
    • hemunistさんが公開してくれたnotebookを使用。
    • CVが悪くなかったのでここにターゲットエンコーディングなど特徴を加えたらいいアンサンブルの種になるのじゃないかなと思ったのですが、subのスコアが0.5くらい悪化してしまいました。
      • testの並びが変わっちゃうのかな?と思ったのですが謎の解決はできませんでした。(皆さんは動かせましたか?
    • ということでtransformerモデルを手札に入れるのは諦めました・・・
  • userでgroupKFoldして学習したモデルで未知ユーザーをアンサンブルしようとしたけどCVが悪すぎてこれも諦める

.

  • 結局いい感じのLightGBMの複数Seedアンサンブルで終わり。もうちょっと色々頭使ったことしたかったです。
  • また、ユーザーごとに10点をつけた作品がn個、アニメごとに10点は何人からみたいな特徴も考えられますが、これまたリークに配慮が必要で結局作らなかったです(何が正解かわからない

また復習頑張ります!!!

Targetencodingについて

リーク対策でまずこんな実装を使っていました

# Target Encoding
# ===========================================================================================
cols = ['user_id','anime_id']
cols_name = ["TE_" + s for s in cols]
for c, name in zip(cols,cols_name):
    arr_mean = np.repeat(np.nan, len(df_train))
    for val in range(CFG.n_fold):
        val_idx = df_train['fold'] == val
        df_agg_mean = df_train[~val_idx].groupby(c)['score'].mean()
        arr_mean[val_idx] = df_train[val_idx][c].map(df_agg_mean)
    df_train[name] = arr_mean
    df_test[name] = df_test[c].map(df_train.groupby(c)['score'].mean())
    CFG.feature_cols += [name, name+'_max', name+'_min']
    # fillna ちょっとリークするかも
    df_test[name] = df_test[name].fillna(df_train[name].mean())
    df_train[name] = df_train[name].fillna(df_train[name].mean())

もう一個 scikit-learnだとこんな感じ ドキュメント 内部でfold切ってくれたり、smoothingしてくれます。

t_enc = preprocessing.TargetEncoder(smooth="auto",target_type='continuous', cv=5, shuffle=True, random_state=29)
# Encodingするカラム配列。複合カテゴリは自分で作る必要がある
feats = ["user_id","anime_id","type","source","genres","bin_members"]
col_names = ["sk_TE_"+ s for s in feats]
# fit_transformでcv切ってエンコーディング
df_train[col_names] = t_enc.fit_transform(df_train[feats], df_train["score"])
# train全データの平均を使える
df_test[col_names] = t_enc.transform(df_test[feats])
CFG.feature_cols += col_names

こっちだとCVがLBと比べてちょっと良くなってしまいます。コードは短いほうが好きなのでこれが使えるといいんですけどどうでしょう・・・?

五等分の花嫁が良かった

atmaCupが始まるちょっと前から五等分の花嫁のアニメを見始めました。昔コミックスを1冊読んだときはそれ以降続き読んでなかったんですけど、気づいたらみるペースが上がっていき、atmaCup期間内でアニメ1期、2期、映画全部見てしまいました。(原作も半分くらい買って読んだ

推しについて

推しは三玖です!!!!!!!!!!!!!!!!!!!!

見た目や声や性格も好みで1話から推してたのですが、自分も卑屈よりな性格なので、卑屈な殻を破って挑戦していく姿に勇気づけられましたし、「テストで一番になったら。おいしいパンが焼けたら。そうやって先延ばしにしてたのは私。」というセリフには刺されました。。。

てか「返事は後で聞くね」ってズルすぎないですか?今まで見たそのシーンで一番です

1期1話を見たとき 三玖一択では?って思ってたのですが、いまはほぼ箱推しになっちゃいました。心理描写がいいですね。一花の焦ってるシーンとか好きでした。ざーさんすごい。お姉さんしつつ乙女になるギャップとかも良かった。二乃はまさにツンデレですけど、ストレートなシーンもいいし、髪切ったあとの髪型が良かった。四葉は髪型が一番好き。アニメ本編ではあまり恋愛要素なかった気がしますが、恋愛パートと言うより映画の過去パートとかで号泣しました。あーいうのに私は弱い。五月は丁寧語とか、ドジっぽいところに可愛さがでてましたね。ふくれっ面がいい。

作画について

さて、大事な要素として作画は欠かせないものなんですけど、1期は作画がちょっと残念なところあって、演技やストーリーをたのしむ必要がありました。2期から製作が変わって良くなりました。映画は更に良くなって特にキャラクターの表情に愛を感じるクオリティだったと思います。尺も136分でアニメ映画にしては長く、上記の通り号泣する要素もあって、とても良い出会いでした。

映画五等分の花嫁マジでいいからみんな1期と2期みてくれ!

舞台について

全然意識してなかったのですが、作中に登場するヤマソトってスーパー完全に東海地方のローカルスーパー ヤマナカだったので調べたら舞台は愛知でした。主に太田川駅あたりみたいですが、行ったことあるためちょっとドヤ顔になっちゃいました。

独り言

今回Hulu使ってテレビとか、タブレットとか、パソコンとかで見てたんですけど、好きなシーンがあちこちにあるせいで巻き戻したり、繰り返しみたいしまくりました。いやー現代のサブスクサービスはいいものですね。好きなシーン何回でも見れるんだもん「あんたみたいな 男でも好きになる女子が 地球上に一人くらいいるって 言ったわよね それが私よ」とか、「返事は後で聞くね」とかめっっっっっちゃみた

推しが選ばれないとかは特に大きなダメージは受けないタイプですけど、4が選ばれて、4つの失恋があるのはちょっと複雑な感情になりますね。

原作が完結してるのでアナザーストーリーとか欲しくはありますが、原作読んでみるとほぼアニメのセリフが原作通りなので、原作なしにいいもの作るのは難しいんじゃないかと思います。

さて、atmaCup開催いただきありがとう、五等分の花嫁という素敵な作品に今更出会いました。ということで今回の記事はおしまいにします!

2023/7/24 五等分の花嫁∽見てきたので追記

  • 原作にあってアニメ化されてないものの映像化ということで、修学旅行と文化祭の間
  • OP-EDの5人の声が立体音響?てきになっててよかった
  • 二乃のツンデレ大画面で見れたの眼福
  • 四葉の過去エピでまた感動した。報われるタイプの感動に本当に弱い
  • 今回は五月がコメディ要素をになってて好きだなーってなった
  • 一花と三玖は控えめだったけどやっぱいいですね
# anime.csvに含まれていない '映画 五等分の花嫁'と'五等分の花嫁∽'を追加
anime_data = {
    'anime_id': ["48548", "54915"],
    'japanese_name': ['映画 五等分の花嫁', '五等分の花嫁∽',]
}
df_anime = pd.concat([df_anime,pd.DataFrame(anime_data)],axis=0).reset_index(drop=True)
user_data = {
    'user_id': ["teyo", "teyo", "teyo","teyo"],
    "anime_id": ["d3892531a5e6986bb0b1","3c4699e295cdc22d676c","48548", "54915"],
    "score": [9,10,10,10]
}
# 新しいuserのscore付きデータを追加
df_train = pd.concat([df_train, pd.DataFrame(user_data)],axis=0).reset_index(drop=True)

※anime_id分からなかった