Ćwiczenie numer 1 Wprowadzenie do języka programistycznego JESS Kontrola przepływu wykonywania -pętle (foreach, if, while ). Tworzenie własnych funkcji przy pomocy polecenia deffunction( ). Fakty, reguły 1. Cel ćwiczenia 1) Celem ćwiczenia jest zapoznanie się z podstawami języka programistycznego JESS. 2) Celem ćwiczenia jest zapoznanie się z kontrolą przepływu wykonywanych operacji przy pomocy pętli. Tworzenie własnych funkcji. 3) Celem ćwiczenia jest zapoznanie się z formami reprezentacji wiedzy w JESS, czyli faktami i regułami. 2. Opis ćwiczenia Jess jest to system ekspertowy opracowany w języku Sun Java przez Ernest Friedman-Hillat Sandia National Laboratoriesas. Pierwsza wersja Jess została stworzona w 1995 roku i rozwija się nieustannie. Programowanie w tym języku również może być wsparte językiem Java. Używany może być zarówno pod Windowsem, a także pod UNIXem. Kompatybilny jest z wszystkimi wersjami Java. Jess można wykorzystać do programowania systemów ekspertowych, których zadaniem jest wierne naśladowanie ludzkiego eksperta. Jess może również spełnić role języka ogólnego programowania. Przy wykorzystaniu bibliotek Java może mieć bardzo szerokie zastosowanie. Na stronie internetowej: http://herzberg.ca.sandia.gov/jess po podaniu danych osobowych (imię, nazwisko i adres e-mail) otrzymać można od twórców JESS, na podany e-mail, login i hasło wymagane do ściągnięcia najnowszej wersji JESS (licencja akademicka, roczna, do korzystania w celach niekomercyjnych). Po ściągnięciu pliku rozpakowujemy go w dowolne miejsce na komputerze. Do poprawnego działania JESS wymaga zainstalowanego oprogramowania JAVA. Najnowsze oprogramowanie można ściągnąć ze strony internetowej: http://www.oracle.com/technetwork/java/javase/downloads/index.html. W folderze JESS wchodzimy do folderu./bin i uruchamiamy aplikację jess.bat, można już korzystać z konsoli JESS.
W folderze JESS./bin tworzymy nowy dokument tekstowy.txt, następnie zmieniamy jego rozszerzenie z.txt na.clp, oraz jego nazwę na własną np.: kod.clp. W pliku tym umieszczamy wszystkie polecenia JESS, zapisujemy plik i w konsoli JESS po wpisaniu komendy (batch kod.clp) uruchomi się program jeżeli nie ma błędów w składni języka! Przykładowe polecenia JESS (należy zwracać uwagę na nawiasy, należy także robić odstępy pomiędzy kolejnymi wyrażeniami w nawiasach jak to ma miejsce w przykładach poniżej!). a) (batch nazwa.clp) wczytanie do konsoli pliku z kodem źródłowym nazwa.clp b) (printout t "aaa" crlf) wypisanie dowolnego stringu pomiędzy " " c) (bind?a 3) dodanie nowej zmiennej lokalnej a = 3 d) (printout t "zmienna a ma wartosc"?a crlf) wypisanie wartości zmiennej a e)?a wypisanie wartości zmiennej a f) ( + 2 2) wykona się dodawanie 2 + 2 (można stosować +, -, *, /) g) ( * ( + 2 2) ( - 6 4)) jako wynik zwrócona zostanie wartość 8, najpierw wykonuje się dodawanie 2 + 2 i odejmowanie 6-4, później wykonuje się mnożenie h) (bind?b ( + 2 2)) utworzona zostanie zmienna b, o wartości = 2 + 2 (czyli 4) i) ( *?a?b) zwrócony zostanie wynik mnożenia wartości zmiennej a i b j) wyczyszczenie pamięci podręcznej JESS, najlepiej stosować zawsze na początku kodu źródłowego Pętle: W programowaniu pętla to jedna z trzech podstawowych konstrukcji programowania strukturalnego (obok instrukcji warunkowej i instrukcji wyboru). Umożliwia cykliczne wykonywanie ciągu instrukcji określoną liczbę razy, do momentu zajścia pewnych warunków, dla każdego elementu listy lub w nieskończoność. W JESS istnieje możliwość zastosowania następujących pętli: a) foreach b) while c) if / then/ else FOREACH: Pętla foreach, pętla "po kolekcji", to w programowaniu rodzaj pętli, której wykonanie polega na powtarzaniu kolejnych iteracji dla wszystkich elementów wybranych danych, takich jak, np. tablica, lista. Pętla ta jest przydatna w sytuacji kiedy mamy już listę zmiennych. Ma ona postać: (foreach <zmienna> <lista> <wyrażenie>+)
(bind?lista (create$ imie nazwisko adres)) (foreach?e?lista (printout t?e crlf)) Wypisane zostaną wszystkie elementy z listy. WHILE: Pętla while (ang. podczas gdy) wykonuje instrukcję tak długo, dopóki jej warunek jest spełniony (ma wartość prawdziwą). Instrukcja sprawdza warunek przed wykonaniem ciała pętli. Pętla while może wykonywać się nieskończoną ilość razy, gdy wyrażenie nigdy nie przyjmie wartości 0 (fałsz), może także nie wykonać się nigdy, gdy wartość przed pierwszym przebiegiem będzie FALSE. Ma ona postać: (while <wartość logiczna (true / false> do <wyrażenie>+) (bind?i 1) (bind?sum 0) (while (<=?i 10) do (bind?sum (+?sum?i)) (bind?i (+?i 1)))?sum Tworzymy dwie zmienne?i,?sum. W pętli while dopóki?i <= 10 zostanie wykonane polecenie do: do zmiennej?sum zostanie zapisana wartość?sum +?i, zmienna?i zostanie zwiększona o 1. Polecenie do będzie się wykonywało do momentu, gdy zmienna?i będzie równa 10. W 1 kroku mamy?sum = 0,?i = 1, pętla while wykona pierwsze iterację: zmienna?sum będzie wynosić 0 + 1 = 1, zmienna?i będzie wynosić 1 + 1 = 2. I tak dalej w kolejnych iteracjach (krokach). Jako wynik ostatecznie dostaniemy 55. Proszę sprawdzić na kalkulatorze czy się zgadza. IF/THEN/ELSE: Pętla ta pozwala na wybór, który fragment kodu ma się wykonać. Instrukcja if (ang. jeśli) to podstawowa instrukcja warunkowa. Gdy spełniony jest jej warunek - wykonany zostanie kod zawarty po słowie then. Fragment else jest opcjonalny. Ma ona postać: (if <wartość logiczna> then <wyrażenie>+ [else <wyrażenie>+]) (bind?lista (create$ imie nazwisko Krakow)) (if (member$ Krakow?lista) then (printout t "Ten ktoś mieszka w Krakowie" crlf)
else (printout t "Ten ktoś mieszka poza Krakowem" crlf)) Jeżeli w liście podamy Kraków to zostanie wykonane polecenie po słowie then, jeżeli podamy inne niż Kraków to wykonany zostanie kod po słowie else. Funkcje: Funkcja - procedura przekazująca wartość w miejsce swojego wywołania. Wywołanie funkcji ma w języku programowania na celu wykonanie odpowiedniego działania zawartego w ciele funkcji. W JESS oprócz funkcji wbudowanych poznane na poprzednich zajęciach (+, - * ), można definiować swoje własne funkcje, które wykonają zadanie napisane przez programistę. Funkcję tworzymy poleceniem (deffunction ()). Polecenie to ma postać: (deffunction <nazwa> (<parametry>*) <działanie (ciało)>*) (deffunction dodawanie (?x?y) (bind?suma (+?x?y)) (printout t "suma wynosi "?suma crlf)) (dodawanie 2 3) Zdefiniowana została funkcja dodająca dwie liczby do siebie. Wywołanie tej funkcji zostało poleceniem (dodawanie 2 3). Liczby 2 i 3 są to parametry przesłane do funkcji, która wykona na nich dodawanie. Jako parametry można przesłać zdefiniowane wcześniej zmienne lokalne np.:?a,?b, gdzie?a i?b zostały utworzone poleceniem (bind ) i przypisano im konkretne wartości. (deffunction pole_romb (?x?y) (bind?pole (/ (*?x?y) 2)) (printout t "Pole wynosi "?pole crlf)) (pole_rombu 2 3) Zdefiniowana funkcja odpowiada za obliczenie pola rombu. Do funkcji przesyłane są 2 parametry długości przekątnych. Funkcja w ciele ma napisaną formułę obliczającą pole i zwracającą wynik. Wywołujemy funkcję z parametrami i otrzymujemy odpowiedni wynik. Reprezentacja wiedzy: Reprezentacja wiedzy w dziedzinie sztucznej inteligencji jest jednym z głównych problemów, które jeszcze nie zostały rozwiązane. Wiedza oznacza zbiór wiadomości z określonej dziedziny, wszelkie zobiektywizowane i utrwalone formy kultury umysłowej i świadomości społecznej powstałe w wyniku kumulowania doświadczeń i uczenia się. Można powiedzieć, że wiedza jest symbolicznym opisem otaczającego nas świata rzeczywistego.
JESS pozwala na reprezentowanie wiedzy w postaci faktów. Zbiór takich faktów zwany jest w składni JESS jako pamięć robocza. Cała taka wiedza (zbiór informacji) jest reprezentowana w postaci faktów w pamięci roboczej. Dla przykładu jeżeli nasz program polega na kontroli klimatyzatora, to faktami będą temperatura, wilgotność, i inne. Fakty jak większość konstrukcji w JESS zapisane są w postaci listy. Fakt jest najmniejszą częścią informacji, która może być dodana lub usunięta z pamięci roboczej systemu ekspertowego. Fakty nie są pojedynczymi atomami, mają zazwyczaj określoną strukturę. Na elementach pamięci roboczej jakimi są w fakty można dokonywać różnych operacji. Do podstawowych operacji należą: reset inicjacja pamięci roboczej assert dodanie nowego faktu retract usunięcie faktu facts wyświetlenie zawartości pamięci roboczej (faktów) clear usunięcie wszystkiego z pamięci roboczej ASSERT: (assert (lista_faktow temperatura wilgotnoscć)) Jako wynik takiego działania zostanie dodany fakt do pamięci roboczej. Aby go wyświetlić posługujemy się poleceniem. RETRACT: (assert (lista_faktow temperatura wilgotnosc)) (assert (lista_faktow2 temperatur2 wilgotnosc2)) (retract 1) Jako wynik takiego działania zostaną utworzone 2 fakty, następnie poleceniem (retract numer_faktu) zostanie usunięty wybrany fakt. Czasami zamiast dodawać fakty poleceniem assert możemy posłużyć się poleceniem deffacts, które pozwala na hurtowe dodawanie faktów. Jest to przydatne kiedy chcemy wprowadzić do pamięci roboczej fakty początkowe, na podstawie których dokona się proces wnioskowania. Konstrukcja taka ma postać:
(deffacts catalog "Product catalog" (product 1 sticky-notes "$1.99") (product 2 paper-clips "$0.99") (product 3 blue-pens "$2.99") (product 4 index-cards "$0.99") (product 5 stapler "$5.99")) Dodane zostanie 5 faktów, które poleceniem załadujemy do pamięci roboczej. Fakty takie można później usuwać podobnie jak we wcześniejszym przykładzie, należy podać tylko odpowiedni numer faktu. W JESS można wyróżnić następujące rodzaje faktów, które różnią się między sobą sposobem ich implementacji: unordered facts (fakty nieuporządkowane) ordered facts (fakty uporządkowane) Fakty nieuporządkowane Ich podstawową charakterystyką jest to, że posiadają określoną strukturę: (deftemplate person (name Jan Kowalski) (age 23 ) (eye-color blue) (hair-color brown)) W JESS można definiować (tworzyć) różne rodzaje faktów nieuporządkowanych. Należy jednak pamiętać, że aby dodać jakiś fakt nieuporządkowany musimy wcześniej zdefiniować jego szablon, co pokazano poniżej: (deftemplate person "People" (slot name) (slot age) (slot gender)) (assert (person (age 34) (name "Bob Smith") (gender Male))) W przypadku, w którym dodając nowy fakt nie podamy wartości slotu, przyjmie on wartość nil (pustą). Np.: nie podajemy slotu name:
(assert (person (age 34) (gender Male))) Taki fakt zostanie dodany, a pod wartość name zostanie przypisana wartość pusta. Można także domyślnie każdemu slotowi przypisać konkretną wartość, jeżeli nie podamy wartości slotu podczas dodawania faktu to zostanie pod niego automatycznie przypisana wartość domyślna. Wartość domyślną dodajemy przy definiowaniu szablonu faktu: (deftemplate person "People" (slot name (default "Jan Kowalski")) (slot age) (slot gender)) Można także tworzyć tzw. multisloty. W sytuacji kiedy chcemy lub wiemy, że dana wartość slotu składa się więcej niż z jednego symbolu, np.: wartość slotu to opis (składający się z kilku symboli), należy posłużyć się trochę inna konstrukcją szablonu faktu: (deftemplate person "People" (slot name) (slot age) (multislot hobbies)) Tworząc fakt jako wartość hobbies możemy użyć ciągu symboli, np.: sport, tv, gry etc.. Osoba taka ma kilka wartości slotu hobbies. (assert (person (age 34) (name "Bob Smith") (gender Male) (hobbies sport, tv, gry etc. ) )) Wartości poszczególnych faktów mogą zostać modyfikowane przy pomocy polecenia modify. (deftemplate person "People" (slot name) (slot age) (slot gender)) (assert (person (age 34) (name "Bob Smith") (gender Male))) (modify 1 (age 23)) Zmieniona zostanie wartość age na nową = 23.
Fakty uporządkowane Są one prostymi listami, gdzie pierwszym polem jest rodzaj kategorii faktu. Np.: (stal niklowana) (data-godzina 12.12.2011 19:30) (lista-numerow 2 32 3 5 6 7 34 32 4) W sytuacji, w której dany fakt nie musi mieć wielu slotów lepiej posłużyć się faktami uporządkowanymi. Fakty takie definiujemy w postaci szablonu przed dodaniem faktu z konkretnymi wartościami. (deftemplate number (slot value)) (assert (number (value 123))) Podobnie jak w przypadku z faktami nieuporządkowanymi możemy posługiwać się poleceniami usuwania I modyfikowania takich faktów. Modyfikowanie: (deftemplate number (slot value)) (assert (number (value 123))) (modify 1 (value 1)) Usuwanie: (deftemplate number (slot value)) (assert (number (value 123))) (retract 1) JESS pozwala na reprezentowanie wiedzy w postaci reguł. Zbiór reguł w JESS to typowa baza wiedzy. Podczas tworzenia systemów ekspertowych najważniejsze i najtrudniejsze jest pozyskanie wiedzy od ekspertów z dziedziny tworzonego systemu. Zdobytą wiedzę należy odpowiednio zapisać w naszym systemie w postaci zbioru reguł. Program posiadający zestaw
faktów i reguł, oraz interfejs współpracy z użytkownikiem, który dokonuje procesu wnioskowania na zestawach tych danych można nazwać systemem ekspertowym. JESS posiada dwa mechanizmy wnioskowania: Wnioskowanie wprzód Wnioskowanie wstecz Na regułach podobnie jak na faktach można dokonywać różnych operacji. Do podstawowych operacji należą: defrule definiowanie nowej reguły undefrule usunięcie reguły run uruchomienie odpalania reguł, czyli proces wnioskowania DEFRULE: (defrule nazwa_reguly "regula nic nie robi" => ) Jako wynik takiego działania zostanie wyświetlone tylko TRUE. Jest to tylko przykład reguły, która nie robi nic. Postać każdej reguły jest następujący: IF THEN, czyli jeżeli coś jest prawdziwe to wykonaj działanie. W JESS słówko THEN to znak =>, aby działanie po prawej stronie znaku wykonało się wszystko co znajduje się po jego lewej stronie musi być prawdziwe. Gdy mamy już tak utworzoną regułę możemy wpisać do konsoli poniższe komendy: (watch facts) (watch activations) (watch rules) (run) Watch facts, activations i rules to funkcje, które w konsoli wyświetlą nam odpowiednie informacje. Reset ładuje fakty do pamięci roboczej (kiedy użytkownik sam nie wprowadzi faktu, w JESS zawsze istnieje fakt początkowy initial, który jest przez niego używany). Run odpowiada za odpalenie reguł. Polega to na tym, że przegląda on bazę reguł i fakty, jeżeli reguła może zostać uruchomiona przez fakt to jest ona uruchamiana. Jest to typowy proces wnioskowania, który JESS wykonuje samodzielnie.
UNDEFRULE: (undefrule nazwa_reguly) Polecenie to usunie nam daną regułę. Poniżej przedstawiam kompletny fragment kodu JESS, w którym mamy funkcję, regułę i fakt: (watch all) (deffunction change-baby () (printout t "Baby is now dry" crlf)) (defrule change-baby-if-wet "If baby is wet, change its diaper."?wet <- (baby-is-wet) => (change-baby) (retract?wet)) (assert (baby-is-wet)) (run) Aby reguła została odpalona musi być fakt uruchamiający tą regułę. Dodaliśmy fakt poleceniem assert. Run uruchomi proces wnioskowania, sprawdzi, że jest jedna reguła, którą można aktywować i to robi. Reguła jako efekt działania uruchamia naszą funkcję, która wyświetla nam informację. (defrule literal-values (letters b c) => ) (watch activations) (assert (letters b d)) (assert (letters b c)) W tym przypadku dodajemy dwa fakty, pierwszy z nich nie uruchomi naszej reguły. Dodaje on litery b i d, aby uruchomić regułę potrzeba liter b i c, litery te dodaje fakt drugi, który uruchomi naszą regułę. (defrule repeated-variables (a?x) (b?x) => (printout t "?x is "?x crlf)) (watch activations) (deffacts repeated-variable-facts (a 1)
(a 2) (b 2) (b 3)) (run) Mamy tutaj regułę, która zostanie aktywowana tylko wtedy, gdy fakty będą się powtarzać. Dodajemy jako fakty 4 liczby, powtarza się liczba z faktu 2 i 3, dlatego to te fakty uruchomią naszą regułę. Przedstawione teraz będą reguły, których przesłanki są bardziej rozbudowane. Przesłanki muszą być prawdziwe, aby reguła została uruchomiona. Jako efekt pracy reguły może zostać wyświetlona informacja, dodany/usunięty/zmodyfikowany fakt, lub nawet wywołanie funkcji. (deftemplate person (slot age)) (defrule find-trustworthy-people-1 (person (age?x)) (test (<?x 30)) => (printout t?x " is under 30!" crlf)) W tym przypadku reguła wymaga aby osoba miała wiek poniżej 30 lat. Tylko dla takiej osoby reguła zadziała. (deftemplate used-car (slot price) (slot mileage)) (deftemplate new-car (slot price) (slot warrantyperiod)) (defrule might-buy-car?candidate <- (or (used-car (mileage?m&:(<?m 50000))) (new-car (price?p&:(<?p 20000)))) => (assert (candidate?candidate))) (assert (new-car (price 18000))) (assert (used-car (mileage 30000))) (run) Tutaj reguł sprawdza nam informacje o samochodach, jeżeli używany samochód ma mniej niż 50k km na liczniku lub nowy ma cenę poniżej 20k zł to samochody takie są polecane użytkownikowi. Aby taka reguła została uruchomiona wystarczy jeden fakt, który ją spełnia (mamy słówko or, and powoduje, że obydwa fakty musiałby być spełnione).
3. Zadania Stworzyć system ekspertowy w oparciu o poznane na zajęciach metody (funkcje, fakty, reguły). 4. Literatura a) http://herzberg.ca.sandia.gov/jess b) Jess in Action Ernest Friedman-Hill c) Tutoriale internetowe Mgr inż. Marynowski Przemysław pmarynow@agh.edu.pl Budynek B5, pokój 202