Development of an AI system for analyzing student engagement
Engagement is a leading predictor of academic performance and dropout risk. LMS systems (Moodle, Canvas, Blackboard) accumulate a digital trace of each student: their activity in materials, timing, and interaction patterns. Machine learning transforms this trace into early signals for supervisors.
Data sources and engagement signals
Behavioral events from LMS:
engagement_signals = {
# Когнитивная вовлечённость
'video_completion_rate': 'процент просмотренного видео (не просто opened)',
'rewatch_rate': 'доля пересматриваемых фрагментов = сложный материал',
'reading_time_vs_expected': 'фактическое время чтения / ожидаемое',
'quiz_attempts': 'повторные попытки = заинтересованность или затруднение',
# Временные паттерны
'session_regularity': 'STD времени между сессиями (высокий = нерегулярность)',
'night_study_ratio': 'доля активности после 22:00 = прокрастинация',
'deadline_clustering': 'концентрация активности перед дедлайнами',
# Социальная вовлечённость
'forum_posts_count': 'посты на форуме курса',
'peer_review_quality': 'оценка работ однокурсников',
'group_project_contribution': 'вклад в командную работу (git-коммиты, если CodeGrade)',
# Сигналы затруднения
'help_requests': 'обращения к преподавателю/AI-тьютору',
'time_on_wrong_answers': 'время на задачах, где несколько неверных попыток'
}
Building an Engagement Score
Composite Engagement Index:
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
def compute_engagement_score(student_weekly_data: pd.DataFrame) -> pd.Series:
"""
Взвешенный индекс по компонентам.
Веса получены из регрессии engagement → итоговая оценка.
"""
weights = {
'video_completion_rate': 0.20,
'quiz_participation_rate': 0.20,
'session_regularity_score': 0.15, # обратная к STD
'forum_activity_score': 0.10,
'assignment_lead_time': 0.15, # дней до дедлайна при сдаче
'material_depth_score': 0.20 # % дополнительных материалов
}
scaler = MinMaxScaler()
normalized = pd.DataFrame(
scaler.fit_transform(student_weekly_data[list(weights.keys())]),
columns=list(weights.keys()),
index=student_weekly_data.index
)
score = sum(normalized[col] * weight for col, weight in weights.items())
return score # 0-1, где >0.7 = высокая вовлечённость
def session_regularity_score(session_timestamps):
"""
Регулярность = обратная к коэффициенту вариации интервалов между сессиями
"""
intervals = np.diff(sorted(session_timestamps))
if len(intervals) < 2:
return 0.5
cv = np.std(intervals) / (np.mean(intervals) + 1e-9)
return max(0, 1 - cv)
Early Warning Model (At-Risk Detection)
XGBoost attrition risk classifier:
from xgboost import XGBClassifier
from sklearn.model_selection import StratifiedKFold
import shap
def build_at_risk_model(historical_data):
"""
Данные: week-1..week-4 engagement features → итоговый результат курса
Цель: предсказать риск неуспешного завершения на 4-й неделе
"""
feature_cols = [
'engagement_score_w1', 'engagement_score_w2',
'engagement_score_w3', 'engagement_score_w4',
'engagement_trend', # slope за 4 недели
'engagement_volatility', # STD за 4 недели
'missed_quizzes_count',
'avg_score_first_assignments',
'prior_course_completion_rate' # история студента
]
model = XGBClassifier(
n_estimators=200,
scale_pos_weight=3, # at-risk студентов обычно ~25%
eval_metric='aucpr'
)
model.fit(
historical_data[feature_cols],
historical_data['at_risk_label']
)
# SHAP для объяснения куратору
explainer = shap.TreeExplainer(model)
return model, explainer
def explain_student_risk(model, explainer, student_features):
"""
Возвращает ТОП-3 причины риска понятным языком
"""
shap_values = explainer.shap_values(student_features)
feature_importance = dict(zip(student_features.index, shap_values[0]))
top_factors = sorted(feature_importance.items(),
key=lambda x: abs(x[1]), reverse=True)[:3]
explanations = {
'engagement_trend': 'снижение активности за последние 4 недели',
'missed_quizzes_count': 'пропущенные тесты',
'engagement_score_w4': 'низкая активность на текущей неделе',
'avg_score_first_assignments': 'слабые результаты первых заданий'
}
return [explanations.get(feat, feat) for feat, _ in top_factors]
Patterns of engagement degradation
Typology of at-risk students:
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
def cluster_engagement_trajectories(student_weekly_scores: pd.DataFrame):
"""
Кластеризация по динамике — разные паттерны требуют разных интервенций
"""
# 8 недель × N студентов → матрица траекторий
trajectories = student_weekly_scores.pivot(
index='student_id', columns='week', values='engagement_score'
).fillna(method='ffill')
kmeans = KMeans(n_clusters=4, random_state=42)
clusters = kmeans.fit_predict(trajectories)
# Типичные паттерны:
# Кластер 0: "Старт высокий, падение к сессии" → напоминания в середине курса
# Кластер 1: "Стабильно низкий с начала" → немедленное вмешательство
# Кластер 2: "Низкий старт, рост" → у студентов был стрессовый период
# Кластер 3: "Стабильно высокий" → норма
return dict(zip(trajectories.index, clusters))
Automatic interventions
Trigger-based alerts for curators:
def generate_intervention_recommendations(student_risk_profile):
rules = [
{
'condition': lambda p: p['engagement_score_current_week'] < 0.2,
'action': 'urgent_curator_call',
'message': 'Студент {name} не активен 2+ недели'
},
{
'condition': lambda p: p['engagement_trend'] < -0.1 and p['at_risk_prob'] > 0.5,
'action': 'automated_email',
'message': 'Приглашение на консультацию + ссылки на проблемные модули'
},
{
'condition': lambda p: p['rewatch_rate'] > 0.4 and p['quiz_attempts_avg'] > 2,
'action': 'suggest_tutoring',
'message': 'Признаки затруднения с материалом — предложить тьютора'
}
]
triggered = [r for r in rules if r['condition'](student_risk_profile)]
return triggered
Integration with LMS API:
Moodle: mod_forum_add_discussion, message_send_instant_messages. Canvas: Events API + Webhooks. Engagement data is updated nightly (batch) and hourly for high-risk students.
Deadlines: Engagement Score + basic at-risk XGBoost + curator dashboard – 3-4 weeks. Trajectory clustering, SHAP explanations, automated interventions, LMS API integration – 2-3 months.







