æŠèŠ
å人çãªåå¿é²ãå ŒããPyTorchã®åºæ¬çãªè§£èª¬ãšãŸãšãã§ããGPTã¿ã€ã ïŒèªå·±ååž°åã®èšèªã¢ãã«ïŒã®äºååŠç¿ã®æŒç¿ã§åŠãã ããšããŸãšããŠã¿ãŸãããå®éã«å°åã®GPTã¢ãã«ã§äºååŠç¿ããŠã¿ãŸããããããŠTransformerãããã¯ã®èªäœããããPyTorchã®é¢æ°ã䜿ãã§ããã ãç°¡åã«GPTã¿ã€ããåçŸããããšãå¿ãããŸããã
äºååŠç¿ãçµäºããæ®µéã ãšæç« ã®ç¶ããäœæããèœåãã身ã«ã€ããŠããŸãããå ¥åããæç€ºå 容ã«åŸã£ãŠè¿çããããã«ãè¿çã®åã远å ãããã¥ãŒãã³ã°ãæç€ºãã¥ãŒãã³ã°ãšåŒã°ããæåž«ãããã¡ã€ã³ãã¥ãŒãã³ã°ã®ããã§ãã
| 確èªãããäºé | éæåºŠ |
|---|---|
| æç€ºã«åŸã£ãè¿çãããŠãããïŒãããã | å€å⯠|
| åŠç¿ããŒã¿ã䜿ã£ãŠæå³ã®ããæç« ãçæã§ãããïŒã | â³ã⯠|
| æç€ºãã¥ãŒãã³ã°ã®ããŒã¿ãåçŸããŠãããïŒ | â |
| åŠç¿åéå€ã®å 容ããæå³ã®ããæç« ãçæã§ãããïŒ | â |

