見出し画像

CALM2で長い文章をまるごと取り扱う

こんばんは、あるふです。
GPTQ版CALM2が長い文章に思ったよりか強かったので、これは宣伝と忘備録がてら、使い方を整理していきます。

まず動かしてみる

CALM2とはサイバーエージェントが作った大規模言語モデルのことです。
GPTQとは大規模言語モデルを4bit量子化する技術です。
transformersのload_in_4bitと違うところは、モデルだけではなく、読み込む文章も*1、VRAMの消費を抑えてくれる働きがあります。
ただし、GPTQ版は癖が強いので、まず動かすところから大変です。
まず、GPTQ版CALM2の場合、GPTQとTritonをセットで入れましょう。
ここでローカルではなくColabで試したいひとはT4インスタンスでは動かないため、A100を使いましょう。そもそも長文をT4で取り扱うにはVRAM不足です。普通の対話で良いならば、通常版のCALM2を使ってください。

pip install auto-gptq[triton]==0.4.2 transformers==4.31.1

このとき、Tritonが正常にインストールされないと正常に動作しません。また、CALM2の仕様上、transformersが4.31.1以上でないとバグります。
次に、正常にインストールされたか確認するために、サンプルコードを動かしてみましょう。

from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer

model_name_or_path = "mmnga/cyberagent-calm2-7b-chat-GPTQ-calib-ja-1k"

# Tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)

# Model
model = AutoGPTQForCausalLM.from_quantized(model_name_or_path,
        use_safetensors=True,
        device="cuda:0",
        use_triton=True,
        quantize_config=None)

# Your test prompt
prompt = """
USER: 今日の夕食のレシピを紹介してください。
ASSISTANT: 
"""
input_ids = tokenizer.encode(prompt, return_tensors="pt")
output_ids=model.generate(
    input_ids=input_ids.to(model.device),
    max_new_tokens=300,
    do_sample=True,
    temperature=0.7,
)
print(tokenizer.decode(output_ids[0], skip_special_tokens=True))

このコードを実行すると、次のような結果が得られます

USER: 今日の夕食のレシピを紹介してください。
ASSISTANT: 

今日の夕食のレシピは「鶏肉と野菜の炒め物」です。

【材料】
・鶏もも肉 150g
・キャベツ 4分の1玉
・玉ねぎ 1個
・にんじん 1本
・ピーマン 2個
・サラダ油 大さじ1
・酒 大さじ1
・醤油 大さじ1
・砂糖 大さじ1
・塩 小さじ1/4
・コショウ 少々

【作り方】

1. 野菜を食べやすい大きさに切ります。
2. フライパンにサラダ油を熱し、鶏肉を炒めます。
3. 鶏肉に火が通ったら、野菜を加えて炒めます。
4. 野菜がしんなりしてきたら、酒と醤油を加え、砂糖と塩を加え、さらに炒めます。
5. 最後にコショウで味を整えたら完成です。

【ポイント】
鶏肉は皮目から焼くことで、カリカリに焼き上がります。野菜は火が通りやすいように小さめに切ることがポイントです。甘辛い味付けでご飯が進む一品です。

このような感じにちゃんとレシピが表示されればインストール成功です。お疲れ様です。もし、ColabのT4で動かすと、ここでTritonのエラーが発生し、表示されません。有料のA100に切り替えてください。A100だとここのとおりに動きます。さて、本題に入りましょう。

論文の本文まるごと読み込んでみる

さて、適当に長い文章を考えたときに、一番私が困るのは英語論文を読むときなんですよね。毎日何本も出てくるので、気が滅入ることがあります。そこで、論文をまるごとCALM2に読み込ませて、質問できるようにしてみます。ここで質問の例として、論文に大切な「新規性」についてまとめさせます。お題はLLaVAという「画像を読み込んで対話できるマルチモーダルモデル」の話です。PDF形式の論文を読み込むために次のパッケージを入れます。

pip install langchain pypdf

普通の論文は本文は8ページぐらいに収めるようにしているので、8ページ分だけ読み込みます。ソースコードは以下のとおりです。大体VRAMが24GBあれば、読み込めます。

import os

from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer

from langchain.document_loaders import PyPDFLoader

model_name_or_path = "mmnga/cyberagent-calm2-7b-chat-GPTQ-calib-ja-1k"

# Tokenizer
os.environ["TOKENIZERS_PARALLELISM"]="true"
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)

