Fuzzy Logic in Python for Adaptive AI, Control, Forecasting, and Brain-Inspired Systems

What fuzzy logic is actually good for

Fuzzy logic is most useful when a system must act on vague, linguistic, or uncertain conditions rather than on a single sharp threshold. Instead of forcing reality into crude binary rules such as “fast/slow” or “safe/unsafe,” fuzzy logic allows graded reasoning such as “speed is moderately high” or “uncertainty is low but rising.” In Python, scikit-fuzzy provides a control-system API with constructs such as antecedents, consequents, rules, control systems, and simulations for building such rule-based decision layers.
══════════════════════════
That makes fuzzy logic especially valuable in:

supervisory control,

rule-based decision support,

interpretable model selection,

safety and fallback logic,

systems where expert knowledge matters.

Fuzzy logic is not a universal substitute for machine learning. It is strongest when the problem has a small or medium number of meaningful variables and when human-readable rules are desirable.

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Python is enough; fuzzy logic does not need MATLAB

In Python, fuzzy inference can be implemented directly with scikit-fuzzy, while machine-learning models can be trained with general frameworks such as TensorFlow and PyTorch. TensorFlow is positioned as an end-to-end platform for creating ML models across desktop, mobile, web, and cloud, while PyTorch provides production-ready distributed training and a broad ecosystem for research and deployment. That means Python can host both the learned models and the fuzzy supervisory layer in one stack.

The correct way to think about this is simple:

TensorFlow / PyTorch train the data-driven models.

Fuzzy logic decides how to use them under changing conditions.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Where fuzzy logic fits in modern AI systems

A good modern use of fuzzy logic is not “replace deep learning with fuzzy rules.” A better use is to place fuzzy logic above trained models as a supervisory layer.

For example, suppose you train several models for the same domain:

one optimized for speed,

one for accuracy,

one for noisy inputs,

one for low-power or edge deployment.

A fuzzy controller can then choose among them using inputs such as:

latency requirement,

confidence score,

input quality,

compute budget,

risk tolerance,

environmental instability.

This gives a practical and interpretable adaptive system. But it must be designed carefully. Dynamic model switching has real costs: switching overhead, calibration mismatch, unstable oscillation between models, and maintenance burden from keeping many models healthy.

So fuzzy logic is useful here, but only when the control variables are clear and the switching policy is worth the complexity.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Forecasting: fuzzy logic can help, but it is not magic

Fuzzy logic has real forecasting use cases, especially in evolving fuzzy systems and neuro-fuzzy systems. A 2024 systematic review found a substantial literature on evolving fuzzy inference systems for forecasting in non-stationary and dynamic settings, including many different structures and application fields. A 2025 open-access study on Baltic Dry Index forecasting used ANFIS and reported better RMSE than a feed-forward neural network and traditional AR/ARMA baselines in that study.

But the correct conclusion is not “fuzzy always beats ML.” The real conclusion is narrower:

fuzzy methods can be useful when the data are non-stationary,

when expert priors matter,

when interpretability matters,

or when a hybrid neuro-fuzzy design captures useful structure.

For many high-dimensional forecasting tasks, plain deep learning, transformers, state-space models, or probabilistic methods may still be better. Fuzzy logic is a tool, not a universal winner.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Brain-inspired methods: where fuzzy logic can and cannot fit

Spiking neural networks (SNNs) are one of the most practical brain-inspired methods now being studied for robotics and temporal data. A robotics survey highlights SNN-based control applications and emphasizes speed, energy efficiency, and computation capability as motivations. A 2024 time-series paper reports SNN forecasting results that are comparable to or better than classic ANN baselines on its benchmarks with lower energy use.

So yes, brain-inspired methods usable today can be combined with fuzzy logic.

Good combinations

Fuzzy logic can sit on top of a brain-inspired model as:

a task selector,

a safety governor,

a resource-aware policy switcher,

an interpretable arbitration layer,

or a high-level context manager.

Example:

an SNN handles perception and temporal dynamics,

a fuzzy layer decides whether the robot should prioritize speed, precision, stability, or caution.

That is a sensible hybrid.

Bad combinations

Fuzzy logic should not be forced to act as the entire brain-like substrate for:

rich perception,

large-scale memory formation,

end-to-end sequence learning,

or high-dimensional continuous control by itself.

In those cases, the fuzzy layer becomes brittle or explodes in rule count.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Neuro-fuzzy systems are legitimate, but they have limits

