dezembro 21, 2025
15 min de leitura

Perguntas de entrevista para engenheiro júnior de Machine Learning

interview
career-advice
job-search
entry-level
Perguntas de entrevista para engenheiro júnior de Machine Learning
Milad Bonakdar

Milad Bonakdar

Autor

Prepare-se para entrevistas júnior de ML com perguntas sobre Python, avaliação de modelos, vazamento de dados, implantação, monitoramento e MLOps.


Perguntas de entrevista para engenheiro júnior de Machine Learning

Em uma entrevista para engenheiro júnior de Machine Learning, você deve saber explicar como escreve Python confiável, treina e avalia modelos, evita vazamento de dados, prepara modelos para implantação e monitora previsões depois do release. Uma boa resposta mostra o algoritmo, as premissas dos dados, a escolha das métricas e os trade-offs de produção.

Use este guia para praticar perguntas comuns em vagas iniciais de engenharia de ML: programação em Python, algoritmos clássicos, validação, dados desbalanceados, model serving, Docker, monitoramento e fundamentos de CI/CD.

Python e Programação (5 Questões)

1. Como você lida com grandes conjuntos de dados que não cabem na memória?

Resposta: Várias técnicas lidam com dados maiores que a RAM disponível:

  • Processamento em Lote: Processa dados em partes
  • Geradores: Retornam dados sob demanda
  • Dask/Ray: Frameworks de computação distribuída
  • Consultas de Banco de Dados: Carrega apenas os dados necessários
  • Arquivos Mapeados na Memória: Acessa o disco como se estivesse na memória
  • Streaming de Dados: Processa dados à medida que chegam
import pandas as pd
import numpy as np

# Ruim: Carrega o conjunto de dados inteiro na memória
# df = pd.read_csv('large_file.csv')  # Pode travar

# Bom: Lê em partes
chunk_size = 10000
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
    # Processa cada parte
    processed = chunk[chunk['value'] > 0]
    # Salva ou agrega resultados
    processed.to_csv('output.csv', mode='a', header=False)

# Usando geradores
def data_generator(filename, batch_size=32):
    while True:
        batch = []
        with open(filename, 'r') as f:
            for line in f:
                batch.append(process_line(line))
                if len(batch) == batch_size:
                    yield np.array(batch)
                    batch = []

# Dask para computação distribuída
import dask.dataframe as dd
ddf = dd.read_csv('large_file.csv')
result = ddf.groupby('category').mean().compute()

Raridade: Muito Comum Dificuldade: Média


2. Explique decoradores em Python e dê um caso de uso de ML.

Resposta: Decoradores modificam ou aprimoram funções sem alterar seu código.

  • Casos de Uso em ML:
    • Medição do tempo de execução da função
    • Registro de previsões (logging)
    • Armazenamento em cache de resultados
    • Validação de entrada
import time
import functools

# Decorador de tempo
def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} levou {end - start:.2f} segundos")
        return result
    return wrapper

