自然言語処理が簡単にできるhugging faceで言語分類モデルを作成

自然言語処理を簡単にしてみたいです

hugging faceを使えば簡単にできるぞ

使ってみたいですね

では使い方をレクチャーするぞ

Hugging Faceについて

hugging faceはTransoformerやGPTなど自然言語処理で高い性能を発揮したモデルを簡単に使用できるライブラリです。

使用できるモデル一覧は下記です。

https://huggingface.co/transformers/pretrained_models.html

日本語のモデルは下記になります。

  • cl-tohoku/bert-base-japanese-char-whole-word-masking
  • cl-tohoku/bert-base-japanese-char
  • cl-tohoku/bert-base-japanese-whole-word-masking
  • cl-tohoku/bert-base-japanese

各モデルで用意されているタスク一覧は下記です。

https://huggingface.co/transformers/task_summary.html

  • テキスト生成
  • テキスト分類
  • 要約

下記にあるGoogle Colabのtoken-classificationを試してみます。

https://huggingface.co/transformers/examples.html

Google Colabで動作しやすいように直リンクが用意されています。

トークン識別

下記で必要なライブラリを取得します。

! pip install datasets transformers

使用するデータ・セットはGLUEになります。こちらのデータ・セットは複数のタスクで使用可能になります。

  • CoLA (Corpus of Linguistic Acceptability) 文章が文法的に正しいかどうかを判断するタスクです。
  • MNLI (Multi-Genre Natural Language Inference) 文章が特定の仮設を伴うか矛盾するか、無関係であるかを判断するタスクです。
  • MRPC (Microsoft Research Paraphrase Corpus) 2つの文章が言い換えられているかどうかを判断するタスクです。
  • QNLI (Question-answering Natural Language Inference) 質問に対する答えが2番めの文章にあるか判定するタスクです。
  • QQP (Quora Question Pairs2) 2つの質問が意味的に同等であるかどうかを判断します。
  • RTE (Recognizing Textual Entailment) 文が特定の仮説を伴うかどうかを判断します。
  • SST-2 (Stanford Sentiment Treebank) 文の感情がポジティブかネガティブかを判断します。
  • STS-B (Semantic Textual Similarity Benchmark) スコアを1から5で2つの文の類似性を判別します。
  • WNLI (Winograd Natural Language Inference) 匿名の代名詞を含む文と、この代名詞が置き換えられた文が含まれるかどうかを判別します

コード上ではlistで各タスクを表しています。

GLUE_TASKS = ["cola", "mnli", "mnli-mm", "mrpc", "qnli", "qqp", "rte", "sst2", "stsb", "wnli"]

今回は文章が文法的に正しいかどうかを判断するタスクを行います。

task = "cola"
model_checkpoint = "distilbert-base-uncased"
batch_size = 16

データ・セットを取得します。datasetsというライブラリを使用すると簡単にデータを取得できます。

データ・セットの確認

glueのデータ・セットで’cola’タスクのデータ・セットを取得します。

from datasets import load_dataset, load_metric

actual_task = "mnli" if task == "mnli-mm" else task
dataset = load_dataset("glue", actual_task)
metric = load_metric('glue', actual_task)

dataset

データ・セットの情報は下記になります。train, validation, test のデータ・セットが用意されています。

それぞれの特徴量はsentence, label, idx になります。

DatasetDict({
    train: Dataset({
        features: ['sentence', 'label', 'idx'],
        num_rows: 8551
    })
    validation: Dataset({
        features: ['sentence', 'label', 'idx'],
        num_rows: 1043
    })
    test: Dataset({
        features: ['sentence', 'label', 'idx'],
        num_rows: 1063
    })
})

実際にデータを確認すると下記のようになります。

dataset["train"][0]

文章のIDとラベル、文章を確認できます。

{'idx': 0,
 'label': 1,
 'sentence': "Our friends won't buy this analysis, let alone the next one we propose."}

下記のコードで文章をランダムにピックアップして、表示することができます。

import datasets
import random
import pandas as pd
from IPython.display import display, HTML

def show_random_elements(dataset, num_examples=10):
    assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
    picks = []
    for _ in range(num_examples):
        pick = random.randint(0, len(dataset)-1)
        while pick in picks:
            pick = random.randint(0, len(dataset)-1)
        picks.append(pick)
    
    df = pd.DataFrame(dataset[picks])
    for column, typ in dataset.features.items():
        if isinstance(typ, datasets.ClassLabel):
            df[column] = df[column].transform(lambda i: typ.names[i])
    display(HTML(df.to_html()))

