Advanced usage
This document illustrates some advance aspects of the package.
[1]:
import zadeh
[2]:
# Build a demo
service = zadeh.FuzzyVariable(
zadeh.FloatDomain("service", 0, 10, 100),
{
"poor": zadeh.GaussianFuzzySet(1.5, 0),
"good": zadeh.GaussianFuzzySet(1.5, 5),
"excellent": zadeh.GaussianFuzzySet(1.5, 10),
},
)
food = zadeh.FuzzyVariable(
zadeh.FloatDomain("food", 0, 10, 100),
{
"rancid": zadeh.TrapezoidalFuzzySet(-2, 0, 1, 3),
"delicious": zadeh.TrapezoidalFuzzySet(7, 9, 10, 12),
},
)
tip = zadeh.FuzzyVariable(
zadeh.FloatDomain("tip", 0, 30, 100),
{
"cheap": zadeh.TriangularFuzzySet(0, 5, 10),
"average": zadeh.TriangularFuzzySet(10, 15, 20),
"generous": zadeh.TriangularFuzzySet(20, 25, 30),
},
)
rule_set = [
((service == "poor") | (food == "rancid")) >> (tip == "cheap"),
(service == "good") >> (tip == "average"),
((service == "excellent") | (food == "delicious")) >> (tip == "generous"),
]
fis = zadeh.FIS([food, service], rule_set, tip)
Compiling models
If you have the gcc compiler available in your system, you can automatically fast, compiled versions of them. The outline of the procedure is following:
C code for the system is generated.
The code is compiled into a dynamic library.
The library is linked and interfaced in Python.
A FIS subclass allows its usage.
To do all of this, just call the compile method of an existing FIS:
[3]:
fisc=fis.compile()
The time improvement can be checked below:
[4]:
%%timeit
fis.get_crisp_output({"food": 0, "service": 8})
854 µs ± 57.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
[5]:
%%timeit
fisc.get_crisp_output({"food": 0, "service": 8})
10.4 µs ± 229 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
The difference might increase in more complex systems, so its worth considering. However, user-defined functions cannot be automatically converted into C code.
Saving and loading models
Models can be saved and loaded using a json format
[6]:
fis.save("/tmp/mymodel.zadeh")
[7]:
!cat /tmp/mymodel.zadeh
{"variables": [{"name": "food", "values": {"rancid": {"type": "trapezoidal", "a": -2.0, "b": 0.0, "c": 1.0, "d": 3.0}, "delicious": {"type": "trapezoidal", "a": 7.0, "b": 9.0, "c": 10.0, "d": 12.0}}, "domain": {"type": "FloatDomain", "name": "food", "min": 0, "max": 10, "steps": 100}}, {"name": "service", "values": {"poor": {"type": "gaussian", "s": 1.5, "a": 0}, "good": {"type": "gaussian", "s": 1.5, "a": 5}, "excellent": {"type": "gaussian", "s": 1.5, "a": 10}}, "domain": {"type": "FloatDomain", "name": "service", "min": 0, "max": 10, "steps": 100}}], "rules": {"rule_list": [{"antecedent": {"type": "or", "children": [{"type": "is", "variable": "service", "value": "poor"}, {"type": "is", "variable": "food", "value": "rancid"}]}, "consequent": {"type": "is", "variable": "tip", "value": "cheap"}, "weight": 1.0}, {"antecedent": {"type": "is", "variable": "service", "value": "good"}, "consequent": {"type": "is", "variable": "tip", "value": "average"}, "weight": 1.0}, {"antecedent": {"type": "or", "children": [{"type": "is", "variable": "service", "value": "excellent"}, {"type": "is", "variable": "food", "value": "delicious"}]}, "consequent": {"type": "is", "variable": "tip", "value": "generous"}, "weight": 1.0}]}, "target": {"name": "tip", "values": {"cheap": {"type": "triangular", "a": 0.0, "b": 5.0, "c": 10.0}, "average": {"type": "triangular", "a": 10.0, "b": 15.0, "c": 20.0}, "generous": {"type": "triangular", "a": 20.0, "b": 25.0, "c": 30.0}}, "domain": {"type": "FloatDomain", "name": "tip", "min": 0, "max": 30, "steps": 100}}, "defuzzification": "centroid"}
[8]:
fis2=zadeh.FIS.load("/tmp/mymodel.zadeh")
Importing models from MATLAB files
MATLAB .fis models can be imported into zadeh, with limited support at the moment of writing.
[9]:
!cat ../tests/data/tipper.fis
% $Revision: 1.1 $
[System]
Name='tipper'
Type='mamdani'
NumInputs=2
NumOutputs=1
NumRules=3
AndMethod='min'
OrMethod='max'
ImpMethod='min'
AggMethod='max'
DefuzzMethod='centroid'
[Input1]
Name='service'
Range=[0 10]
NumMFs=3
MF1='poor':'gaussmf',[1.5 0]
MF2='good':'gaussmf',[1.5 5]
MF3='excellent':'gaussmf',[1.5 10]
[Input2]
Name='food'
Range=[0 10]
NumMFs=2
MF1='rancid':'trapmf',[0 0 1 3]
MF2='delicious':'trapmf',[7 9 10 10]
[Output1]
Name='tip'
Range=[0 30]
NumMFs=3
MF1='cheap':'trimf',[0 5 10]
MF2='average':'trimf',[10 15 20]
MF3='generous':'trimf',[20 25 30]
[Rules]
1 1, 1 (1) : 2
2 0, 2 (1) : 1
3 2, 3 (1) : 2
[10]:
fis3=zadeh.FIS.from_matlab("../tests/data/tipper.fis")
fis3.rules
[10]:
FuzzyRuleSet<if ((service is poor) or (food is rancid)) then (tip is cheap) [1.000000]
if ((service is good) and (food is not delicious)) then (tip is average) [1.000000]
if ((service is excellent) or (food is delicious)) then (tip is generous) [1.000000]>
Tuning models
Suppose you have a built a FIS and you gather some data to validate it. Could it be improved by altering the formal definitions used? You could use the data to evaluate the model with different parameters and choose the best option. The zadeh package provides a scikit-learn-based interface to do so.
[11]:
# Generate synthetic data to test the tuning
# Assume the tipping model is exactly off by two units of tip
import numpy as np
import pandas as pd
df = pd.DataFrame(
[
{
"food": f,
"service": s,
"tip": fis.get_crisp_output({"food": f, "service": s}) - 2.0,
}
for f in np.linspace(0, 10)
for s in np.linspace(0, 10)
]
)
df
[11]:
food | service | tip | |
---|---|---|---|
0 | 0.0 | 0.000000 | 3.074485 |
1 | 0.0 | 0.204082 | 3.115666 |
2 | 0.0 | 0.408163 | 3.175973 |
3 | 0.0 | 0.612245 | 3.262110 |
4 | 0.0 | 0.816327 | 3.381884 |
... | ... | ... | ... |
2495 | 10.0 | 9.183673 | 22.618116 |
2496 | 10.0 | 9.387755 | 22.737890 |
2497 | 10.0 | 9.591837 | 22.824027 |
2498 | 10.0 | 9.795918 | 22.884334 |
2499 | 10.0 | 10.000000 | 22.925515 |
2500 rows × 3 columns
For this constructed data, our model will overestimate the real tips. We could try to modify the definitions of the tip values to see what matches best (which should be lower values).
[12]:
# Grid parameter tuning
tuner = zadeh.FuzzyGridTune(
fis,
{
"target_tip_cheap_b": [3, 5, 7],
"target_tip_average_b": [13, 15, 17],
"target_tip_generous_b": [23, 25, 27],
},
scoring="neg_root_mean_squared_error", # Equivalent to minimizing the RMSE
n_jobs=4, # Parallel jobs
)
[13]:
tuner.fit(df)
[14]:
# The best parameters can be checked
tuner.best_params_
[14]:
{'target_tip_average_b': 13,
'target_tip_cheap_b': 3,
'target_tip_generous_b': 23}
[15]:
# The tuned FIS is available
tuner.tuned_fis_
[15]:
<zadeh.fis.FIS at 0x7f90a7d2a370>
The following table summarizes the parameter syntax:
Meaning |
Syntax |
Example |
Example value |
---|---|---|---|
Parameter of a value of an input |
var_<variable>_<value>_<parameter> |
var_food_rancid_b |
2.0 |
Parameter of a value of the output |
target_<name>_<value>_<parameter> |
target_tip_cheap_c |
3.0 |
Deffuzification method |
defuzzification |
defuzzification |
centroid |
Serving models
Created models can be served using a Flask server.
Consider the following example for model deployment
[16]:
from zadeh import server
fis_flask = server.FISFlask("myserver", fis)
app = fis_flask.app
# This app object can be used to deploy the model
# For example, to run a development server:
# app.run(debug=False, host='0.0.0.0')
# The object can be provied to middleware such as gunicorn for production deployment
[ ]: