Training a Linear Assisted Player by Imitation¶
Author: Rob Hendriks
Last verified: 17 September 2026
This tutorial shows how to bootstrap the trainable linear (Lin) assisted players from a known-correct classical strategy.
Rather than train the complete game end-to-end, we first train the four learned components separately: Alice's measurement and combine layers, and Bob's measurement and combine layers. We then insert those weights into the full models and verify the result in a tournament.
The important representation rule is simple:
the game uses bits, while the trainable model uses logits.
A bit 0 or 1 is represented inside the model by the sign of a logit. In this tutorial we use hard logits -10 and +10. The gameplay adapters form the boundary between these two representations:
game bits → gameplay adapter → model logits → gameplay adapter → game bits
Inside the model — including between learned layers and the shared-resource (SR) layer — values remain logits.
Imports¶
We use the current linear assisted-player stack, the canonical dataset utilities, and the gameplay adapters used during tournament evaluation.
import numpy as np
import tensorflow as tf
from Q_Sea_Battle.game_layout import GameLayout
from Q_Sea_Battle.game_env import GameEnv
from Q_Sea_Battle.tournament import Tournament
from Q_Sea_Battle.lin_trainable_assisted_model_a import LinTrainableAssistedModelA
from Q_Sea_Battle.lin_trainable_assisted_model_b import LinTrainableAssistedModelB
from Q_Sea_Battle.trainable_assisted_players import TrainableAssistedPlayers
from Q_Sea_Battle.gameplay_adapters import GameplayModelAAdapter, GameplayModelBAdapter, hard_logit
from Q_Sea_Battle.lin_dataset_generation_utilities import generate_lin_dataset
from Q_Sea_Battle.lin_dataset_conversion_utilities import (
convert_layer_measure_a,
convert_layer_measure_b,
convert_layer_combine_a,
convert_layer_combine_b,
)
print("TensorFlow:", tf.__version__)
TensorFlow: 2.21.0
1. Choose a Small Test Problem¶
We use a 4×4 field and one communication bit. This is small enough to train quickly, while still exercising the complete linear assisted protocol.
p_rule=1 gives the ideal shared-resource correlation. With zero channel noise, a correctly trained and assembled model should therefore reach a tournament score very close to 1.0.
The measurement layers learn relatively simple local rules. The combine layers must learn parity-like functions and usually need substantially more training.
FIELD_SIZE = 4
COMMS_SIZE = 1
# shared resource (SR) correlation parameter used by your task
P_RULE = 1.0
BETA = 10.0 # magnitude used for hard logits throughout this tutorial
# Dataset / training sizes
DATASET_SIZE = 50_000
BATCH_SIZE = 256
EPOCHS_MEAS = 4 # we can use smaller number of epochs for measurement training, since it is an easier task
EPOCHS_COMB = 100 # parity-like combine layers usually need more training
# DIAL/DRU training settings
SR_MODE_BOOTSTRAP_EVAL = "stochastic"
SR_MODE_DIAL_TRAIN = "replay"
SR_MODE_DIAL_EVAL = "stochastic"
SEED = 123
tf.random.set_seed(SEED)
np.random.seed(SEED)
# Folders
data_dir = Path("notebooks/data")
models_dir = Path("notebooks/models")
data_dir.mkdir(parents=True, exist_ok=True)
models_dir.mkdir(parents=True, exist_ok=True)
n2 = FIELD_SIZE * FIELD_SIZE
print("n2:", n2, "m:", COMMS_SIZE)
n2: 16 m: 1
2. Generate the Classical Imitation Data¶
The canonical dataset is generated in ordinary bits. Before a tensor is presented to a trainable layer, however, we convert it to the representation used by the model.
For a hard-logit magnitude β = 10:
- bit
0becomes logit-10 - bit
1becomes logit+10
This distinction matters. A raw bit value 0 is not a valid logit representation of logical zero: when logits are interpreted by sign, both 0 and 1 would lie on the non-negative side of the decision boundary.
We therefore keep bits only in the canonical dataset and convert every model-facing input and imitation target to hard logits.
# Build layout for data generation (enemy_probability/channel_noise not used by these generators)
layout = GameLayout(field_size=FIELD_SIZE, comms_size=COMMS_SIZE)
# --- Generate canonical linear dataset in bits ---
ds_lin = generate_lin_dataset(n2=n2, m=COMMS_SIZE, num_games=DATASET_SIZE, seed=SEED, validate=True)
# --- Convert to layer-specific training views in hard logits ---
layer_data_meas_a = convert_layer_measure_a(ds_lin, rep_x="hard_logit", rep_y="hard_logit", beta=BETA)
layer_data_comb_a = convert_layer_combine_a(ds_lin, rep_outcome="hard_logit", rep_target="hard_logit", beta=BETA)
layer_data_meas_b = convert_layer_measure_b(ds_lin, rep_x="hard_logit", rep_y="hard_logit", beta=BETA)
layer_data_comb_b = convert_layer_combine_b(
ds_lin,
rep_outcome_b="hard_logit",
rep_comm_in="hard_logit",
rep_shoot="hard_logit",
beta=BETA,
)
print("Datasets ready (bits canonical, logits training views).")
Datasets ready (bits canonical, logits training views).
def logits_bce_from_logit_targets(y_true_logits, y_pred_logits):
"""BCE with logits where targets are semantic logits and labels are derived by sign."""
y_true_logits = tf.cast(y_true_logits, tf.float32)
y_pred_logits = tf.cast(y_pred_logits, tf.float32)
labels = tf.cast(y_true_logits >= 0.0, tf.float32)
per_elem = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=y_pred_logits)
return tf.reduce_mean(per_elem)
def sign_accuracy_from_logit_targets(y_true_logits, y_pred_logits):
"""Accuracy computed by sign agreement between target logits and predicted logits."""
y_true_logits = tf.cast(y_true_logits, tf.float32)
y_pred_logits = tf.cast(y_pred_logits, tf.float32)
target_bits = tf.cast(y_true_logits >= 0.0, tf.float32)
pred_bits = tf.cast(y_pred_logits >= 0.0, tf.float32)
return tf.reduce_mean(tf.cast(tf.equal(target_bits, pred_bits), tf.float32))
def train_single_input_layer(layer, x_train, y_train, *, epochs: int, batch_size: int, learning_rate: float = 1e-3, verbose: int = 1):
"""Train a single-input Keras layer inside a tiny functional model."""
input_tensor = tf.keras.Input(shape=x_train.shape[1:], dtype=tf.float32)
output_tensor = layer(input_tensor)
model = tf.keras.Model(inputs=input_tensor, outputs=output_tensor)
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
loss=logits_bce_from_logit_targets,
metrics=[sign_accuracy_from_logit_targets],
)
model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size, verbose=verbose)
return model
def train_two_input_layer(layer, x_left, x_right, y_train, *, epochs: int, batch_size: int, learning_rate: float = 1e-3, verbose: int = 1):
"""Train a two-input Keras layer inside a tiny functional model."""
left_tensor = tf.keras.Input(shape=x_left.shape[1:], dtype=tf.float32)
right_tensor = tf.keras.Input(shape=x_right.shape[1:], dtype=tf.float32)
output_tensor = layer(left_tensor, right_tensor)
model = tf.keras.Model(inputs=[left_tensor, right_tensor], outputs=output_tensor)
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
loss=logits_bce_from_logit_targets,
metrics=[sign_accuracy_from_logit_targets],
)
model.fit([x_left, x_right], y_train, epochs=epochs, batch_size=batch_size, verbose=verbose)
return model
3. Train the Four Learned Layers¶
We train the layers separately so that each part of the protocol can be checked independently:
LinMeasurementLayerALinCombineLayerALinMeasurementLayerBLinCombineLayerB
The SR layer itself is not learned here; it implements the assisted correlation rule.
The training targets remain logits. Binary cross-entropy still needs 0/1 labels, so the loss converts the sign of each target logit to a label internally. Accuracy is also measured by sign agreement.
This keeps the representation contract intact: the network sees logits, while the loss only translates their sign at the final mathematical boundary.
# --- Train layers ---
from Q_Sea_Battle.lin_measurement_layer_a import LinMeasurementLayerA
from Q_Sea_Battle.lin_measurement_layer_b import LinMeasurementLayerB
from Q_Sea_Battle.lin_combine_layer_a import LinCombineLayerA
from Q_Sea_Battle.lin_combine_layer_b import LinCombineLayerB
# --- Build layers ---
n2 = FIELD_SIZE * FIELD_SIZE
model_a = LinTrainableAssistedModelA(
field_size=FIELD_SIZE,
comms_size=COMMS_SIZE,
sr_mode=SR_MODE_BOOTSTRAP_EVAL, # evaluation mode
seed=SEED,
p_rule=P_RULE,
)
meas_layer_a = model_a.measure_layer
comb_layer_a = model_a.combine_layer
model_b = LinTrainableAssistedModelB(
field_size=FIELD_SIZE,
comms_size=COMMS_SIZE,
sr_mode=SR_MODE_BOOTSTRAP_EVAL, # evaluation mode
seed=SEED,
p_rule=P_RULE,
)
meas_layer_b = model_b.measure_layer
comb_layer_b = model_b.combine_layer
x_meas_a, y_meas_a = layer_data_meas_a[0]
x_comb_a, y_comb_a = layer_data_comb_a[0]
x_meas_b, y_meas_b = layer_data_meas_b[0]
(x_comb_b_left, x_comb_b_right), y_comb_b = layer_data_comb_b[0]
_ = train_single_input_layer(meas_layer_a, x_meas_a, y_meas_a, epochs=EPOCHS_MEAS, batch_size=BATCH_SIZE)
_ = train_single_input_layer(comb_layer_a, x_comb_a, y_comb_a, epochs=EPOCHS_COMB, batch_size=BATCH_SIZE)
_ = train_single_input_layer(meas_layer_b, x_meas_b, y_meas_b, epochs=EPOCHS_MEAS, batch_size=BATCH_SIZE)
_ = train_two_input_layer(comb_layer_b, x_comb_b_left, x_comb_b_right, y_comb_b, epochs=EPOCHS_COMB, batch_size=BATCH_SIZE)
print("Standalone layers trained.")
WARNING:tensorflow:From c:\Users\nly99857\OneDrive - Philips\SW Projects\QSeaBattle\env_QSeaBattle\Lib\site-packages\keras\src\backend\tensorflow\core.py:233: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead. WARNING:tensorflow:TensorFlow GPU support is not available on native Windows for TensorFlow >= 2.11. Even if CUDA/cuDNN are installed, GPU will not be used. Please use WSL2 or the TensorFlow-DirectML plugin. Epoch 1/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - loss: 0.4460 - sign_accuracy_from_logit_targets: 0.8556 Epoch 2/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0280 - sign_accuracy_from_logit_targets: 0.9956 Epoch 3/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0105 - sign_accuracy_from_logit_targets: 0.9994 Epoch 4/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0055 - sign_accuracy_from_logit_targets: 0.9998 Epoch 1/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 1.0599 - sign_accuracy_from_logit_targets: 0.4993 Epoch 2/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.7414 - sign_accuracy_from_logit_targets: 0.5045 Epoch 3/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.7101 - sign_accuracy_from_logit_targets: 0.5337 Epoch 4/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.6692 - sign_accuracy_from_logit_targets: 0.5897 Epoch 5/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.6276 - sign_accuracy_from_logit_targets: 0.6495 Epoch 6/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.5922 - sign_accuracy_from_logit_targets: 0.6926 Epoch 7/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.5592 - sign_accuracy_from_logit_targets: 0.7269 Epoch 8/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.5279 - sign_accuracy_from_logit_targets: 0.7576 Epoch 9/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.4920 - sign_accuracy_from_logit_targets: 0.7841 Epoch 10/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.4622 - sign_accuracy_from_logit_targets: 0.8032 Epoch 11/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.4353 - sign_accuracy_from_logit_targets: 0.8193 Epoch 12/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.4056 - sign_accuracy_from_logit_targets: 0.8365 Epoch 13/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.3751 - sign_accuracy_from_logit_targets: 0.8523 Epoch 14/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.3502 - sign_accuracy_from_logit_targets: 0.8648 Epoch 15/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.3173 - sign_accuracy_from_logit_targets: 0.8842 Epoch 16/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2830 - sign_accuracy_from_logit_targets: 0.9015 Epoch 17/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.2542 - sign_accuracy_from_logit_targets: 0.9163 Epoch 18/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2269 - sign_accuracy_from_logit_targets: 0.9285 Epoch 19/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2003 - sign_accuracy_from_logit_targets: 0.9411 Epoch 20/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1790 - sign_accuracy_from_logit_targets: 0.9507 Epoch 21/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1601 - sign_accuracy_from_logit_targets: 0.9580 Epoch 22/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1435 - sign_accuracy_from_logit_targets: 0.9641 Epoch 23/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1295 - sign_accuracy_from_logit_targets: 0.9689 Epoch 24/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1182 - sign_accuracy_from_logit_targets: 0.9724 Epoch 25/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1082 - sign_accuracy_from_logit_targets: 0.9758 Epoch 26/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0985 - sign_accuracy_from_logit_targets: 0.9790 Epoch 27/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0881 - sign_accuracy_from_logit_targets: 0.9822 Epoch 28/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0794 - sign_accuracy_from_logit_targets: 0.9845 Epoch 29/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0726 - sign_accuracy_from_logit_targets: 0.9859 Epoch 30/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0666 - sign_accuracy_from_logit_targets: 0.9869 Epoch 31/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0614 - sign_accuracy_from_logit_targets: 0.9883 Epoch 32/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0572 - sign_accuracy_from_logit_targets: 0.9892 Epoch 33/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0538 - sign_accuracy_from_logit_targets: 0.9898 Epoch 34/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0508 - sign_accuracy_from_logit_targets: 0.9905 Epoch 35/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0479 - sign_accuracy_from_logit_targets: 0.9911 Epoch 36/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0450 - sign_accuracy_from_logit_targets: 0.9918 Epoch 37/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0425 - sign_accuracy_from_logit_targets: 0.9922 Epoch 38/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0404 - sign_accuracy_from_logit_targets: 0.9926 Epoch 39/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0385 - sign_accuracy_from_logit_targets: 0.9932 Epoch 40/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0368 - sign_accuracy_from_logit_targets: 0.9934 Epoch 41/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0354 - sign_accuracy_from_logit_targets: 0.9935 Epoch 42/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0341 - sign_accuracy_from_logit_targets: 0.9935 Epoch 43/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0329 - sign_accuracy_from_logit_targets: 0.9938 Epoch 44/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 2s 8ms/step - loss: 0.0318 - sign_accuracy_from_logit_targets: 0.9939 Epoch 45/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0308 - sign_accuracy_from_logit_targets: 0.9940 Epoch 46/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0299 - sign_accuracy_from_logit_targets: 0.9942 Epoch 47/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0290 - sign_accuracy_from_logit_targets: 0.9944 Epoch 48/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0281 - sign_accuracy_from_logit_targets: 0.9944 Epoch 49/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0272 - sign_accuracy_from_logit_targets: 0.9946 Epoch 50/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0263 - sign_accuracy_from_logit_targets: 0.9947 Epoch 51/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0256 - sign_accuracy_from_logit_targets: 0.9950 Epoch 52/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0249 - sign_accuracy_from_logit_targets: 0.9951 Epoch 53/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0243 - sign_accuracy_from_logit_targets: 0.9952 Epoch 54/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0238 - sign_accuracy_from_logit_targets: 0.9952 Epoch 55/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0233 - sign_accuracy_from_logit_targets: 0.9953 Epoch 56/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0227 - sign_accuracy_from_logit_targets: 0.9955 Epoch 57/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0222 - sign_accuracy_from_logit_targets: 0.9956 Epoch 58/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0218 - sign_accuracy_from_logit_targets: 0.9957 Epoch 59/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0213 - sign_accuracy_from_logit_targets: 0.9957 Epoch 60/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0209 - sign_accuracy_from_logit_targets: 0.9957 Epoch 61/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0205 - sign_accuracy_from_logit_targets: 0.9957 Epoch 62/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0202 - sign_accuracy_from_logit_targets: 0.9957 Epoch 63/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0198 - sign_accuracy_from_logit_targets: 0.9958 Epoch 64/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0195 - sign_accuracy_from_logit_targets: 0.9960 Epoch 65/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0192 - sign_accuracy_from_logit_targets: 0.9960 Epoch 66/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0189 - sign_accuracy_from_logit_targets: 0.9960 Epoch 67/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0186 - sign_accuracy_from_logit_targets: 0.9961 Epoch 68/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0183 - sign_accuracy_from_logit_targets: 0.9961 Epoch 69/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0180 - sign_accuracy_from_logit_targets: 0.9961 Epoch 70/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0177 - sign_accuracy_from_logit_targets: 0.9961 Epoch 71/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0175 - sign_accuracy_from_logit_targets: 0.9962 Epoch 72/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0172 - sign_accuracy_from_logit_targets: 0.9962 Epoch 73/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0170 - sign_accuracy_from_logit_targets: 0.9963 Epoch 74/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0168 - sign_accuracy_from_logit_targets: 0.9963 Epoch 75/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0165 - sign_accuracy_from_logit_targets: 0.9964 Epoch 76/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0163 - sign_accuracy_from_logit_targets: 0.9964 Epoch 77/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0161 - sign_accuracy_from_logit_targets: 0.9964 Epoch 78/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0159 - sign_accuracy_from_logit_targets: 0.9964 Epoch 79/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0158 - sign_accuracy_from_logit_targets: 0.9964 Epoch 80/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0156 - sign_accuracy_from_logit_targets: 0.9964 Epoch 81/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0154 - sign_accuracy_from_logit_targets: 0.9965 Epoch 82/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0152 - sign_accuracy_from_logit_targets: 0.9965 Epoch 83/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0150 - sign_accuracy_from_logit_targets: 0.9966 Epoch 84/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0148 - sign_accuracy_from_logit_targets: 0.9966 Epoch 85/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0144 - sign_accuracy_from_logit_targets: 0.9967 Epoch 86/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0140 - sign_accuracy_from_logit_targets: 0.9968 Epoch 87/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0137 - sign_accuracy_from_logit_targets: 0.9970 Epoch 88/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0134 - sign_accuracy_from_logit_targets: 0.9970 Epoch 89/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0132 - sign_accuracy_from_logit_targets: 0.9971 Epoch 90/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0130 - sign_accuracy_from_logit_targets: 0.9971 Epoch 91/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0128 - sign_accuracy_from_logit_targets: 0.9972 Epoch 92/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0127 - sign_accuracy_from_logit_targets: 0.9972 Epoch 93/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0125 - sign_accuracy_from_logit_targets: 0.9972 Epoch 94/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0124 - sign_accuracy_from_logit_targets: 0.9972 Epoch 95/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0123 - sign_accuracy_from_logit_targets: 0.9972 Epoch 96/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0122 - sign_accuracy_from_logit_targets: 0.9973 Epoch 97/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0120 - sign_accuracy_from_logit_targets: 0.9973 Epoch 98/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0119 - sign_accuracy_from_logit_targets: 0.9973 Epoch 99/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0118 - sign_accuracy_from_logit_targets: 0.9974 Epoch 100/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0117 - sign_accuracy_from_logit_targets: 0.9974 Epoch 1/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.2549 - sign_accuracy_from_logit_targets: 0.9278 Epoch 2/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0114 - sign_accuracy_from_logit_targets: 1.0000 Epoch 3/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0042 - sign_accuracy_from_logit_targets: 1.0000 Epoch 4/4 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0022 - sign_accuracy_from_logit_targets: 1.0000 Epoch 1/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 3.1315 - sign_accuracy_from_logit_targets: 0.5027 Epoch 2/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 1.2644 - sign_accuracy_from_logit_targets: 0.5120 Epoch 3/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.9416 - sign_accuracy_from_logit_targets: 0.5358 Epoch 4/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.7768 - sign_accuracy_from_logit_targets: 0.5733 Epoch 5/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.6951 - sign_accuracy_from_logit_targets: 0.6054 Epoch 6/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.6534 - sign_accuracy_from_logit_targets: 0.6281 Epoch 7/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.6245 - sign_accuracy_from_logit_targets: 0.6515 Epoch 8/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.6018 - sign_accuracy_from_logit_targets: 0.6719 Epoch 9/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.5720 - sign_accuracy_from_logit_targets: 0.6969 Epoch 10/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.5412 - sign_accuracy_from_logit_targets: 0.7230 Epoch 11/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.5034 - sign_accuracy_from_logit_targets: 0.7524 Epoch 12/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.4724 - sign_accuracy_from_logit_targets: 0.7741 Epoch 13/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.4391 - sign_accuracy_from_logit_targets: 0.7950 Epoch 14/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.3947 - sign_accuracy_from_logit_targets: 0.8218 Epoch 15/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.3379 - sign_accuracy_from_logit_targets: 0.8525 Epoch 16/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2804 - sign_accuracy_from_logit_targets: 0.8836 Epoch 17/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2320 - sign_accuracy_from_logit_targets: 0.9068 Epoch 18/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1910 - sign_accuracy_from_logit_targets: 0.9256 Epoch 19/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.1599 - sign_accuracy_from_logit_targets: 0.9389 Epoch 20/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.1329 - sign_accuracy_from_logit_targets: 0.9505 Epoch 21/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1114 - sign_accuracy_from_logit_targets: 0.9591 Epoch 22/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0960 - sign_accuracy_from_logit_targets: 0.9650 Epoch 23/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0825 - sign_accuracy_from_logit_targets: 0.9707 Epoch 24/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0715 - sign_accuracy_from_logit_targets: 0.9746 Epoch 25/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0624 - sign_accuracy_from_logit_targets: 0.9781 Epoch 26/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0552 - sign_accuracy_from_logit_targets: 0.9813 Epoch 27/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0490 - sign_accuracy_from_logit_targets: 0.9837 Epoch 28/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0436 - sign_accuracy_from_logit_targets: 0.9857 Epoch 29/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0392 - sign_accuracy_from_logit_targets: 0.9872 Epoch 30/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0360 - sign_accuracy_from_logit_targets: 0.9881 Epoch 31/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0333 - sign_accuracy_from_logit_targets: 0.9889 Epoch 32/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0310 - sign_accuracy_from_logit_targets: 0.9896 Epoch 33/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0288 - sign_accuracy_from_logit_targets: 0.9903 Epoch 34/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0271 - sign_accuracy_from_logit_targets: 0.9910 Epoch 35/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0251 - sign_accuracy_from_logit_targets: 0.9918 Epoch 36/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0235 - sign_accuracy_from_logit_targets: 0.9920 Epoch 37/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0221 - sign_accuracy_from_logit_targets: 0.9924 Epoch 38/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0212 - sign_accuracy_from_logit_targets: 0.9927 Epoch 39/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0204 - sign_accuracy_from_logit_targets: 0.9930 Epoch 40/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0199 - sign_accuracy_from_logit_targets: 0.9929 Epoch 41/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0194 - sign_accuracy_from_logit_targets: 0.9932 Epoch 42/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0191 - sign_accuracy_from_logit_targets: 0.9932 Epoch 43/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0190 - sign_accuracy_from_logit_targets: 0.9932 Epoch 44/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0186 - sign_accuracy_from_logit_targets: 0.9933 Epoch 45/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0183 - sign_accuracy_from_logit_targets: 0.9936 Epoch 46/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0183 - sign_accuracy_from_logit_targets: 0.9938 Epoch 47/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0181 - sign_accuracy_from_logit_targets: 0.9935 Epoch 48/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0185 - sign_accuracy_from_logit_targets: 0.9934 Epoch 49/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0184 - sign_accuracy_from_logit_targets: 0.9935 Epoch 50/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0185 - sign_accuracy_from_logit_targets: 0.9934 Epoch 51/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0187 - sign_accuracy_from_logit_targets: 0.9934 Epoch 52/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0188 - sign_accuracy_from_logit_targets: 0.9933 Epoch 53/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0179 - sign_accuracy_from_logit_targets: 0.9937 Epoch 54/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0169 - sign_accuracy_from_logit_targets: 0.9939 Epoch 55/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0175 - sign_accuracy_from_logit_targets: 0.9935 Epoch 56/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0169 - sign_accuracy_from_logit_targets: 0.9940 Epoch 57/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0154 - sign_accuracy_from_logit_targets: 0.9941 Epoch 58/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0159 - sign_accuracy_from_logit_targets: 0.9943 Epoch 59/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0154 - sign_accuracy_from_logit_targets: 0.9943 Epoch 60/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0173 - sign_accuracy_from_logit_targets: 0.9936 Epoch 61/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0154 - sign_accuracy_from_logit_targets: 0.9943 Epoch 62/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0148 - sign_accuracy_from_logit_targets: 0.9949 Epoch 63/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0149 - sign_accuracy_from_logit_targets: 0.9945 Epoch 64/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0152 - sign_accuracy_from_logit_targets: 0.9943 Epoch 65/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0150 - sign_accuracy_from_logit_targets: 0.9945 Epoch 66/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0144 - sign_accuracy_from_logit_targets: 0.9945 Epoch 67/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0135 - sign_accuracy_from_logit_targets: 0.9950 Epoch 68/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0117 - sign_accuracy_from_logit_targets: 0.9958 Epoch 69/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0135 - sign_accuracy_from_logit_targets: 0.9953 Epoch 70/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0132 - sign_accuracy_from_logit_targets: 0.9952 Epoch 71/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0118 - sign_accuracy_from_logit_targets: 0.9961 Epoch 72/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0129 - sign_accuracy_from_logit_targets: 0.9953 Epoch 73/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0162 - sign_accuracy_from_logit_targets: 0.9940 Epoch 74/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0110 - sign_accuracy_from_logit_targets: 0.9960 Epoch 75/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0109 - sign_accuracy_from_logit_targets: 0.9962 Epoch 76/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0128 - sign_accuracy_from_logit_targets: 0.9954 Epoch 77/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0110 - sign_accuracy_from_logit_targets: 0.9962 Epoch 78/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0103 - sign_accuracy_from_logit_targets: 0.9963 Epoch 79/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0103 - sign_accuracy_from_logit_targets: 0.9963 Epoch 80/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0105 - sign_accuracy_from_logit_targets: 0.9963 Epoch 81/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0117 - sign_accuracy_from_logit_targets: 0.9958 Epoch 82/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0131 - sign_accuracy_from_logit_targets: 0.9953 Epoch 83/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0091 - sign_accuracy_from_logit_targets: 0.9969 Epoch 84/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0115 - sign_accuracy_from_logit_targets: 0.9956 Epoch 85/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0131 - sign_accuracy_from_logit_targets: 0.9952 Epoch 86/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0091 - sign_accuracy_from_logit_targets: 0.9971 Epoch 87/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0094 - sign_accuracy_from_logit_targets: 0.9967 Epoch 88/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0099 - sign_accuracy_from_logit_targets: 0.9962 Epoch 89/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0088 - sign_accuracy_from_logit_targets: 0.9969 Epoch 90/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0089 - sign_accuracy_from_logit_targets: 0.9969 Epoch 91/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0154 - sign_accuracy_from_logit_targets: 0.9941 Epoch 92/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0092 - sign_accuracy_from_logit_targets: 0.9969 Epoch 93/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0098 - sign_accuracy_from_logit_targets: 0.9967 Epoch 94/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0099 - sign_accuracy_from_logit_targets: 0.9963 Epoch 95/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0069 - sign_accuracy_from_logit_targets: 0.9977 Epoch 96/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0085 - sign_accuracy_from_logit_targets: 0.9973 Epoch 97/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0127 - sign_accuracy_from_logit_targets: 0.9953 Epoch 98/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0115 - sign_accuracy_from_logit_targets: 0.9958 Epoch 99/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0063 - sign_accuracy_from_logit_targets: 0.9979 Epoch 100/100 196/196 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0077 - sign_accuracy_from_logit_targets: 0.9971 Standalone layers trained.
4. Assemble the Full Models¶
The four layers above were trained in isolation. We now create fresh evaluation models and copy each learned weight set into the corresponding layer.
This step is deliberately explicit: it verifies that the standalone layers and the complete models use the same interfaces and shapes.
The internal model continues to operate entirely on logits. The SR mechanism sits inside that logit-valued computation; we do not convert intermediate values back to bits.
# --- Install into fresh internal models for evaluation ---
# We explicitly copy each trained learned layer into the matching internal-model slot.
# Build source wrappers once so trained layers are materialized.
_ = model_a(tf.zeros((1, n2), tf.float32))
_dummy_gun = tf.zeros((1, n2), tf.float32)
_dummy_comm = tf.zeros((1, COMMS_SIZE), tf.float32)
_dummy_prev_meas_list = [tf.zeros((1, n2), tf.float32)]
_dummy_prev_out_list = [tf.zeros((1, n2), tf.float32)]
_ = model_b([_dummy_gun, _dummy_comm, _dummy_prev_meas_list, _dummy_prev_out_list])
# Create fresh internal wrappers for evaluation contract.
eval_model_a = LinTrainableAssistedModelA(
field_size=FIELD_SIZE,
comms_size=COMMS_SIZE,
sr_mode=SR_MODE_BOOTSTRAP_EVAL,
seed=SEED,
p_rule=P_RULE,
)
eval_model_b = LinTrainableAssistedModelB(
field_size=FIELD_SIZE,
comms_size=COMMS_SIZE,
sr_mode=SR_MODE_BOOTSTRAP_EVAL,
seed=SEED,
p_rule=P_RULE,
)
# Build destination wrappers before assigning layer weights.
_ = eval_model_a(tf.zeros((1, n2), tf.float32))
_ = eval_model_b([_dummy_gun, _dummy_comm, _dummy_prev_meas_list, _dummy_prev_out_list])
# Explicit weight transfer: trained layer -> matching internal-model layer.
eval_model_a.measure_layer.set_weights(meas_layer_a.get_weights())
eval_model_a.combine_layer.set_weights(comb_layer_a.get_weights())
eval_model_b.measure_layer.set_weights(meas_layer_b.get_weights())
eval_model_b.combine_layer.set_weights(comb_layer_b.get_weights())
print("Transferred trained layer weights into eval internal models.")
print("A: meas -> eval_model_a.measure_layer, comb -> eval_model_a.combine_layer")
print("B: meas -> eval_model_b.measure_layer, comb -> eval_model_b.combine_layer")
Transferred trained layer weights into eval internal models. A: meas -> eval_model_a.measure_layer, comb -> eval_model_a.combine_layer B: meas -> eval_model_b.measure_layer, comb -> eval_model_b.combine_layer
5. Check the Internal Representation¶
Before running a tournament, it is useful to inspect one example end-to-end.
The learned layers are free to produce logits of different magnitudes, but logical values are encoded by their sign. At the hardened interfaces we expect values close to ±β.
This small diagnostic is particularly useful after refactoring: if a tensor that should contain logits suddenly contains raw 0/1 bits, the model can look syntactically correct while behaving almost randomly.
def describe_tensor(name, x):
t = tf.cast(tf.convert_to_tensor(x), tf.float32)
arr = t.numpy()
print(
f"{name:24s} shape={arr.shape}, min={arr.min(): .3f}, max={arr.max(): .3f}, mean|x|={np.mean(np.abs(arr)): .3f}"
)
# Single-sample gameplay bits from canonical dataset
field_bits = tf.convert_to_tensor(ds_lin["field_bits"][0:1, 0, :], dtype=tf.float32)
gun_bits = tf.convert_to_tensor(ds_lin["gun_bits"][0:1, 0, :], dtype=tf.float32)
# Build temporary adapters to probe the explicit gameplay boundary.
diag_model_a = GameplayModelAAdapter(internal_model_a=eval_model_a, beta=BETA, harden_between_levels=True)
# Adapter A boundary: bits -> logits (internal) -> bits for gameplay handoff
comm_bits, meas_bits_list, out_bits_list, _comm_logits_dbg = diag_model_a(
field_bits,
explore=False,
return_comm_logits=True,
)
# Internal A tensors from explicit hard-logit input
field_logits_a = hard_logit(field_bits, BETA)
comm_logits_a, meas_logits_a_list, out_logits_a_list = eval_model_a.compute_with_internal(
field_logits_a,
training=False,
)
# Values passed A -> B across gameplay boundary are bits; adapter B remaps to logits.
comm_logits_for_b = hard_logit(comm_bits, BETA)
prev_meas_logits_for_b = hard_logit(meas_bits_list[0], BETA)
prev_out_logits_for_b = hard_logit(out_bits_list[0], BETA)
gun_logits_b = hard_logit(gun_bits, BETA)
shoot_logit_b, meas_logits_b_list, out_logits_b_list, _, _ = eval_model_b.compute_with_internal(
gun_logits_b,
comm_logits_for_b,
[prev_meas_logits_for_b],
[prev_out_logits_for_b],
training=False,
)
print("Internal logit interface diagnostics (single sample):")
describe_tensor("field logits into A", field_logits_a)
describe_tensor("A measurement logits", meas_logits_a_list[0])
describe_tensor("A outcome logits", out_logits_a_list[0])
describe_tensor("A communication logits", comm_logits_a)
describe_tensor("gun logits into B", gun_logits_b)
describe_tensor("A->B prev meas logits", prev_meas_logits_for_b)
describe_tensor("A->B prev out logits", prev_out_logits_for_b)
describe_tensor("A->B comm logits", comm_logits_for_b)
describe_tensor("B measurement logits", meas_logits_b_list[0])
describe_tensor("B outcome logits", out_logits_b_list[0])
describe_tensor("shoot logit", shoot_logit_b)
Internal logit interface diagnostics (single sample): field logits into A shape=(1, 16), min=-10.000, max= 10.000, mean|x|= 10.000 A measurement logits shape=(1, 16), min=-10.688, max= 9.814, mean|x|= 7.637 A outcome logits shape=(1, 16), min=-10.000, max= 10.000, mean|x|= 10.000 A communication logits shape=(1, 1), min= 15.024, max= 15.024, mean|x|= 15.024 gun logits into B shape=(1, 16), min=-10.000, max= 10.000, mean|x|= 10.000 A->B prev meas logits shape=(1, 16), min=-10.000, max= 10.000, mean|x|= 10.000 A->B prev out logits shape=(1, 16), min=-10.000, max= 10.000, mean|x|= 10.000 A->B comm logits shape=(1, 1), min= 10.000, max= 10.000, mean|x|= 10.000 B measurement logits shape=(1, 16), min=-12.103, max= 4.806, mean|x|= 8.673 B outcome logits shape=(1, 16), min=-10.000, max= 10.000, mean|x|= 10.000 shoot logit shape=(1, 1), min=-14.189, max=-14.189, mean|x|= 14.189
6. Run the Model as a Player¶
The final boundary is the gameplay interface.
LinTrainableAssistedModelA/B work with logits. GameplayModelAAdapter/BAdapter translate between that internal representation and the bit-valued interface expected by the player wrappers. TrainableAssistedPlayers can therefore participate in the same tournament machinery as the hand-written players without knowing anything about logits.
In short:
Tournament → Player wrapper (bits) → Gameplay adapter → Lin model (logits)
and the reverse path on the way back out.
With p_rule=1 and no channel noise, the tournament score should be close to 1.0. If the individual layer accuracies are already essentially perfect but the tournament is not, inspect the representation and assembly boundaries before simply adding more training.
# Route gameplay via adapters (bits at game boundary, logits internally).
# Keep SR in gameplay mode for both internals.
eval_model_a.sr_layer.set_sr_mode("stochastic")
eval_model_b.sr_layer.set_sr_mode("stochastic")
gameplay_model_a = GameplayModelAAdapter(internal_model_a=eval_model_a, beta=BETA, harden_between_levels=True)
gameplay_model_b = GameplayModelBAdapter(internal_model_b=eval_model_b, beta=BETA, harden_between_levels=True)
layout_eval = GameLayout(
field_size=FIELD_SIZE,
comms_size=COMMS_SIZE,
enemy_probability=0.5,
channel_noise=0.0,
number_of_games_in_tournament=2_000,
)
env = GameEnv(layout_eval)
players = TrainableAssistedPlayers(layout_eval, model_a=gameplay_model_a, model_b=gameplay_model_b)
# Match historical stochastic gameplay behavior.
players.set_explore(True)
t = Tournament(env, players, layout_eval)
log = t.tournament()
mean_reward, std_err = log.outcome()
print(f"Bootstrap tournament over {layout_eval.number_of_games_in_tournament}: {mean_reward:.4f} ± {std_err:.4f}")
Bootstrap tournament over 2000: 0.9845 ± 0.0028
7. Save the Bootstrap Weights¶
The resulting weights provide a known-good initialization for later end-to-end or reinforcement-learning experiments. We save weights rather than full Keras model serialization so the files remain tied to the current model classes and interfaces.
model_a_path = models_dir / f"lin_model_a_bootstrap_f{FIELD_SIZE}_m{COMMS_SIZE}_r{P_RULE:.2f}.weights.h5"
model_b_path = models_dir / f"lin_model_b_bootstrap_f{FIELD_SIZE}_m{COMMS_SIZE}_r{P_RULE:.2f}.weights.h5"
# Save evaluated internal models (with explicitly transferred trained layer weights).
eval_model_a.save_weights(model_a_path)
eval_model_b.save_weights(model_b_path)
print("Saved weights:")
print(" -", model_a_path)
print(" -", model_b_path)
Saved weights: - notebooks\models\lin_model_a_bootstrap_f4_m1_r1.00.weights.h5 - notebooks\models\lin_model_b_bootstrap_f4_m1_r1.00.weights.h5