EduCast: Hierarchical Forecasting for University Enrollment

View on GitHub

This project started as my Bachelor's thesis back in 2023, where I used decision trees and random forests to predict course enrollment at my university (EPSEM-UPC). For my Master's thesis I took the idea much further: I wanted to see if modelling individual student trajectories with sequential models could actually beat the simple baselines that administrators already use, and whether hierarchical reconciliation could make forecasts consistent across different levels of academic planning.

The work also led to an academic paper currently under review:

Paper under review

Anass Anhari

Anass Anhari Talib

UPCUniversitat Politecnica de Catalunya, Spain
Cristian Rodriguez Rivero

Cristian Rodriguez Rivero

FontysFontys University of Applied Sciences, The Netherlands
Christoph Bergmeir

Christoph Bergmeir

UGRUniversity of Granada, Spain

Larisa Survilo

RTURiga Technical University, Latvia

Why enrollment forecasting matters

Universities need to decide how many instructors, classrooms, lab slots and teaching assistants to assign for each course before students have actually registered. Get it wrong and you end up with overcrowded classrooms or wasted budget on sessions nobody attends.

The usual approach is that programme coordinators look at last year's numbers and adjust based on experience. This works fine when things are stable, but breaks down when curricula change, cohort sizes shift, or courses get renamed or reorganized.

Most existing methods treat this as an aggregate time-series problem: take the enrollment count per course over the years and extrapolate. The problem is that course demand is really the sum of individual student decisions, and aggregate models ignore how each student moves through the curriculum.

The dataset

