Code with Kira

The Current State of ML in Clojure

Published 2024-04-04

I had a really enlightening talk with Daniel Slutsky this week (who is an exceptional data scientist, software engineer, and community organizer I highly recommended meeting if you haven't already) about the current state of the machine learning landscape in Clojure. This post is my attempt to distill it into a summary for the community's benefit, so more people can understand where things are at and what the active developers in this space are working on.

It's no secret I love Clojure and especially working with data in Clojure, but it's fair to say that the Clojure for data science ecosystem is not anywhere near as easy to use or understand as reasonable potential users might expect. This is the main problem I'm focusing on this year, and there is significant effort being put into refining our tools to make them more accessible to a wider audience.

There are already people doing "real" machine learning work in Clojure, though, and below is an overview of what the current state of our libraries and tools are in that area, as of April 2024.

Update 2024-04-08: It's worth mentioning that deep learning and LLM libraries have been intentionally left out of this post in order to keep it a "reasonable" length. There is enough separate work happening in that space that it warrants its own, separate overview.

Summary

There are a lot of links in this post. This table is an attempt to aggregate and summarize them. There are more details worth reading below, but in case you don't have time, this is the gist of it. To make a very long story short, current efforts are heavily focused on consolidating all of these amazing libraries into one (or at least a small number of clearly delineated ones) that is/are easy-to-use, providing a comphrehensive toolkit for doing machine learning in Clojure.

CategoryLibraryDescriptionLicense
Java ML LibrariesTribuoA comprehensive Java ML framework, preferred library for ML workflowsApache 2.0
Smile 2.xCurrently being phased out of main Clojure ML librariesLGPL
Smile 3.xAvoided due to licensingGPL
XGBoost for JVMImplements gradient boosting algorithms, can be used through TribuoApache 2.0
Clojure Wrappersfastmath 2.4.0+Clojure ML/stats library, dependency on Smile 2.xMIT
fastmath 3.xFastmath with no Smile dependencyMIT
fastmath-clusteringFastmath wrapper around Smile clustering, dependency on Smile 2.xEPL-2.0
scicloj.ml.tribuoClojure wrapper for Tribuo, likely to become the main source for ML algorithmsEPL-1.0
scicloj.ml.smileClojure wrapper for more of Smile (than fastmath), likely to be deprecated due to licensingEPL-2.0
scicloj.ml.xgboostClojure wrapper for XGBoost directlyEPL-1.0
tech.ml.datasetCore dataframe/dataset library in Clojure, incorporates some Tribuo functionalityEPL-1.0
tech.mlEarly Clojure ML library, superceded by various scicloj.ml librariesEPL-1.0
Clojure ML PipelinesmetamorphClojure function composition libraryEPL-2.0
metamorph.mlClojure library for composing ML pipelines based on metamorphEPL-2.0
Collections/Frameworksscicloj.mlCollection of Clojure ML libraries and documentation, being deprecated in favour of nojEPL-2.0
nojConsolidated Clojure data science toolkit, likely to become the main, unified entry-point for Clojure's data science stackEPL-1.0
Interoplibpython-cljPython bindings for Clojure, enabling use of Python code and libraries directly from ClojureEPL-2.0
sklearn-cljUtilizes libpython-clj for access to scikit-learn estimators and models from ClojureEPL-1.0
clojisrBridge from Clojure to R, less relevance for ML compared to Python interopEPL-2.0

In addition to all of these libraries, the post mentions the Clojurians Zulip, the main Clojure-for-data-science community discussion forum, where main contributors to the ecosystem are active daily.

Java ML Libraries

There are two (sort of four) popular Java libraries that implement many of the main algorithms and tools used in machine learning today (e.g. classification, regression, clustering, model development, etc.): Tribuo (including XGBoost, more on that in a second) and Smile. We count Smile as two libraries because Smile 2.x is LGPL-licensed, and Smile 3.x is GPL-licensed, which poses some potential conflicts for some end users. The community consensus is converging around moving away from Smile due to the GPL-relicensing issue, focusing instead on Tribuo and hand-rolled solutions.

There is also XGBoost for the JVM, mentioned above, which is an implementation of gradient boosting. XGBoost is a collection of algorithms whereas Tribuo is a more comprehensive framework (including things like data management, model evaluation, and experiment tracking). XGBoost can be used from Tribuo, so I don't exactly count it as a standalone library, although it can also be used in that way.

Clojure wrappers

There are two main "families" of libraries that wrap these Java ML libraries in Clojure.

Fastmath includes statistical as well as machine learning tools for Clojure. Fastmath 2.4.0+ depends on Smile 2, and the forthcoming fastmath 3.x will have no Smile dependency at all. The clustering functionality in fastmath 2.x that depended on smile has been moved to the fastmath-clustering library, which will have a Smile 2.x dependency going forward. There is a strong preference in the community to avoid introducing GPL-licensed libraries into the ecosystem.

Clustering functionality will mostly be provided by scicloj.ml.tribuo going forward which, as you might expect, wraps the Tribuo Java library, and is likely to become the main source of ML algorithms for the ecosystem. This is one of a few libraries in the second family of libraries that wrap the Java libraries mentioned above. Other (self-explanatory) ones include scicloj.ml.smile, which wraps more of Smile than fastmath did (does), and scicloj.ml.xgboost.

It's also worth mentioning tech.ml.dataset (the core dataframe/dataset library underlying tablecloth), which incorporates some of the functionality of tribuo, with the API centred around individual datasets. There also used to be a library called tech.ml, which implements some machine learning tools, but has been deprecated in favour of the various libraries discussed above.

The concept of orienting an API around individual datasets vs something else leads me to the next group of libraries.

Clojure ML Pipelines

Metamorph is a library that implements a function composition mechanism for composing ML pipelines. It arises from the common ML practice of repeatedly running the same set of functions with varied parameters. You might, for example, try many different test/train splits to see how that affects your results, or fit the same data using many different algorithms, or try training your model using different sets of features. This leads to an explosion of pipeline permutations, so it's useful to have machinery to encapsulate the variable components of your ML pipeline into a single function. This is where metamorph.ml comes in.

Metamorph.ml is based on this concept of meta-functions and pipelines. It is currently the central library for orchestrating ML pipelines in Clojure. The API is stable, but there are currently many ways (10+) to achieve the same outcomes. This is great for power users who have complex needs and a clear understanding of the metamorph mental model, but it can be a bit daunting for newcomers, making it more challenging to pick a clear place to start. The community is actively discussing the best approach for consolidating and/or documenting these different approaches in the interest of making Clojure's ML stack more accessible.

Collections/Frameworks

The community is well aware that it is difficult to know where to get started and several efforts have been made in an attempt to make the path more clear for people who want tools that Just Work. scicloj.ml is one such project. It's a collection of libraries (mostly the ones mentioned above) with some lightweight wrappers and efforts in creating documentation.

The community is heading toward deprecating this library, though, in favour of noj, which we are hoping to stabilize in the near future. The goal is to have a single entry-point into the Clojure data science stack, gathering all the tools one would need to work with data consolidated in one place, with seamless interoperability akin to R's tidyverse of libraries.

Interop

It wouldn't be a complete roundup of the state of ML in Clojure without a mention of libpython-clj. This is a library that provides Python bindings for Clojure, so you can call Python code directly from Clojure if necessary. sklearn-clj makes use of this bridge to provide direct access to all of the estimators and models from Python scikit-learn in Clojure, so for cases where something is truly only available in Python, we can still access it.

It's worth also briefly mentioning clojisr here, which is a similar kind of bridge from Clojure to R (and there exist libraries for Julia and Wolframite, too), but these are all less relevant for the specific area of ML, where Python is the overwhelmingly most popular current tool of choice.

More updates

These discussions all happen in the open, on the Clojurian's Zulip instance, which has become the main gathering place of the Clojure-for-data-science community. The #data-science and #noj-dev streams are the most active on these topics at the time of this writing. You can follow along with developments in the trenches over there, or follow the key libraries on github for updates (scicloj.ml.tribuo, metamorph.ml, noj). I will also post periodic updates here and all the other corners of the internet where I lurk. Thank you for reading!

Discuss this post on Hacker News or Reddit

Tagged: clojure scicloj machine learning

Archive