Прикладные программные средства в задачах профессиональной деятельности – 3

Рассмотрим кратко построение моделей на основе нечеткой логики в GNU R на примере пакета sets. Это достаточно простой пакет, в нём есть определенные ограничения, но у него есть немаловажное достоинство — он работает. Тест по материалу для самопроверки доступен вот здесь.

Если данный пакет не установлен, его необходимо предварительно установить.

Подключаем пакет.

library(sets)

Для примера рассмотрим простую систему терморегулятора на основе нечеткой логики. Возьмем в качестве комфортной температуру 18-24 градуса. Температуру ниже 20 градусов будем считать холодной (COLD), а выше 22 градусов жаркой. Температуру от 18 до 24 будем считать комфортной.

Рассмотрим также состояние клапана регулятора батареи отопления, который может быть закрыт, частично закрыт, частично открыт и открыт. Опишем систему с помощью команд пакета sets.

Для начала задам “вселенную”, т.е. то пространство, на котором будет проводиться вычисление.

sets_options("universe", seq(from = 0, to = 100, by = 0.1))

Теперь опишем обе переменные, используя в первом случае трапецоиды, во втором треугольники.

Обратите внимание, что сделано небольшое смещение в температуре и положении клапана. Если этого не сделать, то результаты иногда могут оказать не такие, ккакие вы ожидаете увидеть именно в этих узловых точках, там где одно значение равно 0, а второе одновременно 1, в частности, потому что результат операций зависит от выбранной t-нормы и t-конормы. А вы вроде как ничего не выбирали, хотя и выбрали. Что? Подробнее смотреть ?fuzzy_logic
variables <-set(
    TEMP=fuzzy_variable(COLD=fuzzy_trapezoid(corners = c(-1,0,17,20)),
                    NORMAL=fuzzy_trapezoid(corners = c(18,19,21,24)),
                    HOT=fuzzy_trapezoid(corners = c(22,25,100,101))),
    VALVE = fuzzy_variable(CLOSED=fuzzy_triangular(corners = c(-1,0,25.1)),
                    PART.CLOSED=fuzzy_triangular(corners = c(0,25,75.1)),
                    PART.OPENED=fuzzy_triangular(corners = c(26,75.2,100)),
                    OPENED=fuzzy_triangular(corners = c(75.1,100,101)))
 )

Опишем простые правила регулирования клапана. Если холодно, то открыть, если жарко, то закрыть, если комфортно то не менять. Обратите внимание, что надо предусмотреть все возможные состояния системы. Если мы что-то пропустим, то результат может нас неприятно удивить.

rules <-set(
    fuzzy_rule(TEMP %is% COLD, VALVE %is% OPENED),
    fuzzy_rule(TEMP %is% HOT, VALVE %is% CLOSED),
    fuzzy_rule(TEMP %is% NORMAL &&  VALVE %is% CLOSED ,VALVE %is% CLOSED),
    fuzzy_rule(TEMP %is% NORMAL &&  VALVE %is% PART.CLOSED, VALVE %is% PART.CLOSED),
    fuzzy_rule(TEMP %is% NORMAL &&  VALVE %is% PART.OPENED, VALVE %is% PART.OPENED),
    fuzzy_rule(TEMP %is% NORMAL &&  VALVE %is% OPENED, VALVE %is% OPENED)
    )

Сформируем систему и выведем о ней данные.

system <- fuzzy_system(variables, rules)
print(system)
## A fuzzy system consisting of 2 variables and 6 rules.
## 
## Variables:
## 
## TEMP(COLD, NORMAL, HOT)
## VALVE(CLOSED, PART.CLOSED, PART.OPENED, OPENED)
## 
## Rules:
## 
## TEMP %is% NORMAL && VALVE %is% CLOSED => VALVE %is% CLOSED
## TEMP %is% NORMAL && VALVE %is% OPENED => VALVE %is% OPENED
## TEMP %is% NORMAL && VALVE %is% PART.CLOSED => VALVE %is% PART.CLOSED
## TEMP %is% NORMAL && VALVE %is% PART.OPENED => VALVE %is% PART.OPENED
## TEMP %is% HOT => VALVE %is% CLOSED
## TEMP %is% COLD => VALVE %is% OPENED
plot(system)

Получилось, мягко говоря, не ахти. Выведем отдельный графики с русскими подписями осей.

vars<-as.list(system$variables)
nams <- names(vars)
plot(vars[[2]], main = nams[2],col=rainbow(4),xlab="Температура, град. C",ylab="Степень принадлежности")

Теперь проверим как работает система. Зададим начальные условия.

