ahassoun's picture
Upload 3018 files
ee6e328

A newer version of the Gradio SDK is available: 5.23.3

Upgrade

λŒ€κ·œλͺ¨ μ–Έμ–΄ λͺ¨λΈλ‘œ μƒμ„±ν•˜κΈ° [[generation-with-llms]]

[[open-in-colab]]

LLM λ˜λŠ” λŒ€κ·œλͺ¨ μ–Έμ–΄ λͺ¨λΈμ€ ν…μŠ€νŠΈ μƒμ„±μ˜ 핡심 ꡬ성 μš”μ†Œμž…λ‹ˆλ‹€. κ°„λ‹¨νžˆ λ§ν•˜λ©΄, 주어진 μž…λ ₯ ν…μŠ€νŠΈμ— λŒ€ν•œ λ‹€μŒ 단어(μ •ν™•ν•˜κ²ŒλŠ” 토큰)λ₯Ό μ˜ˆμΈ‘ν•˜κΈ° μœ„ν•΄ ν›ˆλ ¨λœ λŒ€κ·œλͺ¨ 사전 ν›ˆλ ¨ λ³€ν™˜κΈ° λͺ¨λΈλ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€. 토큰을 ν•œ λ²ˆμ— ν•˜λ‚˜μ”© μ˜ˆμΈ‘ν•˜κΈ° λ•Œλ¬Έμ— μƒˆλ‘œμš΄ λ¬Έμž₯을 μƒμ„±ν•˜λ €λ©΄ λͺ¨λΈμ„ ν˜ΈμΆœν•˜λŠ” 것 외에 더 λ³΅μž‘ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€. 즉, μžκΈ°νšŒκ·€ 생성을 μˆ˜ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€.

μžκΈ°νšŒκ·€ 생성은 λͺ‡ 개의 초기 μž…λ ₯값을 μ œκ³΅ν•œ ν›„, κ·Έ 좜λ ₯을 λ‹€μ‹œ λͺ¨λΈμ— μž…λ ₯으둜 μ‚¬μš©ν•˜μ—¬ 반볡적으둜 ν˜ΈμΆœν•˜λŠ” μΆ”λ‘  κ³Όμ •μž…λ‹ˆλ‹€. πŸ€— Transformersμ—μ„œλŠ” [~generation.GenerationMixin.generate] λ©”μ†Œλ“œκ°€ 이 역할을 ν•˜λ©°, μ΄λŠ” 생성 κΈ°λŠ₯을 가진 λͺ¨λ“  λͺ¨λΈμ—μ„œ μ‚¬μš© κ°€λŠ₯ν•©λ‹ˆλ‹€.

이 νŠœν† λ¦¬μ–Όμ—μ„œλŠ” λ‹€μŒ λ‚΄μš©μ„ λ‹€λ£¨κ²Œ λ©λ‹ˆλ‹€:

  • LLM으둜 ν…μŠ€νŠΈ 생성
  • 일반적으둜 λ°œμƒν•˜λŠ” 문제 ν•΄κ²°
  • LLM을 μ΅œλŒ€ν•œ ν™œμš©ν•˜κΈ° μœ„ν•œ λ‹€μŒ 단계

μ‹œμž‘ν•˜κΈ° 전에 ν•„μš”ν•œ λͺ¨λ“  λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μ„€μΉ˜λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”:

pip install transformers bitsandbytes>=0.39.0 -q

ν…μŠ€νŠΈ 생성 [[generate-text]]

인과적 μ–Έμ–΄ λͺ¨λΈλ§(causal language modeling)을 λͺ©μ μœΌλ‘œ ν•™μŠ΅λœ μ–Έμ–΄ λͺ¨λΈμ€ 일련의 ν…μŠ€νŠΈ 토큰을 μž…λ ₯으둜 μ‚¬μš©ν•˜κ³ , κ·Έ 결과둜 λ‹€μŒ 토큰이 λ‚˜μ˜¬ ν™•λ₯  뢄포λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

"LLM의 μ „λ°© 패슀"

