import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Perceptron
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score
from matplotlib import pyplot as plt

#Задание случайного состояния
rs = 42
#Генерируются 500 примеров с 2 признаками, 0 лишними признаками, 2 информативными признаками,
# random_state устанавливается в rs для воспроизводимости данных и n_clusters_per_class устанавливается в 1 для генерации одного кластера классов
X, y = make_classification(n_samples=500, n_features=2, n_redundant=0, n_informative=2, random_state=rs, n_clusters_per_class=1)

# Разделение данных на обучающую и тестовую выборки
#test_size установлен на 0.4, что означает, что 40% данных будет использовано для тестирования.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=rs)

# Инициализация моделей
perceptron = Perceptron()
mlp_10 = MLPClassifier(hidden_layer_sizes=(10,), alpha=0.01, random_state=rs)
mlp_100 = MLPClassifier(hidden_layer_sizes=(100,), alpha=0.01, random_state=rs)

# Обучение моделей
perceptron.fit(X_train, y_train)
mlp_10.fit(X_train, y_train)
mlp_100.fit(X_train, y_train)

# Предсказание на тестовой выборке
y_pred_perceptron = perceptron.predict(X_test)
y_pred_mlp_10 = mlp_10.predict(X_test)
y_pred_mlp_100 = mlp_100.predict(X_test)

# Оценка качества моделей, Оценка точности (accuracy) каждой модели сравнивается с истинными метками классов на тестовой выборке
accuracy_perceptron = accuracy_score(y_test, y_pred_perceptron)
accuracy_mlp_10 = accuracy_score(y_test, y_pred_mlp_10)
accuracy_mlp_100 = accuracy_score(y_test, y_pred_mlp_100)

# Вывод результатов
print("Точность - Perceptron:", accuracy_perceptron)
print("Точность - MLP (10 neurons):", accuracy_mlp_10)
print("Точность - MLP (100 neurons):", accuracy_mlp_100)

# Визуализация данных и границ решения моделей
#Определение интервала значений для графиков, чтобы включить все точки данных и оставить небольшую дополнительную поверхность вокруг границы графика
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                     np.arange(y_min, y_max, 0.02))

#Предсказание класса для каждой точки в сетке графика и изменение формы результата, чтобы соответствовать размерам сетки
Z_perceptron = perceptron.predict(np.c_[xx.ravel(), yy.ravel()])
Z_perceptron = Z_perceptron.reshape(xx.shape)

Z_mlp_10 = mlp_10.predict(np.c_[xx.ravel(), yy.ravel()])
Z_mlp_10 = Z_mlp_10.reshape(xx.shape)

Z_mlp_100 = mlp_100.predict(np.c_[xx.ravel(), yy.ravel()])
Z_mlp_100 = Z_mlp_100.reshape(xx.shape)

#Визуализация данных и границ решения моделей на трех графиках.
# Границы решения отмечены заштрихованными областями, а точки данных на графике окрашены в соответствии с их классами
plt.figure(figsize=(12, 9))
plt.subplot(221)
plt.contourf(xx, yy, Z_perceptron, alpha=0.8)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, edgecolors='k', alpha=0.6)
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolors='k')
plt.title('Персептрон')
plt.xlabel('Признак 1')
plt.ylabel('Признак 2')

plt.subplot(222)
plt.contourf(xx, yy, Z_mlp_10, alpha=0.8)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, edgecolors='k', alpha=0.6)
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolors='k')
plt.title('MLP (10 нейронов)')
plt.xlabel('Признак 1')
plt.ylabel('Признак 2')

plt.subplot(223)
plt.contourf(xx, yy, Z_mlp_100, alpha=0.8)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, edgecolors='k', alpha=0.6)
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, edgecolors='k')
plt.title('MLP (100 нейронов)')
plt.xlabel('Признак 1')
plt.ylabel('Признак 2')

plt.tight_layout()
plt.show()