Quick start¶
This page walks through the complete workflow on the Noisy XOR benchmark from the original
Tsetlin machine paper: 12 Boolean features of which only the first two matter
(y = x0 XOR x1), with 40 % of the training labels flipped.
1. Data¶
Inputs must be Boolean (torch.bool, or anything that casts to it: 0/1 integers, floats,
NumPy arrays). Continuous or categorical data is Booleanized first — see
Booleanization. Labels are integers 0..n_classes-1.
import torch, torchtsetlin as tt
x_train, y_train = tt.data.make_noisy_xor(5000, noise=0.4, seed=0)
x_test, y_test = tt.data.make_noisy_xor(2000, noise=0.0, seed=1)
x_train.shape, x_train.dtype, y_train[:5]
# (torch.Size([5000, 12]), torch.bool, tensor([0, 1, 1, 0, 1]))
2. Model¶
device = "cuda" if torch.cuda.is_available() else "cpu"
model = tt.TsetlinMachine(
n_features=12, # Boolean inputs (None = infer from the first batch)
n_classes=2,
n_clauses=20, # clauses per class (half vote for, half against)
T=15, # vote margin
s=3.9, # specificity
n_states=50, # memory depth per action (default 128)
).to(device)
A TsetlinMachine is a torch.nn.Module. Its state is the integer buffer model.ta_state
of shape (n_classes * n_clauses, 2 * n_features); model.to(device), model.state_dict()
and torch.save work as usual. There are no gradient parameters — learning happens through
feedback, not back-propagation.
3. A manual training loop¶
The Tsetlin machine analogue of loss.backward(); optimizer.step() is one call:
for epoch in range(60):
model.train()
perm = torch.randperm(len(x_train))
for i in range(0, len(x_train), 10):
idx = perm[i:i + 10]
votes = model.update(x_train[idx].to(device), y_train[idx].to(device))
# `votes` are the class sums *before* the update -> free batch metrics
model.eval()
with torch.no_grad():
acc = (model(x_test.to(device)).argmax(1) == y_test.to(device)).float().mean()
print(f"epoch {epoch + 1}: test accuracy {acc:.3f}")
model(x) returns the vote sums (B, n_classes) clamped to [-T, T] — treat them like
logits (argmax for the class, model.predict_proba(x) for probabilities). model.train()
/ model.eval() matter: while learning, a clause without literals is considered True; when
predicting it is False.
4. ... or use the Trainer¶
trainer = tt.Trainer(model, batch_size=10, callbacks=[tt.EarlyStopping(patience=20)])
history = trainer.fit((x_train, y_train), epochs=100, val_data=(x_test, y_test))
trainer.evaluate((x_test, y_test)) # {'accuracy': 0.99...}
trainer.predict(x_test[:5]) # tensor([1, 0, 0, 1, 1])
history.plot() # matplotlib curve of val_accuracy
The Trainer accepts (x, y) tensors, a torch.utils.data.Dataset or a DataLoader, moves
batches to the model's device, and logs one line per epoch.
5. Read the model¶
for rule in model.rules()[:5]:
print(rule)
# IF x0 AND NOT x1 THEN 1
# IF x1 AND NOT x0 THEN 1
# IF x0 AND x1 THEN NOT 1
# ...
print(tt.interpret.explain(model, x_test[0]))
Continue with choosing a device, the training guide or the interpretability guide.