Hybrid systems that combine neural networks and fuzzy inference are real and still active. Recent work such as DCNFIS explicitly combines deep learning with fuzzy inference and argues that such architectures can improve transparency while maintaining competitive accuracy. That makes neuro-fuzzy systems a valid design option when explainability matters.

But they are not automatically superior to standard deep learning. Their value is greatest when:

the problem has interpretable intermediate concepts,

rule transparency is useful,

and the dimensionality is still manageable.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

What to build in practice

A solid Python architecture looks like this:

Layer 1: specialized models

Train several models for distinct operating regimes.

Layer 2: monitoring signals

Continuously compute:

latency,

confidence,

drift,

sensor quality,

uncertainty,

resource load.

Layer 3: fuzzy supervisor

Use fuzzy rules to choose:

which model to run,

whether to fall back,

whether to slow down,

whether to request more evidence.

Layer 4: feedback

Track outcomes and update either:

fuzzy membership functions,

fuzzy rules,

or the underlying models.

This is a realistic adaptive system. It is not a brain emulator. It is an engineering architecture.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Final conclusion

Fuzzy logic remains useful in Python, especially as an interpretable supervisory layer for adaptive systems. It is relevant to control, forecasting, and hybrid AI. It can also be combined with brain-inspired methods such as SNNs and modular cognitive systems. But it should be used where it is strong: handling vagueness, expert rules, safety logic, and transparent decision layers. It should not be inflated into a universal theory of intelligence or sold as a direct path to real-time whole-brain emulation.

Practical synthesis of WBE and Fuzzy Logic

brain-inspired core for perception, memory, temporal dynamics;

fuzzy supervisory layer for interpretable context-dependent control.

That combination is reasonable.
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

Practical method: MLP regression + fuzzy supervisor for model selection

For a house-price style MLP, the target is a scalar regression output. For your case, replace “house price” with gold-price value, gold-price change, or gold signal score. In Python, Keras provides the standard training workflow, including fit(), callbacks such as EarlyStopping and ModelCheckpoint, and model saving in the recommended .keras format. For the fuzzy layer, scikit-fuzzy provides the standard control-system objects: Antecedent, Consequent, Rule, ControlSystem, and ControlSystemSimulation. For a forecasting problem, you should use time-ordered validation, not random splitting; scikit-learn’s TimeSeriesSplit exists specifically because ordinary shuffled splits leak future information into the past.

Right Architecture for this use case

Do not begin with “one big fuzzy system that directly predicts gold price.” That is usually the wrong design.

Use a two-layer design:

Model bank
Train several regression MLPs with:

different feature subsets,

different hidden-layer sizes,

different dropout/L2 settings,

different random initializations.

Fuzzy supervisor
At prediction time, fuzzy logic decides either:

which model to trust most, or

what weight to give each model.

For astrological forecasting program, the second option is usually better: fuzzy-weighted ensemble is better than hard winner-take-all selection, because financial and astrological regimes drift and a single “best” model often changes abruptly. The fuzzy layer is then a supervisory decision system, exactly the kind of problem the skfuzzy.control API is meant for.

Simple Practical Explanation

Simple way to improve forecasting:
Instead of trusting one neural net, train many MLP models with different architectures, feature groups, and starting random weights. Then use fuzzy logic to judge which model is more reliable under current conditions. Fuzzy logic can look at recent error, direction accuracy, and stability, then give each model a weight. Final forecast is a weighted combination of all models. This is more realistic than trusting one model forever, especially in changing markets like gold.

Do not trust only one neural network.

Train several small MLP models on the same forecasting problem:

same target,

but different architectures,

different starting random weights,

and sometimes different parameter groups.

Then do not choose one model forever.

Instead, for each new forecast, use a fuzzy logic supervisor that asks simple questions such as:

Which model has been more accurate recently?

Which model is more stable?

Which model is getting the direction right more often?

Which model is suitable for the current market condition?

Then fuzzy logic gives more weight to better models and less weight to weaker models.

So the final forecast is not:

“Use model 3 only”

but rather:

“Use 50% of model 3, 30% of model 1, and 20% of model 5”

This is useful for gold forecasting because market behavior changes. A single model may work well in one period and badly in another. TensorFlow/Keras is enough for training the models, and scikit-fuzzy is enough for the fuzzy decision layer. For forecasting, the validation must be time-ordered; TimeSeriesSplit exists for exactly that reason.

