Table Of Contents


Built with 🛠 MkDocs - Theme 🖤 Github.

View Example on GitHub

Sonar Rock or Mine Classification

This walkthrough builds a Sonar rock-or-mine classifier one part at a time. It uses logistic regression with the smallest possible neural-network architecture: inputs connected directly to one Sigmoid output.

Problem: Classify a sonar signal as a ROCK or a MINE.

Dataset: 208 sonar observations with 60 numerical signal features and one binary target.

At a glance

ML task Binary classification
Level Beginner
Dataset 208 sonar observations
Inputs 60 numerical sonar-signal features
Target 0 for rock, 1 for mine
Model 60 -> 1 sonar-rock-mine-classification network with a Sigmoid output
Default training 200 CPU epochs
Related concepts Machine Learning Basics, Logistic Regression, Neural Networks, Backpropagation, Deep Netts API, Model Development

The complete flow is:

load -> inspect -> split -> scale -> build -> train -> evaluate -> save -> predict

0. Define the program configuration

Set the dataset path, model path, and expected data shape:

private static final String DATASET_PATH =
        "src/main/resources/datasets/sonar.csv";

private static final String MODEL_PATH =
        "models/sonar-rock-mine-classification.dnet";

private static final int NUM_INPUTS = 60;
private static final int NUM_OUTPUTS = 1;

Every CSV row contains 60 input values followed by one binary target.

1. Configure the runtime

Verify Java and disable CUDA before creating the model:

verifyJavaRuntime();
DeepNetts.getInstance().setUseCuda(false);

Output:

WARNING: Using incubator modules: jdk.incubator.vector

The example requires Java 25 with the Vector API enabled. This compact dataset and model run quickly on a CPU and do not require GPU configuration.

2. Load the Sonar dataset

Read the numerical CSV file:

TabularDataSet<MLDataItem> dataSet =
        DataSets.readCsv(
                DATASET_PATH,
                NUM_INPUTS,
                NUM_OUTPUTS,
                false,
                ","
        );

The file has no header, so the fourth argument is false. Setting it to true would incorrectly discard the first observation as column names.

3. Inspect the data

Confirm that all observations were loaded:

System.out.println("Samples: " + dataSet.size());

Output:

Samples: 208

The expected result is 208. This small check catches missing files and incorrect CSV-header configuration early.

4. Shuffle and split the observations

Shuffle reproducibly and reserve 20% of the data for testing:

dataSet.shuffle(42);

TrainTestSplit split = dataSet.trainTestSplit(0.8);

DataSet<MLDataItem> trainingSet = split.getTrainingSet();
DataSet<MLDataItem> testSet = split.getTestSet();

The model learns only from the training set. The test set estimates how well it handles sonar observations that did not update its weights.

5. Scale the input features

Fit Min-Max scaling on the training set and apply the same transformation to the test set:

MinMaxScaler scaler = DataSets.scaleToMinMax(trainingSet);

scaler.apply(testSet);

Fitting the scaler only on training data prevents information from the test set leaking into model development.

6. Build logistic regression

Create a network with no hidden layers:

FeedForwardNetwork neuralNet = FeedForwardNetwork.builder()
        .addInputLayer(NUM_INPUTS)
        .addOutputLayer(NUM_OUTPUTS, ActivationType.SIGMOID)
        .lossFunction(LossType.CROSS_ENTROPY)
        .randomSeed(42)
        .build();

The architecture is:

60 numerical features -> 1 Sigmoid output

With no hidden layer, the network is logistic regression rather than a multilayer neural network. The Sigmoid output represents the estimated probability of the positive class.

7. Configure and train

Set the training parameters and fit the model:

neuralNet.getTrainer()
        .setStopEpochs(200)
        .setLearningRate(0.01f)
        .setOptimizer(OptimizerType.SGD)
        .setShuffle(true);

neuralNet.train(trainingSet);

Output:

TRAINING NEURAL NETWORK
------------------------------------------------------------------------

