99
Гусев Александр Валерьевич

экспертные системы

Embed Size (px)

Citation preview

Гусев Александр Валерьевич

Системы, способные упростить или заменить работу специалиста-эксперта в какой-то предметной области.

Примеры:G2MYCINPROSPECTORЯндекс-Гуру

В классической продукционной экспертной системе:• База знаний представляет собой множество правил ЕСЛИ-ТО (продукций);• Рабочая память представляет собой множество пар атрибут-значение (троек объект-атрибут-значение), описывающих состояние решаемой задачи;• Механизм логического вывода – это поиск в пространстве состояний множества комбинаций рабочей памяти, переходы в котором задаются правилами.

Эксперт

Инженер по знаниям

База знаний(множество

правил)

Рабочая памятьМеханизм

логического вывода

Пользователь

• Дерево (связный ациклический граф) И-ИЛИ• Каждая вершина дерева транслируется в

продукционное правило (пары <антецендент, консеквент>)

• ЕСЛИ цвет - рыжевато-коричневый И узор –тёмные пятна И класс – млекопитающее И отряд – хищник ТО животное - обезьяна

• Листья дерева соответствуют элементарным фактам, которые являются исходными для решения задачи

Конфликтное множество

Разрешение конфликта

Применяемое правило

Применение правила

Отбор применимых

правил

Цель получена?

База знаний Рабочая область

первое по тексту правило; система приоритетов; правило с максимальным (минимальным)

числом посылок; правило с максимальным числом заключений; …

Прямой вывод (управляемый фактами)

• Движение по дереву снизу вверх (от фактов)• Необходимо знание начальных фактов

Обратный вывод (управляемый целями)

• Движение по дереву сверху вниз• Выдвигаем гипотезу, пытаемся ее доказать• Пользователю задаются вопросы для уточнения фактов

Комбинированный вывод

Естественное представление правил – правила могут формулироваться даже экспертами самостоятельно.

Возможность контроля непротиворечивости базы знаний на этапе добавления правил.

Прозрачный и простой механизм логического вывода.

Очевидный механизм объяснений.

2 варианта реализации: напрямую кодирование правил на Прологе; создание «оболочки» экспертной системы со

специализированным языком представления знаний.

speciality(X,tech_translator) :-studied_languages(X),studied_technical(X).

speciality(X,programmer) :-studied(X,mathematics),studied(X, compscience).

speciality(X,lit_translator) :-studied_languages(X),studied(X,literature).

studied_technical(X) :- studied(X,mathematics).studied_technical(X) :- studied(X,compscience).studied_languages(X) :- studied(X,english).studied_languages(X) :- studied(X,german).

studied(petya,mathematics). studied(vasya,german).studied(petya,compscience). studied(vasya,literature).studied(petya,english).

правил аф

акты

?- specialty(petya,X).

запрос

door(a,b).door(b,c).door(b,d).door(c,d).door(d,e).door(d,f).door(f,g).door(h,i).

move(X,Y) :-door(X,Y);door(Y,X).

path(X,X).path(X,Y) :- move(X,Z), path(Z,Y).

path(X,Y) :- path(X,Y,[X]).path(X,X,_).path(X,Y,L) :-

move(X,Z),not(member(Z,L)),path(Z,Y,[Z|L]).

Идея борьбы с зацикливанием –использовать список уже посещённыхвершин и не посещать их повторно

Графом (точнее, направленным графом) называется пара , где U— множество вершин, — множество

дуг.В отличие от конечных графов, которые могут быть заданы

матрицей смежности, мы будем рассматривать потенциальнобесконечные графы, задаваемые генератором вершинmove/2(in,out) Путем в графе из вершины в вершину называется последовательность , гдеи

Задача поиска в графе ставится следующим образом: Пустьимеется граф G, заданный предикатом move/2. Необходимо подвум заданным вершинам a и b найти все пути (один путь,кратчайший путь и т.д.) из a в b в графе G.

