Traffine I/O

日本語

2023-03-05

BERTに表形式データを組み込む方法

はじめに

BERT は、感情分析、テキスト分類、質問応答などの自然言語処理(NLP)タスクにおいて、優れた成功を収めている人気のある事前学習言語モデルです。ただし、BERTはテキストのみを入力として受け取り、数値データやカテゴリカルデータなどの他の種類のデータを組み込むことができません。

この制限は、表形式のデータをBERTモデルに組み込むことによって解決できます。この方法により、BERTの強力な言語処理機能と表形式のデータを組み合わせて、より堅牢で正確なモデルを作成することができます。

この記事では、表形式のデータをBERTモデルに組み込み、Hugging Face Trainerを使用して学習する方法を紹介します。

BERT モデルに表データを組み込む

以下は、表形式のデータ(数値データやカテゴリカルデータ)を組み込むカスタムBERTモデルを作成し、Hugging Face Trainerを使用して学習するための、ステップバイステップのPyTorchコードです。

ステップ 1:データの準備

最初のステップは、データの準備です。pandasを使用してCSVファイルからデータを読み込み、トレーニングセットとバリデーションセットに分割します。ここでは、数値データとカテゴリカルデータの両方を含む単純なCSVファイルを使用します。

python
import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.read_csv('data.csv')
train_df, val_df = train_test_split(df, test_size=0.2, random_state=42)

トレーニングセットとバリデーションセットのデータフレームを作成した後、Datasetクラスを使用してPyTorchデータセットに変換します。Datasetクラスを継承し、__len__メソッドと__getitem__メソッドを実装するカスタムデータセットを作成します。

python
import torch
from torch.utils.data import Dataset

class CustomDataset(Dataset):
    def __init__(self, data, tokenizer, max_length):
        self.data = data
        self.tokenizer = tokenizer
        self.max_length = max_length

    def __len__(self):
        return len(self.data)

    def __getitem__(self, index):
        row = self.data.iloc[index]

        text = row['text']
        label = row['label']
        numerical_data = torch.tensor(row[['num1', 'num2']].values, dtype=torch.float)
        categorical_data = torch.tensor(row['cat'].values, dtype=torch.long)

        encoding = self.tokenizer(text, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt')

        return {
            'input_ids': encoding['input_ids'][0],
            'attention_mask': encoding['attention_mask'][0],
            'token_type_ids': encoding['token_type_ids'][0],
            'numerical_data': numerical_data,
            'categorical_data': categorical_data,
            'label': torch.tensor(label, dtype=torch.long)
        }

この例では、データフレームから各行を読み込み、テキスト、ラベル、数値データ(num1とnum2)、およびカテゴリデータ(cat)の列を抽出します。次に、トークナイザーを使用してテキストをエンコードし、入力ID、アテンションマスク、トークンタイプID、数値データ、カテゴリデータ、ラベルを含む辞書を返します。

ステップ 2:モデルの定義

次のステップは、モデルの定義です。ハグイングフェイスのBertForSequenceClassificationモデルをベースにして、数値データとカテゴリデータの2つの追加の入力層を追加します。

python
from transformers import BertForSequenceClassification, BertConfig
import torch.nn as nn

class CustomModel(nn.Module):
    def __init__(self, num_numerical_features, num_categorical_features, num_labels):
        super().__init__()

        config = BertConfig.from_pretrained('bert-base-uncased')
        self.bert = BertForSequenceClassification(config)

        self.numerical_layer = nn.Linear(num_numerical_features, 64)
        self.categorical_layer = nn.Embedding(num_categorical_features, 16)

        self.classifier = nn.Linear(768+64+16, num_labels)

    def forward(self, input_ids, attention_mask, token_type_ids, numerical_data, categorical_data):
        bert_output = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids).last_hidden_state[:, 0]

        numerical_output = self.numerical_layer(numerical_data)
        categorical_output = self.categorical_layer(categorical_data).mean(dim=1)

        x = torch.cat((bert_output, numerical_output, categorical_output), dim=1)
        logits = self.classifier(x)

        return logits

この例では、PyTorch nn.Moduleクラスから継承されたカスタムモデルを定義します。モデルには3つのレイヤーがあります。BERTレイヤー、数値データを入力し、64次元のベクトルを出力する数値レイヤー、およびカテゴリデータを入力し、16次元のベクトルを出力するカテゴリレイヤーがあります。これらのレイヤーからの出力をBERTの出力と連結し、線形レイヤーを通過させてロジットを取得します。

ステップ 3:モデルのトレーニング

最後のステップは、Hugging Face Trainerを使用してモデルをトレーニングすることです。AdamWオプティマイザーと交差エントロピー損失関数を使用します。また、正確度を計算するカスタムメトリックを定義します。

python
from transformers import BertTokenizer
from sklearn.metrics import accuracy_score
from transformers import AdamW
from transformers import get_scheduler

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

train_dataset = CustomDataset(train_df, tokenizer, max_length=128)
val_dataset = CustomDataset(val_df, tokenizer, max_length=128)

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

model = CustomModel(num_numerical_features=2, num_categorical_features=4, num_labels=2).to(device)

optimizer = AdamW(model.parameters(), lr=5e-5)
scheduler = get_scheduler("linear", optimizer, num_warmup_steps=0, num_training_steps=len(train_dataset)*5)

def compute_accuracy(pred):
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    return accuracy_score(labels, preds)

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=64,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir='./logs',
    logging_steps=10,
    evaluation_strategy='epoch',
    save_strategy='epoch',
    learning_rate=5e-5,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=val_dataset,
    compute_metrics=compute_accuracy,
    optimizer=optimizer,
    scheduler=scheduler,
)

trainer.train()

この例では、まずトークナイザーとデータセットを作成し、モデルをGPUに移動させます。次に、オプティマイザー、スケジューラー、カスタムメトリックを作成します。最後に、Trainerオブジェクトを作成し、trainメソッドを呼び出します。

参考

https://medium.com/georgian-impact-blog/how-to-incorporate-tabular-data-with-huggingface-transformers-b70ac45fcfb4
https://towardsdatascience.com/how-to-combine-textual-and-numerical-features-for-machine-learning-in-python-dc1526ca94d9

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!