![]() |
Site Archive (Complete) | |||
|
ABOUT US |
CONTACT |
ADVERTISE |
SUBSCRIBE |
SOURCE CODE |
CURRENT PRINT ISSUE |
NEWSLETTERS
|
RESOURCES
|
BLOGS
|
PODCASTS
|
CAREERS
|
||||
September 12, 2003
AI Expert Newsletter - September 2003Dennis Merritt
AI Expert Newsletter is all about artificial intelligence in practice. Features include case studies, technology tutorials, product reviews and AI news—plus classic articles from the original AI Expert magazine! Keep up with the latest in logic programming, expert systems, neural networks, genetic algorithms, and fuzzy logic.
AI - The art and science of making computers do interesting things that are not in their nature. I've been meaning to add code corners to the last few newsletters but haven't found the time. So this newsletter will be all about building the types of AI systems discussed in the last few issues. In other words, this is an issue for those who want to roll up their sleeves and create their own AI systems. The two types of AI application studied are chat bots (July 2003 newsletter) and ontologies (June 2003 newsletter). Both are perfect examples of the ideas expressed in the first newsletter (November 2002) about AI applications in general. Both require a knowledge representation language and a reasoning engine that knows how to apply that knowledge. In the case of chat bots, the knowledge is patterns of inputs and responses that drive a dialog. In the case of ontologies, the knowledge is relationships and attributes of words and concepts. A chat bot tool provides a way to represent the knowledge and a way to deploy it. An ontology tool does the same. There are chat bot and ontology tools available, and good ones at that, but they suffer the problems of all AI tools. They make assumptions about how and what types of knowledge can be represented, and further, on how that knowledge is deployed. If the assumptions fit an application area, that's great. But if not... Well that's a very practical line of thought. The simple truth is, it's a lot more fun to play with your own knowledge representation and reasoning engine, and you can get it to do exactly what you want. The programs can be written in any programming language, but we'll use Prolog in the newsletter because it is particularly versatile for a rapid prototyping and experimentation approach to AI concepts, as well as being very compact and fitting better in a newsletter. Logic programs have the added benefit that they read much like program specifications and can be used for the design of applications intended for implementation in other languages. So if you don't like Prolog, read along and code in Lisp, Mozart, Java or the language of your choice. For those who don't know Prolog, there are boxes with explanatory notes. One final note. Design decisions for the programs were always made in favor of clarity, sometimes at the expense of performance, although this will not be an issue until the knowledge base for either program grows to a large size. As always I like to read your feedback, and for any who request it I can e-mail back the source code developed in this article. Chat BotChat bot is the generic name for programs derived from Weizenbaum's original Eliza program, which mimicked a therapist. Eliza was written almost as a joke, to show how mindless pattern-matching rules can be used to generate realistic conversations, thus proving the software was NOT intelligent. Yet chat bots have come closer to passing the Turing test than other types of programs, and Richard Wallace hypothesizes that that is because much human dialog is, in fact, mindless pattern-matching. Pronoun ReversalOne of the main insights in Eliza was how much can be done simply by changing the grammatical person of a sentence, like 'I' to 'you' and 'mine' to 'yours'. This is called pronoun reversal. We'll start with a very simple, and extremely annoying chat bot that does only that, and adds a question mark at the end. To represent the knowledge of which words to reverse, we'll use Prolog facts, which are logically the same as relations in a relational database. Here are the few we'll use for testing, in a relation call me_you/2, the /2 indicating there are two arguments. The choice of the me_you/2 predicate was the first design decision about knowledge representation.me_you(me,you). me_you(i,you). me_you(my,your). me_you(am,are). Input/OutputThe reasoning engine needs an interface to the user or caller. For the chat bot all that is required is a predicate (like a function) that takes an input string of user text and puts out a response string. A predicate respond with two arguments, the input and the response, provides that service. It is referred to in Prolog as respond/2. Any application that can call Prolog could be used to provide the dialog interface with the user, and Prolog is certainly a language that can call Prolog. Here's a simple loop for a console version of the program that can also be run in a Prolog listener (interpreter).
Core Pattern MatcherHow should a sentence be represented internally by the program? Thinking of the sentence as a list of words seems like a good starting point. Proceeding in a top-down manner, here is the implementation of respond/2.respond(InputString, ResponseString) :- string_to_list(InputString, InputWordList), swap_person(InputWordList, SwappedWordList), form_response(SwappedWordList, ResponseWordList), list_to_string(ResponseWordList, ResponseString), !.
string_to_list(String, List) :- string_tokens(String, List). list_to_string(List, String) :- stringlist_concat(List, ` `, String).
recursive condition - Take the first word in the list and call swap_word/2 to see what the replacement word is. Put that word in the output list and recursively call the rest of input list to generate the rest of the output list. swap_person([], []). swap_person([X|Xs], [Y|Ys]) :- swap_word(X,Y), !, swap_person(Xs,Ys).
One last bit and we're ready to run. We form the response, for now, by simply adding a question mark on the end.swap_word(X,Y) :- me_you(X,Y). swap_word(X,Y) :- me_you(Y,X). swap_word(W,W). append/3 is a common list utility in Prolog that appends one list to another. In this case we're adding the list with the single element, ?, to the end of our output list of words. To use a library of common list utilities we need these two lines at the front of the program. We'll use other list utilities later as well. (If you don't have a library, the implementations of various list utility predicates can be readily found and coded directly in the program.)form_response(In,Out) :- append(In, [?], Out). We're now ready to load the program into a Prolog listener and see how it runs.:- load(list). :- import(list). It's already sounding like some people I know.?- consult(chatbot). yes ?- main. DDJ AI Expert ChatBot Sample > i have problems with my computer you have problems with your computer ? > yes yes ? > will you help me will i help you ? > quit quit ? yes Adding Response PatternsThe "intelligence" of a chat bot lies in the patterns it is told to recognize. Lists of words and variables can represent a pattern to look for, and lists that have those same variables in them can be used for various possible responses.
Here's a response pattern for any input with the word "linux" in
it.
That should drive them nuts. Here are a few more response patterns that might come in handy to keep the conversation going. The last one is a catch-all that is used if none of the other patterns match. It does the job of echoing the input with a question mark. Remember that the input word lists have already had the pronouns swapped before they are used in these patterns.response( [_,linux,_], [ [nobody,supports,linux,'.'] ]).
When there are multiple response patterns to pick from, the engine will pick one at random. Here's another run with the same rules, but a different result.?- main. DDJ AI Expert ChatBot Sample > i get a blue screen on my computer wipe your computer clean and install linux . > ok, i did that but now i cannot get linux to work. nobody supports linux . The Response Pattern Matcher The tricky part of the pattern matcher is setting a variable to possibly multiple words in the input. For example, in the response pattern [_,want,to,X] X might match a phrase. That phrase is best represented as a list of words as well, so we will have an output list with embedded lists in it. A list utility, flatten/2, will fix that problem. For example if the input is [i,want,to,learn,to,fly] X will be bound to [learn,to,fly] and the outputs will look like [why,would,you,want,to,[learn,to,fly]]. flatten/2 converts that to [why,would,you,want,to,learn,to,fly]. Here is a new version of form_response/2 that uses the response/2 rules in the knowledge base portion of the application.> i get a blue screen on my computer why do you use windows ? > i like the windows operating system is it strange to like the windows operating system ? > no why not ? > lots of people like windows lots of people like windows ? > yes why ? > it is easy to use it is easy to use ? form_response(Input,Response) :- response(InputPattern, ResponsePatterns), match(InputPattern, Input), random_elem(ResponsePatterns, ResponsePattern), flatten(ResponsePattern, Response).
fill_var/4 is another recursive predicate that walks the input list looking for the first occurrence of a word that matches the word after the variable. It has four arguments:match([], []). match([Xs], IWs) :- var(Xs), !, Xs = IWs. match([Xs,W|PWs], IWs) :- var(Xs), !, fill_var(Xs, W, IWs, IWsLeft), match([W|PWs], IWsLeft). match([W|PWs], [W|IWs]) :- !, match(PWs, IWs).
It is easy in Prolog to test individual predicates such as this one. When the program is loaded in a Prolog listener, any of the predicates can be queried directly.fill_var([], W, [W|IWs], [W|IWs]) :- !. fill_var([X|Xs], W, [X|IWs], IWsLeft) :- fill_var(Xs, W, IWs, IWsLeft). If you are new to Prolog, the best way to learn to understand code like this is to run it in a debugger, which will provide a trace of Prolog execution and variable bindings. Here is a trace of form_response/2 from one type of Prolog debugger.?- match([a,b,c], [a,b,c]). yes ?- match([a,X,d], [a,b,c,d]). X = [b, c] yes ?- match([_,like,X], [you,like,to,fly,planes]). X = [to, fly, planes] yes ?- match([_,like,X], [you,want,to,fly,planes]). no ?- match([a,b,c], [a,r,c]). no This trace illustrates that the rules are tried in the order they appear. So if a user input might match multiple response patterns, the first one will have priority. At this point, all the code is here that is needed to run the program and get the results displayed a few paragraphs back.DDJ AI Expert ChatBot Sample > i like pizza 'CALL':user:form_response([you, like, pizza], H132) 'CALL':user:response(H321, H322) 'EXIT':user:response([H388, blue, screen, H394], [[why, do, you, use, windows, ?], [wipe, your, computer, clean, and, install, linux, .]]) 'CALL':user:match([H388, blue, screen, H394], [you, like, pizza]) 'FAIL':user:match([H388, blue, screen, H394], [you, like, pizza]) 'REDO':user:response([H388, blue, screen, H394], [[why, do, you, use, windows, ?], [wipe, your, computer, clean, and, install, linux, .]]) 'EXIT':user:response([yes, H390], [[why, ?]]) 'CALL':user:match([yes, H390], [you, like, pizza]) 'FAIL':user:match([yes, H390], [you, like, pizza]) 'REDO':user:response([yes, H390], [[why, ?]]) 'EXIT':user:response([no, H390], [[why, not, ?]]) 'CALL':user:match([no, H390], [you, like, pizza]) 'FAIL':user:match([no, H390], [you, like, pizza]) 'REDO':user:response([no, H390], [[why, not, ?]]) 'EXIT':user:response([H388, you, like, H394], [[how, can, you, like, H394, ?], [is, it, strange, to, like, H394, ?]]) 'CALL':user:match([H388, you, like, H394], [you, like, pizza]) 'EXIT':user:match(['[]', you, like, [pizza]], [you, like, pizza]) 'CALL':user:random_elem([[how, can, you, like, [pizza], ?], [is, it, strange, to, like, [pizza], ?]], H348) 'EXIT':user:random_elem([[how, can, you, like, [pizza], ?], [is, it, strange, to, like, [pizza], ?]], [how, can, you, like, [pizza], ?]) 'CALL':user:flatten([how, can, you, like, [pizza], ?], H132) 'EXIT':user:flatten([how, can, you, like, [pizza], ?], [how, can, you, like, pizza, ?]) 'EXIT':user:form_response([you, like, pizza], [how, can, you, like, pizza, ?]) how can you like pizza ? > EnhancementsThere are a number of enhancements that can be made to the sample.
OntologyTools for ontologies are easy to create and play with in Prolog. As always, first a knowledge representation for the ontology is determined, and then tools to use it are developed.Knowledge RepresentationThe entries for the ontology can be stored as property lists, where each word has a property list associated with it. A property list is just a list whose elements are Property = Value. Using this idea for representing ontological knowledge, here are some entries we might want in order to support an application about food.Here are some more entries about people and allergies.word(pizza, [ kind_of = food, contains = cheese, contains = tomato ]). word(broccoli, [ kind_of = food, color = green ]). word(donut, [ kind_of = food, contains = sugar ]). word(spaghetti_sauce, [ kind_of = food, contains = tomato ]). word(joe, [ instance_of = person, allergy = tomato ]). word(jill, [ instance_of = person, allergy = cheese ]). Reasoning EngineNow, armed with typical list utilities, we can write a predicate that can answer questions about the properties of words in our ontology.property(Word, Property, Value) :- word(Word, PropertyList), member(Property = Value, PropertyList).
?- property(pizza, contains, X). X = cheese ; X = tomato ; no ?- property(X, kind_of, food). X = pizza ; X = broccoli ; X = donut ; X = spaghetti_sauce ; no ?- property(X, kind_of, food), property(X, color, green). X = broccoli ; no ?- findall(X, property(pizza, contains, X), L). X = H19 L = [cheese, tomato] yes Applications of the OntologyThe ontology we developed can be used to support other applications. The types of information we stored in the small sample can be used, for example, in an expert system that warns people about what foods they can and cannot eat based on allergies. The rules of that expert system might be written directly in Prolog. Each of these rules finds a person word and a food word and compares the allergy of the person with what the food contains.Note that the ontology has changed the knowledge engineering task for this allergy expert system. Without the ontology, the knowledge would all be stored in the rules. With the ontology, the rules become simple general purpose rules and the knowledge is stored in the ontology. Here's how these two rules can be used.can_eat(Person, Food) :- property(Person, instance_of, person), property(Person, allergy, Allergy), property(Food, kind_of, food), not property(Food, contains, Allergy). cannot_eat(Person, Food) :- property(Person, instance_of, person), property(Person, allergy, Allergy), property(Food, kind_of, food), property(Food, contains, Allergy). ?- can_eat(joe, pizza). no ?- can_eat(joe, donut). yes ?- can_eat(joe, X). X = broccoli ; X = donut ; no ?- cannot_eat(joe, X). X = pizza ; X = spaghetti_sauce ; no ?- can_eat(X, broccoli). X = joe ; X = jill ; no TransitivityTransitivity is an easy enhancement to add to the ontology engine. Transitivity applies to certain properties, such as kind_of. It means that if X kind_of Y and Y kind_of Z then X kind_of Z. We can define certain properties as transitive by specifying it in the knowledge base.Now if we're looking for a value for a property that is transitive, our engine can recursively call itself.transitive(kind_of). transitive(contains). To test it, make one small change to the ontology: have pizza contain spaghetti_sauce instead of tomato.property(Word, Property, Value) :- word(Word, PropertyList), member(Property = Value, PropertyList). property(Word, Property, Value) :- transitive(Property), word(Word, PropertyList), member(Property = Word2, PropertyList), property(Word2, Property, Value). The test will be to see if joe is still not allowed to eat pizza, as the pizza does not directly contain tomato.word(pizza, [ kind_of = food, contains = cheese, contains = spaghetti_sauce ]). ?- property(pizza, contains, X). X = cheese ; X = spaghetti_sauce ; X = tomato ; no ?- can_eat(joe, pizza). no ?- cannot_eat(joe, pizza). yes Chat Bot with OntologyWe can use the ontology we developed to add more intelligence to the patterns in the chat bot rules. In particular we can specify properties to include in the pattern match. Here's a pattern that recognizes when the user is talking about food.
And here's some to add to the annoying behavior of our technical support.
These last two require the addition of some more information in the ontology.response( [_,property(W,kind_of,windows),_], [ [you,should,dump,W,and,switch,to,unix,'.'] ]). response( [_,property(W,kind_of,unix),_], [ [you,should,dump,W,and,switch,to,windows,'.'] ]). And we have to make some small changes to the basic pattern matcher to recognize and handle these property patterns in the rules. The new clauses are indicated in red.word(windows, [ kind_of = operating_system ]). word(unix, [ kind_of = operating_system ]). word(xp, [ kind_of = windows ]). word(w2000, [ kind_of = windows ]). word(linux, [ kind_of = unix ]). Trying the new pattern rules we get a dialog like this.match([], []). match([Xs], IWs) :- var(Xs), !, Xs = IWs. match([Xs,W|PWs], IWs) :- var(Xs), !, fill_var(Xs, W, IWs, IWsLeft), match([W|PWs], IWsLeft). match([property(W,P,V)|PWs], [W|IWs]) :- property(W,P,V), !, match(PWs,IWs). match([W|PWs], [W|IWs]) :- !, match(PWs, IWs). fill_var([], W, [W|IWs], [W|IWs]) :- !. fill_var([], property(W,P,V), [W|IWs], [W|IWs]) :- property(W,P,V), !. fill_var([X|Xs], W, [X|IWs], IWsLeft) :- fill_var(Xs, W, IWs, IWsLeft). ?- main. DDJ AI Expert ChatBot Sample > i have problmes with xp you should dump xp and switch to unix . > ok i just installed linux you should dump linux and switch to windows . > you are going in circles i am going in circles ? > do you wnat to get a pizza when did you last eat pizza ? > yesterday yesterday ? > yes i ate pizza yesterday are you talking about pizza because you are hungry ?
Until next month,
Dennis Merritt
|
|
||||||||||||||||||||||||||||||||||
|
|