diff --git a/README.md b/README.md index 27cbf37b..30951a92 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,9 @@ Data and code to implement Buscombe et al (2019) optical wave gauging (OWG) usin > Buscombe, Carini, Harrison, Chickadel, and Warrick (in review) Optical wave gauging with deep neural networks. Submitted to Coastal Engineering - Software and data for training deep convolutional neural network models to estimate wave height and wave period from surf zone imagery -This software was tested on Windows 10 with python 3.6, tensorflow 1.11.0 and keras 2.2.4. This software was written by Dr Daniel Buscombe at Northern Arizona University, in the winter of 2018/19. +This software was tested on Windows 10 and Ubuntu Linux with python 3.6, tensorflow 1.11.0 and keras 2.2.4. This software was written by Dr Daniel Buscombe at Northern Arizona University, 2018-2019. THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. IF YOU ENCOUNTER A PROBLEM/BUG OR HAVE A QUESTION OR SUGGESTION, PLEASE USE THE "ISSUES" TAB ON GITHUB. OTHERWISE, THIS SOFTWARE IS UNSUPPORTED. @@ -15,13 +14,13 @@ THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. IF YOU ENCOUNTE ### Folder structure * \conda_env contains yml files for setting up a conda environment -* \conf contains the configuration file with user-definable settings +* \config contains the configuration file with user-definable settings * \train contains files using for training models * \im128 is a file structure that will contain results from model training ## Setting up computing environments -### Install Anaconda pyton distribution +### Install Anaconda python distribution Install the latest version of Anaconda (https://www.anaconda.com/distribution/) @@ -114,10 +113,12 @@ Configuration files are in JSON format, like this: { "samplewise_std_normalization" : true, "samplewise_center" : true, - "input_image_format" : "png" - "input_csv_file" : "IR-training-dataset.csv" - "category" : 'H', - + "input_image_format" : "jpg", + "input_csv_file" : "snap-training-dataset.csv", + "category" : "H", + "prc_lower_withheld": 5, + "prc_upper_withheld": 5, + "horizontal_flip" : false, "vertical_flip" : false, "rotation_range" : 10, @@ -127,11 +128,9 @@ Configuration files are in JSON format, like this: "zoom_range" : 0.2, "fill_mode" : "reflect", - "batch_size" : 64, "img_size" : 128, - "num_epochs" : 100, - "test_size" : 0.33, - "steps_per_epoch" : 100, + "num_epochs" : 5, + "test_size" : 0.4, "dropout_rate" : 0.5, "epsilon" : 0.0001, "min_lr" : 0.0001, @@ -150,8 +149,6 @@ Configuration files are in JSON format, like this: * num_epochs = number of training epochs * test_size = proportion of data set to use for training -* batch_size = number of images to use per model training step -* steps_per_epoch = number of training steps per training epoch * dropout_rate: proportion of neurons to randomly drop in dropout layer * factor: factor by which the learning rate will be reduced. new_lr = lr * factor * epsilon: threshold for measuring the new optimum, to only focus on significant changes. @@ -187,19 +184,15 @@ With height_shift_range=2 possible values are integers [-1, 0, +1], same as with To train models to predict wave height, the following scripts will do so for all combinations of 4 models (MobileNetV1, MobileNetV2, InceptionV3, and InceptionResnet2), and 4 batch sizes (16, 32, 64, and 128 images). ``` -python train_OWG.py +python train_OWG.py -c configfile.json ``` -The following script does the same using generator functions in the training +In the above, ```configfile.json``` is one of the config files in the . Just provide the name of the json file, including the 'json' file extension, not the full path to the file, like this: ``` -python train_OWG_gen.py +python train_OWG.py -c config_IR_H.json ``` -Both scripts provide comparable results and are provided to illustrate two different options for training, mostly for advanced users wishing to modify and adapt the code for other purposes. - -You may notice ```python train_OWG.py``` is marginally faster - The best models are obtained using larger numbers of epochs (say, 100+), but you'll probably want to train them on a GPU (install ```tensorflow-gpu``` instead of ```tensorflow```). To train OWGs for wave period, change the category in the config file to 'T' and run the above again @@ -298,11 +291,6 @@ im128 ---------------batch128 -Then run a script to split large model files to smaller files < 100 MB (so they fit on github) - -``` -python split_model4.py -``` Finally, compile and plot results from all models using @@ -310,18 +298,6 @@ Finally, compile and plot results from all models using python compile_results.py ``` -Data are written out to the Matlab format. For example, for the IR imagery wave height model, the mat file would be: - -``` -IR_all_model_preds_height_128.mat -``` - -and for the IR imagery wave period model, the mat file would be: - -``` -IR_all_model_preds_period_128.mat -``` - ## Operational Mode ### Testing model on a folder of images diff --git a/__pycache__/utils.cpython-36.pyc b/__pycache__/utils.cpython-36.pyc index 73f73575..bf22f21a 100644 Binary files a/__pycache__/utils.cpython-36.pyc and b/__pycache__/utils.cpython-36.pyc differ diff --git a/test_ensemble_OWG_folder.py b/compile_results.py similarity index 81% rename from test_ensemble_OWG_folder.py rename to compile_results.py index 503726ab..e5565170 100644 --- a/test_ensemble_OWG_folder.py +++ b/compile_results.py @@ -1,5 +1,5 @@ -## test_OWG_folder.py -## A script to test a model on independent data +## compile_results.py +## A script to test a model and make plots ## Written by Daniel Buscombe, ## Northern Arizona University ## daniel.buscombe.nau.edu @@ -16,29 +16,31 @@ from imageio import imread from keras.preprocessing.image import ImageDataGenerator from utils import * -import os +import sys, getopt, os + os.environ['CUDA_VISIBLE_DEVICES'] = '-1' ##use CPU from glob import glob import zipfile - from sklearn.model_selection import train_test_split import pandas as pd -from keras.metrics import mean_absolute_error - -def mae_metric(in_gt, in_pred): - return mean_absolute_error(in_gt, in_pred) #============================================================== ## script starts here if __name__ == '__main__': - #image_dir = 'snap_images' - #configfile = 'config_nearshore_H.json' - #configfile = 'config_nearshore_T.json' - - image_dir = 'IR_images' - #configfile = 'config_IR_H.json' - configfile = 'config_IR_T.json' + argv = sys.argv[1:] + try: + opts, args = getopt.getopt(argv,"h:c:") + except getopt.GetoptError: + print('python train_OWG.py -c configfile.json') + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + print('Example usage: python3 train_OWG.py -c conf_IR_h.json') + sys.exit() + elif opt in ("-c"): + configfile = arg + #============================================================== ## user inputs with open(os.getcwd()+os.sep+'config'+os.sep+configfile) as f: @@ -51,13 +53,13 @@ def mae_metric(in_gt, in_pred): samplewise_std_normalization = config["samplewise_std_normalization"] samplewise_center = config["samplewise_center"] num_epochs = int(config["num_epochs"]) ##100 + prc_lower_withheld = config['prc_lower_withheld'] + prc_upper_withheld = config['prc_upper_withheld'] + image_dir = config['image_direc'] base_dir = os.path.normpath(os.getcwd()+os.sep+'train') IMG_SIZE = (im_size, im_size) ##(128, 128) - - prc_lower_withheld = 5 - prc_upper_withheld = 5 # #============================================================== @@ -66,6 +68,8 @@ def mae_metric(in_gt, in_pred): df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))#+".jpg" elif input_csv_file=='IR-training-dataset.csv': df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".png" + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".jpg" df = df.rename(index=str, columns={" H": "H", " T": "T"}) @@ -74,7 +78,10 @@ def mae_metric(in_gt, in_pred): if input_csv_file=='snap-training-dataset.csv': df['time'] = [int(k.split(os.sep)[-1].split('.')[0]) for k in df.path] df = df.sort_values(by='time', axis=0) - + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + df['time'] = [int(k.split(os.sep)[-1].split('.')[0]) for k in df.path] + df = df.sort_values(by='time', axis=0) + ## making subsets of data based on prc_lower_withheld and prc_upper_withheld if (prc_lower_withheld>0) & (prc_upper_withheld>0): up = np.percentile(df[category], 100-prc_upper_withheld) @@ -130,23 +137,16 @@ def mae_metric(in_gt, in_pred): if input_csv_file=='snap-training-dataset.csv': weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' elif input_csv_file=='IR-training-dataset.csv': - weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' + weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.oblique.hdf5' else: if input_csv_file=='snap-training-dataset.csv': weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' elif input_csv_file=='IR-training-dataset.csv': - weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' - if not os.path.isfile(weights_path): #counter==4: - if input_csv_file=='snap-training-dataset.csv': - files = sorted(glob(os.path.dirname(weights_path)+os.sep+'*nearshore*hdf5')) - elif input_csv_file=='IR-training-dataset.csv': - files = sorted(glob(os.path.dirname(weights_path)+os.sep+'*IR*hdf5')) - out_data = b'' - for fn in files: - with open(fn, 'rb') as fp: - out_data += fp.read() - with open(weights_path, 'wb') as fp: - fp.write(out_data) + weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + weights_path=os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.oblique.hdf5' # load json and create model print("Creating model") @@ -173,47 +173,44 @@ def mae_metric(in_gt, in_pred): counter = 1 for model in [1,2,3,4]: for batch_size in [16,32,64,128]: - ## average over batch per model pred_Y = yhat['M'+str(model)+'_B'+str(batch_size)] - #(yhat['M'+str(counter)+'_B16']+yhat['M'+str(counter)+'_B32']+yhat['M'+str(counter)+'_B64']+yhat['M'+str(counter)+'_B128'])/4 pred_Y = np.squeeze(np.asarray(pred_Y)) pred_exY = exyhat['M'+str(model)+'_B'+str(batch_size)] - #(exyhat['M'+str(counter)+'_B16']+exyhat['M'+str(counter)+'_B32']+exyhat['M'+str(counter)+'_B64']+exyhat['M'+str(counter)+'_B128'])/4 pred_exY = np.squeeze(np.asarray(pred_exY)) plt.subplot(4,4,counter) plt.plot(test_Y, pred_Y, 'k.', markersize=3, label = 'predictions') plt.plot(ex_Y, pred_exY, 'bx', markersize=3, label = 'predictions') - if input_csv_file=='snap-training-dataset.csv': + if input_csv_file=='IR-training-dataset.csv': if category=='H': plt.plot([0.5, 2.75], [0.5, 2.75], 'r-', label = 'actual') plt.xlim(0.25,3); plt.ylim(0.25, 3) else: plt.plot([8, 23], [8, 23], 'r-', label = 'actual') plt.xlim(7,24); plt.ylim(7, 24) - elif input_csv_file=='IR-training-dataset.csv': - if category=='H': + else: + if category=='H': plt.plot([0.25, 5.75], [0.25, 5.75], 'r-', label = 'actual') plt.xlim(0,6); plt.ylim(0, 6) else: plt.plot([3, 19], [3, 19], 'r-', label = 'actual') plt.xlim(2,20); plt.ylim(2, 20) if counter==13: - if input_csv_file=='snap-training-dataset.csv': - if category=='H': - plt.xlabel(r'Actual $H_s$ (m)', fontsize=6) - plt.ylabel(r'Predicted $H_s$ (m)', fontsize=6) - elif category=='T': - plt.xlabel(r'Actual $T_p$ (s)', fontsize=6) - plt.ylabel(r'Predicted $T_p$ (s)', fontsize=6) - elif input_csv_file=='IR-training-dataset.csv': + if input_csv_file=='IR-training-dataset.csv': if category=='H': plt.xlabel(r'Actual $H$ (m)', fontsize=6) plt.ylabel(r'Predicted $H$ (m)', fontsize=6) elif category=='T': plt.xlabel(r'Actual $T$ (s)', fontsize=6) - plt.ylabel(r'Predicted $T$ (s)', fontsize=6) + plt.ylabel(r'Predicted $T$ (s)', fontsize=6) + else: + if category=='H': + plt.xlabel(r'Actual $H_s$ (m)', fontsize=6) + plt.ylabel(r'Predicted $H_s$ (m)', fontsize=6) + elif category=='T': + plt.xlabel(r'Actual $T_p$ (s)', fontsize=6) + plt.ylabel(r'Predicted $T_p$ (s)', fontsize=6) rms = np.sqrt(np.nanmean((pred_Y - test_Y)**2)) rsq = np.min(np.corrcoef(test_Y, pred_Y))**2 exrms = np.sqrt(np.nanmean((pred_exY - ex_Y)**2)) @@ -229,6 +226,9 @@ def mae_metric(in_gt, in_pred): plt.savefig('ensemble_allmodels_'+category+'-IR.png', dpi=300, bbox_inches='tight') elif input_csv_file=='snap-training-dataset.csv': plt.savefig('ensemble_allmodels_'+category+'-nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + plt.savefig('ensemble_allmodels_'+category+'-oblique.png', dpi=300, bbox_inches='tight') + plt.close('all') ; del fig diff --git a/config/config_test.json b/config/config_test.json new file mode 100644 index 00000000..70135eb3 --- /dev/null +++ b/config/config_test.json @@ -0,0 +1,12 @@ +{ + "samplewise_std_normalization" : true, + "samplewise_center" : true, + "weights_path" : "im128/res/100epoch/H/model1/batch16/waveheight_weights_model1_16batch.best.nearshore.hdf5", + "input_csv_file" : "train/snap-training-dataset.csv", + "category" : "H", + "im_size" : 128, + "image_direc" : "train/snap_images", + "prc_lower_withheld": 5, + "prc_upper_withheld": 5, + "file_ext": "jpg" +} \ No newline at end of file diff --git a/gen_ts_from_folder.py b/gen_ts_from_folder.py new file mode 100644 index 00000000..bfef1b0f --- /dev/null +++ b/gen_ts_from_folder.py @@ -0,0 +1,84 @@ +## predict_image.py +## A script to test a model on a single image +## Written by Daniel Buscombe, +## Northern Arizona University +## daniel.buscombe.nau.edu + +# import libraries +import sys, getopt, os +import numpy as np +from keras.models import model_from_json +os.environ['CUDA_VISIBLE_DEVICES'] = '-1' ##use CPU +from keras.preprocessing import image +import json + +#============================================================== +## script starts here +if __name__ == '__main__': + + #============================================================== + ## user inputs + argv = sys.argv[1:] + try: + opts, args = getopt.getopt(argv,"h:i:") + except getopt.GetoptError: + print('python predict_image.py -w path/to/folder') + sys.exit(2) + + for opt, arg in opts: + if opt == '-h': + print('Example usage: python predict_image.py -i train/snap_images') + sys.exit() + elif opt in ("-i"): + image_path = arg + + #============================================================== + with open(os.getcwd()+os.sep+'conf'+os.sep+'config_test.json') as f: + config = json.load(f) + + # config variables + im_size = int(config["im_size"]) + category = config["category"] + weights_path = config["weights_path"] + samplewise_std_normalization = config["samplewise_std_normalization"] + samplewise_center = config["samplewise_center"] + file_ext = config["file_ext"] + + IMG_SIZE = (im_size, im_size) + #============================================================== + + # load json and create model + json_file = open(weights_path.replace('.hdf5','.json'), 'r') + loaded_model_json = json_file.read() + json_file.close() + OWG = model_from_json(loaded_model_json) + # load weights into new model + OWG.load_weights(weights_path) + + OWG.compile(optimizer = 'adam', loss = 'mse') + + T = []; V = [] + + for file in glob(image_path+os.sep+'*.'file_ext): + + img = image.load_img(file, target_size=IMG_SIZE) + x = image.img_to_array(img) + x = 0.21*x[:,:,0] + 0.72*x[:,:,1] + 0.07*x[:,:,2] ##rgb to grey + + if samplewise_std_normalization==True: + x = x/np.std(x) + if samplewise_center==True: + x = x - np.mean(x) + x = np.expand_dims(x, axis=0) + x = np.expand_dims(x, axis=3) + + pred_Y = np.squeeze(OWG.predict(x, batch_size = 1, verbose = False)) + print("====================================") + print(category+' = '+str(pred_Y)[:5]) + print("====================================") + t = file.split(os.sep)[-1].split('.')[0] + print("time is ", t) + T.append(t) + V.append(pred_Y) + + diff --git a/p_ex_per_cat.py b/p_ex_per_cat.py new file mode 100644 index 00000000..8679cba8 --- /dev/null +++ b/p_ex_per_cat.py @@ -0,0 +1,142 @@ + +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from skimage.io import imread +from glob import glob +import os, shutil + +from keras.applications.mobilenet import MobileNet +from keras.applications.mobilenetv2 import MobileNetV2 +from keras.applications.inception_v3 import InceptionV3 +from keras.applications.inception_resnet_v2 import InceptionResNetV2 + +from keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, BatchNormalization +from keras.models import Sequential +from keras.preprocessing.image import ImageDataGenerator +from sklearn.model_selection import train_test_split + +import tensorflow as tf + +from sklearn.metrics import precision_score, \ + recall_score, confusion_matrix, accuracy_score, f1_score + + +def flow_from_dataframe(img_data_gen, in_df, path_col, y_col, **dflow_args): + base_dir = os.path.dirname(in_df[path_col].values[0]) + print('## Ignore next message from keras, values are replaced anyways') + df_gen = img_data_gen.flow_from_directory(base_dir, + class_mode = 'sparse', + **dflow_args) + df_gen.filenames = in_df[path_col].values + df_gen.classes = np.stack(in_df[y_col].values) + df_gen.samples = in_df.shape[0] + df_gen.n = in_df.shape[0] + df_gen._set_index_array() + df_gen.directory = '' # since we have the full path + print('Reinserting dataframe: {} images'.format(in_df.shape[0])) + return df_gen + +base_dir = r'C:\Users\ddb265\github_clones\OpticalWaveGauging_DNN\train' + +cat = 'H' +#cat = 'T' + +image_dir = 'snap' +csv_file = 'Nearshore-Training-Oblique-cam2-snap.csv' + + +df = pd.read_csv(os.path.join(base_dir, csv_file)) + +df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".jpg" + +df['exists'] = df['path'].map(os.path.exists) +print(df['exists'].sum(), 'images found of', df.shape[0], 'total') +df = df.rename(index=str, columns={" H": "H", " T": "T"}) +df['time'] = [int(k.split(os.sep)[-1].split('.')[0]) for k in df.path] +df = df.sort_values(by='time', axis=0) + +if cat == 'H': + df.dropna(inplace = True) + df.sample(3) + df['category'] = pd.cut(df[cat], 10) + +elif cat == 'T': + df.dropna(inplace = True) + df.sample(3) + df['category'] = pd.cut(df[cat], 8) + + +X = [] +for k in range(8): + tmp = df.groupby('category').apply(lambda s: s.sample(1)) + for k in tmp['path']: + X.append(imread(k)) + + +if cat=='H': + + fig, m_axs = plt.subplots(8, 8, figsize = (16, 16)) + + counter=1 + for (idx, c_ax) in zip(np.arange(len(X)), m_axs.flatten()): + c_ax.imshow(X[idx], cmap = 'gray') + c_ax.axis('off') + if counter==1: + c_ax.set_title('0.39 - 0.61 m', fontsize=10) + if counter==2: + c_ax.set_title('0.61 - 0.82 m', fontsize=10) + if counter==3: + c_ax.set_title('0.82 - 1.04 m', fontsize=10) + if counter==4: + c_ax.set_title('1.04 - 1.25 m', fontsize=10) + if counter==5: + c_ax.set_title('1.69 - 1.9 m', fontsize=10) + if counter==6: + c_ax.set_title('1.9 - 2.12 m', fontsize=10) + if counter==7: + c_ax.set_title('2.12 - 2.34 m', fontsize=10) + if counter==8: + c_ax.set_title('2.34 - 2.56 m', fontsize=10) + + counter += 1 + + fig.savefig(cat+'_ex_per_cat-oblique.png', dpi = 600) + del fig + plt.close('all') + +else: + + fig, m_axs = plt.subplots(8, 8, figsize = (16, 16)) + + counter=1 + for (idx, c_ax) in zip(np.arange(len(X)), m_axs.flatten()): + c_ax.imshow(X[idx], cmap = 'gray') + c_ax.axis('off') + if counter==1: + c_ax.set_title('7.1 - 9.1 s', fontsize=10) + if counter==2: + c_ax.set_title('9.1 - 11.1 s', fontsize=10) + if counter==3: + c_ax.set_title('11.1 - 13.1 s', fontsize=10) + if counter==4: + c_ax.set_title('13.1 - 15.1 s', fontsize=10) + if counter==5: + c_ax.set_title('15.1 - 17.1 s', fontsize=10) + if counter==6: + c_ax.set_title('17.1 - 19.1 s', fontsize=10) + if counter==7: + c_ax.set_title('19.1 - 21.1 s', fontsize=10) + if counter==8: + c_ax.set_title('21.1 - 23.1 s', fontsize=10) + + counter += 1 + + fig.savefig(cat+'_ex_per_cat-oblique.png', dpi = 600) + del fig + plt.close('all') + + + + + \ No newline at end of file diff --git a/p_feat_weights.py b/p_feat_weights.py new file mode 100644 index 00000000..7e3fb59a --- /dev/null +++ b/p_feat_weights.py @@ -0,0 +1,244 @@ + +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from skimage.io import imread +from glob import glob +import os, shutil + +from keras.applications.inception_resnet_v2 import InceptionResNetV2 + +from keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, BatchNormalization +from keras.preprocessing.image import ImageDataGenerator +from sklearn.model_selection import train_test_split + +from keras.applications.inception_resnet_v2 import preprocess_input +from keras.applications import inception_resnet_v2 +import matplotlib.pyplot as plt + +from keras.preprocessing import image +from keras.models import Model, Sequential +import keras.backend as K + +def rescale(x, NewMin, NewMax): + OldRange = (np.max(x) - np.min(x)) + NewRange = (NewMax - NewMin) + return (((x - np.min(x)) * NewRange) / OldRange) + NewMin + +def deprocess_image(x): + # normalize tensor: center on 0., ensure std is 0.1 + x -= x.mean() + x /= (x.std() + K.epsilon()) + x *= 0.1 + + # clip to [0, 1] + x += 0.5 + x = np.clip(x, 0, 1) + + # convert to RGB array + x *= 255 + if K.image_data_format() == 'channels_first': + x = x.transpose((1, 2, 0)) + x = np.clip(x, 0, 255).astype('uint8') + return x + +def flow_from_dataframe(img_data_gen, in_df, path_col, y_col, **dflow_args): + base_dir = os.path.dirname(in_df[path_col].values[0]) + print('## Ignore next message from keras, values are replaced anyways') + df_gen = img_data_gen.flow_from_directory(base_dir, + class_mode = 'sparse', + **dflow_args) + df_gen.filenames = in_df[path_col].values + df_gen.classes = np.stack(in_df[y_col].values) + df_gen.samples = in_df.shape[0] + df_gen.n = in_df.shape[0] + df_gen._set_index_array() + df_gen.directory = '' # since we have the full path + print('Reinserting dataframe: {} images'.format(in_df.shape[0])) + return df_gen + +base_dir = r'C:\Users\ddb265\github_clones\OpticalWaveGauging_DNN\train' + +IMG_SIZE = (2048, 2448) + +imagetype = 'snap' +image_dir = 'snap' +csv_file = 'Nearshore-Training-Oblique-cam2-snap.csv' + +category = 'T' +weight_path = r'C:\Users\ddb265\github_clones\OWG_DNN_res\200epoch\T\model4\batch16\waveperiod_weights_model4_16batch.best.IR.hdf5' + +# category = 'H' +# weight_path = r'C:\Users\ddb265\github_clones\OWG_DNN_res\200epoch\H\model4\batch16\waveheight_weights_model4_16batch.best.IR.hdf5' + +df = pd.read_csv(os.path.join(base_dir, csv_file)) + +df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".jpg" + +df['exists'] = df['path'].map(os.path.exists) +print(df['exists'].sum(), 'images found of', df.shape[0], 'total') +df = df.rename(index=str, columns={" H": "H", " T": "T"}) + +if category=='H': + #mean = df['waveheight'].mean() + #div = 2*df['waveheight'].std() + #df['zscore'] = df['waveheight'].map(lambda x: (x-mean)/div) + df.dropna(inplace = True) + df.sample(3) + df['category'] = pd.cut(df['H'], 8) + +elif category == 'T': + #mean = df['waveperiod'].mean() + #div = 2*df['waveperiod'].std() + #df['zscore'] = df['waveperiod'].map(lambda x: (x-mean)/div) + df.dropna(inplace = True) + df.sample(3) + df['category'] = pd.cut(df['T'], 8) + + +new_df = df.groupby(['category']).apply(lambda s: s.sample(1)).reset_index(drop = True) +print('New Data Size:', new_df.shape[0], 'Old Size:', df.shape[0]) + +F = [] +for k in new_df['path']: + F.append(k) + + + +core_idg = ImageDataGenerator(samplewise_center=True, + samplewise_std_normalization=True, + horizontal_flip = False, + vertical_flip = False, + height_shift_range = 0, + width_shift_range = 0, + rotation_range = 0, + shear_range = 0, + fill_mode = 'reflect', #'nearest', + zoom_range=0) + +train_gen = flow_from_dataframe(core_idg, new_df, + path_col = 'path', + y_col = category, #'zscore', + target_size = IMG_SIZE, + color_mode = 'grayscale', + batch_size = 8) #10) + + +model1 = InceptionResNetV2(weights='imagenet', include_top=False) +layer_name = 'conv_7b' #'conv_7b_bn' + +# #create a section of the model to output the layer we want +model1 = Model(model1.input, model1.get_layer(layer_name).output) + + +#infiles = glob(r'C:\Users\ddb265\github_clones\NNwaves\im128\res_snap\100epoch\H\model4\batch128\*.hdf5') +# infiles = glob(r'C:\Users\ddb265\github_clones\NNwaves\im128\res_snap\100epoch\T\model4\batch128\*.hdf5') + +# tmp = shutil.copyfile(infiles[0], 'mymodel.hdf5') +# with open(tmp, "ab") as data1, open(infiles[1], "rb") as file2, open(infiles[2], "rb") as file3: + # data1.write(file2.read()) + # data1.write(file3.read()) +# weight_path='mymodel.hdf5' + + +t_x, t_y = next(train_gen) + + +base_model = InceptionResNetV2(input_shape = t_x.shape[1:], + include_top = False, + weights = None) + +owg = Sequential() +owg.add(BatchNormalization(input_shape = (IMG_SIZE[0], IMG_SIZE[1], 1))) +owg.add(base_model) +owg.add(BatchNormalization()) +owg.add(GlobalAveragePooling2D()) +owg.add(Dropout(0.5)) +owg.add(Dense(1, activation = 'linear' )) # linear is what 16bit did +owg.load_weights(weight_path) + +#create a section of the model to output the layer we want +model2 = owg.get_layer('inception_resnet_v2') + +model2 = Model(owg.get_layer('inception_resnet_v2').get_input_at(0), owg.get_layer('inception_resnet_v2').get_layer(layer_name).output) + +counter=0 +c= 0 + +FM1 = [] +FM2 = [] +for file in F: + print(c) + #open and preprocess the cat image + Image = image.load_img(file)#, target_size=image_size) + + Image = np.expand_dims(Image,axis=0) + Image = preprocess_input(Image) + + #get the layer outputs + features1 = model1.predict(Image) + + channel = len(features1) + + Image = image.load_img(file)#, target_size=image_size) + + featureMap = deprocess_image(features1[:,:,:,channel])[0] + FM1.append(featureMap) + + ax=plt.subplot(8,3,counter+1) + plt.imshow(Image, cmap='gray') + ax.set_xticks([]) + ax.set_yticks([]) + if category=='H': + ax.set_ylabel('H = '+str(new_df[category][c])+' m', fontsize=4) + else: + ax.set_ylabel('T = '+str(new_df[category][c])+' s', fontsize=4) + if counter==0: + plt.title('Image', fontsize=6) + + ax=plt.subplot(8,3,counter+2) + plt.imshow(np.log(featureMap/255), vmin=-1, vmax=0, cmap='gray') #np.abs(-np.log(featureMap/np.max(featureMap))), cmap='gray') + ax.set_xticks([]) + ax.set_yticks([]) + if counter==0: + plt.title('Imagenet weights', fontsize=6) + + try: + input = np.expand_dims(imread(file), axis=0) + features2 = model2.predict(np.expand_dims(input.transpose(1,2,0), axis=0)) + except: + input = np.expand_dims(np.expand_dims(imread(file)[:,:,0],axis=0),axis=3) + input = preprocess_input(input) + #get the layer outputs + features2 = model2.predict(input) + + #channel = len(features2) + + featureMap2 = np.max(features2[:,:,:,:][0], axis=2) + featureMap2 = featureMap2/np.max(featureMap2) + featureMap2[featureMap2>.25] = np.min(featureMap2) + featureMap2 = rescale(featureMap2, 0, 1) + + featureMap2 = scipy.signal.medfilt(featureMap2) + + #featureMap2 = deprocess_image(features2[:,:,:,channel])[0] + FM2.append(featureMap2) + + ax=plt.subplot(8,3,counter+3) + #plt.imshow(np.log(featureMap2/255), vmin=-1, vmax=0, cmap='gray') + plt.imshow(featureMap2, vmin=0.1, vmax=0.5, cmap='gray') + ax.set_xticks([]) + ax.set_yticks([]) + if counter==0: + plt.title('Custom weights', fontsize=6) + + counter += 3 + c += 1 + +#plt.savefig('NN_cats_H.png', dpi=1200, bbox_inches='tight') +#plt.savefig('NN_cats_T.png', dpi=1200, bbox_inches='tight') +plt.savefig('oblique_cats_T.png', dpi=1200, bbox_inches='tight') +#plt.savefig('oblique_cats_H.png', dpi=1200, bbox_inches='tight') +plt.close('all') + + diff --git a/split_model4.py b/split_model4.py deleted file mode 100644 index ed20434e..00000000 --- a/split_model4.py +++ /dev/null @@ -1,49 +0,0 @@ -## split_model4.py -## A script to split large model files to smaller files < 100 MB (so they fit on github) -## Written by Daniel Buscombe, -## Northern Arizona University -## daniel.buscombe.nau.edu - -import os -import json - -# load the user configs -with open(os.getcwd()+os.sep+'config'+os.sep+'config_IR_T.json') as f: - config = json.load(f) - -# config variables -im_size = int(config["img_size"]) -epics = int(config["num_epochs"]) ##100 -data = config["category"] ##'H' -input_csv_file = config["input_csv_file"] - -CHUNK_SIZE = int(9.9e+7) - -for batch_size in [16,32,64,128]: - if data=='H': - if input_csv_file=='IR-training-dataset.csv': - infile = os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(epics)+'epoch'+os.sep+data+os.sep+'model4'+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model4_'+str(batch_size)+'batch.best.IR.hdf5' - else: - infile = os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(epics)+'epoch'+os.sep+data+os.sep+'model4'+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model4_'+str(batch_size)+'batch.best.nearshore.hdf5' - elif data=='T': - if input_csv_file=='IR-training-dataset.csv': - infile = os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(epics)+'epoch'+os.sep+data+os.sep+'model4'+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model4_'+str(batch_size)+'batch.best.IR.hdf5' - else: - infile = os.getcwd()+os.sep+'im'+str(im_size)+os.sep+'res'+os.sep+str(epics)+'epoch'+os.sep+data+os.sep+'model4'+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model4_'+str(batch_size)+'batch.best.nearshore.hdf5' - else: - print("Unknown category: "+str(category)) - print("Fix config file, exiting now ...") - import sys - sys.exit() - - file_number = 1 - with open(infile, 'rb') as f: - chunk = f.read(CHUNK_SIZE) - while chunk: - newfile = infile.replace('.hdf5', '.c' + str(file_number)+'.hdf5') - with open(newfile, 'wb') as chunk_file: - chunk_file.write(chunk) - file_number += 1 - chunk = f.read(CHUNK_SIZE) - - os.remove(infile) \ No newline at end of file diff --git a/test_OWG_folder.py b/test_OWG_folder.py new file mode 100644 index 00000000..86b00380 --- /dev/null +++ b/test_OWG_folder.py @@ -0,0 +1,100 @@ +## test_OWG_folder.py +## A script to test a model on independent data +## Written by Daniel Buscombe, +## Northern Arizona University +## daniel.buscombe.nau.edu + +# import libraries +import numpy as np +import json +import matplotlib as mpl +mpl.use('Agg') +import matplotlib.pyplot as plt + +from keras.applications.inception_resnet_v2 import preprocess_input +from keras.models import model_from_json +from imageio import imread +from keras.preprocessing.image import ImageDataGenerator +from utils import * +import os +os.environ['CUDA_VISIBLE_DEVICES'] = '-1' ##use CPU + +from sklearn.model_selection import train_test_split +import pandas as pd + +#============================================================== +## script starts here +if __name__ == '__main__': + + #============================================================== + ## user inputs + with open(os.getcwd()+os.sep+'conf'+os.sep+'config_test.json') as f: + config = json.load(f) + + # config variables + im_size = int(config["im_size"]) + category = config["category"] + input_csv_file = config["input_csv_file"] + image_direc = config["image_direc"] + weights_path = config["weights_path"] + samplewise_std_normalization = config["samplewise_std_normalization"] + samplewise_center = config["samplewise_center"] + + IMG_SIZE = (im_size, im_size) ##(128, 128) + #============================================================== + + # load json and create model + json_file = open(weights_path.replace('.hdf5','.json'), 'r') + loaded_model_json = json_file.read() + json_file.close() + OWG = model_from_json(loaded_model_json) + # load weights into new model + OWG.load_weights(weights_path) + print("Loaded model from disk") + + OWG.compile(optimizer = 'adam', loss = 'mse',metrics = [mae_metric]) + + df = pd.read_csv(input_csv_file) + df['path'] = df['id'].map(lambda x: os.path.join(image_direc,'{}'.format(x))) ##base_dir + df.dropna(inplace = True) + df = df.sort_values(by='time', axis=0) + + train_df, valid_df = train_test_split(df, + test_size = 0.9999, + random_state = 2018, + shuffle=False, + stratify = None) + print('train', train_df.shape[0], 'validation', valid_df.shape[0]) + + im_gen = ImageDataGenerator(samplewise_center=samplewise_center, #True, + samplewise_std_normalization=samplewise_std_normalization, #True, + horizontal_flip = False, + vertical_flip = False, + height_shift_range = 0, + width_shift_range = 0, + rotation_range = 0, + shear_range = 0, + fill_mode = 'reflect', + zoom_range= 0) + + test_X, test_Y = next(gen_from_df(im_gen, + valid_df, + shuffle = False, + path_col = 'path', + y_col = category, #'zscore', + target_size = IMG_SIZE, + color_mode = 'grayscale', + batch_size = len(valid_df))) + + pred_Y = OWG.predict(test_X, batch_size = 1, verbose = True) + fig, ax1 = plt.subplots(1,1, figsize = (6,6)) + ax1.plot(test_Y, pred_Y, 'k.', label = 'predictions') + ax1.plot(test_Y, test_Y, 'r-', label = 'actual') + ax1.legend() + ax1.set_xlabel('Actual H (m)') + ax1.set_ylabel('Predicted H (m)') + plt.savefig('test_model_'+category+'.png', dpi=300, bbox_inches='tight') + plt.close('all') + + + \ No newline at end of file diff --git a/train_OWG.py b/train_OWG.py index bc52738f..baf6a01e 100644 --- a/train_OWG.py +++ b/train_OWG.py @@ -1,5 +1,5 @@ ## train_OWG.py -## A script to train optical wave height gauges for 4 models and 4 batch sizes +## A script to train optical wave gauges for 4 models and 4 batch sizes ## Written by Daniel Buscombe, ## Northern Arizona University ## daniel.buscombe.nau.edu @@ -36,14 +36,7 @@ from keras.preprocessing.image import ImageDataGenerator from utils import * - -from keras.metrics import mean_absolute_error -from scipy.io import savemat - -# mean absolute error -def mae_metric(in_gt, in_pred): - return mean_absolute_error(in_gt, in_pred) - +#from scipy.io import savemat #============================================================== ## script starts here @@ -72,14 +65,12 @@ def mae_metric(in_gt, in_pred): # start time print ("start time - {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))) start = time.time() - print(config) # config variables imsize = int(config["img_size"]) - num_epochs = int(config["num_epochs"]) ##100 + num_epochs = int(config["num_epochs"]) test_size = float(config["test_size"]) - #batch_size = int(config["batch_size"]) height_shift_range = float(config["height_shift_range"]) width_shift_range = float(config["width_shift_range"]) rotation_range = float(config["rotation_range"]) @@ -89,14 +80,13 @@ def mae_metric(in_gt, in_pred): samplewise_center = config["samplewise_center"] shear_range = float(config["shear_range"]) zoom_range = float(config["zoom_range"]) - #steps_per_epoch = int(config["steps_per_epoch"]) dropout_rate = float(config["dropout_rate"]) epsilon = float(config["epsilon"]) min_lr = float(config["min_lr"]) factor = float(config["factor"]) input_image_format = config["input_image_format"] input_csv_file = config["input_csv_file"] - category = config["category"] ##'H' + category = config["category"] fill_mode = config["fill_mode"] prc_lower_withheld = config['prc_lower_withheld'] prc_upper_withheld = config['prc_upper_withheld'] @@ -133,121 +123,97 @@ def mae_metric(in_gt, in_pred): zip_ref = zipfile.ZipFile(destination, 'r') zip_ref.extractall(os.getcwd()+os.sep+'train') zip_ref.close() - os.remove(destination) - - else: #if input_csv_file=='snap-training-dataset.csv': - print('Downloading snap imagery ...') - print('... file is ~0.25GB - takes a while') - #https://drive.google.com/open?id=1TVnuPnrbIhtv0y7BXpiXmJMElf7KnSXx - #url = 'https://drive.google.com/open?id=1QqPUbgXudZSDFXH2VaP30TQYR6PY0acM/view?usp=sharing' - url = 'https://drive.google.com/file/d/1TVnuPnrbIhtv0y7BXpiXmJMElf7KnSXx/view?usp=sharing' - image_dir = 'snap_images' - if not os.path.isdir(os.path.join(base_dir,image_dir)): - file_id = '1TVnuPnrbIhtv0y7BXpiXmJMElf7KnSXx' - #file_id = '1QqPUbgXudZSDFXH2VaP30TQYR6PY0acM' - destination = 'snap_images.zip' - download_file_from_google_drive(file_id, destination) - print('download complete ... unzipping') - zip_ref = zipfile.ZipFile(destination, 'r') - zip_ref.extractall(os.getcwd()+os.sep+'train') - zip_ref.close() - os.remove(destination) + os.remove(destination) + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + image_dir = 'snap' IMG_SIZE = (imsize, imsize) ##(128, 128) - ## loop through 4 different batch sizes - for batch_size in [16,32,64,128]: - print ("[INFO] Batch size = "+str(batch_size)) - archs = {'1':MobileNet, '2':MobileNetV2, '3':InceptionV3, '4':InceptionResNetV2} - counter =1 - #archs = {'4':InceptionResNetV2} - #counter = 4 - ## loop through 4 different base models - for arch in archs: - print("==========================================================") - print("==========================================================") - print("==========================================================") - - print(arch) - - df = pd.read_csv(os.path.join(base_dir, input_csv_file)) - if input_csv_file=='IR-training-dataset.csv': - df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".png" - else: - df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".png" - if category == 'H': - mean = df['H'].mean() - div = df['H'].std() - df['zscore'] = df['H'].map(lambda x: (x-mean)/div) - elif category == 'T': - mean = df['T'].mean() - div = df['T'].std() - df['zscore'] = df['T'].map(lambda x: (x-mean)/div) - else: - print("Unknown category: "+str(category)) - print("Fix config file, exiting now ...") - sys.exit() - - df.dropna(inplace = True) - try: - df = df.sort_values(by='time', axis=0) - except: - df = df.sort_values(by='id', axis=0) - - if category == 'H': - df['category'] = pd.cut(df['H'], 10) - else: - df['category'] = pd.cut(df['T'], 8) - - df['index1'] = df.index + df = pd.read_csv(os.path.join(base_dir, input_csv_file)) + if input_csv_file=='IR-training-dataset.csv': + df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".png" + elif input_csv_file=='snap-training-dataset.csv': + df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x))) + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + df['path'] = df['id'].map(lambda x: os.path.join(base_dir,image_dir,'{}'.format(x)))+".jpg" + + df = df.rename(index=str, columns={" H": "H", " T": "T"}) + + if category == 'H': + mean = df['H'].mean() + div = df['H'].std() + df['zscore'] = df['H'].map(lambda x: (x-mean)/div) + elif category == 'T': + mean = df['T'].mean() + div = df['T'].std() + df['zscore'] = df['T'].map(lambda x: (x-mean)/div) + else: + print("Unknown category: "+str(category)) + print("Fix config file, exiting now ...") + sys.exit() + + df.dropna(inplace = True) + try: + df = df.sort_values(by='time', axis=0) + except: + df = df.sort_values(by='id', axis=0) + + if category == 'H': + df['category'] = pd.cut(df['H'], 10) + else: + df['category'] = pd.cut(df['T'], 8) + + df['index1'] = df.index - if input_csv_file=='IR-training-dataset.csv': - new_df = df.groupby(['category']).apply(lambda x: x.sample(len(df), replace = True)).reset_index(drop = True) - else: - new_df = df.groupby(['category']).apply(lambda x: x.sample(len(df), replace = True)).reset_index(drop = True) + if input_csv_file=='IR-training-dataset.csv': + new_df = df.groupby(['category']).apply(lambda x: x.sample(int(len(df)/2), replace = True)).reset_index(drop = True) + elif input_csv_file=='snap-training-dataset.csv': + new_df = df.groupby(['category']).apply(lambda x: x.sample(int(len(df)/2), replace = True)).reset_index(drop = True) + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + new_df = df.groupby(['category']).apply(lambda x: x.sample(int(len(df)/2), replace = True)).reset_index(drop = True) + + ## making subsets of data based on prc_lower_withheld and prc_upper_withheld + if (prc_lower_withheld>0) & (prc_upper_withheld>0): + up = np.percentile(new_df[category], 100-prc_upper_withheld) + low = np.percentile(new_df[category], prc_lower_withheld) + extreme_df = new_df.loc[(new_df[category] < low) | (new_df[category] > up)] + new_df = new_df.loc[(new_df[category] >= low) & (new_df[category] <= up)] + elif (prc_lower_withheld>0) & (prc_upper_withheld==0): + low = np.percentile(new_df[category], prc_lower_withheld) + extreme_df = new_df.loc[(new_df[category] < low)] + new_df = new_df.loc[(new_df[category] >= low)] + elif (prc_lower_withheld==0) & (prc_upper_withheld>0): + up = np.percentile(new_df[category], 100-prc_upper_withheld) + extreme_df = new_df.loc[(new_df[category] > up)] + new_df = new_df.loc[(new_df[category] <= up)] + + print('New Data Size:', new_df.shape[0], 'Old Size:', df.shape[0]) - ## making subsets of data based on prc_lower_withheld and prc_upper_withheld - if (prc_lower_withheld>0) & (prc_upper_withheld>0): - up = np.percentile(new_df[category], 100-prc_upper_withheld) - low = np.percentile(new_df[category], prc_lower_withheld) - extreme_df = new_df.loc[(new_df[category] < low) | (new_df[category] > up)] - new_df = new_df.loc[(new_df[category] >= low) & (new_df[category] <= up)] - elif (prc_lower_withheld>0) & (prc_upper_withheld==0): - low = np.percentile(new_df[category], prc_lower_withheld) - extreme_df = new_df.loc[(new_df[category] < low)] - new_df = new_df.loc[(new_df[category] >= low)] - elif (prc_lower_withheld==0) & (prc_upper_withheld>0): - up = np.percentile(new_df[category], 100-prc_upper_withheld) - extreme_df = new_df.loc[(new_df[category] > up)] - new_df = new_df.loc[(new_df[category] <= up)] - - print('New Data Size:', new_df.shape[0], 'Old Size:', df.shape[0]) - - train_df, valid_df = train_test_split(new_df, - test_size = test_size, - random_state = 2018, - stratify = new_df['category']) - print('train', train_df.shape[0], 'validation', valid_df.shape[0]) - - im_gen = ImageDataGenerator(samplewise_center=samplewise_center, ##True, - samplewise_std_normalization=samplewise_std_normalization, ##True, - horizontal_flip = horizontal_flip, ##False, - vertical_flip = vertical_flip, ##False, - height_shift_range = height_shift_range, ##0.1, - width_shift_range = width_shift_range, ##0.1, - rotation_range = rotation_range, ##10, - shear_range = shear_range, ##0.05, - fill_mode = fill_mode, ##'reflect', #'nearest', - zoom_range= zoom_range) ##0.2) - - test_X, test_Y = next(gen_from_df(im_gen, - valid_df, - path_col = 'path', - y_col = category, #'zscore', - target_size = IMG_SIZE, - color_mode = 'grayscale', - batch_size = len(valid_df))) - + train_df, valid_df = train_test_split(new_df, + test_size = test_size, + random_state = 2018, + stratify = new_df['category']) + print('train', train_df.shape[0], 'validation', valid_df.shape[0]) + + im_gen = ImageDataGenerator(samplewise_center=samplewise_center, ##True, + samplewise_std_normalization=samplewise_std_normalization, ##True, + horizontal_flip = horizontal_flip, ##False, + vertical_flip = vertical_flip, ##False, + height_shift_range = height_shift_range, ##0.1, + width_shift_range = width_shift_range, ##0.1, + rotation_range = rotation_range, ##10, + shear_range = shear_range, ##0.05, + fill_mode = fill_mode, ##'reflect', #'nearest', + zoom_range= zoom_range) ##0.2) + + test_X, test_Y = next(gen_from_df(im_gen, + valid_df, + path_col = 'path', + y_col = category, #'zscore', + target_size = IMG_SIZE, + color_mode = 'grayscale', + batch_size = len(valid_df))) + # _, test_id = next(gen_from_df(im_gen, # valid_df, # path_col = 'path', @@ -255,16 +221,16 @@ def mae_metric(in_gt, in_pred): # target_size = IMG_SIZE, # color_mode = 'grayscale', # batch_size = len(valid_df))) - - - train_X, train_Y = next(gen_from_df(im_gen, - train_df, - path_col = 'path', - y_col = category, #'zscore', - target_size = IMG_SIZE, - color_mode = 'grayscale', - batch_size = len(train_df))) - + + + train_X, train_Y = next(gen_from_df(im_gen, + train_df, + path_col = 'path', + y_col = category, #'zscore', + target_size = IMG_SIZE, + color_mode = 'grayscale', + batch_size = len(train_df))) + # _, train_id = next(gen_from_df(im_gen, # train_df, # path_col = 'path', @@ -272,22 +238,45 @@ def mae_metric(in_gt, in_pred): # target_size = IMG_SIZE, # color_mode = 'grayscale', # batch_size = len(train_df))) + ex_X, ex_Y = next(gen_from_df(im_gen, + extreme_df, + path_col = 'path', + y_col = category, #'zscore', + target_size = IMG_SIZE, + color_mode = 'grayscale', + batch_size = len(extreme_df))) + + ## loop through 4 different batch sizes + for batch_size in [16,32,64,128]: + print ("[INFO] Batch size = "+str(batch_size)) + archs = {'1':MobileNet, '2':MobileNetV2, '3':InceptionV3, '4':InceptionResNetV2} + counter =1 + ## loop through 4 different base models + for arch in archs: + print("==========================================================") + print("==========================================================") + print("==========================================================") + + print(arch) if category == 'H': if input_csv_file=='IR-training-dataset.csv': weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' - else: - weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' + elif input_csv_file=='snap-training-dataset.csv': + weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.oblique.hdf5' else: if input_csv_file=='IR-training-dataset.csv': weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' - else: - weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' + elif input_csv_file=='snap-training-dataset.csv': + weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.oblique.hdf5' model_checkpoint = ModelCheckpoint(weights_path, monitor='val_loss', verbose=1, save_best_only=True, mode='min', save_weights_only = True) - reduceloss_plat = ReduceLROnPlateau(monitor='val_loss', factor=factor, patience=10, verbose=1, mode='auto', epsilon=epsilon, cooldown=5, min_lr=min_lr) ##0.0001, 0.8 earlystop = EarlyStopping(monitor="val_loss", mode="min", patience=15) callbacks_list = [model_checkpoint, earlystop, reduceloss_plat] @@ -320,21 +309,11 @@ def mae_metric(in_gt, in_pred): json_file.write(model_json) print ("[INFO] Testing optical wave gauge") - - #print("Mean: "+str(mean)) - #print("Stdev: "+str(div)) - - # the model predicts zscores - recover value using pop. mean and standard dev. + pred_Y = np.squeeze(OWG.predict(test_X, batch_size = batch_size, verbose = True)) + + # or if the model predicts zscores - recover value using pop. mean and standard dev. ##div*OWG.predict(test_X, batch_size = batch_size, verbose = True)+mean - - ex_X, ex_Y = next(gen_from_df(im_gen, - extreme_df, - path_col = 'path', - y_col = category, #'zscore', - target_size = IMG_SIZE, - color_mode = 'grayscale', - batch_size = len(extreme_df))) pred_extreme_Y = np.squeeze(OWG.predict(ex_X, batch_size = batch_size, verbose = True)) @@ -351,34 +330,38 @@ def mae_metric(in_gt, in_pred): ax1.set_ylabel('Predicted H (m)') if input_csv_file=='IR-training-dataset.csv': plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='snap-training-dataset.csv': + plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_oblique.png', dpi=300, bbox_inches='tight') else: ax1.set_xlabel('Actual T (s)') ax1.set_ylabel('Predicted T (s)') if input_csv_file=='IR-training-dataset.csv': plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='snap-training-dataset.csv': + plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_oblique.png', dpi=300, bbox_inches='tight') plt.close('all') - out ={} - out['rms'] = np.sqrt(np.nanmean((pred_Y - test_Y)**2)) - out['rsq'] = np.min(np.corrcoef(test_Y, pred_Y))**2 - out['rms_ex'] = np.sqrt(np.nanmean((ex_Y - pred_extreme_Y)**2)) - out['rsq_ex'] = np.min(np.corrcoef(ex_Y, pred_extreme_Y))**2 - out['y'] = test_Y - out['yhat'] = pred_Y - out['yhat_extreme'] = pred_extreme_Y - out['test_X'] = test_X - out['test_Y'] = test_Y - out['extreme_X'] = ex_X - out['extreme_Y'] = ex_Y - out['history_train_mae'] = history.history['mae_metric'] - out['history_val_mae'] = history.history['val_mae_metric'] - out['history_train_loss'] = history.history['loss'] - out['history_val_loss'] = history.history['val_loss'] + # out ={} + # out['rms'] = np.sqrt(np.nanmean((pred_Y - test_Y)**2)) + # out['rsq'] = np.min(np.corrcoef(test_Y, pred_Y))**2 + # out['rms_ex'] = np.sqrt(np.nanmean((ex_Y - pred_extreme_Y)**2)) + # out['rsq_ex'] = np.min(np.corrcoef(ex_Y, pred_extreme_Y))**2 + # out['y'] = test_Y + # out['yhat'] = pred_Y + # out['yhat_extreme'] = pred_extreme_Y + # out['test_X'] = test_X + # out['test_Y'] = test_Y + # out['extreme_X'] = ex_X + # out['extreme_Y'] = ex_Y + # out['history_train_mae'] = history.history['mae_metric'] + # out['history_val_mae'] = history.history['val_mae_metric'] + # out['history_train_loss'] = history.history['loss'] + # out['history_val_loss'] = history.history['val_loss'] # if input_csv_file=='IR-training-dataset.csv': # savemat(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+category+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_IR.mat', out, do_compression=True) @@ -404,9 +387,13 @@ def mae_metric(in_gt, in_pred): plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['train', 'test'], loc='upper left') - - outfile = os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+category+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_'+category+'_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'_loss_acc_curves.png' - + + if input_csv_file=='IR-training-dataset.csv': + outfile = os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+category+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_'+category+'_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'_loss_acc_curves_IR.png' + elif input_csv_file=='snap-training-dataset.csv': + outfile = os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+category+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_'+category+'_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'_loss_acc_curves_nearshore.png' + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + outfile = os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+category+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_'+category+'_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'_loss_acc_curves_oblique.png' plt.savefig(outfile, dpi=300, bbox_inches='tight') plt.close('all') print('Made '+outfile) @@ -425,14 +412,18 @@ def mae_metric(in_gt, in_pred): if category == 'H': if input_csv_file=='IR-training-dataset.csv': fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='snap-training-dataset.csv': + fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_oblique.png', dpi=300, bbox_inches='tight') else: if input_csv_file=='IR-training-dataset.csv': fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: + elif input_csv_file=='snap-training-dataset.csv': fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') - + elif input_csv_file=='Nearshore-Training-Oblique-cam2-snap.csv': + fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'batch_oblique.png', dpi=300, bbox_inches='tight') + counter += 1 diff --git a/train_OWG_gen.py b/train_OWG_gen.py deleted file mode 100644 index ddf4ba32..00000000 --- a/train_OWG_gen.py +++ /dev/null @@ -1,368 +0,0 @@ -## train_OWG.py -## A script to train optical wave height gauges for 4 models and 4 batch sizes -## Written by Daniel Buscombe, -## Northern Arizona University -## daniel.buscombe.nau.edu - -## GPU with 8+GB memory recommended - -# import libraries -import numpy as np -import pandas as pd - -import matplotlib as mpl -mpl.use('Agg') -import matplotlib.pyplot as plt - -import zipfile -import os -import json -import time, datetime -from glob import glob -from keras.applications.mobilenet import MobileNet -from keras.applications.mobilenet_v2 import MobileNetV2 -from keras.applications.inception_v3 import InceptionV3 -from keras.applications.inception_resnet_v2 import InceptionResNetV2 - -from keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, BatchNormalization -from keras.models import Sequential -from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau -from sklearn.model_selection import train_test_split -from keras.preprocessing.image import ImageDataGenerator - -from utils import * - -from keras.metrics import mean_absolute_error - -## mean absolute error -#def mae_metric(in_gt, in_pred): -# return mean_absolute_error(div*in_gt, div*in_pred) - -## mean absolute error -#def mae_metric(in_gt, in_pred): -# return mean_absolute_error(div*in_gt+mean, div*in_pred+mean) - -# mean absolute error -def mae_metric(in_gt, in_pred): - return mean_absolute_error(in_gt, in_pred) - - -#============================================================== -## script starts here -if __name__ == '__main__': - - # start time - print ("start time - {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))) - start = time.time() - - # load the user configs - with open(os.getcwd()+os.sep+'conf'+os.sep+'config.json') as f: - config = json.load(f) - - print(config) - # config variables - imsize = int(config["img_size"]) - num_epochs = int(config["num_epochs"]) ##100 - test_size = float(config["test_size"]) - batch_size = int(config["batch_size"]) - height_shift_range = float(config["height_shift_range"]) - width_shift_range = float(config["width_shift_range"]) - rotation_range = float(config["rotation_range"]) - samplewise_std_normalization = config["samplewise_std_normalization"] - horizontal_flip = config["horizontal_flip"] - vertical_flip = config["vertical_flip"] - samplewise_center = config["samplewise_center"] - shear_range = float(config["shear_range"]) - zoom_range = float(config["zoom_range"]) - steps_per_epoch = int(config["steps_per_epoch"]) - dropout_rate = float(config["dropout_rate"]) - epsilon = float(config["epsilon"]) - min_lr = float(config["min_lr"]) - factor = float(config["factor"]) - input_image_format = config["input_image_format"] - input_csv_file = config["input_csv_file"] - category = config["category"] ##'H' - fill_mode = config["fill_mode"] - - base_dir = os.path.normpath(os.getcwd()+os.sep+'train') - - ## download files and unzip - if input_csv_file=='IR-training-dataset.csv': - print('Downloading IR imagery ...') - print('... file is ~2GB - takes a while') - url = 'https://drive.google.com/file/d/1ljkY4akD8O8ShLyOywTnyQl7GsH3KIJ3/view?usp=sharing' - image_dir = 'IR_images' - if not os.path.isdir(os.path.join(base_dir,image_dir)): - file_id = '1ljkY4akD8O8ShLyOywTnyQl7GsH3KIJ3' - destination = 'IR_images.zip' - download_file_from_google_drive(file_id, destination) - print('download complete ... unzipping') - zip_ref = zipfile.ZipFile(destination, 'r') - zip_ref.extractall(os.getcwd()+os.sep+'train') - zip_ref.close() - os.remove(destination) - - elif input_csv_file=='nearshore-training-dataset.csv': - print('Downloading nearshore imagery ...') - print('... file is ~1GB - takes a while') - url = 'https://drive.google.com/file/d/1QqPUbgXudZSDFXH2VaP30TQYR6PY0acM/view?usp=sharing' - image_dir = 'nearshore_images' - if not os.path.isdir(os.path.join(base_dir,image_dir)): - file_id = '1QqPUbgXudZSDFXH2VaP30TQYR6PY0acM' - destination = 'nearshore_images.zip' - download_file_from_google_drive(file_id, destination) - print('download complete ... unzipping') - zip_ref = zipfile.ZipFile(destination, 'r') - zip_ref.extractall(os.getcwd()+os.sep+'train') - zip_ref.close() - os.remove(destination) - - else: #if input_csv_file=='snap-training-dataset.csv': - print('Downloading snap imagery ...') - print('... file is ~0.25GB - takes a while') - url = 'https://drive.google.com/file/d/1TVnuPnrbIhtv0y7BXpiXmJMElf7KnSXx/view?usp=sharing' - image_dir = 'snap_images' - if not os.path.isdir(os.path.join(base_dir,image_dir)): - file_id = '1TVnuPnrbIhtv0y7BXpiXmJMElf7KnSXx' - destination = 'snap_images.zip' - download_file_from_google_drive(file_id, destination) - print('download complete ... unzipping') - zip_ref = zipfile.ZipFile(destination, 'r') - zip_ref.extractall(os.getcwd()+os.sep+'train') - zip_ref.close() - os.remove(destination) - - IMG_SIZE = (imsize, imsize) ##(128, 128) - - ## loop through 4 different batch sizes - for batch_size in [16,32,64,128]: - print ("[INFO] Batch size = "+str(batch_size)) - - archs = {'1':MobileNet, '2':MobileNetV2, '3':InceptionV3, '4':InceptionResNetV2} - counter =1 - - ## loop through 4 different base models - for arch in archs: - print("==========================================================") - print("==========================================================") - print("==========================================================") - - print(arch) - - df = pd.read_csv(os.path.join(base_dir, input_csv_file)) ##'training-dataset.csv')) - - df['path'] = df['id'].map(lambda x: os.path.join(base_dir, - image_dir, - '{}'.format(x))) - - df['exists'] = df['path'].map(os.path.exists) - print(df['exists'].sum(), 'images found of', df.shape[0], 'total') - - if category == 'H': - mean = df['H'].mean() - div = df['H'].std() - df['zscore'] = df['H'].map(lambda x: (x-mean)/div) - elif category == 'T': - mean = df['T'].mean() - div = df['T'].std() - df['zscore'] = df['T'].map(lambda x: (x-mean)/div) - else: - print("Unknown category: "+str(category)) - print("Fix config file, exiting now ...") - import sys - sys.exit() - - df.dropna(inplace = True) - - if category == 'H': - df['category'] = pd.cut(df['H'], 10) - else: - df['category'] = pd.cut(df['T'], 8) - - if input_csv_file=='IR-training-dataset.csv': - new_df = df.groupby(['category']).apply(lambda x: x.sample(2000, replace = True)).reset_index(drop = True) - else: - new_df = df.groupby(['category']).apply(lambda x: x.sample(400, replace = True)).reset_index(drop = True) - - print('New Data Size:', new_df.shape[0], 'Old Size:', df.shape[0]) - - train_df, valid_df = train_test_split(new_df, - test_size = test_size, #0.33, - random_state = 2018, - stratify = new_df['category']) - print('train', train_df.shape[0], 'validation', valid_df.shape[0]) - - - im_gen = ImageDataGenerator(samplewise_center=samplewise_center, ##True, - samplewise_std_normalization=samplewise_std_normalization, ##True, - horizontal_flip = horizontal_flip, ##False, - vertical_flip = vertical_flip, ##False, - height_shift_range = height_shift_range, ##0.1, - width_shift_range = width_shift_range, ##0.1, - rotation_range = rotation_range, ##10, - shear_range = shear_range, ##0.05, - fill_mode = fill_mode, ##'reflect', #'nearest', - zoom_range= zoom_range) ##0.2) - - train_gen = gen_from_df(im_gen, train_df, - path_col = 'path', - y_col = category, #'zscore', - target_size = IMG_SIZE, - color_mode = 'grayscale', - batch_size = batch_size) ##64) - - valid_gen = gen_from_df(im_gen, valid_df, - path_col = 'path', - y_col = category, #'zscore', - target_size = IMG_SIZE, - color_mode = 'grayscale', - batch_size = batch_size) ##64) - - test_X, test_Y = next(gen_from_df(im_gen, - valid_df, - path_col = 'path', - y_col = category, #'zscore', - target_size = IMG_SIZE, - color_mode = 'grayscale', - batch_size = len(df))) ##1000 - - - t_x, t_y = next(train_gen) - - train_gen.batch_size = batch_size - - if category == 'H': - if input_csv_file=='IR-training-dataset.csv': - weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' - else: - weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveheight_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' - else: - if input_csv_file=='IR-training-dataset.csv': - weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.IR.hdf5' - else: - weights_path=os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'waveperiod_weights_model'+str(counter)+'_'+str(batch_size)+'batch.best.nearshore.hdf5' - - model_checkpoint = ModelCheckpoint(weights_path, monitor='val_loss', verbose=1, - save_best_only=True, mode='min', save_weights_only = True) - - - reduceloss_plat = ReduceLROnPlateau(monitor='val_loss', factor=factor, patience=10, verbose=1, mode='auto', epsilon=epsilon, cooldown=5, min_lr=min_lr) ##0.0001, 0.8 - earlystop = EarlyStopping(monitor="val_loss", mode="min", patience=15) - callbacks_list = [model_checkpoint, earlystop, reduceloss_plat] - - base_model = archs[arch](input_shape = t_x.shape[1:], include_top = False, weights = None) - - print ("[INFO] Training optical wave gauge") - - OWG = Sequential() - OWG.add(BatchNormalization(input_shape = t_x.shape[1:])) - OWG.add(base_model) - OWG.add(BatchNormalization()) - OWG.add(GlobalAveragePooling2D()) - OWG.add(Dropout(dropout_rate)) ##0.5 - OWG.add(Dense(1, activation = 'linear' )) - - OWG.compile(optimizer = 'adam', loss = 'mse', - metrics = [mae_metric]) - - OWG.summary() - - # train the model - history = OWG.fit_generator(train_gen, validation_data = (test_X, test_Y), - epochs = num_epochs, steps_per_epoch= steps_per_epoch, ##100, - callbacks = callbacks_list) - - # load the new model weights - OWG.load_weights(weights_path) - - # serialize model to JSON - model_json = OWG.to_json() - with open(weights_path.replace('.hdf5','.json'), "w") as json_file: - json_file.write(model_json) - - print ("[INFO] Testing optical wave gauge") - - print("Mean: "+str(mean)) - print("Stdev: "+str(div)) - - # the model predicts zscores - recover value using pop. mean and standard dev. - pred_Y = OWG.predict(test_X, batch_size = train_gen.batch_size, verbose = True) ##div*OWG.predict(test_X, batch_size = train_gen.batch_size, verbose = True)+mean - #test_Y = div*test_Y+mean - - print ("[INFO] Creating plots ") - - fig, ax1 = plt.subplots(1,1, figsize = (6,6)) - ax1.plot(test_Y, pred_Y, 'k.', label = 'predictions') - ax1.plot(test_Y, test_Y, 'r-', label = 'actual') - ax1.legend() - if category == 'H': - ax1.set_xlabel('Actual H (m)') - ax1.set_ylabel('Predicted H (m)') - if input_csv_file=='IR-training-dataset.csv': - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') - else: - ax1.set_xlabel('Actual T (s)') - ax1.set_ylabel('Predicted T (s)') - if input_csv_file=='IR-training-dataset.csv': - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') - - plt.close('all') - - # list all data in history - print(history.history.keys()) - - plt.subplot(121) - # summarize history for accuracy - plt.plot(history.history['mae_metric']) - plt.plot(history.history['val_mae_metric']) - plt.ylabel('Mean absolute error') - plt.xlabel('Epoch') - plt.legend(['train', 'test'], loc='upper left') - - plt.subplot(122) - # summarize history for loss - plt.plot(history.history['loss']) - plt.plot(history.history['val_loss']) - #plt.title('model loss') - plt.ylabel('Loss') - plt.xlabel('Epoch') - plt.legend(['train', 'test'], loc='upper left') - - plt.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+category+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_'+category+'_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(batch_size)+'_loss_acc_curves.png', dpi=300, bbox_inches='tight') - plt.close('all') - - - rand_idx = np.random.choice(range(test_X.shape[0]), 9) - fig, m_axs = plt.subplots(3, 3, figsize = (16, 32)) - for (idx, c_ax) in zip(rand_idx, m_axs.flatten()): - c_ax.imshow(test_X[idx, :,:,0], cmap = 'gray') - if category == 'H': - c_ax.set_title('H: %0.3f\nPredicted H: %0.3f' % (test_Y[idx], pred_Y[idx])) - else: - c_ax.set_title('T: %0.3f\nPredicted T: %0.3f' % (test_Y[idx], pred_Y[idx])) - - c_ax.axis('off') - - if category == 'H': - if input_csv_file=='IR-training-dataset.csv': - fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'H'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveheight_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') - else: - if input_csv_file=='IR-training-dataset.csv': - fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_IR.png', dpi=300, bbox_inches='tight') - else: - fig.savefig(os.getcwd()+os.sep+'im'+str(imsize)+os.sep+'res'+os.sep+str(num_epochs)+'epoch'+os.sep+'T'+os.sep+'model'+str(counter)+os.sep+'batch'+str(batch_size)+os.sep+'im'+str(IMG_SIZE[0])+'_waveperiod_predictions_model'+str(counter)+'_'+str(num_epochs)+'epoch'+str(train_gen.batch_size)+'batch_nearshore.png', dpi=300, bbox_inches='tight') - - counter += 1 - - - # end time - end = time.time() - print ("end time - {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))) - - diff --git a/utils.py b/utils.py index 2c679056..6cf4e980 100644 --- a/utils.py +++ b/utils.py @@ -1,5 +1,5 @@ ## utils.py -## Utilities for training optical wave height gauges +## Utilities for training optical wave gauges ## Written by Daniel Buscombe, ## Northern Arizona University ## daniel.buscombe.nau.edu @@ -7,7 +7,13 @@ import os import numpy as np import requests +from keras.metrics import mean_absolute_error +# mean absolute error +def mae_metric(in_gt, in_pred): + return mean_absolute_error(in_gt, in_pred) + + def gen_from_df(img_data_gen, in_df, path_col, y_col, **dflow_args): base_dir = os.path.dirname(in_df[path_col].values[0])