2025-03-01 09:17:21 +04:00

319 KiB
Raw Blame History

Управление каденсом велосипедиста

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

Входные X: speed (текущая скорость) и target (желаемая скорость) / Выходные Y: cadence (необходимый каденс)

In [1]:
import numpy as np
from skfuzzy import control as ctrl

speed = ctrl.Antecedent(np.arange(0, 100, 1), "speed")
target = ctrl.Antecedent(np.arange(0, 100, 1), "target")
cadence = ctrl.Consequent(np.arange(0, 130, 1), "cadence")

Формирование нечетких переменных для лингвистических переменных и их визуализация

In [2]:
import skfuzzy as fuzz

speed["slow"] = fuzz.zmf(speed.universe, 20, 60)
speed["average"] = fuzz.trapmf(speed.universe, [10, 30, 60, 80])
speed["fast"] = fuzz.smf(speed.universe, 60, 80)
speed.view()

target["slow"] = fuzz.zmf(target.universe, 20, 60)
target["average"] = fuzz.trapmf(target.universe, [10, 30, 60, 80])
target["fast"] = fuzz.smf(target.universe, 60, 80)
target.view()

cadence["low"] = fuzz.zmf(cadence.universe, 0, 80)
cadence["average"] = fuzz.trapmf(cadence.universe, [0, 60, 70, 90])
cadence["high"] = fuzz.smf(cadence.universe, 80, 110)
cadence.view()
d:\Методы искусственного интеллекта\MII_Salin_Oleg_PIbd-33\.venv\Lib\site-packages\skfuzzy\control\fuzzyvariable.py:125: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  fig.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Формирование и визуализация базы нечетких правил

В случае ошибки необходимо в файле

.venv/lib/python3.13/site-packages/skfuzzy/control/visualization.py

удалить лишний отступ на 182 строке, должно быть:

if not matplotlib_present:
            raise ImportError("`ControlSystemVisualizer` can only be used "
                              "with `matplotlib` present in the system.")

        self.ctrl = control_system

        self.fig, self.ax = plt.subplots()
In [3]:
rule1 = ctrl.Rule(speed["slow"] & target["fast"], cadence["high"])
rule2 = ctrl.Rule(speed["slow"] & target["average"], cadence["high"])
rule3 = ctrl.Rule(speed["slow"] & target["slow"], cadence["low"])
rule4 = ctrl.Rule(speed["average"] & target["fast"], cadence["high"])
rule5 = ctrl.Rule(speed["average"] & target["average"], cadence["average"])
rule6 = ctrl.Rule(speed["average"] & target["slow"], cadence["low"])
rule7 = ctrl.Rule(speed["fast"] & target["fast"], cadence["high"])
rule8 = ctrl.Rule(speed["fast"] & target["average"], cadence["low"])
rule9 = ctrl.Rule(speed["fast"] & target["slow"], cadence["low"])

rule1.view()
Out[3]:
(<Figure size 640x480 with 1 Axes>, <Axes: >)
No description has been provided for this image

Создание нечеткой системы и добавление нечетких правил в базу знаний нечеткой системы

In [4]:
cadence_ctrl = ctrl.ControlSystem(
    [
        rule1,
        rule2,
        rule3,
        rule4,
        rule5,
        rule6,
        rule7,
        rule8,
        rule9,
    ]
)

cadences = ctrl.ControlSystemSimulation(cadence_ctrl)

Пример расчета выходной переменной cadence на основе входных переменных speed и target

Система также формирует подробный журнал выполнения процесса нечеткого логического вывода

In [5]:
cadences.input["speed"] = 60
cadences.input["target"] = 20
cadences.compute()
cadences.print_state()
cadences.output["cadence"]
=============
 Antecedents 
=============
Antecedent: speed                   = 60
  - slow                            : 0.0
  - average                         : 1.0
  - fast                            : 0.0
Antecedent: target                  = 20
  - slow                            : 1.0
  - average                         : 0.5
  - fast                            : 0.0

=======
 Rules 
=======
RULE #0:
  IF speed[slow] AND target[fast] THEN cadence[high]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[slow]                                            : 0.0
  - target[fast]                                           : 0.0
                              speed[slow] AND target[fast] = 0.0
  Activation (THEN-clause):
                                             cadence[high] : 0.0

RULE #1:
  IF speed[slow] AND target[average] THEN cadence[high]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[slow]                                            : 0.0
  - target[average]                                        : 0.5
                           speed[slow] AND target[average] = 0.0
  Activation (THEN-clause):
                                             cadence[high] : 0.0

