fork download
  1. # your code goes here
  2. import numpy as np
  3.  
  4. class XNORNeuralNetwork:
  5. def __init__(self, hidden_size=4, lr=0.5):
  6. np.random.seed(42)
  7. self.W1 = np.random.randn(2, hidden_size) * 0.5
  8. self.b1 = np.zeros((1, hidden_size))
  9. self.W2 = np.random.randn(hidden_size, 1) * 0.5
  10. self.b2 = np.zeros((1, 1))
  11. self.lr = lr
  12.  
  13. def sigmoid(self, x):
  14. x = np.clip(x, -500, 500)
  15. return 1 / (1 + np.exp(-x))
  16.  
  17. def sigmoid_derivative(self, x):
  18. return x * (1 - x)
  19.  
  20. def forward(self, X):
  21. self.z1 = np.dot(X, self.W1) + self.b1
  22. self.a1 = self.sigmoid(self.z1)
  23. self.z2 = np.dot(self.a1, self.W2) + self.b2
  24. self.a2 = self.sigmoid(self.z2)
  25. return self.a2
  26.  
  27. def train(self, X, y, epochs=10000, verbose=False):
  28. losses = []
  29. X_inverted = 1 - X
  30. y_inverted = 1 - y
  31.  
  32. for epoch in range(epochs):
  33. # ===== ПРЯМОЙ ПРОХОД (сохраняем все промежуточные значения) =====
  34. output_direct = self.forward(X)
  35. a1_direct = self.a1.copy()
  36.  
  37. # ===== ИНВЕРТИРОВАННЫЙ ПРОХОД =====
  38. output_inverted = self.forward(X_inverted)
  39. a1_inverted = self.a1.copy()
  40.  
  41. # ===== ВЫЧИСЛЕНИЕ ГРАДИЕНТОВ ДЛЯ ПРЯМОГО ПУТИ =====
  42. m = X.shape[0]
  43.  
  44. # Ошибка выходного слоя (прямой путь)
  45. delta2_direct = (output_direct - y) * self.sigmoid_derivative(output_direct)
  46. dW2_direct = np.dot(a1_direct.T, delta2_direct) / m
  47. db2_direct = np.sum(delta2_direct, axis=0, keepdims=True) / m
  48.  
  49. # Ошибка скрытого слоя (прямой путь)
  50. delta1_direct = np.dot(delta2_direct, self.W2.T) * self.sigmoid_derivative(a1_direct)
  51. dW1_direct = np.dot(X.T, delta1_direct) / m
  52. db1_direct = np.sum(delta1_direct, axis=0, keepdims=True) / m
  53.  
  54. # ===== ВЫЧИСЛЕНИЕ ГРАДИЕНТОВ ДЛЯ ИНВЕРТИРОВАННОГО ПУТИ =====
  55. delta2_inverted = (output_inverted - y_inverted) * self.sigmoid_derivative(output_inverted)
  56. dW2_inverted = np.dot(a1_inverted.T, delta2_inverted) / m
  57. db2_inverted = np.sum(delta2_inverted, axis=0, keepdims=True) / m
  58.  
  59. delta1_inverted = np.dot(delta2_inverted, self.W2.T) * self.sigmoid_derivative(a1_inverted)
  60. dW1_inverted = np.dot(X_inverted.T, delta1_inverted) / m
  61. db1_inverted = np.sum(delta1_inverted, axis=0, keepdims=True) / m
  62.  
  63. # ===== КОМБИНИРОВАНИЕ И ПРИМЕНЕНИЕ ГРАДИЕНТОВ =====
  64. # Усредняем градиенты от прямого и инвертированного обучения
  65. self.W2 -= self.lr * (dW2_direct + dW2_inverted) / 2
  66. self.b2 -= self.lr * (db2_direct + db2_inverted) / 2
  67. self.W1 -= self.lr * (dW1_direct + dW1_inverted) / 2
  68. self.b1 -= self.lr * (db1_direct + db1_inverted) / 2
  69.  
  70. # ===== ВЫЧИСЛЕНИЕ ОШИБОК ДЛЯ ЛОГИРОВАНИЯ =====
  71. loss_direct = np.mean((output_direct - y) ** 2)
  72. loss_inverted = np.mean((output_inverted - y_inverted) ** 2)
  73. consistency_loss = np.mean((output_direct + output_inverted - 1) ** 2)
  74. total_loss = loss_direct + loss_inverted + 0.3 * consistency_loss
  75.  
  76. losses.append(total_loss)
  77.  
  78. if verbose and epoch % 2000 == 0:
  79. print(f"Epoch {epoch}, Loss: {total_loss:.6f} "
  80. f"[D:{loss_direct:.4f} I:{loss_inverted:.4f} C:{consistency_loss:.4f}]")
  81.  
  82. return losses
  83.  
  84.  
  85. # ===== ДАННЫЕ =====
  86. X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.float64)
  87. y = np.array([[1], [0], [0], [1]], dtype=np.float64)
  88.  
  89. # ===== ОБУЧЕНИЕ =====
  90. print("=" * 70)
  91. print("ОБУЧЕНИЕ XNOR С ПРЯМЫМ И ИНВЕРТИРОВАННЫМ ПРОХОДАМИ")
  92. print("=" * 70)
  93.  
  94. model = XNORNeuralNetwork(hidden_size=4, lr=0.5)
  95. losses = model.train(X, y, epochs=10000, verbose=True)
  96.  
  97. # ===== ТЕСТИРОВАНИЕ =====
  98. print("\n" + "=" * 70)
  99. print("РЕЗУЛЬТАТЫ")
  100. print("=" * 70)
  101.  
  102. print(f"\n{'Вход':<12} {'Цель':<8} {'f(x)':<15} {'Прямое':<10} "
  103. f"{'f(~x)':<15} {'1-f(~x)':<15} {'Ансамбль':<15} {'Сумма':<12} {'✓'}")
  104. print("-" * 110)
  105.  
  106. correct_direct = 0
  107. correct_inv = 0
  108. correct_ensemble = 0
  109.  
  110. for i in range(len(X)):
  111. # Прямой проход
  112. output_direct = model.forward(X[i:i+1])
  113. f_x = output_direct[0][0]
  114. pred_direct = 1 if f_x > 0.5 else 0
  115.  
  116. # Инвертированный проход
  117. X_inv = 1 - X[i:i+1]
  118. output_inverted = model.forward(X_inv)
  119. f_not_x = output_inverted[0][0]
  120.  
  121. # Предсказание через инверсию: y = 1 - f(~x)
  122. pred_from_inv = 1 if (1 - f_not_x) > 0.5 else 0
  123.  
  124. # Ансамбль: среднее между прямым и инвертированным
  125. ensemble_score = (f_x + (1 - f_not_x)) / 2
  126. pred_ensemble = 1 if ensemble_score > 0.5 else 0
  127.  
  128. # Сумма для проверки свойства
  129. sum_check = f_x + f_not_x
  130.  
  131. # Подсчёт точности
  132. if pred_direct == int(y[i][0]):
  133. correct_direct += 1
  134. if pred_from_inv == int(y[i][0]):
  135. correct_inv += 1
  136. if pred_ensemble == int(y[i][0]):
  137. correct_ensemble += 1
  138.  
  139. status = "✓" if abs(sum_check - 1.0) < 0.1 else "✗"
  140.  
  141. print(f"{str(X[i]):<12} {int(y[i][0]):<8} {f_x:<15.6f} {pred_direct:<10} "
  142. f"{f_not_x:<15.6f} {1-f_not_x:<15.6f} {ensemble_score:<15.6f} {sum_check:<12.4f} {status}")
  143.  
  144. print(f"\n{'='*70}")
  145. print(f"Точность прямых предсказаний: {correct_direct}/4")
  146. print(f"Точность через инверсию: {correct_inv}/4")
  147. print(f"Точность ансамбля: {correct_ensemble}/4")
  148.  
  149. # ===== ДЕМОНСТРАЦИЯ СВОЙСТВА =====
  150. print(f"\n{'='*70}")
  151. print("ПРОВЕРКА СВОЙСТВА ИНВЕРСИИ")
  152. print(f"{'='*70}")
  153.  
  154. pairs = [
  155. ("f(0,0) + f(1,1)", np.array([[0, 0]]), np.array([[1, 1]])),
  156. ("f(0,1) + f(1,0)", np.array([[0, 1]]), np.array([[1, 0]])),
  157. ]
  158.  
  159. for name, x1, x2 in pairs:
  160. v1 = model.forward(x1)[0][0]
  161. v2 = model.forward(x2)[0][0]
  162. print(f"{name} = {v1:.6f} + {v2:.6f} = {v1+v2:.6f} ≈ 1")
  163.  
  164. # ===== БОНУС: УСТОЙЧИВОСТЬ К ШУМУ =====
  165. print(f"\n{'='*70}")
  166. print("ТЕСТ НА УСТОЙЧИВОСТЬ К ШУМУ")
  167. print(f"{'='*70}")
  168.  
  169. print("\nДобавляем шум ±0.2 к входам:")
  170. for i in range(len(X)):
  171. noise = np.random.uniform(-0.2, 0.2, 2)
  172. noisy_x = np.clip(X[i] + noise, 0, 1).reshape(1, -1)
  173.  
  174. f_noisy = model.forward(noisy_x)[0][0]
  175. pred_noisy = 1 if f_noisy > 0.5 else 0
  176.  
  177. f_noisy_inv = model.forward(1 - noisy_x)[0][0]
  178. ensemble_noisy = (f_noisy + (1 - f_noisy_inv)) / 2
  179. pred_ens_noisy = 1 if ensemble_noisy > 0.5 else 0
  180.  
  181. print(f" Вход {X[i]} + шум = [{noisy_x[0][0]:.2f}, {noisy_x[0][1]:.2f}]")
  182. print(f" Прямое: {f_noisy:.4f} → {pred_noisy}, "
  183. f"Ансамбль: {ensemble_noisy:.4f} → {pred_ens_noisy}")
