Commit 872d374c authored by Finzel, Bettina's avatar Finzel, Bettina
Browse files

Added output files, updated Readmes and simplified dialogue

parent 3a951b46
This directory contains the files that are needed to produce an explanatory tree for every positive example and the rule(s) covering it.
The Python script that performs this task, needs the background knowledge without Aleph settings, the prover that was implemented based on a meta-interpreter
and extended with tree writing capabilities. The script furthermore splits the hypothesis (theory) learned by Aleph into individual, traceable rules.
\ No newline at end of file
tracks_down(A,B) :-
is_(B,plant), is_(A,herbivore).
tracks_down(A,B) :-
is_(B,herbivore), is_(A,carnivore).
This directory contains the files that are needed for a dialogue with an user.
The dialogue is currently implemented as an interaction in the console after running the script template-based_responder.py.
In order to run it successfully, both scripts, ILP_model_learner.py and the ILP_rule_prover.py must run first.
The images contained in this repository have been taken from https://www.colourbox.de/.
\ No newline at end of file
{"_default": {"1": {"arg": "argo", "path": "argo.jpg"}, "2": {"arg": "bella", "path": "bella.jpg"}, "3": {"arg": "bobby", "path": "bobby.jpg"}, "4": {"arg": "clover", "path": "clover.jpg"}, "5": {"arg": "dandelion", "path": "dandelion.jpg"}, "6": {"arg": "fluffy", "path": "fluffy.jpg"}, "7": {"arg": "parsley", "path": "parsley.jpg"}, "8": {"arg": "rosemary", "path": "rosemary.jpg"}, "9": {"arg": "samson", "path": "samson.jpg"}, "10": {"arg": "tipsie", "path": "tipsie.jpg"}}}
\ No newline at end of file
tracks_down(bobby,dandelion) is_parent is_(dandelion,plant) .
tracks_down(bobby,dandelion) is_parent is_(bobby,herbivore) .
is_(dandelion,plant) is_parent is_a(dandelion,flower) .
is_(dandelion,plant) is_parent is_(flower,plant) .
is_a(dandelion,flower) is_parent true .
is_(flower,plant) is_parent is_a(flower,plant) .
is_a(flower,plant) is_parent true .
is_(bobby,herbivore) is_parent is_a(bobby,rabbit) .
is_(bobby,herbivore) is_parent is_(rabbit,herbivore) .
is_a(bobby,rabbit) is_parent true .
is_(rabbit,herbivore) is_parent is_a(rabbit,herbivore) .
is_a(rabbit,herbivore) is_parent true .
% tree operators
:- op(500,xfx,'is_parent').
:- op(500,xfx,'is_sibling_of').
X is_sibling_of Y :- Z is_parent X, Z is_parent Y, X \== Y.
leaf_node(Node) :- \+ is_parent(Node,Child).
:- op(500,xfx,'is_at_same_level').
X is_at_same_level Y :- W is_parent X,Z is_parent Y,W is_at_same_level Z.
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 24 21:51:33 2021
@author: Bettina Finzel
"""
from pyswip import Prolog
import regex as re
import os
from shutil import copyfile
import matplotlib.pyplot as plt
import matplotlib.image as img
from tinydb import TinyDB, Query
##### Data base #####
db = TinyDB('db.json')
Image = Query()
def insert(argument, image_path):
db.insert({'arg': argument, 'path': image_path})
def search(argument):
results = db.search(Image.arg == argument)
for result in results:
return result['path']
def update(argument, image_path):
db.update({'path': image_path}, Image.arg == argument)
def show_all():
all_entries = db.all()
print(all_entries)
# comment out to fill database
#insert('argo','argo.jpg')
#insert('bella','bella.jpg')
#insert('bobby', 'bobby.jpg')
#insert('clover','clover.jpg')
#insert('dandelion','dandelion.jpg')
#insert('fluffy','fluffy.jpg')
#insert('parsley','parsley.jpg')
#insert('rosemary','rosemary.jpg')
#insert('samson','samson.jpg')
#insert('tipsie','tipsie.jpg')
# comment out to show all entries in database
#show_all()
# navigate to the path, where the explanatory trees are saved
dialogue_path = os.getcwd()
print("Location of this script: " + dialogue_path)
path_parent = os.path.dirname(dialogue_path)
os.chdir(path_parent)
tree_path = os.getcwd()
print("Trees saved at: " + tree_path)
rule_path = path_parent + "/2_Tree"
print("Rules saved at: " + rule_path)
#
#
##### Types of questions #####
global_expl_part1 = "what does" # done
global_expl_part2 = "mean" # done
class_request = "in what relation are" # done
local_expl_request = "explain why" # done
visual_expl_request = "show me" # done
go_back_request = "go back to"
user_quits = "quit"
user_help = "help"
##### Helper functions #####
def replace_last_occurrence_of_substring(string, substring, replacement):
reversed_string = string[::-1]
replaced = reversed_string.replace(substring[::-1], replacement[::-1], 1)
return replaced[::-1]
# translates Prolog predicate into a sentence
def prolog_predicate_to_sentence(predicate):
# create response
splitted_positive_example = re.split(r"[\(\)]", predicate)
predicate_name = splitted_positive_example[0].replace("_", " ")
predicate_arguments = re.split(r"[\,]", splitted_positive_example[1])
if not "is" in predicate_name:
response = predicate_arguments[0] + " " + predicate_name + " " + predicate_arguments[1] + "."
return response
if "is a" in predicate_name:
response = predicate_arguments[0] + " " + predicate_name + " " + predicate_arguments[1] + "."
return response
else:
response = predicate_arguments[0] + " " + predicate_name + "a " + predicate_arguments[1] + "."
return response
def sentence_to_prolog_predicate(sentence_list):
sentence = sentence_list[1] + "_" + sentence_list[2] + "(" + sentence_list[0] + "," + sentence_list[3] + ")"
return sentence
def prolog_rule_to_sentence(rule):
# create response
response = ""
tabless_rule = re.sub("\t", "", rule)
splitted_rule = re.split(r"[\:-]", tabless_rule)
# process the rule head
rule_head_sentence = prolog_predicate_to_sentence(splitted_rule[0])
rule_head_sentence = rule_head_sentence.replace(".", " ")
response = rule_head_sentence
# remove the first elements in splitted rule
# works, if rules do have a body
for i in range(0,2):
splitted_rule.pop(0)
response = response + "because "
# split rule body by white spaces
splitted_rule = splitted_rule[0].split()
for literal in splitted_rule:
rule_literal_sentence = prolog_predicate_to_sentence(literal)
rule_literal_sentence = rule_literal_sentence.replace(".", " ")
response = response + rule_literal_sentence + "and "
response = replace_last_occurrence_of_substring(response, "and", ".")
return response
# shows the image that contains a prototypical instance of the requested concept
def open_prototype(instance):
# searches path of image for the given instance
image_name = search(instance)
# plots image
image = img.imread(dialogue_path + "/" + image_name)
plt.axis('off')
plt.imshow(image)
plt.show()
##### Request to Prolog #####
# For local explanations
def request_prolog_for_local_explanation(query):
prolog = Prolog()
prolog.consult('explanatory_tree.pl',catcherrors=(False)) # omits singletons and operator error
query = query + " is_parent Y"
# transform response into natural language expression
query_response = list(prolog.query(query))
if not query_response:
return []
else:
response = ""
response = response + "Because "
for soln in query_response:
if "true" in soln.get('Y'):
response = response + "it is true."
else:
response = response + prolog_predicate_to_sentence(soln.get('Y')) + " and "
if not "true" in response:
response = response.replace(".", "")
response = replace_last_occurrence_of_substring(response, "and", ".")
return response
##### Process types of requests #####
def process_request(request,last_explanation):
## GLOBAL ##
if global_expl_part1 and global_expl_part2 in request:
# find the rules for the currently learned class
global_explanation = []
for file in os.listdir(rule_path):
if file.endswith(".pl"):
if "Rule" in file:
#print("File matching the given class: " + file)
rule_file = rule_path + "/" + file
file = open(rule_file, 'r')
lines = file.readlines()
one_line_rule_string = '\t'.join([line.strip() for line in lines])
rule_sentence = prolog_rule_to_sentence(one_line_rule_string)
global_explanation.append(rule_sentence)
last_explanation.append(global_explanation)
return global_explanation
## LOCAL ##
# name field for the file that contains the explanatory tree for the given request
explanatory_tree = ""
if class_request in request:
# split the string to separate the question from the parameters
splitted_request = re.split('(are)', request)
# take the argument string at the last index of the list
index = len(splitted_request)
argument_string = splitted_request[index-1]
# split the argument string at spaces
argument_string = re.split(' ', argument_string)
# iterate over the argument string to remove the question mark and conjunctions
index = len(argument_string)-1
while index > 0:
argument_string[index] = argument_string[index].replace("?", "")
if argument_string[index] == "and":
argument_string.remove(argument_string[index])
index = index - 1
argument_string.pop(0)
# find the file that contains the explanatory tree for the requested arguments
for file in os.listdir(tree_path):
if file.endswith(".pl"):
if all(arguments in file for arguments in argument_string):
#print("File matching the given relation: " + file)
explanatory_tree = file
# set the path for the file, put these files together and save them in the dialogue path
tree_file = os.path.join(tree_path,explanatory_tree)
operators_file = os.path.join(tree_path,"operators.pl")
explanatory_tree_file = os.path.join(tree_path,"explanatory_tree.pl")
# put the Prolog operators and the explanatory tree in one file
with open(tree_file) as tf:
tree = tf.read()
with open(operators_file) as of:
operators = of.read()
# merge
tree += "\n"
tree += operators
with open(explanatory_tree_file, 'w') as et:
et.write(tree)
explanatory_tree_file_new = os.path.join(dialogue_path,"explanatory_tree.pl")
copyfile(explanatory_tree_file,explanatory_tree_file_new)
# extract goal from file name by splitting at second underscore
explanatory_tree_name = explanatory_tree.split(".")
explanatory_tree_name = explanatory_tree_name[0]
positive_example = explanatory_tree_name.split('_',2)[-1]
#print("Positive example matching the arguments: " + positive_example)
# create response
class_relation = prolog_predicate_to_sentence(positive_example)
last_explanation.append(class_relation)
return class_relation
if local_expl_request in request:
sentence_list = request.split()[2:]
query = sentence_to_prolog_predicate(sentence_list)
if not request_prolog_for_local_explanation(query):
sentence_list[2] = ""
query = sentence_to_prolog_predicate(sentence_list)
local_explanation = request_prolog_for_local_explanation(query)
if not local_explanation:
print("Request is not valid.")
else:
last_explanation.append(local_explanation)
return local_explanation
else:
local_explanation = request_prolog_for_local_explanation(query)
last_explanation.append(local_explanation)
return local_explanation
if go_back_request in request:
print(last_explanation[len(last_explanation)-2])
if visual_expl_request in request:
image_request = request.replace(visual_expl_request, "").replace("!", "").replace(" ", "")
open_prototype(image_request)
if user_help in request:
print("You can make the following requests:\n")
print("1. What does <class label> mean (e.g., What does tracks down mean)? to ask for a global explanation.\n")
print("2. In what relation are <A> and <B>? (e.g., In what relation are Bobby and dandelion) to ask for the relation between two instances.\n")
print("3. Explain why <A> <class label> <B> (e.g., Explain why bobby tracks down dandelion) to ask for a local explanation for a relation between two instances.\n")
print("4. Explain why <A> is a <B> (e.g., Explain why Bobby is a herbivore) to drill down a local explanation.\n")
print("5. Show me <A>! (e.g., Show me dandelion!) to see an image of a prototypical instance.\n")
print("6. Go back to last explanation - to return to the last explanation.\n")
print("7. Quit - to quit the application.\n")
last_explanation = []
request = ""
while user_quits not in request:
# Simple dialogue interface for multi-level multi-modal explanations
request = input("Please enter a request (enter help for options): ")
request = request.lower()
response = process_request(request, last_explanation)
if response:
print(response)
print("Goodbye!")
true.
leash(-all).
true.
prove_p(tracks_down(tipsie,rosemary)).
true ;
false.
noprotocol.
tracks_down(bobby,dandelion) is_parent is_(dandelion,plant) .
tracks_down(bobby,dandelion) is_parent is_(bobby,herbivore) .
is_(dandelion,plant) is_parent is_a(dandelion,flower) .
is_(dandelion,plant) is_parent is_(flower,plant) .
is_a(dandelion,flower) is_parent true .
is_(flower,plant) is_parent is_a(flower,plant) .
is_a(flower,plant) is_parent true .
is_(bobby,herbivore) is_parent is_a(bobby,rabbit) .
is_(bobby,herbivore) is_parent is_(rabbit,herbivore) .
is_a(bobby,rabbit) is_parent true .
is_(rabbit,herbivore) is_parent is_a(rabbit,herbivore) .
is_a(rabbit,herbivore) is_parent true .
tracks_down(bobby,dandelion) is_parent is_(dandelion,plant) .
tracks_down(bobby,dandelion) is_parent is_(bobby,herbivore) .
is_(dandelion,plant) is_parent is_a(dandelion,flower) .
is_(dandelion,plant) is_parent is_(flower,plant) .
is_a(dandelion,flower) is_parent true .
is_(flower,plant) is_parent is_a(flower,plant) .
is_a(flower,plant) is_parent true .
is_(bobby,herbivore) is_parent is_a(bobby,rabbit) .
is_(bobby,herbivore) is_parent is_(rabbit,herbivore) .
is_a(bobby,rabbit) is_parent true .
is_(rabbit,herbivore) is_parent is_a(rabbit,herbivore) .
is_a(rabbit,herbivore) is_parent true .
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment