使用 Groovy™ 和 Apache Ignite 进行威士忌聚类分析
发布时间:2022-10-27 11:13AM
在上一篇博文中,我们探讨了如何使用Apache Wayang(孵化中)和Apache Spark来扩展k-means聚类算法。 接下来,让我们看看另一种用于扩展此问题的有用技术:Apache Ignite。Ignite 团队最近发布了新版本,但旧版本也适用于我们的示例。
开始之前,快速回顾一下这个问题。
威士忌聚类
这个问题旨在寻找完美的单一麦芽苏格兰威士忌。来自86家酿酒厂的威士忌已由专家品鉴师根据12项标准(酒体、甜度、麦芽味、烟熏味、果味等)进行排名。我们将使用K-means算法来计算质心。
K-means 是一种标准的数据科学聚类技术。在我们的案例中,它将具有相似特征(根据12项标准)的威士忌分组到不同的簇中。如果我们有最喜欢的威士忌,很可能可以通过查看同一簇中的其他实例来找到类似的威士忌。如果我们想尝试改变口味,可以在其他簇中寻找威士忌。质心是簇中间的抽象“点”。对我们来说,它反映了该簇中威士忌每项标准的典型度量。
Apache Ignite
Apache Ignite 是一款用于高性能计算的分布式数据库,具有内存级速度。它使节点集群(或称为*网格*)看起来像一个内存缓存。这种解释大大简化了 Ignite 的功能集。Ignite 可用作
-
一个具有SQL查询和事务特性等特殊功能的内存缓存
-
一个内存数据网格,具有在一个或多个分布式数据库之上的高级读穿透和写穿透能力
-
一个超快速且可水平扩展的内存数据库
-
一个用于自定义或内置任务(包括机器学习)的高性能计算引擎
我们主要使用Ignite的最后一项能力。Ignite的*机器学习API*具有专门构建的、集群感知的机器学习和深度学习算法,用于分类、回归、聚类和推荐等。我们将使用其库中的分布式K-means聚类算法。
实现细节
Apache Ignite 具有将数据读入缓存的特殊功能。我们可以使用 IgniteDataStreamer
或 IgniteCache.loadCache()
从文件、流源、各种数据库源等加载数据。这在使用集群时尤为重要。
对于我们的小示例,数据在一个相对较小的CSV文件中,并且我们将使用单个节点,因此我们只使用Apache Commons CSV读取数据。
var file = getClass().classLoader.getResource('whiskey.csv').file as File
var rows = file.withReader {r -> RFC4180.parse(r).records*.toList() }
var data = rows[1..-1].collect{ it[2..-1]*.toDouble() } as double[][]
我们将通过代码配置我们的单节点Ignite数据缓存(但在更复杂的场景中,我们可以将详细信息放在配置文件中)
var cfg = new IgniteConfiguration(
peerClassLoadingEnabled: true,
discoverySpi: new TcpDiscoverySpi(
ipFinder: new TcpDiscoveryMulticastIpFinder(
addresses: ['127.0.0.1:47500..47509']
)
)
)
我们将创建一些辅助变量
var features = ['Body', 'Sweetness', 'Smoky', 'Medicinal', 'Tobacco',
'Honey', 'Spicy', 'Winey', 'Nutty', 'Malty', 'Fruity', 'Floral']
var pretty = this.&sprintf.curry('%.4f')
var dist = new EuclideanDistance()
var vectorizer = new DoubleArrayVectorizer().labeled(FIRST)
现在我们启动节点,填充缓存,运行k-means算法,并打印结果。
Ignition.start(cfg).withCloseable { ignite ->
println ">>> Ignite grid started for data: ${data.size()} rows X ${data[0].size()} cols"
var dataCache = ignite.createCache(new CacheConfiguration<Integer, double[]>(
name: "TEST_${UUID.randomUUID()}",
affinity: new RendezvousAffinityFunction(false, 10)))
data.indices.each { int i -> dataCache.put(i, data[i]) }
var trainer = new KMeansTrainer().withDistance(dist).withAmountOfClusters(5)
var mdl = trainer.fit(ignite, dataCache, vectorizer)
println ">>> KMeans centroids:\n${features.join(', ')}"
var centroids = mdl.centers*.all()
centroids.each { c -> println c*.get().collect(pretty).join(', ') }
dataCache.destroy()
}
结果
以下是输出
[18:13:11] __________ ________________ [18:13:11] / _/ ___/ |/ / _/_ __/ __/ [18:13:11] _/ // (7 7 // / / / / _/ [18:13:11] /___/\___/_/|_/___/ /_/ /x___/ [18:13:11] [18:13:11] ver. 2.14.0#20220929-sha1:951e8deb [18:13:11] 2022 Copyright(C) Apache Software Foundation ... [18:13:11] Configured plugins: [18:13:11] ^-- ml-inference-plugin 1.0.0 [18:13:14] Ignite node started OK (id=f731e4ab) ... >>> Ignite grid started for data: 86 rows X 13 cols >>> KMeans centroids Body, Sweetness, Smoky, Medicinal, Tobacco, Honey, Spicy, Winey, Nutty, Malty, Fruity, Floral 2.7037, 2.4444, 1.4074, 0.0370, 0.0000, 1.8519, 1.6667, 1.8519, 1.8889, 2.0370, 2.1481, 1.6667 1.8500, 1.9000, 2.0000, 0.9500, 0.1500, 1.1000, 1.5000, 0.6000, 1.5500, 1.7000, 1.3000, 1.5000 1.2667, 2.1333, 0.9333, 0.1333, 0.0000, 1.0667, 0.8000, 0.5333, 1.8000, 1.7333, 2.2667, 2.2667 3.6667, 1.5000, 3.6667, 3.3333, 0.6667, 0.1667, 1.6667, 0.5000, 1.1667, 1.3333, 1.1667, 0.1667 1.5000, 2.8889, 1.0000, 0.2778, 0.1667, 1.0000, 1.2222, 0.6111, 0.5556, 1.7778, 1.6667, 2.0000 [18:13:15] Ignite node stopped OK [uptime=00:00:00.663]
我们可以在雷达图中绘制质心特征。
更多信息
-
包含源代码的仓库:WhiskeyIgnite
-
包含类似示例的仓库,使用多种库,包括 Apache Commons CSV、Weka、Smile、Tribuo 等:Whiskey
-
一个类似的例子,直接使用Apache Spark,但使用的是spark-mllib库中内置的并行化k-means,而不是手工编写的算法:WhiskeySpark