Tech
blog
テックブログ

2026.7.31

    GBDTとLLMを組み合わせたLLM-BoostをTitanicで試してみた

    こんにちは、AIチームの戸田です。

    今回は先日読んだ論文、Transformers Boost the Performance of Decision Trees on Tabular Data across Sample Sizesで提案されていたLLM-Boostという手法が非常に興味深ったので、こちらの研究の紹介とLLM-Boostを、Kaggleでお馴染みのTitanic Datasetで検証してみた結果を共有したいと思います。

    なお、論文ではLLM-BoostとPFN-Boostの2つの手法が提案されていますが、本記事では LLM-Boostに絞って扱わせていただきます。

    LLM-Boost

    現在、画像や音声、自然言語処理といった多くの機械学習モデルでは、ニューラルネットワークがベースとなっています。しかし、テーブルデータに関しては、XGBoostなどの勾配ブースティング決定木(GBDT)が主流です。GBDTは決定木を連続して学習させるアルゴリズムですが、カラム名の意味を考慮することができません。

    一方、人間や大規模言語モデル(LLM)は、カラムの意味を理解することで、ゼロショットで予測を行える場合があります。たとえば、私たちは「築年数」や「価格」といった列ヘッダーを理解しているため、"築年数が古い家は安価" であったり "大きな家は高価" といった予測が可能です。

    しかし、GBDTはテキスト情報を読み取ることができないため、列間の関係性をデータから学習する必要があります。データセットが大規模であれば問題ないのですが、十分なデータを集められない場合もあります。そうしたときには、カラムの情報をうまく活用したいです。

    そこで、本手法では、最初の決定木をLLMに置き換えることで、カラム名の情報をモデルに反映させることを目指します。これにより、カラムの意味を活用した予測が可能となり、モデルの性能向上が期待できます。

    UCIなどのMLデータセットで評価し、学習データが100件程度の小規模なデータセットで、LLMとGBDTの両方の利点を活用し、両者を上回る性能を示したそうです。

    Titanic Datasetでの検証

    Kaggleのチュートリアルとして有名なTitanic Datasetを使ってLLM-Boostを試してみたいと思います。

    環境構築

    Google Corabolatory上で検証のための環境構築を行います。

    LLM-BoostはTabletというテーブルデータのML問題をLLMで予測するためのライブラリをベースに構築されていますので、まずはこちらの環境構築を行います。

    git clone https://github.com/dylan-slack/Tablet.git
    cd Tablet
    python3 -m pip install -e .

    Tabletですが、OpenAI APIにリクエストする部分が古かったのでget_gpt3_revisions関数内の生成結果取得部分(43-52行)を以下のように変更しました

    response = openai.ChatCompletion.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "user", "content": text},
        ],
    )

    続けて著者のGithubリポジトリのREADMEに従ってLLM-Boostの環境構築を行います。

    git clone https://github.com/MayukaJ/LLM-Boost.git
    cd LLM-Boost
    pip install -r requirements.txt

    前処理

    KaggleのコンペティションページからTitanicのデータセットをダウンロードし、任意の場所に解凍します。

    欠損値対応など基本的な前処理に加え、目的変数をLLMが扱えるようにテキストに変換します。

    import pandas as pd
    
    train_df = pd.read_csv("/content/train.csv")
    
    # 目的変数をテキストに変換
    train_df["Survived"] = train_df["Survived"].map({0: "died", 1: "survived"})
    
    # 半分を後段のLLM-Boostの学習データに
    n_eval = len(train_df) // 2
    train_df = train_df.sample(frac=1.0, random_state=0)
    train_x = train_df.iloc[n_eval:].drop(columns=["Survived"])
    train_y = train_df.iloc[n_eval:]["Survived"].values
    eval_x = train_df.iloc[n_eval:].drop(columns=["Survived"])
    eval_y = train_df.iloc[n_eval:]["Survived"].values
    
    dtypes = train_x.dtypes
    names_of_categorical_columns = dtypes[dtypes == "object"].index.tolist()
    names_of_number_columns = dtypes[dtypes != "object"].index.tolist()
    
    # カテゴリ変数の欠損値は"missing"に変換
    train_x[names_of_categorical_columns] = train_x[names_of_categorical_columns].fillna("missing")
    eval_x[names_of_categorical_columns] = eval_x[names_of_categorical_columns].fillna("missing")
    
    # 数値の欠損値は-1に変換
    train_x[names_of_number_columns] = train_x[names_of_number_columns].fillna(-1)
    eval_x[names_of_number_columns] = eval_x[names_of_number_columns].fillna(-1)

    ここでの変数名は便宜上eval_となっていますが、これは次のLLM-Boostでの学習データとなります。Kaggleに慣れている方はStacking Ensembleの際のCross Validationをイメージしていただくと良いかと思います。

    前処理されたデータをTablet向けに変換します。

    from Tablet import create
    
    # LLMへの指示. コンペティションサイトのOverviewより.
    instructions = """The sinking of the Titanic is one of the most infamous shipwrecks in history.
    On April 15, 1912, during her maiden voyage, the widely considered “unsinkable” RMS Titanic sank after colliding with an iceberg. Unfortunately, there weren’t enough lifeboats for everyone onboard, resulting in the death of 1502 out of 2224 passengers and crew.
    While there was some element of luck involved in surviving, it seems some groups of people were more likely to survive than others.
    In this challenge, we ask you to build a predictive model that answers the question: “what sorts of people were more likely to survive?” using passenger data (ie name, age, gender, socio-economic class, etc).
    """
    
    create.create_task(train_x,
                       eval_x,
                       train_y,
                       eval_y,
                       name="titanic",
                       header="You must follow the instructions to predict if passengers had suvived.",
                       nl_instruction=instructions,
                       categorical_columns=names_of_categorical_columns,
                       num_gpt3_revisions=1,  # 実験のため1パターンのみ生成
                       save_loc="./data/benchmark")

    こちらのコードを実行すると、save_locで指定したディレクトリに変換されたデータがJSON形式で保存されます。以下に変換された1サンプルを示します。

    {
            "header": "You must follow the instructions to predict if passengers had suvived.",
            "lift_header": "",
            "class_text": "Answer with one of the following: died | survived.",
            "instructions": "The sinking of the Titanic is one of the most infamous shipwrecks in history.\nOn April 15, 1912, during her maiden voyage, the widely considered \u201cunsinkable\u201d RMS Titanic sank after colliding with an iceberg. Unfortunately, there weren\u2019t enough lifeboats for everyone onboard, resulting in the death of 1502 out of 2224 passengers and crew.\nWhile there was some element of luck involved in surviving, it seems some groups of people were more likely to survive than others.\nIn this challenge, we ask you to build a predictive model that answers the question: \u201cwhat sorts of people were more likely to survive?\u201d using passenger data (ie name, age, gender, socio-economic class, etc).\n",
            "label": "died",
            "serialization": "PassengerId: 109\nPclass: 3\nName: Rekic, Mr. Tido\nSex: male\nAge: 38\nSibSp: 0\nParch: 0\nTicket: 349249\nFare: 7.9\nCabin: missing\nEmbarked: S\n"
    }

    serializationにカラム名とそのデータの値がテキストで入っているのがわかります。このテキストとinstructionsからLLMが予測を行います。

    ちなみにlift_headerが空ですが、こちらはinstructionsを指定しない際にalternate_headerという引数から生成されるようなので、instructionsが指定されていれば問題ないです。(こちら、READMEに記載がなく、コードの該当部分のコメントから読み取ったので間違いがあればご指摘いただきたいです)

    TabletでLLMの予測を作成

    LLM-Boostで使用するLLMはTabletというライブラリ

    from Tablet import evaluate
    
    benchmark_path = "./data/benchmark/performance/"
    tasks = [
        'titanic/prototypes-synthetic-performance-0',
    ]
    evaluator = evaluate.Evaluator(benchmark_path=benchmark_path,
                                   tasks_to_run=tasks,
                                   model="google/flan-t5-base",
                                   encoding_format="flan",
                                   results_file="titanic_results.txt",
                                   k_shot=16)
    evaluator.run_eval(how_many=1)

    LLM-Boostの学習と予測