!date
!python --version
2024年 6月21日 金曜日 15時30分29秒 JST
Python 3.9.6

32. transformers.pipelineを用いた推定例#

参考

基本的には pipeline(task="タスク名") としてタスクを指定するだけでそれに応じたモデルをダウンロードして利用することができる。しかしモデルは必ずしも日本語には対応していないため、場合によっては想定以上に推定結果が悪くなることもある。このノートブックでは「感情分析(極性推定)」を例にデフォルトのモデルを利用した場合の失敗例を示しつつ、それ以降は「適切に動きそうなモデルを探し、指定して利用する」形での実行例を示す。

from transformers import pipeline
import pandas as pd

32.1. 感情分析(極性推定)#

32.1.1. 失敗例#

タスクを指定し、モデルを指定しない場合にはデフォルトで想定されているモデルが自動でダウンロードされる。 この例では “distilbert/distilbert-base-uncased-finetuned-sst-2-english” が自動で選ばれている。 モデル名から分かるとおり英語で学習したモデルのため、日本語だとうまく推定できない。

# これはモデルマッチしていないため、うまく推定できない例
pipe = pipeline(task="sentiment-analysis")
text = "この映画はとても素晴らしい"
outputs = pipe(text)
outputs
No model was supplied, defaulted to distilbert/distilbert-base-uncased-finetuned-sst-2-english and revision af0f99b (https://huggingface.co/distilbert/distilbert-base-uncased-finetuned-sst-2-english).
Using a pipeline without specifying a model name and revision in production is not recommended.
[{'label': 'NEGATIVE', 'score': 0.889319896697998}]

32.1.2. モデルを指定した例#

https://huggingface.co/models からタグ「Text classification」を選び、”sentiment jp” ぐらいのキーワードで絞り込むと5件がヒットする。この中からmr4/bert-base-jp-sentiment-analysisを選んだ。

model_name = "mr4/bert-base-jp-sentiment-analysis"
pipe = pipeline(task="sentiment-analysis", model=model_name)
text = "この映画はとても素晴らしい"
outputs = pipe(text)
outputs
[{'label': 'positive', 'score': 0.9999532699584961}]
# 別例
text = "正直わかりずらい。むだに間があるし。"
outputs = pipe(text)
outputs
[{'label': 'negative', 'score': 0.999911904335022}]

32.2. 全ラベルに対するスコア#

オプションで “return_all_scores=True” を付けることで、全てのラベルに対するスコアを取得できる。

# 別例
text = "正直わかりずらい。むだに間があるし。"
outputs = pipe(text, return_all_scores=True)
outputs
/Users/tnal/.venv/opencalm/lib/python3.9/site-packages/transformers/pipelines/text_classification.py:104: UserWarning: `return_all_scores` is now deprecated,  if want a similar functionality use `top_k=None` instead of `return_all_scores=True` or `top_k=1` instead of `return_all_scores=False`.
  warnings.warn(
[[{'label': 'negative', 'score': 0.999911904335022},
  {'label': 'positive', 'score': 8.806267578620464e-05}]]

32.3. テキスト生成#

タグ「Text Generation」を選び、”jp” で絞り込み、「Most downloads」でソート。その中では小さめのモデル(1.3Bならメモリ1GBぐらいあれば動作する)であるllm-jp/llm-jp-1.3b-v1.0を選択。

model_name = "llm-jp/llm-jp-1.3b-v1.0"
pipe = pipeline(task="text-generation", model=model_name)
text = "ようやく晴れてきましたね。どこかに"
outputs = pipe(text)
outputs
/Users/tnal/.venv/opencalm/lib/python3.9/site-packages/torch/_utils.py:776: UserWarning: TypedStorage is deprecated. It will be removed in the future and UntypedStorage will be the only storage class. This should only matter to you if you are using storages directly.  To access UntypedStorage directly, use tensor.untyped_storage() instead of tensor.storage()
  return self.fget.__get__(instance, owner)()
/Users/tnal/.venv/opencalm/lib/python3.9/site-packages/transformers/generation/utils.py:1141: UserWarning: Using the model-agnostic default `max_length` (=20) to control the generation length. We recommend setting `max_new_tokens` to control the maximum length of the generation.
  warnings.warn(
[{'generated_text': 'ようやく晴れてきましたね。どこかに出かけたい気分です。 さて、今日'}]

32.4. 質問応答#

32.4.1. 失敗例(BERT系)#

タグ「Question Answering」を選び、”jp”で絞り込んだ中から1番目のモデル[jpabbuehl/distilbert-base-uncased-finetuned-squad]を選びました。BERT系は比較的小さなモデルですが、その分あまり精度は良くないかもしれません。(失敗している)

model_name = "jpabbuehl/distilbert-base-uncased-finetuned-squad"
pipe = pipeline(task="question-answering", model=model_name)
question = "先生はどこに住んでいますか?"
context = "病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。"
outputs = pipe(question=question, context=context)
outputs
{'score': 5.8764489949680865e-05,
 'start': 0,
 'end': 24,
 'answer': '病気の都合で車を運転することができなくなりました'}

32.4.2. 少し大きめなtext-generationモデルで回答させてみる#

text-generationモデルは入力の与え方を工夫することで質問応答モデルとしても利用できる。ここでは text-generation で用意した “llm-jp/llm-jp-1.3b-v1.0” を用いて質問文を用意してみた。

model_name = "llm-jp/llm-jp-1.3b-v1.0"
pipe = pipeline(task="text-generation", model=model_name, max_length=100)
question = "先生はどこに住んでいますか?"
context = "病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。"
inputs = f"question: {question}\ncontext: {context}\nanswer: "
outputs = pipe(inputs)
print(outputs)
[{'generated_text': 'question: 先生はどこに住んでいますか?\ncontext: 病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。\nanswer: 大学の近くに住んでいるので、大学の近くに住んでいると思います。\n\n'}]
# 用意した入力文の中身
print(inputs)
question: 先生はどこに住んでいますか?
context: 病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。
answer: 
# 別の与え方
question = "先生はどこに住んでいますか?"
context = "病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。"
inputs = f"質問: {question}\n関連情報: {context}\n回答: "
print(inputs)
print("---")
outputs = pipe(inputs)
print(outputs)
質問: 先生はどこに住んでいますか?
関連情報: 病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。
回答: 
---
[{'generated_text': '質問: 先生はどこに住んでいますか?\n関連情報: 病気の都合で車を運転することができなくなりました。徒歩で通勤できると楽になるため、大学の近くに住んでいます。\n回答: 大学の近くに住んでいるのであれば、大学の近くの病院に行ってください。\n\n'}]