Code with Kira

The Current State of ML in Clojure

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

Published: 2024-04-04

Tagged: clojure scicloj machine learning

OSS Updates January and February 2024

I was lucky enough to get funding this year from Clojurists together to work on some open source projects for the Clojure community. It's been a really fun couple of months getting more involved in the ecosystem and having the time to work on some projects that I've long thought would be valuable for the community. This post is a summary of the things I've been working on over the past two months.

Sponsors

First of all, I want to thank the sponsors that make this work possible. We're living through the worst tech job market since I started working as a software engineer, and I'm lucky to have a little bit of time and runway to work on things I find interesting thanks to the generous sponsors who find my work worthwhile.

Right now my work is primarily funded by Clojurists Together and Cognitect/Nubank. Thank you to these major sponsors, and to everyone who contributes to my continued work in the Clojure open source ecosystem.

If you find the work I do valuable, please share it with others or consider supporting it financially. I would love to be able to turn working on this kind of stuff into a sustainable career in the long term.

Clojure Tidy Tuesdays

The main thing I spent my time working on over the past couple of months was a collection of tutorials and guides for working with data in Clojure. The R for Data Science online learning community publishes toy datasets every week for "Tidy Tuesdays" with a question to answer or example article to reproduce. I've been going through them in Clojure, and it's proven a great tool for uncovering areas for future development in the Clojure data science ecosystem.

Other Work

The explorations with the Tidy Tuesday data have been revealing areas where I think we could benefit from more ergonomic ways to work with tablecloth datasets. I started two little projects each with a couple of little wrappers around existing functions to make them easier to use with tablecloth datasets. So far I'm calling them tcstats (for statistical operations on datasets) and tcutils (with miscellaneous dataset manipulation tools that aren't built-in to tablecloth directly).

I am also still working on the Clojure Data Cookbook. I nudged it forward ever so slightly these last couple of months, and I plan to finish it despite the remaining holes in Clojure's data science stack. I would love to also fill these in eventually, but the Cookbook will be a living document that can easily evolve and be updated as new tools and libraries are developed.

Lastly, one of the main missing pieces I'm discovering we really need to work on in Clojure's data science ecosystem is a robust yet flexible graphics library. There are a few great solutions that already exist, but they take different approaches to graphing that can make them a bit clumsy to work with when it comes time to build more complex visualisations. My dream is to implement a proper grammar of graphics in Clojure, giving the Clojure data ecosystem a "professional quality" graphics library, so to speak. Anyway.. there is still tons of work to do here so I'm grateful for the ongoing funding that will allow me to continue to focus a large amount of time on it for the foreseeable future.

Published: 2024-02-29

Tagged: clojure oss updates clojurists together open source

Clojure Tidy Tuesdays

One of the things I'm going to spend my new-found independent software development time on this year is doing TidyTuesdays in Clojure. It's no surprise that I'm a big fan of Clojure, or that I think there is a lack of good "how to do data science with Clojure" content out there. One of the big things I want to accomplish this year is filling this gap.

To that end, I'll be publishing "translations" of the data-fetching scripts into Clojure and implementations of example data analysis from TidyTuesdays, an initiative of the R for data science online learning community.

The idea is that they post a toy dataset each week along with an example article that uses the same or similar data, and then the community posts their solutions and what they did with it around the internet.

I'll be sharing mine in this Clojure Tidy Tuesdays github repo and posting on Mastodon and LinkedIn. Follow along here or there for more!

Published: 2024-01-12

Tagged: clojure tidy tuesdays

Archive