The practical programming method

Train many MLPs → measure recent quality of each model → use fuzzy logic to assign weights → combine them into one final forecast.

Step 1: decide the target

For gold forecasting, use one of these:

next gold price

next gold change

next gold return

next gold signal score

For your first program, keep it simple:

input row at date t

target = gold value at t+1

Step 2: arrange features in groups

For your astrological program, do not mix everything blindly at first.

Make blocks such as:

astro_core = main astrological parameters

transit = transit-related parameters

cycle = tithi/nakshatra/monthly cycle type parameters

market = previous gold values, momentum, volatility, etc.

Then different models can use different combinations.

Example:

Model A uses astro_core

Model B uses astro_core + transit

Model C uses astro_core + cycle

Model D uses astro_core + transit + market

This is better than one giant messy model.

Step 3: train several MLPs

Use different:

hidden-layer sizes

dropout values

seeds / initial random weights

This is standard Keras work with Sequential, fit(), EarlyStopping, and model saving. A plain stack of layers is exactly what Sequential is for.

Example set:

small = [64, 32]

medium = [128, 64, 32]

deep = [256, 128, 64, 32]

And for each architecture, train with seeds:

11

22

33

That already gives many different models.

Step 4: validate correctly

For house-price toy demos, people often use random train/test split.
For forecasting, that is wrong.

Use time-ordered splits only. TimeSeriesSplit is meant for time-ordered data because ordinary cross-validation can train on future data and evaluate on past data.

So:

train on old data

validate on later data

repeat over several folds

Step 5: measure each model

For every model, store:

rmse = average magnitude error

dir_acc = how often direction was correct

stability = how much performance changes across folds

These three are enough for your first fuzzy system.

Step 6: fuzzy logic gives model weight

The fuzzy system will read:

error = low / medium / high

direction accuracy = weak / fair / strong

stability = unstable / moderate / stable

Output:

model weight = very low / low / medium / high / very high

Example rules:

If error is low and direction is strong and stability is stable → weight very high

If error is high → weight very low

If direction is weak even when error is medium → weight low

If error is medium but direction is strong and stability is stable → weight medium to high

This is exactly the kind of rule-based decision system that scikit-fuzzy supports with Antecedent, Consequent, Rule, ControlSystem, and ControlSystemSimulation.

Step 7: final forecast

Let each trained model predict the next value.

Then fuzzy logic gives a weight to each model.

final forecast = [ ∑(weighti​ × predictioni​) ] / ∑(weighti​)

So the final answer is a weighted combination, not blind trust in one model.

Actual starter code

At the end of this page is a usable starter program CODE-1.

It is not your final production system, but it is the correct starting structure.

It assumes a CSV file like this:

Date

gold_next ← target column

many feature columns such as:

astro_1, astro_2, …

transit_1, transit_2, …

cycle_1, cycle_2, …

market_1, market_2, …

How you adapt CODE-1 to your astrological forecasting program

This is the only part you really need to change first.

A. Replace feature names

Instead of:

"astro_1", "astro_2", …

use your real columns, for example:

FEATURE_GROUPS = {
"astro_core": [
"d1_strength",
"d9_strength",
"d60_strength",
"yoga_score",
"bhava_strength"
],
"transit": [
"gochara_score",
"dasha_score"
],
"cycle": [
"tithi_score",
"nakshatra_score",
"paksha_score"
],
"market": [
"gold_prev_1",
"gold_prev_5",
"volatility_10",
"momentum_10"
]
}

B. Decide target carefully

If your target is too noisy, create one of these instead:

next gold value

next gold difference

next gold percent change

next gold direction only

For first testing, simplest is:
gold_next

C. Add your own fuzzy rules

If you care more about direction than exact magnitude, then make fuzzy rules stricter on direction.

Example:

low error + weak direction = only medium weight

medium error + strong direction + stable = high weight

That will suit financial or astrological signal systems better.

The simplest possible mental picture

Think of this like 5 astrologers:

each astrologer uses a slightly different method

each astrologer also has good and bad periods

fuzzy logic is the chairman who says:

“Today astrologer 2 is more reliable, astrologer 5 is also useful, astrologer 1 should be given less importance.”

That is all.

What not to do

Do not do these mistakes:

do not use only one model

do not use random data shuffle for time forecasting

do not feed 50 fuzzy inputs into one fuzzy system

do not make fuzzy logic predict gold directly at first

do not use hard winner-take-all model selection first

Use fuzzy weights, not hard switching, in the first version.

TimeSeriesSplit is specifically meant for time-ordered data, and Keras already gives the standard training/evaluation/saving workflow you need for this style of experiment.

import os
import json
import math
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import TimeSeriesSplit
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import skfuzzy as fuzz
from skfuzzy import control as ctrl

  1. -----
  2. 1. USER SETTINGS
  3. -----

CSV_FILE = "gold_data.csv"
DATE_COL = "Date"
TARGET_COL = "gold_next"
N_SPLITS = 5
EPOCHS = 100
BATCH_SIZE = 32
MODEL_DIR = "saved_models"

os.makedirs(MODEL_DIR, exist_ok=True)

  1. Feature groups for your astrology/gold system

FEATURE_GROUPS = {
"astro_core": ["astro_1", "astro_2", "astro_3", "astro_4"],
"transit": ["transit_1", "transit_2"],
"cycle": ["cycle_1", "cycle_2"],
"market": ["market_1", "market_2", "market_3"]
}

  1. Each model uses some feature groups + architecture + random seed

MODEL_SPECS = [
{"name": "M1", "groups": ["astro_core"], "layers": [64, 32], "dropout": 0.10, "seed": 11},
{"name": "M2", "groups": ["astro_core", "transit"], "layers": [64, 32], "dropout": 0.10, "seed": 22},
{"name": "M3", "groups": ["astro_core", "cycle"], "layers": [128, 64, 32], "dropout": 0.10, "seed": 33},
{"name": "M4", "groups": ["astro_core", "market"], "layers": [128, 64, 32], "dropout": 0.15, "seed": 44},
{"name": "M5", "groups": ["astro_core", "transit", "market"], "layers": [128, 64, 32], "dropout": 0.15, "seed": 55},
{"name": "M6", "groups": ["astro_core", "transit", "cycle", "market"], "layers": [256, 128, 64, 32], "dropout": 0.20, "seed": 66},
]

  1. -----
  2. 2. FUZZY SYSTEM
  3. -----

def build_fuzzy_system():
# error: 0 best, 1 worst
error = ctrl.Antecedent(np.arange(0, 1.01, 0.01), 'error')
direction = ctrl.Antecedent(np.arange(0, 1.01, 0.01), 'direction')
stability = ctrl.Antecedent(np.arange(0, 1.01, 0.01), 'stability')

weight = ctrl.Consequent(np.arange(0, 101, 1), 'weight')

error['low'] = fuzz.trimf(error.universe, [0.0, 0.0, 0.4])
error['medium'] = fuzz.trimf(error.universe, [0.2, 0.5, 0.8])
error['high'] = fuzz.trimf(error.universe, [0.6, 1.0, 1.0])

direction['weak'] = fuzz.trimf(direction.universe, [0.0, 0.0, 0.5])
direction['fair'] = fuzz.trimf(direction.universe, [0.3, 0.5, 0.7])
direction['strong'] = fuzz.trimf(direction.universe, [0.5, 1.0, 1.0])

stability['unstable'] = fuzz.trimf(stability.universe, [0.0, 0.0, 0.5])
stability['moderate'] = fuzz.trimf(stability.universe, [0.3, 0.5, 0.7])
stability['stable'] = fuzz.trimf(stability.universe, [0.5, 1.0, 1.0])

weight['very_low'] = fuzz.trimf(weight.universe, [0, 0, 20])
weight['low'] = fuzz.trimf(weight.universe, [10, 25, 40])
weight['medium'] = fuzz.trimf(weight.universe, [35, 50, 65])
weight['high'] = fuzz.trimf(weight.universe, [60, 75, 90])
weight['very_high'] = fuzz.trimf(weight.universe, [80, 100, 100])

rules = [
ctrl.Rule(error['low'] & direction['strong'] & stability['stable'], weight['very_high']),
ctrl.Rule(error['low'] & direction['fair'] & stability['stable'], weight['high']),
ctrl.Rule(error['medium'] & direction['strong'] & stability['stable'], weight['high']),
ctrl.Rule(error['medium'] & direction['strong'] & stability['moderate'], weight['medium']),
ctrl.Rule(error['medium'] & direction['fair'] & stability['stable'], weight['medium']),
ctrl.Rule(error['high'], weight['very_low']),
ctrl.Rule(direction['weak'], weight['low']),
ctrl.Rule(stability['unstable'] & error['medium'], weight['low']),
ctrl.Rule(stability['unstable'] & direction['weak'], weight['very_low']),
]

