Skip to content

Add Encoder-Decoder model support - #30

Draft
TahaZahid05 wants to merge 2 commits into
mainfrom
encoder-decoder-model
Draft

Add Encoder-Decoder model support#30
TahaZahid05 wants to merge 2 commits into
mainfrom
encoder-decoder-model

Conversation

@TahaZahid05

Copy link
Copy Markdown
Collaborator

PR Description:

This Pull Request introduces the Encoder-Decoder (Seq2Seq) transformer architecture option to the Trainite CLI toolbox. It fully decouples encoder and decoder hyperparameters, implements robust sequence-to-sequence template routing, and establishes a highly thorough, symmetrical testing suite across all datasets, models, and trainers.


1. Symmetrical & Decoupled Model Architecture

  • Implemented the encoder-decoder transformer in trainite/models/encoder_decoder.py with:
    • Unified MultiHeadAttention supporting causal self-attention, non-causal self-attention, and cross-attention.
    • Separate EncoderBlock and DecoderBlock layer stacks.
    • Sine/Cosine positional encodings tailored individually to encoder and decoder context lengths.
  • Decoupled hyperparameters cleanly in EncoderDecoderModelConfig (num_encoder_layers vs num_decoder_layers, encoder_max_seq_len vs decoder_max_seq_len).

2. Dynamic Template Routing (CLI)

  • Renamed CLI choice transformer to decoder for symmetric clarity.
  • Integrated the --model encoder-decoder option in the CLI (trainite/cli/init.py).
  • Implemented a dynamic routing system that delivers highly tailored, focused files for either model choice without embedding nested if/else checks or branching logic into user-generated boilerplates.

3. Specialized Seq2Seq Dataset & Trainer Templates

  • Dataset (trainite/datasets/string_reverse_seq2seq.py): Yields separate source and target tensors with bespoke padding collation (handling target pads with ignore_index=-100 and inputs with 0).
  • Trainer (trainite/trainers/pretrainer_seq2seq.py): Implements clean pretraining and evaluation loops specifically for sequence-to-sequence forward passes.

4. Fully Aligned and Comprehensive Testing

  • Expanded the unit test suite to be 100% symmetrical between decoder and encoder-decoder paths.
  • Model Tests (tests/models/encoder_decoder_test.py): Added positional encoding dimension checks, spec-based model instantiation, and deterministic/random dropout evaluations.
  • Dataset Tests (tests/datasets/string_reverse_seq2seq_test.py): Added checks for special tokens, character preset variations, sequence lengths, and exception handling.
  • Trainer Tests (tests/trainers/pretrainer_seq2seq_test.py): Integrated trainer lifecycle tests verifying loss flattening, exact accuracy conversions, device selections, and last-checkpoint fallbacks.

Comment thread tests/cli_test.py
Comment on lines +162 to +163
with open(config_path, "r") as f:
config = yaml.safe_load(f)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have helper method for yaml dumping and loading in config.py

Comment thread tests/cli_test.py
Comment on lines +173 to +175

with open(config_path, "w") as f:
yaml.safe_dump(config, f)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Comment thread trainite/cli/init.py
Comment on lines +163 to +185

# Dynamic routing for encoder-decoder setup
dataset_implementation_path = dataset_spec.implementation_path
trainer_implementation_path = trainer_spec.implementation_path
dataset_readme_path = dataset_spec.readme_template_path
trainer_readme_path = trainer_spec.readme_template_path

if model_name == "encoder-decoder":
if dataset_name == "string-reverse":
dataset_implementation_path = Path(
"trainite/datasets/string_reverse_seq2seq.py"
)
dataset_readme_path = Path(
"trainite/templates/components/datasets/string_reverse_seq2seq.md"
)
if trainer_name == "pretrainer":
trainer_implementation_path = Path(
"trainite/trainers/pretrainer_seq2seq.py"
)
trainer_readme_path = Path(
"trainite/templates/components/trainers/pretrainer_seq2seq.md"
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont understand use of it, I thought dataset, trainer and model would be independent you might have to consult with victor, on how modular should the generated code be, can user select different components or based on the use case.

Comment thread trainite/models/encoder_decoder.py
Comment on lines +40 to +41
self.k_proj = nn.Linear(embed_dim, embed_dim, bias=False)
self.v_proj = nn.Linear(embed_dim, embed_dim, bias=False)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can have a single multi dim linear layer for key and value as it would be both either x in case of self and y in case of cross.

Comment on lines +202 to +207
self.encoder_pos_encoding = PositionalEncoding(
hidden_size, encoder_max_seq_len, dropout
)
self.decoder_pos_encoding = PositionalEncoding(
hidden_size, decoder_max_seq_len, dropout
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could use single layer for this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to keep them decoupled incase someone wants to try and modify either one of encoder or decoder in the setup.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the model should be simple enough, if your wants separate encoder , decoder position encoder they can add it themselves

Comment on lines +264 to +269
if (encoder_input_ids == self.embedding.padding_idx).any():
cross_padding_mask = (
encoder_input_ids != self.embedding.padding_idx
).reshape(B_dec, 1, 1, S_enc)
else:
cross_padding_mask = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its same as encoder self padding?

@aaishwarymishra

Copy link
Copy Markdown
Member

Also like I dont think we need new datasets for this model old reverse-string should work and the old trainer should work too.

@TahaZahid05

Copy link
Copy Markdown
Collaborator Author

Also like I dont think we need new datasets for this model old reverse-string should work and the old trainer should work too.

I would discuss this with victor as well, but having the same dataset and pretrainer for both cases can lead to if/else statements inside the file which can be more confusing for the end-user. That is why I opted for creating a seperate file to keep it clean and only the code required for the model they chose.

@TahaZahid05
TahaZahid05 force-pushed the develop branch 2 times, most recently from 29b6131 to 25ef6d9 Compare June 16, 2026 13:59
@TahaZahid05
TahaZahid05 marked this pull request as draft June 19, 2026 20:19
Base automatically changed from develop to main June 23, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants