This walkthrough builds a computer hardware performance predictor one part at a time. It demonstrates multiple linear regression: predicting one continuous numerical value from several numerical features.
Problem: Predict published relative computer performance from six hardware characteristics.
Dataset: 209 computers with six numerical features and the continuous PRP target.
| ML task | Regression |
| Level | Beginner |
| Dataset | 209 computer hardware records |
| Inputs | Six numerical hardware characteristics |
| Target | PRP - Published Relative Performance |
| Model | 6 -> 1 feed-forward network with a linear output |
| Default training | 3000 CPU epochs |
| Related concepts | Machine Learning Basics, Linear Regression, Neural Networks, Backpropagation, Deep Netts API, Model Development |
The model receives six hardware features:
MYCT, MMIN, MMAX, CACH, CHMIN, CHMAX
and predicts one continuous target:
PRP - Published Relative Performance
The complete flow is:
load -> inspect -> split -> standardize -> build -> train -> evaluate -> save -> predict
Start by defining where the dataset and trained model are stored, along with the expected input and output sizes:
private static final String DATASET_PATH =
"src/main/resources/datasets/computer_hardware.csv";
private static final String MODEL_PATH =
"models/computer-hardware-multiple-regression.dnet";
private static final int NUM_INPUTS = 6;
private static final int NUM_OUTPUTS = 1;
NUM_INPUTS matches the six feature columns. NUM_OUTPUTS is one because the model predicts one PRP value. Keeping these values in constants makes the dataset contract visible at the top of the class.
Verify Java and disable CUDA at the beginning of main:
verifyJavaRuntime();
DeepNetts.getInstance().setUseCuda(false);
Output:
WARNING: Using incubator modules: jdk.incubator.vector
The example requires Java 25 with the Vector API enabled. This model and dataset are small, so CPU execution is sufficient and easier to run without CUDA.
Read the prepared CSV with the Deep Netts DataSets API:
TabularDataSet<MLDataItem> dataSet =
DataSets.readCsv(
DATASET_PATH,
NUM_INPUTS,
NUM_OUTPUTS,
true,
","
);
The final two arguments say that the file contains a header and uses a comma delimiter. Deep Netts treats the first six columns as inputs and the final column as the output.
Print the number of samples and column names:
System.out.println("Samples: " + dataSet.size());
System.out.println(
"Columns: " + Arrays.toString(dataSet.getColumnNames())
);
Output:
Samples: 209 Columns: [MYCT, MMIN, MMAX, CACH, CHMIN, CHMAX, PRP]
This small check confirms that the program found the expected file and parsed its structure. It should report 209 samples and the seven expected CSV columns.
Shuffle reproducibly, then 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 seed 42 makes the shuffle repeatable. The model learns from trainingSet; testSet stays unseen until evaluation.
Fit a standardizer on the training data and apply the same transformation to both subsets:
Standardizer standardizer = new Standardizer(trainingSet);
standardizer.apply(trainingSet);
standardizer.apply(testSet);
Hardware features have very different ranges. Standardization puts them on comparable scales, which makes optimization easier. Fitting on training data only prevents test information from leaking into the training pipeline.
Create a network with six inputs and one Linear output:
FeedForwardNetwork neuralNet =
FeedForwardNetwork.builder()
.addInputLayer(NUM_INPUTS)
.addOutputLayer(NUM_OUTPUTS, ActivationType.LINEAR)
.lossFunction(LossType.MEAN_SQUARED_ERROR)
.randomSeed(42)
.build();
The architecture is:
6 -> 1
There are no hidden layers. The network therefore learns a weighted linear combination of the six inputs. Linear activation allows unrestricted numerical predictions, while Mean Squared Error measures regression error during training.
Configure the trainer before passing it the training set:
neuralNet.getTrainer()
.setStopEpochs(3000)
.setLearningRate(0.001f)
.setOptimizer(OptimizerType.SGD)
.setShuffle(true);
neuralNet.train(trainingSet);
Output:
TRAINING NEURAL NETWORK ------------------------------------------------------------------------ Initial Train Error:13374.919 Epoch:1, Time:4ms, TrainError:10256.736, TrainErrorChange:-3118.1826, TrainAccuracy:0.10615444 Epoch:2, Time:1ms, TrainError:6175.8213, TrainErrorChange:-4080.915, TrainAccuracy:0.40210462 Epoch:3, Time:1ms, TrainError:4314.934, TrainErrorChange:-1860.8872, TrainAccuracy:0.56161463 ... Epoch:3000, Time:0ms, TrainError:1249.1343, TrainErrorChange:1.1842041, TrainAccuracy:0.8521152 TRAINING COMPLETED Total Training Time: 475ms ------------------------------------------------------------------------
The middle epochs are omitted for readability. Training error falls sharply in the first few epochs and later stabilizes around 1,249. SGD completes all 3,000 epochs in this run.
Test the trained network with the reserved test set:
RegressionMetrics metrics = (RegressionMetrics) neuralNet.test(testSet);
System.out.println(metrics);
Output:
RegressionMetrics{
r2=0.84360945 Proportion of variance explained by the model. Intuitively how much the model prediction is better than using the mean value as prediction. A value between 0 and 1, where 1 is the best and 0 worst
meanSquaredError=9912.942 Mean/average value of squared errors (the difference betwen actual and predicted value). Highly sensitive to large errors and outliers in inputs. The lower the better ideally 0.
rootMeanSquaredError=99.56376 Squared root of the meanSquaredError. But in the same units as observerd value, makes it easier to interpret, and it is less sensitive to large error values. The lower the better ideally 0.
squaredErrorSum=406430.66 Total sum of squared errors. The lower the better.
meanAbsoluteError=45.59372 Average error. Less sensitive to larger errors and outliers than meanSquaredError. The lower the better ideally 0.
meanAbsolutePercentageError=0.5898638 Mean/average of the absolute errors relative to their targets.Sensitive to relative erors
maxError=504.1809 The biggest error in prediction by the regression model
The held-out r2 of about 0.844 means the model explains a substantial share
of the PRP variance. RMSE, MAE, and the maximum error show that some numerical
prediction errors remain large, so r2 should not be read in isolation.
Attach the fitted standardizer, create the model directory, and save:
neuralNet.setNormalizer(standardizer);
Files.createDirectories(Path.of("models"));
neuralNet.save(MODEL_PATH);
The network was trained on standardized values, so future inputs must use the same transformation. Attaching the standardizer keeps preprocessing together with the saved model.
Represent a new computer using the same feature order as the CSV:
float[] computer = {
26, // MYCT
16000, // MMIN
32000, // MMAX
64, // CACH
16, // CHMIN
24 // CHMAX
};
float predictedPerformance = neuralNet.predict(computer)[0];
System.out.printf("Predicted relative performance: %.2f%n", predictedPerformance);
Output:
Predicted relative performance: 404.55
The result is a continuous predicted PRP value. Feature order must remain
identical to the order used during training.
Pass this option to the Java process through your IDE's application run configuration or the command line:
--add-modules=jdk.incubator.vector
You assembled a complete regression pipeline:
Computer Hardware CSV
|
v
80/20 train/test split
|
v
Standardization
|
v
6 -> 1 linear model
|
v
Training and regression evaluation
|
v
Saved model and new prediction
Hardware performance depends on several processor, memory, and channel measurements, so the network receives all selected numerical features.
PRP is the published relative performance value that the regression network learns to estimate.
Was this helpful?
Thank you!