Quick Start

SMILE is a comprehensive, high-performance machine learning engine for the JVM. SMILE v6 requires Java 25. Besides Java, SMILE also provides idiomatic APIs for Scala and Kotlin. The core module is self-contained (no native dependencies required for most algorithms). Optionally, native BLAS/LAPACK libraries can be used for accelerated linear algebra, and LibTorch (PyTorch C++) enables GPU-accelerated deep learning and LLM inference.

Installation

Add the relevant artifact(s) to your build tool. All artifacts are published to Maven Central.


<!-- Core ML algorithms (classification, regression, clustering, …) -->
<dependency>
  <groupId>com.github.haifengl</groupId>
  <artifactId>smile-core</artifactId>
  <version>6.0.1</version>
</dependency>

<!-- Deep learning + LLMs (requires LibTorch native libraries) -->
<dependency>
  <groupId>com.github.haifengl</groupId>
  <artifactId>smile-deep</artifactId>
  <version>6.0.1</version>
</dependency>

<!-- Natural language processing -->
<dependency>
  <groupId>com.github.haifengl</groupId>
  <artifactId>smile-nlp</artifactId>
  <version>6.0.1</version>
</dependency>

<!-- Swing + Vega-Lite data visualization -->
<dependency>
  <groupId>com.github.haifengl</groupId>
  <artifactId>smile-plot</artifactId>
  <version>6.0.1</version>
</dependency>
            

dependencies {
    // Core ML algorithms
    implementation("com.github.haifengl:smile-core:6.0.1")

    // Idiomatic Kotlin API (wraps smile-core)
    implementation("com.github.haifengl:smile-kotlin:6.0.1")

    // Deep learning + LLMs
    implementation("com.github.haifengl:smile-deep:6.0.1")

    // NLP
    implementation("com.github.haifengl:smile-nlp:6.0.1")

    // Visualization
    implementation("com.github.haifengl:smile-plot:6.0.1")
}
            

// Idiomatic Scala API (pulls in smile-core transitively)
libraryDependencies += "com.github.haifengl" %% "smile-scala" % "6.0.1"

// Deep learning
libraryDependencies += "com.github.haifengl" % "smile-deep" % "6.0.1"
            

Native BLAS / LAPACK (optional)

Several algorithms (manifold learning, Gaussian Process, MLP) benefit significantly from native BLAS/LAPACK. Install the system libraries:


sudo apt update
sudo apt install libopenblas-dev libarpack2
            

brew install arpack
# If macOS SIP strips DYLD_LIBRARY_PATH, copy the dylib to your working directory:
cp /opt/homebrew/lib/libarpack.dylib .
            

Pre-built DLLs are included in the bin/ directory of the release package. Add that directory to your PATH.

GPU / LibTorch (for Deep Learning & LLMs)

To use smile-deep, the LibTorch native libraries must be on java.library.path. For GPU support, make sure your Bytedeco pytorch classifier matches your CUDA version, e.g. linux-x86_64-gpu-cuda12.4. CPU-only inference works out of the box.

Download the Release Package

For the interactive shell and CLI tools, download the self-contained release package from the releases page. It works on macOS, Linux, and Windows.


# Extract and set up native dependencies
cd bin
./setup        # installs required native libraries
./smile        # launches SMILE Studio (desktop notebook IDE)
    

Build from Source

Clone the repository and build with the Gradle wrapper (requires Java 25):


git clone https://github.com/haifengl/smile.git
cd smile
./gradlew build -x test        # build all modules, skip tests
./gradlew :core:build -x test  # build only the core module
    

To run the tests (requires LibTorch on the library path for smile-deep):


./gradlew :core:test
./gradlew :deep:test -DexcludeTags=integration
    

Interactive Shell

SMILE integrates with JShell (available since Java 9). In the home directory of the release package, run:


$ ./smile shell
            

All SMILE classes are pre-imported. To pass more heap memory:


$ ./smile shell -R-Xmx30G
            

When the shell starts you will see the SMILE banner followed by the JShell prompt:


|  Welcome to Smile  -- Version  6.0.1
===============================================================================
|  Welcome to JShell -- Version 25
|  For an introduction type: /help intro

smile>
            

All SMILE definitions are pre-imported. Type /exit to leave. You can run any valid Java expression:


smile> 2 + 3
$1 ==> 5

smile> var iris = Read.arff("data/weka/iris.arff")
iris ==> [...]

smile> var rf = RandomForest.fit(Formula.lhs("class"), iris)
smile> System.out.println(rf.metrics())
            

Running a Script


// examples/iris.jsh
import smile.classification.RandomForest;
import smile.data.formula.Formula;
import smile.io.Read;

var data    = Read.arff("data/weka/iris.arff");
var formula = Formula.lhs("class");
var rf      = RandomForest.fit(formula, data);
System.out.println(rf.metrics());
            

$ ./smile shell examples/iris.jsh
            

The Scala REPL has all high-level SMILE operators pre-imported:


$ ./smile scala
$ ./smile scala -J-Xmx30G   # with extra heap
            

smile> val iris = read.arff("data/weka/iris.arff")
smile> val rf   = randomForest("class" ~, iris)
smile> println(rf.metrics)

// Tab-completion shows available classifiers:
smile> smile.classification.r
randomForest   rbfnet   rda
            

Running a Script


// examples/iris.sc
val data    = read.arff("data/weka/iris.arff")
val formula = "class" ~ "."
val rf      = smile.classification.randomForest(formula, data)
println(s"OOB error = %.2f%%" format 100 * rf.error)
            

$ ./smile scala examples/iris.sc
            

A Kotlin REPL is also available:


$ ./smile kotlin
            

smile> val iris = read.arff("data/weka/iris.arff")
smile> val rf   = randomForest(Formula.lhs("class"), iris)
smile> println(rf.metrics())
            

Training & Inference CLI

The SMILE shell also exposes a command-line interface for training models, batch prediction, and real-time serving — no code required.

Train a Random Forest on the Iris dataset and save the model:


$ ./smile train \
    --data  data/weka/iris.arff \
    --formula "class ~ ." \
    --model iris_rf.sml \
    random-forest

Training metrics: {
  fit time: 191.678 ms,
  score time: 17.059 ms,
  validation data size: 150,
  error: 6,
  accuracy: 96.00%,
  cross entropy: 0.1316
}
            

Run ./smile train -h for a full list of supported algorithms (ada-boost, cart, gradient-boost, lasso, lda, logistic, mlp, ols, qda, random-forest, rbf, rda, ridge, svm, …) and ./smile train random-forest -h for algorithm-specific options.

Run batch inference on a file using a saved model:


$ ./smile predict \
    --model iris_rf.sml \
    --probability \
    data/weka/iris.arff

0 0.9601 0.0205 0.0194
0 0.9599 0.0206 0.0195
...
            

The optional --probability flag outputs posterior class probabilities alongside the predicted label.

Start an HTTP inference server (powered by Quarkus):


$ ./smile serve --model iris_rf.sml
INFO  Listening on: http://0.0.0.0:8080

# Query a single sample
$ curl -X POST http://localhost:8080/api/v1/models/iris_rf-1 \
       -H "Content-Type: application/json" \
       -d '{"sepallength":5.1,"sepalwidth":3.5,"petallength":1.4,"petalwidth":0.2}'

{"class":0,"probability":[0.9599,0.0207,0.0194]}
            

If --model points to a directory, every .sml file is loaded automatically. Discover available endpoints with GET /api/v1/models.

The /stream endpoint processes a JSON-lines or CSV request body element-by-element with full back-pressure, making it safe to send very large payloads (multi-GB):


# Generate 150,000 rows (iris × 1000) and stream them through the server
$ for i in {1..1000}; do
    tail -n 153 data/weka/iris.arff | head -n 150
  done | curl -H "Content-Type: text/plain" \
              -X POST --data-binary @- \
              http://localhost:8080/api/v1/models/iris_rf-1/stream
            

This completes end-to-end (including HTTP transfer and printing) in under 4 seconds for 150,000 samples.

SMILE Studio

SMILE Studio is an integrated desktop IDE for machine learning and data science. It combines an interactive multi-language notebook, an AI-powered agent panel, a file explorer, and a kernel explorer in a single window. Launch it from the release package:


cd bin
./setup    # first-time: installs native libraries
./smile    # launches SMILE Studio
    

SMILE Studio requires Java 25 and a graphical (non-headless) environment. At least 4 GB RAM; 8 GB recommended for large datasets.

Application Layout

PanelDescription
Notebook (center) Multi-cell editor supporting Java (JShell), Scala, and Python kernels. Supports .java, .jsh, .scala, .sc, .py, and .ipynb files.
AI Agent Panel (right) Three built-in agents: Clair the Analyst (end-to-end ML), James the Java Guru (Java/SMILE), and Guido the Pythonista (Python). Supports slash commands, shell execution, and long-term memory via SMILE.md.
Project Explorer (left) File tree of the current working directory. Double-click any supported source file to open it in the notebook.
Kernel Explorer (left) Live view of runtime variables grouped by type (DataFrames, Matrices, Models, Services). Double-click a Model node to save it as a .sml file and register it as an inference service.

Execution Kernels

KernelHow to useNotes
Java (JShell) Open or create a .java / .jsh notebook Runs in a separate JVM; ZGC, 75% max RAM. New notebooks are pre-populated with all SMILE imports.
Scala Open a .scala / .sc notebook JSR-223 script engine; all SMILE Scala operators available.
Python (iPython) Open a .py / .ipynb notebook Requires pip install ipython.

Key Shortcuts

ShortcutAction
Ctrl + EnterRun cell, stay in current cell
Shift + EnterRun cell, move to next cell (creates one if needed)
Alt + EnterRun cell, insert new cell below
TabAI-powered line completion (requires AI service configured)
Ctrl + = / Ctrl + -Increase / decrease font size globally

AI Agents & Magic Commands

Each cell has a Prompt field: type a natural language description and press Enter to have the AI generate code directly into the editor. The Java kernel also supports Maven dependency magic commands:


//!mvn org.apache.commons:commons-math3:3.6.1
// ↑ resolves the artifact transitively and adds it to the JShell classpath
    

In the Agent Panel, use slash commands to drive the full ML workflow without writing code:

CommandDescription
/trainTrain a model via smile train
/predictRun batch inference via smile predict
/serveStart an inference server via smile serve
/plan <goal>Enter planning mode for multi-step tasks
/memory add <text>Persist project context to SMILE.md
/compactSummarize the conversation to free context window space
/clearClear the current agent conversation

Configure the AI provider (OpenAI, Anthropic, Google Gemini, Azure OpenAI, or Vertex AI) and API key under File > Settings…. MCP (Model Context Protocol) servers can be wired in via .smile/mcp.json for extended tool access.

A Gentle Tutorial

This example demonstrates the end-to-end machine learning workflow in SMILE: loading data, training multiple models, evaluating performance, and making predictions.

1. Loading Data

SMILE supports many data formats: Parquet, Avro, Arrow, CSV, ARFF, LibSVM, JSON, and more. All parsers return a DataFrame.


import smile.io.*;
import org.apache.commons.csv.CSVFormat;

// ARFF file (e.g. Weka datasets)
var weather  = Read.arff("data/weka/weather.nominal.arff");

// CSV with a space delimiter and no header row
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 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")
            

Because the USPS data has no header, the parser names columns V1, V2, … The first column (V1) is the class label.

2. The Formula API

A Formula specifies the model in a symbolic, R-style way. The left-hand side (LHS) names the response variable; the right-hand side (RHS) lists predictors. When the RHS is omitted, all remaining columns are used. Terms can also include functions and interaction terms.


import smile.data.formula.Formula;

// Predict "V1" from all other columns
var formula = Formula.lhs("V1");

// Extract feature matrix and label array
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()
            

3. Random Forest

Random Forest is an ensemble of decision trees trained on bootstrap samples of the data. It estimates accuracy via out-of-bag (OOB) samples, so a separate validation set is not strictly required.


import smile.classification.*;
import smile.data.formula.Formula;

var formula = Formula.lhs("V1");

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());
// Training metrics: {accuracy: 97.28%, ...}
            

val formula: Formula = "V1" ~ "."
val forest = randomForest(formula, zipTrain, ntrees = 200)
println(forest.metrics)
            

4. Support Vector Machine

SVM is a kernel-based classifier. It operates on raw arrays rather than DataFrames, so we extract arrays from the formula first. For multi-class problems, use one-vs-one or one-vs-rest strategies.


import smile.math.kernel.GaussianKernel;
import smile.validation.*;

var testx  = formula.x(zipTest).toArray();
var testy  = formula.y(zipTest).toIntArray();

var kernel = new GaussianKernel(8.0);
var svm    = OneVersusOne.fit(x, y,
                 (xi, yi) -> SVM.fit(xi, yi, kernel, 5, 1E-3));

var pred = svm.predict(testx);
System.out.format("Accuracy = %.2f%%%n",
    100.0 * Accuracy.of(testy, pred));
System.out.format("Confusion Matrix:%n%s%n",
    ConfusionMatrix.of(testy, pred));
            

val testx  = formula.x(zipTest).toArray()
val testy  = formula.y(zipTest).toIntArray()
val kernel = new GaussianKernel(8.0)

val svm = ovo(x, y) { (xi, yi) =>
  SVM.fit(xi, yi, kernel, 5, 1E-3)
}

val pred = svm.predict(testx)
println("Accuracy = %.2f%%" format (100.0 * Accuracy.of(testy, pred)))
            

5. Neural Network (MLP)

The classic SMILE MLP is a lightweight CPU-only network suitable for tabular data. Always standardize features before training a neural network.


import smile.base.mlp.*;
import smile.classification.MLP;
import smile.math.MathEx;
import smile.math.TimeFunction;
import smile.validation.metric.Accuracy;

// Standardize features
var scaler = smile.feature.Standardizer.fit(x);
var xn     = scaler.transform(x);
var testxn = scaler.transform(testx);

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]);
    }
    var pred = net.predict(testxn);
    System.out.format("Epoch %d  Accuracy = %.2f%%%n",
        epoch, 100.0 * Accuracy.of(testy, pred));
}
            

import smile.base.mlp._
import smile.math.TimeFunction

val scaler = smile.feature.Standardizer.fit(x)
val xn     = scaler.transform(x)
val testxn = scaler.transform(testx)

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)))
  val pred = net.predict(testxn)
  println(s"Epoch $epoch  Accuracy = %.2f%%" format
      (100.0 * Accuracy.of(testy, pred)))
}
            

6. Making Predictions

Call predict on a single sample or an array. Soft classifiers (neural networks, random forest, logistic regression) can also return posterior class probabilities.


// Hard prediction (class label only)
int label = forest.predict(zipTest.get(0));
int svmLabel = svm.predict(testx[0]);

// Soft prediction (class label + posterior probabilities)
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)
            

7. Saving & Loading Models

Most SMILE models implement java.io.Serializable. Serialize them to disk for production deployment or Spark scoring.


import java.io.*;

// Save
try (var out = new ObjectOutputStream(
        new FileOutputStream("forest.ser"))) {
    out.writeObject(forest);
}

// Load
try (var in = new ObjectInputStream(
        new FileInputStream("forest.ser"))) {
    var loaded = (RandomForest) in.readObject();
    System.out.println(loaded.predict(zipTest.get(0)));
}
            

import java.io._

// Save
val out = new ObjectOutputStream(new FileOutputStream("forest.ser"))
out.writeObject(forest); out.close()

// Load
val in = new ObjectInputStream(new FileInputStream("forest.ser"))
val loaded = in.readObject().asInstanceOf[RandomForest]
in.close()
            

Deep Learning & LLM Quickstart

The smile-deep module wraps LibTorch (PyTorch C++) and provides GPU-accelerated tensors, neural network layers, pretrained vision models, and a complete LLaMA-3 inference stack. See Deep Learning and LLM for full documentation.

Image Classification with EfficientNet-V2


import smile.vision.EfficientNet;
import smile.vision.ImageNet;
import javax.imageio.ImageIO;
import java.io.File;

// Load pretrained EfficientNet-V2-S (21 M parameters, ImageNet top-1 ~84%)
var model = EfficientNet.V2S();

// The model's built-in Transform automatically resizes and normalises the image
var img = ImageIO.read(new File("dog.jpg"));

try (var logits = model.forward(img)) {          // shape [1, 1000]
    var probs    = logits.softmax(1);
    int classIdx = probs.argmax(1, false).intValue();
    System.out.println(ImageNet.INSTANCE.labelOf(classIdx));
}
    

Training a Custom MLP with LibTorch


import smile.deep.*;
import smile.deep.layer.*;
import smile.deep.tensor.Tensor;

var mlp = new SequentialBlock(
    Layer.relu(784, 256),
    Layer.relu(256, 128),
    Layer.logSoftmax(128, 10)
);

var optimizer = Optimizer.adam(mlp.asTorch().parameters(), 1e-3);
var criterion = Loss.nll();

mlp.train(10, trainDataset, criterion, optimizer,
          new smile.deep.metric.Accuracy(), testDataset);
    

LLaMA-3 Chat Completion


import smile.llm.llama.Llama;
import smile.llm.Message;

var llama = Llama.build(
    "model/Meta-Llama-3-8B-Instruct",        // checkpoint directory
    "model/Meta-Llama-3-8B-Instruct/tokenizer.model",
    /*maxBatchSize=*/ 4,
    /*maxSeqLen=*/    2048,
    /*deviceId=*/     (byte) 0               // CUDA:0; -1 for CPU
);

var reply = llama.chat(
    new Message[]{
        Message.system("You are a helpful assistant."),
        Message.user("Explain gradient descent in one paragraph.")
    },
    /*maxGenLen=*/ 256, /*temperature=*/ 0.7,
    /*topp=*/ 0.9, /*logprobs=*/ false, /*seed=*/ 0L, /*publisher=*/ null
);

System.out.println(reply.content());
    

For streaming output, pass a SubmissionPublisher<String> as the last argument. See Large Language Models for full examples including raw token generation and the OpenAI-compatible REST server.

Fork me on GitHub