LLMκ³Ό μžκΈ°νšŒκ·€ 생성을 ν•¨κ»˜ μ‚¬μš©ν•  λ•Œ 핡심적인 뢀뢄은 이 ν™•λ₯  λΆ„ν¬λ‘œλΆ€ν„° λ‹€μŒ 토큰을 μ–΄λ–»κ²Œ κ³ λ₯Ό κ²ƒμΈμ§€μž…λ‹ˆλ‹€. λ‹€μŒ 반볡 과정에 μ‚¬μš©λ  토큰을 κ²°μ •ν•˜λŠ” ν•œ, μ–΄λ– ν•œ 방법도 κ°€λŠ₯ν•©λ‹ˆλ‹€. ν™•λ₯  λΆ„ν¬μ—μ„œ κ°€μž₯ κ°€λŠ₯성이 높은 토큰을 μ„ νƒν•˜λŠ” κ²ƒμ²˜λŸΌ 간단할 μˆ˜λ„ 있고, κ²°κ³Ό λΆ„ν¬μ—μ„œ μƒ˜ν”Œλ§ν•˜κΈ° 전에 μˆ˜μ‹­ 가지 λ³€ν™˜μ„ μ μš©ν•˜λŠ” κ²ƒμ²˜λŸΌ λ³΅μž‘ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

"μžκΈ°νšŒκ·€ 생성은 ν™•λ₯  λΆ„ν¬μ—μ„œ λ‹€μŒ 토큰을 반볡적으둜 μ„ νƒν•˜μ—¬ ν…μŠ€νŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€."

μœ„μ—μ„œ μ„€λͺ…ν•œ 과정은 μ–΄λ–€ μ’…λ£Œ 쑰건이 좩쑱될 λ•ŒκΉŒμ§€ 반볡적으둜 μˆ˜ν–‰λ©λ‹ˆλ‹€. λͺ¨λΈμ΄ μ‹œν€€μŠ€μ˜ 끝(EOS 토큰)을 좜λ ₯ν•  λ•ŒκΉŒμ§€λ₯Ό μ’…λ£Œ 쑰건으둜 ν•˜λŠ” 것이 μ΄μƒμ μž…λ‹ˆλ‹€. 그렇지 μ•Šμ€ κ²½μš°μ—λŠ” 미리 μ •μ˜λœ μ΅œλŒ€ 길이에 λ„λ‹¬ν–ˆμ„ λ•Œ 생성이 μ€‘λ‹¨λ©λ‹ˆλ‹€.

λͺ¨λΈμ΄ μ˜ˆμƒλŒ€λ‘œ λ™μž‘ν•˜κΈ° μœ„ν•΄μ„  토큰 선택 단계와 정지 쑰건을 μ˜¬λ°”λ₯΄κ²Œ μ„€μ •ν•˜λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ 이유둜, 각 λͺ¨λΈμ—λŠ” κΈ°λ³Έ 생성 섀정이 잘 μ •μ˜λœ [~generation.GenerationConfig] 파일이 ν•¨κ»˜ μ œκ³΅λ©λ‹ˆλ‹€.

μ½”λ“œλ₯Ό ν™•μΈν•΄λ΄…μ‹œλ‹€!

κΈ°λ³Έ LLM μ‚¬μš©μ— 관심이 μžˆλ‹€λ©΄, 우리의 Pipeline μΈν„°νŽ˜μ΄μŠ€λ‘œ μ‹œμž‘ν•˜λŠ” 것을 μΆ”μ²œν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ LLM은 μ–‘μžν™”λ‚˜ 토큰 선택 λ‹¨κ³„μ—μ„œμ˜ λ―Έμ„Έν•œ μ œμ–΄μ™€ 같은 κ³ κΈ‰ κΈ°λŠ₯듀을 μ’…μ’… ν•„μš”λ‘œ ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ μž‘μ—…μ€ [~generation.GenerationMixin.generate]λ₯Ό 톡해 κ°€μž₯ 잘 μˆ˜ν–‰λ  수 μžˆμŠ΅λ‹ˆλ‹€. LLM을 μ΄μš©ν•œ μžκΈ°νšŒκ·€ 생성은 μžμ›μ„ 많이 μ†Œλͺ¨ν•˜λ―€λ‘œ, μ μ ˆν•œ μ²˜λ¦¬λŸ‰μ„ μœ„ν•΄ GPUμ—μ„œ μ‹€ν–‰λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

λ¨Όμ €, λͺ¨λΈμ„ λΆˆλŸ¬μ˜€μ„Έμš”.

>>> from transformers import AutoModelForCausalLM

>>> model = AutoModelForCausalLM.from_pretrained(
...     "openlm-research/open_llama_7b", device_map="auto", load_in_4bit=True
... )

