Sonar data - Rocks or Mines?¶
Credit: AITS Cainvas Community
Photo by Dirk Rowe on Dribbble
Sonar (sound navigationa ranging) is a technique based on the principle of reflection of ultrasonic sound waves. These waves propogate through water and reflect on hitting the ocean bed or any object obstructing its path.
Sonar has been widely used in submarine navigation, communication with or detection of objects on or underthe water surface (like other vessels), hazard identification etc.
Neural networks can be used to automate the process of identifying the objects from which the waves bounced off.
import numpy as np
import pandas as pd
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, LSTM
from tensorflow.keras.optimizers import Adam, SGD
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.metrics import confusion_matrix
import random
import matplotlib.pyplot as plt
The dataset¶
This dataset was used in Gorman, R. P., and Sejnowski, T. J. (1988). “Analysis of Hidden Units in a Layered Network Trained to Classify Sonar Targets” in Neural Networks, Vol. 1, pp. 75-89.
The CSV files contain data regarding sonar signals bounced off a metal cylinder (mines - M) and a roughly cylindrical rock (rock - R) at various angles and under various conditions.
df = pd.read_csv('https://cainvas-static.s3.amazonaws.com/media/user_data/cainvas-admin/sonar.all-data.csv', header = None)
df
# The spread of labels in the dataframe
df[60].value_counts()
This is a fairly balanced dataset.
Preprocessing¶
Categorical features¶
The class attribute has R and M to denote the classes. We have to convert them into numeric values.
df[60] = (df[60] == 'M').astype('int')
df
# Storing the class names corresponding to the index in arrays for reference later.
class_names = ['Rocks', 'Mines']
Balancing dataset¶
Even though there is only a difference of only 14 samples, in comparison to the total number of data samples available, this needs to be balanced.
# separating into 2 dataframes, one for each class
df0 = df[df[60] == 0]
df1 = df[df[60] == 1]
print("Number of samples in:")
print("Class label 0 - ", len(df0))
print("Class label 1 - ", len(df1))
# Upsampling
df0 = df0.sample(len(df1), replace = True) # replace = True enables resampling
print('\nAfter resampling - ')
print("Number of samples in:")
print("Class label 0 - ", len(df0))
print("Class label 1 - ", len(df1))
# concatente to form a single dataframe
df = df1.append(df0)
print('Total number of samples - ', len(df))
# defining the input and output columns to separate the dataset in the later cells.
input_columns = list(df.columns[:-1])
output_columns = [df.columns[-1]]
print("Number of input columns: ", len(input_columns))
#print("Input columns: ", ', '.join(input_columns))
print("Number of output columns: ", len(output_columns))
#print("Output columns: ", ', '.join(output_columns))
Train - val split¶
# Splitting into train and val set -- 90-10 split
train_df, val_df = train_test_split(df, test_size = 0.1, random_state = 2)
print("Number of samples in...")
print("Training set: ", len(train_df))
print("Validation set: ", len(val_df))
# Looking into the spread of values in the train and val sets
print("Training - ")
print(train_df[60].value_counts())
print("\nValidation - ")
print(val_df[60].value_counts())
# Splitting into X (input) and y (output)
Xtrain, ytrain = np.array(train_df[input_columns]), np.array(train_df[output_columns])
Xval, yval = np.array(val_df[input_columns]), np.array(val_df[output_columns])
Standardization¶
df.describe()
The range of values for the attributes are almost of the same range, but the little difference has caused a shift of the means.
# Using standard scaler to standardize them to values with mean = 0 and variance = 1.
standard_scaler = StandardScaler()
# Fit on training set alone
Xtrain = standard_scaler.fit_transform(Xtrain)
# Use it to transform val and test input
Xval = standard_scaler.transform(Xval)
#Xtest = standard_scaler.transform(Xtest)
pd.DataFrame(Xtrain).describe()
The means are (almost) 0.
The model¶
model = Sequential([
Dense(64, activation = 'relu', input_shape = Xtrain[0].shape),
Dense(32, activation = 'relu'),
Dense(16, activation = 'relu'),
Dense(1, activation = 'sigmoid')
])
cb = [EarlyStopping(monitor = 'val_loss', patience = 5, restore_best_weights = True)]
model.summary()
model.compile(optimizer=Adam(0.01), loss='binary_crossentropy', metrics=['accuracy'])
history1 = model.fit(Xtrain, ytrain, validation_data = (Xval, yval), epochs=16, callbacks = cb)
model.compile(optimizer=Adam(0.001), loss='binary_crossentropy', metrics=['accuracy'])
history2 = model.fit(Xtrain, ytrain, validation_data = (Xval, yval), epochs=16, callbacks = cb)
model.evaluate(Xval, yval)
Plotting the metrics¶
def plot(history1, history2, variable1, variable2):
# combining metrics from both trainings
var1_history = history1[variable1]
var1_history.extend(history2[variable1])
var2_history = history1[variable2]
var2_history.extend(history2[variable2])
# plotting them
plt.plot(range(len(var1_history)), var1_history)
plt.plot(range(len(var2_history)), var2_history)
plt.legend([variable1, variable2])
plt.title(variable1)
plot(history1.history, history2.history, "accuracy", 'val_accuracy')
plot(history1.history, history2.history, "loss", 'val_loss')
Prediction¶
# pick random test data sample from one batch
x = random.randint(0, len(Xval) - 1)
output_true = np.array(yval)[x][0]
print("True: ", class_names[output_true])
output = model.predict(Xval[x].reshape(1, -1))[0][0]
pred = int(output>0.5) # finding max
print("Predicted: ", class_names[pred], "(",output, "-->", pred, ")") # Picking the label from class_names base don the model
deepC¶
model.save('sonar.h5')
!deepCC sonar.h5
# pick random test data sample from one batch
x = random.randint(0, len(Xval) - 1)
output_true = np.array(yval)[x][0]
print("True: ", class_names[output_true])
np.savetxt('sample.data', Xval[x])
# run exe with input
!sonar_deepC/sonar.exe sample.data
# show predicted output
#nn_out = np.loadtxt('dense_3.out')
#pred = int(nn_out>0.5) # finding max
#print("Predicted: ", class_names[pred], "(",nn_out, "-->", pred, ")") # Picking the label from class_names base don the model output