# Decorador de registro (logging)
def log_predictions(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        predictions = func(*args, **kwargs)
        print(f"Fiz {len(predictions)} previsões")
        print(f"Distribuição das previsões: {np.bincount(predictions)}")
        return predictions
    return wrapper

# Uso
@timer
@log_predictions
def predict_batch(model, X):
    return model.predict(X)

# Decorador de cache (memoização)
def cache_results(func):
    cache = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@cache_results
def expensive_feature_engineering(data_id):
    # Computação custosa
    return processed_features

Raridade: Comum Dificuldade: Média


3. Qual é a diferença entre @staticmethod e @classmethod?

Resposta: Ambos definem métodos que não exigem uma instância.

  • @staticmethod: Sem acesso à classe ou instância
  • @classmethod: Recebe a classe como primeiro argumento
class MLModel:
    model_type = "classifier"
    
    def __init__(self, name):
        self.name = name
    
    # Método regular - precisa de instância
    def predict(self, X):
        return self.model.predict(X)
    
    # Método estático - função utilitária
    @staticmethod
    def preprocess_data(X):
        # Sem acesso a self ou cls
        return (X - X.mean()) / X.std()
    
    # Método de classe - padrão de fábrica (factory pattern)
    @classmethod
    def create_default(cls):
        # Tem acesso a cls
        return cls(name=f"default_{cls.model_type}")
    
    @classmethod
    def from_config(cls, config):
        return cls(name=config['name'])

# Uso
# Método estático - nenhuma instância necessária
processed = MLModel.preprocess_data(X_train)

# Método de classe - cria instância
model = MLModel.create_default()
model2 = MLModel.from_config({'name': 'my_model'})

Raridade: Média Dificuldade: Média


4. Como você lida com exceções em pipelines de ML?

Resposta: O tratamento adequado de erros evita falhas no pipeline e auxilia na depuração.

import logging
from typing import Optional

# Configuração de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ModelTrainingError(Exception):
    """Exceção personalizada para falhas no treinamento do modelo"""
    pass

def train_model(X, y, model_type='random_forest'):
    try:
        logger.info(f"Iniciando o treinamento com {model_type}")
        
        # Valida as entradas
        if X.shape[0] != y.shape[0]:
            raise ValueError("X e y devem ter o mesmo número de amostras")
        
        if X.shape[0] < 100:
            raise ModelTrainingError("Dados de treinamento insuficientes")
        
        # Treina o modelo
        if model_type == 'random_forest':
            from sklearn.ensemble import RandomForestClassifier
            model = RandomForestClassifier()
        else:
            raise ValueError(f"Tipo de modelo desconhecido: {model_type}")
        
        model.fit(X, y)
        logger.info("Treinamento concluído com sucesso")
        return model
        
    except ValueError as e:
        logger.error(f"Erro de validação: {e}")
        raise
    except ModelTrainingError as e:
        logger.error(f"Erro de treinamento: {e}")
        # Poderia recorrer a um modelo mais simples
        return train_fallback_model(X, y)
    except Exception as e:
        logger.error(f"Erro inesperado: {e}")
        raise
    finally:
        logger.info("Tentativa de treinamento finalizada")

# Gerenciador de contexto para gerenciamento de recursos
class ModelLoader:
    def __init__(self, model_path):
        self.model_path = model_path
        self.model = None
    
    def __enter__(self):
        logger.info(f"Carregando modelo de {self.model_path}")
        self.model = load_model(self.model_path)
        return self.model
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        logger.info("Limpando recursos")
        if self.model:
            del self.model
        return False  # Não suprime exceções

# Uso
with ModelLoader('model.pkl') as model:
    predictions = model.predict(X_test)

Raridade: Comum Dificuldade: Média


5. O que são geradores em Python e por que eles são úteis em ML?

Resposta: Geradores retornam valores um de cada vez, economizando memória.

  • Benefícios:
    • Eficiência de memória
    • Avaliação preguiçosa (lazy evaluation)
    • Sequências infinitas
  • Casos de Uso de ML:
    • Carregamento de dados
    • Processamento em lote
    • Aumento de dados (data augmentation)
import numpy as np

# Gerador para processamento em lote
def batch_generator(X, y, batch_size=32):
    n_samples = len(X)
    indices = np.arange(n_samples)
    np.random.shuffle(indices)
    
    for start_idx in range(0, n_samples, batch_size):
        end_idx = min(start_idx + batch_size, n_samples)
        batch_indices = indices[start_idx:end_idx]
        yield X[batch_indices], y[batch_indices]

# Uso no treinamento
for epoch in range(10):
    for X_batch, y_batch in batch_generator(X_train, y_train):
        model.train_on_batch(X_batch, y_batch)

# Gerador de aumento de dados
def augment_images(images, labels):
    for img, label in zip(images, labels):
        # Original
        yield img, label
        # Invertida horizontalmente
        yield np.fliplr(img), label
        # Rotacionada
        yield np.rot90(img), label

# Gerador infinito para treinamento
def infinite_batch_generator(X, y, batch_size=32):
    while True:
        indices = np.random.choice(len(X), batch_size)
        yield X[indices], y[indices]

# Use com steps_per_epoch
gen = infinite_batch_generator(X_train, y_train)
# model.fit(gen, steps_per_epoch=100, epochs=10)

Raridade: Comum Dificuldade: Média


Algoritmos de ML e Teoria (5 Questões)

6. Explique a diferença entre bagging e boosting.

Resposta: Ambos são métodos de ensemble, mas funcionam de forma diferente:

  • Bagging (Bootstrap Aggregating):
    • Treinamento paralelo em subconjuntos aleatórios
    • Reduz a variância
    • Exemplo: Random Forest
  • Boosting:
    • Treinamento sequencial, cada modelo corrige erros anteriores
    • Reduz o viés
    • Exemplos: AdaBoost, Gradient Boosting, XGBoost
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score

# Gera dados
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)

