Skip navigation

Whether or not computers will one day achieve true human-level intelligence or be otherwise able to achieve human-level capabilities is a good topic for a long debate and people have strong feelings on both sides. People opposed to the idea that a machine can be capable of thought will usually appeal to intangible notions that appear to be uniquely human such as love, ability to appreciate music, write poetry, etc… In this post, I want to explore the last point: machine written poetry. I don’t plan to prove anyone wrong but it will be fun to try.

In order to generate poetry, I will use a simple/naive approach that goes by many names including Markov text generation and is also known as the Dissociated Press algorithm. If you want to just see the algorithms and some sample poems, scroll down.

Markov Chains

The idea behind a Markov chain is that you have a system with a number of states and at every time-step you move to a new state. How do you choose the next step? At every state you have a list of outgoing transitions to other states and you pick the next state by picking (at random) from the list of transitions. The transitions have weights so you’re more likely to pick some transitions than others.

Let’s take a look at this simple system that I lifted from Wikipedia. In this system you have just two states A and E. Let’s say you start in A at time t = 0. At time t = 1, you need to choose what to do next. You can pick either of the outgoing transitions out of A. So with a probability of 0.4 you might move to state E and with a probability of 0.6 you might move to state A (i.e., you don’t go anywhere.)

Markov Chain with two states A & E. Source: http://en.wikipedia.org/wiki/Markov_chain

Markov Chain with two states A & E. Source: http://en.wikipedia.org/wiki/Markov_chain

Let’s say you picked the transition that keeps you in A. Now at time t = 2 you have to make a choice again, so you flip a weighted coin and pick the transition out of A to E; now you’re at state E. Now at t=2 you’ve gone through the following sequence of states: A,A,E and are in state E. This system is an order 1 Markov chain. It’s order 1 because the transitions and probabilities available to you are a function of only the state you are in currently. You can extend this idea to be a nth order Markov system, in which case your transition probabilities will be a function of your last n states.

This might look as far from poetry as one can get but we’re getting there: if you think of every possible word in the English language as a possible ‘state’ in a Markov chain, then you can randomly move between words to generate strings of words. In order to build this Markov chain, you need to analyze a lot of English text (in our case poems) and record how often a given word is followed by another word. If word w1 is followed by word w2 10% of the time then draw a transition between w1 and w2 and give it a weight of 0.1, etc… To do this you need a large corpus of “training text”. The machine needs to learn from examples of poetry in order to write poetry. You can even generalize this to create a second-order Markov chain. This method has been used to generate music and other types of texts.

Algorithm

Turns out that you don’t need to explicitly pre-calculate the transition matrix. Instead you can try this algorithm:

  1. Pick a random n-gram of text from your training corpus. Use this n-gram as the beginning of your text (or poem)
  2. For some number of iterations, i:
    1. Take the last n-gram of your poem and find all occurrences of this n-gram in your training corpus
    2. Pick one of the occurrences at random. Take the first word that follows your picked occurrence and append it to your poem. Repeat.

Let’s say you take n=2 and start with the bigram: “to be”. Then you look into your corpus and find that is followed by the following words: “or”, “that”, “happy”, “happy” again and a few others. You pick one of them at random (let’s say “that”) and append it to your growing poem: “to be that”. At the next iteration you look at the last bigram in your poem “be that” and find words that follow it in your corpus, etc…

Results

To test this idea, I used python’s scrapy to scrape the top 500 poems list from http://poemhunter.com and used that as my training corpus. I picked my starting n-gram at random. Here are some sample poems generated for n=2:

say turn it out
doors into the night
who rounded the earth
and everything that is
all that i may
die i hope you
realise that your brothers
yes i know anger
as black as cain
may be i said
to him ambition s
paths appear and bright
meet in her aspect
and her training bra
pity and terror of
eczema and i answer
 —————————————-
the tears and sighs
of the poor streets
where only a few
children fumbled with bare
red fingers in the
frosty silence tlot tlot
in the darkness and
then and there she
stands as if she
were alive i call
that piece a wonder
now fr pandolf chanced
to say that health
and wealth have missed
me say i love
an earnest soul
whose

I’d say these are not bad — with some editing these could probably earn a passing grade in an intro to poetry class!

Some notes:

  • The higher the value of n (i.e. trigram, quadgrams, etc…) the less likely you are to find the n-gram in the corpus which means that you’ll just be copying an existing poem — unless your corpus is huge. The lower your n-gram, the less sense your “poems” will make. I found n=2 to be a good happy ground. If I could get a bigger collection of poems, I could probably go to n=3.

Introduction

I’m working on a project for my Database Systems course. As part of the project, my project partner and I want to be able to connect to our MySQL database and use Weka to train a classifier based on the data found in the database and then use that classifier to make predictions about unseen (future) data. A pretty typical ML exercise. Here’s how it’s done.

For those who don’t know Weka is a machine learning utility and java machine learning library. You can learn about it here: http://www.cs.waikato.ac.nz/ml/weka/ Weka is pretty snazzy in that it allows you to use dozens (perhaps hundreds?) of their machine learning algorithms through a nice Java OOP interface directly in your code or use it as a prototyping/research/study tool right through its GUI and have access to all those algorithms (I’m talking about Support Vector Machines at the click of a button — that’s huge.)

I’m going to do this on Ubuntu 9.10 and I will assume that you already MySQL installed or have remote access to MySQL. The Weka version I am working with is 3.6.

Installing Weka on Ubuntu

At the command prompt in Ubuntu type:

sudo apt-get install weka

This will get Weka installed. (You can now type “weka” at the command line and click on “Explorer” to play with the GUI.)

Installing Eclipse

(Skip if you don’t want to use Eclipse)
Now that Weka is installed, we are going to install Eclipse. In Ubuntu, at the command prompt type: (If you’re using something other than Ubuntu, then follow your OS’s directions instead.)

sudo apt-get install eclipse eclipse-jdt

Installing the MySQL Driver

In Ubuntu type:

sudo apt-get install libmysql-java

This will place into /usr/share/java/mysql-connector-java.jar the jars necessary to talk to MySQL (this is actually a link to the actual jar located in the same directory with the same name + version number.)

Configuring DatabaseUtils.props

This part is very important. Go grab your favorite file unzipper/extractor utility and open /usr/share/java/weka.jar (actually it’s a link to a JAR of the same name with the Weka version number appended) I just use GNOME and point the file browser  /usr/share/java/weka.jar from there extract: /weka/experiment/DatabaseUtils.props.mysql. Put this file into your home directory but rename it to: DatabaseUtils.props. Open this file and edit the following lines:

# JDBC driver (comma-separated list)
jdbcDriver=org.gjt.mm.mysql.Driver
# database URL
jdbcURL=jdbc:mysql://server_name:3306/database_name
server_name should be changed to your MySQL server (for example, ‘localhost’ or ‘dbase.cs.school.edu.org’) and database_name should be changed to the database you want to use.
In this file there will also be things like: “# string, getString() = 0;    –> nominal”
I haven’t exactly figured out what’s going on here but if you’re going to be using varchar(N) in your database tables you need to add the following line to this table:
VARCHAR=0 #that’s a zero not an “oh”
And if you’re using INT (int) then add this line too:
INT=5
etc…
Don’t forget to save.
See here for more details: http://weka.wikispaces.com/Databases

Creating the Project in Eclipse

(Even if you don’t use Eclipse you need to set your CLASSPATH to locations defined at the bottom of this section, so at least do that.)
Click: File -> New -> Java Project

Fill out: Project name:

Click Next

Click on the Libraries tab

Click on Add External JARs…

Browse to /usr/share/java (may differ by OS) and add “mysql-connector-java.jar” and “weka.jar.”

If  you’re not using Eclipse make sure to set your CLASSPATH to /usr/share/java/mysql-connector-java.jar and /usr/share/java/weka.jar

(Note: If you’re not using Ubuntu 9.10 and even if you are, make sure these files are where I say they are; they may shift around between versions of Java/Ubuntu/Weka.)

Writing the Java Code

For more details check out: http://weka.wikispaces.com/Use+WEKA+in+your+Java+code

Create a new Java file in the Eclipse project you just created or wherever you’re doing your programming. At the top of your file type the following:

import weka.core.Instances;
import weka.experiment.InstanceQuery;

then in the body of a function type:

InstanceQuery query = new InstanceQuery();
query.setUsername(“nobody”);
query.setPassword(“”);
query.setQuery(“select * from whatsoever”);
// if your data is sparse, then you can say so too
// query.setSparseData(true);
Instances data = query.retrieveInstances();

That code comes from this Wiki: http://weka.wikispaces.com/Use+WEKA+in+your+Java+code if you got this far you should be able to use the Weka wiki to go from here. I will add more to this post as I get further myself. For now this is as far as I’ve gotten 🙂

Good luck!

# JDBC driver (comma-separated list)jdbcDriver=org.gjt.mm.mysql.Driver# database URLjdbcURL=jdbc:mysql://server_name:3306/database_name

Design a site like this with WordPress.com
Get started