loader = PyPDFLoader("2304.08485.pdf")
data = loader.load()

text=""
for page in range(8):
    text+=data[page].page_content

text=text.replace("\n"," ")

# Model
model = AutoGPTQForCausalLM.from_quantized(model_name_or_path,
        use_safetensors=True,
        device="cuda:0",
        use_triton=True,
        quantize_config=None)

# Your test prompt
prompt = f"""次の論文を読み、ユーザーからの質問に答えなさい。

{text}

USER: この論文の新規性を答えなさい。
ASSISTANT: 
"""
input_ids = tokenizer.encode(prompt, return_tensors="pt")
print(input_ids.size())
output_ids=model.generate(
    input_ids=input_ids.to(model.device),
    max_new_tokens=256,
    do_sample=True,
    temperature=0.7,
)
print(tokenizer.decode(output_ids[0], skip_special_tokens=True))

うまく実行できれば次の通りの出力が得られるはずです。

次の論文を読み、ユーザーからの質問に答えなさい。

(LLaVAの論文8ページ分の文字列。約1万トークン。)

USER: この論文の新規性を答えなさい。
ASSISTANT:
この論文は、Large Language Model(LLM)を使用して、視覚的な情報を理解し、自然言語で記述された命令に従う能力を開発するという新しい概念を提案しています。具体的には、視覚的入力から生成された自然言語を「instruction tuning」することによって、LLMは視覚入力の認識と自然言語の適応能力を向上させることができます。

このように、きれいに出力される場合もありますが、ハルシネーションを起こして変な回答をすることが多いので、予め、アブストラクトを読んでおいて、方向性が間違っていないか確認を取ったほうがいいでしょう。

議事録丸ごと読み込んでみる

次にそれなりに長い議事録をまとめてみましょう。第一回AI時代の知的財産権検討会の議事録を使います。理由はちょうどいい長さであって、個人的に内容を知りたいからです。特にここでは奥邨先生の発言をまとめてもらいます。ソースコードは以下のとおりです。今回は、pypdfが正常に日本語を処理できないので、文面をコピペしてテキストファイルにしています。大体VRAMが24GBあれば、読み込めるかもしれません。48GBで動作確認しました。

import os

from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer

from langchain.document_loaders import PyPDFLoader

model_name_or_path = "mmnga/cyberagent-calm2-7b-chat-GPTQ-calib-ja-1k"

# Tokenizer
os.environ["TOKENIZERS_PARALLELISM"]="true"
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)

with open("ai.txt") as f:
    text=f.read()

# Model
model = AutoGPTQForCausalLM.from_quantized(model_name_or_path,
        use_safetensors=True,
        device="cuda:1",
        use_triton=True,
        quantize_config=None)

# Your test prompt
prompt = f"""次の議事録を読んで、ユーザからの質問に答えなさい。なお、この質問は国にとって重要な役割を果たすので、がんばってください。

{text}

USER: 奥邨委員の主張を100文字程度にまとめなさい。
ASSISTANT: 
"""
input_ids = tokenizer.encode(prompt, return_tensors="pt")
print(input_ids.size())
output_ids=model.generate(
    input_ids=input_ids.to(model.device),
    max_new_tokens=256,
    do_sample=True,
    temperature=0.7,
)
print(tokenizer.decode(output_ids[0], skip_special_tokens=True))

次のように出力されました。

次の議事録を読んで、ユーザからの質問に答えなさい。なお、この質問は国にとって重要な役割を果たすので、がんばってください。

(議事録約4万字)

USER: 奥邨委員の主張を100文字程度にまとめなさい。
ASSISTANT: 

奥邨委員は、AIと知的財産権について、著作権法における創作要件、つまり、AIが生成した作品が「自動的な創作」ではなく「人による創作」にあたるかどうかを問題にしました。また、AI創作物の特徴である「類似性・依拠性」についても言及し、AIと著作権法の関係を再考する必要があると主張しました。

奥邨先生のことを考えるとあっているようなあってないような気がしますが、ここではそれっぽく動いたということで良しとします。なお、本当に当たっているかどうかは保証できません。ハルシネーションかもしれません。このようにすごく長い議事録でも情報を要約して抽出できているような気がします。

追記:小説をまるごと読み込ませて
読書感想文を書かせてみる

夏休みも終わりましたが、なんとなくやってみたかったので、やりました。走れメロスを題材に読書感想文を書かせてみました。VRAMは24GBでたりました。ソースコードは以下のとおりです。

import os

from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer

model_name_or_path = "mmnga/cyberagent-calm2-7b-chat-GPTQ-calib-ja-1k"

# Tokenizer
os.environ["TOKENIZERS_PARALLELISM"]="true"
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)

with open("meros.txt") as f:
    text=f.read()

# Model
model = AutoGPTQForCausalLM.from_quantized(model_name_or_path,
        use_safetensors=True,
        device="cuda:0",
        use_triton=True,
        quantize_config=None)

# Your test prompt
prompt = f"""次の短編小説を読み、ユーザーからの質問に答えなさい。この応答はユーザの学校の成績に関わるためとても重要です。

{text}

USER: この小説の読書感想文を 400字以内にまとめて新しく生成してください。
ASSISTANT: 
"""
input_ids = tokenizer.encode(prompt, return_tensors="pt")
output_ids=model.generate(
    input_ids=input_ids.to(model.device),
    max_new_tokens=1024,
    do_sample=True,
    temperature=0.7,
)
print(tokenizer.decode(output_ids[0], skip_special_tokens=True))

出力は以下の通りとなりました。

次の短編小説を読み、ユーザーからの質問に答えなさい。この応答はユーザの学校の成績に関わるためとても重要です。

(青空文庫の走れメロスをコピペした内容。約1万文字。)

USER: この小説の読書感想文を 400字以内にまとめて新しく生成してください。
ASSISTANT: 

タイトル:走れメロス

物語の概要:太宰治による作品で、古代ローマの詩人オウィディウスの「転身物語」から着想を得た作品です。メロスは友人セリヌンティウスを助けるため、約束を果たすために走り続ける物語で、多くの人々に愛されています。

感想:

まず、読者の皆さんに伝えたいのは、この作品の登場人物の心情や行動に感動しました。メロスは約束を果たすために、命をかけて走り続けました。彼の行動は、本当に勇敢で、感動しました。彼は約束を果たすためだけに、自分自身を犠牲にしました。

また、メロスが約束を果たすために走り続ける姿は、私たちが困難を乗り越えるために必要な勇気を教えてくれました。物語の中で、彼は自分自身を責めたり、疑ったりすることがありましたが、彼は最後まで諦めずに、約束を果たしました。その姿には、多くの人が感銘を受けました。

私が特に感動したのは、メロスが約束を果たす前に、彼自身が疑いや自己嫌悪に陥ったことです。しかし、彼は自分自身を責めながらも、他人に対する信頼と友情を大切にし、約束を果たすために走り続けました。そのプロセスを通じて、彼は自己の信念を再確認し、自分自身に誇りを持つことができました。

また、この作品では、他人に対する信頼と友情の大切さが描かれています。メロスは、彼の友人であるセリヌンティウスを信じ、彼のために行動しました。その結果、彼らは互いに信頼を深め、友情を築くことができました。

最後に、この作品からは、困難を乗り越えるためには、自分自身を信じ、他人に対する信頼と友情を大切にし、最後まで諦めずに立ち向かうことの大切さを教えてくれました。私は、この物語から多くのことを学ぶことができました。ありがとうございました。

そのまま出すと先生に怒られそうなので、加工して使ってください。

CALM2を使うメリット

ローカルで動かしているので、情報漏洩の心配がないことです。また、コンテキスト長32,000トークンと長いので、RAGとは違い文章を丸々理解した上で回答してくれます。
しかし、ハルシネーションが多く、現状では信頼するのは難しい印象です。今後、このような方向に大規模言語モデルが発達していけば、もっと信頼できるものになるでしょう。

まとめ

GPTQ版CALM2が思ったより長文読解に強かったので、使い方を紹介しました。仕事や遊びに使ってみてください。

脚注

*1 厳密に言うとtokenize.encodeの時点では4bitでもなんでもないです。model.generate()でtorch.amp.autocast(device_type=self.device.type)が実装を見る限りかかっているため、VRAMの消費が抑えられているとおもわれます。一方で、load_in_4bitでは実際VRAMの消費が多いため、model.generate()の時点ではかかっていなさそうに見えます。transformersの実装が難解なため、このところは正直わかりません。4bitと断言してすいませんでした。


この記事が気に入ったらサポートをしてみませんか?