RULE #2:
  IF speed[slow] AND target[slow] THEN cadence[low]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[slow]                                            : 0.0
  - target[slow]                                           : 1.0
                              speed[slow] AND target[slow] = 0.0
  Activation (THEN-clause):
                                              cadence[low] : 0.0

RULE #3:
  IF speed[average] AND target[fast] THEN cadence[high]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[average]                                         : 1.0
  - target[fast]                                           : 0.0
                           speed[average] AND target[fast] = 0.0
  Activation (THEN-clause):
                                             cadence[high] : 0.0

RULE #4:
  IF speed[average] AND target[average] THEN cadence[average]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[average]                                         : 1.0
  - target[average]                                        : 0.5
                        speed[average] AND target[average] = 0.5
  Activation (THEN-clause):
                                          cadence[average] : 0.5

RULE #5:
  IF speed[average] AND target[slow] THEN cadence[low]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[average]                                         : 1.0
  - target[slow]                                           : 1.0
                           speed[average] AND target[slow] = 1.0
  Activation (THEN-clause):
                                              cadence[low] : 1.0

RULE #6:
  IF speed[fast] AND target[fast] THEN cadence[high]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[fast]                                            : 0.0
  - target[fast]                                           : 0.0
                              speed[fast] AND target[fast] = 0.0
  Activation (THEN-clause):
                                             cadence[high] : 0.0

RULE #7:
  IF speed[fast] AND target[average] THEN cadence[low]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[fast]                                            : 0.0
  - target[average]                                        : 0.5
                           speed[fast] AND target[average] = 0.0
  Activation (THEN-clause):
                                              cadence[low] : 0.0

RULE #8:
  IF speed[fast] AND target[slow] THEN cadence[low]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - speed[fast]                                            : 0.0
  - target[slow]                                           : 1.0
                              speed[fast] AND target[slow] = 0.0
  Activation (THEN-clause):
                                              cadence[low] : 0.0


==============================
 Intermediaries and Conquests 
==============================
Consequent: cadence                  = 35.97074517705886
  low:
    Accumulate using accumulation_max : 1.0
  average:
    Accumulate using accumulation_max : 0.5
  high:
    Accumulate using accumulation_max : 0.0

Out[5]:
np.float64(35.97074517705886)

Визуализация функции принадлежности для выходной переменной cadence

Функция получена в процессе аккумуляции и используется для дефаззификации значения выходной переменной cadence

In [6]:
cadence.view(sim=cadences)
No description has been provided for this image

Пример решения задачи регрессии на основе нечеткого логического вывода

Загрузка данных

In [7]:
import pandas as pd
from utils import split_stratified_into_train_val_test

df = pd.read_csv("data/car_price_prediction.csv")
df = df.drop(["ID", "Manufacturer", "Model",  "Leatherinterior", "Fueltype", "Cylinders",
          "Doors", "Wheel", "Color", "Levy", "Drive wheels", "Airbags", "Gear box type", "Mileage"], axis=1)
df = df.dropna()
df["Engine volume"] = df["Engine volume"].apply(
    lambda x: float(x.split()[0])
)


cars_train, X_val, cars_test, y_train, y_val, y_test = split_stratified_into_train_val_test(
    df,
    stratify_colname="Category",
    target_colname="Price",
    frac_train=0.80,
    frac_val=0,
    frac_test=0.20
)

cars_train = cars_train.drop("Category", axis=1)
cars_test = cars_test.drop("Category", axis=1)
display("cars_train", cars_train)
display("cars_test", cars_test)
'cars_train'
Price Prodyear Engine volume
3329 188 2009 2.5
15830 16621 2005 2.4
3384 3293 2014 5.7
1672 59464 2016 2.2
10342 8500 2006 1.5
... ... ... ...
8472 470 2015 3.0
7958 6586 2014 1.4
272 16308 2008 2.2
4525 7683 2013 2.0
5251 7997 2004 2.0

15389 rows × 3 columns

'cars_test'
Price Prodyear Engine volume
13393 3763 2016 2.0
4751 470 2011 2.4
11945 706 2010 1.3
5150 470 2019 2.0
10864 5488 2014 2.5
... ... ... ...
5343 7840 1998 2.8
9938 19444 2013 2.0
6576 26343 2017 3.0
12789 39750 2017 1.6
5395 18189 2012 1.9

3848 rows × 3 columns

Инициализация лингвистических переменных и автоматическое формирование нечетких переменных

