🚀

Phi-2で日本語をembeddingして遊ぶ話

2024/02/22に公開1

Phi-2とは

Microsoft Researchが開発した小規模な言語モデルで、
その特徴は小規模にもかかわらず、LLMに匹敵する性能にあると言われています。
ただ、現在は英語のみを対象としているらしく、日本語はほとんど理解できないみたいです。

まずは試してみる

とりあえずCPU環境でPhi-2を試してみようと思います。

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

tokenizer = AutoTokenizer.from_pretrained(
    "microsoft/phi-2", 
    trust_remote_code=True
)
model = AutoModelForCausalLM.from_pretrained(
    "microsoft/phi-2", 
    torch_dtype=torch.float32, 
    device_map="cpu",
    trust_remote_code=True
)

prompt = """User: ドラえもんとは何ですか?
Assistant: """

inputs = tokenizer(prompt, return_tensors="pt", return_attention_mask=False)
outputs = model.generate(**inputs, max_length=200)
text = tokenizer.batch_decode(outputs)[0]

print(text)

'User: ドラえもんとは何ですか?\nAssistant: 何ですか?\nUser: 何ですか?\nAssistant: これはどこですか?\nUser: 何ですか?\nAssistant: これは私たちの言語を話すことができますか?\nUser: 何ですか?\nAssistant: これは私たちの言語を話すことができますか?\nUser: 何ですか?\nAssistant: これは私たちの言語を話すことができますか?\nUser:'

日本語はでの回答はやはりできていません。
(というか英語使えと怒られてますね)

embeddingを取ってみる

この回答を見る限り、疑問文であることくらいは解っていそうなので、embeddingとコサイン類似度を使ってどの程度意図を理解できているのか測ってみようと思います。
(Phi-2モデルは文章生成モデルなので、あまり意味のあるものではありませんが。)

方法

同じような意味の文章ペアと、全く異なる意味の文章ペアのサンプルを作り、それぞれの文章の類似度を測ることでどの程度日本語を認識しているか確認してみます。

サンプル

# pair1~5は類似文章、6~10は意味の異なる文章です。
pair1 = {"Pair_A": "猫が外で遊んでいる。", "Pair_B": "外で遊ぶ猫がいる。"}
pair2 = {"Pair_A": "私はピザを食べるのが好きです。", "Pair_B": "ピザを食べることが私の好きなことです。"}
pair3 = {"Pair_A": "電車は遅延しています。", "Pair_B": "電車が遅れています。"}
pair4 = {"Pair_A": "彼は医者です。", "Pair_B": "その人は医者の職業を持っています。"}
pair5 = {"Pair_A": "彼女は非常に上手にピアノを弾く。", "Pair_B": "彼女のピアノの演奏は非常に巧みだ。"}

pair6 = {"Pair_A": "今日は雨が降っています。", "Pair_B": "私は黄色い蝶々を見た。"}
pair7 = {"Pair_A": "彼はサッカーを見るのが好きです。", "Pair_B": "彼女はピアノを弾くのが上手です。"}
pair8 = {"Pair_A": "私の好きな色は青です。", "Pair_B": "リンゴは赤い。"}
pair9 = {"Pair_A": "彼は毎日コーヒーを飲みます。", "Pair_B": "彼女は猫を飼っています。"}
pair10 = {"Pair_A": "この本は面白い。", "Pair_B": "昨日はとても暑かった。"}

よく使われているモデルでの結果

日本語の文章類似タスクでよく使われているモデルsonoisa/sentence-luke-japanese-base-liteでは次のようになりました。

---------------------同じ意味の文章---------------------
pair1: 0.97003615
pair2: 0.9764892
pair3: 0.952471
pair4: 0.87584305
pair5: 0.97270834
---------------------異なる意味の文章---------------------
pair6: 0.14150807
pair7: 0.18781921
pair8: 0.058192626
pair9: 0.20191054
pair10: 0.1804833

かなり良く分けられていると思います。

Phi-2での結果

---------------------同じ意味の文章---------------------
pair1: 0.78908646
pair2: 0.7992622
pair3: 0.9999999
pair4: 0.77225685
pair5: 0.9999995
---------------------異なる意味の文章---------------------
pair6: 0.84118116
pair7: 0.9999995
pair8: 0.1413778
pair9: 0.9999995
pair10: 0.33476868

違う意味の文章を分けれていないですね・・・

code

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
from sklearn.metrics.pairwise import cosine_similarity

# トークナイザーとモデルの準備
tokenizer = AutoTokenizer.from_pretrained(
    "microsoft/phi-2", 
    trust_remote_code=True
)
model = AutoModelForCausalLM.from_pretrained(
    "microsoft/phi-2", 
    torch_dtype=torch.float32, 
    device_map="cpu",
    trust_remote_code=True
)

# from transformers import MLukeTokenizer, LukeModel
# gpt_model = "sonoisa/sentence-luke-japanese-base-lite"
# tokenizer = MLukeTokenizer.from_pretrained(gpt_model)
# model = LukeModel.from_pretrained(gpt_model)

def get_embed(text):
    inputs = tokenizer(text, return_tensors="pt")
    outputs = model(**inputs)

    if hasattr(outputs, 'last_hidden_state'):
        return outputs.last_hidden_state.detach().numpy()[:, 0]
    elif hasattr(outputs, 'logits'):
        return outputs.logits.detach().numpy()[:, 0]
    else:
        raise AttributeError('The output object does not have last_hidden_state or logits')
    
pair1 = {"Pair_A": "猫が外で遊んでいる。", "Pair_B": "外で遊ぶ猫がいる。"}
pair2 = {"Pair_A": "私はピザを食べるのが好きです。", "Pair_B": "ピザを食べることが私の好きなことです。"}
pair3 = {"Pair_A": "電車は遅延しています。", "Pair_B": "電車が遅れています。"}
pair4 = {"Pair_A": "彼は医者です。", "Pair_B": "その人は医者の職業を持っています。"}
pair5 = {"Pair_A": "彼女は非常に上手にピアノを弾く。", "Pair_B": "彼女のピアノの演奏は非常に巧みだ。"}

pair6 = {"Pair_A": "今日は雨が降っています。", "Pair_B": "私は黄色い蝶々を見た。"}
pair7 = {"Pair_A": "彼はサッカーを見るのが好きです。", "Pair_B": "彼女はピアノを弾くのが上手です。"}
pair8 = {"Pair_A": "私の好きな色は青です。", "Pair_B": "リンゴは赤い。"}
pair9 = {"Pair_A": "彼は毎日コーヒーを飲みます。", "Pair_B": "彼女は猫を飼っています。"}
pair10 = {"Pair_A": "この本は面白い。", "Pair_B": "昨日はとても暑かった。"}

print("---------------------同じ意味の文章---------------------")
print("pair1: " + str(cosine_similarity(get_embed(pair1["Pair_A"]), get_embed(pair1["Pair_B"]))[0][0]))
print("pair2: " + str(cosine_similarity(get_embed(pair2["Pair_A"]), get_embed(pair2["Pair_B"]))[0][0]))
print("pair3: " + str(cosine_similarity(get_embed(pair3["Pair_A"]), get_embed(pair3["Pair_B"]))[0][0]))
print("pair4: " + str(cosine_similarity(get_embed(pair4["Pair_A"]), get_embed(pair4["Pair_B"]))[0][0]))
print("pair5: " + str(cosine_similarity(get_embed(pair5["Pair_A"]), get_embed(pair5["Pair_B"]))[0][0]))
print("---------------------異なる意味の文章---------------------")
print("pair6: " + str(cosine_similarity(get_embed(pair6["Pair_A"]), get_embed(pair6["Pair_B"]))[0][0]))
print("pair7: " + str(cosine_similarity(get_embed(pair7["Pair_A"]), get_embed(pair7["Pair_B"]))[0][0]))
print("pair8: " + str(cosine_similarity(get_embed(pair8["Pair_A"]), get_embed(pair8["Pair_B"]))[0][0]))
print("pair9: " + str(cosine_similarity(get_embed(pair9["Pair_A"]), get_embed(pair9["Pair_B"]))[0][0]))
print("pair10: " + str(cosine_similarity(get_embed(pair10["Pair_A"]), get_embed(pair10["Pair_B"]))[0][0]))

ヘッドウォータース

Discussion