
# Tento skript slouzi k cross-validation testovani CNN modelu

import tensorflow as tf
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D

from keras.models import load_model

import os
import pickle
import fnmatch
import numpy as np
from keras.utils import to_categorical

from keras.callbacks import EarlyStopping


nazev = "ce_power"                              # Název adresáře příslušné galerie dat

DATADIR = "./" + nazev

os.chdir(DATADIR)                               # Prepnuti do adresare



pickle_in = open("X.pickle","rb")               # Otevreni souboru obsahujiciho dataset, nacteni dat do promenne, zavreni souboru
X = pickle.load(pickle_in)
pickle_in.close()

pickle_in = open("y.pickle","rb")
y = pickle.load(pickle_in)
pickle_in.close()

X = X/255.0                                     # Normalizace dat
y = to_categorical(y)                           # One Hot Encoding
y = np.asarray(y)                                       


pickle_in = open("X_val.pickle","rb")           # Stejne tak pro validacni data, resp. data, ktera slouzi k Early Stoppingu (nejsou to tedy data validacni ve smyslu cross VALIDACE)
X_val = pickle.load(pickle_in)
pickle_in.close()

pickle_in = open("y_val.pickle","rb")
y_val = pickle.load(pickle_in)
pickle_in.close()

X_val = X_val/255.0
y_val = to_categorical(y_val)
y_val = np.asarray(y_val)


# mazani stare historie trenovani - tedy tech souboru
for soubor in os.listdir():        #mazu to, neprepisuji, protoze v okamziku kdy snizim pocet folderu "k", tak mi tam zůstanou staré výsledky a to nechci
    if fnmatch.fnmatch(soubor, 'his_*.pickle'):
        os.remove(soubor)




def make_model():                       # Definice modelu CNN
    #model = 1
    model = Sequential()

    model.add(Conv2D(filters=32, kernel_size=(3,3), input_shape=X.shape[1:]))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Conv2D(32, (3, 3), padding='same'))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Conv2D(64, (3, 3), padding='same'))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Flatten())  # this converts our 3D feature maps to 1D feature vectors

    model.add(Dense(512))
    model.add(Dropout(0.2))
    model.add(Dense(32))
    model.add(Dropout(0.2))

    model.add(Dense(2, activation='softmax'))

    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])

    return model




k = 10                                      # pocet folderu, v tomto pripade 10 fold cross validation
num_val_samples = len(y) // k
num_epochs = 60                             # maximalni pocet epoch, pokud by nezafungoval Early Stopping
all_scores = []

pocet_epoch = []

for i in range(k):

    while True:

        model = make_model()                # volani funkce pro vytvoreni modelu

        print('processing fold #', i)
        val_X = X[i * num_val_samples: (i + 1) * num_val_samples]       # vytvoreni validacni a trenovaci mnoziny dat
        val_y = y[i * num_val_samples: (i + 1) * num_val_samples]
        
        partial_X = np.concatenate([X[:i * num_val_samples], X[(i + 1) * num_val_samples:]], axis=0)
        partial_y = np.concatenate([y[:i * num_val_samples], y[(i + 1) * num_val_samples:]], axis=0)
        
        #model = build_model()
        #model.fit(partial_X, partial_y, epochs=num_epochs, batch_size=1, verbose=0)
        #val_mse, val_mae = model.evaluate(val_X, val_y, verbose=0)
        #all_scores.append(val_mae)

        es = EarlyStopping(monitor='val_loss', min_delta = 0.03, mode='auto', verbose=1, patience=2)        # Nastaveni parametru Early Stopping

        his = model.fit(partial_X, partial_y,                                                               # Volani trenovaci metody s nastavenymi parametry trenovani. Metoda vraci histori trenovani, cili prubeh chyby a presnosti behem tohoto procesu
                        batch_size=64,
                        validation_data=(X_val, y_val),
                        epochs=num_epochs,
                        callbacks=[es]) #callbacks=[es]

        if his.history['accuracy'][-1] > 0.8: # pokud se neuci tak znovu vygenerovat model a ucit ho znovu
            
            #ulozit historii
            his_nazev = 'his_'+ str(i) + '.pickle'
            pocet_epoch.append(len(his.history['accuracy'])) #pocet epoch v jednotlivych foldech

            pic_out = open(his_nazev,"wb")
            pickle.dump(his.history, pic_out)
            pic_out.close()
            break



    #nazev = 'model_' + '{}'.format(i) + '.h5'                      # Synteza nazvu pro pripadne ulozeni modelu
    #model.save(str(nazev))                                         # Model neukladam, jen jsem testoval
    
    val_mse, val_mae = model.evaluate(val_X, val_y, verbose=0)      # Validace natrenovaneho modelu
    all_scores.append(val_mae)

    print('val_mse: ', val_mse, 'val_mae: ', val_mae)               # Tisk vysledku validace daneho foldu



# vypocet smerodatne odchylky uspesnosti jednotlivych modelu:
prumer = np.mean(all_scores)
suma = 0
for i in range(len(all_scores)):
    suma += (all_scores[i]-prumer)**2
so = np.sqrt(suma/(len(all_scores)))


# tisk celkovych vysledku
print('all_scores: ', all_scores)
print('avg_score:', np.average(all_scores))
print('smerodatna_odchylka: ', so)
print('pocet_epoch: ', pocet_epoch)