from_pretrained ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  λ•Œ 2개의 ν”Œλž˜κ·Έλ₯Ό μ£Όλͺ©ν•˜μ„Έμš”:

  • device_map은 λͺ¨λΈμ΄ GPU둜 μ΄λ™λ˜λ„λ‘ ν•©λ‹ˆλ‹€.
  • load_in_4bitλŠ” λ¦¬μ†ŒμŠ€ μš”κ΅¬ 사항을 크게 쀄이기 μœ„ν•΄ 4λΉ„νŠΈ 동적 μ–‘μžν™”λ₯Ό μ μš©ν•©λ‹ˆλ‹€.

이 외에도 λͺ¨λΈμ„ μ΄ˆκΈ°ν™”ν•˜λŠ” λ‹€μ–‘ν•œ 방법이 μžˆμ§€λ§Œ, LLM을 처음 μ‹œμž‘ν•  λ•Œ 이 섀정을 μΆ”μ²œν•©λ‹ˆλ‹€.

μ΄μ–΄μ„œ ν…μŠ€νŠΈ μž…λ ₯을 ν† ν¬λ‚˜μ΄μ €μœΌλ‘œ μ „μ²˜λ¦¬ν•˜μ„Έμš”.

>>> from transformers import AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("openlm-research/open_llama_7b")
>>> model_inputs = tokenizer(["A list of colors: red, blue"], return_tensors="pt").to("cuda")

model_inputs λ³€μˆ˜μ—λŠ” ν† ν°ν™”λœ ν…μŠ€νŠΈ μž…λ ₯κ³Ό ν•¨κ»˜ μ–΄ν…μ…˜ λ§ˆμŠ€ν¬κ°€ λ“€μ–΄ μžˆμŠ΅λ‹ˆλ‹€. [~generation.GenerationMixin.generate]λŠ” μ–΄ν…μ…˜ λ§ˆμŠ€ν¬κ°€ μ œκ³΅λ˜μ§€ μ•Šμ•˜μ„ κ²½μš°μ—λ„ 이λ₯Ό μΆ”λ‘ ν•˜λ €κ³  λ…Έλ ₯ν•˜μ§€λ§Œ, μ΅œμƒμ˜ μ„±λŠ₯을 μœ„ν•΄μ„œλŠ” κ°€λŠ₯ν•˜λ©΄ μ–΄ν…μ…˜ 마슀크λ₯Ό μ „λ‹¬ν•˜λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ [~generation.GenerationMixin.generate] λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•΄ μƒμ„±λœ 토큰을 얻은 ν›„, 이λ₯Ό 좜λ ₯ν•˜κΈ° 전에 ν…μŠ€νŠΈ ν˜•νƒœλ‘œ λ³€ν™˜ν•˜μ„Έμš”.

>>> generated_ids = model.generate(**model_inputs)
>>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
'A list of colors: red, blue, green, yellow, black, white, and brown'

이게 μ „λΆ€μž…λ‹ˆλ‹€! λͺ‡ μ€„μ˜ μ½”λ“œλ§ŒμœΌλ‘œ LLM의 λŠ₯λ ₯을 ν™œμš©ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

일반적으둜 λ°œμƒν•˜λŠ” 문제 [[common-pitfalls]]

생성 μ „λž΅μ΄ 많고, 기본값이 항상 μ‚¬μš© 사둀에 μ ν•©ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 좜λ ₯이 μ˜ˆμƒκ³Ό λ‹€λ₯Ό λ•Œ ν”νžˆ λ°œμƒν•˜λŠ” λ¬Έμ œμ™€ 이λ₯Ό ν•΄κ²°ν•˜λŠ” 방법에 λŒ€ν•œ λͺ©λ‘μ„ λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€.

>>> from transformers import AutoModelForCausalLM, AutoTokenizer

>>> tokenizer = AutoTokenizer.from_pretrained("openlm-research/open_llama_7b")
>>> tokenizer.pad_token = tokenizer.eos_token  # Llama has no pad token by default
>>> model = AutoModelForCausalLM.from_pretrained(
...     "openlm-research/open_llama_7b", device_map="auto", load_in_4bit=True
... )

μƒμ„±λœ 좜λ ₯이 λ„ˆλ¬΄ μ§§κ±°λ‚˜ κΈΈλ‹€ [[generated-output-is-too-shortlong]]