UUV ×⊆>< VU ,

>=< VUG , Ua ∈ Ub∈{ } Uxx inii ⊂= ,

0bxax n == ,0

Vxxi ii >∈<∀ − ,1

Функциональные языки: Компактный синтаксис для списков, n-ок (tuples),

вариантных типов.Логические языки: Компактный синтаксис для списков, n-ок (tuples),

вариантных типов. Возможность перебора и поиска различных

решений, заложенная в язык.

Запрос (целевое утверждение) сопоставляется (унифицируется) с головами имеющихся в программе правил и фактов.

Начиная с первого найденного правила, целевое утверждение подменяется правой частью правила (с учетом замены переменных).

Если встречается неуспех (правило не находится), то происходит откат (backtracking).

[a] — очередь из одной исходной вершины [a,b] [a,b,c], [a,b,d] [a,b,d], [a,b,c,d] [a,b,c,d], [a,b,d,e], [a,b,d,f] [a,b,d,e], [a,b,d,f], [a,b,c,d,e], [a,b,c,d,f] [a,b,d,f], [a,b,c,d,e], [a,b,c,d,f] — тупиковый

путь [a,b,d,e] убирается из очереди [a,b,c,d,e], [a,b,c,d,f], [a,b,d,f,g] [a,b,c,d,f], [a,b,d,f,g] — тупиковый путь [a,b,c,d,e] убирается из очереди [a,b,d,f,g], [a,b,c,d,f,g] [a,b,c,d,f,g] — получено первое решение

[a,b,d,f,g], убирается из очереди [] — получено второе решение [a,b,c,d,f,g],

убирается из очереди

?- findall(X, prolong([a,b],X), L).

Если prolong([a,b],X) завершается неуспехом (тупик), то findall также завершается неуспехом

L = [a,b,c],[a,b,d]

path(X,Y,P):- bdth([[X]],Y,P).

bdth([[X|T]|_],X,[X|T]).bdth([P|QI],X,R) :-

findall(Z,prolong(P,Z),T),append(QI,T,QO),!,bdth(QO,X,R).

bdth([_|T],Y,L) :- bdth(T,Y,L).

Первым находит кратчайший путь Алгоритм выглядит менее естественно, требует явногохранения путей в очереди и рассмотрения множественных вариантов Низкая степень декларативности Позволяет искать в путях с циклами

В глубину В ширинуПамять O(lmax) O(bl)

Время O(blmax) = O(1+b+b2+…+bl)

Только контуры безпетель

Может использоватьсядля нахождения путей сциклами

Порядок нахожденияпутей определён move/2

Первым находитсякратчайший путь

Даже если есть короткийпуть – поиск может уйтиот него далеко

Если известенкратчайший путь – оннаходится быстро

path(X,Y,P):- bdth([[X]],Y,P).

bdth([[X|T]|_],X,[X|T]).bdth([P|QI],X,R) :-

findall(Z,prolong(P,Z),T),append(QI,T,QO),!,bdth(QO,X,R).

bdth([_|T],Y,L) :- bdth(T,Y,L). path(X,Y,P):- bdth([[X]],Y,P).

bdth([[X|T]|_],X,[X|T]).bdth([P|QI],X,R) :-

findall(Z,prolong(P,Z),T),append(T, QI,QO),!,bdth(QO,X,R).

bdth([_|T],Y,L) :- bdth(T,Y,L).

Чтобы избежать зацикливания поиска в ширину,ограничим глубину погружения параметромDepthLimit Если постепенно увеличивать этот параметр на 1,то получим поиск, в котором находится первымкратчайший путь

search_id(Start,Finish,Path,DepthLimit) :-depth_id([Start],Finish,Path,DepthLimit).

depth_id([Finish|T],Finish,[Finish|T],_).depth_id(Path,Finish,R,N) :- N>0,

prolong(Path,NewPath), N1 is N-1,depth_id(NewPath,Finish,R,N1).

search_id(Start,Finish,Path):-integer(Level),