system = ctrl.ControlSystem(rules)
return system

FUZZY_SYSTEM = build_fuzzy_system()

def fuzzy_weight(err_norm, dir_acc, stability_score):
sim = ctrl.ControlSystemSimulation(FUZZY_SYSTEM)
sim.input['error'] = float(err_norm)
sim.input['direction'] = float(dir_acc)
sim.input['stability'] = float(stability_score)
sim.compute()
return float(sim.output['weight'])

  1. -----
  2. 3. DATA HELPERS
  3. -----

def load_data():
df = pd.read_csv(CSV_FILE)
df = df.sort_values(DATE_COL).reset_index(drop=True)
return df

def get_feature_list(groups):
cols = []
for g in groups:
cols.extend(FEATURE_GROUPS[g])
return cols

def direction_accuracy(y_true, y_pred):
# Here direction means whether predicted change sign matches true sign
# If target is absolute next value instead of change, convert outside this function.
y_true_sign = np.sign(y_true)
y_pred_sign = np.sign(y_pred)
return np.mean(y_true_sign == y_pred_sign)

  1. -----
  2. 4. MODEL BUILDING
  3. -----

def build_mlp(input_dim, layers, dropout, seed):
tf.keras.utils.set_random_seed(seed)

model = keras.Sequential()
model.add(keras.layers.Input(shape=(input_dim,)))

for units in layers:
model.add(keras.layers.Dense(units, activation='relu'))
if dropout > 0:
model.add(keras.layers.Dropout(dropout))

model.add(keras.layers.Dense(1, activation='linear'))

model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss='mse',
metrics=[keras.metrics.RootMeanSquaredError(name='rmse')]
)
return model

  1. -----
  2. 5. CROSS-VALIDATION EVALUATION
  3. -----

def evaluate_model_spec(df, spec):
features = get_feature_list(spec["groups"])
X = df[features].values.astype(np.float32)
y = df[TARGET_COL].values.astype(np.float32)

tscv = TimeSeriesSplit(n_splits=N_SPLITS)

fold_rmses = []
fold_diraccs = []

for fold_no, (train_idx, test_idx) in enumerate(tscv.split(X), start=1):
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

model = build_mlp(
input_dim=X_train.shape[1],
layers=spec["layers"],
dropout=spec["dropout"],
seed=spec["seed"]
)

callbacks = [
keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=10,
restore_best_weights=True
)
]

model.fit(
X_train, y_train,
validation_data=(X_test, y_test),
epochs=EPOCHS,
batch_size=BATCH_SIZE,
verbose=0,
callbacks=callbacks
)

pred = model.predict(X_test, verbose=0).reshape(-1)
rmse = math.sqrt(mean_squared_error(y_test, pred))
diracc = direction_accuracy(y_test, pred)

fold_rmses.append(rmse)
fold_diraccs.append(diracc)

avg_rmse = float(np.mean(fold_rmses))
avg_diracc = float(np.mean(fold_diraccs))

# Higher stability means less variation across folds
rmse_std = float(np.std(fold_rmses))
stability_score = 1.0 / (1.0 + rmse_std) # simple stable score in (0,1]

return {
"name": spec["name"],
"groups": spec["groups"],
"layers": spec["layers"],
"dropout": spec["dropout"],
"seed": spec["seed"],
"avg_rmse": avg_rmse,
"avg_diracc": avg_diracc,
"rmse_std": rmse_std,
"stability_score_raw": stability_score
}

  1. -----
  2. 6. NORMALIZE METRICS FOR FUZZY INPUT
  3. -----

def normalize_summaries(summaries):
rmses = np.array([s["avg_rmse"] for s in summaries], dtype=np.float32)
stabs = np.array([s["stability_score_raw"] for s in summaries], dtype=np.float32)

rmse_min, rmse_max = float(rmses.min()), float(rmses.max())
stab_min, stab_max = float(stabs.min()), float(stabs.max())

for s in summaries:
if rmse_max > rmse_min:
err_norm = (s["avg_rmse"] - rmse_min) / (rmse_max - rmse_min)
else:
err_norm = 0.0

if stab_max > stab_min:
stability_norm = (s["stability_score_raw"] - stab_min) / (stab_max - stab_min)
else:
stability_norm = 1.0

s["err_norm"] = float(err_norm)
s["stability_norm"] = float(stability_norm)

return summaries

  1. -----
  2. 7. TRAIN FINAL MODELS ON ALL DATA
  3. -----

def train_final_model(df, spec):
features = get_feature_list(spec["groups"])
X = df[features].values.astype(np.float32)
y = df[TARGET_COL].values.astype(np.float32)

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

model = build_mlp(
input_dim=X_scaled.shape[1],
layers=spec["layers"],
dropout=spec["dropout"],
seed=spec["seed"]
)

callbacks = [
keras.callbacks.EarlyStopping(
monitor='loss',
patience=10,
restore_best_weights=True
)
]

model.fit(
X_scaled, y,
epochs=EPOCHS,
batch_size=BATCH_SIZE,
verbose=0,
callbacks=callbacks
)

model_path = os.path.join(MODEL_DIR, f"{spec['name']}.keras")
model.save(model_path)

return model, scaler, model_path

  1. -----
  2. 8. MAIN TRAINING PIPELINE
  3. -----

def train_all():
df = load_data()

summaries = []
for spec in MODEL_SPECS:
print(f"Evaluating {spec['name']} …")
summary = evaluate_model_spec(df, spec)
summaries.append(summary)

summaries = normalize_summaries(summaries)

# Compute fuzzy weights from historical summary metrics
for s in summaries:
s["fuzzy_weight"] = fuzzy_weight(
err_norm=s["err_norm"],
dir_acc=s["avg_diracc"],
stability_score=s["stability_norm"]
)

print("\nModel summaries:")
for s in summaries:
print(json.dumps(s, indent=2))

trained = {}
for spec in MODEL_SPECS:
print(f"Training final model {spec['name']} on all past data …")
model, scaler, model_path = train_final_model(df, spec)
trained[spec["name"]] = {
"model": model,
"scaler": scaler,
"path": model_path
}

with open("model_summaries.json", "w", encoding="utf-8") as f:
json.dump(summaries, f, indent=2)

return summaries, trained

  1. -----
  2. 9. FINAL ENSEMBLE PREDICTION
  3. -----

def predict_one(df_current_row, summaries, trained):
preds = []
weights = []

for s in summaries:
name = s["name"]
spec = next(x for x in MODEL_SPECS if x["name"] == name)

features = get_feature_list(spec["groups"])
x = df_current_row[features].values.astype(np.float32).reshape(1, -1)

scaler = trained[name]["scaler"]
model = trained[name]["model"]

x_scaled = scaler.transform(x)
pred = float(model.predict(x_scaled, verbose=0)[0, 0])

preds.append(pred)
weights.append(max(s["fuzzy_weight"], 0.0001))

preds = np.array(preds, dtype=np.float32)
weights = np.array(weights, dtype=np.float32)

final_pred = float(np.sum(preds * weights) / np.sum(weights))

return final_pred, preds, weights

  1. -----
  2. 10. RUN
  3. -----

if name == "main":
df = load_data()

summaries, trained = train_all()

# Example: use the latest available row to forecast next target
current_row = df.iloc[-1]
final_pred, preds, weights = predict_one(current_row, summaries, trained)

print("\nIndividual predictions:")
for i, s in enumerate(summaries):
print(f"{s['name']}: pred={preds[i]:.6f}, fuzzy_weight={weights[i]:.3f}")

print(f"\nFinal weighted prediction = {final_pred:.6f}")

Minimum Real Code : tiny house-price demo

This demo shows only one idea:

train 3 MLP models

compare them

use fuzzy logic to give each model a weight

final answer = weighted average of all 3 predictions

After you understand this, the same method can be used in your gold / astrological program.

Simple idea

Suppose 3 different models are predicting house price:

Model A = small model

Model B = medium model

Model C = deep model

After training, some models are:

more accurate

more stable

more trustworthy

So instead of blindly using only one model, we use fuzzy logic:

if model error is low and stability is high → give high weight

if model error is high → give low weight

Then final price:
Final Price= (wA​pA​+wB​pB​+wC​pC) / (wA+wB+wC)

Install packages

pip install tensorflow pandas numpy scikit-learn scikit-fuzzy

CSV file format

reate a file named house_prices.csv. Example (You can add many more rows later.):