The data comes from EPSEM-UPC (Escola Politecnica Superior d'Enginyeria de Manresa), covering one engineering programme over 11 academic years.

PropertyValue
Students505
Courses51 (49 with enrollments)
Enrollment records11,928
Period2010 to 2021
Terms per year2 (autumn, spring)
Total terms23
Missing grades155 (1.3%)
Pass rate~70%

The heatmap below shows how enrollment is distributed across courses and terms. First-year compulsory subjects (top rows) consistently attract 40 to 60+ students, while advanced electives at the bottom have much smaller and more variable enrollment.

Enrollment heatmap showing per-course enrollment across all 23 academic terms from 2010 to 2021 Temporal enrollment trends showing total enrollment per term over time

Feature engineering: the 154-dimensional student vector

The core idea is to represent each student's academic state at any given term as a single vector that captures everything relevant about their history. For each student ss and term tt, we build:

xt=[Et    Gt    At    GPAt]\mathbf{x}_t = \bigl[\, E_t \;\big|\; G_t \;\big|\; A_t \;\big|\; \text{GPA}_t \,\bigr]

where:

  • Et{0,1}51E_t \in \{0,1\}^{51} is the enrollment sub-vector: which courses the student is taking this term
  • Gt[1,1]51G_t \in [-1,1]^{51} is the grade sub-vector: normalized grades for taken courses, 1-1 as a sentinel for courses not taken (since 0 is a valid grade)
  • At{0,,5}51A_t \in \{0,\ldots,5\}^{51} is the attempts sub-vector: cumulative number of prior attempts per course, capped at 5
  • GPAt[0,1]\text{GPA}_t \in [0,1] is the cumulative grade point average up to term tt

This gives us 51+51+51+1=15451 + 51 + 51 + 1 = 154 dimensions. The full student history becomes a sequence (x1,x2,,xn1)(\mathbf{x}_1, \mathbf{x}_2, \ldots, \mathbf{x}_{n-1}) that we feed into sequential models using sliding windows of 3 consecutive terms, producing input tensors of shape (3,154)(3, 154).

Micro vs Macro: two levels of modelling

We compared two fundamentally different forecasting strategies:

Micro models work at the student level. They predict the probability that each individual student will enroll in each course next term, then sum those probabilities across all continuing students to get a course-level count:

y^c,t+1=sStp^s,c,t+1\hat{y}_{c,t+1} = \sum_{s \in S_t} \hat{p}_{s,c,t+1}

where StS_t is the set of continuing students and p^s,c,t+1\hat{p}_{s,c,t+1} is the predicted enrollment probability for student ss in course cc.

Macro models work directly with aggregate enrollment counts per course, treating the problem as a traditional time-series forecasting task.

The key advantage of the Micro approach in our setting is the number of training samples. The Macro model only has 15 training windows (aggregate observations), while the Micro approach generates 1,322 student-level training samples from the same data. When your time series is short, having 88x more training examples makes a real difference.

Models compared

ModelLevelInputLoss
Naive lag-2CoursePrior-year count-
Micro LSTM/GRUStudent3×1543 \times 154 sequenceBCE
LightGBMStudentFlattened 3×1543 \times 154log loss
Course2Vec MLPStudentFlat + embeddingsBCE
Macro LSTMAggregate3×2043 \times 204 sequenceMSE

The Naive lag-2 baseline is deliberately simple: it copies each course's enrollment from the same semester of the previous year. This is essentially what most administrators already do.

The Micro LSTM projects each 154-dimensional student-term vector to 64 dimensions, encodes a 3-term sequence with one LSTM layer of 128 hidden units, and maps the final state through dense layers to 51 sigmoid probabilities.

LightGBM flattens the 3-term sliding window into 462 tabular features. Gradient-boosted trees often work surprisingly well on tabular data even without explicit temporal modelling.

Course2Vec MLP adds learned course embeddings (trained on pre-test enrollment sequences using the Skip-gram approach from Word2Vec) to the flattened features. These embeddings capture co-enrollment patterns and curricular proximity.

t-SNE visualization of Course2Vec embeddings showing how courses cluster by semester and curriculum position

The t-SNE plot above shows how the learned course embeddings organize themselves. First-semester courses (red) cluster together at the top, later semesters fan out, and electives scatter around the edges. The model learned curriculum structure without being told about it.

Training

All models were trained on 2010 to 2017 and tested on 2018 to 2021 using a strict chronological split to avoid temporal leakage. Hyperparameters were kept intentionally low-capacity to avoid overfitting on this small dataset: one recurrent layer, 128 hidden units, dropout 0.2, learning rate 0.001, batch size 64 for the Micro LSTM.

Micro LSTM training curves showing BCE loss converging over 100 epochs with good train/validation agreement

The training curves show healthy convergence with minimal overfitting: the validation loss closely tracks the training loss throughout.

Results

Forecasting accuracy (MAE)

The main metric is Mean Absolute Error in students per course, measured on the chronological test set (2018 to 2021):

MAE=1CTtesttTtestcCyc,ty^c,t\text{MAE} = \frac{1}{|C||T_{test}|} \sum_{t \in T_{test}} \sum_{c \in C} |y_{c,t} - \hat{y}_{c,t}|
ModelMAE
Per-course reconciliation (oracle)1.43
LightGBM, student-level1.68
Naive lag-2 baseline1.69
Micro LSTM1.73
Course2Vec MLP1.78
Macro LSTM5.57

The three best practical methods (LightGBM, Naive, Micro LSTM) are all very close, forecasting within roughly two students per course on average. The Macro LSTM performs much worse because it only has 15 training windows to learn from.

An interesting takeaway: the seasonal Naive baseline is genuinely hard to beat on this dataset. Year-over-year enrollment at this school is fairly stable, and the signal that individual trajectory models can capture is modest when the baseline is already that strong.

Actual vs Predicted heatmap

Heatmap comparing actual enrollment counts with Micro LSTM predictions for each course and test term

The Micro LSTM predictions (right columns of each pair) closely track actual values for most courses. The largest errors tend to occur on courses with sudden enrollment spikes or drops that the model hasn't seen before.

Ablation study

Ablation study showing MAE across different hidden sizes, layer counts, dropout rates, window sizes, and architectures

The ablation study reveals several things about what works in this small-data regime:

  • Architecture matters more than tuning. The GRU achieves the lowest MAE at 1.64, outperforming the LSTM (1.73) and especially the BiLSTM (2.01). Simpler gated architectures work better when data is limited.
  • Adding capacity hurts. Going from 1 to 3 layers increases MAE from 1.73 to 2.06. Hidden sizes of 64 to 256 are all similar, but 32 is too small (1.96) and 512 starts overfitting (1.82).
  • Window size is not critical. Windows of 2 to 5 terms all give similar results around 1.73, but a window of 1 term is clearly worse (1.96).

Classification perspective

When evaluated as a per-student binary classification problem (will this student enroll in this course?), the tree-based models achieve reasonable F1 scores:

Average F1 scores for tree-based classifiers on the per-student enrollment classification task

Decision trees at depth 4 achieve the best average F1 (0.653), outperforming XGBoost (0.597), LightGBM (0.561) and Random Forest (0.549). The F1 scores are moderate because sparse electives have very few positive examples, making them inherently hard to classify.

Hierarchical reconciliation

What is hierarchical reconciliation?

In academic planning, forecasts need to be consistent across different levels. Individual student enrollments sum up to per-course counts, which sum up to programme totals, which sum up to the institution total. If you forecast at each level independently, the numbers usually don't add up.

Hierarchical reconciliation adjusts independently generated forecasts so that they are coherent across the hierarchy. The idea is to combine base forecasts from multiple levels (student-level Micro and aggregate Macro) and find a reconciled forecast that is consistent.

We tested five reconciliation strategies:

  • Global weighted blend: weighted combination of Micro and Macro forecasts
  • Simple average: equal-weight average
  • MinT reconciliation: minimum-trace reconciliation that minimizes forecast variance
  • Micro + Naive blend: combining student-level model with the seasonal baseline
  • Per-course weights: oracle that selects the best model per course on the test set (upper bound, not deployable)
Bar chart comparing MAE across different reconciliation strategies, showing per-course oracle weights at 1.43 and Micro standalone at 1.67

The reconciliation paradox

The results show something important: reconciliation does not automatically help. When the aggregate base forecasts are poor (Macro LSTM at 5.57 MAE), mixing them with good student-level forecasts only makes things worse. MinT reconciliation degrades to 2.80, and simple averaging reaches 3.93.

The only combination that slightly improves over Micro standalone (1.67) is Micro + Naive at 1.65. Per-course weights achieve 1.43 but this is an oracle result, selected on the test set, and not a deployable strategy.

The takeaway is that reconciliation requires base forecasts at multiple levels that are individually informative. When one level is much weaker than the other, it contaminates rather than improves the final result.

The EduCast tool

As part of this project I built EduCast, an interactive web application that wraps the entire pipeline: data loading, model training, evaluation, and forecasting. It's designed so that programme coordinators can use it without needing to write code.

EduCast application main setup page showing data loading interface with 505 students, 51 courses, and 23 semesters

The app supports a standardized data format (*.educast.json) that I designed to handle enrollment data from different universities with different term structures, grading scales, and optional clickstream data.

EduCast models page showing all available model configurations for time series and classification experiments

The models page lets you configure and train all the models from a single interface. Each model card shows its type, transform, and hyperparameters, with a button to start training.

EduCast model metrics and evaluation page

Multi-university data collection (what didn't work)

The original plan was to collect enrollment data from multiple European universities and build a cross-institutional forecasting system. We obtained data from Riga Technical University (Latvia) and Vilnius Gediminas Technical University (Lithuania), but three problems prevented us from using it:

  1. Platform migrations. Both institutions had recently migrated their systems. The exported data was a patchwork of old and new formats with renamed, restructured, or missing fields.
  2. Course-code inconsistency. The same course appeared under multiple codes across years, and different courses sometimes shared codes after migrations. We spent considerable time building course-code mappings through co-enrollment graph analysis but couldn't validate them reliably enough.
  3. Unstructured exports. The default Moodle log exports contained large volumes of unrelated records, no schema documentation, and duplicated entries.

This experience taught me that cross-institutional forecasting is a data-harmonization problem before it's a modelling problem. Any future multi-university study needs to invest heavily in standardized data formats and stable course identifiers before modelling can begin.

Key takeaways

  • Student-level modelling works, but seasonal baselines are strong. The Micro LSTM (1.73 MAE) and LightGBM (1.68 MAE) are competitive with a simple Naive baseline (1.69 MAE). All three forecast within about two students per course.
  • More data points matter more than model complexity. The Micro approach generates 88x more training samples than the Macro approach from the same dataset. This sample advantage is what makes student-level modelling viable in small institutional settings.
  • GRU > LSTM > BiLSTM in this regime. Simpler gated architectures generalize better when the dataset is small. Adding layers or capacity leads to overfitting.
  • Reconciliation only helps when base forecasts are individually good. Mixing a strong Micro forecast with a weak Macro forecast makes things worse.
  • Real-world educational data is messy. Course-code instability, platform migrations, and inconsistent identifiers are the biggest practical obstacles.