å³ïŒïŒçæçµæã®ãµã³ãã«
æ¹é
- ã§ããã ãåãã³ãŒãé²è¡
- ã§ããã ãç°¡æœïŒçްããå 容ã¯å²æïŒ
æŒç¿çšã®ãã¡ã€ã«
-
ããŒã¯ãã€ã¶ãŒ: livedoor_homme_tokenizer_8k.json
- livedoorãã¥ãŒã¹ã³ãŒãã¹ã®HOMMEã«ããŽãªãŒãããŒã¿ãšããŠbytelevel BPEã§åŠç¿ãã8KããŒã¯ãã€ã¶ãŒ
-
åŠç¿ããŒã¿ïŒlivedoorãã¥ãŒã¹ã³ãŒãã¹ã®HOMMEã«ããŽãªãŒ
-
äºååŠç¿æžã¿ã¢ãã«ïŒhomme_seq_512_bpe_8k.model
- ç³»åé·512ã®ã¢ãã«ãseq_len=512ã§åäœããŸãããã®ã¢ãã«ã«ãã¡ã€ã³ãã¥ãŒãã³ã°ãã圢ã«ãªããŸãã
-
æç€ºãã¥ãŒãã³ã°æžã¿ã¢ãã«ïŒhomme_seq_512_bpe_8k_it.model
- æç€ºãã¥ãŒãã³ã°ãçµãã£ãã¢ãã«
-
ã³ãŒã: sample_40.ipynb
1. ããŒã¿
æç€ºãã¥ãŒãã³ã°ãªã®ã§HuggingFaceã§å ¬éãããŠããSFT (Supervised Fine Tuning) çšã®ããŒã¿ã»ãããå©çšããã°è¯ãããã§ããããããå ¬éãããŠããSFTçšããŒã¿ã¯äºååŠç¿ã®åéãšç°ãªãå 容ã°ããã«ãªãã®ã§åŠç¿ãã»ãšãã©é²ã¿ãŸããããŸããå ¬éããŒã¿ã¯æåæ°ãå€ãã®ã§ç¬¬38åã§äºååŠç¿ããã¢ãã«ã§ã¯ãæç« ã®åãè©°ããå¿ èŠãšãªããããŒã¿ã掻çšããããªããšããåé¡ãçããŸãã
ããã§ã第38åã®äºååŠç¿ã§å©çšããlivedoorãã¥ãŒã¹ã³ãŒãã¹ã®HOMMEã«ããŽãªãŒã®å 容ãã質åå¿çã®ãããªããŒã¿ã匷åŒã«äœæããŠæŒç¿çšã®SFTããŒã¿ãšããŸããã€ãã§ã«ãããã¯ããããããŸããããããã«ã¡ã¯ããªã©ã®ç°¡åãªæšæ¶æã远å ããŠã¿ãŸããã
ã©ã€ã»ã³ã¹çã«æ¹å€ããããŒã¿ãã¢ããããŒãã§ããªãã®ãæ®å¿µ
ð![]()
ååŠçã®æé
- ããŒã«ã«LLMãå©çšããŠãHOMMEã«ããŽãªãŒã®ãã¡ã€ã«ããšã«ãæ¬æããæ¬¡ã®ãããªäžåäžç圢åŒã®å¯ŸãïŒåäœæããŸããåèšã§500åãããäœæã§ããŸã1ãå¿ããŠããŸããŸãããäœæãŸã§ããããã®æéããããŸãããæéã¬ãã«ã§ãã
{input: æ¥å ã«äŒŒåãã³ãŒãã玹ä»ããŠãã ããã, output: ãžã£ã±ããã«ã¯...}
- æšæ¶æãã¡ãã£ãšããæ¥åžžäŒè©±æãæåã§è¿œå ããŸãããæ¬¡ã®ãããªãããããããªæšæ¶æã®å¯Ÿã30åãããäœæ
{input: ããã«ã¡ã¯, output: ããã«ã¡ã¯ïŒ}
{input: ããããšãããããŸã, output: ãã¡ãããããã€ãããããšãããããŸãã}
- 質åéšåã"input"ãå¿çéšåã"output"ãšããŠJSONãCSVãªã©ã§ä¿åããŸãã
äžã€ã ãæ³šæç¹ã§ãåŠç¿ãããSFTããŒã¿ã®ããŒã¯ã³æ°ãäºååŠç¿ã§å©çšããæå€§ããŒã¯ã³æ°ãè¶ éããªãããã«çãã«ããŸãã æšæ¶æãããŒã«ã«LLM䜿ã£ãŠçæããããšãã§ããŸããã
2. ããŒã¯ãã€ã¶ãŒ
äºååŠç¿ã§å©çšããByteLevelã®BPEã«ããããŒã¯ãã€ã¶ãŒã䜿ããŸããè²ã 远å ããç¹æ®ããŒã¯ã³ãä»å掻èºããŸãð
from tokenizers import Tokenizer
tokenizer_filename = "./tokenizer/livedoor_homme_tokenizer_8k.json"
# å©çšããããŒã¯ãã€ã¶ãŒ
tokenizer = Tokenizer.from_file(tokenizer_filename)
print("ç¹æ®ããŒã¯ã³ID:")
print(f"<pad>: {tokenizer.token_to_id('<pad>')}")
print(f"<eod>: {tokenizer.token_to_id('<eod>')}")
print(f"<system>: {tokenizer.token_to_id('<system>')}")
print(f"<assistant>: {tokenizer.token_to_id('<assistant>')}")
print(f"<user>: {tokenizer.token_to_id('<user>')}")
print(f"size: {tokenizer.get_vocab_size()}")
# ç¹æ®ããŒã¯ã³ID:
# <pad>: 0
# <eod>: 1
# <system>: 2
# <assistant>: 3
# <user>: 4
# size: 8000
3. åŠç¿ããŒã¿ã®äœæ
3.1 ãã£ãããã³ãã¬ãŒãã»ããã³ãããã©ãŒããã
æç€ºãã¥ãŒãã³ã°ã調ã¹ãŠãããš2çš®é¡ã®åœ¢åŒãããèŠãããŸããã#ãèšå·ãå©çšããŠå 容ãåå²ããæ¹æ³ã<|im_start|>ã<s>ã</s>ã®ãããªç¹æ®ããŒã¯ã³ãå©çšããŠæžãäžãæ¹æ³ã§ãã䟿å®äžã¹ã¿ã€ã«ïŒãã¹ã¿ã€ã«ïŒãšããŠãããŸããæã ãAlpaca圢åŒãphi-3圢åŒãšç޹ä»ããŠãããã®ãèŠãããŸããã
ã¹ã¿ã€ã«ïŒ
### System:
ããªãã¯èŠªåãªAIã¢ã·ã¹ã¿ã³ãã§ãã
### Instruction:
å¯å£«å±±ã®é«ãã¯ïŒ
### Response:
å¯å£«å±±ã®æšé«ã¯3,776ã¡ãŒãã«ã§ãã<|endoftext|>
ã¹ã¿ã€ã«ïŒã¯çŽæçã«ããããããã®ãšãç¹æ®ããŒã¯ã³ãäºåã«æºåãããŠããªããŠãæç€ºãã¥ãŒãã³ã°ã§ããå©ç¹ããããŸãã
ã¹ã¿ã€ã«ïŒ
<|im_start|>system
ããªãã¯èŠªåãªAIã¢ã·ã¹ã¿ã³ãã§ãã<|im_end|>
<|im_start|>user
å¯å£«å±±ã®é«ãã¯ïŒ<|im_end|>
<|im_start|>assistant
å¯å£«å±±ã®æšé«ã¯3,776ã¡ãŒãã«ã§ãã<|im_end|>
ã¹ã¿ã€ã«ïŒã¯äºåã«å ¥å¿µãªèšèšãå¿ èŠã§ããã圹å²ãã³ã³ãããŒã«ã§ããã®ã§åœ¢åŒãããŸãåŠç¿ã§ãããã§ãããªããšãªãç¹æ®ããŒã¯ã³ã®èªç±åºŠãé«ããããããªæ°ãããŸãã
ãã€ãããªããåŠã¶ïŒLLMèªäœå
¥éãã®ç·Žç¿åé¡7-1ã«ãããšãã©ã¡ãã®ãã³ãã¬ãŒãã¹ã¿ã€ã«ã§ãã¢ãã«ã®å¿çå質ã¯åçšåºŠãªããã§ããæèŠçãªã®ã§ãããã¹ã¿ã€ã«ïŒãããèŠãããïŒæ°ãããã®ã§ãæŒç¿ã§ã¯ã¹ã¿ã€ã«ïŒã®ç¬èªããŒãžã§ã³ã§è©ŠããŠã¿ãããšæããŸã2ãããã¹ã¿ã€ã«ïŒã«å¯ããã¹ã¿ã€ã«ïŒã®åœ¢ã«ãªããŸã![]()
æŒç¿å©çšã¹ã¿ã€ã«
<system>
ããªãã¯èŠªåãªAIã¢ã·ã¹ã¿ã³ãã§ãã
<user>
å¯å£«å±±ã®é«ãã¯ïŒ
<assistant>
å¯å£«å±±ã®æšé«ã¯3,776ã¡ãŒãã«ã§ãã<eod>
3.2 ãã³ãã¬ãŒãã«åãããããŒã¿äœæ
ç§ã¯JSONL圢åŒã§æç€ºãã¥ãŒãã³ã°çšã®ããŒã¿ãä¿åããŸãããpandasãå©çšããŠãJSONLãèªã¿èŸŒãäºå®ãªã®ã§ãããŒã¿ãã¬ãŒã ãåŒæ°ã«åãDatasetã¯ã©ã¹ãäœæããŠãã³ãã¬ãŒãã«åãããåŠç¿ããŒã¿ãäœæããŠãããŸãã
{"input":"ãããããé¡ãããŸã","output":"ã©ãããããããé¡ãããŸãã"}
{"input":"ãä¹
ãã¶ãã§ã","output":"ãä¹
ãã¶ãã§ãïŒ"}
Datasetã¯ã©ã¹ã®ã«ã¹ã¿ãã€ãºã¯ãç¹æ®ã¡ãœããã®__init__()ã__len__()ã__getitem__()ãå®çŸ©ããã°OKã§ãããdataã¯ããŒã¿ãã¬ãŒã ãconfigã¯ãããã¯ãŒã¯ãåŠç¿ã®èšå®ãšãªããŸãã
class SFTDataset(Dataset):
def __init__(self, data, tokenizer, config):
self.data = data
self.tokenizer = tokenizer
self.max_len = config.seq_len
# ç¹æ®ããŒã¯ã³ID
self.system_id = tokenizer.token_to_id("<system>")
self.user_id = tokenizer.token_to_id("<user>")
self.assistant_id = tokenizer.token_to_id("<assistant>")
self.eod_id = tokenizer.token_to_id("<eod>")
def __len__(self):
return len(self.data)
# (1)
def _enc(self, text):
return self.tokenizer.encode(text, add_special_tokens=False).ids
# (2)
def __getitem__(self, idx):
item = self.data.iloc[idx] # ããŒã¿ãã¬ãŒã ã®indexè¡ãååŸãããã®ã§ data.iloc[]ã䜿ã
system = "ããªãã¯ãããæåŒ·ã®AIã§ããæ¬¡ã®èŠæ±ãé©åã«æºããå¿çãæžããªããã"
instruction = item["input"]
response = item["output"]
# (3) ããã³ããéšåïŒæå€±ãèšç®ããªãïŒ
prompt_ids = []
prompt_ids += [self.system_id] + self._enc(system)
prompt_ids += [self.user_id] + self._enc(instruction)
prompt_ids += [self.assistant_id] # ãããŸã§äžããŠãç¶ããçæããã
# (4) å¿çéšåïŒæå€±ãèšç®ããïŒãæ«å°Ÿã«<eod>ãä»ããŠã忢ããåŠç¿ããã
response_ids = self._enc(response) + [self.eod_id]
# (5)
input_ids = prompt_ids + response_ids
# (6) ããã³ããéšåã¯config.ignore_index=-100ã§ãã¹ã¯ãå¿çéšåã ãåŠç¿å¯Ÿè±¡
labels = [config.ignore_index] * len(prompt_ids) + response_ids
# (7)
# seq_lenã§åãè©°ãïŒé·ãããå¿çã¯<eod>ãåããç¹ã«æ³šæïŒ
input_ids = input_ids[: self.max_len]
labels = labels[: self.max_len]
# (8) input_idsãšlabelsã§ïŒããŒã¯ã³ãããŠããªã
return {"input_ids": input_ids, "labels": labels}
説æã¡ã¢
inputæã»outputæããã£ãããã³ãã¬ãŒã
<system> systemæã®IDå <user> inputæã®IDå <assistant> outputæã®IDå <eod>
ã«åãããŠå ¥åããŒã¿ã®IDåãšæåž«ããŒã¿ã®IDåãåºåãããã®ãDatasetã¯ã©ã¹ãšãªããŸãã
- (1) _enc(text)颿°ã¯tokenizerãå©çšããŠtextãidåã«å€æãã颿°ã§ãã__getitem__ã§ã®ã¿å©çšããŸãã颿°åããçŽæ¥èšè¿°ããŠãè¯ãã£ããããããŸãããéã«ãããå°ã颿°åããŠ__getitem__ãã¹ãããªãšãããŠãè¯ããããããŸããã奜ã¿ã«åãããŠä¿®æ£ããŠãã ãã
- (2) __getitem__ã¯ãæå®ããã€ã³ããã¯ã¹ã«å¯Ÿå¿ããããŒã¿ã«å¯ŸããŠããã£ãããã³ãã¬ãŒãã«åãããidåãšãã¹ã¯ä»ãã©ãã«ãæ±ãã颿°ãšãªããŸããsystemæã§ãããäŸã®ããã«é©åœãªãã®ã§ãåé¡ãããŸãã3ð
- (3) ãã£ãããã³ãã¬ãŒãã«åŸã£ãŠãIDåãäœæããŸãã
- prompt_idsïŒinputæãŸã§ã®IDå
- (4) outputæã«çµç«¯èšå·ã®<eod>ã远å ããŠIDåãäœæããŸãã
- response_idsïŒoutputæ<eod>ã®IDå
- (5) ãã£ãããã³ãã¬ãŒãå šäœåã§ prompt_ids + response_ids ãšãªããŸãã
- (6) labelsã¯æåž«ããŒã¿ã§ããprompt_idsãŸã§ã®IDã -100 (config.ignore_index) ãšããŠåŠç¿æã«æå€±ãèšç®ããªãããã«èšå®ããŸã4ã
- (7) ç³»åé·ããŒã¿ã§åãè©°ããŸããèšèšæã«ããŒã¯ã³ãçãã«æå®ããŠããã®ã§æŒç¿äžåé¡ãªãã®ã§ããå°å³ã«æ³šæãããéšå5
- (8) äºååŠç¿ã§ã¯input_idsãšlabelsã§ïŒããŒã¯ã³ãããããŒã¿ãæºåããŸãããä»åã¯ãå°ãã ãHuggingFaceã£ãœãé°å²æ°ã«å¯ããŠãæå€±èšç®æã«ïŒããŒã¯ã³ããã圢ã§å¯Ÿå¿ããŠã¿ãããšèããŠããŸãð
SFTDatasetã¯ã©ã¹ããäœããããµã³ãã«
dataset = SFTDataset(data=df, tokenizer=tokenizer, config=config)
len(dataset[0]["input_ids"]) # 115
dataset[0]["input_ids"] # [2, 876, ... 273, 4, 3769, ..., 1073, 3, 3411, ..., 273, 1]
dataset[0]["labels"] # [-100, -100, ..., -100, 3411, ...273, 1]
ããŒã¿æ¯ã«ç³»åé·ãç°ãªããŸããDataLoaderã¯ã©ã¹ã®collate颿°ã§çé·åããŸãã
- config.pad_token_id = 0: <pad>ã®ID
- config.ignore_index = -100: æå€±èšç®ãèæ ®ããªãç¹æ®IDå
ãã®2ç¹ã«æ³šæããªããããããµã€ãºæ¯ã«çé·åããcollate颿°ãäœæããã°OKã§ãã
def padding_collate_fn(batch):
# (1)
max_len = max(len(b["input_ids"]) for b in batch)
input_ids = []
labels = []
# (2)
for b in batch:
n_pad = max_len - len(b["input_ids"])
input_ids.append(b["input_ids"]+ [config.pad_token_id] * n_pad)
labels.append(b["labels"] + [config.ignore_index] * n_pad) # padã¯æå€±å¯Ÿè±¡å€
# (3)
return {
"input_ids": torch.tensor(input_ids, dtype=torch.long),
"labels": torch.tensor(labels, dtype=torch.long),
}
説æã¡ã¢
- (1) æå€§ã®ç³»åé·ãæ¢ããŸãã
- (2) ãããæ¯ã«<pad>ãæ¿å ¥ããŸããinput_idsã«ã¯pad_token_idã®ã0ãããã®ãŸãŸè¿œèšãlabelsã®pad_token_idéšåã«ã¯æå€±èšç®å¯Ÿè±¡å€IDã®ãconfig.ignore_index (-100)ãã远èšããŸãã
- (3) LongTensorãšããŠå€ãåºåããŸãã
å®éã«åŠç¿ã«å©çšãããããŒã¿
df = pd.read_json(data_filename, lines=True)
dataset = SFTDataset(data=df, tokenizer=tokenizer, config=config)
dataloader = DataLoader(
dataset=dataset,
batch_size=2
shuffle=True,
collate_fn=padding_collate_fn
)
dataloaderã®å€ã確èªããŠã¿ãŸãã
{'input_ids':
tensor([[ 2, 876,..., 4, 543,..., 3, 733, ...,1],
[ 2, 876, ...,4, 1584,...,3, 4140 ..., 1, 0,...0]]),
'labels':
tensor([[-100, -100,..., -100, 733, ..., 1],
[-100, -100,..., -100, 4140,...,273,..., 1, -100, ...,-100]])}
IDçªå·ããïŒãã®<assistant>ãŸã§ãã-100ãã®ã©ãã«ãæç« åŸæ¹ã®<pad>éšåãã-100ãã©ãã«ãšãªããŸããæ¬¡ã®ãããªãã³ãã¬ãŒãæã«å¯ŸããŠã
<system> systemæã®IDå <user> inputæã®IDå <assistant>
<assistant>以éã®ããŒã¯ã³ãäºæž¬ãã圢ãæç€ºãã¥ãŒãã³ã°ã®åºæ¬ãšãªããŸãã
IDåããã³ãŒããããš
from tokenizers import decoders
tokenizer.decoder = decoders.ByteLevel()
decoded = tokenizer.decode(dataset[0]["input_ids"], skip_special_tokens=False)
print(decoded)
# '<system>ããªãã¯ãããæåŒ·ã®AIã§ããæ¬¡ã®èŠæ±ãé©åã«æºããå¿çãæžããªããã<user>ãã©ãŒãšããŒ21æèŠåºãé座ã«é²åºã«ã€ããŠ<assistant>ç±³åœããµã³ãŒã«ã¹çºã®ãã¡ã¹ããã¡ãã·ã§ã³ãã§ãŒã³ããã©ãŒãšããŒ21ãããªãŒãã³ãããåºèã¯ãæŸåå±é座åºã®æ¬é€š1é5éã®äžéšã«å
¥ããç·é¢ç©çŽ930åªãšããåºå€§ãªå£²å Žã§ãéåžžã®çŽå¶åºãããå€ãã®ååããªãšãŒã·ã§ã³ã§ã¡ã³ãºãã¬ãã£ã¹ã®ã»ããæ¥æ¬åç»å ŽãšãªããããºãŸã§ã®ãã«ã©ã€ã³ãå±éããäºå®ã<eod>'
4. ã¢ãã«èšå®ãšåŠç¿
æç€ºãã¥ãŒãã³ã°ãšã¯ããããã¡ã€ã³ãã¥ãŒãã³ã°ãã³ãŒãã®æµããä»ãŸã§ãšåæ§ã«ã§ããã¯ãïŒ
- æºå (4.1)
- ãããã¯ãŒã¯ã¢ãã«ã®å®çŸ©ãšäœæã(4.2)
- ããŒã¿ã®èªã¿èŸŒã¿ãštorchãã³ãœã«ãžã®å€æã(4.3)
- èª€å·®é¢æ°ãšèª€å·®æå°åã®ææ³ã®éžæã(4.4)
- 倿°æŽæ°ã®ã«ãŒãã(4.5)
- æ€èšŒã(4.6)
4.1 æºå
å©çšããã©ã€ãã©ãªãŒãèªã¿èŸŒã¿ãŸãã
import pandas as pd
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from tokenizers import Tokenizer
from pathlib import Path
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"{device=}")
# ã·ãŒãåºå®ã®å Žåã®èšå®
import random
import numpy as np
def set_seed(seed=55):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed) # CPU + å
šCUDAããã€ã¹ããŸãšããŠèšå®ãã
print(f"seedãèšå®: {seed}")
set_seed()
ã·ãŒãåºå®ã¯ç¹ã«äžèŠã§ããä»åŸã®åèã®ããã«è¿œå ããŠã¿ãŸããã
å©çšããããŒã¿ã»ããã®ãã£ã¬ã¯ããªãŒãããŒã¯ãã€ã¶ãŒãåŠç¿ããã¢ãã«ã®ä¿åãã¡ã€ã«åãæå®ããŸãã
data_filename = "./data/instruction_tuning_data.jsonl" # ããã¯livedoor hommeããQA圢åŒã§äœæããŠãã ãã
tokenizer_filename = "./tokenizer/livedoor_home_tokenizer_8k.json"
pretrain_filename = "model/seq_512_bpe_8k.model"
instruct_model_filename = "model/seq_512_bpe_8k_it.model"
# ããŒã¯ãã€ã¶ãŒ
tokenizer = Tokenizer.from_file(tokenizer_filename)
print(f"size: {tokenizer.get_vocab_size()}") # size: 8000
-
instruction_tuning_data.jsonlã¯hommeã«ããŽãªãŒããQA圢åŒãäœæããŠäœã£ãSFTçšã®ããŒã¿ã»ãããã€ãã§ã«æšæ¶æã远å ããŠããŸããããã¯é 匵ã£ãŠèªäœããŠãã ãã6
- ããŒã¯ãã€ã¶ãŒã¯HOMMEã«ããŽãªãŒã§åŠç¿ããèªåœæ°8000ã®ãã®ã§ãã
4.2 ãããã¯ãŒã¯ã¢ãã«ã®å®çŸ©ãšäœæ
ModelConfigã¯ã©ã¹ã§ãç³»åé·ãç¹æ®ããŒã¯ã³ã®IDãããããµã€ãºãªã©ã®åºæ¬çãªæ°å€ãå®ããŸããDNNã¯ã©ã¹ã§GPTã¿ã€ãã®ãããã¯ãŒã¯ã¢ãã«ãæ§æããŸããäºååŠç¿ã®ã¢ãã«ã«è¿œå ããŠåŠç¿ãã圢ãªã®ã§ãåºæ¬èšå®ããããã¯ãŒã¯æ§é ã¯åäžãšãªããŸãã
class ModelConfig:
def __init__(self, tokenizer):
# ã¢ãã«æ§é
self.vocab_size = tokenizer.get_vocab_size()
self.seq_len = 512 # 128ããŒã¯ã³ã ãšSFTæã«å°ãªã
self.d_model = 256 # 512
self.nhead = 8
self.dim_feedforward = 4*self.d_model
self.num_layers = 6
self.dropout = 0.1
# ç¹æ®ããŒã¯ã³ID
self.pad_token_id = tokenizer.token_to_id("<pad>")
self.eod_token_id = tokenizer.token_to_id("<eod>")
# åŠç¿ããŒã¿ã«é¢ããèšå®
self.context_size = self.seq_len # åŠç¿ã§ããé·ã
self.context_stride = self.context_size # éãªãå
·åã®èª¿æŽ
# åŠç¿èšå®
self.batch_size = 128
self.learning_rate = 0.001 # ããã ãšããã©ã«ããšå€ãããªã
self.num_epochs = 100
self.max_grad_norm = 1.0
self.ignore_index = -100
# 屿§ã远å ã»æŽæ°ããã¡ãœããã远å èšå®æã®ã¿ã€ãã«æ³šæã ãã
def update(self, **kwargs):
"""æž¡ãããããŒã¯ãŒãåŒæ°ã§èšå®ãåçã«è¿œå ã»æŽæ°ãã"""
for key, value in kwargs.items():
setattr(self, key, value)
class DNN(nn.Module):
def __init__(self, config: ModelConfig):
super().__init__()
self.config = config
# åã蟌ã¿å±€
self.token_embedding = nn.Embedding(num_embeddings=config.vocab_size, embedding_dim=config.d_model, padding_idx=config.pad_token_id)
self.pos_embedding = nn.Embedding(num_embeddings=config.seq_len, embedding_dim=config.d_model)
self.dropout = nn.Dropout(config.dropout)
# Transformer layers
# TransformerEncoderã ãã©ãå³äžè§ã«maskã€ããã®ã§ãã¹ã¯ä»ãèªå·±æ³šæã®ã¿ã€ãã«ãªã
causal_transformer_layer = nn.TransformerEncoderLayer(
d_model=config.d_model,
nhead=config.nhead,
dim_feedforward=config.dim_feedforward,
dropout=config.dropout,
activation="gelu",
batch_first=True,
norm_first=True # æ£èŠåã®å Žææå®
)
self.transformer = nn.TransformerEncoder(causal_transformer_layer, num_layers=config.num_layers, enable_nested_tensor=False)
# æåŸã®åºåã«åããæ£èŠåãšFCå±€ãæçµçã«åèªæ°ã«ãªã
self.layer_norm = nn.LayerNorm(config.d_model)
self.fc = nn.Linear(config.d_model, config.vocab_size,bias=False)
self.fc.weight = self.token_embedding.weight # éã¿å
±æ
self.apply(self._init_weights) # åã蟌ã¿éšåã®åæéã¿å€æŽ
def _init_weights(self, module):
if isinstance(module, nn.Linear):
nn.init.normal_(module.weight, mean=0.0, std=0.02)
if module.bias is not None:
nn.init.zeros_(module.bias)
elif isinstance(module, nn.Embedding):
nn.init.normal_(module.weight, mean=0.0, std=0.02)
def forward(self, x):
seq_len = x.size(1)
positions = torch.arange(seq_len,device=x.device)
tok_emb = self.token_embedding(x)
pos_emb = self.pos_embedding(positions).unsqueeze(0)
x = tok_emb + pos_emb
x = self.dropout(x)
# nn.Transformer.generate_square_subsequent_mask ã䜿ã£ãŠãã¹ã¯ãçæ
causal_mask = nn.Transformer.generate_square_subsequent_mask(seq_len, dtype=torch.bool, device=x.device)
# èªå·±ååž°å transformer (transformer decoder)
x = self.transformer(x, mask=causal_mask, is_causal=True)
x = self.layer_norm(x)
# NTPïŒæ¬¡ã®ããŒã¯ã³äºæž¬
logits = self.fc(x)
return logits
DNNéšåã¯ç¬¬38åãšåäžãªã®ã§ãå³ïŒã®ãããªãããã¯ãŒã¯æ§é ã«ãªããŸãã

