Training a neural network is only one part of Machine Learning. A useful model comes from a complete, repeatable workflow that begins with a clearly defined problem and continues through data preparation, evaluation, deployment, and monitoring.
This lesson explains why the stages exist and why their order matters. For focused Java implementation recipes, use the Developer Guide.
Changing this order carelessly can produce misleading evaluation results or a model that behaves differently after deployment.
Begin by stating what the model receives and what it must predict:
Inputs -> model -> target
Then identify the task:
This decision determines the dataset structure, output layer, loss function, evaluation metrics, and interpretation of predictions. If the target is unclear, later implementation decisions cannot be evaluated reliably.
Runtime choices should be explicit and reproducible. The introductory examples use CPU execution because their tabular datasets and networks are small. A more complex environment is useful only when the workload justifies the additional setup and operational cost.
Environment verification belongs in Getting Started, before model-development work begins.
The model can learn only from information represented in the dataset. Before training, confirm:
A program can execute successfully while learning the wrong relationship if the dataset is misunderstood.
Use DFLib to inspect, select, clean, and export model-ready columns before Deep Netts loads the data. See Preparing Data: Clean incomplete tabular data before Deep Netts for examples and data-leakage rules.
TabularDataSet<MLDataItem> dataSet =
DataSets.readCsv(
DATASET_PATH,
NUM_INPUTS,
NUM_OUTPUTS,
true,
","
);
System.out.println("Samples: " + dataSet.size());
System.out.println(
"Columns: " + Arrays.toString(dataSet.getColumnNames())
);
Training and test data have different responsibilities:
| Dataset | Purpose |
|---|---|
| Training set | Learn model parameters and preprocessing values |
| Test set | Estimate behavior on unseen samples |
Shuffle when row order could bias the split, and use a fixed seed when comparing experiments. Do not train on the test set or repeatedly tune decisions against the final test result.
dataSet.shuffle(42);
TrainTestSplit split = dataSet.trainTestSplit(0.8);
DataSet<MLDataItem> trainingSet = split.getTrainingSet();
DataSet<MLDataItem> testSet = split.getTestSet();
Split the dataset before fitting preprocessing:
Complete dataset
|
v
Train/test split
|
v
Fit preprocessing on TRAIN only
|
+------------------+
| |
v v
Transform train Transform test
Fitting preprocessing on the complete dataset lets test information influence training. This is data leakage, and it can make evaluation results look better than they should.
Standardizer standardizer = new Standardizer(trainingSet);
standardizer.apply(trainingSet);
standardizer.apply(testSet);
Architecture should match the prediction problem rather than being complex by default:
Start with a model that is easy to explain and evaluate. Add complexity only when evidence shows that the simpler model is insufficient.
FeedForwardNetwork neuralNet = FeedForwardNetwork.builder()
.addInputLayer(NUM_INPUTS)
.addFullyConnectedLayer(16, ActivationType.TANH)
.addOutputLayer(NUM_OUTPUTS, ActivationType.SOFTMAX)
.lossFunction(LossType.CROSS_ENTROPY)
.randomSeed(42)
.build();
The output layer must represent the target correctly:
| Task | Typical output | Typical loss |
|---|---|---|
| Regression | Linear value | Mean Squared Error |
| Binary classification | Sigmoid probability | Cross-Entropy |
| Multiclass classification | Softmax probabilities | Cross-Entropy |
The activation constrains what the model can output. The loss measures how different that output is from the known target and supplies the training signal.
Training choices include the learning rate, optimizer, epoch limit, batch size, sample shuffling, and stopping conditions. These values are hyperparameters selected by the developer rather than learned as weights.
The goal is not merely to reduce training loss. The model must learn patterns that remain useful on unseen data. Change one major decision at a time and record the configuration so experiments can be compared.
neuralNet.getTrainer()
.setStopEpochs(350)
.setLearningRate(0.001f)
.setOptimizer(OptimizerType.ADAM)
.setShuffle(true);
neuralNet.train(trainingSet);
Compare training behavior with evaluation on unseen data rather than reading training loss in isolation:
| Observation | Likely diagnosis | Reasonable next check |
|---|---|---|
| Training and test performance are both weak | Underfitting | Features, preprocessing, epoch limit, learning rate, or model capacity |
| Training is strong but test performance is much weaker | Overfitting | Data quantity, duplicates, leakage, excessive capacity, or too much training |
| Loss changes sharply or does not settle | Unstable optimization | Learning rate, scaling, batch size, or invalid values |
| Training improves extremely slowly | Slow convergence | Learning rate, preprocessing, activation choice, or architecture |
Change one major factor at a time. Otherwise, even an improved result does not reveal which decision caused the improvement.
Evaluation should answer whether the model is useful for the real prediction problem, not only whether training completed.
Regression commonly examines MAE, RMSE, and R². Classification may examine accuracy, precision, recall, F1, and a confusion matrix. No metric is universally best: the correct choice depends on the target, class balance, baseline performance, and cost of different errors.
Unexpectedly strong results should trigger checks for leakage, duplicates, an unrepresentative split, or target information hidden among the features.
ClassificationMetrics metrics =
(ClassificationMetrics) neuralNet.test(testSet);
System.out.println(metrics);
A trained network expects data transformed exactly as its training inputs were transformed. Treat the model, fitted preprocessing, feature order, class mapping, and relevant metadata as one versioned pipeline.
Saving only learned weights while forgetting preprocessing creates inconsistent predictions after deployment.
neuralNet.setNormalizer(standardizer);
Files.createDirectories(Path.of("models"));
neuralNet.save(MODEL_PATH);
Every prediction input must follow the training contract:
Regression returns a quantity, binary classification usually applies a threshold to a probability, and multiclass classification selects among class probabilities. The application must interpret those outputs explicitly.
float[] probabilities = neuralNet.predict(input);
int predictedClass = 0;
for (int i = 1; i < probabilities.length; i++) {
if (probabilities[i] > probabilities[predictedClass]) {
predictedClass = i;
}
}
Fixed random seeds help compare runs, but reproducibility also requires recording:
Without this context, it is difficult to know whether a result changed because of an intentional improvement or because the experiment started differently.
Before considering an experiment complete, verify that you have:
Model development is a pipeline. Every stage contributes to whether the final prediction can be trusted.
Learn how raw language becomes tokens, numerical representations, model input, and task-specific output.
Next lessonNatural Language Processing Optional
For implementation practice:
Each stage depends on earlier decisions: the problem defines the data, the data defines the model shape, and preprocessing must be preserved for prediction.
Save the trained model, its fitted preprocessing, output labels, and the configuration needed to recreate the input representation.
Was this helpful?
Thank you!