Building Ontologies with Python
An ontology structures and formalizes objects in a domain. It gives us a way to think about how to relate ideas and in order to structure and contextualize knowledge.
Ontologies support logical deductions, using reasoners, and linking knowledge from different sources.
Let’s try building an ontology of Starbucks coffees and their characteristics. We will then use this to classify new, unknown coffees.
Basic Ontology & Classification
You can follow along with this notebook.
So with this sketch of a relationship model, let’s code a basic ontology using owlready2, defining a Coffee, its Roast and Region as a starting point.
Owlready allows us to define ontologies locally or as a webURL, in this case I’m just using a dummy URL. With this empty ontology, onto, I am defining classes. These classes are entities in my model, and subclasses represent child objects.
Relationships can be defined among the classes or as data properties. There are two ways to define a relationship, both involve defining a domain and a range, which I think of as the to and from or source and target;
The second method is more concise and will be used henceforth.
Finally, we are going to define the characteristics of two different coffees and try to classify some unknown samples we’ve come across. These are individuals we’re defining, as distinct from the types of coffees or their relationships; so we will define two individuals and classify them.
Now we can “sync_reasoner()” to try to classify these based on our definitions. These are moved from .is_a “Coffee” to “Veranda” and “Pike”.
More Advanced
With the second notebook, let’s explore some more attributes.
Using some of the same attributes, let’s explore the same data but with some added relationships and attributes;
Disjoint means that individuals cannot belong to both classes simultaneously. So we will define our roasts as disjoint.
On class definitions, we can use logical operators and, or, not.
The restriction .only means if the entities or classes have this relationship it must be to the specified class (not all owners have pets, but if they have pets it must be dogs). The .some means the two must be related (all owners must have pets).
Closed World
By default OWL uses open world reasoning, anything that’s not specifically restricted or prohibited is assumed to be allowed or possible. In this case I’ve created a new “has_processing” relationship for Coffee but not defined it as a .some attribute on any of my coffee types, so when I close the world I get an error from my reasoner;
With the same code, if I comment out close_world, I see no error, that’s because it’s entirely possible, with open world, this relationship exists in the Veranda class as well as it was not explicitly prohibited.
From here I’ll be using the third notebook in the repository.
You’ll also notice that because this is an ObjectProperty, rather than a FunctionalProperty, I use brackets and assign a list of Processing types; that is because Coffee can be processed in more than one way (Washed and Semi-Washed in some of our cases). We can have a list of Processing types in our class definition.
Finally, from_region in this example is not linked to a class, it’s linked to an individual we’ve instantiated from the Region classes. This is because we defined an inverse relationship to region. Now, when we instantiate a Region, we can see which coffees are grown in it without defining this relationship again.
Previously demonstrated using .value to restrict or define which classes for Roast should be in scope, this is not best practice and should be converted to .some . only to restrict, inconsistency results from using .value, as seen here;
We see the Yukon_Blend restricts with .some .only for Roast; when defining coffee3 we must (it will not work otherwise) call Medium_Roast(), the other coffees do not include the parenthesis, this is the result of using .value when restricting or defining the classes and their relationships; .value should only be used for a DataProperty like “has_body”.
FunctionalProperties
Most of the relationships and properties have been FunctionalProperties; these indicate that there can only be one of this type of relationship. A coffee can only have one roast, or one value for ‘body’. The Wash relationship, on the other hand, is an “ObjectProperty”. This means we can have more than one kind of wash, in fact some coffees are Washed and Semi-Washed. This can be seen in the Yukon Blend, which has both and whose class definition contains an or statement to include or allow both;