å³ïŒïŒGPTã¿ã€ãã®ãããã¯ãŒã¯æ§é
äºååŠç¿ããéã¿ãèªã¿èŸŒã¿ãæç€ºãã¥ãŒãã³ã°çšã«å¿ èŠãªå€æ°ãäžæžãããŸãã
checkpoint = torch.load(pretrain_filename, map_location=device)
config = ModelConfig(tokenizer)
config.__dict__.update(checkpoint["config"])
# configã®è¿œå èšå®ãšæ°å€ã®å€æŽ
# update颿°ã䜿ã£ãŠSFTåãã«äžæžã
config.update(
batch_size = 128,
learning_rate = 3e-4,
num_epochs = 60,
)
model = DNN(config).to(device)
model.load_state_dict(checkpoint["model_state_dict"])
説æã¡ã¢
ModelConfigã«è¿œå ããupdate()ã¡ãœãããå©çšããŠãããããµã€ãºãåŠç¿çãåŠç¿åæ°ã倿ŽããŸãã
- learning_rateïŒåŠç¿çã¯äºååŠç¿ã§äœ¿ã£ã 1e-3ãããå°ããªå€ãæå®ããã°OKã§ãã
- num_epochsïŒãšããã¯æ°ã§åŠç¿ããŠã¿ãŸãããããŒã¿æ°å°ãªãã®ã§ç§ã¬ãã«ã§çµãããŸããåãéãã«æ³šæã§ãð
4.3 ããŒã¿ã®èªã¿èŸŒã¿ãštorchãã³ãœã«ãžã®å€æ
SFTDatasetã¯ã©ã¹ã§ãinputãšoutputãããŒã«æã€ããŒã¿ããã£ãããã³ãã¬ãŒãã«é©çšããŠãããŸããDataLoaderã§ãããããšã«<pad>ãæ¿å ¥ããŠç³»åé·ãçãããªãããã«æŽããŠãããŸããããã¯åæ²ã§ããð
class SFTDataset(Dataset):
def __init__(self, data, tokenizer, config):
self.data = data
self.tokenizer = tokenizer
self.max_len = config.seq_len
# ç¹æ®ããŒã¯ã³ID
self.system_id = tokenizer.token_to_id("<system>")
self.user_id = tokenizer.token_to_id("<user>")
self.assistant_id = tokenizer.token_to_id("<assistant>")
self.eod_id = tokenizer.token_to_id("<eod>")
def __len__(self):
return len(self.data)
def _enc(self, text):
return self.tokenizer.encode(text, add_special_tokens=False).ids
def __getitem__(self, idx):
item = self.data.iloc[idx] # ããŒã¿ãã¬ãŒã ã®indexè¡ãååŸãããã®ã§ data.iloc[]ã䜿ã
system = "ããªãã¯ãããæåŒ·ã®AIã§ããæ¬¡ã®èŠæ±ãé©åã«æºããå¿çãæžããªããã"
instruction = item["input"]
response = item["output"]
# ããã³ããéšåïŒæå€±ãèšç®ããªãïŒ
prompt_ids = []
prompt_ids += [self.system_id] + self._enc(system)
prompt_ids += [self.user_id] + self._enc(instruction)
prompt_ids += [self.assistant_id] # ãããŸã§äžããŠãç¶ããçæããã
# å¿çéšåïŒæå€±ãèšç®ããïŒãæ«å°Ÿã«<eod>ãä»ããŠã忢ããåŠç¿ããã
response_ids = self._enc(response) + [self.eod_id]
input_ids = prompt_ids + response_ids
# ããã³ããéšåã¯config.ignore_index = -100ã§ãã¹ã¯ãå¿çéšåã ãåŠç¿å¯Ÿè±¡
labels = [config.ignore_index] * len(prompt_ids) + response_ids
# seq_lenã§åãè©°ãïŒé·ãããå¿çã¯<eod>ãåããç¹ã«æ³šæïŒ
# seq_len=512ãªã®ã§å€å倧äžå€«
input_ids = input_ids[: self.max_len]
labels = labels[: self.max_len]
return {"input_ids": input_ids, "labels": labels}
å ·äœäŸ
- input: "ãã©ãŒãšããŒ21æèŠåº..."
- output: "ç±³åœããµã³ãŒã«ã¹çºã®ãã¡ã¹ããã¡ãã·ã§ã³ãã§ãŒã³..."
ãã£ãããã³ãã¬ãŒãã«åœãŠã¯ãããšã次ã®ãããªåœ¢ã«ãªããŸãã å®éã¯æ¹è¡ãªãã®ïŒè¡ ã«ãªã£ãŠããŸãã
<system>
ããªãã¯ãããæåŒ·ã®AIã§ããæ¬¡ã®èŠæ±ãé©åã«æºããå¿çãæžããªããã
<user>
ãã©ãŒãšããŒ21æèŠåºãé座ã«é²åºã«ã€ããŠèª¬æããŠãã ããã
<assistant>
ç±³åœããµã³ãŒã«ã¹çºã®ãã¡ã¹ããã¡ãã·ã§ã³ãã§ãŒã³ããã©ãŒãšããŒ21ãããªãŒãã³ãããåºèã¯ãæŸåå±é座åºã®æ¬é€š1é5éã®äžéšã«å
¥ããç·é¢ç©çŽ930åªãšããåºå€§ãªå£²å Žã§ãéåžžã®çŽå¶åºãããå€ãã®ååããªãšãŒã·ã§ã³ã§ã¡ã³ãºãã¬ãã£ã¹ã®ã»ããæ¥æ¬åç»å ŽãšãªããããºãŸã§ã®ãã«ã©ã€ã³ãå±éããäºå®ã
<eod>
äžèšã®åœ¢åŒã«å€æãããæãIDåããã®ãSFTDatasetã¯ã©ã¹ã®åœ¹å²ãšãªããŸããçé·åã®ããã®collate颿°ãå®çŸ©ããŸãããããåæ²ã§ãðŠ
def padding_collate_fn(batch):
max_len = max(len(b["input_ids"]) for b in batch)
input_ids = []
labels = []
for b in batch:
n_pad = max_len - len(b["input_ids"])
input_ids.append(b["input_ids"]+ [config.pad_token_id] * n_pad)
labels.append(b["labels"] + [config.ignore_index] * n_pad) # padã¯æå€±å¯Ÿè±¡å€
return {
"input_ids": torch.tensor(input_ids, dtype=torch.long),
"labels": torch.tensor(labels, dtype=torch.long),
}
df = pd.read_json(data_filename, lines=True)
dataset = SFTDataset(data=df, tokenizer=tokenizer, config=config)
dataloader = DataLoader(
dataset=dataset,
batch_size=config.batch_size, # ã¡ã¢ãªè¶³ããªãå Žåã¯å°ãããã
shuffle=True,
num_workers=0, # CPUã®å©çšã³ã¢æ°ã¿ãããªãã®CPUã³ã¢ã®ååãããïŒ
pin_memory=torch.cuda.is_available(), # GPU䜿ãæTrue
drop_last=True,
collate_fn=padding_collate_fn
)
DataLoaderã®collate颿°ãå©çšããŠãããããšã«ç³»åé·ã®é·ããæããŸãã
å
·äœäŸ
next(iter(dataloader))ã«ãã£ãŠäžèº«ã確èªããŠã¿ãŸãããlabelsã®IDéšåã<assistant>ãŸã§ãã-100ãã<pad>ãã-100ãã«ããŠããã®ã§è¡šç€ºããããã«ãããªã£ãŠããŸã![]()
{'input_ids': tensor([[ 2, 876, 2796, ..., 337, 273, 1, 0],
[ 2, 876, 2796, ..., 1139, 4841, 273, 1],
...,
[ 2, 876, 2796, ...,273, 1, 0,..., 0]]),
'labels': tensor([[-100,..., -100, 1047, 6405,..., 273, 1, -100],
[-100,..., -100, 1629, 381, 266, ... 273, 1],
...,
[-100,..., -100, 294, 528, ... 273, 1, -100,..., -100])}
4.4 èª€å·®é¢æ°ãšèª€å·®æå°åã®ææ³ã®éžæ
CrossEntropyæå€±ãšAdamWã䜿ããŸããAdamWã®éã¿æžè¡°ã«ã€ããŠãä»åŸè§ŠããŠããäºå®ã§ãã
criterion = nn.CrossEntropyLoss(ignore_index=config.ignore_index) # æå€±èšç®æã«ignore_index=-100ãå©çšããªã
optimizer = torch.optim.AdamW(model.parameters(),lr=config.learning_rate)
4.5 倿°æŽæ°ã®ã«ãŒã
åŠç¿ããããŒã¿æ°ãå°ãªãã®ã§epochæ°ã§ç¹°ãè¿ããŸãããäºååŠç¿æãšåãæ§æã§ãã
泚æç¹
- SFTDatasetã®input_idsãšlabelsã®äœææ¹æ³ã«æ³šæã§ããïŒããŒã¯ã³ãããŠããŸããã
- GPTã¿ã€ããªã®ã§æ¬¡ã®ããŒã¯ã³ãäºæž¬ããã¹ã¿ã€ã«ãšãªããŸãããSFTDatasetã®åºåã ãšæåž«ããŒã¿ã®ããŒã¯ã³ãäºæž¬ãããããŒã¯ã³ãšåãäœçœ®ã«ãªã£ãŠããŸãã
- æå€±ãèšç®ãããšãã«ïŒããŒã¯ã³ãããå¿ èŠããããŸã7ã
å
·äœäŸ
HuggingFaceã©ã€ãã©ãªãŒãžã®æ¥ç¶ã«åããŠå
·äœäŸã§ç¢ºèªããŠã¿ãŸããããªãåé·ã«ãªããŸã8![]()
(1) IDã®ç¢ºèª
- <pad>: 0
- <eod>: 1
- <system>: 2
- <assistant>: 3
- <user>: 4
- systemæ¬æ: [10, 11]
- useræ: [20, 21]
- assistantæïŒå¿çæïŒ: [30, 31, 32]
(2) SFTDataset.__getitem__( )ã®åºå
衚ã®ããã«labelsã¯input_idsãšåãäœçœ®ã«äžŠãã§ããŸãã
<system>ãã<assistant>ãŸã§ã®IDïŒ [2, 10, 11] + [4, 20, 21] + [3]
å¿çã®IDïŒ [30, 31, 32] + [1]
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| input_ids | 2 | 10 | 11 | 4 | 20 | 21 | 3 | 30 | 31 | 32 | 1 |
| labels | -100 | -100 | -100 | -100 | -100 | -100 | -100 | 30 | 31 | 32 | 1 |
(3) ã¢ãã«ã®åºå
logits = model(input_ids) # (batch_size=1, seq_len=11, vocab_size=8000)
- logits[6]ïŒäœçœ®6ïŒ<assistant>ïŒã®åºåå€ãlogits[6]ã®å€ãmaxã«ãªãIDã¯æ¬¡ã®ããŒã¯ã³ã§ããã30ã=labels[7]ã§ãã£ãŠã»ãã
- logits[7]ïŒäœçœ®7ïŒ30ïŒã®åºåå€ãlogits[7]ã®å€ãmaxãšãªãIDã¯æ¬¡ã®ããŒã¯ã³ã§ããã31ã=labels[8]ã§ãã£ãŠã»ãã
ã€ãŸã logits[t] ãäºæž¬ãã¹ãæ£è§£ã¯labels[t+1] ãšãªããŸãã
(4) æå€±èšç®çšã«ã·ãããããŠäœçœ®ãåããã
shift_logits = logits[:, :-1, :] # äœçœ® 0ã9 (10å)
shift_labels = labels[:, 1:] # äœçœ® 1ã10 (10å)
ã·ããåŸã®æå€±èšç®çšã®å¯Ÿå¿ã次ã®è¡šã®ããã«ãªããŸãã
| äœçœ®çªå· | shift_logits ã®äžèº« | shift_labels ã®äžèº« | 説æ |
|---|---|---|---|
| 0 | logits[0] | labels[1] = -100 | æå€±èšç®ããªã |
| ïž | ïž | ïž | ïž |
| 5 | logits[5] | labels[6] = -100 | æå€±èšç®ããªã |
| 6 | logits[6]=<assistant>ID | labels[7] = 30 | 次ã®ããŒã¯ã³ïŒå¿ç1ããŒã¯ã³ç®ïŒãäºæž¬ |
| 7 | logits[7]=30 | labels[8] = 31 | 次ã®ããŒã¯ã³ãäºæž¬ |
| 8 | logits[8]=31 | labels[9] = 32 | 次ã®ããŒã¯ã³ãäºæž¬ |
| 9 | logits[9]=32 | labels[10] = 4 | <eod>ãäºæž¬ |
æå€±ãèšç®ãããã®ã¯äœçœ®çªå·6ã9ã®4ç®æã ãã§ãã
æŽæ°ã«ãŒã
æå€±èšç®ã®åã«labelsã1ããŒã¯ã³ãããã圢ã«å€åœ¢ããŠããããšã倧äºã§ãã
model.train()
use_bf16 = torch.cuda.is_available() and torch.cuda.is_bf16_supported()
for epoch in range(config.num_epochs):
total_loss = 0.0
for batch in dataloader:
input_ids = batch["input_ids"].to(device, non_blocking=True)
labels = batch["labels"].to(device, non_blocking=True)
optimizer.zero_grad()
with torch.autocast(device_type=device.type, dtype=torch.bfloat16, enabled=use_bf16):
logits = model(input_ids)
# 次ããŒã¯ã³äºæž¬ã®ãã1ã€ãããïŒãããä»åã®ãã€ã³ãïŒ
shift_logits = logits[:, :-1, :]
shift_labels = labels[:, 1:]
loss = criterion(
shift_logits.reshape(-1, config.vocab_size),
shift_labels.reshape(-1),
)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), config.max_grad_norm)
optimizer.step()
total_loss += loss.item()
if (epoch+1)%10 == 0:
print(f"Epoch {epoch+1}/{config.num_epochs} | Loss: {total_loss/len(dataloader):.4f}")
# éåŠç¿ããŠããŸã£ã orz
# Epoch 10/60 | Loss: 2.0750
# Epoch 20/60 | Loss: 1.1597
# Epoch 30/60 | Loss: 0.6753
# Epoch 40/60 | Loss: 0.4014
# Epoch 50/60 | Loss: 0.2654
# Epoch 60/60 | Loss: 0.1854
説æã¡ã¢
-
shift_logits = logits[:, :-1, :]ïŒïŒããããµã€ãºãç³»åé·âïŒãèªåœæ°=8000ïŒ -
shift_labels = labels[:, 1:]ïŒïŒããããµã€ãºãç³»åé·âïŒïŒ
ã¢ãã«ã®ä¿åãšåŸ©å ã¯ãããŸã§ã©ããã§ãããŸããåŠç¿æéãç§ã§çµäºãªã®ã§ä¿åããªããŠãè¯ããããªæ°ãããŸããã·ãŒããåºå®ããªããšåããããã®æå€±ã§ãè¿çãå€ãã£ãŠããã®ãè峿·±ãã£ãããð€
# ã¢ãã«ã®ä¿å
torch.save({
"model_state_dict": model.state_dict(),
"config": config.__dict__, # configãäžç·ã«ä¿å
}, instruct_model_filename)
# ã¢ãã«ã®åŸ©å
checkpoint = torch.load(instruct_model_filename)
config = ModelConfig(tokenizer)
config.__dict__.update(checkpoint["config"])
model = DNN(config).to(device)
model.load_state_dict(checkpoint["model_state_dict"])
4.6 æ€èšŒ
ãããŸã§é·ãã£ãã§ãããããšäžæ¯ð
æç« çæãšãªããŸããGreedyã«ããåçŽãªæ¹æ³ã§è©ŠããŠã¿ãŸããããããŸã§ã®Greedyã«ããçæã®å段éãšããŠãå
¥åæããã£ãããã³ãã¬ãŒãã«å€æŽããå¿
èŠããããŸããbuild_chat_promptãšãã颿°ãäœæããŠã¿ãŸããã
from tokenizers import decoders # ByteLevelã®æã¯å©çšãã
tokenizer.decoder = decoders.ByteLevel()
# (1) åºæ¬ã®æç« çæé¢æ°ã次ã®Greedyã«ãã£ãŠããŒã¯ã³ãçæããã ã(ãããŸã§ãšåã)
@torch.inference_mode()
def generate_text(
model,
input_ids,
max_new_tokens=config.seq_len,
eos_token_id=None,
):
model.eval()
generated = input_ids.to(device)
seq_len = model.config.seq_len
# greedyã§åçŽã«çæ
# eos_token_idã§çæçµäº
for _ in range(max_new_tokens):
logits = model(generated[:, -seq_len:])
next_token = logits[:, -1, :].argmax(dim=-1, keepdim=True)
generated = torch.cat([generated, next_token], dim=1)
if eos_token_id is not None and next_token.item() == eos_token_id:
break
return generated
# (2) ãã£ãããã³ãã¬ãŒãã«åããã颿°
def build_chat_prompt(tokenizer, instruction, system=None):
ids = []
ids += [tokenizer.token_to_id("<system>")] + tokenizer.encode(system, add_special_tokens=False).ids
ids += [tokenizer.token_to_id("<user>")] + tokenizer.encode(instruction, add_special_tokens=False).ids
ids += [tokenizer.token_to_id("<assistant>")] # ããããå
ãçæ
return ids
# (3) å®éã«å
¥åæïŒpromptïŒããæç« ã衚瀺ãã颿°
def response(prompt):
system_msg = "ããªãã¯ãããæåŒ·ã®AIã§ããæ¬¡ã®èŠæ±ãé©åã«æºããå¿çãæžããªããã"
#system_msg = "以äžã¯ãã¿ã¹ã¯ã説æããæç€ºã§ããèŠæ±ãé©åã«æºããå¿çãæžããªããã"
prompt_ids = build_chat_prompt(tokenizer, prompt, system=system_msg)
input_ids = torch.tensor([prompt_ids], dtype=torch.long).to(device)
output_ids = generate_text(
model=model,
input_ids=input_ids,
max_new_tokens=256,
eos_token_id=config.eod_token_id, # <eod>ã§åæ¢
)
# å¿çéšåã ãåãåºã
response_only = output_ids[0].tolist()[len(prompt_ids):]
print(tokenizer.decode(response_only, skip_special_tokens=False))
説æã¡ã¢
- (1) generate_text()ïŒinput_idsã«å¯Ÿããè¿çãçæãã颿°ãgreedyã§ïŒããŒã¯ã³ããšçæããŠãããŸãã
- (2) build_chat_prompt()ïŒã<system>説ææ<user>å ¥åæ<assistant>å¿çæããšãã圢ç¶ã«æãäžŠã³æ¿ããŠãtokenizer.encodeã§IDåããŸãã
- (3) response(prompt)ïŒpromptã«å¯ŸããŠbuild_chat_prompt(ã)ã§ãã£ãããã³ãã¬ãŒãåŸã«IDåãgenerate_text(ã)ã§å¿çæçæãšãªããŸãã
prompt.csvãã¡ã€ã«ã«èšèŒããããµã³ãã«æç« ã«å¯ŸããŠã®è¿çãšãªããŸããprompt.csvã¯HOMMEã«ããŽãªãŒãSFTçšã®ããŒã¿ã«èšèŒãããŠããå 容ã«è¿ãå 容ã»å®éã«èšèŒãããŠããå 容ãéžæããŸããã
df = pd.read_csv("data/prompt.csv")
for num, prompt in enumerate(df["prompt"]):
print(f"[{num+1}] > {prompt}")
response(prompt)
è¿çã®äžéšãèšèŒããŸããã
- <eod>ã§æç« ãæ¢ãŸã£ãŠããããšãããæç€ºã«åŸã£ãçµç«¯ãããŠããŸããçæãããŠããæç« ã«æå³ãããã®ããšããç¹ã ãšã[5] ã¯NGã§ãããããåŠç¿ããŒã¿ã®æ¬æäžã«é¡äŒŒã®å 容ãããã®ã§ãããéãæç« ãã€ãªãã§ããŸã£ãå°è±¡ã
- åŠç¿ããŒã¿ã䜿ã£ãæç« ãçæããŠãããšããã®ã¯ããã埮åŠãªæããããŸãã[1]ã[3]ã[4] ã®å¿çãæã£ããããçããð
- æç€ºãã¥ãŒãã³ã°ããŒã¿ãåçŸããŠããããšãã芳ç¹ãã確èªããŠã¿ãŸããæšæ¶æãšãªã[8]ãš[9]ã®è¿çã¯å®å šã«ã³ããŒæã«ãªã£ãŠããŸããã
- å®å šãªåéå€ã ãšãããããªè¿çã«ãªããŸããåãåèªã®ç¹°ãè¿ããªã©ããèŠããããã¿ãŒã³ã§ãã
- [10]çªç®ã®ããã«æ¯èŒçè¿ãå 容ã§ã詊ããŸãããæå³äžæçã§ãããããããæç« ãæžããŠããã®ãããæå³ãããã
### HOMMEã«ããŽãªãŒãSFTçšã«å€åœ¢ããæç«
[1] > 芪ãç·ã®åã«å°±ããããè·æ¥ã®ã©ã³ãã³ã°ã§1äœã«æããããã®ã¯ã©ã®è·æ¥ã§ããã
å
¬åå¡<eod>
[2] > ããŒã«ã»ã¹ãã¹ãçºè¡šããã³ã¬ã¯ã·ã§ã³ã®å
šäœçãªã³ã³ã»ããã¯ã©ã®ãããªãã®ã§ããã
è±åœæã®äŒçµ±ãèæ¯ã«ãæ¬æ°ãªãã©ã«ã ãçŽ æã§ãã¬ãã·ã³ã°ã楜ããããªã»ã¯ã©ã·ãã¯ãªãã®ã§ãã<eod>
[3] > ããŒã«ã»ã¹ãã¹ã®ãThe Cityããšãããªã¹ããŠã©ããã®äŸ¡æ Œã¯ãããã§ããïŒ"
3äž8,850åã§ãã<eod>
[4] > 幎å1000äžå以äžã®ããžãã¹ããŒãœã³ãåããŠã¿ãããšæãåœã¯ïŒ
ã·ã³ã¬ããŒã«<eod>
[5] > 転è·ã«ãããåžå ŽäŸ¡å€ãã¢ãããããããã®æ¹æ³ã«ã€ããŠè§£èª¬ããŠãã ããã
ãä»äºãé¢çœããªããã»ã»ã»è¥æç€ŸäŒäººã®æ©ã¿ã¯å°œããªããã®ããããªæ§ã
ãªæ©ã¿ã«èŸå£4å§åйããçãããŸãã<eod>
[6] > ãããç·åãæã€ããããããšããèšèãã€ã¡ãŒãžã«ã€ããŠ
ããããããšããèšèã ããäžäººæ©ãããŠããããã«æããããç¹ããSNSäžã§ã¯å€§ããªããžãã¹ãããŠããããã«èŠããã¡ã ããå®éã¯äººéé¢ä¿ãèãå Žåãããç¹ã§ãã<eod>
[7] > ããšã¿86ãäžéã®æ³šç®ãéããçç±ã«ã€ããŠ
ã86ããšããä¿ç§°ã«ç±æ¥ãããAE86ã®ããã«ãŠãŒã¶ãŒããæããããŠãŒã¶ãŒãè²ãŠãè»ã«ãªã£ãŠæ¬²ããããšããæãã蟌ããããŠããããã§ãã<eod>
### SFTçšããŒã¿ã«è¿œå ããæšæ¶æ ã³ããŒæãè¿çããŠããŸã
[8] > ããã«ã¡ã¯
ããã«ã¡ã¯!<eod>
[9] > ã©ã¡ãããæ¥ãŸãããïŒ
ç§ã¯ç°äžçããæ¥ãŸããã<eod>
### HOMMEã«ã®ãªã®ãªéãªã£ãŠããITåéã®è©±é¡ããåºé¡
[10] > SNSã§ã®é
åãã¢ãããããããã®ã¹ãã«ãšç¥èã«ã€ããŠ
ãAndroidãã«ãããå瀟ã®ã¢ããããŒãå¶äœç¥èãç¿¡ããAndroidæèŒã ãã§ãªããåœãµãŒãã¹ãããªã©ãè€æ°ã¢ããªãæµããŠããã皌æ¥ã®çµéšããã€ã³ã¿ãŒãããã€ã³ã¿ãŒãããäžã§å¿
èŠã®åºæ¥äºãšãå®¶åºã«å®¶åºã¯ã¹ããŒããã©ã³ãžãå©çšããããã€ã³ã¿ãŒãããäžã§ããªãã®ããœã³ã³ãããå¿
èŠå¿
èŠå¿
èŠããããŸãã<eod>
ãããªãã«æå³ã®ããæç« ãçæããŠ<eod>ã§æžãçµããŠããç¹ã¯è©äŸ¡ãããïŒãããïŒå¿çåãçãã§ããå¿çåã®é·ããããå°ã調æŽããã°è¯ãã£ãããªããã®ãããã¯æ¯èŒæ€èšããã»ããè¯ãããã ã
çæãããæã«ã€ããŠã§ãããåãæåã®ç¹°ãè¿ããä»åã¯ãããŸãèŠãããŸããã§ãããå ¥åæã«ãã£ãŠã¯ããåŸããšæããŸãã
ïŒã¿ãŒã³ã®å¯Ÿè©±ã ãã§ããbotã£ãœã圢ã§è©ŠããŠã¿ãŸãããæ£çŽãåŠç¿ããŒã¿ãç¥ããªããšäŒè©±ã§ããªãðãŸãã«å€å žçãªãšãã¹ããŒãã·ã¹ãã ã人工ç¡èœã£ãœãæåã ãð
for i in range(5):
prompt = input(f">")
print(f"[{i+1}] > {prompt}")
response(prompt)
[1] > ããã«ã¡ã¯ãããã³ããŒè¿ç
ããã«ã¡ã¯!<eod>
[2] > ããšã¿86ã«ã€ããŠèª¬æããŠãããäºååŠç¿ããŒã¿ã«ããå
容ããè¿ç
ã86ããšããä¿ç§°ã«è¡šãããæããããŠãŒã¶ãŒãè²ãŠãè»ã«ãªã£ãŠæ¬²ããã86ã®ããã«ãåç¥ããè»ã«ãªã£ãŠæ¬²ããã86ããšããæããæããããŠãŒã¶ãŒãè²ãŠãè»ã«ãªã£ãŠæ¬²ããããšããæãã蟌ããããŠããããã§ãã<eod>
[3] > å³è§£èª¬æãšã¯äœã§ããããïŒãããäºååŠç¿ããŒã¿ã«è¿ãå
容ããã¯ãã ãã©NG
çžå€ãã£ãŠãããã ããã<eod>
[4] > ã¹ããŒããã©ã³ã®è³Œå
¥çç±ã«ã€ããŠæããŠãã ããããããSFTçšã«é¡äŒŒããŒã¿ããã¿ã€ãäžéšã衚瀺ããŠããã£ãœã
ãPCãµã€ããé²èЧã§ããããããšãã¢ããªãè±å¯ããšããæèŠã§ããã<eod>
[5] > ããããšãããããŸãããããã³ããŒè¿ç
ãã¡ãããããã€ãããããšãããããŸãã<eod>
å°åã§ã¯ãããŸããGPTã¿ã€ãã§åŠç¿ïŒãããïŒïŒããã ãšãæ¬åœã«äººå·¥ç¡èœã£ãœãããããããè¿çã®å¶åŸ¡ãã§ããªã人工ç¡èœð
åŠç¿ããŒã¿ãšã¢ãã«ãµã€ãºãè¶ å·šå€§ã«ãªããšçªç¶èœåãå€ãã£ãŠããã®ãéåžžã«è峿·±ãéãã§ããã²ãŒã ã«ãããããªã¬ãã«ã極éãŸã§äžãã£ãŠèœåãèŠéããã€ã¡ãŒãžãªã®ããªðãããšãäºåã«è©°ã蟌ãã ããŒã¿éãå€ãã ããªã®ã...ããå°ãã¢ãã«ãµã€ãºãããŒã¿éãå¢ãããŠå€åãæ¥œããã§ã¿ãããã®ã§ãã
次å
Transformerå±€ãå®è£ ããŠGPTã®Llamaåã«åãçµãã§ã¿ãããšèããŠããŸããããããRMSNormãSwiGLUãRoPEãKVãã£ãã·ã¥ããããå¿ èŠã«ãªããããðµãé£ãðµ
åèã«ããæžç±ãšãµã€ã
ä»åã¯è«æã§ã¯ãªããŠã解説æ¬ãäžå¿ã«å匷ããŸããã
- æè€ 康æ¯
(2026)ããŒãããäœãDeep Learning â» âLLMç·šããªã©ã€ãªãŒã»ãžã£ãã³
- å€è²å·ãã§å³è§£ãäžå¯§ãããæç€ºãã¥ãŒãã³ã°ã®è©±é¡ã¯ code bot äŸã§åºç€ã«æ²èŒãããŠããŸãããããåèã«ãæšæ¶æãSFTã®ããŒã¿ã«æ··ããããšã«ããŸããã
- Sebastian Raschka å·£ç± æ èŒ (ç£ä¿®), æ ªåŒäŒç€Ÿã¯ã€ãŒã èš³ (2025) ãã€ãããªããåŠã¶ïŒLLM èªäœå
¥éããã€ããåºç
- ãã¡ããæåãåèã§ãèš³æ¬ã§ãOKããªãæžç±ã®ïŒç« ãæç€ºãã¥ãŒãã³ã°ã«é¢ãã話é¡ã§ããå°å³ã«ç·Žç¿åé¡ãšãã®è§£çãçè§£ä¿é²ã«åœ¹ç«ã¡ãŸããð
-
æ©æ¢°åŠç¿ãšæ
å ±æè¡
- ãã€ããªããåèã«ããŠããŸããèŠããã³å 容ãå¢ããŠããã
- ZennãQiitaã®é¢é£èšäºãåèã«ããŸãããæ²¢å±±ããã®ã§æ²èŒçç¥


ç®æ¬¡ããŒãž
泚
-
èªåã¯Gemma4ã§ããŒã¿çæã詊ã¿ãŸãããæã ããïŒãçãªåé¡ãäœæããŠããŸããããæ¥µç«¯ã«ãããããªãå Žåã¯ããã®ãŸãŸå©çšããŸãããè¿çãNGãªããŒã¿ã¯é¢åãªã®ã§åé€ããŠ500åã»ã©ã«ãªããŸãããããŒã«ã«LLMã®ã¢ãã«ãµã€ãºã倧ããã»ãããªããšãªãããåçã§ã¯ãªãããšæããŸããVRAMãæéãšçžè«ãã€ã€æ±ºããŠãã ããã â©
-
ããŒã¯ãã€ã¶ãŒã®èªåœæ°ãéãããŠããã®ã§systemãuserãªã©ãïŒããŒã¯ã³ãšããŠèªèããŠããŸãããä»åã¯<system>ã<user>ãªã©ã®ããŒã¯ã³ãäºåã«æºåããŠå¯Ÿå¿ããŠã¿ãŸããã â©
-
systeméšåãåºå®ããã«æ§ã ãªåœ¹å²ãæºåã察å¿ããå¿çããŒã¿ãæããããšã§ã圹å²ã«å¿ããè¿çãšãªããŸãã â©
-
ãæå€±èšç®ããé€å€ãããâ ã該åœéšåãåŠç¿ãããªãããªã®ã§æ³šæã§ãã<assistant>以éã ãã§ãæ¡ä»¶ä»ãçæèŠåãç²åŸã§ããŸãããããïŒãã¹ãп倱èšç®ããã»ãããããšããç ç©¶ãããå°ã調ã¹ããå 容ã§ããããŸãã
- Shi ä» (2024) "Instruction Tuning With Loss Over Instructions" Advances in Neural Information Processing Systems 37
-
SFTã®ããŒã¿ãäœãéã«è¿çéšåãçãã«ãªãããã«æèãããããããé·ãã®è¿çãé¢åã ããSFTçšã®ããŒã¿æ§é ãåŠç¿çµæã«åæ ãããã®ã§ãã£ãããšèšèšããããã®ãè¯ããšæããŸããð â©
-
SFTããŒã¿é¢åïŒç¢ºãã«ãããæããŸããææªãlivedoorã³ãŒãã¹ã®ã¿ã€ãã«æãšæ¬æã®å é 2æãå©çšããŠã
- 質åæïŒãã¿ã€ãã«æãã«ã€ããŠç޹ä»ããŠãã ãã
- å¿çæïŒãæ¬æã®å é 2æã
ãšãã圢ã§ãSFTçšã®ããŒã¿ãäœæããŠã圢åŒãåŠç¿ããŠããããšããæŒç¿ã¯å¯èœã§ãããè¿çãããNGã«ãªããŸãã â©
-
第38åã®äºååŠç¿ã§å©çšããRandomGPTDatasetã§ã¯ïŒããŒã¯ã³ãããã圢ã§labelsãäœæããŠãŸããã â©
-
工倫ããŠã³ã³ãã¯ãã«ãŸãšãããããšæãã®ã§ããðŠãåé·ãªäžã«ãããã«ããããšæããŸããæéãããšãã«ã§ãæ¹å€ããããã
â©