# Bagging - Random Forest
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf_scores = cross_val_score(rf, X, y, cv=5)
print(f"Random Forest CV: {rf_scores.mean():.3f} (+/- {rf_scores.std():.3f})")

# Boosting - Gradient Boosting
gb = GradientBoostingClassifier(n_estimators=100, random_state=42)
gb_scores = cross_val_score(gb, X, y, cv=5)
print(f"Gradient Boosting CV: {gb_scores.mean():.3f} (+/- {gb_scores.std():.3f})")

# XGBoost (boosting avançado)
import xgboost as xgb
xgb_model = xgb.XGBClassifier(n_estimators=100, random_state=42)
xgb_scores = cross_val_score(xgb_model, X, y, cv=5)
print(f"XGBoost CV: {xgb_scores.mean():.3f} (+/- {xgb_scores.std():.3f})")

Raridade: Muito Comum Dificuldade: Média


7. Como você lida com conjuntos de dados desbalanceados?

Resposta: Dados desbalanceados podem enviesar modelos em direção à classe majoritária.

  • Técnicas:
    • Reamostragem: SMOTE, subamostragem
    • Pesos de classe: Penaliza a classificação incorreta
    • Métodos de ensemble: Balanced Random Forest
    • Avaliação: Use F1, precisão, recall (não acurácia)
    • Ajuste de limite: Otimize o limite de decisão
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from collections import Counter

# Cria um conjunto de dados desbalanceado
X, y = make_classification(
    n_samples=1000, n_features=20,
    weights=[0.95, 0.05],  # 95% classe 0, 5% classe 1
    random_state=42
)

print(f"Distribuição original: {Counter(y)}")

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 1. Pesos de classe
model_weighted = RandomForestClassifier(class_weight='balanced', random_state=42)
model_weighted.fit(X_train, y_train)
print("\nCom pesos de classe:")
print(classification_report(y_test, model_weighted.predict(X_test)))

# 2. SMOTE (sobreamostragem da classe minoritária)
smote = SMOTE(random_state=42)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
print(f"Após SMOTE: {Counter(y_train_smote)}")

model_smote = RandomForestClassifier(random_state=42)
model_smote.fit(X_train_smote, y_train_smote)
print("\nCom SMOTE:")
print(classification_report(y_test, model_smote.predict(X_test)))

# 3. Ajuste de limite
y_proba = model_weighted.predict_proba(X_test)[:, 1]
threshold = 0.3  # Limite inferior favorece a classe minoritária
y_pred_adjusted = (y_proba >= threshold).astype(int)
print("\nCom limite ajustado:")
print(classification_report(y_test, y_pred_adjusted))

Raridade: Muito Comum Dificuldade: Média


8. O que é validação cruzada e por que é importante?

Resposta: A validação cruzada avalia o desempenho do modelo de forma mais confiável do que uma única divisão treino-teste.

  • Tipos:
    • K-Fold: Divida em k folds
    • Stratified K-Fold: Preserva a distribuição da classe
    • Time Series Split: Respeita a ordem temporal
  • Benefícios:
    • Estimativa de desempenho mais robusta
    • Usa todos os dados para treinamento e validação
    • Detecta overfitting
from sklearn.model_selection import (
    cross_val_score, KFold, StratifiedKFold,
    TimeSeriesSplit, cross_validate
)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# Carrega os dados
data = load_iris()
X, y = data.data, data.target

model = RandomForestClassifier(random_state=42)

# K-Fold padrão
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kfold)
print(f"K-Fold scores: {scores}")
print(f"Média: {scores.mean():.3f} (+/- {scores.std():.3f})")

# Stratified K-Fold (preserva a distribuição da classe)
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
stratified_scores = cross_val_score(model, X, y, cv=stratified_kfold)
print(f"\nStratified K-Fold: {stratified_scores.mean():.3f}")

