fork download
  1. # your code goes here
  2. import numpy as np
  3.  
  4. class XNORNeuralNetwork:
  5. def __init__(self, lr=0.5):
  6. np.random.seed(42)
  7. # Ключевой момент: 4 нейрона в скрытом слое для 4 примеров
  8. self.W1 = np.random.randn(2, 4) * 1.0
  9. self.b1 = np.random.randn(1, 4) * 0.5
  10. self.W2 = np.random.randn(4, 1) * 1.0
  11. self.b2 = np.random.randn(1, 1) * 0.5
  12. self.lr = lr
  13.  
  14. def tanh(self, x):
  15. return np.tanh(x)
  16.  
  17. def tanh_derivative(self, x):
  18. return 1 - np.tanh(x) ** 2
  19.  
  20. def sigmoid(self, x):
  21. x = np.clip(x, -500, 500)
  22. return 1 / (1 + np.exp(-x))
  23.  
  24. def sigmoid_derivative(self, x):
  25. return x * (1 - x)
  26.  
  27. def forward(self, X):
  28. self.z1 = np.dot(X, self.W1) + self.b1
  29. self.a1 = self.tanh(self.z1) # tanh вместо сигмоиды
  30.  
  31. self.z2 = np.dot(self.a1, self.W2) + self.b2
  32. self.a2 = self.sigmoid(self.z2)
  33.  
  34. return self.a2
  35.  
  36. def train(self, X, y, epochs=20000, verbose=False):
  37. # Расширяем данные: добавляем инвертированные примеры
  38. X_augmented = np.vstack([X, 1 - X])
  39. y_augmented = np.vstack([y, 1 - y])
  40.  
  41. # Добавляем небольшой шум для разрушения симметрии
  42. noise = np.random.randn(*X_augmented.shape) * 0.01
  43. X_augmented = X_augmented + noise
  44. X_augmented = np.clip(X_augmented, 0, 1)
  45.  
  46. losses = []
  47.  
  48. for epoch in range(epochs):
  49. # Перемешивание
  50. idx = np.random.permutation(len(X_augmented))
  51. X_batch = X_augmented[idx]
  52. y_batch = y_augmented[idx]
  53.  
  54. # Прямой проход
  55. output = self.forward(X_batch)
  56.  
  57. # Бинарная кросс-энтропия
  58. eps = 1e-8
  59. loss = -np.mean(y_batch * np.log(output + eps) +
  60. (1 - y_batch) * np.log(1 - output + eps))
  61. losses.append(loss)
  62.  
  63. # Обратный проход
  64. m = X_batch.shape[0]
  65.  
  66. # Выходной слой
  67. delta2 = (output - y_batch) / m
  68.  
  69. # Скрытый слой
  70. delta1 = np.dot(delta2, self.W2.T) * self.tanh_derivative(self.z1)
  71.  
  72. # Градиенты
  73. dW2 = np.dot(self.a1.T, delta2)
  74. db2 = np.sum(delta2, axis=0, keepdims=True)
  75. dW1 = np.dot(X_batch.T, delta1)
  76. db1 = np.sum(delta1, axis=0, keepdims=True)
  77.  
  78. # L2 регуляризация
  79. lambda_l2 = 0.0001
  80. dW2 += lambda_l2 * self.W2
  81. dW1 += lambda_l2 * self.W1
  82.  
  83. # Обновление с momentum
  84. if not hasattr(self, 'vW1'):
  85. self.vW1 = np.zeros_like(self.W1)
  86. self.vb1 = np.zeros_like(self.b1)
  87. self.vW2 = np.zeros_like(self.W2)
  88. self.vb2 = np.zeros_like(self.b2)
  89.  
  90. momentum = 0.9
  91. self.vW2 = momentum * self.vW2 + self.lr * dW2
  92. self.vb2 = momentum * self.vb2 + self.lr * db2
  93. self.vW1 = momentum * self.vW1 + self.lr * dW1
  94. self.vb1 = momentum * self.vb1 + self.lr * db1
  95.  
  96. self.W2 -= self.vW2
  97. self.b2 -= self.vb2
  98. self.W1 -= self.vW1
  99. self.b1 -= self.vb1
  100.  
  101. # Адаптивное уменьшение learning rate
  102. if epoch % 5000 == 0 and epoch > 0:
  103. self.lr *= 0.7
  104.  
  105. if verbose and epoch % 5000 == 0:
  106. pred = self.forward(X)
  107. acc = np.mean((pred > 0.5) == y)
  108. print(f"Epoch {epoch}, Loss: {loss:.6f}, Accuracy: {acc:.2f}, LR: {self.lr:.4f}")
  109.  
  110. return losses
  111.  
  112.  
  113. # ===== ДАННЫЕ =====
  114. X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=np.float64)
  115. y = np.array([[1], [0], [0], [1]], dtype=np.float64)
  116.  
  117. print("=" * 70)
  118. print("ГАРАНТИРОВАННОЕ ОБУЧЕНИЕ XNOR")
  119. print("=" * 70)
  120.  
  121. # Множественные попытки с разными сидами
  122. best_model = None
  123. best_accuracy = 0
  124. best_predictions = None
  125.  
  126. for attempt in range(10):
  127. np.random.seed(attempt)
  128.  
  129. model = XNORNeuralNetwork(lr=0.1)
  130. losses = model.train(X, y, epochs=2000, verbose=False)
  131.  
  132. predictions = model.forward(X)
  133. accuracy = np.mean((predictions > 0.5) == y)
  134.  
  135. print(f"Попытка {attempt + 1}: точность = {accuracy*100:.0f}%")
  136.  
  137. if accuracy > best_accuracy:
  138. best_accuracy = accuracy
  139. best_model = model
  140. best_predictions = predictions.copy()
  141.  
  142. if accuracy == 1.0:
  143. print("✓ Достигнута 100% точность!")
  144. break
  145.  
  146. model = best_model
  147. print(f"\nЛучшая точность: {best_accuracy*100:.0f}%")
  148.  
  149. # ===== ТЕСТИРОВАНИЕ С ИНВЕРСИЕЙ =====
  150. print("\n" + "=" * 70)
  151. print("ТЕСТИРОВАНИЕ С ИСПОЛЬЗОВАНИЕМ ИНВЕРСИИ")
  152. print("=" * 70)
  153.  
  154. print(f"\n{'Вход':<12} {'Цель':<8} {'f(x)':<12} {'Предск.':<10} "
  155. f"{'f(~x)':<12} {'1-f(~x)':<12} {'Ансамбль':<12} {'Сумма':<10}")
  156. print("-" * 90)
  157.  
  158. correct_direct = 0
  159. correct_inv = 0
  160. correct_ensemble = 0
  161.  
  162. for i in range(len(X)):
  163. # Прямой проход
  164. f_x = model.forward(X[i:i+1])[0][0]
  165. pred_direct = 1 if f_x > 0.5 else 0
  166.  
  167. # Инвертированный проход
  168. X_inv = 1 - X[i:i+1]
  169. f_not_x = model.forward(X_inv)[0][0]
  170.  
  171. # Предсказание через инверсию
  172. pred_from_inv = 1 if (1 - f_not_x) > 0.5 else 0
  173.  
  174. # Ансамбль
  175. ensemble = (f_x + (1 - f_not_x)) / 2
  176. pred_ensemble = 1 if ensemble > 0.5 else 0
  177.  
  178. # Сумма для проверки
  179. sum_check = f_x + f_not_x
  180.  
  181. # Подсчёт точности
  182. target = int(y[i][0])
  183. if pred_direct == target: correct_direct += 1
  184. if pred_from_inv == target: correct_inv += 1
  185. if pred_ensemble == target: correct_ensemble += 1
  186.  
  187. print(f"{str(X[i]):<12} {target:<8} {f_x:<12.6f} {pred_direct:<10} "
  188. f"{f_not_x:<12.6f} {1-f_not_x:<12.6f} {ensemble:<12.6f} {sum_check:<10.6f}")
  189.  
  190. print(f"\n{'='*70}")
  191. print(f"РЕЗУЛЬТАТЫ:")
  192. print(f" Точность прямых предсказаний: {correct_direct}/4")
  193. print(f" Точность через инверсию: {correct_inv}/4")
  194. print(f" Точность ансамбля: {correct_ensemble}/4")
  195.  
  196. # ===== ПРОВЕРКА СВОЙСТВА ИНВЕРСИИ =====
  197. print(f"\n{'='*70}")
  198. print("ПРОВЕРКА СВОЙСТВА f(x) + f(~x) ≈ 1")
  199. print(f"{'='*70}")
  200.  
  201. v00 = model.forward(np.array([[0, 0]]))[0][0]
  202. v11 = model.forward(np.array([[1, 1]]))[0][0]
  203. print(f"f(0,0) + f(1,1) = {v00:.6f} + {v11:.6f} = {v00+v11:.6f}")
  204.  
  205. v01 = model.forward(np.array([[0, 1]]))[0][0]
  206. v10 = model.forward(np.array([[1, 0]]))[0][0]
  207. print(f"f(0,1) + f(1,0) = {v01:.6f} + {v10:.6f} = {v01+v10:.6f}")
  208.  
  209. # ===== ДЕМОНСТРАЦИЯ ПРЕИМУЩЕСТВА АНСАМБЛЯ =====
  210. print(f"\n{'='*70}")
  211. print("ПРЕИМУЩЕСТВО АНСАМБЛЯ ПРИ ШУМЕ")
  212. print(f"{'='*70}")
  213.  
  214. print("\nТест с добавлением шума к входам:")
  215. for i in range(len(X)):
  216. # Добавляем шум
  217. noise = np.random.uniform(-0.25, 0.25, 2)
  218. noisy_x = np.clip(X[i] + noise, 0, 1).reshape(1, -1)
  219.  
  220. # Прямое предсказание
  221. f_noisy = model.forward(noisy_x)[0][0]
  222. pred_noisy = 1 if f_noisy > 0.5 else 0
  223.  
  224. # Ансамбль с инверсией
  225. f_noisy_inv = model.forward(1 - noisy_x)[0][0]
  226. ensemble_noisy = (f_noisy + (1 - f_noisy_inv)) / 2
  227. pred_ens_noisy = 1 if ensemble_noisy > 0.5 else 0
  228.  
  229. target = int(y[i][0])
  230. status_direct = "✓" if pred_noisy == target else "✗"
  231. status_ens = "✓" if pred_ens_noisy == target else "✗"
  232.  
  233. print(f"Вход {X[i]} -> зашумлён [{noisy_x[0][0]:.2f}, {noisy_x[0][1]:.2f}]:")
  234. print(f" Прямое: {f_noisy:.4f} -> {pred_noisy} {status_direct}")
  235. print(f" Ансамбль: {ensemble_noisy:.4f} -> {pred_ens_noisy} {status_ens}")
