День 5 Летней школы Julia
Работа с FuzzyLogic.jl
Подключим библиотеку
В Julia работать с нечеткой логикой можно при помощи библиотеки FuzzyLogic.jl. Для подключения выполните следующую ячейку кода.
Pkg.add("FuzzyLogic")
using FuzzyLogic
Будем рассматривать систему нечеткого вывода Мамдани. Она включает следующие этапы работы.
- Преобразование входных значений в нечеткие функции принадлежности.
- Выполнение правил для вычисления нечетких выходных функций.
- Дефаззифиция нечётких выходных функций, чтобы получить «чёткие» выходные значения.
Пример "Чаевые в ресторане"
Рассмотрим ситуацию в ресторане: после окончания обслуживания принято оставлять официанту чаевые.
Задача состоит в том, чтобы разработать некоторую экспертную систему, которая была бы реализована в виде системы нечеткого вывода и позволяла бы определять величину чаевых на основе субъективных оценок посетителей качества обслуживания и качества приготовления заказанных блюд.
Реализация этой задачи с помощью системы нечеткого вывода Мамдани представлена ниже. Далее разберем каждую строку кода.
fis = @mamfis function tipper(service, food)::tip
service := begin
domain = 0:10
poor = GaussianMF(0.0, 1.5)
good = GaussianMF(5.0, 1.5)
excellent = GaussianMF(10.0, 1.5)
end
food := begin
domain = 0:10
rancid = TrapezoidalMF(-2, 0, 1, 3)
delicious = TrapezoidalMF(7, 9, 10, 12)
end
tip := begin
domain = 0:30
cheap = TriangularMF(0, 5, 10)
average = TriangularMF(10, 15, 20)
generous = TriangularMF(20, 25, 30)
end
service == poor || food == rancid--> tip == cheap
service == good --> tip == average
service == excellent || food == delicious --> tip == generous
end
Построение системы логического вывода
Систему логического вывода Мамдани можно создать с помощью макроса @mamfis. Теперь подробнее рассмотрим компоненты. Первая строка:
function tipper(service, food)::tip
определяет основные свойства системы, в частности:
- имя функции
tipperбудет именем системы - входные аргументы
service,foodпредставляют входные переменные системы - аннотация типа вывода
::tipпредставляет собой выходную переменную системы. Если у системы несколько выходов, они должны быть заключены в фигурные скобки, например::{tip1, tip2}
Следующий блок — это блок определения переменных, обозначаемый оператором :=. Этот блок используется для определения области значений и функций принадлежности переменной, например
service := begin
domain = 0:10
poor = GaussianMF(0.0, 1.5)
good = GaussianMF(5.0, 1.5)
excellent = GaussianMF(10.0, 1.5)
end
Порядок инструкций внутри begin ... end блока не имеет значения.
-
Строка
domain = 0:10устанавливает область определения переменной в интервале [0,10]. Обратите внимание, что установка области определения обязательна -
Другие строки определяют функции принадлежности переменной.
Например, poor = GaussianMF(0.0, 1.5) означает, что переменная имеет функцию принадлежности Гаусса с именем poor со средним значением 0.0 и стандартным отклонением 1.5. Полный список поддерживаемых функций принадлежности и их параметров можно найти в разделе Функции принадлежности документации.
Далее мы описываем блоки правил.
Нечёткое отношение, такое как service is poor, описывается с помощью оператора ==, например service == poor.
Допущение, то есть левая часть правила, может быть любым логическим высказыванием, соединяющим нечёткие отношения с помощью операторов && (И) и || (ИЛИ).
Следствие, то есть правая часть правила, представляет собой нечёткое отношение для выходной переменной.
Допущение и следствие соединяются с помощью оператора -->.
Например, правило
service == poor || food == rancid --> tip == cheap
Если обслуживание плохое или еда несвежая, то чаевые будут маленькими.
Обратите внимание, что в условии может быть любое логическое высказывание, вы можете использовать как && и || связки, а также вложенные высказывания.
Например, следующее является верным правилом
service == poor || food = rancid && service == good
Соединительные элементы подчиняются правилам приоритета Джулии, поэтому && имеет более высокий приоритет, чем ||.
Если у вас несколько выходов, то результатом должен быть, например, кортеж
service == poor || food == rancid --> (tip1 == cheap, tip2 == cheap)
Визуализация данных
Библиотека предлагает инструменты для визуализации вашей системы нечёткого вывода. Для этого необходимо установить и импортировать библиотеку Plots.jl.
using Plots
Функции принадлежности заданной переменной можно построить, вызвав plot(fis, varname), где fis — созданная вами система логического вывода, а varname — имя переменной, которую вы хотите визуализировать, в виде символа. Например,
plot(fis, :service)
Если в plot указать только объект системы логического вывода, будут выведены правила логического вывода, по одному в строке.
plot(fis)
Если FIS имеет не более 2 входов, мы можем построить генерирующую поверхность системы, используя функцию gensurf. Это поверхность, визуализирующая, как выходные данные изменяются в зависимости от входных данных.
gensurf(fis)
Чтобы выполнить вывод, вы можете вызвать описанную выше систему вывода в качестве функции, передав входные значения в качестве параметров. Обратите внимание, что система не принимает позиционные аргументы, но входные данные следует передавать в виде пар «имя-значение». Например,
res = fis(service=1, food=2)
В результате получается словарь, содержащий выходные значения, соответствующие каждой выходной переменной. Значение конкретной выходной переменной можно извлечь, используя имя переменной в качестве ключа.
res[:tip]