[~generation.GenerationConfig] νŒŒμΌμ—μ„œ λ³„λ„λ‘œ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, generateλŠ” 기본적으둜 μ΅œλŒ€ 20개의 토큰을 λ°˜ν™˜ν•©λ‹ˆλ‹€. generate ν˜ΈμΆœμ—μ„œ max_new_tokens을 μˆ˜λ™μœΌλ‘œ μ„€μ •ν•˜μ—¬ λ°˜ν™˜ν•  수 μžˆλŠ” μƒˆ ν† ν°μ˜ μ΅œλŒ€ 수λ₯Ό μ„€μ •ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. LLM(μ •ν™•ν•˜κ²ŒλŠ” 디코더 μ „μš© λͺ¨λΈ)은 μž…λ ₯ ν”„λ‘¬ν”„νŠΈλ„ 좜λ ₯의 μΌλΆ€λ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.

>>> model_inputs = tokenizer(["A sequence of numbers: 1, 2"], return_tensors="pt").to("cuda")

>>> # By default, the output will contain up to 20 tokens
>>> generated_ids = model.generate(**model_inputs)
>>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
'A sequence of numbers: 1, 2, 3, 4, 5'

>>> # Setting `max_new_tokens` allows you to control the maximum length
>>> generated_ids = model.generate(**model_inputs, max_new_tokens=50)
>>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
'A sequence of numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,'

잘λͺ»λœ 생성 λͺ¨λ“œ [[incorrect-generation-mode]]

기본적으둜 [~generation.GenerationConfig] νŒŒμΌμ—μ„œ λ³„λ„λ‘œ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, generateλŠ” 각 λ°˜λ³΅μ—μ„œ κ°€μž₯ ν™•λ₯ μ΄ 높은 토큰을 μ„ νƒν•©λ‹ˆλ‹€(그리디 λ””μ½”λ”©). ν•˜λ €λŠ” μž‘μ—…μ— 따라 이 방법은 λ°”λžŒμ§ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ±—λ΄‡μ΄λ‚˜ 에세이 μž‘μ„±κ³Ό 같은 창의적인 μž‘μ—…μ€ μƒ˜ν”Œλ§μ΄ 적합할 수 μžˆμŠ΅λ‹ˆλ‹€. 반면, μ˜€λ””μ˜€λ₯Ό ν…μŠ€νŠΈλ‘œ λ³€ν™˜ν•˜κ±°λ‚˜ λ²ˆμ—­κ³Ό 같은 μž…λ ₯ 기반 μž‘μ—…μ€ 그리디 디코딩이 더 적합할 수 μžˆμŠ΅λ‹ˆλ‹€. do_sample=True둜 μƒ˜ν”Œλ§μ„ ν™œμ„±ν™”ν•  수 있으며, 이 μ£Όμ œμ— λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ€ 이 λΈ”λ‘œκ·Έ ν¬μŠ€νŠΈμ—μ„œ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

>>> # Set seed or reproducibility -- you don't need this unless you want full reproducibility
>>> from transformers import set_seed
>>> set_seed(0)

>>> model_inputs = tokenizer(["I am a cat."], return_tensors="pt").to("cuda")

>>> # LLM + greedy decoding = repetitive, boring output
>>> generated_ids = model.generate(**model_inputs)
>>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
'I am a cat. I am a cat. I am a cat. I am a cat'

>>> # With sampling, the output becomes more creative!
>>> generated_ids = model.generate(**model_inputs, do_sample=True)
>>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
'I am a cat.\nI just need to be. I am always.\nEvery time'

잘λͺ»λœ νŒ¨λ”© [[wrong-padding-side]]

LLM은 디코더 μ „μš© ꡬ쑰λ₯Ό 가지고 μžˆμ–΄, μž…λ ₯ ν”„λ‘¬ν”„νŠΈμ— λŒ€ν•΄ μ§€μ†μ μœΌλ‘œ 반볡 처리λ₯Ό ν•©λ‹ˆλ‹€. μž…λ ₯ λ°μ΄ν„°μ˜ 길이가 λ‹€λ₯΄λ©΄ νŒ¨λ”© μž‘μ—…μ΄ ν•„μš”ν•©λ‹ˆλ‹€. LLM은 νŒ¨λ”© ν† ν°μ—μ„œ μž‘λ™μ„ 이어가도둝 μ„€κ³„λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ—, μž…λ ₯ μ™Όμͺ½μ— νŒ¨λ”©μ΄ μΆ”κ°€ λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€. 그리고 μ–΄ν…μ…˜ λ§ˆμŠ€ν¬λ„ κΌ­ generate ν•¨μˆ˜μ— μ „λ‹¬λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€!

