Kuzushiji OCR
Does a recurrent layer help read cursive Japanese sequences, and does the help grow with length?
- Context
- Deep Learning coursework, individual
- My part
- Everything: data composition, both models, evaluation
- Stack
- Python, TensorFlow, Keras, CTC, BiLSTM
- Year
- 2026

The problem
Kuzushiji is the cursive script most pre-modern Japanese texts were written in, and few people can still read it. Recognising single characters is a solved classroom exercise. Reading a line is not: the model gets an image of several characters with no boundaries marked and has to output them in order, however many there are.
The question was narrow on purpose. Does adding a recurrent layer, which reads the visual features in sequence, help transcribe these lines? And if it does, does the help grow as the lines get longer, where order should matter more?
Building a fair task
The notebook composes its own lines at runtime from Kuzushiji-49’s 28 × 28 single-character glyphs: 2 to 5 characters placed left to right on a 32 × 128 canvas, at one common scale.
- No leaks between splitsThe official K49 training glyphs are split into separate pools for training and validation composites, and the official test glyphs are used only for test composites. No single glyph image appears in two splits.
- Balanced12,000 training, 2,000 validation and 3,000 test lines, equal across lengths 2 to 5, with characters drawn uniformly across 49 classes.
- Checked before trustedLabel and blank validation, a check that 16 timesteps can represent every target, and overfitting runs on 64 sequences (98.4% for the CNN, 100% for the CRNN) to prove each model can learn the task at all.
Two models, one difference
Both share the same convolutional feature extractor, which turns the image into 16 ordered slices from left to right, and both are trained with CTC, which learns to map those 16 slices to a label sequence of any length, using a blank symbol for “nothing here” and to separate repeated characters.
After the CNN
CNN-CTC Dense layer, 128 unitsCRNN-CTC Bidirectional LSTM, 64 units each way
Parameters
CNN-CTC 164,786CRNN-CTC 263,602
Training
CNN-CTC Seed 42, Adam 1e-3, batch 64, up to 30 epochsCRNN-CTC Identical
Checkpoint
CNN-CTC Lowest validation CTC lossCRNN-CTC Identical
Decoding
CNN-CTC Greedy CTCCRNN-CTC Identical
| CNN-CTC | CRNN-CTC | |
|---|---|---|
| After the CNN | Dense layer, 128 units | Bidirectional LSTM, 64 units each way |
| Parameters | 164,786 | 263,602 |
| Training | Seed 42, Adam 1e-3, batch 64, up to 30 epochs | Identical |
| Checkpoint | Lowest validation CTC loss | Identical |
| Decoding | Greedy CTC | Identical |
The test set stayed untouched until both configurations and their checkpoints were frozen.
By length and by error
2
CNN exact 52.1%CRNN exact 56.0%CNN CER 27.3%CRNN CER 24.8%
3
CNN exact 38.0%CRNN exact 41.5%CNN CER 28.2%CRNN CER 25.4%
4
CNN exact 27.5%CRNN exact 31.7%CNN CER 27.9%CRNN CER 24.3%
5
CNN exact 21.6%CRNN exact 24.5%CNN CER 27.6%CRNN CER 25.0%
| Length | CNN exact | CRNN exact | CNN CER | CRNN CER |
|---|---|---|---|---|
| 2 | 52.1% | 56.0% | 27.3% | 24.8% |
| 3 | 38.0% | 41.5% | 28.2% | 25.4% |
| 4 | 27.5% | 31.7% | 27.9% | 24.3% |
| 5 | 21.6% | 24.5% | 27.6% | 25.0% |
The CRNN wins at every length, by 2.9 to 4.3 points of exact accuracy, but the largest gain is at length 4, not 5. The error types say more than the totals: the CNN dropped whole characters 442 times, the CRNN 52 times. Recurrence mostly fixed missing characters; wrong characters stayed about as common (2,374 substitutions against 2,522).
Held-out predictions, both ways

Decisions
Change exactly one thing
Same features, data, seed, optimiser, checkpoint rule and decoder; the only difference is the BiLSTM. Anything else and the comparison measures two changes at once.
Keep glyphs from crossing splits
Composing lines from a shared pool would let the test set contain glyph images the model trained on in other lines. Splitting the source glyphs first means the test measures reading unseen handwriting, not recognising familiar strokes.
Prove the pipeline can learn before comparing
CTC fails quietly when labels, blanks or timesteps are wrong. Overfitting a tiny set to near-perfect accuracy shows each model and its decoder work, so a gap between them reflects the architecture, not a bug.
Report error types, not just a rate
A 10% relative drop in character error sounds like general improvement. Splitting it into deletions, substitutions and insertions shows where it came from, and keeps the paper from claiming the CRNN recognises characters better when it mostly stopped skipping them.
Results
38.4% vs 34.8%
Exact sequence accuracy, CRNN-CTC vs CNN-CTC, on 3,000 held-out lines
442 → 52
Characters dropped from the transcription, CNN vs CRNN
4.5×
Longer training for the recurrent model, with 60% more parameters
Both correct
Lines 719
CRNN only
Lines 434
CNN only
Lines 325
Both wrong
Lines 1,522
| Held-out outcome | Lines |
|---|---|
| Both correct | 719 |
| CRNN only | 434 |
| CNN only | 325 |
| Both wrong | 1,522 |
Individual research project, written up as a paper. The hypothesis is only partly supported: recurrence helps at every length, but the benefit doesn’t grow with length. Both models lost about 18 points of exact accuracy from validation to test, a gap this experiment doesn’t explain.
Limits
- Synthetic lines of clean glyphs with random characters and no linguistic context: this is not document OCR.
- One configuration and one seed per model, so run-to-run variation is unmeasured.
- The CRNN also has more capacity; recurrence isn’t separated from parameter count.
- Greedy decoding only, with no beam search or language model.
- Only 157 test lines had adjacent repeated characters, too few to say anything about repeats.

