Skip to content

ANFIS (trainable TSK)

ANFIS is a first-order Takagi-Sugeno system whose parameters are learned from data (Jang, 1993). Over a grid partition, each of the p inputs gets n_mf Gaussian membership functions, giving n_mf ** p rules; each rule emits an affine function of the inputs.

Training is Jang's hybrid scheme, one pass per epoch:

  1. with the premise (Gaussian) parameters fixed, the affine consequents are solved in closed form by least squares (the output is linear in them);
  2. with the consequents fixed, the premise centers and widths take a gradient-descent step on the MSE.
import numpy as np
import fuzzytool as fz

x = np.linspace(0, 2 * np.pi, 200)
y = np.sin(x)

model = fz.ANFIS(n_inputs=1, n_mf=6).fit(x[:, None], y, epochs=100)
model.predict(x[:, None])     # approximates sin(x)
model.history_                # RMSE per epoch

X is always 2-D (n_samples, n_features).

Choosing a partition

A grid partition needs n_mf ** p rules, so it stops being usable past three or four inputs — 5 inputs with 3 MFs each is already 243 rules and 1458 consequent parameters. A cluster partition puts one rule per fuzzy c-means cluster, each with its own Gaussian per input, so you choose the rule count directly:

model = fz.ANFIS(n_inputs=5, partition="cluster", n_rules=10).fit(X, y, epochs=20)

On a 5-input problem with 600 samples this is 42× faster with 24× fewer rules and essentially the same fit. Use the grid partition when you want a readable linguistic partition over a couple of inputs; use the cluster partition for anything wider.

Regularization and early stopping

Cluster rules overlap by construction, which makes the consequent least-squares solve ill-conditioned. That partition therefore defaults to a small ridge; the grid partition keeps Jang's original unregularized solve.

fz.ANFIS(5, partition="cluster", n_rules=10)             # ridge=1e-6 by default
fz.ANFIS(5, partition="cluster", n_rules=10, ridge=0.0)  # opt out
fz.ANFIS(2, n_mf=4, ridge=1e-5)                          # opt in on a grid

Set tol to stop once the training RMSE stops improving:

fz.ANFIS(1, n_mf=4, tol=1e-4, patience=3).fit(X, y, epochs=200)

Exporting the trained model

to_tsk() turns a fitted ANFIS into a plain TSK system — so the trained model becomes an ordinary rule base you can print, explain, audit and save:

system = model.to_tsk(["x0", "x1"])
system.explain(x0=1.0, x1=2.0)
fz.save(system, "learned.json")

For a scatter partition without the gradient step, see chiu_tsk and cmeans_tsk, which fit the consequents in a single least-squares pass.