UFPR LicComp
GRADIO — Interface Web para Python
Prof. Jéfer Prof. Jéfer Benedett Dörr • UFPR Palotina
🚀 PRÓXIMO NÍVEL Do Protótipo à Produção

🌍 Leve seu modelo para o mundo real

🎯 O que você pode fazer:

  • ✅ Transformar seu modelo Gradio em API
  • ✅ Rodar em dispositivos embarcados (Jetson Nano, Raspberry Pi)
  • ✅ Converter para apps mobile (Android/iOS)
  • ✅ Criar sistemas de monitoramento em tempo real

⚡ Tecnologias envolvidas:

  • 🔷 ONNX - formato universal de modelos
  • 🔷 TensorFlow Lite - para dispositivos móveis
  • 🔷 TensorRT - otimização NVIDIA
  • 🔷 FastAPI - para criar APIs
⚡ Otimização (para rodar “de verdade” no hardware)

Em produção, a pergunta não é “funciona?”, é: quantos FPS, com quanta bateria e com qual estabilidade.

  • Quantização (FP32 → FP16/INT8): reduz tamanho e acelera inferência.
  • TensorRT (Jetson): compila o grafo e usa kernels otimizados da GPU NVIDIA.
  • Batch=1: no edge, quase sempre é “um frame por vez”.

Regra prática: modelo menor + bem otimizado costuma ganhar de modelo grande “cru”.

💡 Imagine isso: O modelo que você treinou e testou no Gradio voando em um drone sobre lavouras, detectando pragas em tempo real!

✅ Checklist de Produção (o que separa demo de sistema real)
  • Dataset real: fotos do campo (luz, poeira, sombra, ângulo) → não só PlantVillage.
  • Validação correta: split por fazenda/talhão (evita “vazar” imagens parecidas no treino e teste).
  • Métricas certas: além de acurácia → precision/recall e F1 (falso alarme custa defensivo).
  • Latência alvo: defina FPS mínimo (ex.: 20–30 FPS) e aceite trade-off (modelo menor).
  • Robustez: augmentação e teste em condições extremas (contraste baixo, motion blur, chuva).
  • Monitoramento: logging, drift, re-treino (mundo real muda a cada safra).

🚁 Caso 1: Monitoramento com Drone (Jetson Nano)

📦 Converter para ONNX
import torch
import torchvision.models as models

# Seu modelo treinado (exemplo: ResNet)
modelo = models.resnet18(pretrained=True)
modelo.eval()

# Criando entrada exemplo
exemplo = torch.randn(1, 3, 224, 224)

# Exportar para ONNX
torch.onnx.export(
    modelo,                     # modelo treinado
    exemplo,                    # entrada exemplo
    "modelo_drone.onnx",        # nome do arquivo
    export_params=True,         # exportar parâmetros treinados
    opset_version=11,           # versão do ONNX
    input_names=['input'],      # nome da entrada
    output_names=['output'],    # nome da saída
    dynamic_axes={
        'input': {0: 'batch_size'},
        'output': {0: 'batch_size'}
    }
)

print("✅ Modelo convertido para ONNX!")
🚁 Código no Drone (Jetson)
import cv2
import numpy as np
import onnxruntime as ort
import time

# Carregar modelo otimizado para Jetson
sess = ort.InferenceSession("modelo_drone.onnx")

# Capturar vídeo da câmera do drone
cap = cv2.VideoCapture(0)  # ou feed do drone

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # Pré-processamento
    input_frame = cv2.resize(frame, (224, 224))
    input_frame = input_frame.transpose(2,0,1)  # HWC -> CHW
    input_frame = input_frame.astype(np.float32) / 255.0
    input_frame = np.expand_dims(input_frame, axis=0)
    
    # Inferência
    start = time.time()
    outputs = sess.run(['output'], {'input': input_frame})
    fps = 1 / (time.time() - start)
    
    # Detecção (exemplo: praga ou não)
    if outputs[0][0][0] > 0.5:
        cv2.putText(frame, "⚠️ PRAGA DETECTADA!", 
                   (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 
                   1, (0, 0, 255), 2)
    
    cv2.putText(frame, f"FPS: {fps:.1f}", (10, 60),
               cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2)
    
    # Enviar alerta se necessário
    if outputs[0][0][0] > 0.8:
        print("🚨 ALERTA CRÍTICO: Enviando coordenadas...")
        # Aqui você enviaria GPS do drone
        
    cv2.imshow('Drone Monitor', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
🎚️ Operação: “Quando eu disparo alerta?”

Em campo, falso positivo custa dinheiro (defensivo/tempo). Falso negativo custa produtividade.

  • conf ≥ 0.80: alerta crítico (ação imediata).
  • 0.50 ≤ conf < 0.80: “suspeita” (marcar no mapa + pedir verificação).
  • conf < 0.50: ignora (ou só registra para análise posterior).

Dica: ajuste limiar usando curva Precision–Recall, não “no chute”.

📱 Caso 2: App Android/iOS (TensorFlow Lite)

📱 Mobile (por que faz sentido no agro?)
  • Offline: muitas áreas rurais sem rede → TFLite roda sem internet.
  • Privacidade: fotos da lavoura não precisam sair do dispositivo.
  • Baixa latência: resposta imediata para o técnico no talhão.
  • Coleta inteligente: app salva casos incertos para melhorar o dataset.
📱 Converter para TFLite
import tensorflow as tf

# Carregar modelo Keras (treinado)
modelo = tf.keras.models.load_model("modelo_agro.h5")

# Converter para TensorFlow Lite
converter = tf.lite.TFLiteConverter.from_keras_model(modelo)

# Otimizações para mobile
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]

tflite_model = converter.convert()

# Salvar
with open('modelo_mobile.tflite', 'wb') as f:
    f.write(tflite_model)

print("✅ Modelo pronto para Android/iOS!")
⚡ Kotlin (Android) - Trecho
// Android - TensorFlow Lite
class ClassificadorAgro {
    private val tflite: Interpreter
    
    init {
        // Carregar modelo do assets
        val model = loadModelFile(context, "modelo_mobile.tflite")
        tflite = Interpreter(model)
    }
    
    fun classificarSolo(umidade: Float, ph: Float, nitrogenio: Float): String {
        val inputs = arrayOf(floatArrayOf(umidade, ph, nitrogenio))
        val outputs = Array(1) { FloatArray(3) }
        
        tflite.run(inputs, outputs)
        
        return when (outputs[0].indices.maxByOrNull { outputs[0][it] }) {
            0 -> "🌱 Solo ideal para plantio"
            1 -> "⚠️ Necessita correção"
            2 -> "🚫 Solo inadequado"
            else -> "🤖 Analisando..."
        }
    }
}

🌐 Caso 3: API REST para integração

⚡ FastAPI (servidor)
from fastapi import FastAPI, UploadFile
import uvicorn
import numpy as np
import onnxruntime as ort
from PIL import Image
import io

app = FastAPI(title="API Agro 5.0")

# Carregar modelo ONNX
sess = ort.InferenceSession("modelo_drone.onnx")

@app.post("/predict")
async def predict(file: UploadFile):
    # Ler imagem
    image = Image.open(io.BytesIO(await file.read()))
    image = image.resize((224, 224))
    
    # Pré-processar
    input_array = np.array(image).astype(np.float32) / 255.0
    input_array = input_array.transpose(2,0,1)
    input_array = np.expand_dims(input_array, axis=0)
    
    # Inferência
    outputs = sess.run(['output'], {'input': input_array})
    
    return {
        "probabilidade": float(outputs[0][0][0]),
        "classificacao": "PRAGA" if outputs[0][0][0] > 0.5 else "SAUDÁVEL",
        "status": "success"
    }

@app.get("/health")
async def health():
    return {"status": "online", "modelo": "Agro 5.0 v1"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
📱 Cliente Python
import requests

# Enviar imagem para API
url = "http://192.168.0.100:8000/predict"
files = {"file": ("foto.jpg", open("lavoura.jpg", "rb"), "image/jpeg")}

response = requests.post(url, files=files)
resultado = response.json()

print(f"🌾 Resultado: {resultado['classificacao']}")
print(f"📊 Confiança: {resultado['probabilidade']*100:.1f}%")

# Se for praga, acionar alerta
if resultado['classificacao'] == "PRAGA":
    requests.post("http://api.drone/alert", 
                 json={"coordenadas": "-25.432, -54.588"})

📊 Comparativo: Onde rodar seu modelo

Plataforma Formato Velocidade Caso de uso
💻 Gradio (prototipagem) Python puro ⏱️ Rápido dev Testes, aulas, validação
🚁 Jetson Nano ONNX/TensorRT ⚡ 30-60 FPS Drones, robótica, edge
📱 Android/iOS TFLite 📱 20-40 FPS Apps mobile, campo
☁️ Nuvem (API) FastAPI/ONNX 🌐 Escalável Sistemas web, integração
📏 Métricas de Deploy (o que medir)
✅ Checklist rápido:
- Tamanho do modelo (MB): ____ 
- Tempo por frame (ms): ____  → FPS = 1000/ms
- RAM pico (MB): ____
- Consumo (W) no Jetson / bateria no mobile: ____
- Precisão operacional: Precision/Recall no campo: ____

🎯 Do Gradio para o mundo real - roteiro prático:

  1. Comece no Gradio/Colab → valide sua ideia
  2. Exporte para ONNX/TFLite → prepare para produção
  3. Otimize para o dispositivo → Jetson (TensorRT), Mobile (TFLite)
  4. Embargue no hardware → drone, robô, app, API
  5. Monitore e melhore → colete dados do mundo real

🌟 Exemplo real: Monitoramento de lavoura com drone

  • Treinamento: Modelo detecta pragas (Gradio/Colab)
  • Conversão: ONNX + TensorRT para Jetson Nano
  • Drone: DJI + Jetson Nano processando em tempo real
  • Alerta: Ao detectar praga, envia coordenadas
  • Ação: Pulverização localizada automática
🚁→🌽

pulverização seletiva → menos desperdício (70% menos defensivos)

Detecção precoce: +35% produtividade (decisão mais rápida e menos dano acumulado)

🛰️ Do bounding box ao mapa de decisão (Agro 5.0)
  1. Detecção no frame (YOLO) → caixas + confiança.
  2. Carimbo de GPS (latitude/longitude) + timestamp.
  3. Georreferenciamento: relaciona o pixel ao solo (altitude + câmera + IMU).
  4. Heatmap: agrega detecções por área (foco de praga/doença).
  5. Ação: pulverização localizada / vistoria dirigida / abertura de OS.
📚 Material complementar
🔗 NVIDIA Jetson Nano Guide 🔗 TensorFlow Lite Tutorial 🔗 ONNX Runtime Docs