CNN for Image Classification in Java

Overview

Convolutional Neural Networks (CNNs) for image classification in Java using Deep Learning frameworks. We'll cover multiple approaches including DL4J, TensorFlow Java, and custom implementations.

1. Setup and Dependencies

Maven Dependencies

<!-- pom.xml -->
<properties>
<dl4j.version>1.0.0-M2.1</dl4j.version>
<nd4j.version>1.0.0-M2.1</nd4j.version>
</properties>
<dependencies>
<!-- DeepLearning4J Core -->
<dependency>
<groupId>org.deeplearning4j</groupId>
<artifactId>deeplearning4j-core</artifactId>
<version>${dl4j.version}</version>
</dependency>
<!-- DeepLearning4J Model Zoo -->
<dependency>
<groupId>org.deeplearning4j</groupId>
<artifactId>deeplearning4j-zoo</artifactId>
<version>${dl4j.version}</version>
</dependency>
<!-- ND4J Backend -->
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>nd4j-native-platform</artifactId>
<version>${nd4j.version}</version>
</dependency>
<!-- TensorFlow Java -->
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>tensorflow-core-platform</artifactId>
<version>0.4.1</version>
</dependency>
<!-- Image Processing -->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.9.4</version>
</dependency>
<!-- DataVec for data processing -->
<dependency>
<groupId>org.datavec</groupId>
<artifactId>datavec-data-image</artifactId>
<version>${dl4j.version}</version>
</dependency>
</dependencies>

2. Basic CNN with DeepLearning4J

Simple CNN Architecture

