import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn import preprocessing
import sklearn.metrics as met
from termcolor import colored


df = pd.read_csv("iris.csv")

features = df.columns[:4].tolist()

x=df[features]
x.columns = features
y=df["Species"]

scaler = preprocessing.StandardScaler().fit(x)
x =pd.DataFrame(scaler.transform(x))
x.columns = features


x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=0.7, stratify=y)

"""
hidden_layer_sizes  - brojevi neurona u skrivenim slojevima
                      default=100

activation - aktivaciona fja
                      identity    f(x) = x
                      logistic    sigmoidna fja  f(x) = 1 / (1 + exp(-x))
                      tanh        tangens hiperbolicki f(x) = tanh(x)
                      relu        f(x) = max(0, x)

solver - resavac za optimizaciju tezina
                      sgd   stohastickog opadajuceg gradijenta

batch_size - velicina serija: broj instanci u jednom koraku za racunanje gradijenta
             default = 200

learning_rate - stopa ucenja pri azuriranju tezina
              constant   konstantna, zadata sa learning_rate_init
              invscaling  postepeno smanjenje stope ucenja u koraku t, effective_learning_rate = learning_rate_init / pow(t, power_t)
              adaptive    stopa ucenja se ne menja dok se vrednost fje gubitka  smanjuje.
                          Kad se u dva uzastopna koraka gubitak ne smanji za bar vrednost tol,
                          ili se precisnost nad skupom za validaciju (ako je zadato da postoji takav skup)
                          za bar vrednost tol, stopa ucenja se seli sa

learning_rate_init - inicijalna stopa ucenja
              default=0.001
power_t
         default=0.5

max_iter - maksimalan broj iteracija
               default=200

tol - tolerancija optimizacije za gubitak ili preciznost
               default = 1e-4

shuffle - da li izvrsiti mesanje instanci za svaku iteraciju
          default=False

verbose - da li ispisati poruke o progresu na standardni izlaz

early_stopping - da li izvrsiti rano zaustavljanje kada se preciznost nad skupom za validaciju
                 ne povecava, za validaciju se onda uzima 10% trening skupa
                 default=False

validation_fraction - koji deo skupa za treniranje se koristi za validaciju
                      Primenjivo ako je early_stopping=True
                default=0.1
"""

params = [{'solver':['sgd'],
           'learning_rate':['constant', 'invscaling', 'adaptive'],
           'learning_rate_init':[0.01, 0.005, 0.002, 0.001],
            'activation' : ['identity', 'logistic', 'tanh', 'relu' ],
            'hidden_layer_sizes' : [(10,3), (10,10),],
           'max_iter': [500]

           }]


clf = GridSearchCV(MLPClassifier(), params, cv=5)
clf.fit(x_train, y_train)

print("Najbolji parametri:")
print(clf.best_params_)
print()
print("Ocena uspeha po klasifikatorima:")
means = clf.cv_results_['mean_test_score']
stds = clf.cv_results_['std_test_score']
for mean, std, params in zip(means, stds, clf.cv_results_['params']):
    print("%0.3f (+/-%0.03f) za %s" % (mean, std * 2, params))
print()

print("Izvestaj za test skup:")
y_true, y_pred = y_test, clf.predict(x_test)
cnf_matrix = met.confusion_matrix(y_test, y_pred)
print("Matrica konfuzije", cnf_matrix, sep="\n")
print("\n")

accuracy = met.accuracy_score(y_test, y_pred)
print("Preciznost", accuracy)
print("\n")

class_report = met.classification_report(y_test, y_pred, target_names=clf.classes_)
print("Izvestaj klasifikacije", class_report, sep="\n")

print('Broj iteracija: ', clf.best_estimator_.n_iter_)
print('Broj slojeva: ', clf.best_estimator_.n_layers_)
print('Koeficijenti:', clf.best_estimator_.coefs_, sep='\n')
print('Bias:', clf.best_estimator_.intercepts_, sep='\n')
