- Determine the structure of the sentence
- Use your knowledge to understand what that structure means
- Use your background knowledge to make inferences
- Learn new facts based on what was said
- Generate a reply
Talking Owl Project: Developer's Journal
The Talking Owl Project is an experiment in Community Artificial Intelligence: a place where you can go to talk to AI Chatbots (the "talking owls"), create your own, and watch them talk to each other. This blog is journal of technical notes produced during the development of the Talking Owl Project website. If you are a developer you might find it interesting, but be warned: it's kept like a diary. Absolutely no attempt has been made to make it accessible, organized or coherent.
Tuesday, October 4, 2011
How does a Talking Owl reply to a query?
Saturday, September 24, 2011
The structure of owl brains
As I'm coding the details of the Talking Owl chatterbots for the Talking Owl Project, I'm discovering something kind of cool about the way that the object/class organization of the code is panning out.
So far, it seems to me that each Talking Owl chatterbot (class Owl) requires four objects as separate components for different aspects of "mental processing" during the processing of input from a user: a component of class LanguageParser, that represents an intermediate syntactic representation of a sentence that exists in short-term memory, and contains the methods to transform a sentence into this syntactic representation; a component of class SynsetStore, that contains the long-term memory lexical knowledge that is required to convert the syntactic representation into meanings; a component of class TripleStore, that contains general world knowledge stored in long-term memory; and a component of class MentalModel, that represents active knowledge in working memory, and integrates the meanings derived from the input sentence with the real-world knowledge retrieved from long-term memory in order to build up a representation of the sentence and inferences that can be concluded from it.
Why is this cool? Because this modularity roughly corresponds to real modularity in the brain!
This is a diagram of a Talking Owl Brain:
The LanguageParser roughly corresponds to the function of Broca's area: language comprehension. The SynsetStore corresponds roughly to the function of Wernicke's area: where lexical knowledge is stored. The TripleStore corresponds roughly to the hippocampus: long-term associative memory. And finally, the MentalModel corresponds roughly to (of course) the pre-frontal cortex: the area where active memory representations are used to create mental models and perform reasoning!
I don't know why I find this so satisfying. Maybe in some way it affirms for me the idea that these functional components of language comprehension really represent true functional modules, so that whether the "Language Comprehension Machine" is being programmed in an object-oriented language or evolved over millions of years, either way the outcome is the same!
Thursday, June 30, 2011
SimpleRDFElement Class: an extension of SimpleXML
In my last post, SimpleXML and Namespace Quirks, I complained about how bad the namespace handling is in the SimpleXML framework in PHP.
Since then, I have searched all over the web looking for pre-made solutions to the problem of parsing RDF XML that is heavy with namespace use, and found nothing that fits my needs. I did find some extensive RDF parsing frameworks written in PHP, but they were way too involved: I don't want to have to install a dozen class files when all I want to do is convert a simple RDF/XML string into triples. I also found some "simple" solutions that were inadequate, like simply replacing the ":" character in the string to an "_" character, so that it no longer had to deal with namespaces at all. (This is a terrible solution because namespace prefixes are just "shortcuts" to a URL, and different people can use different prefix characters to represent the same namespace.)
So, on failure to find anything acceptable, I wrote my own solution.
Please enjoy SimpleRDFElement: a class that extends the SimpleXMLElement class, and that therefore can be used elegantly hand-in-hand with code for SimpleXML
The source code is one file: simplerdfelement.php (opens as plain txt file)
(Obviously save it as a .php file and include it in your php script to use it)
If you have your RDF-style XML stored as text in a string variable $xml, then you can create your SimpleRDFElement Object this way:
$xmlobj = simplexml_load_string($xml,'SimpleRDFElement');
The resulting object, $xmlobj, acts just like a SimpleXMLObject, except you have a few more methods available:
- $xmlobj->getPrefix()
- Returns the namespace prefix of the root element of the object, based on the namespace definitions defined by the XML text.
- $xmlobj->getNamespace()
- Returns the full URI of the namespace of the root element of the object, based on the namespace definitions defined by the XML text.
- $xmlobj->getFullName()
- Returns the full qualified name of the root element, using the prefix-colon-tagname format, e.g. rdfs:Class
- $xmlobj->getFullURI()
- Returns the full URI of the root element, using the expanded URI of the namespace followed by the element tag name, e.g. http://www.w3.org/2000/01/rdf-schema#Class
- $xmlobj->getChildNodes()
- Returns an array of all of the child elements (as SimpleRDFElement Objects) of the current top-level element. Unlike the built-in children() method, this returns all child elements regardless of namespace.
- $xmlobj->getAttributes()
- Returns an array of all of the attributes (as individual SimpleRDFElement Objects) of the current top-level element. Unlike the built-in attributes() method, this returns all attributes regardless of namespace.
- $xmlobj->getTriples()
- Returns an array SimpleRDFTriple objects. This is a simple helper class that defines an object with three properties: tripleSubject, triplePredicate, tripleObject. This method parses the top level element and constructs triples based on that element, its attributes, and its immediate child elements. It is not recursive.
Wednesday, June 29, 2011
SimpleXML and Namespace Quirks
The knowledge that the Talking Owls have in The Talking Owl Project is represented as RDF Triples. That is, the most basic unit of knowledge is a structure with this format: {Subject, Predicate, Object}. The most rich framework for representing knowledge in RDF Triples is Web Ontology Language or "OWL". That's actually where the pun of the name comes from: the chatbots learn and talk using knowledge that is representing using the "OWL" framework, and thus are "Talking Owls".
RDF Triples are represented in a standard XML format that looks something like this:
<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"> <rdf:Description rdf:id="#owl42"> <rdfs:label>Pigwidgeon</rdfs:label> <rdf:type rdf:resource="urn:concept:TalkingOwl" /> </rdf:Description> </rdf:rdf>
This bit of XML contains two triples of information about an entity that I have whimsically given an ID of #owl42. The "knowledge" about #owl42 expressed in this XML is:
- #owl42 has the label "Pigwidgeon"
- #owl42 is an entity of the type referred to by a URI urn:concept:TalkingOwl
I'm not going to go into any detail explaining RDF and OWL and XML here, so from this point on I'm going to assume that this is basic stuff that you are familiar with.
The thing is, one of the basic operations that we have to do in the Talking Owl Project is to take bits of knowledge serialized as XML strings like the above and convert it into an "object" that represents the triples. The first step in doing this is to parse the string into an XML object tree.
Enter: SimpleXML
This is a very light-weight module of PHP that allows you to manipulate XML object trees. Just a short bit of code will take a text XML string and convert it into an object representation of the tree.
According to the documentation, the function simplexml_load_string($xml) will return a SimpleXML Object if it is able to parse the XML, and false if there is a problem with the XML string.
So I can do this, right?
$xmlobj = simplexml_load_string($xml) or die('Bad XML!');
WRONG.
Use this code with the string of XML provided above and you will get the "Bad XML!" message, even though the XML isn't bad.
I know the XML isn't bad, because I do not get the "Bad XML!" message if I do this:
$xmlobj = simplexml_load_string($xml); if ($xmlobj===false) die('Bad XML!');
The function is not actually returning a false value. It is returning an object that is evaluating to false.
So, next, I try this:
$xmlobj = simplexml_load_string($xml); if ($xmlobj===false) die('Bad XML!'); print_r($xmlobj);
The result is a completely empty object:
SimpleXMLElement Object ( )
But now I will show you what was really making me tear my hair out. The object is not actually empty. I can use methods on the SimpleXML object to get the namespaces and even to re-construct an XML string from the object, and both show that the contents of the XML are correctly represented.
This is the code:
$xmlobj = simplexml_load_string($xml); if ($xmlobj===false) die('Bad XML!'); print_r($xmlobj->getNamespaces()); print_r($xmlobj->asXML());
This is the result:
Array ( [rdf] => http://www.w3.org/1999/02/22-rdf-syntax-ns# [rdfs] => http://www.w3.org/2000/01/rdf-schema# ) <?xml version="1.0"?> <rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"> <rdf:Description rdf:id="#owl42"> <rdfs:label>Pigwidgeon</rdfs:label> <rdf:type rdf:resource="urn:concept:TalkingOwl" /> </rdf:Description> </rdf:rdf>
It's obvious that the data is there. But, the object evaluates to false, and print_r shows it as an empty object.
So how do I get at the contents of the object?
The PHP documentation discusses a few methods to get inside an object, including the children() method. So let's try viewing the children.
print_r($xmlobj->children());
No dice: the result is still an empty object:
SimpleXMLElement Object ( )
What if we don't trust print_r() so we iterate over the children instead?
foreach ($xmlobj->children() as $node) { print($node->getName().'<br />'); }
Also no dice! This returns nothing at all.
So this still looks like an empty object.
Now, if you look at the documentation that specifically talks about namespaces, you will find that you can use the children() method with a parameter, to get the children that belong to a specific namespace. So, since my little example document above only has one child (rdf:Description), I can try it with the namespace parameter:
print_r($xmlobj->children('rdf',true));
Now this gives us the result we want!
SimpleXMLElement Object ( [Description] => SimpleXMLElement Object ( [@attributes] => Array ( [id] => owl42 ) [type] => SimpleXMLElement Object ( [@attributes] => Array ( [resource] => urn:concept:TalkingOwl ) ) ) )
This is much better: it returns a node representing the rdf:Description node and it's children!
No, wait.
....and some of its children.
Notice that the rdfs:label child is not present in the structure, because it is from a different namespace. We have specified with the children() function that we want direct children only with a specific namespace, and apparently the function interprets that to apply to all subtrees as well.
This is interesting, not necessarily intuitive, and a huge pain when using SimpleXML to parse RDF and OWL strings in particular. With OWL/RDF, there is commonly a mixture of namespaces in the children of any given element.
Despite the fact that some sources on the web (which out of politeness will remain unnamed) claim that the children() method with no parameter will return all children regardless of namespace, this is false: children() with no parameter returns only elements in the default namespace.
Since it is a standard convention in RDF/OWL XML to have no default namespace (i.e. to have every element identified with a namespace prefix), that means that if you create a SimpleXML object from an OWL/RDF XML string, you will most likely get what looks like an empty object, unless you deliberately iterate over every namespace declared in the object. And then, you have to deliberately iterate over every namespace when retrieving the children of each child (because the children of a particular object are often from mixed namespaces). And then you have to iterate over all namespaces for each of their children... and so on.
This isn't particularly difficult to code, per se. Merely cumbersome. And it took me a while to figure out, so I thought I would share the discovery. Apparently, there be no shortcuts for those of you who want to use SimpleXML to parse RDF/OWL documents with multiple namespaces. You will just have to do a lot of namespace looping.
Tuesday, June 21, 2011
The conversation loop: replying to a statement
The actual AI XML web service part of the Talking Owl Project (as opposed to all of the fancy-schmancy UI application front end stuff, that lets you search for Owls to talk to, favorite the conversations you like the most, and all of that "user experience" stuff) really only needs to perform a very basic function: a single arc (or iteration) of the "conversation loop."
In other words: it has to reply to a statement from a user.
As simple as this sounds, I feel like I really need to wrap my head around the best approach to the top-level, abstract steps involved in this kind of operation. When I think about it conceptually (that is, verbally; that is, not in terms of programming or code), the basic steps seem to be this:
- I have some knowledge of the conversation so far in my working memory.
- I get the sentence from the user.
- I "parse" the sentence, or break it down into a useful representation of its parts
- I "understand" the sentence, or come up with some kind of mental representation of what it means
- I add the mental representation of the meaning of the sentence to the knowledge already in my working memory
- I make inferences based on the newly combined mental models: this can include inferring new knowledge, answering questions by filling in gaps, or coming up with questions based on gaps or conflicts that appear.
- Add any interesting knowledge that I've gained into long-term memory.
- Generate a verbal sentence response based on the contents of working memory after all of that has taken place.
So are you can see, there is a lot going on for what seems like a "simple step."
The reality is, when looking at it in terms of pseudo-code, there are additional book-keeping types of steps that need to be in there as well: validating input, logging the user's input in the database, logging the owl's response, and so on.
Additionally, it's important to keep in mind what pieces of the puzzle are involved in what operation. For example, when I refer to "parsing the sentence" I can't simply pseudo-code it as:
inputstructure = inputquery->parse();
or
inputstructure = TalkingOwl::parse(inputquery);
because sentences don't parse themselves, and parsing isn't an objective activity. When a sentence is parsed, it is parsed by a particular brain, and different brains may parse the same sentence in different ways. Thus, the pseudo-code for that step has to look something like,
inputstructure = CurrentOwl->parse(inputquery);
And so, my first stab at the pseudo-code for the top-level of abstraction on this service is something like this:
// Create the OwlQuery object from the input xml parameter // * Parses the string input to find the XML components // * Assigns the XML component values to the query object // * Catches invalid formats or missing data to stop execution // $owlquery = new OwlQuery( $_GET['xml'] ); // Present the query to the Parliament of Owls // * Finds or initializes the active conversation // * Finds the active owl // * Validates that the query is valid for the active user // Parliament::presentQuery( $owlquery ); // Retrieve the current active owl from the Parliament // $owl = Parliament::getOwl(); // Retrieve the current active conversation from the Parliament // $conversation = Parliament::getConversation(); // Initialize the Owl's working memory for the current // conversation. (Note: an owl can carry on multiple // conversations at once, therefore it's "current memory // state" is stored in the database with respect to // each conversation it has. This step is required // so that the Owl object is initialized with the // correct working memory state for THIS conversation.) // $owl->loadMemoryXML( $conversation->getMemoryXML() ); // Use the owl's brain to parse the query sentence into parts // $parsedquery = $owl->parseQuery( $owlquery ); // Use the owl's brain to convert the parsed sentence object // into OWL/RDF triples that represent the meaning of the // sentence. // $querymodel = $owl->understand( $parsedquery ) // Add the mental model of the meaning of the sentence // to the existing working memory of the owl // $owl->loadMemoryXML( $querymodel->getXML() ); // We now have the owl's mental representation of the // conversation WITH the user's input but WITHOUT any // further processing. Log this entry into the // conversation object. // $conversation->logEntry(_USER, $owlquery->text, $owl->getMemoryXML() ); // Run some simple inference and OWL/RDF triple resolution // based on the combination of the two models, and the contents // of long-term memory. This will fill in response nodes to // unbound RDF nodes representing queries, and will generate // query nodes where appropriate. // $owl->infer(); // Store triples in long-term memory, and update weights and parameters // $owl->learn(); // Generate a response from the Owl based on the current // contents of working memory. // $responsetext = $owl->speak(); // Log the owl's contribution to the conversation, along // with the Owl's working memory contents after speaking. // $conversation->logEntry(_OWL, $responsetext, $owl->getMemoryXML() ); // Generate and OwlResponse object based on the most recent entry into // the conversation log. // $owlresponse = new OwlResponse( $conversation ); // Print the response as XML // print( $owlresponse->getXML() );
This will yield XML output in response to the XML string input that it received as the query. Exactly what I want for a back-end REST service. It will be the job of the front-end application UI to take that printed XML and display it as something pretty on the screen.
Sunday, June 19, 2011
Joomla Anniversary Module
This has got to be the most pointless Joomla Module ever, but I will admit, it cracks me up.
First, some background.
A while back, I created a pointless web tool called The Anniversarator, at http://anniversarator.com, which lets you tell it when your relationship started, and it comes up with some kind of nonsense "anniversary" that you can celebrate today. It also works with birthdays. It will produce results like:
Today is the first New Moon after your 30 month anniversary!
Today is your 32 lunar cycle anniversary!
Today is your 10-bit day (1024 days) anniversary!
Today is the first Full Moon after your 1000 day anniversary!
Today is your 1 gross week anniversary (144 weeks)
Today is your 3 Martian year anniversary!
...and so on. It tells you the normal ones, too.
At any rate, on a whim when I created the site I decided I might as well make it available as an XML REST web service, as well, so that if anyone wanted to they could integrate its output into their own iphone apps or websites or what-not.
Joomla has provided the perfect "what-not" opportunity.
First, you have to install the PHP Module Extension (Fiji Web Design). This functions basically like the built-in HTML module, but allows you to include custom PHP as well as HTML an javascript.
Create a PHP Module in Joomla, and use this code:
<?php $database =& JFactory::getDBO(); $user =& JFactory::getUser(); $html = ''; if ($html=='') { $query = "SELECT DATE_FORMAT(`registerDate`,'%Y') as regyear, DATE_FORMAT(`registerDate`,'%m') as regmonth, DATE_FORMAT(`registerDate`,'%d') as regday FROM `jos_users` WHERE `id` = '".$user->get('id')."'"; $database->setQuery( $query ); $rows = $database->loadObjectList(); $regyear = '0000'; $regmonth = '00'; $regday = '00'; foreach($rows as $row){ $regyear = $row->regyear; $regmonth = $row->regmonth; $regday = $row->regday; } $remote_url = 'http://anniversarator.com/?t=anniversary&m='.$regmonth.'& d='.$regday.'&y='.$regyear.'&format=text'; $result = file_get_contents($remote_url); $result = str_replace('anniversary!','anniversary of joining this site!',$result); $html = '<p style="line-height:1.0em; padding:10px;">'.$result.'</p><p style="text-align:right; padding- right:1em; font-size:0.8em;">powered by <a target="_blank" href="http://anniversarator.com/">anniversarator</a></p>'; } print($html); ?>
This will display for the user a module that shows the "anniversary with of joining the website", for example: "This is your 2 fortnight anniversary of joining this site! Congratulations!" will appear in a side-bar box (or where ever you place the module)
Why would you want to display this for your users? I don't know. But there it is, if you want it.
Saturday, June 18, 2011
Joomla Article Dynamic Content: Quick Hack
After searching around for a little while to find a way to easily add custom PHP into Joomla articles as main content, so that main articles could have dynamic content, I came up dry.
I am a huge fan of the PHP Modules extension (Fiji Web Design), but it only allows you to define modules with arbitrary PHP, they will not let me dynamically alter the content of an article based on things like the contents of a database, or a special query string sent with the page URL, or anything like that.
So, I had to come up with a hack on my own.
These are the steps I took:
- Create the article that you want to have dynamic content on. Anything that will be displayed the same all the time, you can enter as normal article text. Any blocks or sections that you want to change dynamically, based on the contents of a database table or whatever, simply put in some kind of special keyword text. So it will look something like this:The most recently created items in our database are:Save your article so that it looks just like that
{{PHP-RECENT-LIST}}
Check them out! - In your installation of Joomla, find the script that actually renders the views of article pages. Usually, this will be found in the folder /components/com_content/views/article in a file called view.html.php
- In this file, there is a function called "display". Somewhere appropriately near the end of this function, insert your code that will replace your custom text label with the HTML that you want
How do you replace the text? The body of the article is stored in a variable called $item->text. So you can simply create a code-block that looks something like this inside the function:
if (strpos($item->text,'{{PHP-RECENT-LIST}}')) { $html = ''; $database =& JFactory::getDBO(); $query = (query that gets the stuff you need); $database->setQuery( $query ); $rows = $database->loadObjectList(); foreach($rows as $row){ $html .= (stuff); } $item->text = str_replace('{{PHP-RECENT-LIST}}', $html, $item->text); }
Now, I know this is a total hack. (At least in the sense that it requires you to modify code that comes with the core Joomla package.) I'm sure there is a more elegant or object-oriented or exensible or modular or otherwise modern-and-or-cool way of doing this. But this is quick and it works. Just make sure you keep these modifications together and well-commented so you don't have trouble hunting them down later when you want to change them