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.8):
  6. np.random.seed(42)
  7. # Инициализация He для лучшей сходимости
  8. self.W1 = np.random.randn(2, hidden_size) * np.sqrt(2.0/2)
  9. self.b1 = np.zeros((1, hidden_size))
  10. self.W2 = np.random.randn(hidden_size, 1) * np.sqrt(2.0/hidden_size)
  11. self.b2 = np.zeros((1, 1))
  12. self.lr = lr
  13.  
  14. def sigmoid(self, x):
  15. x = np.clip(x, -500, 500)
  16. return 1 / (1 + np.exp(-x))
  17.  
  18. def sigmoid_derivative(self, x):
  19. return x * (1 - x)
  20.  
  21. def forward(self, X):
  22. self.z1 = np.dot(X, self.W1) + self.b1
  23. self.a1 = self.sigmoid(self.z1)
  24. self.z2 = np.dot(self.a1, self.W2) + self.b2
  25. self.a2 = self.sigmoid(self.z2)
  26. return self.a2
  27.  
  28. def backward(self, X, y, output):
  29. m = X.shape[0]
  30. delta2 = (output - y) * self.sigmoid_derivative(output)
  31. dW2 = np.dot(self.a1.T, delta2) / m
  32. db2 = np.sum(delta2, axis=0, keepdims=True) / m
  33.  
  34. delta1 = np.dot(delta2, self.W2.T) * self.sigmoid_derivative(self.a1)
  35. dW1 = np.dot(X.T, delta1) / m
  36. db1 = np.sum(delta1, axis=0, keepdims=True) / m
  37.  
  38. # Сохраняем градиенты
  39. self.dW2 = dW2
  40. self.db2 = db2
  41. self.dW1 = dW1
  42. self.db1 = db1
  43.  
  44. def apply_gradients(self):
  45. self.W2 -= self.lr * self.dW2
  46. self.b2 -= self.lr * self.db2
  47. self.W1 -= self.lr * self.dW1
  48. self.b1 -= self.lr * self.db1
  49.  
  50. def train(self, X, y, epochs=15000, verbose=False):
  51. losses = []
  52. X_inverted = 1 - X
  53. y_inverted = 1 - y
  54.  
  55. for epoch in range(epochs):
  56. # Прямой проход для обычных данных
  57. output_direct = self.forward(X)
  58. loss_direct = np.mean((output_direct - y) ** 2)
  59.  
  60. # Вычисляем градиенты для прямого прохода
  61. self.backward(X, y, output_direct)
  62. # Сохраняем градиенты
  63. dW1_direct = self.dW1.copy()
  64. db1_direct = self.db1.copy()
  65. dW2_direct = self.dW2.copy()
  66. db2_direct = self.db2.copy()
  67.  
  68. # Прямой проход для инвертированных данных
  69. output_inverted = self.forward(X_inverted)
  70. loss_inverted = np.mean((output_inverted - y_inverted) ** 2)
  71.  
  72. # Вычисляем градиенты для инвертированного прохода
  73. self.backward(X_inverted, y_inverted, output_inverted)
  74.  
  75. # Объединяем градиенты с весами
  76. alpha = 0.5 # баланс между прямым и инвертированным обучением
  77. self.dW1 = alpha * dW1_direct + (1 - alpha) * self.dW1
  78. self.db1 = alpha * db1_direct + (1 - alpha) * self.db1
  79. self.dW2 = alpha * dW2_direct + (1 - alpha) * self.dW2
  80. self.db2 = alpha * db2_direct + (1 - alpha) * self.db2
  81.  
  82. # Применяем комбинированные градиенты
  83. self.apply_gradients()
  84.  
  85. # Дополнительный loss для свойства инверсии
  86. consistency_loss = np.mean((output_direct + output_inverted - 1) ** 2)
  87.  
  88. total_loss = loss_direct + loss_inverted + 0.3 * consistency_loss
  89. losses.append(total_loss)
  90.  
  91. if verbose and epoch % 1000 == 0:
  92. print(f"Epoch {epoch}, Total Loss: {total_loss:.6f}")
  93. print(f" Direct: {loss_direct:.6f}, Inverted: {loss_inverted:.6f}, "
  94. f"Consistency: {consistency_loss:.6f}")
  95. print(f" Predictions: {output_direct.flatten()}")
  96.  
  97. return losses
  98.  
  99.  
  100. # ===== ДАННЫЕ =====
  101. X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.float64)
  102. y = np.array([[1], [0], [0], [1]], dtype=np.float64)
  103.  
  104. print("=" * 70)
  105. print("XNOR С КОМБИНИРОВАННЫМИ ГРАДИЕНТАМИ")
  106. print("=" * 70)
  107.  
  108. # Обучение
  109. model = XNORNeuralNetwork(hidden_size=5, lr=0.3)
  110. losses = model.train(X, y, epochs=2000, verbose=True)
  111.  
  112. # ===== ТЕСТИРОВАНИЕ =====
  113. print("\n" + "=" * 70)
  114. print("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ")
  115. print("=" * 70)
  116.  
  117. print(f"\n{'Вход':<12} {'Цель':<8} {'f(x)':<15} {'Предск.':<10} "
  118. f"{'f(~x)':<15} {'Сумма':<15} {'Ансамбль':<15} {'Статус'}")
  119. print("-" * 95)
  120.  
  121. correct_direct = 0
  122. correct_inverted = 0
  123. correct_ensemble = 0
  124.  
  125. for i in range(len(X)):
  126. # Прямой проход
  127. f_x = model.forward(X[i:i+1])[0][0]
  128. pred_direct = 1 if f_x > 0.5 else 0
  129.  
  130. # Инвертированный проход
  131. X_inv_i = 1 - X[i]
  132. f_not_x = model.forward(X_inv_i.reshape(1, -1))[0][0]
  133. pred_inverted_from_not = 1 if (1 - f_not_x) > 0.5 else 0
  134.  
  135. # Ансамблевое предсказание
  136. ensemble_score = (f_x + (1 - f_not_x)) / 2
  137. pred_ensemble = 1 if ensemble_score > 0.5 else 0
  138.  
  139. # Проверка свойства
  140. sum_outputs = f_x + f_not_x
  141.  
  142. # Подсчёт точности
  143. if pred_direct == int(y[i][0]):
  144. correct_direct += 1
  145.  
  146. if pred_inverted_from_not == int(y[i][0]):
  147. correct_inverted += 1
  148.  
  149. if pred_ensemble == int(y[i][0]):
  150. correct_ensemble += 1
  151.  
  152. # Статус
  153. if abs(sum_outputs - 1.0) < 0.1:
  154. consistency = "✓"
  155. elif abs(sum_outputs - 1.0) < 0.2:
  156. consistency = "~"
  157. else:
  158. consistency = "✗"
  159.  
  160. print(f"{str(X[i]):<12} {int(y[i][0]):<8} {f_x:<15.6f} {pred_direct:<10} "
  161. f"{f_not_x:<15.6f} {sum_outputs:<15.6f} {ensemble_score:<15.6f} {consistency}")
  162.  
  163. print(f"\nТочность прямых предсказаний: {correct_direct}/4 ({(correct_direct/4)*100:.0f}%)")
  164. print(f"Точность через инверсию: {correct_inverted}/4 ({(correct_inverted/4)*100:.0f}%)")
  165. print(f"Точность ансамбля: {correct_ensemble}/4 ({(correct_ensemble/4)*100:.0f}%)")
  166.  
  167. # ===== ПРОВЕРКА СВОЙСТВА ИНВЕРСИИ =====
  168. print("\n" + "=" * 70)
  169. print("ПРОВЕРКА СВОЙСТВА: f(x) + f(NOT x) ≈ 1")
  170. print("=" * 70)
  171.  
  172. test_pairs = [
  173. ("f(0,0) + f(1,1)", np.array([[0, 0]]), np.array([[1, 1]])),
  174. ("f(0,1) + f(1,0)", np.array([[0, 1]]), np.array([[1, 0]])),
  175. ]
  176.  
  177. for name, x1, x2 in test_pairs:
  178. f1 = model.forward(x1)[0][0]
  179. f2 = model.forward(x2)[0][0]
  180. print(f"{name} = {f1:.6f} + {f2:.6f} = {f1 + f2:.6f} ≈ 1")
  181.  
  182. # ===== ДЕМОНСТРАЦИЯ УЛУЧШЕНИЯ =====
  183. print("\n" + "=" * 70)
  184. print("ПРЕИМУЩЕСТВО АНСАМБЛЯ")
  185. print("=" * 70)
  186.  
  187. # Тест с добавлением шума
  188. print("\nТест с шумом (добавляем случайный шум к входам):")
  189. noise_level = 0.15
  190. for i in range(len(X)):
  191. noisy_x = X[i] + np.random.randn(2) * noise_level
  192. noisy_x = np.clip(noisy_x, 0, 1).reshape(1, -1)
  193.  
  194. f_x_noisy = model.forward(noisy_x)[0][0]
  195. pred_direct_noisy = 1 if f_x_noisy > 0.5 else 0
  196.  
  197. # Ансамбль с инверсией для зашумлённых данных
  198. noisy_x_inv = 1 - noisy_x
  199. f_not_x_noisy = model.forward(noisy_x_inv)[0][0]
  200. ensemble_score_noisy = (f_x_noisy + (1 - f_not_x_noisy)) / 2
  201. pred_ensemble_noisy = 1 if ensemble_score_noisy > 0.5 else 0
  202.  
  203. print(f"Вход: {X[i]} -> Зашумлённый: [{noisy_x[0][0]:.2f}, {noisy_x[0][1]:.2f}]")
  204. print(f" Прямое: {f_x_noisy:.4f} -> {pred_direct_noisy}, "
  205. f"Ансамбль: {ensemble_score_noisy:.4f} -> {pred_ensemble_noisy}")
