Chat VectorにならぬCode Vectorは作れるのか
はじめに
Chat Vectorと呼ばれる、重みの足し引きでFine TuningなしにChat能力を事前学習モデルに付与できるという技術あります。
つまりこういうことですね。
ChatVector = Llama2-chat - Llama2
でChat能力を抽出し、
New-Model-chat = New-Model + ChatVector
でNew-ModelにChat能力を付与できます。(この時、New-ModelはLlama2の追加事前学習モデルである必要があります。)
これがシンプルかつ強力すぎて感動を覚えたので、Chat以外の例えばCoding能力やMath Reasoning能力でも同じことができるのかを簡単に試してみました。結果を先に書くと、Code Vectorはうまくいきませんでした。Code LlamaはLlama2に追加の事前学習をしており、流石にモデルの中身が変わりすぎていたのだと考えます。あくまでRLHFやInstruction Tuningのような出力のTuningに限っての手段なのかもしれません。
備忘録として書き残しておきます。
1. モデルの取得
# 英語ベースモデル
base_model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
device_map="cpu",
)
# Skilledモデル
skilled_model = AutoModelForCausalLM.from_pretrained(
"codellama/CodeLlama-7b-hf",
torch_dtype=torch.bfloat16,
device_map="cpu",
)
# 日本語ベースモデル
jp_model = AutoModelForCausalLM.from_pretrained(
"tokyotech-llm/Swallow-7b-hf",
torch_dtype=torch.bfloat16,
device_map="cuda",
)
2. Skillの抽出/保存
CodeLlamaからCoding能力を抽出し、SkillTreeという名前で保存しました。
# 除外対象
skip_layers = ["model.embed_tokens.weight", "model.norm.weight", "lm_head.weight"]
for k, v in base_model.state_dict().items():
# layernormも除外
if (k in skip_layers) or ("layernorm" in k):
continue
chat_vector = skilled_model.state_dict()[k] - base_model.state_dict()[k]
new_v = chat_vector.to(v.device)
v.copy_(new_v)
model_name = "HachiML/SkillTree-Code-llama2-7b-hf"
base_model.save_pretrained(f"./models/{model_name}", repo_id=model_name, push_to_hub=True)
抽出したスキルは以下に格納してあります。
3. Skillの付与/保存
抽出したSkillTreeをSwallow-7bに付与します。
# 除外対象
skip_layers = ["model.embed_tokens.weight", "model.norm.weight", "lm_head.weight"]
for k, v in base_model.state_dict().items():
# layernormも除外
if (k in skip_layers) or ("layernorm" in k):
continue
chat_vector = skilled_model.state_dict()[k] - base_model.state_dict()[k]
new_v = chat_vector.to(v.device)
v.copy_(new_v)
model_name = "HachiML/Swallow-7b-hf-CodeSkill"
jp_tokenizer.save_pretrained(f"./models/{model_name}", repo_id=model_name, push_to_hub=True)
jp_model.save_pretrained(f"./models/{model_name}", repo_id=model_name, push_to_hub=True)
スキル付与したモデルは以下に格納しました。
4. 検証
モデルを取得
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "HachiML/Swallow-7b-hf-CodeSkill"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.bfloat16, device_map="auto")
検証の実施
prompt = "東京工業大学の主なキャンパスは、"
input_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
tokens = model.generate(input_ids.to(device=model.device), max_new_tokens=128, temperature=0.99, top_p=0.95, do_sample=True)
out = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(out)
prompt = "def add(a, b):\n"
input_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
tokens = model.generate(input_ids.to(device=model.device), max_new_tokens=128, temperature=0.99, top_p=0.95, do_sample=True)
out = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(out)
prompt = "import socket\n\ndef ping_exponential_backoff(host: str):\n"
input_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
tokens = model.generate(input_ids.to(device=model.device), max_new_tokens=128, temperature=0.99, top_p=0.95, do_sample=True)
out = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(out)
prompt = """from typing import List\n\ndef filter_by_substring(strings: List[str], substring: str) -> List[str]:\n \"\"\" Filter an input list of strings only for ones that contain given substring\n >>> filter_by_substring([], 'a') []\n >>> filter_by_substring(['abc', 'bacd', 'cde', 'array'], 'a') ['abc', 'bacd', 'array']\n\"\"\""""
input_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
tokens = model.generate(input_ids.to(device=model.device), max_new_tokens=128, temperature=0.99, top_p=0.95, do_sample=True)
out = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(out)
結果はモデルが壊れたことを確認できました。