Success #stdin #stdout 4.7s 42256KB
stdin
Standard input is empty
stdout
======================================================================
ОБУЧЕНИЕ XNOR С ПРЯМЫМ И ИНВЕРТИРОВАННЫМ ПРОХОДАМИ
======================================================================
Epoch 0, Loss: 0.516354 [D:0.2547 I:0.2557 C:0.0201]
Epoch 2000, Loss: 0.500033 [D:0.2499 I:0.2501 C:0.0000]
Epoch 4000, Loss: 0.500009 [D:0.2500 I:0.2501 C:0.0000]
Epoch 6000, Loss: 0.500003 [D:0.2500 I:0.2500 C:0.0000]
Epoch 8000, Loss: 0.500001 [D:0.2500 I:0.2500 C:0.0000]

======================================================================
РЕЗУЛЬТАТЫ
======================================================================

Вход         Цель     f(x)            Прямое     f(~x)           1-f(~x)         Ансамбль        Сумма        ✓
--------------------------------------------------------------------------------------------------------------
[0. 0.]      1        0.500386        1          0.499621        0.500379        0.500382        1.0000       ✓
[0. 1.]      0        0.500635        1          0.499421        0.500579        0.500607        1.0001       ✓
[1. 0.]      0        0.499421        0          0.500635        0.499365        0.499393        1.0001       ✓
[1. 1.]      1        0.499621        0          0.500386        0.499614        0.499618        1.0000       ✓

======================================================================
Точность прямых предсказаний:    2/4
Точность через инверсию:         2/4
Точность ансамбля:               2/4

======================================================================
ПРОВЕРКА СВОЙСТВА ИНВЕРСИИ
======================================================================
f(0,0) + f(1,1) = 0.500386 + 0.499621 = 1.000007 ≈ 1
f(0,1) + f(1,0) = 0.500635 + 0.499421 = 1.000056 ≈ 1

======================================================================
ТЕСТ НА УСТОЙЧИВОСТЬ К ШУМУ
======================================================================

Добавляем шум ±0.2 к входам:
  Вход [0. 0.] + шум = [0.00, 0.01]
    Прямое: 0.5004 → 1, Ансамбль: 0.5004 → 1
  Вход [0. 1.] + шум = [0.00, 0.92]
    Прямое: 0.5006 → 1, Ансамбль: 0.5006 → 1
  Вход [1. 0.] + шум = [1.00, 0.00]
    Прямое: 0.4994 → 0, Ансамбль: 0.4994 → 0
  Вход [1. 1.] + шум = [0.92, 0.95]
    Прямое: 0.4997 → 0, Ансамбль: 0.4997 → 0