>>> # The tokenizer initialized above has right-padding active by default: the 1st sequence,
>>> # which is shorter, has padding on the right side. Generation fails.
>>> model_inputs = tokenizer(
...     ["1, 2, 3", "A, B, C, D, E"], padding=True, return_tensors="pt"
... ).to("cuda")
>>> generated_ids = model.generate(**model_inputs)
>>> tokenizer.batch_decode(generated_ids[0], skip_special_tokens=True)[0]
''

>>> # With left-padding, it works as expected!
>>> tokenizer = AutoTokenizer.from_pretrained("openlm-research/open_llama_7b", padding_side="left")
>>> tokenizer.pad_token = tokenizer.eos_token  # Llama has no pad token by default
>>> model_inputs = tokenizer(
...     ["1, 2, 3", "A, B, C, D, E"], padding=True, return_tensors="pt"
... ).to("cuda")
>>> generated_ids = model.generate(**model_inputs)
>>> tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
'1, 2, 3, 4, 5, 6,'

μΆ”κ°€ 자료 [[further-resources]]

μžκΈ°νšŒκ·€ 생성 ν”„λ‘œμ„ΈμŠ€λŠ” μƒλŒ€μ μœΌλ‘œ λ‹¨μˆœν•œ νŽΈμ΄μ§€λ§Œ, LLM을 μ΅œλŒ€ν•œ ν™œμš©ν•˜λ €λ©΄ μ—¬λŸ¬ 가지 μš”μ†Œλ₯Ό κ³ λ €ν•΄μ•Ό ν•˜λ―€λ‘œ 쉽지 μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. LLM에 λŒ€ν•œ 더 κΉŠμ€ 이해와 ν™œμš©μ„ μœ„ν•œ λ‹€μŒ λ‹¨κ³„λŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€:

κ³ κΈ‰ 생성 μ‚¬μš© [[advanced-generate-usage]]

  1. κ°€μ΄λ“œλŠ” λ‹€μ–‘ν•œ 생성 방법을 μ œμ–΄ν•˜λŠ” 방법, 생성 μ„€μ • νŒŒμΌμ„ μ„€μ •ν•˜λŠ” 방법, 좜λ ₯을 μŠ€νŠΈλ¦¬λ°ν•˜λŠ” 방법에 λŒ€ν•΄ μ„€λͺ…ν•©λ‹ˆλ‹€.
  2. [~generation.GenerationConfig]와 [~generation.GenerationMixin.generate], generate-related classesλ₯Ό μ°Έμ‘°ν•΄λ³΄μ„Έμš”.

LLM λ¦¬λ”λ³΄λ“œ [[llm-leaderboards]]

  1. Open LLM LeaderboardλŠ” μ˜€ν”ˆ μ†ŒμŠ€ λͺ¨λΈμ˜ ν’ˆμ§ˆμ— 쀑점을 λ‘‘λ‹ˆλ‹€.
  2. Open LLM-Perf LeaderboardλŠ” LLM μ²˜λ¦¬λŸ‰μ— 쀑점을 λ‘‘λ‹ˆλ‹€.

지연 μ‹œκ°„ 및 μ²˜λ¦¬λŸ‰ [[latency-and-throughput]]

  1. λ©”λͺ¨λ¦¬ μš”κ΅¬ 사항을 쀄이렀면, 동적 μ–‘μžν™”μ— λŒ€ν•œ κ°€μ΄λ“œλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.

κ΄€λ ¨ 라이브러리 [[related-libraries]]

  1. text-generation-inferenceλŠ” LLM을 μœ„ν•œ μ‹€μ œ 운영 ν™˜κ²½μ— μ ν•©ν•œ μ„œλ²„μž…λ‹ˆλ‹€.
  2. optimum은 νŠΉμ • ν•˜λ“œμ›¨μ–΄ μž₯μΉ˜μ—μ„œ LLM을 μ΅œμ ν™”ν•˜κΈ° μœ„ν•΄ πŸ€— Transformersλ₯Ό ν™•μž₯ν•œ κ²ƒμž…λ‹ˆλ‹€.