추론 가이드¶
CLI 또는 Python API로 Bit-Axon 모델을 실행합니다. 이 가이드에서는 빠른 추론, 대화형 채팅, 스트리밍, sampling 전략, 모델 로딩을 다룹니다.
빠른 시작¶
Bit-Axon을 설치하고 모델을 다운로드한 뒤 단일 명령으로 텍스트를 생성합니다.
pip install bit-axon
bit-axon download skyoo2003/bit-axon
bit-axon run "Explain quantum computing in simple terms."
CLI는 기본적으로 스트리밍으로 출력하며, 완료 시 토큰 수와 속도를 출력합니다.
Quantum computing uses qubits instead of classical bits...
── 128 tokens · 42.3 tok/s · TTFT 180ms ──
CLI 추론¶
단일 프롬프트¶
프롬프트를 위치 인자로 전달합니다. 모든 생성 파라미터를 플래그로 사용할 수 있습니다.
bit-axon run "Write a haiku about debugging" \
--model skyoo2003/bit-axon \
--max-tokens 256 \
--temperature 0.7 \
--top-k 40 \
--top-p 0.9 \
--seed 42
| 플래그 | 단축 | 기본값 | 설명 |
|---|---|---|---|
--model | -m | skyoo2003/bit-axon | 로컬 경로 또는 HuggingFace 저장소 ID |
--tokenizer | -t | 모델과 동일 | Tokenizer 경로 또는 HF 저장소 ID |
--max-tokens | 512 | 생성할 최대 토큰 수 | |
--temperature | 0.6 | Sampling temperature | |
--top-k | 50 | Top-k 필터링 | |
--top-p | 0.95 | Nucleus sampling 임계값 | |
--seed | 없음 | 재현성을 위한 난수 시드 | |
--no-stream | false | 마지막에 전체 응답 출력 |
위치 인자가 없으면 stdin에서 입력을 파이프로 받습니다.
대화형 채팅¶
--chat(또는 -c)으로 다중 턴 대화를 시작합니다.
채팅 루프는 턴 간 대화 기록을 유지하며, tokenizer의 채팅 템플릿을 자동으로 적용합니다. exit을 입력하거나 Ctrl+C를 눌러 종료합니다.
You: What are the main differences between Rust and Go?
Assistant: Rust prioritizes memory safety through ownership...
You: Which one would you pick for a web API?
Assistant: For a web API, Go is often the pragmatic choice...
소형 모델로 테스트¶
--config-small을 사용하면 가중치 다운로드 없이 즉시 작은 모델을 실행할 수 있습니다. 파이프라인 테스트에 유용합니다.
Python API¶
모델 로딩¶
load_model로 로컬 디렉토리나 HuggingFace Hub 저장소에서 가중치를 로드합니다. 로드 시 NF4 양자화를 적용하려면 quantize=True를 전달하세요.
from bit_axon.inference import load_model
# HuggingFace Hub에서 (자동 다운로드 및 캐싱)
model = load_model("skyoo2003/bit-axon", quantize=True)
# 로컬 디렉토리에서
model = load_model("./my-model", quantize=True)
load_model은 가중치 디렉토리에서 config.json을 찾습니다. 없으면 BitAxonConfig() 기본값으로 대체합니다. 디렉토리 내의 모든 .safetensors 파일이 로드됩니다.
사용자 정의 설정을 전달할 수도 있습니다.
from bit_axon import BitAxonConfig
from bit_axon.inference import load_model
config = BitAxonConfig(hidden_dim=256, num_layers=4)
model = load_model("./tiny-model", config=config)
기본 생성¶
generate 함수는 전체 자기회귀 루프를 실행합니다. 프롬프트를 프리필한 다음 max_tokens에 도달하거나 EOS 토큰이 샘플링될 때까지 한 토큰씩 디코딩합니다.
from bit_axon.inference import load_model, generate, GenerateConfig
from bit_axon.tokenizer import QwenTokenizerWrapper
model = load_model("skyoo2003/bit-axon", quantize=True)
tokenizer = QwenTokenizerWrapper("skyoo2003/bit-axon")
result = generate(
model,
tokenizer,
"Explain async/await in Python.",
config=GenerateConfig(max_tokens=256),
)
print(result.text)
print(f"Speed: {result.tokens_per_sec:.1f} tok/s")
GenerateConfig 옵션¶
GenerateConfig는 생성 동작을 제어합니다. 모든 필드에 합리적인 기본값이 있습니다.
config = GenerateConfig(
max_tokens=512, # 생성할 최대 토큰 수
temperature=0.6, # Sampling temperature (0.0 = 탐욕적, 1.0 = 창의적)
top_k=50, # Sampling 중 상위 k logits 유지 (0 = 비활성화)
top_p=0.95, # Nucleus sampling 임계값 (1.0 = 비활성화)
repetition_penalty=1.0, # 반복 토큰 페널티 (1.0 = 비활성화)
seed=42, # 재현성을 위한 난수 시드
)
result = generate(model, tokenizer, "prompt", config=config)
Tip
일관성이 중요한 작업(코드 생성, 구조화된 출력 등)에는 temperature=0.0으로 설정하세요. 결정론적 탐욕 디코딩을 사용합니다.
GenerateResult 필드¶
generate는 출력과 성능 메트릭이 포함된 GenerateResult를 반환합니다.
result = generate(model, tokenizer, "Hello, world!")
result.text # 디코딩된 출력 문자열
result.token_ids # 생성된 토큰 ID 목록 (프롬프트 제외)
result.prompt_tokens # 입력 프롬프트의 토큰 수
result.completion_tokens # 생성된 토큰 수
result.tokens_per_sec # 생성 처리량
result.time_to_first_token_ms # 프리필 시작부터 첫 토큰까지의 시간
메시지로 채팅¶
메시지 목록을 전달하여 tokenizer의 채팅 템플릿을 사용합니다.
messages = [
{"role": "system", "content": "You are a concise technical writer."},
{"role": "user", "content": "Explain KV caching."},
]
result = generate(model, tokenizer, "", messages=messages)
print(result.text)
또는 chat=True 플래그를 사용하여 단일 프롬프트를 채팅 템플릿으로 감쌀 수 있습니다.
스트리밍¶
stream=True로 설정하면 토큰이 생성될 때마다 부분 텍스트를 산출하는 제너레이터를 받습니다.
from bit_axon.inference import load_model, generate, GenerateConfig
from bit_axon.tokenizer import QwenTokenizerWrapper
model = load_model("skyoo2003/bit-axon", quantize=True)
tokenizer = QwenTokenizerWrapper("skyoo2003/bit-axon")
config = GenerateConfig(max_tokens=256, temperature=0.7)
for text in generate(model, tokenizer, "Tell me a story.", config=config, stream=True):
print(text, end="", flush=True)
# 제너레이터가 소진되면 GenerateResult 반환:
gen = generate(model, tokenizer, "prompt", config=config, stream=True)
for text in gen:
print(text, end="", flush=True)
result = gen.return_value
print(f"\n\n{result.completion_tokens} tokens at {result.tokens_per_sec:.1f} tok/s")
스트리밍은 채팅 모드에서도 동작합니다.
messages = [{"role": "user", "content": "Write a poem about the sea."}]
for text in generate(model, tokenizer, "", messages=messages, stream=True):
print(text, end="", flush=True)
Sampling 전략¶
Bit-Axon은 세 가지 sampling 필터를 순차적으로 적용합니다: temperature 스케일링, top-k 필터링, top-p (nucleus) sampling. 이들은 함께 조합되므로 세밀한 제어가 가능합니다.
Temperature¶
Temperature는 sampling 전 logits를 스케일링하여 출력의 무작위성을 제어합니다.
# 결정론적 출력: 항상 최고 확률 토큰 선택
greedy = GenerateConfig(temperature=0.0)
# 기본값: 약간의 무작위성, 대부분의 작업에 적합한 균형
balanced = GenerateConfig(temperature=0.6)
# 창의적: 더 높은 무작위성, 더 다양한 출력
creative = GenerateConfig(temperature=1.0)
- 0.0:
argmax를 통한 탐욕 디코딩. 무작위성 없음. 사실적이거나 코드 작성 작업에 최적. - 0.6: 기본값. 출력의 일관성을 유지하면서 통제된 변화를 추가.
- 1.0: 스케일링 없음. 순수 확률 분포, 최대 다양성.
- 1.0 초과: 분포를 더 평탄하게 만들어 무작위성은 높아지지만 일관성이 저하됨.
Warning
매우 높은 temperature(1.5 이상)는 일관성 없는 텍스트를 생성하는 경향이 있습니다. 대부분의 실용적인 사용 사례는 0.0과 1.0 사이에 있습니다.
Top-k 필터링¶
Top-k는 확률이 가장 높은 k개 토큰만 유지하고 나머지는 버립니다.
# 적극적: 상위 10개 토큰만 고려
config = GenerateConfig(top_k=10)
# 기본값: 상위 50개 토큰
config = GenerateConfig(top_k=50)
# 비활성화: 모든 토큰 고려
config = GenerateConfig(top_k=0)
값이 작을수록 모델이 더 집중되지만 덜 창의적입니다. top_k=0으로 설정하면 필터링이 완전히 비활성화됩니다.
Top-p (Nucleus) Sampling¶
Top-p는 누적 확률이 임계값을 초과하는 가장 작은 토큰 집합을 선택합니다.
# 엄격: 가장 확률이 높은 토큰만
config = GenerateConfig(top_p=0.8)
# 기본값: 적절한 균형
config = GenerateConfig(top_p=0.95)
# 비활성화: 필터링 없음
config = GenerateConfig(top_p=1.0)
Top-p는 확률 분포에 동적으로 적응합니다. 모델이 확신이 있을 때(하나의 토큰이 지배적), 여전히 작은 집합에서 선택합니다. 불확실할 때는 더 많은 옵션을 고려합니다.
전략 조합¶
세 필터는 순서대로 적용됩니다: temperature, top-k, top-p. 함께 잘 동작합니다.
# 집중적이고 사실적인 출력
config = GenerateConfig(temperature=0.2, top_k=20, top_p=0.85)
# 균형 잡힌 스토리텔링
config = GenerateConfig(temperature=0.7, top_k=50, top_p=0.95)
# 매우 창의적인 브레인스토밍
config = GenerateConfig(temperature=1.0, top_k=100, top_p=0.99)
재현 가능한 출력¶
시드를 설정하면 실행 간에 동일한 출력을 얻을 수 있습니다.
config = GenerateConfig(temperature=0.8, seed=42)
result1 = generate(model, tokenizer, "What is life?", config=config)
result2 = generate(model, tokenizer, "What is life?", config=config)
assert result1.text == result2.text # True
KV Cache¶
Bit-Axon의 24레이어 아키텍처는 샌드위치 구조에 맞춘 하이브리드 캐싱 전략을 사용합니다.
- 레이어 1-8 (순수 SSM): 외부 cache 없음. SSM 레이어는 토큰당 O(1)로 증가하는 내부 상태 벡터를 유지하므로 KV cache가 필요 없습니다.
- 레이어 9-16 (SWA + MoE): 슬라이딩 윈도우 어텐션을 위해
KVCache객체를 사용합니다. 이 cache는 4K 어텐션 윈도우의 key/value 쌍을 저장합니다. - 레이어 17-24 (SSM + MoE): 외부 cache 없음. 레이어 1-8과 마찬가지로 SSM 상태가 모든 것을 내부적으로 처리합니다.
모델을 호출하면 길이 24의 caches 목록이 반환됩니다. 위치 0-8과 17-23은 None이며, 위치 8-16은 KVCache 인스턴스를 갖습니다.
import mlx.core as mx
input_ids = mx.array([[1, 42, 100, 200, 500]], dtype=mx.uint32)
logits, caches = model(input_ids)
# caches[0:8] -> None (SSM 레이어)
# caches[8:16] -> KVCache 객체 (SWA 레이어)
# caches[16:24] -> None (SSM + MoE 레이어)
자기회귀 생성 중 cache는 각 디코드 스텝마다 전달됩니다.
logits, caches = model(input_ids) # Prefill
logits, caches = model(next_token, cache=caches) # 디코드 스텝 1
logits, caches = model(next_token, cache=caches) # 디코드 스텝 2
generate 함수는 cache 관리를 자동으로 처리합니다. 사용자 정의 생성 루프를 작성하는 경우에만 cache를 직접 다루면 됩니다.
Info
24개 레이어 중 8개만 KV cache를 사용하므로, Bit-Axon의 추론 중 메모리 사용량은 작게 유지됩니다. 이는 16GB Apple Silicon 기기에서 모델을 실행하기 위한 의도적인 설계 선택입니다.