Skip to content

About

Code completion on CodeSearchNet: Seq2Seq LSTM vs CodeT5 vs decoder-only Transformer, served via FastAPI

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Python Code Autocomplete

A neural code-completion model that predicts the remainder of a line of Python source from the preceding context. Built as an LSTM sequence-to-sequence model with teacher forcing, trained on the CodeSearchNet Python corpus, with hyperparameters selected by Optuna.

Stack: Python · PyTorch · Hugging Face Datasets · Optuna


Overview

Rather than predicting a single next token, the model treats completion as a short translation task: an encoder reads a fixed window of preceding tokens, and a decoder generates tokens until the end of the current logical line. This lets a single forward pass produce a whole-line suggestion instead of one token.

The pipeline runs in three stages, one notebook each:

CodeSearchNet Python (455,243 functions)
        │
        ▼
  01  Tokenize with Python's `tokenize` module
      Strip comments and docstrings
      80/10/10 train / test / validation split
      Build vocabulary, encode to integer IDs
        │
        ▼
  02  Chunk into (context → line-completion) pairs
      SEQ_LEN = 32, STRIDE = 16
        │
        ▼
  03  Seq2seq LSTM · Optuna tuning · training · evaluation

Tokenization

Source is tokenized with Python's own tokenize module rather than a generic subword tokenizer, so the units are real Python tokens. Comments and docstrings are dropped — a docstring is identified as a STRING token immediately following INDENT, NEWLINE, or DEDENT — leaving the model to learn code structure instead of prose.

Vocabulary

Built from the training split only, to avoid leaking validation and test tokens into the vocabulary. It keeps the 50,000 most frequent tokens that appear at least twice, plus four special tokens:

Token ID Purpose
<PAD> 0 Padding, ignored by both loss and accuracy
<UNK> 1 Out-of-vocabulary token
<BOS> 2 Start of sequence
<EOS> 3 End of sequence

Building training pairs

A sliding window of 32 tokens with stride 16 produces each example. The target is not a fixed span: it runs from the split point to the nearest newline (capped at 50 tokens), so each example teaches the model to finish exactly one logical line.

              split point
                   │
  ... tokens ──────┤────────────── ...
  └── 32-token ────┘└─ target: to end of line ─┘
       context

Encoder input is wrapped in <BOS> … <EOS>; the decoder is fed <BOS> + target and supervised against target + <EOS>. Batches are padded with pad_sequence via a custom collate_fn, since targets vary in length.


Model

An encoder–decoder LSTM. The encoder compresses the context window into its final hidden and cell states; the decoder is initialised from those states and generates the completion one token at a time.

  context tokens                          generated tokens
        │                                        ▲
        ▼                                        │
   Embedding                                 Linear → vocab
        │                                        ▲
        ▼                                        │
   LSTM (n_layers, dropout)  ──(hidden, cell)──▶ LSTM (n_layers, dropout)
                                                 ▲
                                                 │
                                            Embedding
                                                 ▲
                                                 │
                              <BOS> / previous token / ground truth
                                        (teacher forcing)

During training, each decoder step is fed the ground-truth previous token with probability teacher_forcing_ratio, and its own previous prediction otherwise. Evaluation always runs with teacher_forcing_ratio=0.0, so reported numbers reflect free-running generation rather than teacher-forced decoding.

Loss: CrossEntropyLoss(ignore_index=vocab['<PAD>']) Metric: token-level accuracy, masked so padding is excluded from the denominator


Hyperparameter Tuning

Tuned with Optuna — TPE sampler (seed=42) with a Hyperband pruner, minimising validation loss over 10 trials. Trials run on a 5,000-example training subset and 1,000-example validation subset to keep the search affordable.

Hyperparameter Search space
hidden_dim 128, 256, 384, 512
n_layers 1 – 3
dropout 0.1 – 0.5
learning_rate 1e-4 – 3e-3 (log)
weight_decay 1e-6 – 1e-3 (log)
teacher_forcing_ratio 0.3 – 0.7

The best configuration is then retrained on the full training set with gradient clipping (clip=1.0), a ReduceLROnPlateau schedule, and best-checkpoint saving on validation loss.


Results

Metrics are not yet recorded — the notebooks were committed with their outputs cleared, so no training curves, tuned hyperparameters, or test scores are preserved in the repository.

To produce them, run notebooks/03_lstm_model_.ipynb end to end. It reports:

  • best validation loss and best hyperparameters (Optuna study)
  • per-trial results as a dataframe
  • training/validation loss and accuracy curves
  • final test loss and token accuracy

Re-committing that notebook with outputs intact would make the results reproducible for anyone reading the repository.


Repository Layout

notebooks/
  01_data_encoding.ipynb                 tokenization, split, vocabulary, encoding
  02_chunking_dataset_preparation.ipynb  windowing into context/target pairs
  03_lstm_model_.ipynb                   seq2seq model, Optuna tuning, training, eval
src/
  data/preprocess.py                     tokenize, split, vocab, dataset, dataloaders
  models/lstm_model.py                   encoder, decoder, seq2seq, greedy completion
  train.py                               training loop, Optuna tuning, evaluation

The notebooks are the exploratory record; src/ is the same pipeline as runnable modules. Import-time side effects were removed, so importing preprocess no longer downloads the dataset — call build_and_cache() once, then get_dataloaders().


Getting Started

pip install torch datasets scikit-learn optuna matplotlib tqdm

Run the pipeline from src/:

python -m src.data.preprocess          # tokenize + encode -> artifacts/
python -m src.train tune --trials 10   # Optuna search -> artifacts/best_params.json
python -m src.train train --epochs 20  # train, checkpoint, report test metrics

preprocess accepts --limit N to tokenize only the first N functions, which is useful for a quick end-to-end check before committing to the full corpus.

Alternatively, run the notebooks in order — 01 → 02 → 03. Stage 01 downloads the dataset from the Hugging Face Hub and writes vocab.pkl plus the encoded splits; later stages load those artifacts.

A GPU is recommended. The model moves to CUDA when available and falls back to CPU, but the vocabulary is large enough (up to 50,004 entries) that the decoder's output projection dominates runtime on CPU.

Setting HF_TOKEN before stage 01 avoids the Hugging Face rate limit on unauthenticated downloads.


Dataset

CodeSearchNet Python — 455,243 Python functions, loaded via datasets.load_dataset. Raw data and generated artifacts (.pkl files, checkpoints) are not committed.


Roadmap

  • Tokenization and encoding pipeline
  • Chunked dataset and dataloaders
  • Seq2seq LSTM model
  • Optuna hyperparameter tuning
  • Runnable src/ modules matching the notebook pipeline
  • Greedy completion (Seq2Seq.complete)
  • Record and commit final metrics
  • Top-k / beam search decoding
  • Decode generated token ids back to formatted source

About

Code completion on CodeSearchNet: Seq2Seq LSTM vs CodeT5 vs decoder-only Transformer, served via FastAPI

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages