🤗

Hugging Face Courseで学ぶ自然言語処理とTransformer 【part1】

2021/06/18に公開

はじめに

この記事はHugging Face CourseのIntroduction~Transformers, what can they do?あたりの内容を自身の見解なども合わせてまとめたものになります。

Hugging Faceって?

  • Hugging Faceとは主に自然言語処理のモデル開発やそれらのオープンソース提供を行っているアメリカの会社で、機械が人間と同じようにテキストを理解する技術開発に貢献することを目標としているそうです。私もこういう理念には強く共感できます。
  • Transformerを軸においた技術開発がメインに行われているようです。
  • ロゴがなんかかわいい。

Logo

Transformerについて

Transformerとは系列データを逐次処理で学習するのではなく、まとめて学習させられるように設計されたモデルで、それによってGPUによる並列処理の恩恵をより受けられるようになり、大規模なデータセットによる学習済みモデルが作られるようになりました。そしてそれらをもとにfine-tuningして個別のタスクへの転用が容易になったという経緯があり、自然言語タスクにおけるブレークスルーとも言われる技術です。

世の中には先人たちによってTransformerについて解説されている記事などもたくさんあるので、詳細についてはぜひ検索して参考にしていただくと良いと思います。(自分もそのうち記事にまとめます。。)

Hugging Face Course

自社のプラットフォームをより多くの人に活用してもらうため、教育コンテンツの提供も始めているようです。
Hugging Face Course

ということでちょっとやってみることに。

自然言語処理(Natural Language Processing)の簡単なおさらい

  • 自然言語処理(Natural Language Processing, NLP)は人が話す言語に関するあらゆる知的探究を、言語学および機械学習の視点からアプローチする分野です。
  • 単語それぞれの意味を理解するだけでなく、単語間の関係性や文脈といったコンテキストの理解も目指します。
  • 主なタスクの例としては以下のようなものがあります。
    1. テキスト分類(メールのスパム判定、レビューのスコア分類、文法的に正しいかの判定など)
    2. 系列ラベリング(単語の品詞分類、固有表現認識など)
    3. テキスト生成
    4. 質問応答(Q&A)
    5. テキスト変換(機械翻訳、文書要約など)
  • テキストのみに限らず、音声認識や画像キャプションなどのタスクも自然言語処理が関わってきます。

自然言語処理の難しさ

言語というのは曖昧性を含むもので、文の意味を一意に解釈するのはその文だけを見るだけでは不十分な場合が多いです。人間は周辺文脈から意味をある程度読み取ることができますが、機械はそうはいきません。
機械に人間と同レベルの意味理解を実現させるには、テキストに対する変換処理を適切に、十分に注意しながら行う必要があるのです。

Transformer pipelineを使って見よう

ここからはHugging Face CourseのTransformers, what can they do?に沿った内容になります。

自然言語処理の色々なタスクを解決するためにTransformerモデルは非常に有効で、活用ケースも増えつつあります。
Hugging Faceが提供するTransformersライブラリによって学習済みモデルを簡単に扱うことができるようになっています(凄い)。
今回はその中で最もベーシックなpipelineを試しに使ってみようというところになります。

pipelineに生のテキストデータを渡すことで以下のステップが実行されます。

  1. モデルへの入力形式に変換するための前処理が実行される。
  2. 変換された入力テキストがモデルへ入力される。
  3. モデルの推論結果が扱いやすい形に後処理されて出力される。

classification

まずはテキスト分類のpipelineの実行を試してみます。

環境構築に手間のかからないGoogle Colabで実行しました。

まずはtransformersライブラリをインストール。

!pip install transformers

そして下記を実行。(Hugging Face Courseの内容から入力するテキストは勝手に変えて試しています)

from transformers import pipeline

text = "I can't wait to go traveling abroad again."
classifier = pipeline("sentiment-analysis")
classifier(text)

出力結果

[{'label': 'POSITIVE', 'score': 0.9476662278175354}]

といった感じで、たったこれだけでテキストのネガポジ判定をしてくれました。
特に何も指定しなければ、英語の学習済みモデルがclassifierオブジェクト作成時にダウンロードされてキャッシュされるようです。

下記のようにすれば複数のテキストに対しても結果をまとめて返してくれます。

texts = [
         "I don't want to go out today because it's raining heavily.",
         "I want to go out today because it's such a sunny day.",
         "The movie I watched last week was surprisingly boring!",
         "The movie I watched last night was so exciting.",
]

classifier(texts)

出力結果

[{'label': 'NEGATIVE', 'score': 0.9942654967308044},
 {'label': 'POSITIVE', 'score': 0.9996583461761475},
 {'label': 'NEGATIVE', 'score': 0.9997628331184387},
 {'label': 'POSITIVE', 'score': 0.9997842311859131}]

ちょっとした文ならこれで十分良い性能が期待できそうですね。

別の言語の文章を分類したい場合は、その言語の学習済みモデルとtokenizerを別途指定する必要があるようです。

以下、日本語のテキストに対してColaboratoryでやる例です。

  1. 必要なライブラリのインストール
!pip install fugashi
!pip install ipadic
  1. モデルとtokenizerを指定[1]
from transformers import pipeline, AutoModelForSequenceClassification, BertJapaneseTokenizer

model = AutoModelForSequenceClassification.from_pretrained('daigo/bert-base-japanese-sentiment') 
tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
classifier = pipeline("sentiment-analysis",model=model,tokenizer=tokenizer)
  1. 試してみる
jp_texts = [
    "今日は雨が凄いので外出したくないな。",
    "今日は天気が良いので外に出かけたいな。",
    "先週見た映画はびっくりするぐらい退屈だったぜ!",
    "昨夜見た映画は面白かったなあ。"
]
classifier(jp_texts)

出力結果

[{'label': 'ポジティブ', 'score': 0.7394903898239136},
 {'label': 'ポジティブ', 'score': 0.9837973713874817},
 {'label': 'ネガティブ', 'score': 0.6697437167167664},
 {'label': 'ポジティブ', 'score': 0.9660218954086304}]

英語の時とほぼ同じ意味にしたつもりですが、やや精度に差がありますね。
とはいえ日本語でもこんなに簡単にテキスト分類モデルが利用できるのは素晴らしいと感じます。

その他利用可能なpipeline

  • feature-extraction (get the vector representation of a text)
  • fill-mask
  • ner (named entity recognition)
  • question-answering
  • sentiment-analysis
  • summarization
  • text-generation
  • translation
  • zero-shot-classification

などがあるようです。
ここでは気になったものをいくつかピックアップしてみました。

Zero-shot classification

テキスト分類のモデルを作るには、テキストデータに対して対象となるラベル付けを行い、それらを学習データとしてモデルに学習させる必要がある。しかし、大量のテキストデータに対してラベル付けを行うには労力が必要ですし、テキストの内容によっては専門知識が必要な場合もあります。
zero-shot classificationはその問題を解決する一つの方法で、ラベル付けしたテキストを用意することなく、分類したいラベルをpipelineに直接与えてやるだけで、そのラベルに対する推論結果を返してくれるというものです。

本当にそんなことできるのか、とりあえず試してみます。

from transformers import pipeline

classifier = pipeline("zero-shot-classification")
classifier(
    "The movie I watched last week was surprisingly boring!",
    candidate_labels=["politics", "sports", "entertainment"]
)

テキストのジャンルを3種類適当に設定して分類できるか試してみます。
※初回実行時はモデルのダウンロードに少々時間がかかります。

出力結果

{'labels': ['entertainment', 'science', 'politics', 'sports'],
 'scores': [0.8598918318748474,
  0.0586254745721817,
  0.04766526073217392,
  0.033817488700151443],
 'sequence': 'The movie I watched last week was surprisingly boring!'}

となりました。
映画のことを述べているテキストに対して"entertainment"が最も高いスコアになっており、正しく分類できていそうです。

ラベルの数を増やしてみました。

classifier(
    "The movie I watched last week was surprisingly boring!",
    candidate_labels=["love", "like", "neutral", "don't like", "hate"]
)

テキストがレビューだとして、5段階評価のうちどれに該当するかを推論できるかという感じです。

出力結果

{'labels': ["don't like", 'like', 'neutral', 'hate', 'love'],
 'scores': [0.7904922366142273,
  0.11177799850702286,
  0.08907864987850189,
  0.00606259610503912,
  0.002588509116321802],
 'sequence': 'The movie I watched last week was surprisingly boring!'}

"don't like"が最も高くなり、とりあえず間違ってはなさそうです。
"hate"のスコアが思ったよりも低いのが気になりましたが、もしかしたら"love"や"like"の意味合いとは若干異なるニュアンスを含む単語なのかもしれません。
テキストの内容を色々変えて、狙ったラベルを出力させられるかなど試してみると面白いかもしれませんね。

Text Generation

テキスト生成とは、モデルに出だしの数単語を与えて、それに続く部分を自動生成してテキスト全体を完成させるというものです。

from transformers import pipeline

generator = pipeline("text-generation")
generator("Artificial Intelligence is")

出力結果

[{'generated_text': 'Artificial Intelligence is a unique phenomenon that exists only among scientists, and it is very much in the public imagination.\n\nThis paper shows that artificial brains are highly capable of recognizing and acting as humans, and yet only one is capable of recognizing these'}]

論文の概要文ぽいテキストが生成されました。

もう一度同じ入力で試してみます。

[{'generated_text': 'Artificial Intelligence is just as fast-paced and agile as the AI from the Intel architecture as it is mobile and more efficient. And this is true even if the new machine does not actually run any code at all. When you run the same AI'}]

今度は、意味がよくわからないですが技術記事の書き出しみたいなテキストになりました。

このようにテキスト生成はランダム性を含むので、同じ入力でも実行のたびに出力が変化します。
実社会での活用を考えると出力を制御できないとなかなか使い勝手が悪くなってしまうため、
テキスト生成に関する主要な課題の一つにもなっています。[2]

pipelineではnum_return_sequencemax_lengthを指定することで、出力テキストの長さを制御することができるようです。

generator = pipeline("text-generation")
generator("Artificial Intelligence is", max_length=15, num_return_sequences=2)

この場合は単語数最大15のテキストを2個生成します。

出力結果

[{'generated_text': 'Artificial Intelligence is something that takes a fairly basic set of tools and puts'},
 {'generated_text': 'Artificial Intelligence is the subject of a new book, the Virtual Worlds,'}]

単語数制御できても文が未完成だったりするので、使うときは長めに出力してピリオドで切り取るとかした方が良いのかな・・・?

Translation

機械翻訳はGoogle翻訳やDeepLなど、他言語でちょっとわからない文章をさっと翻訳して意味を確認する分には十分便利で使えるものが出てきています。多言語での日常会話を補助する目的でも機械翻訳の活用場面は増えてきています。

一方、本の執筆、重要なドキュメント、論文など正確さが求められる文章を翻訳する際には、テキストをそのまま翻訳機にかけるだけではまだ不十分なケースが多く、人の介入が求められます。

pipelineを使う場合は、前者のちょっと補助的に使う目的の活用が適切かなと思います。

ここでは日本語→英語のモデルを試しに使ってみます。

  1. 必要なライブラリのインストール
!pip install sentencepiece==0.1.94

sentencepieceはtokenizerの一種で、テキストを単純に単語ごとに分割するのではなく、低頻度単語は更に細かい文字列へ分割するサブワードと呼ばれる分割方法の考え方を、テキスト全体に対して実施し、語彙数を減らそうというアプローチを取るものです。

TransformerベースのモデルのTokenizerにsentencepieceが使われるケースがちらほらあるようです。

  1. pipelineの実行
from transformers import pipeline

translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ja-en")
translator("自然言語処理は面白い")

出力結果

[{'translation_text': "It's fun to process natural language."}]

ちょっと微妙な結果ですが、まあ意味は合ってますね。笑

まとめ

その他の例についてはHugging Face CourseのTransformers, what can they do?で一通り紹介されているので、気になる方はチェックしてみてください。

いくつか試してみましたが、さくっと使えるレベルのpipelineはデモ目的な一面もあり、
特定のタスクにのみ最適化されたものがほとんどのようです。
pipelineをカスタマイズして、自分の解きたいタスクに最適化させるには更にステップを踏む必要があるようです。

また次回以降でみていきたいと思います。

脚注
  1. 参考ブログ https://note.com/npaka/n/ne0adde2d7ae8 ↩︎

  2. https://arxiv.org/abs/2005.01822 ↩︎

Discussion