my-llm-api / inference /engine.py
Khushhal1's picture
Upload 20 files
ad4ff0f verified
Raw
History Blame Contribute Delete
5.48 kB
import torch
import torch.nn.functional as F
from model.transformer import Transformer
from tokenizer.tokenizer import BPETokenizer
class InferenceEngine:
def __init__(self, model: Transformer, tokenizer: BPETokenizer, device: str = "cpu"):
self.model = model
self.tokenizer = tokenizer
self.device = device
self.model.to(self.device)
self.model.eval()
def _get_kv_caches(self, batch_size: int, max_seq_len: int):
params = self.model.params
head_dim = params.dim // params.n_heads
n_kv_heads = params.n_kv_heads if params.n_kv_heads else params.n_heads
kv_caches = []
for _ in range(params.n_layers):
k_cache = torch.zeros(batch_size, max_seq_len, n_kv_heads, head_dim).to(self.device)
v_cache = torch.zeros(batch_size, max_seq_len, n_kv_heads, head_dim).to(self.device)
kv_caches.append((k_cache, v_cache))
return kv_caches
@torch.no_grad()
def generate(
self,
prompt: str,
max_new_tokens: int = 50,
temperature: float = 0.7,
top_p: float = 0.9,
top_k: int = 50,
stream: bool = False
):
if not stream:
return self._generate_no_stream(prompt, max_new_tokens, temperature, top_p, top_k)
return self._generate_stream(prompt, max_new_tokens, temperature, top_p, top_k)
def _generate_no_stream(self, prompt, max_new_tokens, temperature, top_p, top_k):
tokens = self.tokenizer.encode(prompt, bos=True, eos=False)
x = torch.tensor(tokens).unsqueeze(0).to(self.device)
bsz = x.shape[0]
kv_caches = self._get_kv_caches(bsz, self.model.params.max_seq_len)
# Initial forward to fill cache
logits = self.model(x, start_pos=0, kv_caches=kv_caches)
generated = tokens
start_pos = x.shape[1]
for _ in range(max_new_tokens):
logits = self.model(x[:, -1:], start_pos=start_pos, kv_caches=kv_caches)
logits = logits[:, -1, :] / max(temperature, 1e-5)
# Top-k filtering
if top_k > 0:
v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
logits[logits < v[:, [-1]]] = float('-inf')
# Top-p (nucleus) filtering
if top_p < 1.0:
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
sorted_indices_to_remove = cumulative_probs > top_p
sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
sorted_indices_to_remove[..., 0] = False
indices_to_remove = sorted_indices[sorted_indices_to_remove]
logits[:, indices_to_remove] = float('-inf')
probs = F.softmax(logits, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
token_id = next_token.item()
generated.append(token_id)
x = next_token
start_pos += 1
if token_id == self.tokenizer.special_tokens["<eos>"]:
break
return self.tokenizer.decode(generated)
def _generate_stream(self, prompt, max_new_tokens, temperature, top_p, top_k):
tokens = self.tokenizer.encode(prompt, bos=True, eos=False)
x = torch.tensor(tokens).unsqueeze(0).to(self.device)
bsz = x.shape[0]
kv_caches = self._get_kv_caches(bsz, self.model.params.max_seq_len)
self.model(x, start_pos=0, kv_caches=kv_caches)
start_pos = x.shape[1]
for _ in range(max_new_tokens):
logits = self.model(x[:, -1:], start_pos=start_pos, kv_caches=kv_caches)
logits = logits[:, -1, :] / max(temperature, 1e-5)
if top_k > 0:
v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
logits[logits < v[:, [-1]]] = float('-inf')
if top_p < 1.0:
sorted_logits, sorted_indices = torch.sort(logits, descending=True)
cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
sorted_indices_to_remove = cumulative_probs > top_p
sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
sorted_indices_to_remove[..., 0] = False
indices_to_remove = sorted_indices[sorted_indices_to_remove]
logits[:, indices_to_remove] = float('-inf')
probs = F.softmax(logits, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
token_id = next_token.item()
yield self.tokenizer.decode([token_id])
x = next_token
start_pos += 1
if token_id == self.tokenizer.special_tokens["<eos>"]:
break
if __name__ == "__main__":
# Test KV-cache inference
from model.transformer import ModelArgs
params = ModelArgs(dim=256, n_layers=2, n_heads=4, vocab_size=100284)
model = Transformer(params)
tokenizer = BPETokenizer()
engine = InferenceEngine(model, tokenizer)
print("Testing KV-cache generation...")
res = engine.generate("Once upon a time", max_new_tokens=10, stream=False)
print(f"Result: {res}")