import org.deeplearning4j.nn.api.OptimizationAlgorithm;
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.inputs.InputType;
import org.deeplearning4j.nn.conf.layers.*;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.deeplearning4j.optimize.listeners.ScoreIterationListener;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.learning.config.Adam;
import org.nd4j.linalg.lossfunctions.LossFunctions;
public class SimpleCNN {
private MultiLayerNetwork model;
private int height = 28;
private int width = 28;
private int channels = 1; // grayscale
private int numClasses = 10; // e.g., MNIST digits
public SimpleCNN() {
buildModel();
}
private void buildModel() {
MultiLayerConfiguration configuration = new NeuralNetConfiguration.Builder()
.seed(12345)
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
.updater(new Adam(0.001))
.weightInit(WeightInit.XAVIER)
.list()
// Convolutional Layer 1
.layer(new ConvolutionLayer.Builder()
.name("conv1")
.nIn(channels)
.nOut(32)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
// Pooling Layer 1
.layer(new SubsamplingLayer.Builder()
.name("pool1")
.poolingType(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
// Convolutional Layer 2
.layer(new ConvolutionLayer.Builder()
.name("conv2")
.nOut(64)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
// Pooling Layer 2
.layer(new SubsamplingLayer.Builder()
.name("pool2")
.poolingType(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
// Fully Connected Layer 1
.layer(new DenseLayer.Builder()
.name("fc1")
.nOut(128)
.activation(Activation.RELU)
.build())
// Output Layer
.layer(new OutputLayer.Builder()
.name("output")
.lossFunction(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.nOut(numClasses)
.activation(Activation.SOFTMAX)
.build())
.setInputType(InputType.convolutional(height, width, channels))
.build();
model = new MultiLayerNetwork(configuration);
model.init();
// Add listener for training progress
model.setListeners(new ScoreIterationListener(100));
}
public MultiLayerNetwork getModel() {
return model;
}
public void printModelSummary() {
System.out.println("CNN Model Architecture:");
System.out.println(model.summary());
}
public static void main(String[] args) {
SimpleCNN cnn = new SimpleCNN();
cnn.printModelSummary();
}
}

Advanced CNN with Batch Normalization and Dropout

import org.deeplearning4j.nn.conf.*;
import org.deeplearning4j.nn.conf.layers.*;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.linalg.learning.config.Nesterovs;
public class AdvancedCNN {
private MultiLayerNetwork model;
private int height = 224;
private int width = 224;
private int channels = 3; // RGB
private int numClasses = 1000; // ImageNet classes
public AdvancedCNN() {
buildAdvancedModel();
}
private void buildAdvancedModel() {
double dropoutRate = 0.5;
double learningRate = 0.01;
double momentum = 0.9;
MultiLayerConfiguration configuration = new NeuralNetConfiguration.Builder()
.seed(12345)
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
.updater(new Nesterovs(learningRate, momentum))
.weightInit(WeightInit.RELU)
.l2(0.0001) // L2 regularization
.list()
// Block 1
.layer(new ConvolutionLayer.Builder()
.name("conv1_1")
.nIn(channels)
.nOut(64)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new ConvolutionLayer.Builder()
.name("conv1_2")
.nOut(64)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new SubsamplingLayer.Builder()
.name("pool1")
.poolingType(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
.layer(new DropoutLayer.Builder(dropoutRate).build())
// Block 2
.layer(new ConvolutionLayer.Builder()
.name("conv2_1")
.nOut(128)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new ConvolutionLayer.Builder()
.name("conv2_2")
.nOut(128)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new SubsamplingLayer.Builder()
.name("pool2")
.poolingType(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
.layer(new DropoutLayer.Builder(dropoutRate).build())
// Block 3
.layer(new ConvolutionLayer.Builder()
.name("conv3_1")
.nOut(256)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new ConvolutionLayer.Builder()
.name("conv3_2")
.nOut(256)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new ConvolutionLayer.Builder()
.name("conv3_3")
.nOut(256)
.kernelSize(3, 3)
.stride(1, 1)
.padding(1, 1)
.activation(Activation.RELU)
.build())
.layer(new BatchNormalization.Builder().build())
.layer(new SubsamplingLayer.Builder()
.name("pool3")
.poolingType(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
.layer(new DropoutLayer.Builder(dropoutRate).build())
// Fully Connected Layers
.layer(new DenseLayer.Builder()
.name("fc1")
.nOut(1024)
.activation(Activation.RELU)
.build())
.layer(new DropoutLayer.Builder(dropoutRate).build())
.layer(new DenseLayer.Builder()
.name("fc2")
.nOut(1024)
.activation(Activation.RELU)
.build())
.layer(new DropoutLayer.Builder(dropoutRate).build())
// Output Layer
.layer(new OutputLayer.Builder()
.name("output")
.lossFunction(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.nOut(numClasses)
.activation(Activation.SOFTMAX)
.build())
.setInputType(InputType.convolutional(height, width, channels))
.build();
model = new MultiLayerNetwork(configuration);
model.init();
model.setListeners(new ScoreIterationListener(10));
}
public MultiLayerNetwork getModel() {
return model;
}
public static void main(String[] args) {
AdvancedCNN cnn = new AdvancedCNN();
System.out.println("Advanced CNN Model Summary:");
System.out.println(cnn.getModel().summary());
}
}

3. Data Preparation and Loading

Image Data Pipeline

import org.datavec.api.io.labels.ParentPathLabelGenerator;
import org.datavec.api.split.FileSplit;
import org.datavec.image.loader.NativeImageLoader;
import org.datavec.image.recordreader.ImageRecordReader;
import org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.dataset.api.preprocessor.DataNormalization;
import org.nd4j.linalg.dataset.api.preprocessor.ImagePreProcessingScaler;
import org.nd4j.linalg.dataset.api.preprocessor.VGG16ImagePreProcessor;
import java.io.File;
import java.util.Random;
public class ImageDataLoader {
private int height;
private int width;
private int channels;
private int numClasses;
private int batchSize;
public ImageDataLoader(int height, int width, int channels, int numClasses, int batchSize) {
this.height = height;
this.width = width;
this.channels = channels;
this.numClasses = numClasses;
this.batchSize = batchSize;
}
public DataSetIterator loadTrainingData(String dataPath) throws Exception {
return createDataSetIterator(dataPath, true);
}
public DataSetIterator loadTestData(String dataPath) throws Exception {
return createDataSetIterator(dataPath, false);
}
private DataSetIterator createDataSetIterator(String dataPath, boolean isTraining) throws Exception {
// Define label generator (assumes directory structure: root/class1/, root/class2/, etc.)
ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator();
// Split the data
File mainPath = new File(dataPath);
FileSplit fileSplit = new FileSplit(mainPath, NativeImageLoader.ALLOWED_FORMATS, new Random(12345));
// Create image record reader
ImageRecordReader recordReader = new ImageRecordReader(height, width, channels, labelMaker);
if (isTraining) {
// For training, initialize with training data split
recordReader.initialize(fileSplit);
} else {
// For testing, you might want a different split
recordReader.initialize(fileSplit);
}
// Create dataset iterator
DataSetIterator iterator = new RecordReaderDataSetIterator(
recordReader, batchSize, 1, numClasses);
// Normalize pixel values to 0-1
DataNormalization scaler = new ImagePreProcessingScaler(0, 1);
scaler.fit(iterator);
iterator.setPreProcessor(scaler);
return iterator;
}
// For VGG16 pre-trained model
public DataSetIterator loadDataForVGG16(String dataPath) throws Exception {
ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator();
File mainPath = new File(dataPath);
FileSplit fileSplit = new FileSplit(mainPath, NativeImageLoader.ALLOWED_FORMATS, new Random(12345));
ImageRecordReader recordReader = new ImageRecordReader(224, 224, 3, labelMaker);
recordReader.initialize(fileSplit);
DataSetIterator iterator = new RecordReaderDataSetIterator(
recordReader, batchSize, 1, numClasses);
// VGG16 specific preprocessing
DataNormalization vggScaler = new VGG16ImagePreProcessor();
iterator.setPreProcessor(vggScaler);
return iterator;
}
// Data augmentation for training
public DataSetIterator createAugmentedIterator(String dataPath) throws Exception {
ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator();
File mainPath = new File(dataPath);
FileSplit fileSplit = new FileSplit(mainPath, NativeImageLoader.ALLOWED_FORMATS, new Random(12345));
ImageRecordReader recordReader = new ImageRecordReader(height, width, channels, labelMaker);
recordReader.initialize(fileSplit);
// Note: For actual data augmentation, you'd need to extend ImageRecordReader
// or use DataVec's transform process
DataSetIterator iterator = new RecordReaderDataSetIterator(
recordReader, batchSize, 1, numClasses);
DataNormalization scaler = new ImagePreProcessingScaler(0, 1);
scaler.fit(iterator);
iterator.setPreProcessor(scaler);
return iterator;
}
}

Custom Data Generator for Large Datasets

import org.nd4j.linalg.dataset.DataSet;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.factory.Nd4j;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class CustomImageIterator implements DataSetIterator {
private int batchSize;
private int numExamples;
private int currentBatch = 0;
private int height, width, channels;
private int numClasses;
private List<File> imageFiles;
private List<Integer> labels;
public CustomImageIterator(List<File> imageFiles, List<Integer> labels, 
int batchSize, int height, int width, int channels, int numClasses) {
this.imageFiles = imageFiles;
this.labels = labels;
this.batchSize = batchSize;
this.height = height;
this.width = width;
this.channels = channels;
this.numClasses = numClasses;
this.numExamples = imageFiles.size();
}
@Override
public DataSet next(int num) {
int actualBatchSize = Math.min(num, numExamples - currentBatch * batchSize);
// Create arrays for features and labels
float[][][][] features = new float[actualBatchSize][channels][height][width];
float[][] labelData = new float[actualBatchSize][numClasses];
for (int i = 0; i < actualBatchSize; i++) {
int index = currentBatch * batchSize + i;
try {
// Load and preprocess image
float[][][] image = loadAndPreprocessImage(imageFiles.get(index));
features[i] = image;
// Set label (one-hot encoding)
int label = labels.get(index);
labelData[i][label] = 1.0f;
} catch (Exception e) {
System.err.println("Error loading image: " + imageFiles.get(index).getName());
e.printStackTrace();
}
}
currentBatch++;
return new DataSet(
Nd4j.create(features),
Nd4j.create(labelData)
);
}
private float[][][] loadAndPreprocessImage(File imageFile) throws Exception {
// Simplified image loading - in practice, use ImageIO or NativeImageLoader
// This is a placeholder implementation
float[][][] image = new float[channels][height][width];
// Here you would implement actual image loading and preprocessing
// For example: resize, normalize, etc.
return image;
}
@Override
public int inputColumns() {
return height * width * channels;
}
@Override
public int totalOutcomes() {
return numClasses;
}
@Override
public boolean resetSupported() {
return true;
}
@Override
public boolean asyncSupported() {
return true;
}
@Override
public void reset() {
currentBatch = 0;
// Shuffle data for next epoch
Collections.shuffle(imageFiles);
}
@Override
public int batch() {
return batchSize;
}
@Override
public void setPreProcessor(org.nd4j.linalg.dataset.api.DataSetPreProcessor preProcessor) {
// Implementation needed
}
@Override
public org.nd4j.linalg.dataset.api.DataSetPreProcessor getPreProcessor() {
return null;
}
@Override
public List<String> getLabels() {
return null; // Return class labels if available
}
@Override
public boolean hasNext() {
return currentBatch * batchSize < numExamples;
}
@Override
public DataSet next() {
return next(batchSize);
}
}

4. Training and Evaluation

Complete Training Pipeline

import org.deeplearning4j.api.storage.StatsStorage;
import org.deeplearning4j.eval.Evaluation;
import org.deeplearning4j.nn.api.Model;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.optimize.api.BaseTrainingListener;
import org.deeplearning4j.ui.api.UIServer;
import org.deeplearning4j.ui.stats.StatsListener;
import org.deeplearning4j.ui.storage.InMemoryStatsStorage;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.primitives.Pair;
import java.util.ArrayList;
import java.util.List;
public class CNNTrainer {
private MultiLayerNetwork model;
private List<TrainingHistory> trainingHistory;
public CNNTrainer(MultiLayerNetwork model) {
this.model = model;
this.trainingHistory = new ArrayList<>();
}
public void train(DataSetIterator trainData, DataSetIterator testData, int numEpochs) {
// Setup UI server for monitoring (optional)
UIServer uiServer = UIServer.getInstance();
StatsStorage statsStorage = new InMemoryStatsStorage();
uiServer.attach(statsStorage);
model.setListeners(
new StatsListener(statsStorage),
new TrainingProgressListener()
);
System.out.println("Starting training...");
for (int epoch = 0; epoch < numEpochs; epoch++) {
System.out.println("Epoch " + (epoch + 1) + "/" + numEpochs);
// Train for one epoch
model.fit(trainData);
// Evaluate on test data
Evaluation eval = model.evaluate(testData);
System.out.println(eval.stats());
// Save training history
trainingHistory.add(new TrainingHistory(
epoch + 1,
model.score(),
eval.accuracy(),
eval.precision(),
eval.recall(),
eval.f1()
));
// Reset iterators for next epoch
trainData.reset();
testData.reset();
}
System.out.println("Training completed!");
}
public void saveModel(String filePath) throws Exception {
model.save(new File(filePath), true);
}
public void loadModel(String filePath) throws Exception {
model = MultiLayerNetwork.load(new File(filePath), true);
}
public Evaluation evaluate(DataSetIterator testData) {
return model.evaluate(testData);
}
public String predict(DataSetIterator data) {
// Implementation for single prediction
return "Prediction result";
}
// Custom training listener for progress monitoring
private class TrainingProgressListener extends BaseTrainingListener {
@Override
public void iterationDone(Model model, int iteration, int epoch) {
if (iteration % 100 == 0) {
System.out.printf("Epoch %d, Iteration %d, Score: %.4f%n", 
epoch, iteration, model.score());
}
}
}
// Training history record
public static class TrainingHistory {
private int epoch;
private double trainingLoss;
private double testAccuracy;
private double precision;
private double recall;
private double f1;
public TrainingHistory(int epoch, double trainingLoss, double testAccuracy,
double precision, double recall, double f1) {
this.epoch = epoch;
this.trainingLoss = trainingLoss;
this.testAccuracy = testAccuracy;
this.precision = precision;
this.recall = recall;
this.f1 = f1;
}
// Getters
public int getEpoch() { return epoch; }
public double getTrainingLoss() { return trainingLoss; }
public double getTestAccuracy() { return testAccuracy; }
public double getPrecision() { return precision; }
public double getRecall() { return recall; }
public double getF1() { return f1; }
}
}

Transfer Learning with Pre-trained Models

import org.deeplearning4j.nn.conf.distribution.NormalDistribution;
import org.deeplearning4j.nn.transferlearning.TransferLearning;
import org.deeplearning4j.zoo.PretrainedType;
import org.deeplearning4j.zoo.model.VGG16;
public class TransferLearningCNN {
private MultiLayerNetwork model;
public TransferLearningCNN() throws Exception {
setupTransferLearning();
}
private void setupTransferLearning() throws Exception {
// Load pre-trained VGG16 model
System.out.println("Loading VGG16 pre-trained model...");
ZooModel<?> zooModel = VGG16.builder().build();
MultiLayerNetwork pretrained = (MultiLayerNetwork) zooModel.initPretrained(PretrainedType.IMAGENET);
System.out.println("Pre-trained model loaded successfully!");
// Modify the model for transfer learning
model = new TransferLearning.Builder(pretrained)
.fineTuneConfiguration(new FineTuneConfiguration.Builder()
.updater(new Adam(1e-5))
.seed(12345)
.build())
.setFeatureExtractor("fc2") // Freeze layers up to fc2
.removeOutputLayer()
.addLayer(new OutputLayer.Builder()
.name("new_output")
.nIn(4096) // VGG16 fc2 output size
.nOut(10)  // Your number of classes
.weightInit(new NormalDistribution(0, 0.2))
.activation(Activation.SOFTMAX)
.lossFunction(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.build())
.build();
System.out.println("Transfer learning model configured!");
System.out.println(model.summary());
}
public void trainWithTransferLearning(DataSetIterator trainData, DataSetIterator testData, int numEpochs) {
CNNTrainer trainer = new CNNTrainer(model);
trainer.train(trainData, testData, numEpochs);
}
public MultiLayerNetwork getModel() {
return model;
}
public static void main(String[] args) {
try {
TransferLearningCNN transferCNN = new TransferLearningCNN();
// Example usage with your data
ImageDataLoader dataLoader = new ImageDataLoader(224, 224, 3, 10, 32);
// DataSetIterator trainData = dataLoader.loadTrainingData("path/to/train");
// DataSetIterator testData = dataLoader.loadTestData("path/to/test");
// transferCNN.trainWithTransferLearning(trainData, testData, 10);
} catch (Exception e) {
e.printStackTrace();
}
}
}

5. Model Inference and Prediction

Prediction Interface

import org.datavec.image.loader.NativeImageLoader;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.api.preprocessor.DataNormalization;
import org.nd4j.linalg.dataset.api.preprocessor.ImagePreProcessingScaler;
import org.nd4j.linalg.factory.Nd4j;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
public class ImageClassifier {
private MultiLayerNetwork model;
private NativeImageLoader imageLoader;
private int height, width, channels;
private String[] classLabels;
public ImageClassifier(MultiLayerNetwork model, int height, int width, int channels, String[] classLabels) {
this.model = model;
this.height = height;
this.width = width;
this.channels = channels;
this.classLabels = classLabels;
this.imageLoader = new NativeImageLoader(height, width, channels);
}
public ClassificationResult classify(File imageFile) throws Exception {
// Load image
INDArray image = imageLoader.asMatrix(imageFile);
// Normalize image
DataNormalization scaler = new ImagePreProcessingScaler(0, 1);
scaler.transform(image);
// Perform prediction
INDArray output = model.output(image);
return processOutput(output, 5); // Return top 5 predictions
}
public ClassificationResult classify(INDArray image) {
INDArray output = model.output(image);
return processOutput(output, 5);
}
private ClassificationResult processOutput(INDArray output, int topK) {
float[] probabilities = output.toFloatVector();
// Get top K predictions
PriorityQueue<Prediction> topPredictions = new PriorityQueue<>(
Comparator.comparingDouble(Prediction::getProbability).reversed()
);
for (int i = 0; i < probabilities.length; i++) {
topPredictions.offer(new Prediction(
classLabels[i],
i,
probabilities[i]
));
}
List<Prediction> results = new ArrayList<>();
for (int i = 0; i < topK && !topPredictions.isEmpty(); i++) {
results.add(topPredictions.poll());
}
return new ClassificationResult(results);
}
public static class ClassificationResult {
private List<Prediction> predictions;
private long inferenceTime;
public ClassificationResult(List<Prediction> predictions) {
this.predictions = predictions;
}
public Prediction getTopPrediction() {
return predictions.isEmpty() ? null : predictions.get(0);
}
public List<Prediction> getTopKPredictions(int k) {
return predictions.subList(0, Math.min(k, predictions.size()));
}
public void printResults() {
System.out.println("Classification Results:");
for (int i = 0; i < predictions.size(); i++) {
Prediction p = predictions.get(i);
System.out.printf("%d. %s: %.4f (%.2f%%)%n", 
i + 1, p.getClassName(), p.getProbability(), p.getProbability() * 100);
}
}
}
public static class Prediction {
private String className;
private int classIndex;
private double probability;
public Prediction(String className, int classIndex, double probability) {
this.className = className;
this.classIndex = classIndex;
this.probability = probability;
}
// Getters
public String getClassName() { return className; }
public int getClassIndex() { return classIndex; }
public double getProbability() { return probability; }
}
// Batch prediction
public List<ClassificationResult> classifyBatch(List<File> imageFiles) throws Exception {
List<ClassificationResult> results = new ArrayList<>();
for (File imageFile : imageFiles) {
results.add(classify(imageFile));
}
return results;
}
}

6. Complete Example: MNIST Classification

End-to-End MNIST CNN

import org.deeplearning4j.datasets.iterator.impl.MnistDataSetIterator;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
public class MNISTClassifier {
public static void main(String[] args) throws Exception {
// Configuration
int batchSize = 64;
int numEpochs = 10;
int height = 28;
int width = 28;
int channels = 1;
int numClasses = 10;
// Load MNIST dataset
System.out.println("Loading MNIST dataset...");
DataSetIterator mnistTrain = new MnistDataSetIterator(batchSize, true, 12345);
DataSetIterator mnistTest = new MnistDataSetIterator(batchSize, false, 12345);
// Build CNN model
System.out.println("Building CNN model...");
SimpleCNN cnn = new SimpleCNN();
MultiLayerNetwork model = cnn.getModel();
// Train model
System.out.println("Starting training...");
CNNTrainer trainer = new CNNTrainer(model);
trainer.train(mnistTrain, mnistTest, numEpochs);
// Final evaluation
System.out.println("Final evaluation:");
Evaluation finalEval = model.evaluate(mnistTest);
System.out.println(finalEval.stats());
// Save model
trainer.saveModel("mnist_cnn_model.zip");
System.out.println("Model saved successfully!");
// Example of loading and using the model for prediction
// loadAndUseModel();
}
private static void loadAndUseModel() throws Exception {
// Load saved model
MultiLayerNetwork loadedModel = MultiLayerNetwork.load(new File("mnist_cnn_model.zip"), true);
// Create classifier
String[] mnistLabels = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
ImageClassifier classifier = new ImageClassifier(loadedModel, 28, 28, 1, mnistLabels);
// Example: classify a new image
// File testImage = new File("test_digit.png");
// ClassificationResult result = classifier.classify(testImage);
// result.printResults();
}
}

Key Features Covered:

  1. Basic CNN Architecture - Convolutional, pooling, and fully connected layers
  2. Advanced CNN Features - Batch normalization, dropout, advanced optimizers
  3. Data Pipeline - Image loading, preprocessing, and augmentation
  4. Training Pipeline - Complete training with monitoring and evaluation
  5. Transfer Learning - Using pre-trained models like VGG16
  6. Model Inference - Prediction interface with confidence scores
  7. Complete Example - End-to-end MNIST classification

This comprehensive CNN implementation provides everything needed to build, train, and deploy image classification models in Java using modern deep learning techniques.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper