在 Java 生态中做机器学习,选择其实不多。Weka 太老、DL4J 太重、Mahout 要搭 Hadoop。但有一个库,功能全、性能好、API 优雅,却很少有人提起——Smile (Statistical Machine Intelligence & Learning Engine)
这个月初(2026年5月4日),Smile 发布了 6.1.0 版本,代码库已经非常成熟。本文从实战出发,带你快速掌握 Smile 的核心功能。

一、Smile 是什么?

Smile 是一个纯 Java/Scala 的机器学习框架,涵盖:

  • 分类:决策树、随机森林、SVM、KNN、神经网络
  • 回归:线性回归、LASSO、弹性网络、回归树
  • 聚类:K-Means、DBSCAN、GMM、谱聚类
  • 关联规则:FPGrowth
  • 时间序列:ARIMA、季节分解
  • NLP:词袋模型、TF-IDF
  • 数据可视化:SWING 图表、Scala Plot
  • 数值计算:矩阵运算、插值、优化

不依赖 Hadoop/Spark,一个 jar 包搞定所有。
在这里插入图片描述

二、5 分钟上手

2.1 Maven 依赖

<dependency>
    <groupId>com.github.haifengl</groupId>
    <artifactId>smile-core</artifactId>
    <version>6.1.0</version>
</dependency>

注意:Smile 6.1.0 需要 Java 25+。如果你还在 Java 11/17/21,可以用 Smile 3.x 版本:

<version>3.1.1</version>

2.2 第一个示例:分类鸢尾花

Smile 内置了经典数据集,三行代码跑一个分类器:

import smile.classification.RandomForest;
import smile.data.DataFrame;
import smile.data.type.DataTypes;
import smile.io.Read;

// 读取内置鸢尾花数据
DataFrame iris = Read.arff("https://haifengl.github.io/smile/datasets/weka/iris.arff");
double[][] x = iris.select(0, 1, 2, 3).toArray();
int[] y = iris.column("class").toIntArray();

// 训练随机森林
RandomForest model = RandomForest.fit(x, y);

// 预测
int prediction = model.predict(new double[]{5.1, 3.5, 1.4, 0.2});
System.out.println("预测结果: " + prediction);

三、实用场景示例

3.1 分类:预测客户流失

// 准备数据: [使用时长, 月消费, 投诉次数] → 是否流失(0/1)
double[][] X = {
    {12, 89, 0}, {3, 150, 2}, {24, 45, 0},
    {6, 200, 5}, {18, 120, 1}, {1, 300, 8}
};
int[] Y = {0, 1, 0, 1, 0, 1};

// 训练决策树
var tree = smile.classification.DecisionTree.fit(X, Y);

// 预测新客户
double[] newCustomer = {8, 130, 1};
int result = tree.predict(newCustomer);
System.out.println(result == 0 ? "✅ 留存" : "❌ 可能流失");

3.2 聚类:客户分群

import smile.clustering.KMeans;

// 20 个客户的消费数据: [年龄, 年消费额(万)]
double[][] customers = {
    {25, 3.2}, {30, 4.1}, {22, 2.8}, {35, 5.5}, {28, 3.9},
    {45, 7.2}, {50, 8.1}, {42, 6.5}, {55, 9.0}, {48, 7.8},
    {60, 2.1}, {65, 1.5}, {58, 2.8}, {70, 1.2}, {62, 1.9},
    {32, 5.8}, {38, 6.2}, {29, 4.5}, {41, 5.9}, {33, 4.8}
};

// 聚成 3 类
KMeans kmeans = KMeans.fit(customers, 3);

// 查看每个客户的所属类别
for (int i = 0; i < customers.length; i++) {
    System.out.printf("客户%2d (年龄%2d, 消费%.1f万) → 第%d类\n",
            i + 1, (int) customers[i][0], customers[i][1], kmeans.predict(customers[i]));
}

运行后你会发现:年轻高消费一类、中年高消费一类、老年低消费一类——完全符合业务直觉。

3.3 关联规则:购物篮分析

import smile.association.FPGrowth;
import smile.association.FPTree;

// 商品编码: 0=牛奶, 1=面包, 2=啤酒, 3=尿布, 4=鸡蛋
int[][] transactions = {
    {0, 1, 2, 3}, {0, 1, 4}, {2, 3}, {0, 1, 2, 3, 4},
    {1, 2}, {0, 3}, {0, 1, 3}, {0, 2, 3}
};

String[] products = {"牛奶", "面包", "啤酒", "尿布", "鸡蛋"};

// 最小支持度 3 次
FPTree tree = new FPTree(3, transactions);
var itemsets = FPGrowth.apply(tree).toList();

System.out.println("=== 频繁项集 ===");
for (var is : itemsets) {
    if (is.items().length < 2) continue;
    String s = java.util.Arrays.stream(is.items())
            .mapToObj(i -> products[i])
            .collect(java.util.stream.Collectors.joining(" + "));
    System.out.println(s + "  (出现" + is.support() + "次)");
}

3.4 时间序列预测:销售额预测

Smile 的 smile.timeseries 包内置了 ARIMA 模型:

import smile.timeseries.ARIMA;

// 过去 12 个月销售额(万元)
double[] sales = {120, 135, 128, 142, 138, 150,
                  145, 158, 152, 165, 160, 172};

// 自动拟合 ARIMA 模型(自动选择最优参数)
ARIMA model = ARIMA.autoFit(sales);

// 预测未来 3 个月
double[] forecast = model.forecast(3);

System.out.println("=== 预测结果 ===");
for (int i = 0; i < forecast.length; i++) {
    System.out.printf("第%d个月: %.1f万元\n", i + 1, forecast[i]);
}

3.5 NLP:文本 TF-IDF 特征提取

import smile.nlp.BagOfWords;
import smile.nlp.TermFrequency;
import smile.nlp.TextTerms;

// 三份文档
String[] docs = {
    "苹果发布了新的iPhone",
    "苹果公司的财报超出预期",
    "华为发布了新的Mate手机"
};

// 构建词袋
BagOfWords bow = new BagOfWords();
for (String doc : docs) {
    bow.add(doc);
}
TextTerms terms = bow.terms();

// 输出每个文档的 TF-IDF 向量
for (int i = 0; i < docs.length; i++) {
    double[] tfidf = TermFrequency.tfidf(bow, docs[i]);
    System.out.println("文档" + (i + 1) + " 特征维度: " + tfidf.length);
}

四、性能对比

Smile 的性能在 Java ML 库中属于第一梯队。官方数据:

算法 Smile Weka scikit-learn
随机森林 (1万样本×100特征) 0.8s 4.2s 1.1s
K-Means (10万点×50维) 1.2s 5.8s 1.5s
SVM (1万样本) 2.1s 12s 3.0s

(数据来自 Smile 官方基准测试)

五、常见问题

Q1: Smile 6.x 和 3.x 有什么不同?

对比项 3.x 6.x
最低 Java 版本 Java 8 Java 25
包结构 smile.* smile.*(基本一致)
新功能 大数据集成、ONNX 支持
FPGrowth API 包级私有 包级私有(仍未公开)

建议:Java 21+ 用 3.1.1,Java 25+ 用 6.1.0。

Q2: 数据量很大怎么办?

Smile 本身是单机内存计算。大数据场景可以:

  1. 用 Smile 做特征工程 → 导出到 Spark/XGBoost 训练
  2. 使用 Smile 的流式 API(smile.stream)处理增量数据
  3. 结合 Apache Arrow 列式内存格式处理高维数据

六、总结

Smile 是 Java 生态中功能最全面的原生 ML 库,没有之一。如果你在 Java 项目中需要做机器学习,完全没有必要去调 Python 服务——一个 Smile 全搞定。

完整项目示例

本文所有示例代码都在以下目录:

src/main/java/org/ml/learn/MarketBasketExample.java

运行方式:

mvn exec:java -Dexec.mainClass="org.ml.learn.MarketBasketExample"

更多推荐