# Shakespeare, topology, and machine learning

## Tags: projects, research

« Previous post: Scatterplot matrices with gnuplot — Next post: Homology statistics for 2-manifolds »

In this post, I want to briefly explain how to combine methods from “topological data analysis” and “machine learning”. Given a Shakespearean social network, I want to predict whether it is likely to be a comedy or not.

# Preliminaries & terms

Let’s first discuss some terms: a Shakespearean social network is a co-occurrence network generated from an annotated corpus of Shakespeare’s plays. The basic idea is to calculate a graph from the play in which characters that co-occur in a scene are connected by an edge. The edge weight roughly indicates how many lines of text a character has—the idea being that less important characters should be connected by edges with smaller weights.

I already wrote two posts about these networks, with the first one talking about the general ideas behind these networks and providing some basic statistics, and the second one talking about a more formal evaluation in the form of a short paper titled ‘Shall I compare thee to a network?’—Visualizing the Topological Structure of Shakespeare’s Plays.

The short paper focuses more on visualizing structural similarities between plays. Much has happened in the meantime, however. I started working on Aleph, a library for exploring persistent homology. Moreover, encouraged by previous publications (namely, my two papers about analysing clustering algorithms and dimensionality reduction methods), I started to delve a little bit deeper into machine learning.

In some sense, this post is a continuation of my work in that area.

# Topological data analysis & persistent homology

Since I want to keep this post brief (I plan on writing up a more detailed explanation as a follow-up post), I merely focus on the basic idea behind my analysis. Using persistent homology, a method from topological data analysis, it is possible to measure the amount of ‘holes’ in a network. Yes, you read that correctly. While many algorithms describe what is there, persistent homology focuses on what is missing. This paradigm is highly successful and has been applied in many different application domains so far—let me just mention Gunnar Carlsson’s paper Topological pattern recognition for point cloud data as one particularly striking example of this.

At the heart of persistent homology is the concept of a persistence diagram, a two-dimensional scatter plot that depicts scale information about each hole. For every dimension of an input data set, we have a separate persistence diagram. These diagrams are commonly taken as a feature descriptor that summarizes a data set. Their appeal lies in certain structural properties such as robustness. Furthermore, it is possible to calculate the “Wasserstein distance” between them. This makes it possible to compare two extremely complex objects, shapes, or spaces by a rather simple algorithm!

# Distances & kernels

In the aforementioned short paper, I used the Wasserstein distance between persistence diagrams in order to obtain a low-dimensional embedding of Shakespeare’s plays. Proximity in this embedding means that the co-occurrence networks of two plays have a similar structure. Among other things, one of the results of the short paper was that comedies such as A Midsummer Night’s Dream appear to form clusters, while tragedies such as Hamlet appear to be spread out more.

It is thus a natural question to ask whether it is possible to decide if a random Shakespeare play, described as a co-occurrence network, is a comedy or not. This seems like a perfect job for machine learning! Based on the successful usages of persistent homology in other contexts, it seemed natural to me to use it in order to assess the dissimilarity between the networks. There are other options out there for doing this—and it would be an interesting side-project to compare all of them—but seeing that most of my research centres on persistent homology one way or the other, I will stick with it.

In order to unleash the power of machine learning on these unsuspecting networks, we require the definition of a suitable “kernel”. A kernel function permits us to use supervised learning algorithms without ever having to specify coordinates in the feature space. Given the fact that the feature space is extremely hard to describe—notwithstanding current efforts to introduce vector-based representations of persistence diagrams, such as persistence images—kernel methods are a blessing. Thanks to scikit-learn, they can be used quite easily.

There are multiple options for calculating kernels. Here, I experimented a little bit with a simple Wasserstein kernel (obtained from negating the Wasserstein distance values) as well as persistence indicator functions( of which I hope to write more in a subsequent post).

I am leaving out the generation process of the kernel matrices for now, because I plan on describing them in a subsequent blog post: my library for calculating persistent homology is still subject to change and I do not want to prematurely write about APIs that are not yet ready.

# Experiments & results

After generating the kernel matrices with Aleph, we can use “support vector machines” to test how easily we may detect comedies. Some caution is necessary, though: there are only 37 plays, of which 17 are comedies. A standard splitting of the data set into training and test data is thus not useful here because there are not enough samples. I thus opted for “cross-validation techniques”, more precisely leave-p-out cross-validation, with p up to 3.

The results are as follows:

Kernel Accuracy Average ROC AUC
Persistence indicator functions, d=1 0.86 0.91
Persistence indicator functions, d=2 0.73 0.80
Persistence indicator functions, d=3 0.66 0.74
Wasserstein, d=1 0.86 0.95
Wasserstein, d=2 0.81 0.94
Wasserstein, d=3 0.81 0.93

The entries in the third column refer to the area under the curve of the “receiver operating characteristic” and give an idea about how the classifier will perform in general. These values are to be taken with a grain of salt, however, because there are few samples of both classes. I selected the accuracy performance measure because there are only two classes and they are not highly skewed. Given that the probability of a random play being a comedy is about 0.46, it is nice to see that we are able to detect comedies rather well.

Nonetheless, the values in the table are somewhat surprising. First of all, from personal experience I would have expected a slightly better performance from the second Wasserstein distance—it is the one that is commonly used because its local costs resemble the basic Euclidean distance. This has the nice property of suppressing topological features with low persistence values. It thus appears that these features are useful for classification purposes. Paul Bendich suspected something similar in his paper Persistent Homology Analysis of Brain Artery Trees:

That said, there is no guarantee of persistence being correlated with importance, just with reliability. Indeed, one of the findings in Section 5 is that dots of not-particularly-high persistence have the most distinguishing power in our specific application.

# What now?

This was/is a fun project for me as it combines several interesting aspects. I look forward to trying out more kernels, such as the one proposed by Reininghaus et al. in A Stable Multi-Scale Kernel for Topological Machine Learning.

One of the results of this paper was that the Wasserstein distance is theoretically unsuitable as a kernel function. This is somewhat unfortunate because it still appears to work. I sincerely hope that the persistence indicator function proves to be a viable candidate for kernel methods because it can be calculated very quickly and, in contrast to persistence diagrams, permits the definition of a unique mean function.

As soon as time permits, I shall write a more detailed article about the technical details of this experiment. Please take a look at the GitHub repository that I created for this article. Here, you will find the raw data along with some scripts to reproduce the values reported in this blog post.

Have fun and feel free to drop me a line if you are interested in these (or related) methods. I will be more than happy to have an interesting discussion.