Tutorial
Machine learning builds a model from example data so a computer can make predictions or decisions on new inputs — without being programmed for every case. The goal is generalization: accurate results on samples the model has never seen.
This page walks the main ideas of machine learning and shows each one in SMILE. Paste the snippets into SMILE Studio (or the shell) as you go. When a topic needs more depth, follow the See link to the dedicated page.
How to Follow This Tutorial
- Launch Studio from a project directory that can see the release
data/tree (or copy those files into your project). - Use a Java notebook for the Java tabs, or
./smile scalafor Scala. Kotlin is shown only for loading data. - Later cells reuse names from earlier ones (
zipTrain,formula,forest, …). Run the sections in order, or paste the few setup lines you need.
Learning Paradigms
Tasks differ by the feedback available to the learner.
| Paradigm | Feedback | Typical Problems | SMILE Pages |
|---|---|---|---|
| Supervised | Labelled input–output pairs | Classification, regression, sequence labelling | Classification, Regression, Deep Learning, Time Series, Sequence Labeling, Natural Language Processing |
| Unsupervised | No labels — find structure | Clustering, dimensionality reduction, association rules | Clustering, Manifold Learning, Association Rules |
| Semi-supervised | A few labels + many unlabelled samples | Label propagation, self-training | Classification |
| Self-supervised | Pseudo-labels derived from the data itself | Language modelling, contrastive learning | LLM, Deep Learning |
| Reinforcement | Scalar reward from an environment | Games, robotics, control | Not implemented in SMILE |
Typical ML Workflow
Most projects follow the same lifecycle. The rest of this tutorial walks it with code.
- Problem definition — classification, regression, clustering, …
- Data collection & exploration — Data Processing, Data Visualization.
- Data preparation & feature engineering — clean, encode, scale, impute, select, split. Feature Engineering (including missing-value imputation).
- Model selection & training — fit candidate algorithms via the Unified Training Interface.
- Evaluation — Model Validation (and Hyperparameter Optimization).
- Deployment — serialize a
.smlmodel or serve it with SMILE Serve.
Loading Data
SMILE reads Parquet, Avro, Arrow, CSV, ARFF, LibSVM, JSON, and more. Parsers return a DataFrame. See Data Processing.
import smile.io.*;
import org.apache.commons.csv.CSVFormat;
var weather = Read.arff("data/weka/weather.nominal.arff");
var iris = Read.arff("data/weka/iris.arff");
var format = CSVFormat.DEFAULT.withDelimiter(' ');
var zipTrain = Read.csv("data/usps/zip.train", format);
var zipTest = Read.csv("data/usps/zip.test", format);
import smile.io._
val weather = read.arff("data/weka/weather.nominal.arff")
val iris = read.arff("data/weka/iris.arff")
val zipTrain = read.csv("data/usps/zip.train", delimiter = " ", header = false)
val zipTest = read.csv("data/usps/zip.test", delimiter = " ", header = false)
import smile.io.*
val weather = Read.arff("data/weka/weather.nominal.arff")
val zipTrain = Read.csv("data/usps/zip.train")
USPS files have no header, so columns are named V1, V2, … V1 is the digit label (0–9); the rest are 256 pixel intensities.
The Formula API
A Formula names the response (left-hand side) and predictors (right-hand side), in an R-style way. Omit the RHS to use every remaining column.
import smile.data.formula.Formula;
var formula = Formula.lhs("V1");
var x = formula.x(zipTrain).toArray();
var y = formula.y(zipTrain).toIntArray();
val formula: Formula = "V1" ~ "."
val x = formula.x(zipTrain).toArray()
val y = formula.y(zipTrain).toIntArray()
Features
A feature is a measurable property of each example. Algorithms work better when numeric columns are on comparable scales and categoricals are encoded. Standardization subtracts the mean and divides by the standard deviation — important before neural nets and many distance-based methods. See Feature Engineering.
import smile.feature.transform.Standardizer;
var pixels = zipTrain.drop("V1");
var scaler = Standardizer.fit(pixels);
var xn = scaler.apply(pixels).toArray();
import smile.feature.transform.Standardizer
val pixels = zipTrain.drop("V1")
val scaler = Standardizer.fit(pixels)
val xn = scaler.apply(pixels).toArray()
Supervised Classification
Supervised learning maps features to a known target. For classification the target is a class label. A model that merely memorizes the training set overfits and fails on new data. Ensembles such as Random Forest average many trees and estimate error from out-of-bag (OOB) samples, so a hold-out set is optional for a first look. See Classification.
import smile.classification.*;
var prop = new java.util.Properties();
prop.setProperty("smile.random.forest.trees", "200");
var forest = RandomForest.fit(formula, zipTrain, prop);
System.out.println(forest.metrics());
val forest = randomForest(formula, zipTrain, ntrees = 200)
println(forest.metrics)
Kernel methods (SVM)
Support vector machines work on arrays and a kernel (here Gaussian). For more than two classes, one-versus-one wraps binary SVMs.
import smile.math.kernel.GaussianKernel;
import smile.validation.metric.Accuracy;
var testx = formula.x(zipTest).toArray();
var testy = formula.y(zipTest).toIntArray();
var svm = OneVersusOne.fit(x, y,
(xi, yi) -> SVM.fit(xi, yi, new GaussianKernel(8.0), 5, 1E-3));
System.out.format("Accuracy = %.2f%%%n",
100.0 * Accuracy.of(testy, svm.predict(testx)));
val testx = formula.x(zipTest).toArray()
val testy = formula.y(zipTest).toIntArray()
val svm = ovo(x, y) { (xi, yi) =>
SVM.fit(xi, yi, new GaussianKernel(8.0), 5, 1E-3)
}
println("Accuracy = %.2f%%" format (100.0 * Accuracy.of(testy, svm.predict(testx))))
Regression
Regression predicts a continuous response. Ordinary least squares (OLS) fits a linear model and reports coefficients and residual diagnostics. See Regression.
import smile.regression.OLS;
import smile.data.formula.Formula;
var planes = Read.arff("data/weka/regression/2dplanes.arff");
var lm = OLS.fit(Formula.lhs("y"), planes);
System.out.println(lm);
val planes = read.arff("data/weka/regression/2dplanes.arff")
val lm = lm("y" ~ ".", planes)
println(lm)
Validation
Training metrics are optimistic. Cross-validation repeatedly holds out folds so you estimate error on data the model did not train on. See Model Validation.
import smile.classification.DecisionTree;
import smile.validation.CrossValidation;
var cv = CrossValidation.classification(10, Formula.lhs("class"), iris,
(f, data) -> DecisionTree.fit(f, data));
System.out.println(cv);
val cv = CrossValidation.classification(10, Formula.lhs("class"), iris,
(f, data) => DecisionTree.fit(f, data))
println(cv)
Unsupervised Learning
Without labels, clustering groups similar rows. K-Means partitions vectors into k spherical clusters. See Clustering.
import smile.clustering.KMeans;
var clusters = KMeans.fit(x, 10, 100);
System.out.println(clusters);
val clusters = kmeans(x, k = 10, maxIter = 100)
println(clusters)
Principal component analysis (PCA) finds linear directions of maximum variance — a first step in dimensionality reduction and visualization. Nonlinear methods (t-SNE, UMAP, IsoMap) live on Manifold Learning.
import smile.feature.extraction.PCA;
var pca = PCA.fit(x).getProjection(20);
var z = pca.apply(x);
import smile.feature.extraction.PCA
val pca = PCA.fit(x).getProjection(20)
val z = pca.apply(x)
Neural Nets and Deep Learning
The classic SMILE MLP is a small CPU network for tabular data. Standardize first. For GPU tensors, EfficientNet, and LibTorch training, see Deep Learning. SMILE does not implement GANs, diffusion models, or reinforcement learning.
import smile.base.mlp.*;
import smile.classification.MLP;
import smile.math.MathEx;
import smile.math.TimeFunction;
import smile.validation.metric.Accuracy;
var testxn = scaler.apply(zipTest.drop("V1")).toArray();
var net = new MLP(
Layer.input(256),
Layer.sigmoid(768),
Layer.sigmoid(192),
Layer.sigmoid(30),
Layer.mle(10, OutputFunction.SIGMOID)
);
net.setLearningRate(TimeFunction.linear(0.01, 20000, 0.001));
for (int epoch = 0; epoch < 10; epoch++) {
for (int i : MathEx.permutate(xn.length)) {
net.update(xn[i], y[i]);
}
System.out.format("Epoch %d Accuracy = %.2f%%%n",
epoch, 100.0 * Accuracy.of(testy, net.predict(testxn)));
}
import smile.base.mlp._
import smile.math.TimeFunction
val testxn = scaler.apply(zipTest.drop("V1")).toArray()
val net = new MLP(
Layer.input(256),
Layer.sigmoid(768),
Layer.sigmoid(192),
Layer.sigmoid(30),
Layer.mle(10, OutputFunction.SIGMOID)
)
net.setLearningRate(TimeFunction.linear(0.01, 20000, 0.001))
(0 until 10).foreach { epoch =>
MathEx.permutate(xn.length).foreach(i => net.update(xn(i), y(i)))
println(s"Epoch $epoch Accuracy = %.2f%%" format
(100.0 * Accuracy.of(testy, net.predict(testxn))))
}
Large Language Models
Transformer LLMs generate text by attending over tokens in a context window. SMILE can run Llama and Qwen checkpoints locally and serve an OpenAI-compatible chat API. Library usage and chat examples are on Large Language Model; production serving is on SMILE Serve.
Making Predictions
Call predict on one sample or an array. Soft classifiers
(random forest, neural nets, logistic regression) can also fill a
probability vector.
int label = forest.predict(zipTest.get(0));
int svmLabel = svm.predict(testx[0]);
var proba = new double[10];
forest.predict(zipTest.get(0), proba);
net.predict(testxn[0], proba);
val label = forest.predict(zipTest.get(0))
val svmLabel = svm.predict(testx(0))
val proba = new Array[Double](10)
forest.predict(zipTest.get(0), proba)
net.predict(testxn(0), proba)
Saving and Loading Models
Most SMILE algorithms implement java.io.Serializable. In your
own application you can write a fitted model with a standard
ObjectOutputStream (or smile.io.Write.object)
and load it later for scoring — no SMILE Serve service required.
import java.io.*;
try (var out = new ObjectOutputStream(
new FileOutputStream("forest.sml"))) {
out.writeObject(forest);
}
try (var in = new ObjectInputStream(
new FileInputStream("forest.sml"))) {
var loaded = (RandomForest) in.readObject();
System.out.println(loaded.predict(zipTest.get(0)));
}
import java.io._
val out = new ObjectOutputStream(new FileOutputStream("forest.sml"))
out.writeObject(forest); out.close()
val in = new ObjectInputStream(new FileInputStream("forest.sml"))
val loaded = in.readObject().asInstanceOf[RandomForest]
in.close()
SMILE Serve is stricter: each .sml
file must contain a smile.model.Model
— typically a ClassificationModel or RegressionModel.
That wrapper carries the formula, input schema, train/validation/test
metrics, and tags (id, version, …) that the HTTP
API exposes. A bare algorithm object is skipped at Serve startup.
smile train and Studio’s Kernel Explorer already save this
boxed form.
import java.nio.file.Path;
import smile.io.Read;
import smile.io.Write;
import smile.model.ClassificationModel;
import smile.model.Model;
var boxed = Model.classification("random-forest", formula, zipTrain, zipTest, prop);
boxed.setTag(Model.ID, "usps-rf");
boxed.setTag(Model.VERSION, "1");
Write.object(boxed, Path.of("forest.sml"));
var loaded = (ClassificationModel) Read.object(Path.of("forest.sml"));
System.out.println(loaded.predict(zipTest.get(0)));
import java.nio.file.Path
import smile.io.{Read, Write}
import smile.model.{ClassificationModel, Model}
val boxed = Model.classification("random-forest", formula, zipTrain, zipTest, prop)
boxed.setTag(Model.ID, "usps-rf")
boxed.setTag(Model.VERSION, "1")
Write.object(boxed, Path.of("forest.sml"))
val loaded = Read.object(Path.of("forest.sml")).asInstanceOf[ClassificationModel]
println(loaded.predict(zipTest.get(0)))