Success #stdin #stdout 1.64s 42296KB
stdin
Standard input is empty
stdout
======================================================================
XNOR С КОМБИНИРОВАННЫМИ ГРАДИЕНТАМИ
======================================================================
Epoch 0, Total Loss: 0.842910
  Direct: 0.358701, Inverted: 0.356118, Consistency: 0.426969
  Predictions: [0.20297451 0.193935   0.15522443 0.14101829]
Epoch 1000, Total Loss: 0.500139
  Direct: 0.252447, Inverted: 0.247685, Consistency: 0.000023
  Predictions: [0.50287937 0.51241584 0.49288812 0.49290005]

======================================================================
РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ
======================================================================

Вход         Цель     f(x)            Предск.    f(~x)           Сумма           Ансамбль        Статус
-----------------------------------------------------------------------------------------------
[0. 0.]      1        0.500777        1          0.494956        0.995733        0.502911        ✓
[0. 1.]      0        0.505127        1          0.499791        1.004918        0.502668        ✓
[1. 0.]      0        0.499791        0          0.505127        1.004918        0.497332        ✓
[1. 1.]      1        0.494956        0          0.500777        0.995733        0.497089        ✓

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

======================================================================
ПРОВЕРКА СВОЙСТВА: f(x) + f(NOT x) ≈ 1
======================================================================
f(0,0) + f(1,1) = 0.500777 + 0.494956 = 0.995733 ≈ 1
f(0,1) + f(1,0) = 0.505127 + 0.499791 = 1.004918 ≈ 1

======================================================================
ПРЕИМУЩЕСТВО АНСАМБЛЯ
======================================================================

Тест с шумом (добавляем случайный шум к входам):
Вход: [0. 0.] -> Зашумлённый: [0.00, 0.00]
  Прямое: 0.5008 -> 1, Ансамбль: 0.5029 -> 1
Вход: [0. 1.] -> Зашумлённый: [0.05, 0.86]
  Прямое: 0.5042 -> 1, Ансамбль: 0.5026 -> 1
Вход: [1. 0.] -> Зашумлённый: [0.79, 0.22]
  Прямое: 0.4984 -> 0, Ансамбль: 0.4979 -> 0
Вход: [1. 1.] -> Зашумлённый: [0.97, 1.00]
  Прямое: 0.4952 -> 0, Ансамбль: 0.4973 -> 0