Initial Train Error:0.69915146
Epoch:1, Time:8ms, TrainError:0.6907358, TrainErrorChange:-0.008415639, TrainAccuracy:0.5903614
Epoch:2, Time:2ms, TrainError:0.6626929, TrainErrorChange:-0.028042912, TrainAccuracy:0.62650603
Epoch:3, Time:2ms, TrainError:0.64100194, TrainErrorChange:-0.021690965, TrainAccuracy:0.6626506
...
Epoch:200, Time:1ms, TrainError:0.31379443, TrainErrorChange:-0.0015623271, TrainAccuracy:0.87349397

TRAINING COMPLETED
Total Training Time: 124ms
------------------------------------------------------------------------

SGD updates the output weights and bias to reduce Cross-Entropy loss. The middle epochs are omitted for readability. Across all 200 epochs, training error fell from about 0.70 to 0.31 and training accuracy reached about 87.3%.

8. Evaluate the classifier

Evaluate predictions on the held-out test set:

ClassificationMetrics metrics = (ClassificationMetrics) neuralNet.test(testSet);
System.out.println(metrics);

Output:

Class: out1
Total items: 41
True positive:15.0 Number of examples correctly classified as positive
True negative:15.0 Number of examples correctly classified as negative
False positive:2.0 Number of examples incorrectly classified as positive
False negative:9.0 Number of examples incorrectly classified as negative
Accuracy (ACC): 0.73170733 How often is a classifier correct in total (percent of correct classifications)
Precision (PPV): 0.88235295 How often is a classifier correct when it gives positive prediction
Recall: 0.625 When it is actually positive class, how often does it give positive prediction
F1 Score: 0.7317073 Harmonic average (balance) of precision and recall
Specificity (TNR): 0.88235295 When it is actually negative class, how often does it give negative prediction
Fall-out (FPR): 0.11764706 How often it gives false positive prediction in total (percent of false positive predictions)
False negative rate (FNR): 0.375 How often it gives false negative prediction in total (percent of false negative predictions)

The model correctly classified about 73.2% of the 41 held-out observations. Its high precision but lower recall means mine predictions are usually correct, while nine actual mines were missed.

9. Save the trained model

Create the output directory and serialize the model:

Files.createDirectories(Path.of("models"));
FileIO.writeToFile(neuralNet, MODEL_PATH);

The example saves models/sonar-rock-mine-classification.dnet. Loading is intentionally not demonstrated while the Deep Netts 4 beta runtime is still finalizing model deserialization.

10. Use the trained model

Use a real, already-preprocessed test observation, predict its mine probability, and compare the predicted and actual classes:

MLDataItem sonarExample = testSet.get(0);
float[] sonarSignal = sonarExample.getInput().getValues();
float actualTarget = sonarExample.getTargetOutput().getValues()[0];
float mineProbability = neuralNet.predict(sonarSignal)[0];

System.out.printf("Mine probability: %.4f%n", mineProbability);
System.out.println("Predicted: "
        + (mineProbability >= 0.5f ? "MINE" : "ROCK"));
System.out.println("Actual: "
        + (actualTarget >= 0.5f ? "MINE" : "ROCK"));

Output:

Mine probability: 0.8297
Predicted: MINE
Actual: MINE

The Sigmoid output is above the 0.5 threshold, so this held-out observation is correctly classified as MINE.

Run the example

Pass this option to the Java process through your IDE's application run configuration or the command line:

--add-modules=jdk.incubator.vector

Logistic regression or a neural network?

Deep Netts represents both models with FeedForwardNetwork. Their architectures distinguish them:

Model Architecture Capacity
Logistic Regression 60 -> 1 Learns a linear decision boundary
Spam Classification network 5 -> 32 -> 16 -> 1 Learns non-linear feature combinations

Logistic regression is a useful baseline: it is compact, fast, and easy to interpret before trying a model with hidden layers.

What you built

Sonar CSV with 208 observations
        |
        v
80/20 train/test split
        |
        v
Min-Max scaling
        |
        v
60 -> 1 sonar-rock-mine-classification model
        |
        v
Training and classification evaluation
        |
        v
Saved model and test-sample prediction

← Back to Classification Examples

The numerical features represent reflected sonar energy measured across frequency bands.

Every sample belongs to one of two labels: rock or mine, so the model uses a single binary output.

Was this helpful?