size_sqft,bedrooms,age_years,price
1200,2,10,210000
1500,3,8,260000
1800,3,5,310000
2200,4,12,340000
900,2,20,150000
1400,3,15,230000
1700,3,7,295000
2500,4,6,420000
1100,2,18,175000
2000,4,9,360000
1300,3,14,220000
1600,3,10,275000
1900,4,8,325000
2100,4,4,390000
1000,2,25,140000
1550,3,11,250000
1750,3,9,300000
2300,4,7,410000
1250,2,16,205000
1450,3,13,240000

Full tiny Python program

import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
import skfuzzy as fuzz
from skfuzzy import control as ctrl

  1. ---------
  2. 1. LOAD DATA
  3. ---------

df = pd.read_csv("house_prices.csv")

X = df[["size_sqft", "bedrooms", "age_years"]].values.astype(np.float32)
y = df["price"].values.astype(np.float32)

  1. Simple train/test split for house-price demo

X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)

  1. Scale input features

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

  1. ---------
  2. 2. BUILD MLP MODELS
  3. ---------

def build_model(input_dim, layers, seed):
tf.keras.utils.set_random_seed(seed)

model = keras.Sequential()
model.add(keras.layers.Input(shape=(input_dim,)))

for units in layers:
model.add(keras.layers.Dense(units, activation="relu"))

model.add(keras.layers.Dense(1, activation="linear"))

model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss="mse"
)
return model

model_specs = [
{"name": "A", "layers": [16], "seed": 11},
{"name": "B", "layers": [32, 16], "seed": 22},
{"name": "C", "layers": [64, 32, 16], "seed": 33},
]

  1. ---------
  2. 3. TRAIN MODELS AND MEASURE ERROR
  3. ---------

results = []

for spec in model_specs:
model = build_model(X_train.shape[1], spec["layers"], spec["seed"])

early_stop = keras.callbacks.EarlyStopping(
monitor="val_loss",
patience=20,
restore_best_weights=True
)

history = model.fit(
X_train, y_train,
validation_split=0.2,
epochs=300,
batch_size=4,
verbose=0,
callbacks=[early_stop]
)

pred = model.predict(X_test, verbose=0).reshape(-1)
rmse = np.sqrt(mean_squared_error(y_test, pred))

# stability = how much validation loss moved around near the end
val_losses = history.history["val_loss"]
if len(val_losses) >= 10:
stability_raw = np.std(val_losses[-10:])
else:
stability_raw = np.std(val_losses)

results.append({
"name": spec["name"],
"model": model,
"rmse": float(rmse),
"stability_raw": float(stability_raw)
})

  1. ---------
  2. 4. NORMALIZE METRICS
  3. error_norm: 0 = best, 1 = worst
  4. stability_norm: 0 = bad, 1 = good
  5. ---------

rmses = np.array([r["rmse"] for r in results], dtype=np.float32)
stabs = np.array([r["stability_raw"] for r in results], dtype=np.float32)

rmse_min, rmse_max = rmses.min(), rmses.max()
stab_min, stab_max = stabs.min(), stabs.max()

for r in results:
if rmse_max > rmse_min:
r["error_norm"] = float((r["rmse"] - rmse_min) / (rmse_max - rmse_min))
else:
r["error_norm"] = 0.0

# lower raw stability std means better stability
if stab_max > stab_min:
badness = (r["stability_raw"] - stab_min) / (stab_max - stab_min)
r["stability_norm"] = float(1.0 - badness)
else:
r["stability_norm"] = 1.0

  1. ---------
  2. 5. FUZZY LOGIC SYSTEM
  3. Inputs:
  4. error_norm : 0 best, 1 worst
  5. stability_norm : 0 bad, 1 good
  6. Output:
  7. weight : 0 to 100
  8. ---------

error = ctrl.Antecedent(np.arange(0, 1.01, 0.01), "error")
stability = ctrl.Antecedent(np.arange(0, 1.01, 0.01), "stability")
weight = ctrl.Consequent(np.arange(0, 101, 1), "weight")

error["low"] = fuzz.trimf(error.universe, [0.0, 0.0, 0.4])
error["medium"] = fuzz.trimf(error.universe, [0.2, 0.5, 0.8])
error["high"] = fuzz.trimf(error.universe, [0.6, 1.0, 1.0])

stability["low"] = fuzz.trimf(stability.universe, [0.0, 0.0, 0.4])
stability["medium"] = fuzz.trimf(stability.universe, [0.2, 0.5, 0.8])
stability["high"] = fuzz.trimf(stability.universe, [0.6, 1.0, 1.0])

weight["very_low"] = fuzz.trimf(weight.universe, [0, 0, 20])
weight["low"] = fuzz.trimf(weight.universe, [10, 25, 40])
weight["medium"] = fuzz.trimf(weight.universe, [35, 50, 65])
weight["high"] = fuzz.trimf(weight.universe, [60, 75, 90])
weight["very_high"] = fuzz.trimf(weight.universe, [80, 100, 100])

rules = [
ctrl.Rule(error["low"] & stability["high"], weight["very_high"]),
ctrl.Rule(error["low"] & stability["medium"], weight["high"]),
ctrl.Rule(error["medium"] & stability["high"], weight["high"]),
ctrl.Rule(error["medium"] & stability["medium"], weight["medium"]),
ctrl.Rule(error["medium"] & stability["low"], weight["low"]),
ctrl.Rule(error["high"], weight["very_low"]),
]

fuzzy_system = ctrl.ControlSystem(rules)

def get_fuzzy_weight(error_value, stability_value):
sim = ctrl.ControlSystemSimulation(fuzzy_system)
sim.input["error"] = error_value
sim.input["stability"] = stability_value
sim.compute()
return float(sim.output["weight"])

for r in results:
r["fuzzy_weight"] = get_fuzzy_weight(r["error_norm"], r["stability_norm"])

  1. ---------
  2. 6. TEST ON ONE NEW HOUSE
  3. ---------

new_house = pd.DataFrame([{
"size_sqft": 1850,
"bedrooms": 3,
"age_years": 8
}])

new_X = scaler.transform(new_house.values.astype(np.float32))

predictions = []
weights = []

for r in results:
pred = float(r["model"].predict(new_X, verbose=0)[0, 0])
w = max(r["fuzzy_weight"], 0.0001)

predictions.append(pred)
weights.append(w)

final_price = np.sum(np.array(predictions) * np.array(weights)) / np.sum(weights)

  1. ---------
  2. 7. PRINT RESULTS
  3. ---------

print("\nModel Results")
print("-" * 60)
for r, pred in zip(results, predictions):
print(
f"Model {r['name']} | "
f"RMSE={r['rmse']:.2f} | "
f"ErrorNorm={r['error_norm']:.3f} | "
f"StabilityNorm={r['stability_norm']:.3f} | "
f"FuzzyWeight={r['fuzzy_weight']:.2f} | "
f"Prediction={pred:.2f}"
)

print("-" * 60)
print(f"Final Weighted House Price = {final_price:.2f}")

What this program does

It trains 3 MLPs

A = small

B = medium

C = deeper

It measures 2 things

RMSE = how wrong the model is

stability = whether validation loss is jumping around too much

It uses fuzzy logic

If a model has:

low error

high stability

then fuzzy logic gives it high weight.

If a model has:

high error

then fuzzy logic gives it very low weight.

Then it predicts one new house

Each model predicts price.

Then final answer is weighted average.

How to run it

In terminal:

python fuzzy_house_price_demo.py

You will see output like:

Model A | RMSE=18000.22 | ErrorNorm=0.10 | StabilityNorm=0.75 | FuzzyWeight=82.50 | Prediction=302000.00
Model B | RMSE=21000.44 | ErrorNorm=0.40 | StabilityNorm=0.60 | FuzzyWeight=55.30 | Prediction=308500.00
Model C | RMSE=26000.89 | ErrorNorm=1.00 | StabilityNorm=0.30 | FuzzyWeight=12.10 | Prediction=315000.00
Final Weighted House Price = 305100.25

How to understand it in plain language

This program says:

Model A is good, so trust it more

Model B is acceptable, so trust it somewhat

Model C is weak, so trust it very little

Then combine all 3.

That is the whole idea.

How to convert this into astrological gold system

After you understand this demo, only 4 things change:

Replace input columns

Instead of:

size_sqft

bedrooms

age_years

you will use things like:

d1_strength

d9_strength

d60_strength

gochara_score

dasha_score

nakshatra_score

tithi_score

gold_prev_1

gold_prev_5

Replace target

Instead of house price, use:

gold_next
or

gold_change_next

Replace random split

For gold forecasting, later use time-based split, not random split.

Add more fuzzy inputs later

For gold, later you may also add:

direction accuracy

regime match

recent success in bull phase / bear phase

But not now. First understand this 2-input fuzzy demo.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Noncommercial 2.5 License.