search_id(Start,Finish,Path,Level).integer(1).integer(M) :- integer(N), M is N+1.

Сложность:Для пути длины l поиск происходит l+1 разПервая вершина обходится (l+1) раз, второй уровень – l раз и т.д.(l+1)*1 + l*b + (l-1)*b2 + … + 1*bl = O(bl)

Поиск в глубину по сложности не сильно превосходит поиск в ширину,сохраняя все его положительные стороны и исключая требования к памяти.

move(s(A,B,down,YN),s(C,B,down,YN)) :- vertex(C).move(s(A,A,down,YN),s(B,B,down,YN)) :- vertex(B).move(s(A,A,UD,YN),s(A,A,DU,YN)) :-

permute([DU,UD],[down,up]).move(s(e,e,up,no),s(e,e,up,yes)).

Проследите за перемещением обезьяны! Реальные перемещение не будут совпадать споследовательностью вызовов move, поскольку будут откаты В ширину vs. в глубину vs. погружение – ответ сразу не очевиден!

Алгоритмы поиска (в особенности поиск в глубину) эффективно реализуются на языках логического программирования

Базовые алгоритмы поиска обладают взаимоисключающими достоинствами и недостатками

Алгоритм поиска с заглублением сочетает в себе достоинства обоих методов, хотя и производит исключительно много ненужных вычислений

Чтобы решать реальные задачи, нужно иметь методы поиска, использующие знания о направлении поиска.

Assembler (x86, …) C, C++, C#, Java Pascal …

Brainfuck? FORTH? LISP, FP, ML, Haskell, OCaml, F#, …

Парадигма программирования, которая рассматривает выполнение программы как вычисление математических функций (выражений) Неизменяемые данные, нет состояния среды

Стиль программирования, позволяющий писать программы, свободные от ошибок

Язык программирования F# (и целое семейство «странных» языков вместе с ним: ML, Haskell, …)

Императивное – мы говорим компьютеру, как решать задачу (что делать)

Основной акцент – манипулирование ячейками памяти◦ Оператор присваивания

Функции как способ декомпозиции задачи на более простые

Первый язык программирования высокого уровня – ФОРТРАН – был создан Дж.Бэкусом, чтобы математики могли программировать на уровне формул.

1950

• программирование переключателей

• машинные коды

• язык ассемблера

• FORTRAN

1960 1970 1980 1990 2000 2010

1954-57 г., Дж.Бэкус

0000 0A 12 1F 4B C3 E0 EE F10008 C3 1D 23 17 F2 00 0C 0D0010 …

MOV AX, [ARG1]ADD AX, [ARG2]MOV [RES], AXJMP NEXT

ARG1: DB 10ARG2: DB 20RES: DB 0NEXT: …

S = 0DO 10 I=1,10S = S + I*I

10 CONTINUE

Вычисление факториала:

function fact(x:integer):integer;var i, r : integer;begin r:=1; for i:=1 to x do r:=r*i; fact:=rend;

let rec fact x = if x=1 then 1 else x*fact(x-1);;

let rec fact = function 1 -> 1 | x -> x*fact(x-1);;

Pascal F#

Определение функции похоже на математическое определение факториала◦ Функциональное программирование имеет очень четкую

математическую основу◦ Рассуждение о программах: доказательство корректности, …

Определение последовательности действий – рекурсивно◦ При умелом программировании не ведет к падению

эффективности (компилятор сводит к итерации) Отсутствует оператор присваивания◦ let имеет другую семантику – связывание имен◦ Будучи один раз связанным, имя не может менять свое

значение (в рамках области видимости)◦ А это значит – нет побочных эффектов!◦ Раз в императивной программе 90% - это операторы

присваивания, то функциональные программы на 90% короче!

Это не «чистая» императивная программа. В «чистых» императивных языках (ФОРТРАН) нет

рекурсии Нет операторов присваивания

«:= » -это возврат результата из функции, а не присваивание

