SciBERTを用いたtextデータの特徴量抽出
はじめに
今回のデータではtitleやabstractといったtextデータから特徴量を抽出することが鍵となりそうです。このディスカッションではBERTといわれる自然言語処理モデルの中で、SciBERTという科学論文誌で事前学習したモデルで特徴量抽出する手法を簡単に紹介します。
実装紹介
まずSciBERTのpre-training modelを取得しましょう。
https://github.com/allenai/scibert このreadmeのPyTorch HunggingFace Modelsからダウンロードします。
この際tar拡張子でダウンロードするため解凍する必要があります。
![831dd928-d837-4749-9694-cb383244c17d.png](https://probspace-stg.s3-ap-northeast-1.amazonaws.com/uploads/user/3829af4975d54ab6a4fb9e6732d93d8d/images/UserImage_153/831dd928-d837-4749-9694-cb383244c17d.png =300x)
次に実装です。実装はatmaCup#10のディスカッションにあがっているkaeruruさんの「[オランダ語対応] 事前学習済み BERT モデルを使ったテキスト特徴抽出について」を参考にしています。
GPUを用いなければかなり時間がかかるため、GPU環境がない方はGoogle Colaboratoryを用いることをお勧めします。
import pandas as pd
import numpy as np
import torch
import transformers
from pathlib import Path
from transformers import BertTokenizer
from tqdm import tqdm
tqdm.pandas()
class BertSequenceVectorizer:
def __init__(self):
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
self.model_name = 'models/scibert_scivocab_uncased' # modelを保存しているpathを指定
self.tokenizer = BertTokenizer.from_pretrained(self.model_name)
self.bert_model = transformers.BertModel.from_pretrained(self.model_name)
self.bert_model = self.bert_model.to(self.device)
self.max_len = 128
print(self.device)
def vectorize(self, sentence : str) -> np.array:
inp = self.tokenizer.encode(sentence)
len_inp = len(inp)
if len_inp >= self.max_len:
inputs = inp[:self.max_len]
masks = [1] * self.max_len
else:
inputs = inp + [0] * (self.max_len - len_inp)
masks = [1] * len_inp + [0] * (self.max_len - len_inp)
inputs_tensor = torch.tensor([inputs], dtype=torch.long).to(self.device)
masks_tensor = torch.tensor([masks], dtype=torch.long).to(self.device)
bert_out = self.bert_model(inputs_tensor, masks_tensor)
seq_out, pooled_out = bert_out['last_hidden_state'], bert_out['pooler_output']
if torch.cuda.is_available():
return seq_out[0][0].cpu().detach().numpy()
else:
return seq_out[0][0].detach().numpy()
DATA_DIR = Path('data') # dataディレクトリを指定
FEATURES_DIR = Path('features') # 特徴量を保存するディレクトリを指定
# 自分は読み込み速度をあげるためpickleファイルに書き直しています
train = pd.read_pickle(DATA_DIR / 'train_data_doi.pickle')
test = pd.read_pickle(DATA_DIR / 'test_data.pickle')
BSV = BertSequenceVectorizer()
for col in ['title', 'abstract']:
train_bert = train[col].fillna('nan').progress_apply(lambda x: BSV.vectorize(x))
test_bert = test[col].fillna('nan').progress_apply(lambda x: BSV.vectorize(x))
pd.DataFrame(train_bert.tolist(),columns=[f'{col}_scibert_{i}' for i in range(768)]).to_pickle(FEATURES_DIR / f'scibert_train_{col}.pickle')
pd.DataFrame(test_bert.tolist(),columns=[f'{col}_scibert_{i}' for i in range(768)]).to_pickle(FEATURES_DIR / f'scibert_test_{col}.pickle')
精度感について
自分は特徴量抽出後PCAで10次元に圧縮しました。
入れる前のCVは0.4934、LB0.4932、入れた後はCV:0.4927、LB:0.4921と0.01ほどスコアに貢献しました。
おわりに
@0.49切られている方へ
つよいとくちょうりょうをおしえてください!