# Time Series Split (para dados temporais)
tscv = TimeSeriesSplit(n_splits=5)
ts_scores = cross_val_score(model, X, y, cv=tscv)
print(f"Time Series CV: {ts_scores.mean():.3f}")

# Múltiplas métricas
cv_results = cross_validate(
    model, X, y, cv=5,
    scoring=['accuracy', 'precision_macro', 'recall_macro', 'f1_macro'],
    return_train_score=True
)

print(f"\nAcurácia: {cv_results['test_accuracy'].mean():.3f}")
print(f"Precisão: {cv_results['test_precision_macro'].mean():.3f}")
print(f"Recall: {cv_results['test_recall_macro'].mean():.3f}")
print(f"F1: {cv_results['test_f1_macro'].mean():.3f}")

Raridade: Muito Comum Dificuldade: Fácil


9. Explique precisão, recall e F1-score.

Resposta: Métricas de classificação para avaliar o desempenho do modelo:

  • Precisão: Das previsões positivas, quantas estão corretas
    • Fórmula: TP / (TP + FP)
    • Use quando: Falsos positivos são custosos
  • Recall: Dos positivos reais, quantos foram encontrados
    • Fórmula: TP / (TP + FN)
    • Use quando: Falsos negativos são custosos
  • F1-Score: Média harmônica de precisão e recall
    • Fórmula: 2 × (Precisão × Recall) / (Precisão + Recall)
    • Use quando: Precisa de equilíbrio entre precisão e recall
from sklearn.metrics import (
    precision_score, recall_score, f1_score,
    classification_report, confusion_matrix
)
import numpy as np

# Exemplo de previsões
y_true = np.array([0, 1, 1, 0, 1, 1, 0, 1, 0, 0])
y_pred = np.array([0, 1, 0, 0, 1, 1, 0, 1, 1, 0])

# Calcula as métricas
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)

print(f"Precisão: {precision:.3f}")  # 0.800
print(f"Recall: {recall:.3f}")        # 0.800
print(f"F1-Score: {f1:.3f}")          # 0.800

# Matriz de confusão
cm = confusion_matrix(y_true, y_pred)
print(f"\nMatriz de Confusão:\n{cm}")
# [[4 1]
#  [1 4]]

# Relatório de classificação (todas as métricas)
print("\nRelatório de Classificação:")
print(classification_report(y_true, y_pred))

# Exemplo de trade-off
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

# Obtém previsões de probabilidade
y_proba = model.predict_proba(X_test)[:, 1]

# Calcula precisão-recall em diferentes limites
precisions, recalls, thresholds = precision_recall_curve(y_test, y_proba)

# Encontra o limite ideal (maximiza F1)
f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-10)
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx]
print(f"Limite ideal: {optimal_threshold:.3f}")

Raridade: Muito Comum Dificuldade: Fácil


10. O que é regularização e quando você a usaria?

Resposta: A regularização evita o overfitting, penalizando a complexidade do modelo.

  • Tipos:
    • L1 (Lasso): Adiciona o valor absoluto dos coeficientes
    • L2 (Ridge): Adiciona os coeficientes ao quadrado
    • Elastic Net: Combina L1 e L2
  • Quando usar:
    • Alta variância (overfitting)
    • Muitos recursos
    • Multicolinearidade
