
Google Colab ã§ Rinna-3.6B ã®LoRAãã¡ã€ã³ãã¥ãŒãã³ã°ã詊ã
ãGoogle Colabãã§ãRinna-3.6Bãã®LoRAãã¡ã€ã³ãã¥ãŒãã³ã°ã詊ããã®ã§ãŸãšããŸããã
ãæ³šæãGoogle Colab Pro/Pro+ ã® A100ã§åäœç¢ºèªããŠããŸããVRAMã¯14.0GBå¿ èŠã§ããã
1. Rinna-3.6B
ãOpenCALM-7Bãã¯ãããµã€ããŒãšãŒãžã§ã³ãããéçºãããæ¥æ¬èªLLMã§ããåçšå©çšå¯èœãªã©ã€ã»ã³ã¹ã§å ¬éãããŠããããã®ã¢ãã«ãããŒã¹ã«ãã¥ãŒãã³ã°ããããšã§ã察話åAIçã®éçºãå¯èœã§ãã
ãRinna-3.6Bãã¯ããRinnaããéçºãããæ¥æ¬èªLLMã§ããåçšå©çšå¯èœãªã©ã€ã»ã³ã¹ã§å ¬éãããŠããããã®ã¢ãã«ãããŒã¹ã«ãã¥ãŒãã³ã°ããããšã§ã察話åAIçã®éçºãå¯èœã§ãã
2. åŠç¿
ãGoogle Colabãã§ãRinna-3.6Bãã®LoRAãã¡ã€ã³ãã¥ãŒãã³ã°ãè¡ããŸããããŒã¿ã»ããã¯@kun1em0nããã®ãkunishou/databricks-dolly-15k-jaãã䜿ãããŠããããŸããã
åŠç¿æé ã¯ã次ã®ãšããã§ãã
(1) ã¡ãã¥ãŒãç·šéâããŒãããã¯ã®èšå®ãã§ããããŒããŠã§ã¢ã¢ã¯ã»ã©ã¬ãŒã¿ãã§ãGPUãã§ãA100ããéžæã
(2) Googleãã©ã€ãã®ããŠã³ãã
# Googleãã©ã€ãã®ããŠã³ã
from google.colab import drive
drive.mount("/content/drive")(3) äœæ¥ãã©ã«ããžã®ç§»åã
# äœæ¥ãã©ã«ããžã®ç§»å
import os
os.makedirs("/content/drive/My Drive/work", exist_ok=True)
%cd '/content/drive/My Drive/work'(4) åºæ¬ãã©ã¡ãŒã¿ã®å®çŸ©ã
# åºæ¬ãã©ã¡ãŒã¿
model_name = "rinna/japanese-gpt-neox-3.6b"
dataset = "kunishou/databricks-dolly-15k-ja"
peft_name = "lora-rinna-3.6b"
output_dir = "lora-rinna-3.6b-results"以äžã®ãã©ã¡ãŒã¿ãå®çŸ©ããŸãã
ã»model_name : ã¢ãã«å
ã»dataset : ããŒã¿ã»ããå
ã»peft_name : PEFTã®åºåãã©ã«ã
ã»output_dir : åŠç¿çµæã®åºåãã©ã«ã
(5) PEFTã®ã€ã³ã¹ããŒã«ã
Rinnaã®ããŒã¯ããŒã¶ãŒã§ã¯ããsentencepieceããå¿
èŠã«ãªããŸãã
# PEFTã®ã€ã³ã¹ããŒã«
!pip install -Uqq git+https://github.com/huggingface/peft.git
!pip install -Uqq transformers datasets accelerate bitsandbytes
!pip install sentencepiece(6) ããŒã¯ãã€ã¶ãŒã®æºåã
Rinnaã®ããŒã¯ãã€ã¶ãŒã§ã¯ããuse_fast=Falseããå¿
èŠã«ãªããŸãã
from transformers import AutoTokenizer
# ããŒã¯ãã€ã¶ãŒã®æºå
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)(7) ããŒã¯ãã€ã¶ãŒã®ã¹ãã·ã£ã«ããŒã¯ã³ã®ç¢ºèªã
EOSã¯3ã§ããã
# ã¹ãã·ã£ã«ããŒã¯ã³ã®ç¢ºèª
print(tokenizer.special_tokens_map)
print("bos_token :", tokenizer.bos_token, ",", tokenizer.bos_token_id)
print("eos_token :", tokenizer.eos_token, ",", tokenizer.eos_token_id)
print("unk_token :", tokenizer.unk_token, ",", tokenizer.unk_token_id)
print("pad_token :", tokenizer.pad_token, ",", tokenizer.pad_token_id){'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}
bos_token : <s> , 2
eos_token : </s> , 3
unk_token : [UNK] , 1
pad_token : [PAD] , 0(8) ããŒã¯ãã€ãºé¢æ°ã®å®çŸ©ã
CALMã®tokenizer()ã¯EOSãèªå远å ãããªãã£ãã®ã§ã+"<|endtext|>"ããä»å ããŸããããRinnaã®tokenizer()ã¯èªå远å ãããŠãŸããã
CUTOFF_LEN = 256 # ã³ã³ããã¹ãé·
# ããŒã¯ãã€ãº
def tokenize(prompt, tokenizer):
result = tokenizer(
prompt,
truncation=True,
max_length=CUTOFF_LEN,
padding=False,
)
return {
"input_ids": result["input_ids"],
"attention_mask": result["attention_mask"],
}(9) ããŒã¯ãã€ãºé¢æ°ã®ç¢ºèªã
ãinput_idsãã®æåŸã«EOSã3ãã远å ãããŠãããšã確èªããŸãã
# ããŒã¯ãã€ãºã®åäœç¢ºèª
tokenize("hi there", tokenizer){'input_ids': [3201, 634, 1304, 3], 'attention_mask': [1, 1, 1, 1]}(10) ããŒã¿ã»ããã®æºåã
from datasets import load_dataset
# ããŒã¿ã»ããã®æºå
data = load_dataset(dataset)(11) ããŒã¿ã»ããã®ç¢ºèªã
# ããŒã¿ã»ããã®ç¢ºèª
data["train"][5]{'category': 'information_extraction',
'instruction': 'ã¹ãã€ã«ã¡ã€ãã®æã«ãç§ã®æ¹ãå€ãã®é§ãæã£ãŠããããç§ã®åã¡ã§ããïŒ',
'output': 'ãããã\nã¹ãã€ã«ã¡ã€ããšã¯ãåŒãåããç¶æ
ã®ããšã§ããã©ã¡ããããå€ãã®é§ãæç²ãããããŸãã¯åªå¢ã§ãããã¯é¢ä¿ãªã',
'input': 'ã¹ãã€ã«ã¡ã€ããšã¯ããã§ã¹ã«ãããŠãæçªãåã£ãŠãããã¬ãŒã€ãŒããã§ãã¯ãããŠããããåæ³çãªæããªãç¶æ
ã®ããšã§ãããã¹ãã€ã«ã¡ã€ãã®çµæãåŒãåããšãªããçµç€ã§ã¯ãã¹ãã€ã«ã¡ã€ãã¯å£å¢ã«ãããã¬ã€ã€ãŒãè² ããã®ã§ã¯ãªããã²ãŒã ãåŒãåããããšãå¯èœã«ããæŠè¡ã§ãã[2]ãããè€éãªããžã·ã§ã³ã§ã¯ãã¹ãã€ã«ã¡ã€ãã¯ããçšã§ãéåžžã¯åªå¢åŽãäžæ³šæãªå Žåã«ã®ã¿æåããè©æ¬ºã®åœ¢ããšã[åŒçš] ã¹ãã€ã«ã¡ã€ãã¯çµç€ç ç©¶ãä»ã®ãã§ã¹ã®åé¡ã«ãããŠãå
±éã®ããŒãã§ããã\n\nã¹ãã€ã«ã¡ã€ããåŒãåãã«çµ±äžãããã®ã¯19äžçŽã§ããããã以åã¯ãã¹ãã€ã«ã¡ã€ãããŠãããã¬ã€ã€ãŒã®åå©ãåŒãåããè² ããšã¿ãªãããããååãšãªã£ãããã¹ãã€ã«ã¡ã€ãããŠãããã¬ã€ã€ãŒã¯ã¿ãŒã³ã倱ãããšã«ãªã£ãããšããã®æ±ãã¯æ§ã
ã§ãã£ããã¹ãã€ã«ã¡ã€ãã®ã«ãŒã«ã¯ããã§ã¹ä»¥å€ã®ãã£ãã©ã³ã¬ç³»ã²ãŒã ããšã«ç°ãªãã',
'index': '5'}(12) ããã³ãããã³ãã¬ãŒãã®æºåã
åŠç¿çšã®ã¬ã¹ãã³ã¹å
容ããçã«ãªããŸãããRinna-instructionãã«ç¿ã£ãŠãæ¥æ¬èªãã€æ¹è¡ã<NL>ã«ããŠããŸãã
# ããã³ãããã³ãã¬ãŒãã®æºå
def generate_prompt(data_point):
if data_point["input"]:
result = f"""### æç€º:
{data_point["instruction"]}
### å
¥å:
{data_point["input"]}
### åç:
{data_point["output"]}"""
else:
result = f"""### æç€º:
{data_point["instruction"]}
### åç:
{data_point["output"]}"""
# æ¹è¡â<NL>
result = result.replace('\n', '<NL>')
return result(13) ããã³ãããã³ãã¬ãŒãã®ç¢ºèªã
# ããã³ãããã³ãã¬ãŒãã®ç¢ºèª
print(generate_prompt(data["train"][5]))### æç€º:<NL>ã¹ãã€ã«ã¡ã€ãã®æã«ãç§ã®æ¹ãå€ãã®é§ãæã£ãŠããããç§ã®åã¡ã§ããïŒ<NL><NL>### å
¥å:<NL>ã¹ãã€ã«ã¡ã€ããšã¯ããã§ã¹ã«ãããŠãæçªãåã£ãŠãããã¬ãŒã€ãŒããã§ãã¯ãããŠããããåæ³çãªæããªãç¶æ
ã®ããšã§ãããã¹ãã€ã«ã¡ã€ãã®çµæãåŒãåããšãªããçµç€ã§ã¯ãã¹ãã€ã«ã¡ã€ãã¯å£å¢ã«ãããã¬ã€ã€ãŒãè² ããã®ã§ã¯ãªããã²ãŒã ãåŒãåããããšãå¯èœã«ããæŠè¡ã§ãã[2]ãããè€éãªããžã·ã§ã³ã§ã¯ãã¹ãã€ã«ã¡ã€ãã¯ããçšã§ãéåžžã¯åªå¢åŽãäžæ³šæãªå Žåã«ã®ã¿æåããè©æ¬ºã®åœ¢ããšã[åŒçš] ã¹ãã€ã«ã¡ã€ãã¯çµç€ç ç©¶ãä»ã®ãã§ã¹ã®åé¡ã«ãããŠãå
±éã®ããŒãã§ããã<NL><NL>ã¹ãã€ã«ã¡ã€ããåŒãåãã«çµ±äžãããã®ã¯19äžçŽã§ããããã以åã¯ãã¹ãã€ã«ã¡ã€ãããŠãããã¬ã€ã€ãŒã®åå©ãåŒãåããè² ããšã¿ãªãããããååãšãªã£ãããã¹ãã€ã«ã¡ã€ãããŠãããã¬ã€ã€ãŒã¯ã¿ãŒã³ã倱ãããšã«ãªã£ãããšããã®æ±ãã¯æ§ã
ã§ãã£ããã¹ãã€ã«ã¡ã€ãã®ã«ãŒã«ã¯ããã§ã¹ä»¥å€ã®ãã£ãã©ã³ã¬ç³»ã²ãŒã ããšã«ç°ãªãã<NL><NL>### åç:<NL>ãããã<NL>ã¹ãã€ã«ã¡ã€ããšã¯ãåŒãåããç¶æ
ã®ããšã§ããã©ã¡ããããå€ãã®é§ãæç²ãããããŸãã¯åªå¢ã§ãããã¯é¢ä¿ãªã(14) åŠç¿ããŒã¿ãšæ€èšŒããŒã¿ã®æºåã
VAL_SET_SIZE = 2000
# åŠç¿ããŒã¿ãšæ€èšŒããŒã¿ã®æºå
train_val = data["train"].train_test_split(
test_size=VAL_SET_SIZE, shuffle=True, seed=42
)
train_data = train_val["train"]
val_data = train_val["test"]
train_data = train_data.shuffle().map(lambda x: tokenize(generate_prompt(x), tokenizer))
val_data = val_data.shuffle().map(lambda x: tokenize(generate_prompt(x), tokenizer))(15) ã¢ãã«ã®æºåã
ãload_in_8bit=Trueãã§int8éååãæå¹ã«ããŠããŸãã
from transformers import AutoModelForCausalLM
# ã¢ãã«ã®æºå
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_8bit=True,
device_map="auto",
)(16) LoRAã¢ãã«ã®æºåã
from peft import LoraConfig, get_peft_model, prepare_model_for_int8_training, TaskType
# LoRAã®ãã©ã¡ãŒã¿
lora_config = LoraConfig(
r= 8,
lora_alpha=16,
target_modules=["query_key_value"],
lora_dropout=0.05,
bias="none",
task_type=TaskType.CAUSAL_LM
)
# ã¢ãã«ã®ååŠç
model = prepare_model_for_int8_training(model)
# LoRAã¢ãã«ã®æºå
model = get_peft_model(model, lora_config)
# åŠç¿å¯èœãã©ã¡ãŒã¿ã®ç¢ºèª
model.print_trainable_parameters()trainable params: 3244032 || all params: 3610489344 || trainable%: 0.08985020286491116ãLoraConfigãã®äž»ãªãã©ã¡ãŒã¿ã¯ã次ã®ãšããã§ãã
ã»r (int) : low-rankè¡åã®æ¬¡å æ°ããã®å€ãå°ãããšãåŠç¿å¯èœãªãã©ã¡ãŒã¿ãå°ãªããªã
ã»lora_alpha (int) : low-rankè¡åã®ã¹ã±ãŒãªã³ã°ãã¡ã¯ã¿ãŒã倧ããã»ã©LoRAãæŽ»æ§å
ã»lora_dropout (float) : LoRAã¬ã€ã€ãŒã®ããããã¢ãŠã確çãåŠç¿æã«ã©ã³ãã ã«äžéšã®ããŒããç¡å¹ã«ããŠæ±åæ§èœåäž
ã»target_modules (Union[List[str],str])
LoRAãé©çšããã¢ãžã¥ãŒã«ã®åå
ã»bias (str) : ãã€ã¢ã¹çš®å¥ ("none", "all", "lora_only")
ã»task_type (Union[str, peft.utils.config.TaskType] = None) : ã¿ã¹ã¯çš®å¥ (CAUSAL_LM)
(17) ãã¬ãŒããŒã®æºåã
import transformers
eval_steps = 200
save_steps = 200
logging_steps = 20
# ãã¬ãŒããŒã®æºå
trainer = transformers.Trainer(
model=model,
train_dataset=train_data,
eval_dataset=val_data,
args=transformers.TrainingArguments(
num_train_epochs=3,
learning_rate=3e-4,
logging_steps=logging_steps,
evaluation_strategy="steps",
save_strategy="steps",
eval_steps=eval_steps,
save_steps=save_steps,
output_dir=output_dir,
report_to="none",
save_total_limit=3,
push_to_hub=False,
auto_find_batch_size=True
),
data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)ãTrainingArgumentsãã®äž»ãªãã©ã¡ãŒã¿ã¯ã次ã®ãšããã§ãã
ã»num_train_epochs (float, optional, defaults to 3.0) : åŠç¿ãšããã¯æ°
ã»max_steps (int, optional, defaults to -1) : åŠç¿ã¹ãããæ°
ã»output_dir (str) : åºåãã©ã«ã (predictionãšcheckpointã®ä¿å)
ã»report_to (str or List[str], optional, defaults to "all") : çµæãšãã°ã®ã¬ããŒãå ("azure_ml", "comet_ml", "mlflow", "neptune", "tensorboard","clearml", "wandb")
ã»save_total_limit (int, optional) : ä¿åãããã§ãã¯ãã€ã³ãã®æå€§æ°
ã»auto_find_batch_size (bool, optional, defaults to False) : ããããµã€ãºã®èªåèšå®
ã»resume_from_checkpoint (str, optional) : åéãã§ãã¯ãã€ã³ã
ã»logging_strategy (str or IntervalStrategy, optional, defaults to "steps")
ãã°æŠç¥ ("no", "steps", "epoch")
ã»evaluation_strategy (str or IntervalStrategy, optional, defaults to "no")
è©äŸ¡æŠç¥ ("no", "steps", "epoch")
ã»save_strategy (str or IntervalStrategy, optional, defaults to "steps")
ä¿åæŠç¥ ("no", "steps", "epoch")
ã»logging_steps (int or float, optional, defaults to 500) : ãã°éé (ã¹ããã)
ã»eval_steps (int or float, optional) : è©äŸ¡éé (ã¹ããã)
ã»save_steps (int or float, optional, defaults to 500) : ä¿åéé (ã¹ããã)
(18) åŠç¿ã®å®è¡ã
3æéã»ã©ããããŸããã
# åŠç¿ã®å®è¡
model.config.use_cache = False
trainer.train()
model.config.use_cache = True
# LoRAã¢ãã«ã®ä¿å
trainer.model.save_pretrained(peft_name)
3. æšè«
ãGoogle Colabãã§ãRinna-3.6Bãã®LoRAã¢ãã«ã®æšè«æé ã¯ã次ã®ãšããã§ãã
(1) ã¢ãã«ãšããŒã¯ãã€ã¶ãŒã®æºåã
Rinnaã®ããŒã¯ãã€ã¶ãŒã§ã¯ããuse_fast=Falseããå¿
èŠã«ãªããŸãã
import torch
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
# ã¢ãã«ã®æºå
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_8bit=True,
device_map="auto",
)
# ããŒã¯ãã€ã¶ãŒã®æºå
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
# LoRAã¢ãã«ã®æºå
model = PeftModel.from_pretrained(
model,
peft_name,
device_map="auto"
)
# è©äŸ¡ã¢ãŒã
model.eval()(2) ããã³ãããã³ãã¬ãŒãã®æºåã
æšè«çšã®ã¬ã¹ãã³ã¹å
容ãªãçã«ãªããŸãã
# ããã³ãããã³ãã¬ãŒãã®æºå
def generate_prompt(data_point):
if data_point["input"]:
result = f"""### æç€º:
{data_point["instruction"]}
### å
¥å:
{data_point["input"]}
### åç:
"""
else:
result = f"""### æç€º:
{data_point["instruction"]}
### åç:
"""
# æ¹è¡â<NL>
result = result.replace('\n', '<NL>')
return result(3) ããã¹ãçæé¢æ°ã®å®çŸ©ã
CALMã®tokenizer()ã¯EOSãèªå远å ãããªãã£ãã®ã§ãããRinnaã®tokenizer()ã¯èªå远å ãããŠããŸããããadd_special_tokens=Falseãæå®ããŸããã
# ããã¹ãçæé¢æ°ã®å®çŸ©
def generate(instruction,input=None,maxTokens=256):
# æšè«
prompt = generate_prompt({'instruction':instruction,'input':input})
input_ids = tokenizer(prompt,
return_tensors="pt",
truncation=True,
add_special_tokens=False).input_ids.cuda()
outputs = model.generate(
input_ids=input_ids,
max_new_tokens=maxTokens,
do_sample=True,
temperature=0.7,
top_p=0.75,
top_k=40,
no_repeat_ngram_size=2,
)
outputs = outputs[0].tolist()
print(tokenizer.decode(outputs))
# EOSããŒã¯ã³ã«ãããããããã³ãŒãå®äº
if tokenizer.eos_token_id in outputs:
eos_index = outputs.index(tokenizer.eos_token_id)
decoded = tokenizer.decode(outputs[:eos_index])
# ã¬ã¹ãã³ã¹å
容ã®ã¿æœåº
sentinel = "### åç:"
sentinelLoc = decoded.find(sentinel)
if sentinelLoc >= 0:
result = decoded[sentinelLoc+len(sentinel):]
print(result.replace("<NL>", "\n")) # <NL>âæ¹è¡
else:
print('Warning: Expected prompt template to be emitted. Ignoring output.')
else:
print('Warning: no <eos> detected ignoring output')(4) æšè«ã®å®è¡ã
質åã«å¯Ÿããçãã ãè¿ããŠEOSããŠãããããåŠç¿ã§ããŠããã§ãã
generate("èªç¶èšèªåŠçãšã¯ïŒ")人éãæ¥åžžçã«äœ¿ã£ãŠããèªç¶ãªèšèªãçè§£ããèªç¶ã«æç« ãçæããæè¡ãgenerate("æ¥æ¬ã®éŠéœã¯ïŒ")æ±äº¬ã¯ãæ¥æ¬ã®éŠéœã§ããgenerate("ãŸã©ãâãã®ã«ã§äžçªããããã®ã¯ïŒ")æçŸã»ããã¯ããŸã©ãããã®èŠªåã§ãããé女ã®äœ¿ãéã§ãããã¥ãŠããšã®åã§ãæéãé¡ããèªåã®åœãæã£ãŠãããå°å¥³ã®åœãæãããã«ãéæ³å°å¥³ã«ãªãã ã»ãã»ãŒãã¯ãéæ³å°å¥³ãŸã©ãâ
ãã¥ããã®ã¢ãã¡ã§äººæ°ãšãªããå€ãã®ãã¡ã³ã«æãããŸããã