In [8]:
prodyear = ctrl.Antecedent(np.arange(1885, 2025, 1), "prodyear")
engine_volume = ctrl.Antecedent(cars_train["Engine volume"].sort_values().unique(), "engine_volume")
print(cars_train["Price"].max())
price = ctrl.Consequent(np.arange(cars_train["Price"].min() // 100, 
                                  cars_train["Price"].max() // 100, 1), "price")

prodyear["old"] = fuzz.zmf(prodyear.universe, 1885, 2020)
prodyear["new"] = fuzz.smf(prodyear.universe, 2015, 2020)
prodyear.view()
engine_volume.automf(3, variable_type="quant")
engine_volume.view()
price["very cheap"] = fuzz.trimf(price.universe, [0, 50, 100])
price["cheap"] = fuzz.trimf(price.universe, [80, 120, 200])
price["average"] = fuzz.trapmf(price.universe, [150, 250, 500, 1000])
price["expensive"] = fuzz.trimf(price.universe, [800, 1500, 2000])
price["very expensive"] = fuzz.smf(price.universe, 1800, 2500)
price.view()
26307500
d:\Методы искусственного интеллекта\MII_Salin_Oleg_PIbd-33\.venv\Lib\site-packages\skfuzzy\control\fuzzyvariable.py:125: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  fig.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Нечеткие правила

In [9]:
rule11 = ctrl.Rule(
    engine_volume["low"] & prodyear["old"],
    price["very cheap"],
)
rule12 = ctrl.Rule(
    engine_volume["average"] & prodyear["old"],
    price["cheap"],
)
rule13 = ctrl.Rule(
    engine_volume["high"] & prodyear["old"],
    price["average"],
)
rule21 = ctrl.Rule(
    engine_volume["low"] & prodyear["new"],
    price["average"],
)
rule22 = ctrl.Rule(
    engine_volume["average"] & prodyear["new"],
    price["expensive"],
)
rule23 = ctrl.Rule(
    engine_volume["high"] & prodyear["new"],
    price["very expensive"],
)

Создание нечеткой системы

In [10]:
fuzzy_rules = [
    rule11,
    rule12,
    rule13,
    rule21,
    rule22, 
    rule23,
]

price_cntrl = ctrl.ControlSystem(fuzzy_rules)

sim = ctrl.ControlSystemSimulation(price_cntrl)

fuzzy_rules
Out[10]:
[IF engine_volume[low] AND prodyear[old] THEN price[very cheap]
 	AND aggregation function : fmin
 	OR aggregation function  : fmax,
 IF engine_volume[average] AND prodyear[old] THEN price[cheap]
 	AND aggregation function : fmin
 	OR aggregation function  : fmax,
 IF engine_volume[high] AND prodyear[old] THEN price[average]
 	AND aggregation function : fmin
 	OR aggregation function  : fmax,
 IF engine_volume[low] AND prodyear[new] THEN price[average]
 	AND aggregation function : fmin
 	OR aggregation function  : fmax,
 IF engine_volume[average] AND prodyear[new] THEN price[expensive]
 	AND aggregation function : fmin
 	OR aggregation function  : fmax,
 IF engine_volume[high] AND prodyear[new] THEN price[very expensive]
 	AND aggregation function : fmin
 	OR aggregation function  : fmax]

Пример использования полученной нечеткой системы

In [11]:
sim.input["prodyear"] = 2016
sim.input["engine_volume"] = 1.5
sim.compute()
sim.print_state()
display(sim.output["price"])
=============
 Antecedents 
=============
Antecedent: engine_volume           = 1.5
  - low                             : 0.85
  - average                         : 0.15
  - high                            : 0.0
Antecedent: prodyear                = 2016
  - old                             : 0.0017558299039780523
  - new                             : 0.08000000000000002

=======
 Rules 
=======
RULE #0:
  IF engine_volume[low] AND prodyear[old] THEN price[very cheap]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - engine_volume[low]                                     : 0.85
  - prodyear[old]                                          : 0.0017558299039780523
                      engine_volume[low] AND prodyear[old] = 0.0017558299039780523
  Activation (THEN-clause):
                                         price[very cheap] : 0.0017558299039780523

RULE #1:
  IF engine_volume[average] AND prodyear[old] THEN price[cheap]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - engine_volume[average]                                 : 0.15
  - prodyear[old]                                          : 0.0017558299039780523
                  engine_volume[average] AND prodyear[old] = 0.0017558299039780523
  Activation (THEN-clause):
                                              price[cheap] : 0.0017558299039780523

RULE #2:
  IF engine_volume[high] AND prodyear[old] THEN price[average]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - engine_volume[high]                                    : 0.0
  - prodyear[old]                                          : 0.0017558299039780523
                     engine_volume[high] AND prodyear[old] = 0.0
  Activation (THEN-clause):
                                            price[average] : 0.0

RULE #3:
  IF engine_volume[low] AND prodyear[new] THEN price[average]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - engine_volume[low]                                     : 0.85
  - prodyear[new]                                          : 0.08000000000000002
                      engine_volume[low] AND prodyear[new] = 0.08000000000000002
  Activation (THEN-clause):
                                            price[average] : 0.08000000000000002

RULE #4:
  IF engine_volume[average] AND prodyear[new] THEN price[expensive]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - engine_volume[average]                                 : 0.15
  - prodyear[new]                                          : 0.08000000000000002
                  engine_volume[average] AND prodyear[new] = 0.08000000000000002
  Activation (THEN-clause):
                                          price[expensive] : 0.08000000000000002

RULE #5:
  IF engine_volume[high] AND prodyear[new] THEN price[very expensive]
	AND aggregation function : fmin
	OR aggregation function  : fmax

  Aggregation (IF-clause):
  - engine_volume[high]                                    : 0.0
  - prodyear[new]                                          : 0.08000000000000002
                     engine_volume[high] AND prodyear[new] = 0.0
  Activation (THEN-clause):
                                     price[very expensive] : 0.0


==============================
 Intermediaries and Conquests 
==============================
Consequent: price                    = 1065.2447541258305
  very cheap:
    Accumulate using accumulation_max : 0.0017558299039780523
  cheap:
    Accumulate using accumulation_max : 0.0017558299039780523
  average:
    Accumulate using accumulation_max : 0.08000000000000002
  expensive:
    Accumulate using accumulation_max : 0.08000000000000002
  very expensive:
    Accumulate using accumulation_max : 0.0

np.float64(1065.2447541258305)

Функция для автоматизации вычисления целевой переменной Y на основе вектора признаков X

In [12]:
def fuzzy_pred(row):
    sim.input["prodyear"] = row["Prodyear"]
    sim.input["engine_volume"] = row["Engine volume"]
    sim.compute()
    return sim.output["price"]

Тестирование нечеткой системы на обучающей выборке

In [13]:
result_train = cars_train.copy()

result_train["PricePred"] = result_train.apply(fuzzy_pred, axis=1)

result_train.head(15)
Out[13]:
Price Prodyear Engine volume PricePred
3329 188 2009 2.5 99.900555
15830 16621 2005 2.4 99.815314
3384 3293 2014 5.7 99.970383
1672 59464 2016 2.2 1065.244754
10342 8500 2006 1.5 99.839062
11936 5018 2011 1.8 99.933398
15103 21012 2012 2.5 99.947365
4720 1568 2016 3.5 1065.244754
9631 34811 2012 3.0 99.947365
6122 21181 2012 2.0 99.947365
3261 23521 2014 2.5 99.970383
12788 10349 2009 1.8 99.900555
11298 76029 2016 2.3 1065.244754
5113 17562 2012 1.8 99.947365
12635 22580 2016 2.0 1065.244754

Тестирование нечеткой системы на тестовой выборке

In [14]:
result_test = cars_test.copy()

result_test["PricePred"] = result_test.apply(fuzzy_pred, axis=1)
result_test["PricePred"] = result_test["PricePred"].apply(lambda x: x * 100)

display(result_test)
Price Prodyear Engine volume PricePred
13393 3763 2016 2.0 106524.475413
4751 470 2011 2.4 9993.339772
11945 706 2010 1.3 9991.779367
5150 470 2019 2.0 77990.662719
10864 5488 2014 2.5 9997.038307
... ... ... ... ...
5343 7840 1998 2.8 9960.397848
9938 19444 2013 2.0 9995.969432
6576 26343 2017 3.0 103432.672018
12789 39750 2017 1.6 90913.773797
5395 18189 2012 1.9 9994.736527

3848 rows × 4 columns

Оценка результатов на основе метрик для задачи регрессии

In [15]:
import math
from sklearn import metrics


rmetrics = {}

rmetrics["RMSE_train"] = math.sqrt(
    metrics.mean_squared_error(result_test["Price"], result_test["PricePred"])
)
rmetrics["RMSE_test"] = math.sqrt(
    metrics.mean_squared_error(result_test["Price"], result_test["PricePred"])
)
rmetrics["RMAE_test"] = math.sqrt(
    metrics.mean_absolute_error(result_test["Price"], result_test["PricePred"])
)
rmetrics["R2_test"] = metrics.r2_score(
    result_test["Price"], result_test["PricePred"]
)

rmetrics
Out[15]:
{'RMSE_train': 34126.997123579145,
 'RMSE_test': 34126.997123579145,
 'RMAE_test': 140.19755698149206,
 'R2_test': -2.0018563819330226}