인과 언어 모델링
인과 언어 모델링 (Causal language modeling)
언어 모델링에는 인과적(causal) 방식과 마스킹된(masked) 방식 두 가지가 있습니다. 이 가이드는 인과 언어 모델링을 설명합니다. 인과 언어 모델은 텍스트 생성에 자주 사용되며, 직접 만든 텍스트 어드벤처를 고르는 창의적인 애플리케이션이나 Copilot, CodeParrot 같은 지능형 코딩 어시스턴트에 사용할 수 있습니다.
출처: 문서
본문
인과 언어 모델링은 토큰 시퀀스에서 다음 토큰을 예측하며, 모델은 왼쪽에 있는 토큰만 참조(attend)할 수 있습니다. 즉, 모델은 미래의 토큰을 볼 수 없습니다. GPT-2가 인과 언어 모델의 예입니다.
이 가이드에서 다룰 내용은 다음과 같습니다.
- ELI5 데이터셋의 r/askscience 하위 집합에서 DistilGPT2를 파인튜닝합니다.
- 파인튜닝된 모델을 추론(inference)에 사용합니다.
이 작업과 호환되는 모든 아키텍처와 체크포인트를 보려면 task-page를 확인하는 것을 권장합니다.
시작하기 전에 필요한 라이브러리를 모두 설치했는지 확인해 주세요.
pip install transformers datasets evaluate
Hugging Face 계정에 로그인해 모델을 커뮤니티에 업로드하고 공유하는 것을 권장합니다. 프롬프트가 나타나면 토큰을 입력해 로그인하세요.
>>> from huggingface_hub import notebook_login
>>> notebook_login()
ELI5 데이터셋 로드하기
🤗 Datasets 라이브러리로 ELI5-Category 데이터셋의 처음 5000개 예시를 로드하는 것부터 시작하겠습니다. 이렇게 하면 전체 데이터셋으로 훈련하는 데 더 많은 시간을 쓰기 전에 실험해 보고 모든 것이 잘 작동하는지 확인할 수 있습니다.
>>> from datasets import load_dataset
>>> eli5 = load_dataset("dany0407/eli5_category", split="train[:5000]")
train_test_split 메서드로 데이터셋의 train 스플릿을 train과 test 세트로 나눕니다.
>>> eli5 = eli5.train_test_split(test_size=0.2)
그런 다음 예시를 하나 살펴봅니다.
>>> eli5["train"][0]
{'q_id': '7h191n',
'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
'selftext': '',
'category': 'Economics',
'subreddit': 'explainlikeimfive',
'answers': {'a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
'text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
'score': [21, 19, 5, 3],
'text_urls': [[],
[],
[],
['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']]},
'title_urls': ['url'],
'selftext_urls': ['url']}
많아 보일 수 있지만, 실제로 관심 있는 것은 text 필드뿐입니다. 언어 모델링 작업의 멋진 점은 라벨이 필요 없다는 것입니다(비지도 작업이라고도 함). 다음 단어가 곧 라벨이기 때문입니다.
전처리 (Preprocess)
다음 단계는 text 하위 필드를 처리할 DistilGPT2 토크나이저를 로드하는 것입니다.
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilgpt2")
위의 예시에서 text 필드가 실제로는 answers 안에 중첩되어 있다는 것을 알 수 있습니다. 즉, flatten 메서드로 중첩 구조에서 text 하위 필드를 추출해야 합니다.
>>> eli5 = eli5.flatten()
>>> eli5["train"][0]
{'q_id': '7h191n',
'title': 'What does the tax bill that was passed today mean? How will it affect Americans in each tax bracket?',
'selftext': '',
'category': 'Economics',
'subreddit': 'explainlikeimfive',
'answers.a_id': ['dqnds8l', 'dqnd1jl', 'dqng3i1', 'dqnku5x'],
'answers.text': ["The tax bill is 500 pages long and there were a lot of changes still going on right to the end. It's not just an adjustment to the income tax brackets, it's a whole bunch of changes. As such there is no good answer to your question. The big take aways are: - Big reduction in corporate income tax rate will make large companies very happy. - Pass through rate change will make certain styles of business (law firms, hedge funds) extremely happy - Income tax changes are moderate, and are set to expire (though it's the kind of thing that might just always get re-applied without being made permanent) - People in high tax states (California, New York) lose out, and many of them will end up with their taxes raised.",
'None yet. It has to be reconciled with a vastly different house bill and then passed again.',
'Also: does this apply to 2017 taxes? Or does it start with 2018 taxes?',
'This article explains both the House and senate bills, including the proposed changes to your income taxes based on your income level. URL_0'],
'answers.score': [21, 19, 5, 3],
'answers.text_urls': [[],
[],
[],
['https://www.investopedia.com/news/trumps-tax-reform-what-can-be-done/']],
'title_urls': ['url'],
'selftext_urls': ['url']}
이제 각 하위 필드는 answers 접두사로 표시된 별도의 컬럼이 되었고, text 필드는 이제 리스트입니다. 각 문장을 개별적으로 토크나이즈하는 대신 리스트를 문자열로 변환해 공동으로 토크나이즈할 수 있습니다.
다음은 각 예시의 문자열 리스트를 합치고 결과를 토크나이즈하는 첫 번째 전처리 함수입니다.
>>> def preprocess_function(examples):
... return tokenizer([" ".join(x) for x in examples["answers.text"]])
이 전처리 함수를 전체 데이터셋에 적용하려면 🤗 Datasets map 메서드를 사용하세요. batched=True로 설정하면 데이터셋의 여러 요소를 한 번에 처리하고 num_proc으로 프로세스 수를 늘려 map 함수를 더 빠르게 할 수 있습니다. 필요하지 않은 컬럼은 모두 제거하세요.
>>> tokenized_eli5 = eli5.map(
... preprocess_function,
... batched=True,
... num_proc=4,
... remove_columns=eli5["train"].column_names,
... )
이 데이터셋에는 토큰 시퀀스가 포함되어 있지만, 일부는 모델의 최대 입력 길이보다 깁니다.
이제 두 번째 전처리 함수를 사용해
- 모든 시퀀스를 연결하고
- 연결된 시퀀스를
block_size로 정의된 더 짧은 청크로 나눌 수 있습니다.block_size는 최대 입력 길이보다 짧아야 하고 GPU RAM에 충분히 맞을 정도로 짧아야 합니다.
>>> block_size = 128
>>> def group_texts(examples):
... # Concatenate all texts.
... concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
... total_length = len(concatenated_examples[list(examples.keys())[0]])
... # We drop the small remainder, we could add padding if the model supported it instead of this drop, you can
... # customize this part to your needs.
... if total_length >= block_size:
... total_length = (total_length // block_size) * block_size
... # Split by chunks of block_size.
... result = {
... k: [t[i : i + block_size] for i in range(0, total_length, block_size)]
... for k, t in concatenated_examples.items()
... }
... result["labels"] = result["input_ids"].copy()
... return result
전체 데이터셋에 group_texts 함수를 적용합니다.
>>> lm_dataset = tokenized_eli5.map(group_texts, batched=True, num_proc=4)
이제 DataCollatorForLanguageModeling을 사용해 예시 배치를 만듭니다. 전체 데이터셋을 최대 길이로 패딩하는 것보다 콜레이션 중에 문장을 배치에서 가장 긴 길이로 동적으로 패딩하는 것이 더 효율적입니다.
끝 시퀀스 토큰을 패딩 토큰으로 사용하고 mlm=False로 설정합니다. 이렇게 하면 입력이 한 요소씩 오른쪽으로 이동한 라벨로 사용됩니다.
>>> from transformers import DataCollatorForLanguageModeling
>>> tokenizer.pad_token = tokenizer.eos_token
>>> data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
훈련 (Train)
Trainer로 모델을 파인튜닝하는 방법이 익숙하지 않다면 basic tutorial를 확인해 보세요!
이제 모델 훈련을 시작할 준비가 되었습니다! AutoModelForCausalLM으로 DistilGPT2를 로드합니다.
>>> from transformers import AutoModelForCausalLM, TrainingArguments, Trainer
>>> model = AutoModelForCausalLM.from_pretrained("distilbert/distilgpt2")
이 시점에서 남은 단계는 세 가지뿐입니다.
- TrainingArguments에서 훈련 하이퍼파라미터를 정의합니다. 유일한 필수 매개변수는 모델을 저장할 위치를 지정하는
output_dir입니다.push_to_hub=True로 설정하면 이 모델을 Hub에 푸시합니다(모델을 업로드하려면 Hugging Face에 로그인해야 합니다). - Trainer에 모델, 데이터셋, 데이터 콜레이터와 함께 훈련 인자를 전달합니다.
- train()을 호출해 모델을 파인튜닝합니다.
>>> training_args = TrainingArguments(
... output_dir="my_awesome_eli5_clm-model",
... eval_strategy="epoch",
... learning_rate=2e-5,
... weight_decay=0.01,
... push_to_hub=True,
... )
>>> trainer = Trainer(
... model=model,
... args=training_args,
... train_dataset=lm_dataset["train"],
... eval_dataset=lm_dataset["test"],
... data_collator=data_collator,
... processing_class=tokenizer,
... )
>>> trainer.train()
훈련이 끝나면 evaluate() 메서드로 모델을 평가하고 perplexity를 얻습니다.
>>> import math
>>> eval_results = trainer.evaluate()
>>> print(f"Perplexity: {math.exp(eval_results['eval_loss']):.2f}")
Perplexity: 49.61
그런 다음 push_to_hub() 메서드로 모델을 Hub에 공유해 모두가 사용할 수 있게 하세요.
>>> trainer.push_to_hub()
인과 언어 모델링을 위해 모델을 파인튜닝하는 더 심층적인 예시는 해당 PyTorch notebook을 참고하세요.
추론 (Inference)
좋습니다. 이제 모델을 파인튜닝했으니 추론에 사용할 수 있습니다!
텍스트를 생성할 프롬프트를 생각해 보세요.
>>> prompt = "Somatic hypermutation allows the immune system to"
파인튜닝된 모델을 추론에 사용해 보는 가장 간단한 방법은 pipeline()에서 사용하는 것입니다. 모델로 텍스트 생성용 pipeline을 만들고 텍스트를 전달합니다.
>>> from transformers import pipeline
>>> generator = pipeline("text-generation", model="username/my_awesome_eli5_clm-model")
>>> generator(prompt)
[{'generated_text': "Somatic hypermutation allows the immune system to be able to effectively reverse the damage caused by an infection.\n\n\nThe damage caused by an infection is caused by the immune system's ability to perform its own self-correcting tasks."}]
텍스트를 토크나이즈하고 input_ids를 PyTorch 텐서로 반환받습니다.
>>> from transformers import AutoTokenizer
>>> tokenizer = AutoTokenizer.from_pretrained("username/my_awesome_eli5_clm-model")
>>> inputs = tokenizer(prompt, return_tensors="pt").input_ids
generate() 메서드를 사용해 텍스트를 생성합니다. 다양한 텍스트 생성 전략과 생성을 제어하는 매개변수에 대한 자세한 내용은 Text generation strategies 페이지를 확인하세요.
>>> from transformers import AutoModelForCausalLM
>>> model = AutoModelForCausalLM.from_pretrained("username/my_awesome_eli5_clm-model")
>>> outputs = model.generate(inputs, max_new_tokens=100, do_sample=True, top_k=50, top_p=0.95)
생성된 토큰 id를 다시 텍스트로 디코딩합니다.
>>> tokenizer.batch_decode(outputs, skip_special_tokens=True)
["Somatic hypermutation allows the immune system to react to drugs with the ability to adapt to a different environmental situation. In other words, a system of 'hypermutation' can help the immune system to adapt to a different environmental situation or in some cases even a single life. In contrast, researchers at the University of Massachusetts-Boston have found that 'hypermutation' is much stronger in mice than in humans but can be found in humans, and that it's not completely unknown to the immune system. A study on how the immune system"]