Implementação Mínima — Modelo Próprio com Phi-2 e TinyLlama
Baixar PDFImplementação passo a passo de um modelo de linguagem pré-treinado (Phi-2, TinyLlama) com inferência, fine-tuning com LoRA, RAG e API REST. Código completo e executável para demonstração em entrevista.
Implementação Mínima — Modelo Próprio com Phi-2 e TinyLlama
Meu caro buscador (dessa vez, buscador de modelo), você quer demonstrar na entrevista que consegue colocar um modelo em funcionamento. Não um modelo da OpenAI — um modelo seu, rodando na sua máquina.
Vou te dar o código completo. Do download do modelo até uma API REST servindo respostas. Tudo rodando local, sem custo, pronto para demonstrar.
1. Qual Modelo Usar?
| Modelo | Parâmetros | VRAM mínima | Melhor para |
|---|---|---|---|
| Phi-2 (Microsoft) | 2.7B | 6GB | Código, QA, raciocínio lógico |
| TinyLlama | 1.1B | 4GB | Texto geral, chat simples |
| Llama 3.2 1B (Meta) | 1B | 4GB | Chat, instruções simples |
| Qwen2.5-1.5B (Alibaba) | 1.5B | 4GB | Multilíngue, código |
Recomendação: Use Phi-2 — é o melhor custo-benefício para demonstração. 2.7B de parâmetros, roda em laptop comum, e tem qualidade surpreendente para o tamanho.
2. Setup do Ambiente
2.1 Instalação
# Criar ambiente virtual
python -m venv ai-demo
source ai-demo/bin/activate # Linux/Mac
# ai-demo\Scripts\activate # Windows
# Instalar dependências
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install transformers accelerate safetensors
pip install fastapi uvicorn
pip install langchain langchain-community chromadb
pip install sentence-transformers
pip install peft bitsandbytes # para LoRA/fine-tuning2.2 Verificar instalação
import torch
print(f"PyTorch: {torch.__version__}")
print(f"CUDA disponível: {torch.cuda.is_available()}")
print(f"MPS disponível: {torch.backends.mps.is_available()}") # Mac M1/M23. Passo 1 — Inferência Básica (Phi-2)
3.1 Carregar e gerar texto
"""
01_phi2_inference.py
Inferência básica com Phi-2 — o "Hello World" do modelo próprio.
"""
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
# Detectar dispositivo
if torch.cuda.is_available():
device = "cuda"
elif torch.backends.mps.is_available():
device = "mps"
else:
device = "cpu"
print(f"Usando dispositivo: {device}")
# Carregar modelo (2.7B params, ~5GB em disco)
model_name = "microsoft/phi-2"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16 if device != "cpu" else torch.float32,
trust_remote_code=True,
low_cpu_mem_usage=True,
).to(device)
# Função de geração
def generate(prompt: str, max_new_tokens: int = 200, temperature: float = 0.7) -> str:
inputs = tokenizer(prompt, return_tensors="pt").to(device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=temperature,
do_sample=True,
top_p=0.9,
pad_token_id=tokenizer.eos_token_id,
)
return tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
# Testar
if __name__ == "__main__":
# Pergunta e resposta
prompt = "Instruct: Explique o que é um Transformer em 3 parágrafos.\nOutput:"
print("=== Phi-2 ===")
print(generate(prompt))
# Código
prompt_code = "def fibonacci(n):"
print("\n=== Código ===")
print(generate(prompt_code, max_new_tokens=100))4. Passo 2 — Fine-tuning com LoRA
4.1 Preparar dataset
"""
02_prepare_dataset.py
Cria um dataset simples de exemplo para fine-tuning.
"""
import json
# Dataset de exemplo: perguntas e respostas sobre um domínio específico
dataset = [
{
"instruction": "O que é RAG?",
"response": "RAG (Retrieval-Augmented Generation) é uma técnica que combina busca de informações com geração de texto. O modelo primeiro recupera documentos relevantes de uma base de conhecimento e depois usa esses documentos como contexto para gerar a resposta, reduzindo alucinações."
},
{
"instruction": "Como funciona LoRA?",
"response": "LoRA (Low-Rank Adaptation) é uma técnica de fine-tuning que congela os pesos originais do modelo e treina apenas matrizes de baixa rank adicionadas às camadas de atenção. Isso reduz drasticamente o número de parâmetros treináveis (de bilhões para milhões) e o custo computacional."
},
{
"instruction": "O que é prompt engineering?",
"response": "Prompt engineering é a arte de criar instruções eficazes para modelos de linguagem. Inclui técnicas como few-shot learning (fornecer exemplos), chain-of-thought (pedir raciocínio passo a passo) e system prompts (definir persona e regras do modelo)."
},
# Adicione mais exemplos...
]
# Salvar no formato correto
formatted = []
for item in dataset:
formatted.append({
"text": f"Instruct: {item['instruction']}\nOutput: {item['response']}"
})
with open("dataset.jsonl", "w") as f:
for item in formatted:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f"Dataset criado com {len(formatted)} exemplos")4.2 Fine-tuning com LoRA
"""
03_finetune_lora.py
Fine-tuning do Phi-2 com LoRA — treina em minutos, não em dias.
"""
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer
import torch
# Configuração
MODEL_NAME = "microsoft/phi-2"
DATASET_PATH = "dataset.jsonl"
OUTPUT_DIR = "./phi-2-finetuned"
# Carregar modelo e tokenizer
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
torch_dtype=torch.float16,
trust_remote_code=True,
low_cpu_mem_usage=True,
)
# Configurar LoRA
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # rank — quanto maior, mais capacidade (8-64)
lora_alpha=32, # scaling factor
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "dense"], # camadas de atenção
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Saída: trainable params: 4,194,304 || all params: 2,780,000,000 || trainable%: 0.15%
# Carregar dataset
dataset = load_dataset("json", data_files=DATASET_PATH, split="train")
# Configurar treinamento
training_args = TrainingArguments(
output_dir=OUTPUT_DIR,
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=2,
learning_rate=2e-4,
warmup_steps=100,
logging_steps=10,
save_strategy="epoch",
fp16=True,
optim="adamw_torch",
)
# Trainer
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=512,
tokenizer=tokenizer,
)
# Treinar
print("Iniciando fine-tuning...")
trainer.train()
# Salvar adapters
model.save_pretrained(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
print(f"Modelo fine-tunado salvo em {OUTPUT_DIR}")4.3 Carregar modelo fine-tunado
"""
04_load_finetuned.py
Carrega o modelo com adapters LoRA aplicados.
"""
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
import torch
device = "cuda" if torch.cuda.is_available() else "cpu"
# Carregar modelo base
base_model = AutoModelForCausalLM.from_pretrained(
"microsoft/phi-2",
torch_dtype=torch.float16,
trust_remote_code=True,
low_cpu_mem_usage=True,
)
# Aplicar adapters LoRA
model = PeftModel.from_pretrained(base_model, "./phi-2-finetuned")
model = model.merge_and_unload() # merge adapters no modelo base
tokenizer = AutoTokenizer.from_pretrained("microsoft/phi-2", trust_remote_code=True)
# Testar
prompt = "Instruct: O que é RAG?\nOutput:"
inputs = tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=150, temperature=0.7)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))5. Passo 3 — RAG com o Modelo Local
5.1 Pipeline completo
"""
05_rag_local.py
RAG usando Phi-2 local + ChromaDB + Sentence Transformers.
Sem dependência de API externa — tudo rodando na sua máquina.
"""
from transformers import AutoModelForCausalLM, AutoTokenizer
from sentence_transformers import SentenceTransformer
import chromadb
import torch
# --- MODELO DE GERAÇÃO (Phi-2) ---
gen_model = AutoModelForCausalLM.from_pretrained(
"microsoft/phi-2",
torch_dtype=torch.float16,
trust_remote_code=True,
low_cpu_mem_usage=True,
)
gen_tokenizer = AutoTokenizer.from_pretrained("microsoft/phi-2", trust_remote_code=True)
# --- MODELO DE EMBEDDING ---
embed_model = SentenceTransformer("all-MiniLM-L6-v2") # 80MB, rápido
# --- VECTOR DB ---
chroma = chromadb.Client()
collection = chroma.create_collection("knowledge")
# --- INDEXAR DOCUMENTOS ---
documents = [
"RAG combina busca de informações com geração de texto para reduzir alucinações.",
"LoRA permite fine-tuning eficiente treinando apenas matrizes de baixa rank.",
"Transformers usam atenção self-attention para processar sequências inteiras.",
"Embeddings são representações vetoriares que capturam significado semântico.",
"PyTorch é o framework dominante para deep learning em pesquisa e produção.",
]
embeddings = embed_model.encode(documents).tolist()
collection.add(
documents=documents,
embeddings=embeddings,
ids=[f"doc_{i}" for i in range(len(documents))],
)
# --- FUNÇÃO DE BUSCA ---
def retrieve(query: str, top_k: int = 3) -> list[str]:
query_embedding = embed_model.encode([query]).tolist()
results = collection.query(query_embeddings=query_embedding, n_results=top_k)
return results["documents"][0]
# --- FUNÇÃO DE GERAÇÃO COM CONTEXTO ---
def rag_query(question: str) -> str:
# 1. Recuperar documentos relevantes
docs = retrieve(question, top_k=3)
context = "\n".join(f"- {doc}" for doc in docs)
# 2. Construir prompt com contexto
prompt = f"""Contexto:
{context}
Com base no contexto acima, responda à pergunta de forma precisa e concisa.
Pergunta: {question}
Resposta:"""
# 3. Gerar resposta
inputs = gen_tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = gen_model.generate(
**inputs,
max_new_tokens=200,
temperature=0.3,
do_sample=True,
pad_token_id=gen_tokenizer.eos_token_id,
)
return gen_tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True
)
# --- TESTAR ---
if __name__ == "__main__":
perguntas = [
"O que é RAG e como funciona?",
"Como LoRA reduz o custo de fine-tuning?",
"Qual framework é usado para deep learning?",
]
for pergunta in perguntas:
print(f"\n{'='*60}")
print(f"PERGUNTA: {pergunta}")
print(f"RESPOSTA: {rag_query(pergunta)}")6. Passo 4 — API REST com FastAPI
6.1 Servir o modelo via API
"""
06_api_server.py
API REST servindo o modelo Phi-2 + RAG.
Pronto para demonstração em entrevista.
"""
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from transformers import AutoModelForCausalLM, AutoTokenizer
from sentence_transformers import SentenceTransformer
import chromadb
import torch
import time
app = FastAPI(title="Phi-2 RAG API", version="1.0.0")
# --- MODELOS ---
print("Carregando modelos...")
device = "cuda" if torch.cuda.is_available() else "cpu"
gen_model = AutoModelForCausalLM.from_pretrained(
"microsoft/phi-2",
torch_dtype=torch.float16 if device != "cpu" else torch.float32,
trust_remote_code=True,
low_cpu_mem_usage=True,
).to(device)
gen_tokenizer = AutoTokenizer.from_pretrained("microsoft/phi-2", trust_remote_code=True)
embed_model = SentenceTransformer("all-MiniLM-L6-v2")
# --- VECTOR DB ---
chroma = chromadb.Client()
collection = chroma.create_collection("knowledge")
# --- SCHEMAS ---
class IndexRequest(BaseModel):
documents: list[str]
class ChatRequest(BaseModel):
question: str
top_k: int = 3
max_tokens: int = 200
temperature: float = 0.3
class ChatResponse(BaseModel):
answer: str
sources: list[str]
latency_ms: float
class HealthResponse(BaseModel):
status: str
model: str
device: str
docs_indexed: int
# --- ENDPOINTS ---
@app.get("/health", response_model=HealthResponse)
async def health():
return HealthResponse(
status="ok",
model="microsoft/phi-2",
device=device,
docs_indexed=collection.count(),
)
@app.post("/index")
async def index_documents(req: IndexRequest):
embeddings = embed_model.encode(req.documents).tolist()
start_id = collection.count()
collection.add(
documents=req.documents,
embeddings=embeddings,
ids=[f"doc_{start_id + i}" for i in range(len(req.documents))],
)
return {"indexed": len(req.documents), "total": collection.count()}
@app.post("/chat", response_model=ChatResponse)
async def chat(req: ChatRequest):
start = time.time()
try:
# 1. Retrieve
if collection.count() == 0:
docs = []
context = "Nenhum documento indexado."
else:
query_emb = embed_model.encode([req.question]).tolist()
results = collection.query(query_embeddings=query_emb, n_results=req.top_k)
docs = results["documents"][0]
context = "\n".join(f"- {doc}" for doc in docs)
# 2. Generate
prompt = f"""Contexto:
{context}
Com base no contexto acima, responda à pergunta.
Pergunta: {req.question}
Resposta:"""
inputs = gen_tokenizer(prompt, return_tensors="pt").to(device)
with torch.no_grad():
outputs = gen_model.generate(
**inputs,
max_new_tokens=req.max_tokens,
temperature=req.temperature,
do_sample=True,
pad_token_id=gen_tokenizer.eos_token_id,
)
answer = gen_tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True,
)
latency = (time.time() - start) * 1000
return ChatResponse(answer=answer, sources=docs, latency_ms=latency)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)6.2 Testar a API
# Iniciar servidor
python 06_api_server.py
# Health check
curl http://localhost:8000/health
# Indexar documentos
curl -X POST http://localhost:8000/index \
-H "Content-Type: application/json" \
-d '{"documents": [
"RAG combina busca com geração para reduzir alucinações.",
"LoRA treina apenas matrizes de baixa rank.",
"Transformers usam self-attention."
]}'
# Perguntar
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"question": "Como RAG reduz alucinações?"}'7. Passo 5 — Dockerizar
7.1 Dockerfile
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir \
torch \
transformers \
accelerate \
sentence-transformers \
chromadb \
fastapi \
uvicorn \
peft
COPY . .
# Pré-baixar modelos no build
RUN python -c "from transformers import AutoTokenizer; AutoTokenizer.from_pretrained('microsoft/phi-2')"
RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"
EXPOSE 8000
CMD ["uvicorn", "06_api_server:app", "--host", "0.0.0.0", "--port", "8000"]7.2 docker-compose.yml
version: "3.8"
services:
phi2-api:
build: .
ports:
- "8000:8000"
volumes:
- model_cache:/root/.cache
environment:
- PYTHONUNBUFFERED=1
volumes:
model_cache:8. Resumo — O que Você Tem Agora
| Componente | Arquivo | O que demonstra |
|---|---|---|
| Inferência básica | 01_phi2_inference.py | Carregar modelo, gerar texto |
| Fine-tuning LoRA | 03_finetune_lora.py | Treinar modelo próprio |
| RAG local | 05_rag_local.py | Pipeline completo sem API externa |
| API REST | 06_api_server.py | Servir modelo via HTTP |
| Docker | Dockerfile | Deploy containerizado |
Na entrevista, diga: "Eu tenho uma implementação de um modelo Phi-2 de 2.7B rodando localmente com RAG, fine-tuning com LoRA e API REST em Docker. Quer que eu mostre?"
Isso mostra que você não apenas usa APIs — você constrói sistemas de IA.
Dica Final
O código acima funciona em qualquer laptop com 8GB de RAM. O Phi-2 é leve o suficiente para rodar em CPU (mais lento, mas funciona). Se tiver GPU, fica muito mais rápido.
Não precisa implementar tudo antes da entrevista. Escolha um — o RAG local ou a API — e deixe pronto para demonstrar. Um projeto que funciona vale mais que dez que estão "quase prontos".
Referências Teóricas — YouTube, Papers e Artigos para Entrevista
Compilação de vídeos, papers e artigos essenciais para embasar teoricamente em entrevistas e testes práticos de engenharia de IA. Canais, cursos e leituras recomendadas.
Simulação de Entrevista — Engenheiro de IA (Paper Completo)
Diálogo completo entre recrutador e desenvolvedor em entrevista técnica de engenharia de IA. Inclui testes práticos de live coding, criação de modelo proprietário PT-BR, calculadora CLI em Python e TypeScript, e avaliação de requisitos técnicos.