data<-list(TEMP=5,VALVE=0)
fc1<-fuzzy_inference(system,data)
plot(fc1)

Проведем дефаззификацию, под которой в системах нечеткого вывода понимают процесс перехода от функции принадлежности выходной лингвистической переменной к её четкому (числовому) значению. Обращаю ваше внимание, что это  всего лишь один из возможных методов.

gset_defuzzify(fc1, "centroid")
## [1] 91.73333

Получили результат, что клапан открыт на 91.7%. ПУсть прошло какое-то время и при таком положении клапана воздух в помещении нагрелся до 17 градусов.

data<-list(TEMP=17,VALVE=91.7)
fc1<-fuzzy_inference(system,data)
plot(fc1)

gset_defuzzify(fc1, "centroid")
## [1] 91.73333

Ничего не поменялось. Нагрелось до 18 градусов:

data<-list(TEMP=18,VALVE=91.7)
fc1<-fuzzy_inference(system,data)
plot(fc1)

gset_defuzzify(fc1, "centroid")
## [1] 91.03544

Мы прикрыли клапан до 91%. Температура выросла еще на 1 градус.

data<-list(TEMP=19,VALVE=91)
fc1<-fuzzy_inference(system,data)
plot(fc1)

gset_defuzzify(fc1, "centroid")
## [1] 70.63561

Температура стабилизировалась, клапан ещё чуть прикрыт.

data<-list(TEMP=19,VALVE=70.6)
fc1<-fuzzy_inference(system,data)
plot(fc1)

gset_defuzzify(fc1, "centroid")
## [1] 64.90177

Температура падает.

data<-list(TEMP=18.1,VALVE=64.9)
fc1<-fuzzy_inference(system,data)
plot(fc1)

gset_defuzzify(fc1, "centroid")
## [1] 69.54573

Тогда немного приоткроем клапан…

Имитационная модель

Рассмотрим некое абстракное помещение, которое мы обогреваем. Пусть теплопотери будут прямо пропорциональны разнице температур между наружним и внутренним помещением с учетом площади поверхности и коэффициента теплопотерь.

Пусть наружняя температура равна 0, а теплопотери в минуту равны T/30, где Т — температура в помещении. Пусть нагреватель при ста процентах мощности нагревает воздух на 1 градус за 1 минуту.

Промоделируем работу двух систем с использованием различных подходов к дефаззификации.

Обратите внимание на использование округления (функция round) до одного знака после запятой. Поскольку пакет достаточно простой, то защиты от ошибок такого рода нет и при значениях не совпадающей со шкалой дискретизации могут возникнуть ошибки вида NaN.
result<-data.frame(time=1:100,TEMP=rep(0.0,100),K=rep(0.0,100),TEMP1=rep(0.0,100),K1=rep(0.0,100))
for(i in 1:99) {
  fc1<-fuzzy_inference(system,list(TEMP=result$TEMP[i],VALVE=result$K[i]))  
  result$K[i+1]<-round(gset_defuzzify(fc1, "centroid"),1)
  result$TEMP[i+1]<-round(result$TEMP[i]+(1-result$TEMP[i+1]/30)*result$K[i+1]/100,1)
  fc1<-fuzzy_inference(system,list(TEMP=result$TEMP1[i],VALVE=result$K1[i]))  
  result$K1[i+1]<-round(gset_defuzzify(fc1, "meanofmax"),1)
  result$TEMP1[i+1]<-round(result$TEMP1[i]+(1-result$TEMP1[i+1]/30)*result$K1[i+1]/100,1)
  
}
plot(TEMP~time,data=result,col="red",xlab="Время, мин.", ylab="Температура, градусы.",type="l")
lines(result$TEMP1, col="blue")


plot(K~time,data=result,col="red",xlab="Время, мин.", ylab="Открытие вентиля, проценты.",type="l")
lines(result$K1, col="blue")


Обратите внимание, что модель с центроидом не стабилизируется.

Примерно так всё оно и работает и в реальной жизни, поэтому надо достаточно вдумчиво подходить к проектированию таких систем.

Как нетрудно увидеть, результат будет зависеть о выбранных t-нормы и t-конормы, функций принадлежности, а также метода дефаззификации. Всё это даёт простор для творчества, поэтому для каждой задачи надо вдумчиво подходить к настройке всех параметров и понимать, почему вы из всех возможных вариантов выбрали именно этот.  Использовать же нечеткую логику для банальной свертки показателей в бизнес-задачах на мой взгляд моветон, поскольку придает «наукообразность» задаче, которую можно решить другими более прозрачными способами.