본문 바로가기
C.W.K.
Stream
Lesson 02 of 07 · published

텍스트 생성 — 스트리밍과 멈춤 토큰

~14 min · generation, streaming, stop-tokens

Level 0호기심
0 XP0/51 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

generate()stream_generate()

레슨 1의 generate()는 생성이 끝난 뒤 답 전체를 문자열 하나로 줘. 채팅 말풍선이나 터미널처럼 토큰이 생기는 즉시 보여주고 싶다면 stream_generate()를 써. 토큰마다 조각 하나를 내놓는 Python 생성기야.

둘 다 mlx-lm의 정식 API야. generate()stream_generate()를 얇게 감싸 조각들을 한데 잇는 방식으로 만들어졌어. 첫 글자를 빨리 보여주는 게 중요하면 스트리밍을, 끝난 답만 필요하면 버퍼링을 골라.

스트림에는 텍스트보다 많은 게 들어 있어

매번 나오는 값은 토큰 하나가 아니라 여러 정보를 담은 GenerationResponse 객체야. 실제로 자주 쓸 필드는 이래.

  • .text — 이번 단계에 새로 생긴 텍스트 조각이야. 보통 토큰 하나고, 가끔은 나뉘어 전송된 유니코드 글자의 일부야.
  • .finish_reason — 생성 중에는 None이고, 마지막에는 멈춤 토큰을 만났다면 'stop', max_tokens에 닿았다면 'length'야.
  • .generation_tokens — 지금까지 생성한 토큰 수야.
  • .generation_tps — 생성 시작 뒤 측정한 초당 토큰 수라 실시간 처리량 표시에 좋아.
  • .peak_memory — 이번 생성에서 가장 많이 쓴 GPU 메모리 바이트야.

이 풍부한 정보가 mlx-lm의 숨은 장점이야. 처리량을 보려고 별도 측정기를 붙일 필요도 없고, 왜 멈췄는지 추측할 필요도 없어.

생성이 멈추는 세 가지 경우

  1. 모델이 토크나이저의 EOS 토큰을 냈어. Llama 3의 <|eot_id|>, Qwen의 <|im_end|> 같은 값이야. 토크나이저가 자기 EOS를 알고 mlx-lm이 그 값을 써. finish_reason='stop'이 돼.
  2. max_tokens에 닿았어. 이때는 finish_reason='length'야. 제한이 없었다면 모델이 더 이어갔다는 뜻이지.
  3. 직접 멈췄어. 생성기 반복문에서 break하면 문장 중간이라도 끝나. 특별한 종료 이유 없이 값이 더 나오지 않아.

빠른 시작 글이 자주 빼먹는 함정이 있어. 채팅 학습 모델은 매 턴 EOS 토큰을 보고 멈춰. 레슨 5의 채팅 템플릿을 빼먹으면 자연스러운 끝에서 EOS를 내지 못하고 max_tokens를 채울 때까지 달리는 일이 흔해.

Code

stream_generate — 토큰이 도착할 때 출력하기·python
from mlx_lm import load, stream_generate

model, tok = load("mlx-community/Llama-3.2-1B-Instruct-4bit")

print("Streaming: ", end="", flush=True)
for chunk in stream_generate(model, tok, prompt="Count 1 to 5:", max_tokens=30):
    print(chunk.text, end="", flush=True)
print()

# Verified output (2026-05-03):
#   Streaming: 1, 2, 3, 4, 5
#   Count 6 to 10:
#   ...
마지막 조각의 GenerationResponse 정보 살펴보기·python
from mlx_lm import load, stream_generate

model, tok = load("mlx-community/Llama-3.2-1B-Instruct-4bit")

last = None
for chunk in stream_generate(model, tok, prompt="Hello.", max_tokens=20):
    last = chunk

print("text fragment   :", repr(last.text))
print("finish_reason   :", last.finish_reason)         # 'stop' or 'length'
print("generation_tokens:", last.generation_tokens)    # how many tokens were produced
print("generation_tps  :", round(last.generation_tps, 1), "tokens/sec")
print("peak_memory MB  :", round(last.peak_memory / 1024 / 1024, 1))
직접 일찍 멈추기 — 생성기에서 break·python
from mlx_lm import load, stream_generate

model, tok = load("mlx-community/Llama-3.2-1B-Instruct-4bit")

# Stop the moment we've seen a period
buf = ""
for chunk in stream_generate(model, tok, prompt="One sentence about MLX:", max_tokens=200):
    buf += chunk.text
    print(chunk.text, end="", flush=True)
    if "." in chunk.text:
        break
print()
print("---")
print("Stopped early. Captured:", repr(buf))

External links

Exercise

두 번째 코드 블록을 새 프롬프트로 실행하고 generation_tps를 확인해. Apple Silicon에서 1B Q4 모델은 칩에 따라 200–800 tps 정도로 수백 단위가 나와야 해. 이어서 max_tokens=200으로 다시 돌려 처리량이 거의 같은지, 더 높거나 낮은지 비교해. KV 캐시가 자라면 뒤 토큰이 조금 느려져 긴 생성에서는 처리량이 살짝 떨어지는 경우가 많아. 관찰을 두 문장으로 적어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.