def perda_2d(t):
return t[0] ** 2 + 25 * t[1] ** 2
def trajetoria(cria_otimizador, passos=60):
theta = torch.tensor([-4.5, 1.0], requires_grad=True)
otimizador = cria_otimizador([theta])
caminho, perdas = [theta.detach().clone()], []
for _ in range(passos):
otimizador.zero_grad()
perda = perda_2d(theta)
perda.backward()
otimizador.step()
caminho.append(theta.detach().clone())
perdas.append(perda.item())
return torch.stack(caminho).numpy(), np.array(perdas)
configs_2d = {
"SGD (η = 0,035)": (lambda p: torch.optim.SGD(p, lr=0.035), CINZA),
"SGD + momentum (η = 0,02, β = 0,7)": (lambda p: torch.optim.SGD(p, lr=0.02, momentum=0.7), AZUL),
"Adam (η = 0,2)": (lambda p: torch.optim.Adam(p, lr=0.2), LARANJA),
}
resultados_2d = {nome: trajetoria(f) for nome, (f, _) in configs_2d.items()}
T1, T2 = np.meshgrid(np.linspace(-5, 2, 300), np.linspace(-1.3, 1.3, 300))
fig, axes = plt.subplots(1, 2, figsize=(15, 4.5), width_ratios=[1.6, 1])
axes[0].contour(T1, T2, T1 ** 2 + 25 * T2 ** 2, levels=np.logspace(-1, 1.8, 12), colors=CINZA, linewidths=0.6, alpha=0.6)
for nome, (caminho, perdas) in resultados_2d.items():
cor = configs_2d[nome][1]
axes[0].plot(caminho[:, 0], caminho[:, 1], color=cor, marker="o", ms=3, lw=1.5, label=nome)
axes[1].plot(perdas, color=cor, lw=2, label=nome)
axes[0].scatter(0, 0, marker="*", s=250, color=VERDE, zorder=3)
axes[0].set(xlabel="θ₁", ylabel="θ₂", title="Caminho de cada otimizador (60 passos)")
axes[0].set_aspect("equal")
axes[0].grid(False)
axes[0].legend(frameon=False, loc="upper center", bbox_to_anchor=(0.5, -0.18), ncols=3)
axes[1].set(yscale="log", xlabel="passo", ylabel="perda (escala log)", title="Perda ao longo dos passos")
plt.tight_layout()
plt.show()
for nome, (_, perdas) in resultados_2d.items():
print(f"{nome:<36} perda após 20 passos = {perdas[19]:.3f} após 60 = {perdas[-1]:.5f}")