Contenido¶
- Librerías y paquetes
Conceptos básicos de modelos generativos
- Modelo Generativo.
- Variable Latente.
- Tratabilidad y flexibilidad.
Modelo de difusión de juguete una distribución 2 dimensional
- Proceso de Difusión Directa (Forward).
- Proceso de Difusión Inversa (Reverse).
Librerías y paquetes¶
import Pkg
Pkg.add("Distributions");
Pkg.add("Plots");
Pkg.add("GaussianMixtures");
Pkg.add("Flux");
Pkg.add("ProgressMeter");
Pkg.add("MLUtils");
Pkg.add("Statistics");
Pkg.add("Optimisers");
Pkg.add("Zygote");
using GaussianMixtures;
using Random, LinearAlgebra;
using Distributions, Plots;
using Flux, ProgressMeter, MLUtils, Statistics;
using Flux: onehotbatch, crossentropy, logitcrossentropy;
using MLUtils: DataLoader;
Random: para controlar la semilla y reproducibilidad.Distributions: para definir y muestrear distribuciones gaussianas multivariadas.GaussianMixtures: Para generar modelos generativos de Mezcla Gaussiana.
Conceptos básicos de modelos generativos¶
Dado un conjunto de datos con etiquetas $D =\{\vec{x}_i, y_i\}_{i=0}^{n}$, un modelo generativo busca aproximar la distribución conjunta sobre todas las observaciones y etiquetas $p(x,y)$ o, si el dataset no tiene etiquetas $D =\{\vec{x}_i\}_{i=0}^{n}$, la distribución $p(\vec{x_1},\ldots, \vec{x_n})$ de los datos observados.
Una vez que tenemos una buena aproximación de esa distribución, podemos muestrear nuevas observaciones $x$ plausibles, es decir, generar datos nuevos que "parezcan" parte del mismo fenómeno que el conjunto de entrenamiento $x$.
Un modelo discriminativo se enfoca en la probabilidad condicional $p(y\mid x)$ o directamente en una frontera de decisión. Su objetivo es clasificar o predecir una etiqueta a partir de una observación: no necesita saber cómo se generan todos los detalles de $x$, sólo necesita la información suficiente para distinguir clases.
En cambio, un generativo debe capturar la estructura completa de los datos (correlaciones, modos, forma de la densidad) para poder reconstruir o producir muestras nuevas.
Qué aprenden
- Generativos: la distribución conjunta $p(x,y)$ (o $p(x)$ si no hay etiquetas).
- Discriminativos: la probabilidad condicional $p(y\mid x)$ o la regla de clasificación directamente.
Para qué sirven
- Generar nuevos ejemplos (data augmentation, síntesis de imágenes, audio, texto).
- Estimación de densidad (detectar anomalías, compresión, modelado probabilístico).
- Inferencia con variables latentes: escribir $p(x)=\int p(x\mid z)\,p(z)\,dz$ y aprender un espacio latente $z$ que explica la variabilidad de los datos.
Tipos importantes
- Autoregresivos (PixelCNN, Transformer-based): densidad explícita, muestreo secuencial.
- Variational Autoencoders (VAEs): modelo explícito con latente y función de evidencia (ELBO).
- Generative Adversarial Networks (GANs): modelo implícito que produce buenos samples, pero sin probabilidad tractable.
- Normalizing flows: transformaciones invertibles con densidad exacta y tractable.
- Modelos de difusión / score-based: aprenden gradientes de la log-densidad $\nabla_x \log p(x)$ y muestrean invirtiendo un proceso de ruido.
Ventajas / desventajas
- Ventaja: permiten generar y hacer inferencia completa sobre la distribución de los datos.
- Desventaja: aprender $p(x)$ puede ser más difícil (costoso y con trade-offs entre calidad de sample, tractabilidad y evaluación). Los discriminativos suelen ser más simples y eficientes para tareas de clasificación.
# 1. Definimos un modelo generativo (Mezcla de 2 Gaussianas)
# Componente 1: Media -2, Desviación Estándar 0.5
# Componente 2: Media 3, Desviación Estándar 1.0
# La mezcla está ponderada 40% a la componente 1 y 60% a la 2.
gmm = MixtureModel(
[Normal(-2, 0.5), Normal(3, 1.0)], # Las dos componentes Gaussianas
[0.4, 0.6] # Sus pesos
)
# 2. Generamos 1000 nuevas muestras a partir del modelo
nuevas_muestras = rand(gmm, 1000)
# 3. Visualizamos los datos generados
histogram(nuevas_muestras, bins=50, normalize=:pdf, label="Datos Generados",
title="Muestras generadas por un GMM", legend=:topleft,
color=:cornflowerblue)
plot!(x -> pdf(gmm, x), -5, 6, label="Densidad Real del Modelo", lw=3, color=:red)
Variable latente¶
Una variable latente$(z)$ es una variable oculta o no observada que el modelo utiliza para capturar alguna estructura o caracteristica subyacente en los datos. Los modelos generativos a menudo usan variables latentes para simplificar la descripción de datos complejos. Por ejemplo, en GMM, la variable latente podria ser de cual de las compotentes gaussianas proviene este punto de datos o en un autoencoder variacional(VAE) para caras, una variable latente podria controlar el angulo de la sonrisa o el color del pelo.
Tratabilidad y Flexibilidad¶
Estos dos conceptos estan fuertemente relacionados y en conflicto.
Tratabilidad: Se refiere a la viabilidad computacional de las operaciones clave de un modelo. Un modelo es tratable si podemos calcular de forma eficiente la probabilidad de un dato $(p(x))$, entrenar el modelo y generar nuevas muestras. Si estas operaciones son demasiado lentas o requieren una cantidad de recursos prohibitiva, el modelo es intratable.
Flexibilidad: Es la capacidad del modelo para capturar distribuciones de datos muy complejas y variadas. Un modelo de una sola gaussiana es poco flexible; solo puede aprender datos con forma de campana. Un VAE o un GAN son muy flexibles y pueden aprender distribuciones complejas como las de imágenes de caras.
Modelos simples (Gaussian, Laplace):
- Pros: $ p(x) $ es analítico, entrenar y muestrear es rápido.
- Contras: estructura limitada (poca capacidad para datos complejos).
Modelos flexibles $ \frac{\phi(x)}{Z} $:
- Pros: teóricamente pueden representar cualquier forma.
- Contras: $ Z = \int \phi(x)\,dx $ es intratable → muestreo y evaluación costosos.
El reto en el modelado generativo es encontrar un buen equilibrio: queremos un modelo lo suficientemente flexible para capturar los datos reales, pero que siga siendo tratable para poder entrenarlo y usarlo, y que no exceda en flexivilidad porque puede causar overfitting. Aqui mostramos un ejemplo de un modelo simple el cual no captura los datos tan bien y un modelo mas riguroso que si lo hace
gmm = MixtureModel([Normal(-2, 0.5), Normal(3, 1.0)], [0.4, 0.6])
# Proceso de generación explícito con la variable latente
function generar_muestra_con_latente(modelo)
# 1. Muestrear la variable latente (z): ¿Qué componente usamos? (1 o 2)
# La probabilidad de elegir la componente 1 es 0.4, y la 2 es 0.6.
z_latente = rand(Categorical(modelo.prior.p))
# 2. Muestrear el dato (x) desde la componente elegida
x_dato = rand(modelo.components[z_latente])
return (dato=x_dato, componente_origen=z_latente)
end
muestra = generar_muestra_con_latente(gmm)
println("Dato generado: $(round(muestra.dato, digits=2)), Origen (variable latente z): Componente $(muestra.componente_origen)")
Dato generado: -1.59, Origen (variable latente z): Componente 1
datos_reales = vcat(rand(Normal(-3, 1.0), 200), rand(Normal(4, 1.5), 300))
k = 2
gmm_ajustado = GMM(k, datos_reales)
componentes_del_modelo = Vector{Normal{Float64}}()
for i in 1:k
media = gmm_ajustado.μ[i]
varianza = gmm_ajustado.Σ[i][1]
desviacion_estandar = sqrt(varianza)
componente_actual = Normal(media, desviacion_estandar)
push!(componentes_del_modelo, componente_actual)
end
modelo_flexible = MixtureModel(componentes_del_modelo, gmm_ajustado.w)
modelo_simple = fit(Normal, datos_reales)
histogram(datos_reales, bins=60, normalize=:pdf, label="Datos Reales", color=:gray,
legend=:topleft)
plot!(x -> pdf(modelo_simple, x), -8, 9, label="Modelo Poco Flexible (Normal)", lw=3, color=:orange)
plot!(x -> pdf(modelo_flexible, x), -8, 9, label="Modelo Flexible (GMM k=2)", lw=3, color=:green,
title="Comparando Flexibilidad de Modelos")
[ Info: Initializing GMM, 2 Gaussians diag covariance 1 dimensions using 500 data points
K-means converged with 4 iterations (objv = 750.0533255631107)
┌ Info: K-means with 500 data points using 4 iterations └ 125.0 data points per parameter
Falta: comparar el costo de implementar un GMM vs una distribución Normal a partir de los datos
Modelo de difusión para distribuciones 2D¶
Este experimento en Julia implementa un toy dataset de dos cúmulos gaussianos en 2D y simula un proceso de difusión hacia adelante,difusión inversa y difusión inversa con guía condicional generando un GIFs animados que muestran la degradación progresiva de la estructura original así como la reconstrucción a partir del ruido en la difusión inversa.
# -------------------------------------
# 1. Definición del dataset
# -------------------------------------
Random.seed!(1234)
μ1, μ2 = [-2.0, -2.0], [2.0, 2.0]
σ = 0.5
N = 500
gauss1 = MvNormal(μ1, σ^2 * I(2))
gauss2 = MvNormal(μ2, σ^2 * I(2))
data = hcat(rand(gauss1, N), rand(gauss2, N))
labels = vcat(fill(0, N), fill(1, N));
- Establecemos semilla
1234para reproducibilidad. - Creamos dos gaussianas 2D centradas en μ1 y μ2 con desviación σ.
dataes un arreglo de tamaño2×1000con puntos de ambos cúmulos.
# -------------------------------------
# 2. Parámetros de difusión
# -------------------------------------
T = 500;
β = range(1e-4, 0.02, length=T);
αs = 1 .- β;
αc = cumprod(αs);
sqrt_αc = sqrt.(αc);
sqrt_1m_αc = sqrt.(1 .- αc);
T: número total de pasos de difusión.β: lista lineal de varianzas de ruido entre1e-4y0.02.α: producto acumulado de(1 − β_t), útil para fórmulas teóricas.
Se usa la lista de varianzas β para añadir cada vez más ruido, de forma progresiva en las iteraciones, de manera que en los primeros pasos hay relativamente poco ruido añadiendose.
Difusión hacia adelante¶
El proceso directo es el encargado de corromper progresivamente los datos originales con ruido gaussiano. Este proceso se modela como una cadena de Markov: en cada paso de tiempo se pasa de $x_{t-1}$ a $x_t$ mediante una transición probabilística que depende sólo de $x_{t-1}$.
$$ q(x^{(t)}\mid x^{(t-1)}) = \mathcal{N}\bigl(x^{(t)};\,\sqrt{1-\beta_t}\,x^{(t-1)},\,\beta_t\,I \bigr) $$
donde $\beta_t$ es un parámetro de ruido en el paso $t$. En otras palabras, en cada paso se desplaza ligeramente la imagen añadiendo ruido: la media de $x_t$ es $\sqrt{1-\beta_t}x_{t-1}$ y su covarianza es $\beta_t I$.
No se entrena nada en el proceso directo: los parámetros $\beta_t$ suelen elegirse a mano tal que para algún $T$, $x_T$ sea prácticamente ruido blanco gaussiano. El modelo de difusión entrenará en cambio el proceso inverso necesario para deshacer esta difusión.
# -------------------------------------
# 3. Forward diffusion snapshots
# -------------------------------------
X = copy(data)
snapshots = [copy(X)]
t_interval = 50
for t in 1:T
noise = randn(size(X))
X .= sqrt(αs[t]) .* X .+ sqrt(1-αs[t]) .* noise
if t % t_interval == 0
push!(snapshots, copy(X))
end
end
- Copiamos
dataenXpara preservar los datos originales. - Guardamos el estado inicial (
t=0) ensnapshots. - Cada iteración añade ruido gaussiano de varianza
β[t]. - Cada 10 pasos (cuando
t % interval == 0) almacenamos un snapshot.
$\beta_t$ = varianza de ruido en paso t
Kernel: $$ q\bigl(x^{(t)}\mid x^{(t-1)}\bigr) =\mathcal{N}\bigl(\sqrt{1-\beta_t}\,x^{(t-1)},\,\beta_t I\bigr). $$- √(1−βₜ): atenua la señal.
- √βₜ: escala el ruido nuevo.
$\alpha_t$ = $\prod_{i=1}^{t}$(1−βᵢ)
- Fracción de señal que sobrevive tras t pasos.
- Permite derivar en closed‐form: $$ q\bigl(x^{(t)}\mid x^{(0)}\bigr) =\mathcal{N}\bigl(\sqrt{\alpha_t}\,x^{(0)},\,(1-\alpha_t)I\bigr). $$
lista de incremento lineal de βₜ
- βₜ crece suavemente de ~0 a ~0.02.
- primeros pasos → poco ruido
- últimos pasos → mucho ruido
- βₜ crece suavemente de ~0 a ~0.02.
# Animación snapshots
anim = @animate for (i, Xsnap) in enumerate(snapshots)
t = (i-1) * t_interval
scatter(data[1,:], data[2,:], ms=2, color=:blue, label="Original",
title="Forward t=$t", xlim=(-6,6), ylim=(-6,6), legend=:topright)
scatter!(Xsnap[1,:], Xsnap[2,:], ms=2, color=:red, alpha=0.5, label="Difuso")
end
gif(anim, "forward_diffusion.gif", fps=7)
[ Info: Saved animation to /content/forward_diffusion.gif
@animaterecorre cada snapshot.- En cada frame superpone los datos originales (azul) y los difusos (rojo).
- El título indica el paso
tcorrespondiente.
# -------------------------------------
# 4. Modelo de denoising ε_θ
# -------------------------------------
denoise_model = Chain(
Dense(3, 128, relu),
Dense(128, 128, relu),
Dense(128, 2)
) |> f32
function forward_diffusion(x0, t_idxs)
noise = randn(Float32, size(x0))
a = sqrt_αc[t_idxs]'
b = sqrt_1m_αc[t_idxs]'
xt = a .* x0 .+ b .* noise
return xt, noise
end
# -------------------------------------
# 5. Entrenamiento de ε_θ
# -------------------------------------
opt = Flux.Adam(1e-3)
state_den = Flux.setup(opt, denoise_model)
x0_train = Float32.(data)
epochs = 2000; batch = 128
println("Entrenando denoise_model...")
@showprogress for epoch in 1:epochs
idx = rand(1:size(x0_train, 2), batch)
x_batch = x0_train[:, idx]
t_idxs = rand(1:T, batch)
xt, real_noise = forward_diffusion(x_batch, t_idxs)
t_norm = Float32.(t_idxs' / T)
input = vcat(xt, t_norm)
loss, grads = Flux.withgradient(denoise_model) do m
pred_noise = m(input)
Flux.mse(pred_noise, real_noise)
end
Flux.update!(state_den, denoise_model, grads[1])
end
println("denoise_model entrenado.")
Entrenando denoise_model...
┌ Warning: Layer with Float32 parameters got Float64 input. │ The input will be converted, but any earlier layers may be very slow. │ layer = Dense(3 => 128, relu) # 512 parameters │ summary(x) = "3×128 Matrix{Float64}" └ @ Flux ~/.julia/packages/Flux/uRn8o/src/layers/stateless.jl:60 Progress: 100%|█████████████████████████████████████████| Time: 0:00:32
denoise_model entrenado.
# -------------------------------------
# 6. Entrenamiento del clasificador
# -------------------------------------
emb_clf = Flux.Embedding(T, 32)
clf_head = Chain(
Dense(34, 64, relu), # 2 (datos) + 32 (embedding)
Dense(64, 32, relu),
Dense(32, 2) # Logits, sin softmax
)
model_clf = (embedding=emb_clf, head=clf_head)
opt_c = Flux.Adam(1e-3)
state_clf = Flux.setup(opt_c, model_clf)
println("Entrenando clasificador...")
@showprogress for epoch in 1:1000
loader = DataLoader((data, labels), batchsize=128, shuffle=true)
for (x0b, yb) in loader
x0b = Float32.(x0b)
t_idxs = rand(1:T, length(yb))
xt, _ = forward_diffusion(x0b, t_idxs)
yoh = onehotbatch(yb, [0, 1])
loss, grads = Flux.withgradient(model_clf) do m
te = m.embedding(t_idxs)
inp = vcat(xt, te)
logits = m.head(inp)
Flux.logitcrossentropy(logits, yoh)
end
Flux.update!(state_clf, model_clf, grads[1])
end
end
println("Clasificador entrenado.")
Entrenando clasificador...
Progress: 100%|█████████████████████████████████████████| Time: 0:00:13
Clasificador entrenado.
# -------------------------------------
# 8. Muestreo inverso con guía
# -------------------------------------
using Zygote
using ProgressMeter
using Plots
# Ajustes: reduce num_samples o aumenta snapshot_interval si es muy lento
num_samples = 500 # mismo número para todos los procesos para facilitar la comparación
snapshot_interval = 10 # cada cuántos timesteps guardamos un snapshot
s_guidance = 30.0f0 # fuerza de la guía (s)
gif_fps = 4
# Función que devuelve un arreglo de snapshots (cada snapshot es 2 × num_samples)
# Ahora acepta un argumento opcional `classifier` para evitar referenciar una variable global `model_clf`.
# `classifier` debe ser un NamedTuple o tupla con campos/índices: (embedding=..., head=...)
function sample_reverse_history(model;
guided=false,
classifier=nothing, # <-- pasar model_clf aquí cuando guided=true
s=30.0f0,
num_samples=500,
target_class=0,
snapshot_interval=10)
if guided && classifier === nothing
error("guided=true pero no se pasó `classifier`. Pasa classifier=model_clf (ej. (embedding=..., head=...)).")
end
xt = randn(Float32, 2, num_samples)
histories = Vector{Array{Float32,2}}()
push!(histories, copy(xt)) # t = T (estado inicial ruidoso)
class_idx = target_class + 1
@showprogress for ti in T:-1:1
# normalización temporal en la forma (1, num_samples)
t_norm = fill(Float32(ti / T), 1, num_samples)
model_input = vcat(xt, t_norm) # (2 + 1) × num_samples si ese es el formato esperado
ε_pred = model(model_input) # 2 × num_samples
αt = αs[ti]
αc_t = αc[ti]
βt = β[ti]
term1 = (1.0f0 / sqrt(αt)) .* (xt .- (βt / sqrt(1.0f0 - αc_t)) .* ε_pred)
if guided
# usar el classifier pasado como argumento en lugar de `model_clf` global
emb_layer = classifier.embedding
head_layer = classifier.head
# embeddings del timestep: (emb_dim, num_samples)
te = emb_layer(fill(ti, num_samples))
# sanity check del índice de clase (opcional, solo en la primera iteración)
if ti == T
logits_dummy = head_layer(vcat(xt[:,1:1], te[:,1:1]))
n_classes = size(logits_dummy, 1)
if class_idx < 1 || class_idx > n_classes
error("target_class fuera de rango. Debe ser 0..$(n_classes-1). Recibido: $target_class")
end
end
# inicializar ∇logp con la misma forma que xt
∇logp = zeros(Float32, size(xt))
# gradiente por ejemplo (puede ser lento para num_samples grandes)
for i in 1:num_samples
x_sample = xt[:, i:i] # 2×1
te_i = te[:, i:i] # emb_dim × 1
f = x -> begin
logits = head_layer(vcat(x, te_i)) # 2×1
logp = Flux.logsoftmax(logits; dims=1) # 2×1
return logp[class_idx] # escalar 1×1
end
g_tuple = Zygote.gradient(f, x_sample)
g = g_tuple[1] # 2×1
∇logp[:, i] = vec(g)
end
# aplicar término de guía (como en tu pseudocódigo)
term1 .+= s .* βt .* ∇logp
end
# ruido de transición si no es el último paso
if ti > 1
xt = term1 .+ sqrt(βt) .* randn(Float32, size(xt))
else
xt = term1
end
# guardar snapshot en los intervalos deseados (y guardar también el final)
if (ti % snapshot_interval == 0) || (ti == 1)
push!(histories, copy(xt))
end
end
return histories
end
# --- Generar historiales: sin guía y con guía (clase 0 y 1) ---
println("Generando historial SIN guía...")
hist_ng = sample_reverse_history(denoise_model; guided=false, s=s_guidance,
num_samples=num_samples, target_class=0,
snapshot_interval=snapshot_interval)
# Para las corridas guiadas, PASAMOS explícitamente el classifier (model_clf) como argumento.
# Asegúrate de que `model_clf` exista en el scope (p. ej. (embedding=emb_clf, head=clf_head)).
# Si tu classifier está guardado en una variable distinta, pásala aquí.
if !isdefined(Main, :model_clf)
error("No se encontró `model_clf` en el scope. Define model_clf = (embedding=..., head=...) antes de llamar a las versiones guiadas.")
end
println("Generando historial GUIADA (clase 0)...")
hist_g0 = sample_reverse_history(denoise_model; guided=true, classifier=model_clf,
s=s_guidance, num_samples=num_samples, target_class=0,
snapshot_interval=snapshot_interval)
println("Generando historial GUIADA (clase 1)...")
hist_g1 = sample_reverse_history(denoise_model; guided=true, classifier=model_clf,
s=s_guidance, num_samples=num_samples, target_class=1,
snapshot_interval=snapshot_interval)
# Asegurar que las longitudes coincidan para la animación (usar el mínimo)
n_frames = min(length(hist_ng), length(hist_g0), length(hist_g1))
# Crear animación con 2 subplots por frame:
anim = @animate for k in 1:n_frames
t_current = T - ((k-1) * snapshot_interval) # aproximación del timestep mostrado
# Subplot izquierdo: sin guía
p_left = scatter(data[1,:], data[2,:], ms=2, color=:blue, alpha=0.25, label="Original")
scatter!(p_left, hist_ng[k][1,:], hist_ng[k][2,:], ms=3, color=:gray, alpha=0.5, label="Sin guía")
title!(p_left, "Sin guía t≈$t_current")
xlims!(p_left, -6, 6); ylims!(p_left, -6, 6)
xlabel!(p_left, ""); ylabel!(p_left, "")
plot!(p_left, legend=:topright)
# Subplot derecho: ambas guiadas superpuestas (rojo y verde)
p_right = scatter(data[1,:], data[2,:], ms=2, color=:blue, alpha=0.25, label="Original")
scatter!(p_right, hist_g0[k][1,:], hist_g0[k][2,:], ms=3, color=:red, alpha=0.6, label="Guiada c=0")
scatter!(p_right, hist_g1[k][1,:], hist_g1[k][2,:], ms=3, color=:green, alpha=0.6, label="Guiada c=1")
title!(p_right, "Guiadas t≈$t_current")
xlims!(p_right, -6, 6); ylims!(p_right, -6, 6)
xlabel!(p_right, ""); ylabel!(p_right, "")
plot!(p_right, legend=:topright)
# Combinar los dos subplots en una sola figura (layout 1x2)
plot(p_left, p_right, layout=(1,2), size=(1050,600))
end
# Guardar GIF
gif(anim, "guided_vs_unguided_generation.gif", fps=gif_fps)
println("GIF guardado como guided_vs_unguided_generation.gif")
Generando historial SIN guía...
Progress: 100%|█████████████████████████████████████████| Time: 0:00:00
Generando historial GUIADA (clase 0)...
Progress: 100%|█████████████████████████████████████████| Time: 0:00:11
Generando historial GUIADA (clase 1)...
Progress: 100%|█████████████████████████████████████████| Time: 0:00:11
GIF guardado como guided_vs_unguided_generation.gif
[ Info: Saved animation to /content/guided_vs_unguided_generation.gif
gif(anim, "guided_vs_unguided_generation.gif", fps=gif_fps)
[ Info: Saved animation to /content/guided_vs_unguided_generation.gif
# -------------------------------------
# 9. Evaluación del clasificador
# -------------------------------------
test_t = 250
test_samples = 100
test_data = Float32.(hcat(rand(gauss1, test_samples), rand(gauss2, test_samples)))
test_labels = vcat(fill(0, test_samples), fill(1, test_samples))
t_idxs = fill(test_t, size(test_data, 2))
test_xt, _ = forward_diffusion(test_data, t_idxs)
te = model_clf.embedding(t_idxs)
clf_input = vcat(test_xt, te)
logits = model_clf.head(clf_input)
# Corrección final: Conversión correcta de logits a clases
preds = Flux.onecold(logits, [1, 2]) .- 1 # Convertir a 0 o 1
accuracy = sum(preds .== test_labels) / length(test_labels)
println("Precisión del clasificador en t=$test_t: $(round(accuracy*100, digits=2))%")
Precisión del clasificador en t=250: 96.5%
Referencias¶
- J. Ho, A. Jain, and P. Abbeel, Denoising diffusion probabilistic models, arXiv preprint arxiv:2006.11239, (2020).
- A. J. Jonathan Ho, Denoising diffusion probabilistic models. https://github.com/hojonathanho/diffusion, 2020.
- J. Sohl-Dickstein, E. A. Weiss, N. Maheswaranathan, and S. Ganguli, Deep un- supervised learning using nonequilibrium thermodynamics, CoRR, abs/1503.03585 (2015).