Kaique Mitsuo Silva Yamamoto
Ia

Implementação Mínima — Modelo Próprio com Phi-2 e TinyLlama

Implementaçã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?

ModeloParâmetrosVRAM mínimaMelhor para
Phi-2 (Microsoft)2.7B6GBCódigo, QA, raciocínio lógico
TinyLlama1.1B4GBTexto geral, chat simples
Llama 3.2 1B (Meta)1B4GBChat, instruções simples
Qwen2.5-1.5B (Alibaba)1.5B4GBMultilí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-tuning

2.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/M2

3. 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

ComponenteArquivoO que demonstra
Inferência básica01_phi2_inference.pyCarregar modelo, gerar texto
Fine-tuning LoRA03_finetune_lora.pyTreinar modelo próprio
RAG local05_rag_local.pyPipeline completo sem API externa
API REST06_api_server.pyServir modelo via HTTP
DockerDockerfileDeploy 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".

On this page