Databricks、Delta、トランスフォーマーを用いた迅速なNLP開発

[Rapid NLP Development With Databricks, Delta, and Transformers \- The Databricks Blog](https://www.databricks.com/blog/2022/09/09/rapid-nlp-development-databricks-delta-and-transformers.html)の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

自由記述のテキストデータは、構造化データの領域では利用できないアクション可能な洞察を提供することができます。保険会社は、他の方法では知ることができない主訴の特性を理解するために、主訴を調整する担当者のノートを活用するかも知れません。IT部門は、サポートチケットのリクエストを適切な専門チームにルーティングするために、効率的にチケットを解析するかも知れません。自由記述のテキストからこのレベルの価値を生成することは困難となることがありますが、トランスフォーマーモデルと呼ばれる一連のモデルは、企業のデータサイエンス実践者が容易に活用できる強力なツールセットを提供しています。

トランスフォーマーモデルは、これまでの手法よりも効果的かつ効率的にテキストのセマンティックを捕捉する[セルフアテンション](https://proceedings.neurips.cc/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf)というニューラルネットワークアーキテクチャを活用しています。また、これらは、モデルの開発者によって[masked language modeling](https://www.youtube.com/watch?v=mqElG5QJWUg)やnext sentence predictionのようなテクニックを用いて大規模テキストコーパスによってトレーニングされたことを意味する転送学習という形態を取ります。このモデルは、本書の焦点であるテキスト分類を含む様々な後段のタスクで使用することができる、[ワードエンベディング](https://en.wikipedia.org/wiki/Word_embedding)を生成するように設計されています。

本書では、トランスフォーマーモデルのハイレベルの概要と、これらをトレーニングする際の検討事項を説明します。実装の詳細や[Delta Lake](https://docs.databricks.com/delta/index.html)や[マネージドMLflow](https://www.databricks.com/jp/product/managed-mlflow)とのインテグレーションに関しては、[ソリューションアクセラレータ](https://github.com/marshackVB/rapid_nlp_blog)を参照ください。

# トランスフォーマーを使い始める

[Hugging Face](https://huggingface.co/course/chapter1/1)はトランスフォーマーモデルを発見・アクセスしやすくすることにフォーカスしている企業です。多様なモデルやデータセットへのアクセスを提供しています。Hugging Faceによってメンテナンスされている[transformersライブラリ](https://huggingface.co/docs/transformers/index)を用いることで、お使いのDatabricksワークスペースでアーティファクトをダウンロードし、使用することができます。このライブラリはDatabrikcs機械学習ランタイムバージョン10.4以降に含まれており、それより新しいバージョンでは[pipでインストール](https://pypi.org/project/transformers/)することができます。

ライブラリを使い始めるには、Hugging Faceの[モデルハブ](https://huggingface.co/models)から[bert\-base\-uncased](https://huggingface.co/bert-base-uncased)のようなトランスフォーマーのアーキテクチャを選択します。そして、トークナイザーとモデルをダンロードするために以下のコードを実行します。

```py:Python
from transformers import AutoTokenizer, AutoModel

model_type = 'bert-base-uncased'
tokenizer = AutoTokenizer.from_pretrained(model_type)
model = AutoModel.from_pretrained(model_type)
```

# トークナイザーによるデータの前処理

トークナイザーはいくつかの前処理ステップを実行します。最初に、テキストをトークンに分割し、トークンをモデルのボキャブラリーにマッピングします。[BERT](https://arxiv.org/abs/1810.04805)のボキャブラリは、単語、単語の断片、数字、句読点や記号の30,522エントリーから構成されています。また、モデルには観測の開始地点([CLS])や文の区切り([SEP])のような情報を捕捉する特殊なトークンが含まれています。

```py:Python
from itertools import islice

# Display the first five entries in BERT's vocabulary
for token, token_id in islice(tokenizer.vocab.items(), 5):
  print(token_id, token)
```

![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-12.jpg)

```py:Python
# Display BERT's special tokens
for token_name, token_symbol in tokenizer.special_tokens_map.items():
  print(token_name, token_symbol)
```

![](https://cms.databricks.com/sites/default/files/inline-images/CODE4.png)
*BERTのボキャブラリのトークンIDとトークン、特殊なトークン*

「Databricks」のようにBERTのボキャブラリに存在しないトークンは、マッチするように断片に分割されます。

```py:Python
token_ids = tokenizer.encode("transformers on Databricks are awesome")
Token_ids
```

![](https://cms.databricks.com/sites/default/files/inline-images/Out%5B6%5D%20%5B101%2C%2019081%2C%202006%2C%202951%2C%2025646%2C%202015%2C%202024%2C%2012476%2C%20102%5D.png)

```py:Python
# Map token ids to BERT's tokens
id_to_token = {token_id: token for token, token_id in tokenizer.vocab.items()}

[id_to_token[id] for id in token_ids]
```

![](https://cms.databricks.com/sites/default/files/inline-images/CODE2.png)
*トークン化されたシーケンスは、BERTのボキャブラリーや特殊なトークンにマッピングし直すことができるトークンIDのリストを生成します。*

また、トークナイザーは入力シーケンスの切り取りとパディングを行います。それぞれのモデルには、許容できるトークン化シーケンスの最大長を持っています。BERTや多くの他のモデルでは、その長さは512トークンです。入力テキストをトークン化する際、最初の512トークン以降に生成されたすべてのトークンは削除、あるいは「切り取られ」ます。

さらに、トークンのシーケンスは「パディング」されます。トランスフォーマーモデルは、一度のすべてのトレーニングデータセットに対してトレーニングするのではなく、データのバッチに対してトレーニングされます。それぞれのバッチは同じ長さである必要がありますが、観測されるテキストの長さは大きく異なります。トークン化されたあるシーケンスは512要素よりもはるかに長いかも知れませんし、他のものははるかに短いかもしれません。均一な長さにする必要がある際、パディングによってトークン化されたシーケンスにゼロが追加されます。ゼロの値はBERTのボキャブラリの特殊トークンである[PAD]を表します。

```py:Python
records = ["transformers are easy to run on Databricks",
           "transformers can read from Delta",
           "transformers are powerful"]

def tokenize(batch):
  """
  Truncate to the max_length; pad any resulting sequences with 
  length less than max_length
  """

  return tokenizer(batch, padding='max_length', truncation=True, max_length=10, return_tensors="pt")
  
tokenized = tokenize(records)

tokenized_lengths = [len(sequence) for sequence in tokenized['input_ids']]

print("Tokenized and padded sequences")
for sequence in tokenized['input_ids']:
  print(sequence)
  
print(f"\nTokenized sequence lengths\n{tokenized_lengths}")
```

![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-9.png)
*同じ処理ステップでトークン化されパディングされたテキストシーケンス*

トークナイザーの切り取りとパディングの挙動は設定可能であり、テストし比較することができる[様々な戦略](https://huggingface.co/docs/transformers/pad_truncation)が存在します。トレーニング時間を短縮するために切り取りを行いますが、より長いシーケンスが一般的である場合、情報の損失によって予測パフォーマンスの妨げになることがあります。優れた汎用的な戦略として[dynamic padding](https://www.youtube.com/watch?v=7q5NyFT8REg)を検討してください。このテクニックはトークン化ではなくモデルトレーニングの際にシーケンスをパディングします。ダイナミックパディングは、バッチで最長の長さにそれぞれのバッチをパディングし、パディングされるトークンの数が最小になるように維持します。

# ワードエンベディングを用いたテキスト分類

トークン化されたテキストは、ワードエンベディングを生成するために直接入力することができ、特殊トークンを含むそれぞれの入力トークンに対して一つのエンベディングを生成します。これらのエンベディングは様々な自然言語処理タスクで使用することができます。

```py:Python
import torch

with torch.no_grad():
  token_embeddings = model(input_ids = tokenized['input_ids'], 
                           attention_mask = tokenized['attention_mask']).last_hidden_state

sequence_length = [len(embedding_sequence) for embedding_sequence in token_embeddings]

cls_embedding = token_embeddings[0][0]

embedding_dim = cls_embedding.shape[0]

print(f"\nEmebdding sequence lengths\n{sequence_length}")

print(f"\nDimension of a single token embedding\n{int(embedding_dim)}")
```

![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-10.png)
*モデルの最後のレイヤー(最後の隠し状態)で生成されたエンベディングの長さと次元*

例えば、テキスト分類においては、それぞれの観測事項の特殊トークン[CLS]に関連づけられたエンベディングのみを使用するのが一般的です。このエンベディングは、テキストを一連のユーザー定義カテゴリーに分類するフィードフォワードニューラルネットワークに入力されます。トランスフォーマーライブラリは、[AutoModelForSequenceClassification](https://huggingface.co/transformers/v3.0.2/model_doc/auto.html#automodelforsequenceclassification)クラスを通じてこのアーキテクチャをすぐに使用できるように実装しています。このクラスによって、ユーザーはトランスフォーマーのモデル名とモデルのニューラルネットワークのレイヤーの最後にアタッチされる`classification head`を指定することができます。そして、シンプルに分類するラベルの数を指定します。例として、Hugging Faceのデータハブで利用できる[banking77 dataset](https://huggingface.co/datasets/banking77)には、銀行に関する質問が77の意図に分類されています。このため、モデルの`num_labels`パラメーターは77に設定されます。

```py:Python
from transformers import AutoModelForSequenceClassification
sequence_classification_model = AutoModelForSequenceClassification.from_pretrained(model_type, num_labels=77)

sequence_classification_model.classifier
```

![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-11.png)
*マルチクラス分類のために分類ヘッドが追加されたBERTトランスフォーマーモデル*

そして、このモデルはトレーニングデータセットに対してファインチューンされます。トレーニングにおいては、エンベディングや分類ヘッドを生成するレイヤーを含むネットワークのすべてのレイヤーで学習可能なパラメーターがアップでオートされます。このファインチューニングされたモデルから、予測ラベルと確率を生成することができます。
![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-1.jpg)
*banking77データセットに対してファインチューンされたトランスフォーマーモデルから生成された予測結果*

banking77データセットなどを用いたモデルトレーニングの実装の詳細については[solution accelerator](https://github.com/marshackVB/rapid_nlp_blog)をご覧ください。

## トランスフォーマーモデルの最適化

トランスフォーマーモデルは、トレーニングし推論に適用するには大規模で膨大な計算量を必要とします。本書で議論しているBERTモデルは、1.1億の学習可能なパラメーターを有しています。より最近のアーキテクチャのいくつかはさらに大規模なものとなっています。極端な例ではGPT-3は1750億のパラメーターがあります。幸運なことに、モデルのトレーニング時間を削減し、推論をスピードアップする方法があります。

モデルのファミリーに[distilled models(蒸留モデル)](https://arxiv.org/abs/1910.01108)というものがあり、これは大規模なモデルである「教師」を小規模なバージョンである「生徒」に圧縮することで、モデルのサイズと計算処理の複雑度を削減します。Hugging Faceモデルハブにおいては、通常これらのモデルの名前には`distil`が含まれており、[distilbert\-base\-uncased](https://huggingface.co/distilbert-base-uncased)といったものとなります。蒸留モデルはよりクイックにファインチューンし、大規模な教師よりも高速に推論を行うことができます。以下の[エクスペリメント](https://docs.databricks.com/applications/mlflow/tracking.html)では、映画のレビューと感情を含む[IMDB dataset](https://huggingface.co/datasets/imdb)に対するモデルを比較しています。モデルサイズ、GPUメモリー消費、トレーニング時間、すべての評価用データセットのスコアリングに要した時間の大きな違いに注意してください。興味深いことに、予測パフォーマンスはモデル全体で大きな違いはありません。
![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-2.jpg)
*デフォルトのトランスフォーマートレーナー引数とバッチサイズ16でトレーニング、評価を行った際のモデルの比較。*

蒸留の他に、[トレーニングの設定とGPUタイプ](https://huggingface.co/docs/transformers/performance)は大きなインパクトをもたらします。ファインチューニングのプロセスを管理するトランスフォーマーの[Trainer](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments)クラスの設定を調整することで、トレーニングと推論の時間を劇的に削減することができます。IMDBデータセットに対してモデルのファインチューニングを行う際、バッチサイズやトレーニングにおける[数値精度](https://huggingface.co/docs/transformers/perf_train_gpu_one#floating-data-types)、[勾配集積ステップ](https://huggingface.co/docs/transformers/perf_train_gpu_one#gradient-accumulation)に関係する設定の調整は、単一のトレーニングエポックにおけるトレーニングと推論の時間の大きな削減につながりました。
![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-3.jpg)
*単一エポックにおいて異なるトレーニング設定の比較*

さらに、GPUタイプの選択はトレーニングと推論の時間にインパクトを与えます。
![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-4.jpg)
*IMDBデータセットに対してNVIDIA V100 GPUタイプは最も速いトレーニングを実現しますが、最も高価です。*

GPUインスタンスはトレーニングに必要で、推論も劇的に高速化しますが、GPUの推論はオプションです。CPUによる推論を加速するには[量子化](https://pytorch.org/docs/stable/quantization.html)と蒸留モデルの使用を検討してください。量子化は、推論のレーテンシーを削減するために、高速でより精度の低い数値表現を使用します。これは、ファインチューンされたトランスフォーマーモデルに容易に適用することができます。

```py:Python
import torch
import torch.nn as nn
from torch.quantization import quantize_dynamic

quantized_model = quantize_dynamic(trainer.model.to("cpu"),
                                   {nn.Linear},
                                   dtype=torch.qint8)
```

banking77データセットに対してファインチューンされたdistilbert-base-uncasedモデルの線形レイヤーを量子化することで、サイズを約半分にすることができました。CPUによる推論のレーテンシーは2/3に削減され、テストデータに対するモデルのF1スコアは0.01減少しただけでした。

# 事前トレーニングおよび事前ファインチューン

あるケースにおいては、すぐに利用できるオプションがすでにそんざいしているので、テキスト分類モデルのファインチューニングが不要というケースがあります。例えば、[distilbert\-base\-uncased\-finetuned\-sst\-2\-english](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)モデルは、テキストと感情のカテゴリを含む[SST\-2 dataset](https://huggingface.co/datasets/sst2)に対してファインチューニングされた事前トレーニング済みのモデルから構成されています。このモデルとトークナイザーは、[パイプライン](https://huggingface.co/docs/transformers/main_classes/pipelines)の形態でロードすることができ、追加のトレーニングなしに生のテキストに直接適用することができます。[以前のブログ記事](https://qiita.com/taka_yayoi/items/84e446540277918b1a9c)ではこのトピックにディープダイブしています。

```py:Python
from transformers import pipeline

sentiment_pipeline = pipeline('sentiment-analysis')

records = ["Transformers on Databricks are the best!",
                 "Without Delta, our data lake has devolved into a data swamp!"]

for prediction in sentiment_pipeline(records):
  print(prediction)
```

![](https://cms.databricks.com/sites/default/files/inline-images/db-289-blog-img-12.png)
*ファインチューンされた分類パイプラインから感情を予測*

# まとめ

トランスフォーマーはパワフルかつアクセス可能で、Databricksレイクハウスプラットフォームはモデルのファミリーのトレーニングと管理に優れています。Delta Lakeは効率的かつ高精度な機械学習や分析のデータ基盤を提供します。フレンドリーなユーザーインタフェースを通じて、GPUインスタンスや[機械学習ランタイム](https://www.databricks.com/jp/product/machine-learning-runtime)を含む[クラスター](https://qiita.com/taka_yayoi/items/c5d99cd77fe4bfcf69f0)を配備する柔軟性は、データサイエンティストによるトランスフォーマーのトレーニングを強力に支援します。また、異なるモデルやトレーニング設定による実験は、[マネージドMLflow](https://www.databricks.com/jp/product/managed-mlflow)によって容易に管理されます。結果はクリアにドキュメント化され共有可能となり、作業内容が失われることは決してなく、最終モデルを容易にデプロイすることができます。

トランスフォーマーモデルのトレーニングと比較をスタートするには、[このリポジトリ](https://github.com/marshackVB/rapid_nlp_blog)をRepoとしてお使いのDatabricksワークスペースにクローンしてください。


### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)