まだタイトルない

アウトプット用です

Transformersのモデルキャッシュを別ディスクに移す(Windows編)

こんにちは。

Hugging Face TransformersでローカルでLLMを動かすとき、最初にモデルをダウンロードし、その後はローカルにキャッシュされたモデルを使って実行しますよね。

ところが、デフォルトではこのキャッシュ先がOSと同じストレージ(Cドライブなど)になっています。新しいモデルをどんどん試したいときでも、「OSディスクにどんどんデータが溜まるのはちょっと嫌だな…」と感じる人も多いはず。(私は嫌です。)

そこで今回は、別途用意しているML用ストレージ(私の場合はFドライブ)に、Transformersのモデルキャッシュを移します。(N番煎じ

結論

  • デフォルト設定は~/.cache/huggingface
  • 環境変数の設定でHF_HOMEを任意の場所に設定する

デフォルト設定

~/.cache/huggingface

設定状況の確認

環境変数 HF_HOME を確認

echo $HF_HOME

か、下記のようなコードを実行

import os

# HF_HOME(環境変数)が使われているか確認
hf_home = os.getenv("HF_HOME")
if hf_home:
    print(f"HF_HOME is set to: {hf_home}")
else:
    print("HF_HOME is not set. Default path (~/.cache/huggingface) will be used.")

デフォルト設定で実際に動かしてみる

from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "sbintuitions/sarashina2.2-0.5b-instruct-v0.1"

# トークナイザーとモデルをロード
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

prompt = "『葬送のフリーレン』は"

# トークン化
inputs = tokenizer(prompt, return_tensors="pt")

outputs = model.generate(**inputs, max_new_tokens=100)

# 結果をデコードして表示
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
『葬送のフリーレン』は、長野県を舞台とし、主人公のフリーレンが様々な物語や事件に巻き込まれながら成長していくストーリーです。フリーレンは、魔法使いであり、高い magical skill と優れた共感能力を持っています。彼女は魔法使いとしてのスキルだけでなく、物語や事件を解決する能力も持ち合わせており、その過程で多くの教訓を学びます。特に、彼女が出会う人々や遭遇する困難は、成長にとって重要な材料となります。また、物語の中には魔法や歴史、文化に関する要素も含まれており、読者は様々な視点に触れることができます。『葬送

キャッシュフォルダ

~/.cache/huggingface にファイルができていることを確認できました。

変更する

今回は恒久的に設定したいので環境変数を設定します。下記のようにFディレクトリを指定します

環境変数設定画面

もう一回コードの実行をします。

出力

『葬送のフリーレン』は、仏教と現代ファンタジーが融合した物語で、主人公のフリーレンは魔法使いとして生まれながら、仏教の教えを学びながら成長していくストーリーです。この物語において、仏教の教えがどのように重要な役割を果たし、どのように物語に深みを与えているのかについて考察します。

まず、仏教の教えが物語の中でどのように登場するかについてですが、『葬送のフリーレン』では、仏教的な価値観や瞑想、そして死生観が頻繁に登場します。特に、主人公のフリーレンが魔法使いとして生きる中で、死に対する認識や宗教

新しいキャッシュフォルダ

無事ローカルディスクFに保存されることが確認できました。

以上!

AESコンペでやった実験の振り返り

久々にkaggleのコンペをしたので振り返ります。といってもメダルを取ったわけでもないので今回は自分の解法を紹介というわけではなく実験管理について開示します。

結構世の中のkagglerが実験数300とか1000とか言ってる話を聞いて、個人的にナンバリングの粒度が気になってるのでまず自分の粒度を開示しようというような感じです。(全然人それぞれでいい問題だけど気になっちゃう性格

管理方法

まずコンペ期間のメモは基本的にnotionでとってます。

テンプレボタンを押すとコンペ用のページが作成されます。

こんな感じにメモをとっていく

実験

実験は絶賛管理方法を模索中です。今回はnotion内のデータベースを使って管理してみました。 (正直notionにデータベースをあまり増やしたくないのでなにかもっといい方法ないかなって思ってます)

一旦今回は実験、アイデア、todoをまとめて入れました。

実験ごとのページは、こんな感じの項目を作ってみました(もっと考察とか色々書くことがありますね・・・

お気持ち的に学生時代の実験って、色々設定とか結果とか考察書いてた気がしてそこら辺のことやりたいなって感じです。

実験の粒度

今回の実験番号は50くらいまでで終わって、だいたいこんな感じでやってました。悪い癖なのですが結局ハイパラいじったり、モデルかえたりが多くなっていて情けないです。

1実験1noteの関係でdropout ratioの探索も番号が試しただけ増えてるんですけど、世の中の人はこういうのは1つの番号でやってるんですかね?

  1. ベースラインを deberta-baseで作る
  2. deberta-v3-smallに買えて精度が上がるか確認
  3. gradaccを8にする
  4. fold毎にサブしてCV-LBの相関を確認する
  5. maxlen増やす
  6. maxlen増やす
  7. epoch数増やしたりしてみる
  8. StratifiedKFoldとかGroupKFoldでいいCVをさがす
  9. なんか公開ノートと比べてLBが悪いので公開ノートの設定に合わせる
    1. weight decayを0.1=>0.01
  10. なんか公開ノートと比べてLBが悪いので公開ノートの設定に合わせる
    1. labelを1-6でなく0-5にする
    2. この辺で推論ノートのmaxlenの設定が悪かったことに気付く
  11. quadratic weighted kappa lossをためす
  12. kaggle original をoof
    1. ここでかどうか忘れたけどStratifiedKFoldをして、kaggle original のデータだけのCVちがLB塗装感が取れることがわかっていこうその計測方法をした
  13. add_tokens
  14. vocabularyを更に増やす
  15. prompt2,3をvalidationに入れない
    1. 2と3は精度が良かったから
  16. モデルを大きくしたときの挙動確認(microsoft/deberta-v3-base
  17. モデルを大きくしたときの挙動確認(microsoft/deberta-v3-large
  18. promptを頭につける Please rate the quality of the following essay statement on a scale of 1 to 6 :
  19. RmsNormを使う
  20. mean pooling
  21. 分類タスクでやってみる
  22. 分類モデルにdropoutつける
  23. 分類モデルにdropoutつける
  24. 分類モデルにdropoutつける
  25. 分類モデルにdropoutつける
  26. 分類モデルにdropoutつける
  27. 分類モデルにdropoutつける
  28. microsoft/deberta-v3-largeの回帰でdrop outつけてみる(正則化が必要だと思って
    1. この辺でexp_lgbmもやった
  29. persuade2.0追加データ
  30. trainの部分だけに追加データを入れる分類
    1. validには追加データを入れないやつ
  31. dropout ratio調整
  32. dropout ratio調整
  33. dropout ratio調整
  34. dropout ratio調整
  35. dropout ratio調整
  36. dropout ratio調整
  37. deepset/deberta-v3-large-squad2でdropout ratio調整
  38. deepset/deberta-v3-large-squad2でdropout ratio調整
  39. deepset/deberta-v3-large-squad2でdropout ratio調整
  40. deepset/deberta-v3-large-squad2でdropout ratio調整
  41. deepset/deberta-v3-large-squad2でdropout ratio調整
  42. deepset/deberta-v3-large-squad2でdropout ratio調整
  43. LGBM公開ノートの前処理したあとのtextでdeberta
  44. deberta-v2-xlarge
  45. roberta-large
    1. なんかエラーでた
  46. exp018からデータ数を増やしたやつ
  47. microsoft/deberta-v3-base
  48. 記録されてない
  49. 記録されてない
  50. 記録されてない
  51. 記録されてない
  52. 記録されてない
  53. stacking
  54. stacking
  55. stacking

自分はこんな感じだよーって話リプとかDMとかでざっくばらんにお話できたら幸い!

MLエンジニアへのキャリアチェンジ:情報収集編

こんにちは、今更ですが転職しました!2023年の振り返りでも少し触れましたが、社内SEからAIエンジニアにキャリアチェンジしました。

本日は、選考を受ける企業をどのように、どこで知ったのかについてお話ししたいと思います。情報源は大きく分けて2つ、Twitter(≒Kaggle)と転職エージェントになります。

1.twitter

Kaggleを通じて知り合った人にお願いして話を聞かせてもらいました。雑に飲み会を企画して話を聞いたり、カジュアル面談という形でお話させてもらったりしました。AI系の仕事といっても、大企業のDX部門のAIチームだったり、プロダクトを作っている企業だったり、SIerのような業務をしている企業、データ分析がメインの企業など、さまざまな種類の仕事があることを学びました。また、もっと具体出来な業務についてのお話もたくさん聞くことができました。

ある程度KaggleをやってTwitterで交流を持っていたからこそできたことかもしれません。また、Twitterで「AI系の仕事探してます!」とツイートして、そこに連絡が来て知った企業もありました。

2.転職エージェント

大企業のデータサイエンスチームにも興味があったので、転職エージェントにも登録しました(私はdさんを利用しました)。担当者はKaggleをあまり認知していなかったようですが、それでも大手自動車メーカーのデータサイエンスチームなど、関連する求人票を集めてもらえました。世間的にAIをやっていると認知されていない企業も知ることができて良かったと思います。

ただし、転職サイトを利用する場合、カジュアル面談は基本的にないので、求人票と企業サイトを見て判断する必要があります。

(尚、だいたい書類で落ちたがこれはまた別の話)

その他の情報収集方法

インターネットで直接調べることも有効です。

  • 例えば「JR東海 データサイエンティスト」のように検索して、関連する子会社や部門がないか直接探してみる。
  • 最近ではKaggle Master 分析レポートを参考にして、Kagglerがいる企業を探す。
  • WantedlyやLinkedInにも登録し、少数ですが連絡をもらうことができました。

最後に

最後に、KagglerなどAI業界の人々からの認知度と社会的な認知度が一致していないことがあるため、たまに冷静に考える時間を作ることも大切かもしれません。

これらの情報が、これからAIエンジニアへのキャリアチェンジを考えている方々の参考になれば幸いです。

LLM - Detect AI Generated Text 雑に振り返り

こんにちは。先日まで開催されてた"LLM - Detect AI Generated Text"に参加してました。

個人の取り組みとしては参加期間も短くディスカッションも追いきれず、公開ノートを弄って終わりでした。

久しぶりの機械学習(特にNLP)ということで今コンペはどんなモデルや手法が使われてるのか軽くメモを残しておきます。

※想像以上に雑なクオリティになってしまいました。ごめんなさい。英語の読解が正しくできてない部分もあるかもしれません。ご承知おきください。

solution毎のメモはコチラに公開してあります。

概要

  • LLMによって生成されたテキストを検出する
    • 学生が書いたエッセイとさまざまな LLM によって生成されたエッセイを識別する
  • エッセイは中学生、高校生が書いたものと大規模言語モデルが書いたもの
    • どのLLMで生成したかは非公開
  • 評価指標
  • LLMで生成された確立を提出

Data

  • 約10,000件のエッセイ
  • trainデータは1378件与えられ、学生が書いたものが1375件、LLMが3件
  • 残り約9,000件がpublicとprivateLB
    • test data内の人とLLMの比率は非公開
  • エッセイは7種のプロンプトのうち一つに応じて書かれている
    • 各プロンプトで、学生は 1 つ以上の原文を読み、応答を書くように指示されました
    • これと同じ情報が、エッセイの生成時に LLM への入力として提供される場合と提供されない場合がある(ここが意味わからなかった)
  • train set は2つのプロンプトから構成されている

これが与えられた情報です。特徴としてはLLMのエッセイを識別したいのにLLMで生成したサンプルが3件しか与えられてないところだと思います。

参加者の調査によってもう少しデータについての情報が増えました。

  • 7つのプロンプトが判明する(link)
  • testにはtrainとちがうpromptのデータしかない
    • testの5種のうちpublicに含まれてないpromptがある(link)

図で表すとこのような感じになります。

public/privateでpromptが分かれていたため、publicにoverfitしていた人が大きくshakedownするということが起きました。

External data

上述の通りLLMが生成したデータが3件しかないため、参加者が作成した追加データセットを使用する人が多かったです。いろんな人が生成データを共有し、それをまとめる人がいたり、更新が止まったりとこの情報を追いかけることがすでに大変でした。

主要な公開データセットは @thedrcat 氏が公開していたデータセットv2,v3,v4あたりだと思います。上位の方々はこのデータを厳選したり、さらにエッセイを自作していたようです。

persuade_corpusということも特定されたので、人が書いたエッセイデータも参加者は追加で使用できました。

typo

本コンペで議論されていた大テーマの一つですが、今一つ実態を理解できてないので短く・・・(こんなんじゃコンペ参加してたと言えないですねごめんなさい

  • 高速にtypoを直す(あくまで本データセットtypo生成をハックした手法)
  • 通常typoは隣接する文字などに間違うが、データをよく見ると完全にランダムに間違えられていた(link)
    • 3割の確率でtypoを挿入/置換した(4th)
  • tfidfでmin_df = 2 とすると単一のエッセイ内に発生するngramが削除される。二つの異なるエッセイで同じtypoが発生する確率は低いと考える

銀上位あたりまで公開ノートを微修正で行けた

私がこのコンペを見てなかった期間に、testdata(こっちのほうが公式ののLLMデータが多く含まれてる)を使ってtokenizerをつくって、TF-IDFで特徴量を作ってオンラインで学習、推測をするノートが投稿され、大半の公開ノートがこれの派生になってる状態でした。

終了後、各参加者の投稿を見ると公開されてたハイスコアノートを弄って銀メダルという方もいました。 catboostのweightを大きくしただけであったり、ランダムフォレストを追加した人もいたのが印象的でした。

また、上位の人もアンサンブルの種にはしていました。公開ノートはpublicにoverfitしていましたが手法としては有効なものだったように思います。

上位はtransformerモデルを学習していた

印象としては上記TF-IDFを使ったモデルにtransformerモデルをアンサンブルさせていた人が上位にいたように思います。

  • mistral7BをQLoRA
  • Deberta-v3-largeをLoRAでなく普通に学習
  • deberta-v3-smallやdistilbertをsubmit時にtraining などがありました。

Pseudo-Label

  • testデータに対する予測値で、自信をもって予測されてるものに疑似ラベルを付けて学習しなおす手法も多くのチームが採用していました。
    • 気持ちとしては、自作したエッセイとhostが用意してるLLM産エッセイが同じクオリティとは言い切れないため、hostが用意したエッセイの情報を使った方が有効というところだと思います。

データセットをよくする方に注力した参加者が多かった

  • 自分でたくさんエッセイを生成したり、有志で共有されてたデータセットのデータを吟味してるチームも多かった(こ、これがデータセントリック!?)
  • 今後コンペで勝つには良質なデータを生成するスキルも身につけなきゃいけないのかなーということをひしひしと感じた。
    • 英語わからんから良質かどうか判断つかないのがハンディキャップすぎる

その他

  • llama 7b と tiny llama 1.1B を使用してghostbusterを実装(1st)
  • 人間とAIのペアを作ってMargin Ranking Lossで学習(1st)
  • promptを予測するサブタスクを用意し、そのタスクのlogitを特徴量として使用
  • trainデータを少しずつ増やしていく(予測させて間違えたものをtrainに追加して学習など
  • rankingでアンサンブル
  • roberta-large-openai-detector(GPT-2の出力を検知するモデル)
  • token sizeが512のモデルの場合、頭512tokenとお尻512tokenの2つを予測

2023年振り返りと2024年の目標

2023年の振り返りをしていきます。この一年は私にとって色々な目標を達成したり、あえて辞める選択をするなど非常に濃密な1年間でした。人間社会としても2022年の暮にChatGPTが登場して、ここまでたった1年ですごい変化が起きており濃密な1年だったと思います。

AtCoder Algorithm 入水

2022年度内の達成を目指してましたが、実力不足であったりDDoSがあったりとでGW後の入水になりました。PAST本上級が途中で終わってたりするので、これの読了とPAST受験、入青をまったり目指すかもしれないです。

AtCoderHeuristic 入水

9/24の回からAHCにも取り組み始めました。

特に明確な理由目的はなく、どれくらい行けるんだろう?という興味ではじめてみました。

12月に入水できました。まだ焼きなましたり出来ないので本番で使えるようになりたいです。こっちもまったり入青目指すかもしれないです。

ひとりディズニー

king of ひとり〇〇だと思うのでやってみたかったんですよね。二日間かけてどっちも行った。普通に楽しかった。トイマニ楽しかったけど1人は寂しいかな・・・・

個人的にはひとりボルダリングのほうが無理です。

ひとり海外旅行

海外旅行は好きですがこれまでは複数人で行ってて、同行者の助けがありながら無事帰ってこれてたと思っていたので、一人でできるかという意味で挑戦しました。初めてということで難易度が優しいと言われている台湾に行きました。

こうして無事に帰ってこれてますが、海外旅行中の貴重な体験その場で一緒にいる人に共有したいと思ったので、今後海外旅行は人としたいなーって思いました。

転職活動

8月くらいからカジュアル面談しはじめて、11月くらいから選考に入り始めました。実際には1月から視野に入れていてkagglerの皆さんとたくさんご飯に行って色々話を聞いてました。活動中私に時間を撮ってくれた皆さん本当にありがとうございました!!!!!!!!!

貯金

N歳までに金融資産額hoge円を目標にしてましたがダメでした。N歳の間にと目標を切り替えて、こっちは無事達成できました。

100人とご飯食べる

あまり積極的に人とご飯に行くタイプじゃないので意図的に100という設定をして色んな人とご飯を食べに行くようにしました。色んな人の考え方とか話を聞くことは自分の幅を広げることになるんじゃないかなと思ったからです。結果としては64と未達出終わりましたが、いい感じだったと思います。(語彙力力尽きた

その他出来なかったこと

マジで早くしないと老いていしまいどんどん難易度が高くなってしまう。

来年の目標

  • 健康
  • Kaggleで金メダル

結婚願望はあるのでそろそろそこに向けて動くべきだと思いますがネットに向かって宣言するのも違う気がするのでこんな感じにゆるく記載して〆ようと思います!

AHC027参加記・AHC入水しました!

こんにちは

AHC026に参加してきました!

415th / 3136で水パフォ、無事入水することができました

AHC027

課題の確認

問題はcontest page参照

考察

※正しくない部分はあると思います

  • 各マスの汚れやすさdは1~1000
  • j-k-1は N=20の時-9から17の値を取る、中心が正のあたりにある正規分布
  • 毎ターンsum(dij)-d現在地分スコアが増加する
  • dが大きいマスに移動距離を増やしてまで寄り道することは基本的にスコアの悪化を招く
    • N=40の時は全体で最低でも1600汚れるので、1マスで相殺できない

つまり方針としては、Lをなるべく小さくなるようにしつつ、どうしても訪問済みのマスを通る必要があるときはなるべくdが大きいルートを通る。

でいいとおもってましたが、、、、上位のgifを見る限り違ったようです;;

最終提出

code

BFSで一番近い未訪問マスへの経路を探す処理をベースに実装しました。

隣接するマスを調べる上下右左の順序を24通り試したりもしましたが、そもそもLを小さくという方針がよくなかったのか微改善しかできませんでした。

丁寧に経路長とスコアの相関を取ったら相関係数0.915だったので確信してたんですけどね・・・・

AHC入水

AHC024,025,026,027で入水することができました。

各振り返り記事は下記にございます。

感想

AHCをはじめる前までは「焼きなまし法とやらを使わないと戦えないのでは?」と思っていたので、焼きなまし法や山登り法がどういうものなのか座学してからAHC024からヒューリスティクスデビューをしました。

しかし、Contest本番になるとその手法の適用方法がわからず、その場その場においてどうしたほうがスコアがよくなるかというルールを作っていく貪欲なアプローチしかできずすべてのコンテストで貪欲なアプローチで水色までなってしまいました。

余談ですが、アルゴリズムはすでに水色まで取り組んでおり、この貪欲に進める部分で早いソート方法は何だろう?とか、BFSを使おうとかそういったアルゴリズムで学んできた知識は活かすことができたのではないかと思っています。

各コンテストの上位解法のヴィジュアライザを見ると何が起きてるのか全くわからないぐらい上には上があります。

今後の課題としては焼きなまし法や山登り、ビームサーチなどを駆使した最適化コードを実装できるようになるというところだと思います。

引き続き頑張ります!

AHC026参加記

こんにちは

AHC026に参加してきました!

212th/1644で青パフォでした(入水しそこねた・・・・・

今回はちょっと手短に振り返ります

課題の確認

問題はcontest page参照

ソリティアと言ってる参加者が多くたしかにそんな感じだと思います。

考察

  • 取り除くアイテムは200個、操作は5000回できる
    • 毎回上に乗ってるものをどかしてから取り除いても400回なので結構余裕
    • 1つ取り除くのに25回移動させられるので制約違反は気にしなくて良さそう
  • 目的の数字の上にN個荷物が乗っていたらそれを撤去するにはNの体力消費をすればできる
    • 何回に分けてもN消費することは変わらないというのは多分ポイント

とりあえず愚直に上に乗っかってる荷物を隣に移す

code

from collections import defaultdict
N, M = map(int, input().split())
B = [list(map(int, input().split())) for _ in range(M)]

def get_over_n(n):
    # nの上に置かれた数字と箱の番号
    for m in range(M):
        for i in range(len(B[m])):
            if B[m][i] != n:continue
            if i == (len(B[m])-1):
                return 0, m
            else:
                return B[m][i+1], m

def move(n, cur_b, nxt_b):
    #nを箱bから箱bに移す
    idx = -1
    for i in range(len(B[cur_b])):
        if B[cur_b][i] == n:
            idx = i
    B[nxt_b] = B[nxt_b]+B[cur_b][idx:]
    B[cur_b] = B[cur_b][:idx]


for n in range(1,N+1):
    over_n, cur = get_over_n(n)
    # 上に乗ってる荷物をどける
    if over_n != 0:
        nxt = (cur+1)%M
        move(over_n, cur, nxt)
        print(over_n, nxt+1)
    print(n,0)
    B[cur].remove(n)

nxt = (cur+1)%Mで隣の箱の番号を計算してそのまま移します

これで1,221,582で710位くらいで茶パフォです

自分以外のボックスで一番使われるのが遅いボックスに移動させる

code

def get_next_box(n):
    # 次移動させるボックスを計算する
    s = set(range(M))
    s.remove(box_dict[n])
    while len(s)>1 and n < 200:
        n += 1
        idx = box_dict[n]
        if idx in s:
            s.remove(idx)
    l = list(s)
    return l[0]

こんな感じの関数で直近で使わなそうなボックスをとってきます。whileの条件を考えるのとsetの中に要素が複数残る可能性があることに注意が必要です

これをすると 1,356,657で426位、水パフォまで上がります

同じスコアの人めちゃくちゃいたけど同じ方針????

移動させるときに直近で使いそうなものが上に来るように入れ替えてみる

code

# N個のダンボール
# M個の山
# 最大5000回作業できる
# - 1個の箱を取り出すために25回位操作してもいいと言える
from collections import defaultdict
import numpy as np
N, M = map(int, input().split())
B = [list(map(int, input().split())) for _ in range(M)]
D = 2 # 何個移動を考える?

# nはboxmにある
box_dict = defaultdict(int)
for i in range(M):
    for v in B[i]:
        box_dict[v] = i

def get_over_n(n):
    # nの上に置かれた数字と箱の番号(0-index)
    for m in range(M):
        for i in range(len(B[m])):
            if B[m][i] != n:continue
            if i == (len(B[m])-1):
                return 0, m
            else:
                return B[m][i+1], m

def get_best_sep_idx(l):
    # 2回に分けて移動させる時、どこで区切るといいか求める
    sorted_l = sorted(l)
    best = 10**18
    best_idx = -1
    for i in range(len(l)):
        tmp = l[i:]+l[:i]
        cnt = 0
        for n in range(D):
            _ = np.argmin(tmp)
            cnt += len(tmp) - _ - 1
            tmp.remove(tmp[_])
        if best > cnt:
            best = cnt
            best_idx = i
    return best_idx

def move2(n, cur_b, nxt_b):
    #nを箱cur_bから箱nxt_bに移す
    idx = -1
    for i in range(len(B[cur_b])):
        if B[cur_b][i] == n:
            idx = i
    move_l = B[cur_b][idx:]
    # dictionary更新
    for v in move_l:
        box_dict[v] = nxt_b

    if len(move_l) >= D:
        best_idx = get_best_sep_idx(move_l)
        if best_idx == 0:
            B[nxt_b] = B[nxt_b]+move_l
            B[cur_b] = B[cur_b][:idx]
            print(move_l[0],nxt_b+1)
        else:
            print(move_l[best_idx],nxt_b+1)
            print(move_l[0],nxt_b+1)
            move_l = move_l[best_idx:] + move_l[:best_idx]
            B[nxt_b] = B[nxt_b]+move_l
            B[cur_b] = B[cur_b][:idx]
    else:
        B[nxt_b] = B[nxt_b]+move_l
        B[cur_b] = B[cur_b][:idx]
        print(move_l[0],nxt_b+1)

def get_next_box(n):
    # 次移動させるボックスを計算する
    s = set(range(M))
    s.remove(box_dict[n])
    while len(s)>1 and n < 200:
        n += 1
        idx = box_dict[n]
        if idx in s:
            s.remove(idx)
    l = list(s)
    return l[0]

for n in range(1,N+1):
    over_n, cur = get_over_n(n)
    # 上に乗ってる荷物をどける
    if over_n != 0:
        nxt = get_next_box(n)
        move2(over_n, cur, nxt)
    print(n,0)
    B[cur].remove(n)

3を取り出したいときに、83を動かすよりも、56を動かしてから83を動かしたほうがいいんじゃない?みたいなアイデアです。

一応関数を作って、移動させる集合の中で軽い方から何個か移動させるときの最適なんてものも考えましたがあまり劇的な改善は見られませんでした

これをすると1,381,277になって212位、青パフォです

感想

  • 典型的ビームサーチ?って思いましたが使ったこと無いので貪欲なシミュレーション?しながらやりました
  • 上に乗ってるものを複数箇所に散らばらせて移動させることを何らかのルールのもとで来たら伸びるんだろうなーと思いましたが何もわかりませんでした。
  • 次水色に慣れるように頑張ります。開催ありがとうございました!!!