from sklearn.linear_model import Ridge, Lasso, ElasticNet, LinearRegression
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# Gera dados com muitos recursos
X, y = make_regression(
    n_samples=100, n_features=50,
    n_informative=10, noise=10, random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# Sem regularização
lr = LinearRegression()
lr.fit(X_train, y_train)
lr_score = r2_score(y_test, lr.predict(X_test))
print(f"Linear Regression R²: {lr_score:.3f}")

# Regularização L2 (Ridge)
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
ridge_score = r2_score(y_test, ridge.predict(X_test))
print(f"Ridge R²: {ridge_score:.3f}")

# Regularização L1 (Lasso)
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
lasso_score = r2_score(y_test, lasso.predict(X_test))
print(f"Lasso R²: {lasso_score:.3f}")
print(f"Coeficientes não-zero de Lasso: {np.sum(lasso.coef_ != 0)}")

# Elastic Net (L1 + L2)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic.fit(X_train, y_train)
elastic_score = r2_score(y_test, elastic.predict(X_test))
print(f"Elastic Net R²: {elastic_score:.3f}")

# Ajuste de hiperparâmetros para alpha
from sklearn.model_selection import GridSearchCV

param_grid = {'alpha': [0.001, 0.01, 0.1, 1.0, 10.0]}
grid = GridSearchCV(Ridge(), param_grid, cv=5)
grid.fit(X_train, y_train)
print(f"\nMelhor alpha: {grid.best_params_['alpha']}")
print(f"Melhor pontuação CV: {grid.best_score_:.3f}")

Raridade: Muito Comum Dificuldade: Média


Treinamento e Implementação de Modelos (5 Questões)

11. Como você salva e carrega modelos em produção?

Resposta: A serialização de modelos permite a implementação e reutilização.

import joblib
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# Treina o modelo
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier()
model.fit(X, y)

# Método 1: Joblib (recomendado para scikit-learn)
joblib.dump(model, 'model.joblib')
loaded_model = joblib.load('model.joblib')

# Método 2: Pickle
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

with open('model.pkl', 'rb') as f:
    loaded_model = pickle.load(f)

# Para TensorFlow/Keras
import tensorflow as tf

# Salva o modelo inteiro
# keras_model.save('model.h5')
# loaded_model = tf.keras.models.load_model('model.h5')

# Salva apenas os pesos
# keras_model.save_weights('model_weights.h5')
# new_model.load_weights('model_weights.h5')

# Para PyTorch
import torch

# Salva o dicionário de estado do modelo
# torch.save(model.state_dict(), 'model.pth')
# model.load_state_dict(torch.load('model.pth'))

# Salva o modelo inteiro
# torch.save(model, 'model_complete.pth')
# model = torch.load('model_complete.pth')

# Controle de versão para modelos
import datetime

model_version = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
model_path = f'models/model_{model_version}.joblib'
joblib.dump(model, model_path)
print(f"Modelo salvo em {model_path}")

Raridade: Muito Comum Dificuldade: Fácil


12. Como você cria uma API REST para servir modelos?

Resposta: APIs REST tornam os modelos acessíveis a aplicativos.

from flask import Flask, request, jsonify
import joblib
import numpy as np

app = Flask(__name__)

# Carrega o modelo na inicialização
model = joblib.load('model.joblib')

@app.route('/predict', methods=['POST'])
def predict():
    try:
        # Obtém dados da requisição
        data = request.get_json()
        features = np.array(data['features']).reshape(1, -1)
        
        # Faz a previsão
        prediction = model.predict(features)
        probability = model.predict_proba(features)
        
        # Retorna a resposta
        return jsonify({
            'prediction': int(prediction[0]),
            'probability': probability[0].tolist(),
            'status': 'success'
        })
    
    except Exception as e:
        return jsonify({
            'error': str(e),
            'status': 'error'
        }), 400

@app.route('/health', methods=['GET'])
def health():
    return jsonify({'status': 'healthy'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

# Alternativa FastAPI (moderna, mais rápida)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class PredictionRequest(BaseModel):
    features: list

class PredictionResponse(BaseModel):
    prediction: int
    probability: list
    status: str

@app.post("/predict", response_model=PredictionResponse)
async def predict(request: PredictionRequest):
    try:
        features = np.array(request.features).reshape(1, -1)
        prediction = model.predict(features)
        probability = model.predict_proba(features)
        
        return PredictionResponse(
            prediction=int(prediction[0]),
            probability=probability[0].tolist(),
            status="success"
        )
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

# Uso:
# curl -X POST "http://localhost:5000/predict" \
#      -H "Content-Type: application/json" \
#      -d '{"features": [5.1, 3.5, 1.4, 0.2]}'

Raridade: Muito Comum Dificuldade: Média


13. O que é Docker e por que é útil para a implementação de ML?

Resposta: Os contêineres Docker empacotam aplicativos com todas as dependências.

  • Benefícios:
    • Reprodutibilidade
    • Consistência entre ambientes
    • Implementação fácil
    • Isolamento
# Dockerfile para modelo de ML
FROM python:3.9-slim

WORKDIR /app

# Copia os requisitos
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copia o modelo e o código
COPY model.joblib .
COPY app.py .

# Expõe a porta
EXPOSE 5000

# Executa o aplicativo
CMD ["python", "app.py"]
# Constrói a imagem Docker
docker build -t ml-model:v1 .

# Executa o contêiner
docker run -p 5000:5000 ml-model:v1

# Docker Compose para configuração multi-contêiner
# docker-compose.yml
version: '3.8'
services:
  model:
    build: .
    ports:
      - "5000:5000"
    environment:
      - MODEL_PATH=/app/model.joblib
    volumes:
      - ./models:/app/models

Raridade: Comum Dificuldade: Média


14. Como você monitora o desempenho do modelo em produção?

Resposta: O monitoramento detecta a degradação do modelo e garante a confiabilidade.

  • O que Monitorar:
    • Métricas de previsão: Acurácia, latência
    • Data drift: Mudanças na distribuição de entrada
    • Model drift: Degradação do desempenho
    • Métricas do sistema: CPU, memória, erros
import logging
from datetime import datetime
import numpy as np

class ModelMonitor:
    def __init__(self, model):
        self.model = model
        self.predictions = []
        self.actuals = []
        self.latencies = []
        self.input_stats = []
        
        # Configuração de logging
        logging.basicConfig(
            filename='model_monitor.log',
            level=logging.INFO,
            format='%(asctime)s - %(message)s'
        )
    
    def predict(self, X):
        import time
        
        # Rastreia estatísticas de entrada
        self.input_stats.append({
            'mean': X.mean(),
            'std': X.std(),
            'min': X.min(),
            'max': X.max()
        })
        
        # Mede a latência
        start = time.time()
        prediction = self.model.predict(X)
        latency = time.time() - start
        
        self.predictions.append(prediction)
        self.latencies.append(latency)
        
        # Registra a previsão
        logging.info(f"Previsão: {prediction}, Latência: {latency:.3f}s")
        
        # Alerta se a latência for muito alta
        if latency > 1.0:
            logging.warning(f"Alta latência detectada: {latency:.3f}s")
        
        return prediction
    
    def log_actual(self, y_true):
        self.actuals.append(y_true)
        
        # Calcula a acurácia se tivermos dados suficientes
        if len(self.actuals) >= 100:
            accuracy = np.mean(
                np.array(self.predictions[-100:]) == np.array(self.actuals[-100:])
            )
            logging.info(f"Acurácia contínua (últimos 100): {accuracy:.3f}")
            
            if accuracy < 0.8:
                logging.error(f"Desempenho do modelo degradado: {accuracy:.3f}")
    
    def check_data_drift(self, reference_stats):
        if not self.input_stats:
            return
        
        current_stats = self.input_stats[-1]
        
        # Detecção simples de drift (compara as médias)
        mean_diff = abs(current_stats['mean'] - reference_stats['mean'])
        if mean_diff > 2 * reference_stats['std']:
            logging.warning(f"Data drift detectado: diferença da média = {mean_diff:.3f}")

# Uso
monitor = ModelMonitor(model)
prediction = monitor.predict(X_new)
# Mais tarde, quando o rótulo real estiver disponível
monitor.log_actual(y_true)

Raridade: Comum Dificuldade: Média


15. O que é CI/CD para machine learning?

Resposta: CI/CD automatiza o teste e a implementação de modelos de ML.

  • Integração Contínua:
    • Teste automatizado
    • Verificações de qualidade do código
    • Validação do modelo
  • Implementação Contínua:
    • Implementação automatizada
    • Capacidades de rollback
    • Teste A/B
# .github/workflows/ml-pipeline.yml
name: ML Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.9
      
      - name: Install dependencies
        run: |
          pip
Newsletter subscription

Dicas de carreira semanais que realmente funcionam

Receba as últimas ideias diretamente na sua caixa de entrada

Sua Próxima Entrevista Está a Apenas um Currículo de Distância

Crie um currículo profissional e otimizado em minutos. Não são necessárias habilidades de design—apenas resultados comprovados.

Criar meu currículo

Compartilhar esta publicação

Supere a Taxa de Rejeição de 75% do ATS

3 em cada 4 currículos nunca chegam a um olho humano. Nossa otimização de palavras-chave aumenta sua taxa de aprovação em até 80%, garantindo que os recrutadores realmente vejam seu potencial.