show_random_elements(dataset["train"])

前処理

文章をトークナイズするための処理です。下記の関数では自動的に使用するモデルに適したトークナイザーを取得しています。

from transformers import AutoTokenizer
    
model_checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, use_fast=True)

下記がトークナイズ処理をしている結果です。文字列を数字変換するとともに学習に必要なAttention Maskの作成も行っています。

tokenizer("Hello, this one sentence!", "And this sentence goes with it.")
{'input_ids': [101, 7592, 1010, 2023, 2028, 6251, 999, 102, 1998, 2023, 6251, 3632, 2007, 2009, 1012, 102], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}

各タスクごとに取得する情報を設定します。

task_to_keys = {
    "cola": ("sentence", None),
    "mnli": ("premise", "hypothesis"),
    "mnli-mm": ("premise", "hypothesis"),
    "mrpc": ("sentence1", "sentence2"),
    "qnli": ("question", "sentence"),
    "qqp": ("question1", "question2"),
    "rte": ("sentence1", "sentence2"),
    "sst2": ("sentence", None),
    "stsb": ("sentence1", "sentence2"),
    "wnli": ("sentence1", "sentence2"),
}

どのような情報が取得できるか確認します。cola sentence の情報しかないので下記のコードで動作確認するとsentence のみ取得していることが確認できます。

sentence1_key, sentence2_key = task_to_keys[task]
if sentence2_key is None:
    print(f"Sentence: {dataset['train'][0][sentence1_key]}")
else:
    print(f"Sentence 1: {dataset['train'][0][sentence1_key]}")
    print(f"Sentence 2: {dataset['train'][0][sentence2_key]}")
Sentence: Our friends won't buy this analysis, let alone the next one we propose.

前処理コードを用意します。

このコードでデータに前処理を加えます。

def preprocess_function(examples):
    if sentence2_key is None:
        return tokenizer(examples[sentence1_key], truncation=True)
    return tokenizer(examples[sentence1_key], examples[sentence2_key], truncation=True)

取得したデータに前処理を行います。この記述でまとめてデータに対してトークナイズ処理がかけられるため便利な手法になります。

encoded_dataset = dataset.map(preprocess_function, batched=True)

モデルの学習

下記のコードで学習済みモデルを取得します。

文章識別の学習済みモデルを取得し、最終層を変更しています。

‘cola’のタスクで文章が文法的に正しいか正しくないかを判断しているため、ラベル数は2になります。

from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer

model_checkpoint = "distilbert-base-uncased"
num_labels = 3 if task.startswith("mnli") else 1 if task=="stsb" else 2
model = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, num_labels=num_labels)

モデルの学習をするための関数を設定します。

学習時の性能を測るためmatthews_correlationを設定しています。2 値分類の性能を測るために用いられる尺度です。

2値分類はデータの偏りがあると精度では正確に性能を測れないため、この尺度を用います。

metric_name = "pearson" if task == "stsb" else "matthews_correlation" if task == "cola" else "accuracy"

args = TrainingArguments(
    "test-glue",
    evaluation_strategy = "epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    num_train_epochs=5,
    weight_decay=0.01,
    load_best_model_at_end=True,
    metric_for_best_model=metric_name,
)

学習中に性能を計測できるようにmatthews_correlationを計測するための関数を設定しておきます。

def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    if task != "stsb":
        predictions = np.argmax(predictions, axis=1)
    else:
        predictions = predictions[:, 0]
    return metric.compute(predictions=predictions, references=labels)

下記コードで学習するモデル、学習データなどを設定します。

validation_key = "validation_mismatched" if task == "mnli-mm" else "validation_matched" if task == "mnli" else "validation"
trainer = Trainer(
    model,
    args,
    train_dataset=encoded_dataset["train"],
    eval_dataset=encoded_dataset[validation_key],
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

下記のコード1行で学習ができます。

trainer.train()

下記のように学習時のロスとmatthews_correlationの値を確認できます。

学習したモデルの性能を確認します。

trainer.evaluate()
{'eval_loss': 0.6563718318939209,
 'eval_matthews_correlation': 0.5432575763528743,
 'epoch': 5.0,
 'total_flos': 354601098011100}

学習データの取得、前処理、学習済みモデルの提供まで簡単に行えるようになっていたぞ

今回のデータは英語でしたよね。日本語も簡単にできると良いのですが・・・・

日本語はまた別記事で取り上げるぞ

お、あるんですね!!

Hugging Faceを用いて日本語のデータのポジネガ分析を行い方は下記をご覧ください。

Close Bitnami banner
Bitnami