eXclusive OR (XOR) es una operación lógica booleana muy utilizada en criptografía y en la generación de bits de paridad para la comprobación de errores y la tolerancia a fallos. XOR compara dos bits de entrada y genera un bit de salida. La lógica es sencilla. Si los bits son iguales, el resultado es 0. Si los bits son diferentes, el resultado es
# Funciones auxiliares
sigmoid <- function(x) {
1 / (1 + exp(-x))
}
sigmoid_deriv <- function(x) {
sigmoid(x) * (1 - sigmoid(x))
}
# Valores iniciales
X1 <- 0
X2 <- 1
O1 <- 1 # salida esperada
alpha <- 0.25
# Pesos iniciales según el ejemplo
w1 <- 0.1
w2 <- 0.5
w3 <- -0.7
w4 <- 0.3
w5 <- 0.2
w6 <- 0.4
b1 <- 0
b2 <- 0
b3 <- 0
# Epoca 1
# Forward pass
z1 <- w1*X1 + w3*X2 + b1
a1 <- sigmoid(z1)
z2 <- w2*X1 + w4*X2 + b2
a2 <- sigmoid(z2)
z3 <- w5*a1 + w6*a2 + b3
a3 <- sigmoid(z3)
# Error total
Etotal <- 0.5 * (O1 - a3)^2
cat("Época 1 - Error total:", Etotal, "\n")
## Época 1 - Error total: 0.09095176
# Backpropagation
dE_dz3 <- (a3 - O1) * sigmoid_deriv(z3)
# Gradientes capa de salida
dE_dw5 <- dE_dz3 * a1
dE_dw6 <- dE_dz3 * a2
# Actualización capa de salida
w5_new <- w5 - alpha * dE_dw5
w6_new <- w6 - alpha * dE_dw6
# Gradientes capa oculta
dE_dz1 <- dE_dz3 * w5 * sigmoid_deriv(z1)
dE_dz2 <- dE_dz3 * w6 * sigmoid_deriv(z2)
dE_dw1 <- dE_dz1 * X1
dE_dw3 <- dE_dz1 * X2
dE_dw2 <- dE_dz2 * X1
dE_dw4 <- dE_dz2 * X2
# Actualización capa oculta
w1_new <- w1 - alpha * dE_dw1
w2_new <- w2 - alpha * dE_dw2
w3_new <- w3 - alpha * dE_dw3
w4_new <- w4 - alpha * dE_dw4
cat("\nPesos después de la Época 1:\n")
##
## Pesos después de la Época 1:
print(c(w1_new, w2_new, w3_new, w4_new, w5_new, w6_new))
## [1] 0.1000000 0.5000000 -0.6988435 0.3025502 0.2086538 0.4149817
# Gráfico después de época 1
library(igraph)
##
## Adjuntando el paquete: 'igraph'
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
g <- graph_from_literal(
X1 -+ h1, X1 -+ h2,
X2 -+ h1, X2 -+ h2,
h1 -+ O, h2 -+ O
)
E(g)$label <- c(
round(w1_new,6), round(w2_new,6),
round(w3_new,6), round(w4_new,6),
round(w5_new,6), round(w6_new,6)
)
plot(g,
edge.label = E(g)$label,
vertex.size = 30,
vertex.label.cex = 1,
vertex.color = "red"
)
Se observa en la grafica anterior los pesos actualizados despues de realizar la primera epoca ahora se realizara la segunda epoca con los pesos actualizados
# ÉPOCA 2
# Usamos los nuevos pesos
w1 <- w1_new; w2 <- w2_new; w3 <- w3_new
w4 <- w4_new; w5 <- w5_new; w6 <- w6_new
# Forward pass
z1 <- w1*X1 + w3*X2 + b1
a1 <- sigmoid(z1)
z2 <- w2*X1 + w4*X2 + b2
a2 <- sigmoid(z2)
z3 <- w5*a1 + w6*a2 + b3
a3 <- sigmoid(z3)
# Error total
Etotal2 <- 0.5 * (O1 - a3)^2
cat("\nÉpoca 2 - Error total:", Etotal2, "\n")
##
## Época 2 - Error total: 0.08972707
# Backpropagation
dE_dz3 <- (a3 - O1) * sigmoid_deriv(z3)
# Gradientes capa salida
dE_dw5 <- dE_dz3 * a1
dE_dw6 <- dE_dz3 * a2
# Actualización capa salida
w5_new2 <- w5 - alpha * dE_dw5
w6_new2 <- w6 - alpha * dE_dw6
# Gradientes capa oculta
dE_dz1 <- dE_dz3 * w5 * sigmoid_deriv(z1)
dE_dz2 <- dE_dz3 * w6 * sigmoid_deriv(z2)
dE_dw1 <- dE_dz1 * X1
dE_dw3 <- dE_dz1 * X2
dE_dw2 <- dE_dz2 * X1
dE_dw4 <- dE_dz2 * X2
# Actualización capa oculta
w1_new2 <- w1 - alpha * dE_dw1
w2_new2 <- w2 - alpha * dE_dw2
w3_new2 <- w3 - alpha * dE_dw3
w4_new2 <- w4 - alpha * dE_dw4
cat("\nPesos después de la Época 2:\n")
##
## Pesos después de la Época 2:
print(c(w1_new2, w2_new2, w3_new2, w4_new2, w5_new2, w6_new2))
## [1] 0.1000000 0.5000000 -0.6976468 0.3051725 0.2172406 0.4298520
# Gráfico después de época 2
g2 <- graph_from_literal(
X1 -+ h1, X1 -+ h2,
X2 -+ h1, X2 -+ h2,
h1 -+ O, h2 -+ O
)
E(g2)$label <- c(
round(w1_new2,6), round(w2_new2,6),
round(w3_new2,6), round(w4_new2,6),
round(w5_new2,6), round(w6_new2,6)
)
plot(g2,
edge.label = E(g2)$label,
vertex.size = 30,
vertex.label.cex = 1,
vertex.color = "lightblue"
)
En la grafica se muestra los pesos actualizados despues de la segunda epoca entre mas epocas se realicen mas ira reduciendo el error total de los pesos para obtener el valor deseado que es 1. en las dos espocas podemos observar que el error en la primera era de 0.090 y en la segunda 0.089 redujo un poco.