Why Swift is the Next Big Language for Data Science and Machine Learning
When it comes to data science and machine learning, Python has reigned supreme as the language of choice for many years now. Its vast ecosystem of powerful open source libraries like NumPy, pandas, and scikit-learn, combined with an easy-to-learn syntax, have made it an invaluable tool for data scientists worldwide.
But there‘s a new contender on the block that‘s been generating a lot of buzz lately: Swift. Developed by Apple and open sourced in 2015, Swift has rapidly grown to become one of the most loved programming languages. And now, it‘s making inroads into the world of data science and ML.
As someone who‘s been playing around with Swift for data science projects, I believe it has the potential to not only become a mainstream language in this space, but possibly even dethrone Python one day. Here‘s why:
The Advantages of Swift for Data Science
There are several key reasons why Swift is well-suited for numerical computing and machine learning tasks:
Performance
One of the biggest advantages of Swift is performance. It‘s a compiled language that runs extremely fast, thanks to its tight integration with the LLVM compiler infrastructure. In fact, Swift has been shown to be 8.4x faster than Python at base language benchmarks. This is a huge win for computationally intensive data science and ML workloads.
Syntax
If you‘re coming from a Python background, you‘ll find Swift‘s syntax to be clean, expressive and easy to pick up. It shares many similarities with Python, like type inference, named parameters and first-class functions. At the same time, Swift introduces modern language features like optionals, generics and functional programming patterns.
Here‘s a quick taste of what Swift code looks like:
let numbers = [1, 2, 3, 4, 5]
let squares = numbers.map { $0 * $0 }
print(squares) // [1, 4, 9, 16, 25]
Safety
Swift places a big emphasis on safety and maintainability. It‘s a type-safe language that catches many errors at compile time rather than runtime. It uses optionals to force you to safely handle null values. And it offers many other features that help you write more correct and robust code, like value types, non-nullable types, and exclusive access to memory.
This focus on safety is invaluable in a data science context, where you‘re often working with large, complex codebases and datasets. Having a compiler that can catch errors early helps reduce bugs and improves productivity.
The Swift Data Science Ecosystem
While the Swift data science ecosystem is still young compared to Python‘s, there‘s already an impressive array of libraries and tools available. Here are some of the key players:
Swift for TensorFlow
Developed by Google in collaboration with Apple, Swift for TensorFlow is a powerful library that allows you to build and train machine learning models in Swift. It provides a simple, Keras-like API for constructing models, as well as low-level primitives for more advanced use cases.
One of the coolest things about Swift for TensorFlow is that it‘s not just a wrapper around the underlying TensorFlow C++ code. Instead, it‘s deeply integrated with the Swift language itself, using Swift‘s powerful metaprogramming capabilities to provide a first-class ML experience.
For example, Swift for TensorFlow uses Swift‘s autodiff feature to automatically compute gradients for backpropagation. It also leverages Swift‘s Just In Time (JIT) compiler to optimize and accelerate models on the fly.
Here‘s a snippet of what building a model in Swift for TensorFlow looks like:
import TensorFlow
struct Net: Layer {
var conv = Conv2D(filterShape: (5, 5, 3, 6), activation: relu)
var pool = MaxPool2D(poolSize: (2, 2), strides: (2, 2))
var flat = Flatten()
var dense = Dense(inputSize: 1176, outputSize: 10, activation: softmax)
@differentiable
func call(_ input: Tensor<Float>) -> Tensor<Float> {
return input
.sequenced(through: conv, pool, flat, dense)
}
}
let model = Net()
let optimizer = Adam(for: model)
As you can see, the code is concise and readable, very similar to Keras. But under the hood, Swift for TensorFlow is doing a lot of heavy lifting to make this possible.
Swift for NumPy, pandas and more
Another great thing about Swift is its interoperability with Python. You can easily import and use popular Python libraries like NumPy and pandas directly in your Swift code.
Here‘s an example of using NumPy in Swift:
import Python
let np = Python.import("numpy")
let array = np.array([[1, 2, 3],
[4, 5, 6]])
print(array)
// [[1, 2, 3],
// [4, 5, 6]]
And here‘s how you can create a pandas DataFrame:
let pd = Python.import("pandas")
let df = pd.DataFrame(
[
["apple", 10],
["orange", 20],
["banana", 30]
],
columns: ["fruit", "price"]
)
print(df)
// fruit price
// 0 apple 10
// 1 orange 20
// 2 banana 30
This interoperability is a huge productivity boost. It means you can leverage the vast ecosystem of Python libraries in your Swift code, without having to rewrite everything from scratch.
Core ML
For building ML-powered apps, Apple‘s Core ML framework is a game changer. It allows you to integrate trained machine learning models into your iOS, macOS, watchOS and tvOS apps.
The great thing about Core ML is that it supports a wide variety of popular model formats, including Keras, scikit-learn, XGBoost, LibSVM, and more. You can train a model in your favorite Python-based framework, then convert it to Core ML format and drop it into your Swift app.
Core ML also provides a high-level API for making predictions with your models. For example, here‘s how you can use a trained image classification model to predict the contents of an image:
let model = try VNCoreMLModel(for: MyImageClassifier().model)
let request = VNCoreMLRequest(model: model) { request, error in
guard let results = request.results as? [VNClassificationObservation]
else { fatalError("Unable to get prediction results") }
for result in results {
print("\(result.identifier): \(result.confidence)")
}
}
let handler = VNImageRequestHandler(ciImage: ciImage)
try? handler.perform([request])
In just a few lines of code, we‘re able to load a trained model, make a prediction request, and get back the classification results. Core ML handles all the heavy lifting of optimizing the model for the device‘s hardware and running the inference efficiently.
Putting it All Together: An Example Project
To give you a taste of what it‘s like to build a machine learning project in Swift, let‘s walk through an example of creating a digit recognition model using the MNIST dataset.
We‘ll be using the Swift for TensorFlow library to build and train a convolutional neural network. Here‘s the step-by-step process:
Step 1: Import the necessary libraries
import TensorFlow
import Python
let plt = Python.import("matplotlib.pyplot")
let np = Python.import("numpy")
We‘ll be using the TensorFlow library to build our model, as well as matplotlib and numpy for visualizing the data.
Step 2: Load and preprocess the data
let mnist = MNIST(batchSize: 128)
print("Training data shape: \(mnist.trainingImages.shape)")
print("Test data shape: \(mnist.testImages.shape)")
// Training data shape: [60000, 28, 28, 1]
// Test data shape: [10000, 28, 28, 1]
The MNIST class loads the MNIST dataset and provides helpers for accessing the training and test images and labels. We‘ll be using a batch size of 128 for training our model.
Step 3: Define the model architecture
struct Net: Layer {
var conv1 = Conv2D(filterShape: (5, 5, 1, 32), padding: .same, activation: relu)
var pool1 = MaxPool2D(poolSize: (2, 2), strides: (2, 2))
var conv2 = Conv2D(filterShape: (5, 5, 32, 64), padding: .same, activation: relu)
var pool2 = MaxPool2D(poolSize: (2, 2), strides: (2, 2))
var flat = Flatten()
var dense1 = Dense(inputSize: 3136, outputSize: 1024, activation: relu)
var dense2 = Dense(inputSize: 1024, outputSize: 10)
@differentiable
func call(_ input: Tensor<Float>) -> Tensor<Float> {
return input
.sequenced(through: conv1, pool1, conv2, pool2, flat, dense1, dense2)
}
}
Our model is a simple convolutional neural network with two convolutional layers, two max pooling layers, and two fully connected layers. We define the layers as properties of a Net struct that conforms to the Layer protocol.
The @differentiable attribute on the call function tells Swift for TensorFlow to automatically generate the backward pass for this function, which will be used during training to compute gradients.
Step 4: Create an instance of the model and an optimizer
let model = Net()
let optimizer = Adam(for: model)
We create an instance of our Net model and an Adam optimizer that will be used to train the model.
Step 5: Train the model
for epoch in 1...10 {
var loss: Float = 0
var correct: Int = 0
var total: Int = 0
for batch in mnist.trainingImages.batched(128) {
let (images, labels) = (batch.first, batch.second)
let logits = model(images)
let crossEntropy = softmaxCrossEntropy(logits: logits, labels: labels)
let gradients = gradient(at: model) { model -> Tensor<Float> in
let logits = model(images)
return softmaxCrossEntropy(logits: logits, labels: labels)
}
optimizer.update(&model, along: gradients)
loss += crossEntropy.scalarized()
let predictions = logits.argmax(squeezingAxis: 1)
correct += Int(predictions .== labels).sum().scalarized()
total += images.shape[0]
}
let accuracy = Float(correct) / Float(total)
print("Epoch \(epoch): Loss: \(loss / Float(total)), Accuracy: \(accuracy)")
}
We train the model for 10 epochs, iterating over the training data in batches of 128. For each batch, we:
- Run the model on the input images to get the logits (the raw output of the model before the softmax activation).
- Compute the cross-entropy loss between the logits and the true labels.
- Use autodiff to compute the gradients of the loss with respect to the model parameters.
- Use the optimizer to update the model parameters based on the gradients.
- Keep track of the running loss and accuracy for the current epoch.
After each epoch, we print out the average loss and accuracy on the training set.
Step 6: Evaluate the model on the test set
var testLoss: Float = 0
var testCorrect: Int = 0
var testTotal: Int = 0
for batch in mnist.testImages.batched(128) {
let (images, labels) = (batch.first, batch.second)
let logits = model(images)
testLoss += softmaxCrossEntropy(logits: logits, labels: labels).scalarized()
let predictions = logits.argmax(squeezingAxis: 1)
testCorrect += Int(predictions .== labels).sum().scalarized()
testTotal += images.shape[0]
}
let testAccuracy = Float(testCorrect) / Float(testTotal)
print("Test loss: \(testLoss / Float(testTotal)), accuracy: \(testAccuracy)")
After training, we evaluate the model on the test set to see how well it generalizes to new data. The process is similar to training, but we don‘t update the model parameters.
Step 7: Visualize the results
let idx = Int.random(in: 0..<testTotal)
let sample = mnist.testImages[idx].squeezingShape(at: 0)
let prediction = model(mnist.testImages[idx].expandingShape(at: 0)).argmax().scalarized()
let label = mnist.testLabels[idx].scalarized()
plt.imshow(sample, cmap: "gray")
plt.title("Prediction: \(prediction), Label: \(label)")
plt.show()
Finally, we can visualize a random sample from the test set along with the model‘s prediction and the true label. This gives us a sense of how well the model is performing on individual examples.
And there you have it! A complete example of training a digit recognition model in Swift using convolutional neural networks. The full code for this example is available here.
Conclusion
I hope this article has given you a taste of what‘s possible with Swift for data science and machine learning. While the ecosystem is still young, it‘s growing rapidly, and I believe it has the potential to become a major player in this space.
With its fast performance, clean syntax, focus on safety, and tight integration with powerful libraries like TensorFlow, Swift offers a compelling alternative to Python for data science and ML projects. And with frameworks like Core ML, you can easily deploy your Swift models to production in iOS and macOS apps.
Of course, Python isn‘t going away anytime soon. It will likely remain the dominant language for data science for years to come. But as someone who loves exploring new languages and paradigms, I‘m excited to see how the Swift ecosystem evolves and matures over time.
I predict that in the next few years, we‘ll see more and more data scientists and ML researchers picking up Swift and using it for their projects. And who knows, maybe someday we‘ll even see Swift replace Python as the go-to language for data science!
What do you think? Have you tried using Swift for data science or machine learning? What has your experience been like? Let me know in the comments below!