Uploaded model
- Developed by: Chrom256
- License: gemma
- Finetuned from model : google/gemma-2-9b
This gemma2 model was trained 2x faster with Unsloth and Huggingface's TRL library.
概要
GoogleのGemma-2-9bをベースに、LoRAを使用して最適化した日本語対応モデルです。
本モデルは東京大学松尾・岩澤研究室大 規模言語モデル Deep Learning 応用講座(2024)におけるLLMコンペティション提出用に作成されたものです。
訓練データ
以下のデータセットを用いてInstruction-tuningを実施しました。 Googleより公開されている、Gemma用の指示チューニング用フォーマットを参考にしました。
- llm-jp/magpie-sft-v1.0 (Apache License 2.0)
全て使用しました
- Aratako/Magpie-Tanuki-8B-annotated-96k (Apache License 2.0)
primary_tagでCoding & Debuggingに該当するものは除外し、その上でinput_qualityがgood、excellentに該当するものを使用しました
使用方法
Google Colabで実行する事を想定してコードを作成しました。
実行コード全文のコードをご参照ください。
実行環境に応じて修正をお願いします。
実行前準備
シークレットにHF_TOKENを配置してください
- 名前に'HF_TOKEN'を入力
- 値にHugging Faceトークンを入力して保存
ファイルタブ内にelyza-tasks-100-TV_0.jsonlを配置してください L4でのコード実行時間は全体で約45分でした。
事前準備(環境構築、モデルのロード等):約18分
推論:約27分
メモリが不足する事はありませんでした。 出力物は新規作成されるoutputファイルの中に、Output.jsonl、として出力されます。
通信環境によっては出力されたOutput.jsonlファイルが見えないことがありますが、ファイルタブ内での再読み込み等により表示、ダウンロードできる事を複数回の検証により確認しています。
ライセンス
本モデルは利用したモデル、訓練データの関係で以下のライセンスの影響を受けます。
- Gemma Terms of Use
- 使用した訓練データのライセンス
本モデルはコンペティション提出用に作成されたものであり、それ以外の用途での使用は想定しておりません。その点、ご注意頂けますと幸いです。
導入・推論用コードの解説
正確なコードは「実行用コード全文」の項目をご参照ください。Google Colab上で最後まで問題なく実行できる事を確認しております。 以下、コードの解説です。
環境構築部分
Google Colab以外で実行する際には、HF_TOKENは直接コード中に記載してください。 現在はモデルがPublicとなっているため、HF_TOKENは必要ないかもしれません。 しかし、コンペに提出したJSONLファイルを出力する際に使用したコードと、全く同じコードを提出するのが望ましいと判断し、記述を残したままにしています。
!pip install -q transformers==4.46.3 accelerate bitsandbytes
!pip install -q tqdm
!pip install flash-attn --no-build-isolation
import os
import torch
import json
from tqdm import tqdm
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from torch.cuda.amp import autocast
from concurrent.futures import ThreadPoolExecutor
import threading
#モデルをロードするのに必要なHF_TOKENの読み込み
from google.colab import userdata
HF_TOKEN = userdata.get('HF_TOKEN')
if HF_TOKEN is None:
raise ValueError("HF_TOKENが設定されていません。上記の手順でトークンを設定してください。")
モデルの準備部分
ロードを高速化する目的でPythonのThreadPoolExecutorクラスを使用し、マルチスレッド処理を実装しました。
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
#モデルのロード
def load_model_and_tokenizer():
model_id = "Chrom256/gemma-2-9b-it-lora_20241216_033631"
base_model_id = "google/gemma-2-9b"
downloaded_components = {"model": None, "tokenizer": None}
download_lock = threading.Lock()
def download_base_model():
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(
base_model_id,
quantization_config=quantization_config,
device_map="auto",
trust_remote_code=True,
torch_dtype=torch.bfloat16,
attn_implementation="eager",
low_cpu_mem_usage=True,
token=HF_TOKEN
)
with download_lock:
downloaded_components["model"] = model
def download_tokenizer():
tokenizer = AutoTokenizer.from_pretrained(
model_id,
trust_remote_code=True,
token=HF_TOKEN
)
with download_lock:
downloaded_components["tokenizer"] = tokenizer
torch.cuda.empty_cache()
#ロードを高速化する目的でThreadPoolExecutorを使用しました
with ThreadPoolExecutor(max_workers=2) as executor:
model_future = executor.submit(download_base_model)
tokenizer_future = executor.submit(download_tokenizer)
model_future.result()
tokenizer_future.result()
model = downloaded_components["model"]
tokenizer = downloaded_components["tokenizer"]
#GPUのキャッシュをクリアする事で、メモリが足りなくなる状況を防ぎます
torch.cuda.empty_cache()
#エラー発生対策
try:
adapter_path = model_id
print(f"Loading adapter from {adapter_path}")
model.load_adapter(adapter_path, "default", token=HF_TOKEN)
print("Adapter loaded successfully")
except Exception as e:
print(f"Error loading adapter: {e}")
raise
model.config.use_cache = True
model.eval()
torch.cuda.empty_cache()
return model, tokenizer
推論部分
簡潔で装飾や特殊記号を含まない出力が出るようプロンプトを設定すると共に、コンペルール内で出力の整形を行っています。
def run_inference(model, tokenizer, tokenized_inputs, generation_config, batch_size=4):
results = []
for i in tqdm(range(0, len(tokenized_inputs), batch_size)):
batch = tokenized_inputs[i:i+batch_size]
prompts = [
f"""<start_of_turn>system
簡潔に回答してください。装飾や特殊記号は使用しないでください。
<end_of_turn>
<start_of_turn>user
{item["input"]}
<end_of_turn>
<start_of_turn>model
""" for item in batch
]
inputs = tokenizer(
prompts,
padding=True,
truncation=True,
return_tensors="pt"
).to(model.device)
with torch.no_grad(), autocast(dtype=torch.bfloat16):
outputs = model.generate(
**inputs,
pad_token_id=tokenizer.pad_token_id,
eos_token_id=tokenizer.eos_token_id,
**generation_config
)
for idx, output in enumerate(outputs):
response = tokenizer.decode(output, skip_special_tokens=True)
if 'model\n' in response:
response = response.split('model\n')[-1].strip()
elif 'model' in response:
response = response.split('model')[-1].strip()
response = post_process_output(response)
results.append({
"task_id": batch[idx]["task_id"],
"input": batch[idx]["input"],
"output": response
})
del outputs, inputs
torch.cuda.empty_cache()
return results
#出力には余計な装飾が含まれている事があったため、コンペルールに従った方法で出力を整形
def post_process_output(response):
response = response.strip()
symbols_to_replace = ['**', '`', '|', '```', '---', '===']
for symbol in symbols_to_replace:
response = response.replace(symbol, ' ')
return ' '.join(response.split())
#設定
GENERATION_CONFIG = {
"max_new_tokens": 512,
"use_cache": True,
"do_sample": False,
"num_beams": 4,
"repetition_penalty": 1.2,
"length_penalty": 1.0,
"early_stopping": False
}
他の関数
問題ファイルを読み込む関数と、出力をJSONL形式で出力する関数です。
#問題の読み込み
def load_input_data(file_path):
tokenized_inputs = []
with open(file_path, "r") as f:
for line in f:
if line.strip():
dt = json.loads(line)
tokenized_inputs.append({
"task_id": dt["task_id"],
"input": dt["input"]
})
return tokenized_inputs
#出力の保存
def save_results(results, output_dir):
os.makedirs(output_dir, exist_ok=True)
jsonl_path = os.path.join(output_dir, "Output.jsonl")
with open(jsonl_path, 'w', encoding='utf-8') as f:
for item in results:
json.dump(item, f, ensure_ascii=False)
f.write('\n')
print(f"Saved results to: {jsonl_path}")
全て実行
以上の関数を順番に実行
def main():
#モデルのロード
model, tokenizer = load_model_and_tokenizer()
#推論する問題の読み込み
tokenized_inputs = load_input_data("/content/elyza-tasks-100-TV_0.jsonl")
#推論実行
results = run_inference(model, tokenizer, tokenized_inputs, GENERATION_CONFIG)
#結果を保存
save_results(results, "output")
if __name__ == "__main__":
main()
実行用コード全文
推論用のコード全文を提示します。
!pip install -q transformers==4.46.3 accelerate bitsandbytes
!pip install -q tqdm
!pip install flash-attn --no-build-isolation
import os
import torch
import json
from tqdm import tqdm
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from torch.cuda.amp import autocast
from concurrent.futures import ThreadPoolExecutor
import threading
print("【重要】以下の手順でHugging Faceトークンを設定しておいてください")
print("1. 左メニューの'シークレット'タブを開く")
print("2. '新しいシークレット'をクリック")
print("3. 名前に'HF_TOKEN'を入力")
print("4. 値にHugging Faceトークンを入力して保存")
print("ファイルタブ内にelyza-tasks-100-TV_0.jsonlを配置しておいてください")
print("出力物は、新規に作成されるoutputファイルの中に格納されます")
from google.colab import userdata
HF_TOKEN = userdata.get('HF_TOKEN')
if HF_TOKEN is None:
raise ValueError("HF_TOKENが設定されていません。上記の手順でトークンを設定してください。")
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
def load_model_and_tokenizer():
model_id = "Chrom256/gemma-2-9b-it-lora_20241216_033631"
base_model_id = "google/gemma-2-9b"
downloaded_components = {"model": None, "tokenizer": None}
download_lock = threading.Lock()
def download_base_model():
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(
base_model_id,
quantization_config=quantization_config,
device_map="auto",
trust_remote_code=True,
torch_dtype=torch.bfloat16,
attn_implementation="eager",
low_cpu_mem_usage=True,
token=HF_TOKEN
)
with download_lock:
downloaded_components["model"] = model
def download_tokenizer():
tokenizer = AutoTokenizer.from_pretrained(
model_id,
trust_remote_code=True,
token=HF_TOKEN
)
with download_lock:
downloaded_components["tokenizer"] = tokenizer
torch.cuda.empty_cache()
with ThreadPoolExecutor(max_workers=2) as executor:
model_future = executor.submit(download_base_model)
tokenizer_future = executor.submit(download_tokenizer)
model_future.result()
tokenizer_future.result()
model = downloaded_components["model"]
tokenizer = downloaded_components["tokenizer"]
torch.cuda.empty_cache()
try:
adapter_path = model_id
print(f"Loading adapter from {adapter_path}")
model.load_adapter(adapter_path, "default", token=HF_TOKEN)
print("Adapter loaded successfully")
except Exception as e:
print(f"Error loading adapter: {e}")
raise
model.config.use_cache = True
model.eval()
torch.cuda.empty_cache()
return model, tokenizer
def run_inference(model, tokenizer, tokenized_inputs, generation_config, batch_size=4):
results = []
for i in tqdm(range(0, len(tokenized_inputs), batch_size)):
batch = tokenized_inputs[i:i+batch_size]
prompts = [
f"""<start_of_turn>system
簡潔に回答してください。装飾や特殊記号は使用しないでください。
<end_of_turn>
<start_of_turn>user
{item["input"]}
<end_of_turn>
<start_of_turn>model
""" for item in batch
]
inputs = tokenizer(
prompts,
padding=True,
truncation=True,
return_tensors="pt"
).to(model.device)
with torch.no_grad(), autocast(dtype=torch.bfloat16):
outputs = model.generate(
**inputs,
pad_token_id=tokenizer.pad_token_id,
eos_token_id=tokenizer.eos_token_id,
**generation_config
)
for idx, output in enumerate(outputs):
response = tokenizer.decode(output, skip_special_tokens=True)
if 'model\n' in response:
response = response.split('model\n')[-1].strip()
elif 'model' in response:
response = response.split('model')[-1].strip()
response = post_process_output(response)
results.append({
"task_id": batch[idx]["task_id"],
"input": batch[idx]["input"],
"output": response
})
del outputs, inputs
torch.cuda.empty_cache()
return results
def post_process_output(response):
response = response.strip()
symbols_to_replace = ['**', '`', '|', '```', '---', '===']
for symbol in symbols_to_replace:
response = response.replace(symbol, ' ')
return ' '.join(response.split())
GENERATION_CONFIG = {
"max_new_tokens": 512,
"use_cache": True,
"do_sample": False,
"num_beams": 4,
"repetition_penalty": 1.2,
"length_penalty": 1.0,
"early_stopping": False
}
def load_input_data(file_path):
tokenized_inputs = []
with open(file_path, "r") as f:
for line in f:
if line.strip():
dt = json.loads(line)
tokenized_inputs.append({
"task_id": dt["task_id"],
"input": dt["input"]
})
return tokenized_inputs
def save_results(results, output_dir):
os.makedirs(output_dir, exist_ok=True)
jsonl_path = os.path.join(output_dir, "Output.jsonl")
with open(jsonl_path, 'w', encoding='utf-8') as f:
for item in results:
json.dump(item, f, ensure_ascii=False)
f.write('\n')
print(f"Saved results to: {jsonl_path}")
def main():
model, tokenizer = load_model_and_tokenizer()
tokenized_inputs = load_input_data("/content/elyza-tasks-100-TV_0.jsonl")
results = run_inference(model, tokenizer, tokenized_inputs, GENERATION_CONFIG)
save_results(results, "output")
if __name__ == "__main__":
main()
Model tree for Chrom256/gemma-2-9b-it-lora_20241216_033631
Base model
google/gemma-2-9b