# 02_wineCheckStop.py

import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense
import os
from tensorflow.python.keras.callbacks import ModelCheckpoint
from tensorflow.python.keras.callbacks import EarlyStopping
import tensorflow as tf
import matplotlib.pyplot as plt
'''
콜백 함수 : 시스템이 어떠한 요구 사항을 충족 했을 때, 스스로 동작하는 함수
ModelCheckPoint : 매 epoch마다 모델을 자동으로 저장해주는 콜백 함수
EarlyStopping : 학습이 진행되는 동안 개선의 여지가 보이지 않으면 강제로 스톱해주는 콜백 함수
'''
'''
파일을 읽어 들여서, 전체에서 15%만 샘플링합니다.
모델을 생성한 다음 학습시킵니다.
매 epoch마다 모델을 학습시켜 업데이트 한 후 해당 모델을 저장합니다.
콜백 함수에 대한 객체를 생성합니다.
fit() 함세의 callbacks 매개 변수에 콜백 객체를 대입해줍니다.
History 객체를 이용하여 비용 함수와 정확도 정보를 확인합니다.
비용 함수와 정확도에 대한 시각화를 수행합니다.
'''


filename = 'wine.csv'
df_wine = pd.read_csv(filename, header=None)

print(df_wine.shape)
print('-' * 30)

# sample : 샘플링을 수행해주는 함수
# frac : fraction
df = df_wine.sample(frac=0.15)
print(df.shape)

data = df.values

table_col = data.shape[1]
y_column = 1
x_column = table_col - 1

x = data[:, 0: x_column]
y = data[:, x_column]

model = Sequential()
model.add(Dense(units=30, input_dim=x_column, activation='relu'))
model.add(Dense(units=12, activation='relu'))
model.add(Dense(units=8, activation='relu'))
model.add(Dense(units=y_column, activation='sigmoid'))

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

model_dir = './model/'  # 모델을 저장할 폴더

if not os.path.exists(model_dir):
    os.mkdir(model_dir)

# epoch : 에포크
# val_loss : 검증용 데이터셋의 손실오차

model_name = model_dir + '{epoch:02d}-{val_loss:.4f}.hdf'

# filepath : 파일을 저장할 경로, monitor : 모니터링할 대상
# loss(학습 셋 오차), val_loss(검증용 셋 오차), acc(학습 정확도), val_acc(검증용 셋 정확도)
# save_best_only : 이전 단계보다 모델이 더 개선되었을 경우에만 저장한다.
mcp = ModelCheckpoint(filepath=model_name, monitor='val_loss', verbose=1,\
                      save_best_only=True)

# patience=100 : 테스트의 오차가 개선되지 않더라도 100번 기다리겠습니다.
es = EarlyStopping(monitor='val_loss', patience=100)

# validation_split : 훈련에 반영하지 않고, 테스트 용으로 사용할 데이터 셋의 비율
#                    실제 학습에 사용되지느는 않고, 매 epock의 끝 자락에서 metrics를 평가하느
# callbacks : 콜백 함수 객체들을 리스트 형식으로 지정해 줍니다.
history = model.fit(x, y, validation_split=0.2, epochs=35, batch_size=500, callbacks=[es, mcp])

# History 객체 : fit() 함수의 반환 객체
val_loss = history.history['val_loss']
print(val_loss)
print('-'*30)

accuracy = history.history['accuracy']
print(accuracy)
print('-'*30)

x_len = np.arange(len(accuracy))

plt.plot(x_len, val_loss, 'o', c='red', markersize=3)

filename = 'wineTest.png'
plt.savefig(filename)
print(filename + '저장됨')

print('finished')