function fact(x:integer):integer;begin if x=1 then fact:=1 else fact:=x*fact(x-1)end;

Описание решения сложной задачи с помощью множества простых шагов

Основная проблема – борьба со сложностью Способы борьбы со сложностью:◦ Декомпозиция («разделяй и властвуй»)◦ Абстракция («не обращай внимание на мелочи»)

Как это воплощено в современных языках:◦ Структурное программирование,

процедуры/функции◦ Объектная ориентированность (наследование,

полиморфизм)

Какие способы комбинирования функций доступны в традиционном программировании?function myexp(x:real):real;var s : real; i : integer;begin s:=0; for i:=0 to 10 do s:=s+taylor(x,i);end;

function taylor(x : real, i:integer):real;begin taylor:=power(x,i)/fact(i);end;

Вызов

Композиция

Более богатые возможности композиции за счет рассмотрения «функций-как-данных»

iter – функциональная абстракция, лежащая в основе вычисления myexp, pow, fact и др.

iter – может быть получено как частный случай абстракцииlet iter f a b i = fold_left f i [a..b];;

let rec iter f a b i = if a>b then i else f (iter f (a+1) b i) a;; let pow x n = iter (fun y i -> y*x) 1 n 1.0;;let fact n = iter (fun y i -> y*(float i)) 1 n 1.0;;

let taylor x n = pow x n / fact n;;

let myexp x = iter (fun y n -> y+taylor x n) 0 15 0.0;;

f(f(f(…f(i,b),b-1)),…,a+1),a)

При этом:◦ Технология мемоизации и ленивых вычислений могут увеличить

эффективность вычисления факториала и степени до линейной, за счет запоминания предыдущих результатов вычислений◦ Функционально-декомпозированный (более простой для человека)

алгоритм будет обладать такой же эффективностью, что и более сложный алгоритм вычисления суммы в одном цикле (домножение предыдущего слагаемого на фактор)

iter

fold_left

MyExp

Taylor

Pow Fact

Сравните:

function sum_even(L:List):integer;var s : integer;begin s:=0; foreach (var x in L) do if x mod 2 = 0 then s:=s+x; sum_even:=s;end;

let sum_even L = sum(filter (fun x->x%2==0) L);;

Императивный подходНа первый взгляд – большая эффективность по памяти (не создается копия списка), по времени (один проход по списку)Для определения суммы нечетных элементов придется переписывать функцию

Функциональный подходВысокий уровень абстракции -> суммирование нечетных элементов получается автоматическиПроще для программистаПусть компилятор заботится об эффективности! Большая эффективность при параллельных вычислениях

(возможность распараллеливания, поскольку sum и filter не имеют зависимостей по данным)

При использовании ленивых вычислений – получаем однопроходный алгоритм, эквивалентный итерационному!

Мы описываем функции, работающие над данными – система программирования решает,

как их вычислять!

let rec fact = function 1 -> 1 | x -> x*fact(x-1);;

var res = (from x in L where (x%2==0) select x^2).sum();

let sum_even L = sum(filter (fun x->x%2==0) L);;

void quickSort (int a[], int l, int r){ int i = l; int j = r; int x = a[(l + r) / 2]; do { while (a[i] < x) i++; while (x < a[j]) j--; if (i <= j) { int temp = a[i]; a[i++] = a[j]; a[j--] = temp; } } while (i <= j); if (l < j) quickSort (a, l, j); if (i < r) quickSort (a, i, r); }

let rec quicksort = function [] -> []| h::t -> quicksort ([ for x in t when x<=h -> x]) @ [h] @ quicksort ([ for x in t when x>h -> x]);;

flipH

flipV

mirror

invert

stack

glue

,

,

Какие функции можно получить композицией?Какие свойства можно увидеть?

right

mirror = flipH o flipV = flipV o flipH flipH o flipH = flipV o flipV = Id flipH = right o right

flipH

flipV

mirrorfli

pV

flipH

right right

правилаф

актызапрос