## This code covers encoding and one-hot encoding
## It also covers multilayer ANNs using tf.keras
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 12 20:22:42 2025
@author: profa
"""
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split
import tensorflow.keras
#from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout, LSTM
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
from tensorflow.keras import layers
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
#from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
######################################
## Build, Train, Visualize, Evaluate ANN
## Input size: 4
## Two Layers
## Layer 1 has 3 units
## Layer 2 as 2 units
## Output size: 3
#########################################
## Get the Data---
## LINK TO DATASET ##
## https://drive.google.com/drive/u/0/folders/1H8S3wXU-qEGz0bG1oPXIRN2pWY0y2yjj
## Path to dataset on my computer
## YOU will update this path for YOUR computer
filename="Mod4_SummerStudentAdmissionsData_3Labeled_4D.csv"
## UPDATE THIS to YOUR PATH !!!!!!!!!!!!!!!!!!!!!!!!!!
path="C:/Users/profa/Desktop/C_O/........./Datasets/"
DF=pd.read_csv(path+filename)
print(DF)
###############
## Split DF into Training and Testing sets
# Random sampling *without replacement*
TrainDF, TestDF = train_test_split(DF, test_size=0.3)
print(TrainDF.shape)
print(TestDF.shape)
print(TestDF)
###########
## IMPORTANT ##
##
## When creating an output that is larger than one
## while using Softmax
## it is REQUIRED to first make sure that the input
## is ONE-HOT ENCODED.
###########################################
###### One-hot encode the labels for
###### Train and Test datasets
###########################################
## For Training
## Get the label from the Training data, convert
## it to an array, and transpose it.
## It is strongly recommend that you print each part to
## see what is going on.
## print(TrainDF)
## print(TrainDF.iloc[:,0])
## print(np.array(TrainDF.iloc[:,0]))
TrainingLabels = np.array(TrainDF.iloc[:,0])
#TrainingLabels = np.array([TrainingLabels]).T ## transpose
print("Train Labels are\n", TrainingLabels)
## Now - convert the Admit, Decline, Waitlist into 0, 1, 2.
## These are just labels, so the order of the 0, 1, 2 does not matter.
## This process is called "encoding"
## class sklearn.preprocessing.LabelEncoder
MyEncoder=LabelEncoder()
Encoded_Train_Labels = MyEncoder.fit_transform(TrainingLabels)
print(Encoded_Train_Labels)
## Notice now that the labels are show as 0, 1, 2
## Next, we will change the 0, 1, 2 into one-hot encoded vectors
## where the 0 will be encoded as [1, 0, 0]
## the 1 will be encoded as [0, 1, 0]
## the 2 will be encoded as [0, 0, 1]
## We will use the Sklearn OneHotEncoder
## https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
## Instantiate
MyOneHotEncoder=OneHotEncoder()
## Before we one hot encode our Encoded_Train_Labels from above,
## we must "reshape" them.
Encoded_Train_Labels = Encoded_Train_Labels.reshape(-1,1)
print(Encoded_Train_Labels)
OneHotEncodedTrainLabels=MyOneHotEncoder.fit_transform(Encoded_Train_Labels).toarray()
print(OneHotEncodedTrainLabels)
## Let's save these as TrainingLabels
TrainingLabels=OneHotEncodedTrainLabels
## Note that our labels are now one-hot encoded
## and are called TrainingLabels
## LAST STEP - remove/drop the label off of the TrainDF dataset.
TrainDF = TrainDF.drop(columns="Decision", axis=1)
print(TrainDF)
##################------------------------------------
## Repeat all of the above for the Testing Labels
TestingLabels = np.array(TestDF.iloc[:,0])
print(TestingLabels )
## Keep the original Testing Labels for later
TestingLabels_Copy = TestingLabels
print(TestingLabels_Copy)
## Now - convert the Admit, Decline, Waitlist into 0, 1, 2.
MyEncoder=LabelEncoder()
Encoded_Test_Labels = MyEncoder.fit_transform(TestingLabels)
print(Encoded_Test_Labels)
## Notice now that the labels are show as 0, 1, 2
## Next, we will change the 0, 1, 2 into one-hot encoded vectors
## where the 0 will be encoded as [1, 0, 0]
## the 1 will be encoded as [0, 1, 0]
## the 2 will be encoded as [0, 0, 1]
## We will use the Sklearn OneHotEncoder
## https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
## Instantiate
MyOneHotEncoder=OneHotEncoder()
## Before we one hot encode our Encoded_Test_Labels from above,
## we must "reshape" them.
Encoded_Test_Labels = Encoded_Test_Labels.reshape(-1,1)
print(Encoded_Test_Labels)
OneHotEncodedTestLabels=MyOneHotEncoder.fit_transform(Encoded_Test_Labels).toarray()
print(OneHotEncodedTestLabels)
## Let's save these as TrainingLabels
TestingLabels=OneHotEncodedTestLabels
## LAST STEP - remove/drop the label off of the TestDF dataset.
TestDF = TestDF.drop(columns="Decision", axis=1)
print(TestDF)
##################------------------------------------
### Next, we will normalize the data using Min/Max
## There are many normalization and standardization options
## We will use Sklearn StandardScalar
## https://scikit-learn.org/1.6/modules/generated/sklearn.preprocessing.MinMaxScaler.html
## Instantiate
MyMM=MinMaxScaler()
TrainDF=MyMM.fit_transform(TrainDF)
print(TrainDF)
# ## Notice two things
# ## First, the data is scaled
# ## Next, the data is an array (not a dataframe)
# print(type(TrainDF))
# ## Repeat the above for the Testing Data
TestDF=MyMM.fit_transform(TestDF)
print(TestDF)
##########################################
## Using Keras ---
## Building the Model
## Summary
## Compile
## train with fit
## test and visualize
#################################################
## Step 1
## Create a TF - Keras NN Model
## https://keras.io/guides/sequential_model/
My_NN_Model = tf.keras.models.Sequential([
tf.keras.layers.Dense(3, input_shape=(4,), activation='relu'),
tf.keras.layers.Dense(2, activation='relu'),
tf.keras.layers.Dense(3, activation='softmax')
])
## This offers a summary of the model and notes the number of parameters
My_NN_Model.summary()
## Print the parameters - weights
first_layer_weights = My_NN_Model.layers[0].get_weights()[0]
#first_layer_biases = My_NN_Model.layers[0].get_weights()[1]
second_layer_weights = My_NN_Model.layers[1].get_weights()[0]
third_layer_weights = My_NN_Model.layers[2].get_weights()[0]
print("The first layer weights are: \n", first_layer_weights)
print("The first layer weights shape is\n",first_layer_weights.shape )
print("The second layer weights are: \n", second_layer_weights)
print("The third layer weights are: \n", third_layer_weights)
## Compile the Model
My_loss_function = keras.losses.CategoricalCrossentropy()
My_optimizer = tf.keras.optimizers.Adam(learning_rate=0.005)
My_NN_Model.compile(
loss=My_loss_function,
metrics=["accuracy"],
optimizer=My_optimizer
)
## Fit the Model to the data (train the model)
## NOTE: You can change the "epochs" to train the model longer.
## It is a good idea to try different epochs values
Hist=My_NN_Model.fit(TrainDF,TrainingLabels, epochs=300, validation_data=(TestDF, TestingLabels))
## batch_size= is also an option here for batch training
## This will visualize the accuracy of the trained model
plt.plot(Hist.history['accuracy'], label='accuracy')
plt.plot(Hist.history['val_accuracy'], label = 'val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.ylim([0.1, 1])
plt.legend(loc='lower right')
plt.show()
##Test the Model
Test_Loss, Test_Accuracy = My_NN_Model.evaluate(TestDF, TestingLabels)
## Save the Model
My_NN_Model.export("Example2_NN_Model")
## Predictions
predictions=My_NN_Model.predict(TestDF)
print(predictions)
## Important !! ##
## Take a look at the printed predictions
## While each run is different because the parameters of the
## model are random, let's use my output
## as an example.
## The first prediction is [9.5004809e-01 1.1108044e-03 4.8841193e-02]
## Let's view this rounded and in decimal notation.
## [.950 .0011 .049]
## This is telling us that the prediction is .95 (or 95%) likely to be
## [1, 0, 0] which is label "0" which is Admit in our case.
## Remember - we converted Admit, Decline, Waitlist to 0, 1, 2
## and then one-hot encoded them to [1, 0, 0] for 0 (Admit), etc. (see above)
## You can code Python to update the predictions
## into human-friendly outcomes
## We will use numpy argmax. Here is how it works in general:
print(np.argmax([.01, .3, .69])) ## This prints "2" be .69 is the max
NumPred=len(predictions)
#print(NumPred)
print(predictions)
PredictionsList=[]
print(type(PredictionsList))
for i in range(NumPred):
max_pred=np.argmax(predictions[i])
#print(max_pred)
if(max_pred == 0):
pred="Admit"
elif(max_pred==1):
pred="Decline"
else:
pred="Waitlist"
PredictionsList.append(pred)
print(PredictionsList)
## This area creates a confusion matrix to visualize the
## accuracy of the model on the test data.
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
print("The prediction accuracy via confusion matrix is:\n")
labels = ["Admit", "Decline", "Waitlist"]
print(TestingLabels_Copy)
print(PredictionsList)
cm=confusion_matrix(TestingLabels_Copy, PredictionsList)
print(cm)
import seaborn as sns
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(13,13))
sns.heatmap(cm, annot=True, fmt='g', ax=ax, annot_kws={'size': 18})
ax.set_xlabel('Predicted labels')
ax.set_ylabel('True/Actual labels')
ax.set_title('Confusion Matrix: NN')
ax.xaxis.set_ticklabels(["0:Admit","1:Decline", "2:Waitlist"],rotation=90, fontsize = 12)
ax.yaxis.set_ticklabels(["0:Admit","1:Decline", "2:Waitlist"],rotation=0, fontsize = 12)