Success #stdin #stdout 0.99s 41980KB
stdin
Standard input is empty
stdout
======================================================================
ГАРАНТИРОВАННОЕ ОБУЧЕНИЕ XNOR
======================================================================
Попытка 1: точность = 100%
✓ Достигнута 100% точность!

Лучшая точность: 100%

======================================================================
ТЕСТИРОВАНИЕ С ИСПОЛЬЗОВАНИЕМ ИНВЕРСИИ
======================================================================

Вход         Цель     f(x)         Предск.    f(~x)        1-f(~x)      Ансамбль     Сумма     
------------------------------------------------------------------------------------------
[0. 0.]      1        0.508417     1          0.504603     0.495397     0.501907     1.013020  
[0. 1.]      0        0.498243     0          0.489496     0.510504     0.504373     0.987739  
[1. 0.]      0        0.489496     0          0.498243     0.501757     0.495627     0.987739  
[1. 1.]      1        0.504603     1          0.508417     0.491583     0.498093     1.013020  

======================================================================
РЕЗУЛЬТАТЫ:
  Точность прямых предсказаний: 4/4
  Точность через инверсию:      0/4
  Точность ансамбля:            2/4

======================================================================
ПРОВЕРКА СВОЙСТВА f(x) + f(~x) ≈ 1
======================================================================
f(0,0) + f(1,1) = 0.508417 + 0.504603 = 1.013020
f(0,1) + f(1,0) = 0.498243 + 0.489496 = 0.987739

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

Тест с добавлением шума к входам:
Вход [0. 0.] -> зашумлён [0.00, 0.00]:
  Прямое: 0.5084 -> 1 ✓
  Ансамбль: 0.5019 -> 1 ✓
Вход [0. 1.] -> зашумлён [0.00, 0.77]:
  Прямое: 0.5024 -> 1 ✗
  Ансамбль: 0.5020 -> 1 ✗
Вход [1. 0.] -> зашумлён [1.00, 0.04]:
  Прямое: 0.4913 -> 0 ✓
  Ансамбль: 0.4962 -> 0 ✓
Вход [1. 1.] -> зашумлён [1.00, 0.82]:
  Прямое: 0.5049 -> 1 ✓
  Ансамбль: 0.4971 -> 0 ✗