- Overview
- Architecture
- Model Components
- Technical Specifications
- Data Format
- Installation
- Dependencies
- Quick Start
- Usage
The LBKT (Learning Behavior-aware Knowledge Tracing) model is designed to track learners' knowledge states by capturing the complex effects of multiple learning behaviors on the learning and forgetting process. The model focuses on three key learning behaviors:
- Speed: How quickly learners respond to questions (quantified via time factors)
- Attempts: Number of attempts made before success (quantified via attempts factors)
- Hints: Usage of hints during learning (quantified via hints factors)
The model investigates how these behaviors individually and collectively affect knowledge acquisition and forgetting, providing a more nuanced understanding of learner proficiency through a sophisticated recurrent neural network architecture.
Folder PATH listing
+---assets <-- Contains static assets and images
│ banner.png <-- Project banner image
│
+---data <-- Contains datasets and data files
│ attempts_factor.json <-- Attempts behavior factor data
│ hints_factor.json <-- Hints behavior factor data
│ kc2index.json <-- Knowledge component to index mapping
│ new_records.json <-- New records data file
│ new_test_data.json <-- Test dataset
│ new_train_data.json <-- Training dataset
│ new_valid_data.json <-- Validation dataset
│ q_matrix.json <-- Q-matrix for topic-KC relationships
│ README.md <-- Documentation for data directory
│ time_factor.json <-- Time behavior factor data
│ topic_attempts_para.json <-- Topic attempts parameters
│ topic_count.json <-- Topic count statistics
│ topic_hints_para.json <-- Topic hints parameters
│ topic_time_para.json <-- Topic time parameters
│ topic2index.json <-- Topic to index mapping
│
+---data_processing <-- Contains data preprocessing scripts
│ data_preprocesing.ipynb <-- Data preprocessing notebook
│ README.md <-- Documentation for data processing
│
+---src <-- Contains source code files
│ cell.py <-- LBKTcell implementation
│ data_helper.py <-- Data loading and processing utilities
│ layer.py <-- Layer1 gated mechanism implementation
│ model.py <-- Recurrent model implementation
│ README.md <-- Documentation for source code
│ train.py <-- Training script
│
.gitignore <-- Git exclusions
LICENSE <-- License information
README.md <-- Project overview and documentation
requirements.txt <-- Python dependencies
The LBKT model implements a recurrent neural network architecture that processes sequences of learning interactions. The model consists of several key components working together to track knowledge states while accounting for learning behaviors.
flowchart TD
A[Input Data] --> B[Data Processing]
B --> C["Topics Array<br/>Shape: batch_size × seq_len"]
B --> D["Resps Array<br/>Shape: batch_size × seq_len"]
B --> E["Time Factors<br/>Shape: batch_size × seq_len"]
B --> F["Attempts Factors<br/>Shape: batch_size × seq_len"]
B --> G["Hints Factors<br/>Shape: batch_size × seq_len"]
B --> H["Masks Array<br/>Shape: batch_size × seq_len"]
C --> I[Recurrent Model]
D --> I
E --> I
F --> I
G --> I
H --> I
I --> J["Topic Embedding<br/>dim_tp=128"]
I --> K["Response Embedding<br/>dim_hidden=50"]
J --> L["Interaction Embedding<br/>num_units=128"]
K --> L
L --> M[RNN with LBKTcell]
E --> M
F --> M
G --> M
I --> N["Q-Matrix Lookup<br/>Shape: batch_size × seq_len × memory_size"]
N --> M
M --> O["Knowledge State<br/>Shape: batch_size × seq_len × memory_size × num_units"]
M --> P["Predictions<br/>Shape: batch_size × (seq_len-1) × 1<br/>(sliced from step 1 onwards)"]
M --> Q["Improvements<br/>Shape: batch_size × seq_len × 1<br/>(includes initial state)"]
style A fill:#e1f5ff
style I fill:#fff4e1
style M fill:#ffe1f5
style P fill:#e1ffe1
style Q fill:#e1ffe1
The LBKT architecture integrates three core modules that work together to model the complex dynamics of knowledge acquisition and retention:
This module isolates and quantifies the individual impact of each learning behavior (response speed, number of attempts, and hint usage) on knowledge acquisition. By processing each behavior type separately through specialized gated mechanisms, the model captures how different learning strategies affect knowledge gain independently before considering their interactions.
This module models the synergistic and interactive effects of multiple behaviors working together. Rather than treating behaviors as independent, this module captures how combinations of behaviors (e.g., fast response with multiple attempts) create compound effects on learning that differ from the sum of individual effects. The fusion mechanism learns to weight and combine these interactions adaptively.
This module integrates both learning (from behavior-affected knowledge acquisition) and forgetting (from time and interaction patterns) to update the learner's knowledge state. The forget gate dynamically balances retention of previous knowledge with incorporation of new learning gains, enabling the model to track knowledge evolution over extended learning sequences.
Module Integration: These three modules operate sequentially within each time step: (1) individual behavior effects are quantified, (2) they are fused to compute a unified learning gain, and (3) the knowledge state is updated by combining this gain with a forget gate that modulates retention of prior knowledge. This design allows the model to capture both immediate learning effects and long-term knowledge dynamics. For detailed implementation, see Section 3: Model Components.
This section provides detailed implementation specifications for each component of the LBKT model. For a high-level overview of how these components work together, see Section 2.2: Core Architectural Modules.
The Recurrent class is the main model wrapper that processes sequences of learning interactions. It orchestrates the embedding layers, input processing, and recurrent computation that form the foundation of the LBKT architecture.
Key Components:
- Topic Embedding Layer: Embeds topic indices into
dim_tp=128dimensional vectors usingGlorotNormalinitialization - Response Embedding Layer: Embeds response values (0/1) into
dim_hidden=50dimensional vectors - Input Processing Layer: Dense layer with ReLU activation transforming concatenated embeddings to
num_units=128 - RNN Layer: Wraps
LBKTcellwithreturn_sequences=Truefor sequence processing - Initial Knowledge State: Learnable parameter
init_hwith shape(memory_size, num_units)
Forward Pass:
- Embed topics and responses:
topic_emb(batch_size, seq_len, dim_tp),resps_emb(batch_size, seq_len, dim_hidden) - Lookup Q-matrix correlation weights:
correlation_weight(batch_size, seq_len, memory_size) - Compute interaction embeddings:
acts_emb = Dense(concat([topic_emb, resps_emb]))→ (batch_size, seq_len, num_units) - Expand behavior factors to (batch_size, seq_len, 1)
- Concatenate inputs:
[acts_emb, correlation_weight, topic_emb, time_factor, attempt_factor, hint_factor] - Process through RNN with initial state
h_init(batch_size, memory_size, num_units) - Split RNN output into predictions and improvements
- Return predictions starting from step 1 (skip initial state):
preds[:, 1:]andimprove(full sequence)
The LBKTcell is the core recurrent cell that implements the three architectural modules described in Section 2.2. It processes learning behaviors and updates knowledge states through the differentiated behavior quantification, fused behavior measurement, and knowledge state update mechanisms.
Key Components:
- Three Behavior Gain Layers:
time_gain,attempt_gain,hint_gain(eachLayer1withnum_units=128) - Fusion Weights: Three weight tensors with shape
(r=4, num_units+1=129, num_units=128)for each behavior - Fusion Bias: Shape
(1, num_units=128) - Fusion Matrix:
Wfwith shape(1, r=4) - Forget Gate: Dense layer with sigmoid activation
- Output Layer: Dense layer with sigmoid activation for predictions
- Dropout Layer: Applied to learning gain with rate
dropout=0.2
Internal Architecture:
flowchart TD
A["Input Concatenation"] --> B["Split Inputs"]
B --> C["interact_emb<br/>Shape: batch × num_units"]
B --> D["correlation_weight<br/>Shape: batch × memory_size"]
B --> E["topic_emb<br/>Shape: batch × dim_tp"]
B --> F["time_factor<br/>Shape: batch × 1"]
B --> G["attempt_factor<br/>Shape: batch × 1"]
B --> H["hint_factor<br/>Shape: batch × 1"]
I["h_pre: Previous State<br/>Shape: batch × memory_size × num_units"] --> J["Weighted Knowledge State<br/>h_pre_tilde<br/>Shape: batch × num_units"]
J --> K["Prediction Before Learning<br/>preds<br/>Shape: batch × 1"]
C --> L["Time Gain Layer<br/>Layer1"]
F --> L
J --> L
C --> M["Attempt Gain Layer<br/>Layer1"]
G --> M
J --> M
C --> N["Hint Gain Layer<br/>Layer1"]
H --> N
J --> N
L --> O["Fusion Time<br/>Shape: batch × r × num_units"]
M --> P["Fusion Attempt<br/>Shape: batch × r × num_units"]
N --> Q["Fusion Hint<br/>Shape: batch × r × num_units"]
O --> R["Element-wise Multiply<br/>fusion_all<br/>Shape: batch × r × num_units"]
P --> R
Q --> R
R --> S["Fusion Reduction<br/>Shape: batch × num_units"]
S --> T["Learning Gain<br/>ReLU activation<br/>Shape: batch × num_units"]
T --> U["Distribute to KCs<br/>LG<br/>Shape: batch × memory_size × num_units"]
I --> V["Forget Gate<br/>sigmoid activation<br/>Shape: batch × memory_size × num_units"]
C --> V
F --> V
G --> V
H --> V
U --> W["Apply Dropout<br/>rate = 0.2"]
V --> X["Update Knowledge State<br/>h = h_pre × forget_gate + LG<br/>Shape: batch × memory_size × num_units"]
W --> X
X --> Y["Weighted New State<br/>h_tilde<br/>Shape: batch × num_units"]
Y --> Z["Prediction After Learning<br/>after_preds<br/>Shape: batch × 1"]
K --> AA["Improvement<br/>improve = (after_preds - preds) / (1 - preds)<br/>Shape: batch × 1"]
Z --> AA
K --> BB["Output<br/>preds, improve<br/>Shape: batch × 2"]
AA --> BB
style A fill:#e1f5ff
style I fill:#fff4e1
style T fill:#ffe1f5
style X fill:#e1ffe1
style BB fill:#e1ffe1
Mathematical Formulations:
-
Weighted Knowledge State:
$$\tilde{h}{pre} = \sum{i=1}^{memory_size} correlation_weight_i \cdot h_{pre,i}$$
where
$correlation_weight \in \mathbb{R}^{memory_size}$ is the Q-matrix correlation weight for the current topic (extracted from the Q-matrix based on the topic),$h_{pre} \in \mathbb{R}^{memory_size \times num_units}$ is the previous knowledge state, and the output shape is$(batch_size, num_units)$ . This operation computes a weighted sum of knowledge components, where weights indicate the relevance of each knowledge component to the current topic. -
Prediction Before Learning:
$$b_{out} = [\tilde{h}{pre}, e{topic}]$$
$$preds = \frac{1}{num_units} \sum_{i=1}^{num_units} \sigma(W_{out} \cdot b_{out} + bias_{out})$$ where
$\sigma$ is the sigmoid activation, and the output shape is$(batch_size, 1)$ . -
Behavior Gain (from Layer1):
$$gate = k + \frac{1-k}{1 + e^{-d \cdot (factor - b)}}$$ where
$k=0.3$ ,$d=10$ ,$b=0.3$ are hyperparameters.$$w = \sigma\left(\left(W \cdot [h, e_{interact}] + b\right) \odot gate\right)$$ where
$\odot$ denotes element-wise multiplication, and the output shape is$(batch_size, num_units)$ . -
Fusion Mechanism:
$$gain_{time}^* = [gain_{time}, \mathbf{1}] \in \mathbb{R}^{num_units+1}$$ $$fusion_{time} = gain_{time}^* \cdot W_{time} \in \mathbb{R}^{r \times num_units}$$ $$fusion_{all} = fusion_{time} \odot fusion_{attempt} \odot fusion_{hint}$$ $$learning_gain = ReLU\left(W_f \cdot transpose(fusion_{all}) + b_{fusion}\right)$$ where
$transpose(fusion_{all})$ transposes dimensions from$(batch_size, r, num_units)$ to$(r, batch_size, num_units)$ for matrix multiplication with$W_f \in \mathbb{R}^{1 \times r}$ ,$r=4$ is the fusion rank, and the output shape is$(batch_size, num_units)$ . -
Knowledge State Update:
$$LG_{i,j} = correlation_weight_i \cdot learning_gain_j$$ where
$i$ indexes knowledge components and$j$ indexes hidden units, resulting in shape$(batch_size, memory_size, num_units)$ .$$forget_gate = \sigma\left(W_{forget} \cdot [h_{pre}, e_{interact}, \text{tile}(time_factor), \text{tile}(attempt_factor), \text{tile}(hint_factor)] + b_{forget}\right)$$ where the input to the forget gate Dense layer is the concatenation of:
-
$h_{pre} \in \mathbb{R}^{memory_size \times num_units}$ (previous knowledge state) -
$e_{interact}$ tiled to$\mathbb{R}^{memory_size \times num_units}$ (interaction embedding) - Behavior factors tiled to
$\mathbb{R}^{memory_size \times dim_hidden}$ (where$dim_hidden=50$ )
The total input dimension is
$(2 \cdot num_units + 3 \cdot dim_hidden)$ , and the output shape is$(batch_size, memory_size, num_units)$ .$$h = h_{pre} \odot forget_gate + Dropout(LG)$$ where the output shape is
$(batch_size, memory_size, num_units)$ . -
-
Improvement Metric:
$$improve = \frac{preds_{after} - preds_{before}}{1 - preds_{before}}$$ where the output shape is
$(batch_size, 1)$ .
The Layer1 class implements the gated mechanism for the Differentiated Behavior Effect Quantifying Module. It processes individual behavior factors (time, attempts, hints) to compute behavior-specific learning gains that are subsequently fused in the LBKTcell.
Parameters:
num_units: Number of hidden units (default: 128)d: Steepness parameter for sigmoid gate (default: 10)k: Minimum gate value (default: 0.3)b: Bias parameter for sigmoid gate (default: 0.3)
Architecture:
-
Weight Matrix: Shape
(2 * num_units, num_units)withGlorotNormalinitialization and L2 regularization -
Bias Vector: Shape
(1, num_units)withGlorotNormalinitialization and L2 regularization -
Gate Formula:
$$gate = k + \frac{1-k}{1 + e^{-d \cdot (factor - b)}}$$ where
$k=0.3$ ,$d=10$ , and$b=0.3$ are hyperparameters. -
Activation: Sigmoid applied to the gated linear transformation:
$$w = \sigma\left(\left(W \cdot [h, e_{interact}] + b\right) \odot gate\right)$$ where
$\sigma$ is the sigmoid function and$\odot$ denotes element-wise multiplication.
Forward Pass:
- Compute sigmoid gate based on behavior factor
- Concatenate previous knowledge state
hand interaction embeddinginteract_emb - Apply linear transformation:
w = concat([h, interact_emb]) @ weight + bias - Modulate with gate:
w = sigmoid(w * gate) - Return gated output
The codebase follows this modular structure:
flowchart TD
A[train.py] --> B[LBKT Class]
A --> C[Data Loading Functions]
C --> D["form_data<br/>data_helper.py"]
C --> E["fit_batch<br/>data_helper.py"]
B --> F["Recurrent Model<br/>model.py"]
F --> G["Embedding Layers<br/>Topic: dim_tp=128<br/>Response: dim_hidden=50"]
F --> H["Input Processing Layer<br/>Dense: num_units=128"]
F --> I["RNN Layer<br/>LBKTcell"]
I --> J["LBKTcell<br/>cell.py"]
J --> K["Layer1: time_gain<br/>layer.py"]
J --> L["Layer1: attempt_gain<br/>layer.py"]
J --> M["Layer1: hint_gain<br/>layer.py"]
J --> N["Fusion Mechanism<br/>Rank r=4"]
J --> O["Forget Gate<br/>Dense + Sigmoid"]
J --> P["Output Layer<br/>Dense + Sigmoid"]
B --> Q["Training Loop<br/>train_one_epoch"]
B --> R["Validation Loop<br/>test_one_epoch"]
Q --> S["Loss Computation<br/>BinaryCrossentropy"]
Q --> T["Optimizer<br/>Adam + InverseTimeDecay"]
R --> U["Metrics Computation<br/>AUC, Accuracy, RMSE"]
style A fill:#e1f5ff
style F fill:#fff4e1
style J fill:#ffe1f5
style K fill:#e1ffe1
style L fill:#e1ffe1
style M fill:#e1ffe1
Input Shapes:
Topics:(batch_size, seq_len)whereseq_len=100(pad_len)Resps:(batch_size, seq_len)Masks:(batch_size, seq_len)time_factor:(batch_size, seq_len)attempts_factor:(batch_size, seq_len)hints_factor:(batch_size, seq_len)
Embedding Shapes:
topic_emb:(batch_size, seq_len, dim_tp=128)resps_emb:(batch_size, seq_len, dim_hidden=50)acts_emb:(batch_size, seq_len, num_units=128)correlation_weight:(batch_size, seq_len, memory_size)
State Shapes:
h_init:(batch_size, memory_size, num_units=128)h_pre:(batch_size, memory_size, num_units=128)h:(batch_size, memory_size, num_units=128)h_pre_tilde:(batch_size, num_units=128)
Output Shapes:
preds:(batch_size, seq_len-1, 1)(sliced from step 1 onwards, skipping initial state)improve:(batch_size, seq_len, 1)(includes all time steps including initial state)- RNN internal output:
(batch_size, seq_len, 2)(concatenated preds and improve before slicing)
| Category | Parameter | Value | Location |
|---|---|---|---|
| Model Architecture | num_units |
128 | train.py, cell.py |
dim_tp |
128 | train.py, model.py |
|
dim_hidden |
50 | train.py, model.py |
|
memory_size |
Variable (from kc2index) | train.py |
|
num_resps |
2 | train.py |
|
dropout |
0.2 | train.py, cell.py |
|
| Sequence Processing | pad_len |
100 | train.py, data_helper.py |
BATCH_SIZE |
16 (default) | train.py |
|
| Training | EPOCHS |
100 | train.py |
lr |
0.005 (default) | train.py |
|
q_factor |
0.01 (default) | train.py |
|
patience |
5 | train.py |
|
min_delta |
0.000001 | train.py |
|
decay_rate |
0.5 | train.py |
|
| Layer1 Parameters | d |
10 | layer.py |
k |
0.3 | layer.py |
|
b |
0.3 | layer.py |
|
| Fusion Mechanism | r (rank) |
4 | cell.py |
-
Q-Matrix Smoothing:
$$Q = (1 - \alpha) \cdot Q_{original} + \alpha \cdot \mathbf{1}$$ where
$\alpha = q_factor$ (default: 0.01) is the smoothing factor, and$\mathbf{1}$ is a matrix of ones with the same shape as$Q_{original}$ . -
Sigmoid Gate (Layer1):
$$gate = k + \frac{1-k}{1 + e^{-d \cdot (factor - b)}}$$ where
$k=0.3$ ,$d=10$ , and$b=0.3$ are hyperparameters controlling the gate's behavior. -
Prediction:
$$preds = \frac{1}{num_units} \sum_{i=1}^{num_units} \sigma\left(W_{out} \cdot [\tilde{h}, e_{topic}] + b_{out}\right)$$ where
$\sigma$ is the sigmoid activation function,$\tilde{h}$ is the weighted knowledge state, and$e_{topic}$ is the topic embedding. -
Improvement:
$$improve = \frac{preds_{after} - preds_{before}}{1 - preds_{before}}$$ This metric quantifies the relative improvement in prediction after learning compared to before learning.
-
Knowledge State Update:
$$h^{(t)} = h^{(t-1)} \odot f_{gate} + Dropout(LG^{(t)})$$ where
$f_{gate}$ is the forget gate,$LG^{(t)}$ is the distributed learning gain, and$\odot$ denotes element-wise multiplication. -
Learning Rate Schedule:
$$lr(t) = \frac{lr_0}{1 + \rho \cdot \frac{t}{decay_steps}}$$ where
$\rho = 0.5$ is the decay rate,$t$ is the current training step, and$decay_steps$ is the number of steps per epoch.
The model expects JSON files with the following structure:
Training/Validation/Test Data (new_train_data.json, new_valid_data.json, new_test_data.json):
{
"user_id_1": [
[topic_id, response, correctness, timestamp, answer_time, attempts, hints],
[topic_id, response, correctness, timestamp, answer_time, attempts, hints],
...
],
"user_id_2": [...],
...
}Field Descriptions:
topic_id: Identifier for the topic/exercise (integer)response: Response value (typically 0 or 1, integer)correctness: Whether the answer was correct (integer, 0 or 1)timestamp: When the interaction occurred (numeric)answer_time: Time taken to answer (numeric)attempts: Number of attempts made (integer)hints: Number of hints used (integer)
Behavior Factor Files (time_factor.json, attempts_factor.json, hints_factor.json):
{
"user_id_1": [factor_value_1, factor_value_2, ...],
"user_id_2": [factor_value_1, factor_value_2, ...],
...
}Factor values are normalized and represent the behavior's effect on learning. The length of each factor array must match the number of interactions for that user.
Mapping Files:
topic2index.json: Maps topic IDs (as strings) to numerical indices{ "topic_id_1": 0, "topic_id_2": 1, ... }kc2index.json: Maps knowledge component IDs to numerical indices{ "kc_id_1": 0, "kc_id_2": 1, ... }q_matrix.json: Q-matrix representing topic-KC relationships (2D array)Shape:[ [1, 0, 1, ...], // Topic 0 requires KCs 0 and 2 [0, 1, 1, ...], // Topic 1 requires KCs 1 and 2 ... ]
(num_topics, num_kcs), values typically 0 or 1 (or probabilities)
The data processing follows this workflow:
flowchart TD
A[Load JSON Files] --> B[Read User Records]
B --> C[Extract Sequences per User]
C --> D["Map Topic IDs to Indices<br/>topic2index"]
C --> E["Extract Fields<br/>topics, resps, atime, attempts, hints"]
C --> F["Load Behavior Factors<br/>time_factor, attempts_factor, hints_factor"]
D --> G{"Sequence Length > pad_len?"}
E --> G
F --> G
G -->|Yes| H["Split into Multiple Sequences<br/>Each of length pad_len"]
G -->|No| I{"Remaining Length >= 10?"}
H --> J["Create Full Sequences<br/>Masks = all 1s"]
H --> K[Update Remaining Length]
K --> I
I -->|Yes| L["Pad Remaining Sequence<br/>Add zeros to reach pad_len"]
I -->|No| M[Discard Sequence]
L --> N["Create Mask<br/>1s for valid, 0s for padding"]
J --> O[Convert to NumPy Arrays]
N --> O
O --> P["Topics: int32<br/>Shape: n_sequences × pad_len"]
O --> Q["Resps: int32<br/>Shape: n_sequences × pad_len"]
O --> R["AnswerTime: float<br/>Shape: n_sequences × pad_len"]
O --> S["Attempts: int32<br/>Shape: n_sequences × pad_len"]
O --> T["Hints: int32<br/>Shape: n_sequences × pad_len"]
O --> U["Masks: int32<br/>Shape: n_sequences × pad_len"]
O --> V["Time_Factor: float<br/>Shape: n_sequences × pad_len"]
O --> W["Attempts_Factor: float<br/>Shape: n_sequences × pad_len"]
O --> X["Hints_Factor: float<br/>Shape: n_sequences × pad_len"]
P --> Y["fit_batch: Ensure Batch Divisibility"]
Q --> Y
R --> Y
S --> Y
T --> Y
U --> Y
V --> Y
W --> Y
X --> Y
Y --> Z["Add Zero-Padded Sequences<br/>Until len % batch_size == 0"]
Z --> AA[Return Processed Data]
style A fill:#e1f5ff
style G fill:#fff4e1
style O fill:#ffe1f5
style Y fill:#e1ffe1
Processing Details:
- Sequence Splitting: Sequences longer than
pad_len=100are split into multiple sequences of length 100 - Padding: Remaining sequences shorter than
pad_lenare padded with zeros to reachpad_len - Filtering: Sequences with remaining length < 10 after splitting are discarded
- Masking: Masks indicate valid positions (1) vs padding (0)
- Batch Fitting: Additional zero-padded sequences are added to ensure dataset size is divisible by batch size
For detailed data format specifications, see data/README.md.
- Python 3.7 or higher
- pip package manager
- Clone the repository:
git clone <repository-url>
cd Reading-Proficiency-LBKT- Create a virtual environment (recommended):
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Install EduData (if needed for data preprocessing):
pip install EduDataNote: For more information about EduData, visit: https://github.com/bigdata-ustc/EduData
Core dependencies (see requirements.txt for versions):
- TensorFlow (>=2.10.0, <3.0.0): Deep learning framework
- NumPy (>=1.21.0): Numerical computing
- SciPy (>=1.7.0): Scientific computing
- scikit-learn (>=1.0.0): Machine learning utilities (metrics)
- pandas (>=1.3.0): Data manipulation
- tqdm (>=4.62.0): Progress bars
- Jupyter (>=1.0.0): Notebook support
- ipykernel (>=6.0.0): Jupyter kernel support
Note: EduData package may need to be installed separately for data preprocessing. Refer to: https://github.com/bigdata-ustc/EduData
-
Ensure your data is preprocessed and placed in the
data/directory (see Data Format) -
Run training:
cd src
python train.py --batch_size 16 --lrate 0.005 --q_factor 0.01- The model will automatically:
- Train on the training set
- Validate on the validation set (with early stopping)
- Evaluate on the test set
- Save the best model checkpoint to
data/checkpoints/LBKT_assist2009/
The training script accepts the following command-line arguments:
--batch_sizeor-bs: Batch size for training (default: 16)--lrateor-lr: Learning rate (default: 0.005)--q_factoror-qf: Q-matrix factor for smoothing (default: 0.01)
Basic training with default parameters:
python train.pyCustom parameters:
python train.py --batch_size 32 --lrate 0.01 --q_factor 0.02Key hyperparameters defined in train.py:
| Parameter | Default Value | Description |
|---|---|---|
pad_len |
100 | Maximum sequence length for padding/truncation |
dim_tp |
128 | Topic embedding dimension |
num_resps |
2 | Number of response types (correct/incorrect) |
num_units |
128 | Number of hidden units in RNN cell |
dropout |
0.2 | Dropout rate for regularization |
dim_hidden |
50 | Response embedding dimension |
EPOCHS |
100 | Maximum number of training epochs |
BATCH_SIZE |
16 | Batch size (configurable via CLI) |
lr |
0.005 | Learning rate (configurable via CLI) |
q_factor |
0.01 | Q-matrix smoothing factor (configurable via CLI) |
The training process follows this workflow:
flowchart TD
A[Start Training] --> B[Load Data Files]
B --> C["form_data: Process Sequences"]
C --> D["Split Long Sequences<br/>pad_len=100"]
C --> E["Pad Short Sequences<br/>Discard if length < 10"]
D --> F["fit_batch: Ensure Batch Divisibility"]
E --> F
F --> G[Initialize Model]
G --> H[Load Q-Matrix]
H --> I["Apply Q-Matrix Smoothing<br/>Q = (1-α)Q + α·1"]
I --> J[Create LBKT Model Instance]
J --> K["Initialize Optimizer<br/>Adam with InverseTimeDecay<br/>decay_rate=0.5"]
K --> L["Training Loop: Epoch 0 to EPOCHS-1"]
L --> M[Shuffle Training Data]
M --> N[Batch Iteration]
N --> O["Forward Pass<br/>recurrent.call"]
O --> P["Compute Loss<br/>BinaryCrossentropy with sample weights"]
P --> Q[Backward Pass<br/>Compute Gradients]
Q --> R["Update Parameters<br/>optimizer.apply_gradients"]
R --> S{More Batches?}
S -->|Yes| N
S -->|No| T["Compute Training Metrics<br/>Loss, AUC, Accuracy"]
T --> U[Validation Loop]
U --> V[Forward Pass on Validation Set]
V --> W["Compute Validation Metrics<br/>Loss, AUC, Accuracy, RMSE"]
W --> X{Validation AUC Improved?}
X -->|Yes| Y["Save Model Checkpoint<br/>best_test_auc = valid_auc<br/>count = 0"]
X -->|No| Z{"Check Early Stopping<br/>valid_auc - best_test_auc < min_delta?"}
Z -->|Yes| AA[Increment Count]
Z -->|No| AB[Reset Count to 0]
AA --> AC{"Count >= Patience=5?"}
AC -->|Yes| AD["Early Stopping<br/>Break Training Loop"]
AC -->|No| AE{More Epochs?}
AB --> AE
Y --> AE
AE -->|Yes| L
AE -->|No| AF[Load Best Checkpoint]
AD --> AF
AF --> AG[Test Evaluation]
AG --> AH[Forward Pass on Test Set]
AH --> AI["Compute Test Metrics<br/>Loss, AUC, Accuracy, RMSE"]
AI --> AJ[Print Results]
AJ --> AK[End]
style A fill:#e1f5ff
style J fill:#fff4e1
style L fill:#ffe1f5
style Y fill:#e1ffe1
style AD fill:#ffe1e1
style AI fill:#e1ffe1
Training Details:
-
Loss Function: Binary Cross-Entropy with
reduction='sum'and sample weights from masks$$L_{total} = \sum_{i=1}^{N} mask_i \cdot \left[y_i \log(\hat{y}_i) + (1-y_i) \log(1-\hat{y}_i)\right]$$ $$L_{batch} = \frac{L_{total}}{N_{valid}}$$ where
$N_{valid}$ is the number of valid (non-padded) samples in the batch. -
Optimizer: Adam with Inverse Time Decay learning rate schedule
$$lr(t) = \frac{lr_0}{1 + \rho \cdot \frac{t}{decay_steps}}$$ where
$\rho = 0.5$ is the decay rate,$t$ is the current step, and$decay_steps$ is the number of steps per epoch. -
Early Stopping:
- Patience: 5 epochs
- Minimum delta: 0.000001
- Monitors validation AUC
- Saves best model based on validation AUC
-
Evaluation Metrics:
-
AUC: Area Under ROC Curve using
sklearn.metrics.roc_auc_score - Accuracy: Binary classification accuracy with threshold at 0.5
-
RMSE: Root Mean Squared Error using
sklearn.metrics.mean_squared_errorwithsquared=False -
Loss: Binary Cross-Entropy computed as
target * log(pred) + (1-target) * log(1-pred)
-
AUC: Area Under ROC Curve using
The model outputs:
- Training loss (Binary Cross-Entropy) per epoch
- Validation metrics per epoch: AUC, Accuracy, RMSE
- Test metrics (final): Loss, AUC, Accuracy, RMSE
- Model checkpoints saved in
data/checkpoints/LBKT_assist2009/
