Artigo 37 — Redes Neurais com TensorFlow e Keras
Prof. Ricardo Matos Módulo 7 · Data Science e Machine Learning · Artigo 37 de 52
Introdução
Redes neurais artificiais são o motor por trás do reconhecimento de imagens, tradução automática, geração de texto e dezenas de outras aplicações que pareciam impossíveis há uma década. TensorFlow é a plataforma de deep learning mais usada no mundo, e Keras é sua API de alto nível — que permite construir e treinar redes neurais complexas com poucas linhas de código. Neste artigo construímos redes do zero, entendemos seus componentes e as aplicamos em problemas reais.
Instalação
pip install tensorflow
# ou, para Macs com Apple Silicon:
pip install tensorflow-macos tensorflow-metal
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models, callbacks
print(f"TensorFlow: {tf.__version__}")
print(f"GPU disponível: {len(tf.config.list_physical_devices('GPU')) > 0}")
O Neurônio Artificial
Um neurônio artificial recebe entradas, aplica pesos, soma e passa por uma função de ativação:
Entradas Pesos Soma + Bias Ativação Saída
x₁ ──── w₁ ─┐
x₂ ──── w₂ ──┤──► Σ(xᵢwᵢ) + b ──► f(z) ──► ŷ
x₃ ──── w₃ ─┘
import numpy as np
# Simulando um neurônio manualmente
def neuronio(entradas, pesos, bias, ativacao="relu"):
z = np.dot(entradas, pesos) + bias
if ativacao == "relu":
return max(0, z)
elif ativacao == "sigmoid":
return 1 / (1 + np.exp(-z))
elif ativacao == "tanh":
return np.tanh(z)
return z
entradas = np.array([0.5, 0.3, 0.8])
pesos = np.array([0.4, 0.7, 0.2])
bias = 0.1
saida = neuronio(entradas, pesos, bias, "relu")
print(f"Saída do neurônio: {saida:.4f}")
Funções de Ativação
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 200)
funcoes = {
"ReLU": np.maximum(0, x),
"Sigmoid": 1 / (1 + np.exp(-x)),
"Tanh": np.tanh(x),
"LeakyReLU": np.where(x > 0, x, 0.1 * x),
}
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
for ax, (nome, y) in zip(axes, funcoes.items()):
ax.plot(x, y, color="#2E75B6", linewidth=2)
ax.axhline(0, color="gray", linewidth=0.5)
ax.axvline(0, color="gray", linewidth=0.5)
ax.set_title(nome)
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("funcoes_ativacao.png", dpi=120)
plt.show()
Primeira Rede Neural: Classificação Binária
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# Dataset — previsão de aprovação
np.random.seed(42)
n = 1000
X = np.column_stack([
np.random.uniform(1, 10, n), # horas_estudo
np.random.randint(0, 20, n), # faltas
np.random.uniform(3, 10, n), # nota_anterior
])
y = ((0.5 * X[:, 0] + 0.3 * X[:, 2] - 0.1 * X[:, 1] +
np.random.normal(0, 0.5, n)) >= 5).astype(int)
# Divisão e normalização
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# Construindo a rede
def construir_modelo(input_dim: int) -> keras.Model:
modelo = keras.Sequential([
# Camada de entrada
layers.Input(shape=(input_dim,)),
# Camadas ocultas
layers.Dense(64, activation="relu"),
layers.BatchNormalization(),
layers.Dropout(0.3),
layers.Dense(32, activation="relu"),
layers.BatchNormalization(),
layers.Dropout(0.2),
layers.Dense(16, activation="relu"),
# Camada de saída — 1 neurônio, sigmoid para binário
layers.Dense(1, activation="sigmoid")
])
modelo.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss="binary_crossentropy",
metrics=["accuracy", keras.metrics.AUC(name="auc")]
)
return modelo
modelo = construir_modelo(X_train.shape[1])
modelo.summary()
Treinamento e Callbacks
# Callbacks — comportamentos durante o treinamento
lista_callbacks = [
# Para o treinamento se não melhorar
callbacks.EarlyStopping(
monitor="val_loss",
patience=15,
restore_best_weights=True,
verbose=1
),
# Reduz learning rate quando estagna
callbacks.ReduceLROnPlateau(
monitor="val_loss",
factor=0.5,
patience=7,
min_lr=1e-6,
verbose=1
),
# Salva o melhor modelo
callbacks.ModelCheckpoint(
filepath="melhor_modelo.keras",
monitor="val_auc",
save_best_only=True,
mode="max",
verbose=1
),
# Logs para TensorBoard
callbacks.TensorBoard(log_dir="./logs", histogram_freq=1),
]
# Treinamento
historico = modelo.fit(
X_train, y_train,
epochs=100,
batch_size=32,
validation_split=0.2,
callbacks=lista_callbacks,
verbose=1
)
# Avaliação no conjunto de teste
loss, acc, auc = modelo.evaluate(X_test, y_test, verbose=0)
print(f"\nResultados no Teste:")
print(f" Loss: {loss:.4f}")
print(f" Acurácia: {acc:.4f}")
print(f" AUC: {auc:.4f}")
Visualizando o Treinamento
import matplotlib.pyplot as plt
def plotar_historico(historico):
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
metricas = [
("loss", "Loss", "Loss de Treino vs Validação"),
("accuracy", "Acurácia", "Acurácia de Treino vs Validação"),
("auc", "AUC", "AUC de Treino vs Validação"),
]
for ax, (metrica, ylabel, titulo) in zip(axes, metricas):
ax.plot(historico.history[metrica],
label="Treino", color="#2E75B6")
ax.plot(historico.history[f"val_{metrica}"],
label="Validação", color="#C00000", linestyle="--")
ax.set_title(titulo)
ax.set_xlabel("Época")
ax.set_ylabel(ylabel)
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("historico_treinamento.png", dpi=120)
plt.show()
plotar_historico(historico)
Regressão com Redes Neurais
# Prevendo nota_final (valor contínuo)
y_reg = 0.4 * X[:, 0] + 0.3 * X[:, 2] - 0.1 * X[:, 1] + np.random.normal(0, 0.3, n)
y_reg = np.clip(y_reg, 0, 10)
X_tr, X_te, y_tr, y_te = train_test_split(X, y_reg, test_size=0.2, random_state=42)
X_tr = scaler.fit_transform(X_tr)
X_te = scaler.transform(X_te)
def modelo_regressao(input_dim: int) -> keras.Model:
modelo = keras.Sequential([
layers.Input(shape=(input_dim,)),
layers.Dense(128, activation="relu"),
layers.Dropout(0.2),
layers.Dense(64, activation="relu"),
layers.Dropout(0.2),
layers.Dense(32, activation="relu"),
layers.Dense(1) # sem ativação — saída linear
])
modelo.compile(
optimizer="adam",
loss="mse",
metrics=["mae", keras.metrics.RootMeanSquaredError(name="rmse")]
)
return modelo
modelo_reg = modelo_regressao(3)
historico_reg = modelo_reg.fit(
X_tr, y_tr,
epochs=100,
batch_size=32,
validation_split=0.2,
callbacks=[callbacks.EarlyStopping(patience=10, restore_best_weights=True)],
verbose=0
)
mse, mae, rmse = modelo_reg.evaluate(X_te, y_te, verbose=0)
print(f"MSE: {mse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"RMSE: {rmse:.4f}")
# Visualizando predições
y_pred_reg = modelo_reg.predict(X_te, verbose=0).flatten()
plt.figure(figsize=(8, 5))
plt.scatter(y_te, y_pred_reg, alpha=0.4, color="#2E75B6")
plt.plot([0, 10], [0, 10], "r--", label="Ideal")
plt.xlabel("Nota Real")
plt.ylabel("Nota Prevista")
plt.title("Real vs Previsto")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("regressao_nn.png", dpi=120)
plt.show()
API Funcional: Redes Complexas
A API Sequencial é simples, mas a API Funcional permite arquiteturas complexas — múltiplas entradas, saídas ou conexões residuais:
from tensorflow.keras import Input, Model
def construir_rede_funcional():
# Múltiplas entradas
entrada_academica = Input(shape=(3,), name="academica") # horas, faltas, nota
entrada_pessoal = Input(shape=(2,), name="pessoal") # idade, renda_familiar
# Ramo acadêmico
x1 = layers.Dense(32, activation="relu")(entrada_academica)
x1 = layers.Dropout(0.2)(x1)
x1 = layers.Dense(16, activation="relu")(x1)
# Ramo pessoal
x2 = layers.Dense(16, activation="relu")(entrada_pessoal)
x2 = layers.Dense(8, activation="relu")(x2)
# Combinando os ramos
combinado = layers.Concatenate()([x1, x2])
x = layers.Dense(32, activation="relu")(combinado)
x = layers.Dropout(0.3)(x)
# Múltiplas saídas
saida_aprovado = layers.Dense(1, activation="sigmoid", name="aprovado")(x)
saida_nota = layers.Dense(1, activation="linear", name="nota")(x)
modelo = Model(
inputs=[entrada_academica, entrada_pessoal],
outputs=[saida_aprovado, saida_nota]
)
modelo.compile(
optimizer="adam",
loss={
"aprovado": "binary_crossentropy",
"nota": "mse"
},
loss_weights={"aprovado": 1.0, "nota": 0.5},
metrics={
"aprovado": ["accuracy"],
"nota": ["mae"]
}
)
return modelo
modelo_funcional = construir_rede_funcional()
keras.utils.plot_model(
modelo_funcional,
to_file="arquitetura.png",
show_shapes=True,
dpi=80
)
modelo_funcional.summary()
Regularização: Evitando Overfitting
from tensorflow.keras import regularizers
def modelo_regularizado(input_dim: int) -> keras.Model:
return keras.Sequential([
layers.Input(shape=(input_dim,)),
# L2 regularization — penaliza pesos grandes
layers.Dense(128, activation="relu",
kernel_regularizer=regularizers.l2(0.001)),
layers.BatchNormalization(),
layers.Dropout(0.4),
# L1 regularization — incentiva esparsidade
layers.Dense(64, activation="relu",
kernel_regularizer=regularizers.l1(0.001)),
layers.Dropout(0.3),
# Combinação L1 + L2 (Elastic Net)
layers.Dense(32, activation="relu",
kernel_regularizer=regularizers.l1_l2(l1=0.001, l2=0.001)),
layers.Dense(1, activation="sigmoid")
])
# Comparando modelos com e sem regularização
historicos = {}
for nome, model in [
("Sem regularização", construir_modelo(3)),
("Com regularização", modelo_regularizado(3)),
]:
h = model.fit(
X_train, y_train,
epochs=50,
batch_size=32,
validation_split=0.2,
callbacks=[callbacks.EarlyStopping(patience=10, restore_best_weights=True)],
verbose=0
)
historicos[nome] = h
loss, acc, auc = model.evaluate(X_test, y_test, verbose=0)
print(f"{nome:25} | Acc: {acc:.4f} | AUC: {auc:.4f}")
Transfer Learning
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
def modelo_transfer_learning(num_classes: int) -> keras.Model:
"""
Usa MobileNetV2 pré-treinado no ImageNet como extrator de features.
Útil quando o dataset próprio é pequeno.
"""
# Base pré-treinada — congela os pesos
base = MobileNetV2(
weights="imagenet",
include_top=False, # remove a camada de classificação
input_shape=(224, 224, 3)
)
base.trainable = False # congela
# Adiciona cabeçalho de classificação customizado
modelo = keras.Sequential([
base,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation="relu"),
layers.Dropout(0.3),
layers.Dense(num_classes, activation="softmax")
])
modelo.compile(
optimizer=keras.optimizers.Adam(1e-3),
loss="categorical_crossentropy",
metrics=["accuracy"]
)
return modelo
# Fine-tuning — descongela as últimas camadas da base
def fine_tuning(modelo_base: keras.Model, num_layers_descongelar: int = 20):
base = modelo_base.layers[0]
base.trainable = True
# Congela tudo exceto as últimas N camadas
for layer in base.layers[:-num_layers_descongelar]:
layer.trainable = False
modelo_base.compile(
optimizer=keras.optimizers.Adam(1e-5), # lr menor para fine-tuning
loss="categorical_crossentropy",
metrics=["accuracy"]
)
return modelo_base
Salvando e Carregando Modelos
# Formato recomendado — .keras (nativo Keras 3)
modelo.save("modelo_completo.keras")
modelo_carregado = keras.models.load_model("modelo_completo.keras")
# Apenas os pesos
modelo.save_weights("pesos.weights.h5")
modelo_novo = construir_modelo(3)
modelo_novo.load_weights("pesos.weights.h5")
# Exportando para TensorFlow SavedModel (para TensorFlow Serving)
modelo.export("saved_model/")
# Convertendo para TensorFlow Lite (mobile e edge devices)
converter = tf.lite.TFLiteConverter.from_keras_model(modelo)
tflite_model = converter.convert()
with open("modelo.tflite", "wb") as f:
f.write(tflite_model)
print("Modelos salvos com sucesso.")
Exemplo Completo: Pipeline Completo de Deep Learning
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, callbacks
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report
import matplotlib.pyplot as plt
class PipelineDeepLearning:
"""Pipeline completo de classificação com rede neural."""
def __init__(self, nome: str = "modelo"):
self.nome = nome
self.scaler = StandardScaler()
self.modelo = None
self.historico = None
def preparar_dados(self, X, y, test_size=0.2, val_size=0.2):
X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=test_size,
random_state=42, stratify=y
)
X_tr = self.scaler.fit_transform(X_tr)
X_te = self.scaler.transform(X_te)
self._X_test = X_te
self._y_test = y_te
return X_tr, X_te, y_tr, y_te
def construir(self, input_dim: int, camadas: list = None):
camadas = camadas or [64, 32, 16]
entradas = layers.Input(shape=(input_dim,))
x = entradas
for unidades in camadas:
x = layers.Dense(unidades, activation="relu")(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(0.3)(x)
saida = layers.Dense(1, activation="sigmoid")(x)
self.modelo = keras.Model(entradas, saida, name=self.nome)
self.modelo.compile(
optimizer=keras.optimizers.Adam(0.001),
loss="binary_crossentropy",
metrics=["accuracy", keras.metrics.AUC(name="auc")]
)
return self
def treinar(self, X_train, y_train, epochs=100, batch_size=32):
self.historico = self.modelo.fit(
X_train, y_train,
epochs=epochs,
batch_size=batch_size,
validation_split=0.2,
callbacks=[
callbacks.EarlyStopping(patience=15, restore_best_weights=True),
callbacks.ReduceLROnPlateau(patience=7, factor=0.5, verbose=0)
],
verbose=0
)
epocas_treinadas = len(self.historico.history["loss"])
print(f"Treinado por {epocas_treinadas} épocas.")
return self
def avaliar(self):
y_pred_prob = self.modelo.predict(self._X_test, verbose=0).flatten()
y_pred = (y_pred_prob >= 0.5).astype(int)
loss, acc, auc = self.modelo.evaluate(self._X_test, self._y_test, verbose=0)
print(f"\n{'='*40}")
print(f"Modelo: {self.nome}")
print(f" Loss: {loss:.4f}")
print(f" Acurácia: {acc:.4f}")
print(f" AUC: {auc:.4f}")
print(classification_report(
self._y_test, y_pred,
target_names=["Reprovado", "Aprovado"]
))
def salvar(self):
self.modelo.save(f"{self.nome}.keras")
import joblib
joblib.dump(self.scaler, f"{self.nome}_scaler.pkl")
print(f"Pipeline salvo: {self.nome}.keras")
def prever(self, X_novo):
X_proc = self.scaler.transform(X_novo)
prob = self.modelo.predict(X_proc, verbose=0).flatten()
return prob
# Usando o pipeline
np.random.seed(42)
n = 2000
X = np.column_stack([
np.random.uniform(1, 10, n),
np.random.randint(0, 20, n),
np.random.uniform(3, 10, n),
np.random.uniform(0, 5, n),
])
y = ((0.5*X[:,0] + 0.3*X[:,2] - 0.1*X[:,1] + 0.2*X[:,3]
+ np.random.normal(0, 0.5, n)) >= 5).astype(int)
pipeline = PipelineDeepLearning("aprovacao_nn")
X_tr, X_te, y_tr, y_te = pipeline.preparar_dados(X, y)
pipeline.construir(input_dim=4, camadas=[128, 64, 32])
pipeline.treinar(X_tr, y_tr, epochs=150)
pipeline.avaliar()
pipeline.salvar()
# Prevendo novos alunos
novos = np.array([
[8.0, 2, 9.0, 4.0], # perfil forte
[2.0, 18, 4.0, 0.5], # perfil fraco
])
probs = pipeline.prever(novos)
for i, p in enumerate(probs, 1):
print(f"Aluno {i}: {p:.1%} de probabilidade de aprovação")
Resumo
- Redes neurais são empilhamentos de camadas densas com funções de ativação não-lineares
Sequentialé adequada para arquiteturas lineares; a API Funcional permite múltiplas entradas e saídasBatchNormalizationestabiliza o treinamento;Dropoutprevine overfittingEarlyStoppingeReduceLROnPlateauautomatizam o controle do treinamento- Regressão usa ativação linear na saída e loss MSE; classificação usa sigmoid/softmax e cross-entropy
- Transfer Learning reaproveita modelos pré-treinados — essencial quando os dados são escassos
- Salve sempre o pipeline completo incluindo o scaler — necessário para predições em produção
- TensorBoard visualiza métricas, grafos e distribuições de pesos em tempo real
Referências e Leituras Complementares
- TensorFlow — documentação oficial — https://www.tensorflow.org/api_docs/python/tf
- Keras — guia do desenvolvedor — https://keras.io/guides/
- TensorFlow Playground — visualização interativa — https://playground.tensorflow.org/
- Transfer Learning com Keras — https://keras.io/guides/transfer_learning/
- GÉRON, Aurélien. Hands-On Machine Learning with Scikit-Learn, Keras and TensorFlow. 3. ed. O'Reilly Media, 2022. Cap. 10–16 — redes neurais do básico ao avançado.
- CHOLLET, François. Deep Learning with Python. 2. ed. Manning, 2021. — escrito pelo criador do Keras, referência definitiva sobre deep learning prático.
- GOODFELLOW, Ian; BENGIO, Yoshua; COURVILLE, Aaron. Deep Learning. MIT Press, 2016. — fundamentos teóricos do deep learning, disponível em https://www.deeplearningbook.org/
Prof. Ricardo Matos — Dominando o Python em 1 Ano · Artigo 37 de 52 Próximo: Artigo 38 — Processamento de Linguagem Natural (NLP)
you asked
Sim