# ============================================================ # English Learning Assistant — TinyKAN-Distill # Author: Mouna Khlifi — University of Kairouan, Tunisia # ============================================================ import gradio as gr import torch import torch.nn as nn import numpy as np from sentence_transformers import SentenceTransformer import sys, os, random import google.generativeai as genai os.system("pip install -q git+https://github.com/KindXiaoming/pykan.git google-generativeai") sys.path.insert(0, '/tmp/pykan') from kan import KAN class TinyKANDistilled(nn.Module): def __init__(self, input_dim=9, grid=3): super().__init__() self.kan_main = KAN(width=[[input_dim,0],[16,0],[8,0],[1,0]], grid=grid) self.linear_branch = nn.Sequential(nn.Linear(input_dim,8), nn.GELU(), nn.Linear(8,1)) self.gate = nn.Sequential(nn.Linear(input_dim,8), nn.ReLU(), nn.Linear(8,2), nn.Softmax(dim=1)) self.skip = nn.Linear(input_dim, 1) def forward(self, x): k = self.kan_main(x); l = self.linear_branch(x) g = self.gate(x) return (g * torch.cat([k,l],dim=1)).sum(dim=1,keepdim=True) + 0.05*self.skip(x) def get_gates(self, x): with torch.no_grad(): return self.gate(x).cpu().numpy()[0] DEVICE = "cpu" encoders = { "MiniLM": SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2"), "MPNet": SentenceTransformer("sentence-transformers/all-mpnet-base-v2"), "RoBERTa": SentenceTransformer("sentence-transformers/stsb-roberta-base"), } tinykan = TinyKANDistilled(9, 3) tinykan.load_state_dict(torch.load("tinykan_cosine.pth", map_location=DEVICE, weights_only=False)) tinykan.eval() GEMINI_KEY = os.environ.get("GEMINI_API_KEY", "") gemini_model = None if GEMINI_KEY: genai.configure(api_key=GEMINI_KEY) gemini_model = genai.GenerativeModel("gemini-1.5-flash") EXERCISES = { "translation": [ {"fr": "Le soleil se lève à l'est.", "en": "The sun rises in the east.", "level": "A1"}, {"fr": "Il fait beau aujourd'hui.", "en": "The weather is nice today.", "level": "A1"}, {"fr": "J'ai un chat et un chien.", "en": "I have a cat and a dog.", "level": "A1"}, {"fr": "J'apprends l'anglais depuis deux ans.", "en": "I have been learning English for two years.", "level": "A2"}, {"fr": "Pouvez-vous me montrer le chemin vers la gare ?", "en": "Could you show me the way to the train station?", "level": "A2"}, {"fr": "Nous avons visité Paris l'été dernier.", "en": "We visited Paris last summer.", "level": "A2"}, {"fr": "Cette décision pourrait avoir des conséquences majeures.", "en": "This decision could have major consequences.", "level": "B1"}, {"fr": "La technologie transforme notre façon de communiquer.", "en": "Technology is transforming the way we communicate.", "level": "B1"}, {"fr": "Il est important de protéger l'environnement.", "en": "It is important to protect the environment.", "level": "B1"}, {"fr": "Les recherches scientifiques ont démontré l'efficacité de cette méthode.", "en": "Scientific research has demonstrated the effectiveness of this method.", "level": "B2"}, {"fr": "Les énergies renouvelables représentent l'avenir de notre planète.", "en": "Renewable energies represent the future of our planet.", "level": "B2"}, {"fr": "Il est impératif que nous prenions des mesures immédiates.", "en": "It is imperative that we take immediate action.", "level": "C1"}, {"fr": "La mondialisation a engendré des transformations socio-économiques sans précédent.", "en": "Globalization has brought about unprecedented socio-economic transformations.", "level": "C2"}, ], "error_correction": [ {"wrong": "She don't like to go at school every days.", "correct": "She doesn't like to go to school every day.", "level": "A2", "error_type": "Grammar + Preposition + Plural"}, {"wrong": "He have been work here since three years.", "correct": "He has been working here for three years.", "level": "B1", "error_type": "Grammar (Present Perfect Continuous) + Preposition"}, {"wrong": "She is more smarter than her sister.", "correct": "She is smarter than her sister.", "level": "B1", "error_type": "Grammar (Double comparative)"}, {"wrong": "The goverment anounced a new enviromental policy.", "correct": "The government announced a new environmental policy.", "level": "B2", "error_type": "Spelling"}, {"wrong": "If I would have more time, I will study more.", "correct": "If I had more time, I would study more.", "level": "B2", "error_type": "Conjugation (Conditional type 2)"}, {"wrong": "Despite of his efforts, he don't succeed to pass the exam.", "correct": "Despite his efforts, he did not succeed in passing the exam.", "level": "C1", "error_type": "Grammar + Preposition + Conjugation"}, ], "fill_blank": [ {"display": "She _____ (study) English for five years before she moved to London.", "hint": "Past Perfect", "answer": "had studied", "level": "B1", "error_type": "Conjugation — Past Perfect"}, {"display": "The new policy will have a significant _____ on the economy. (affect / effect)", "hint": "Noun form required", "answer": "effect", "level": "B2", "error_type": "Vocabulary — affect vs. effect"}, {"display": "She has been interested _____ linguistics since her undergraduate studies.", "hint": "Preposition after 'interested'", "answer": "in", "level": "B1", "error_type": "Prepositional usage"}, {"display": "_____ research published in Nature suggests a breakthrough in cancer treatment.", "hint": "Definite article — specific reference", "answer": "The", "level": "C1", "error_type": "Article usage — definite"}, {"display": "By the time she arrives, we _____ (finish) the project.", "hint": "Future Perfect", "answer": "will have finished", "level": "B2", "error_type": "Conjugation — Future Perfect"}, ], "vocabulary": [ {"fr_def": "Personne qui travaille avec vous dans la même organisation\n/ A person who works with you in the same organisation", "answer": "colleague", "synonyms": "co-worker, associate, teammate", "level": "A2", "error_type": "Professional vocabulary"}, {"fr_def": "Rendre quelque chose plus simple à comprendre ou à réaliser\n/ To make something easier to understand or accomplish", "answer": "simplify", "synonyms": "streamline, facilitate, clarify", "level": "B1", "error_type": "Action verb — register"}, {"fr_def": "Qui ne peut pas être évité ou empêché\n/ That which cannot be avoided or prevented", "answer": "inevitable", "synonyms": "unavoidable, inescapable, certain", "level": "B2", "error_type": "Academic adjective"}, {"fr_def": "Ensemble des règles juridiques régissant une société\n/ The body of laws governing a society", "answer": "legislation", "synonyms": "law, regulation, statute, ordinance", "level": "B2", "error_type": "Legal and academic vocabulary"}, {"fr_def": "Étude scientifique du langage et de sa structure\n/ The scientific study of language and its structure", "answer": "linguistics", "synonyms": "language study, philology", "level": "C1", "error_type": "Academic discipline — terminology"}, ], "comprehension": [ { "passage": "Artificial intelligence (AI) has rapidly transformed numerous sectors, including healthcare, education, and transportation. Machine learning algorithms can now diagnose diseases with accuracy comparable to experienced physicians. However, ethical concerns about data privacy and algorithmic bias remain significant challenges that must be addressed by policymakers and researchers alike.", "question": "According to the passage, what two ethical concerns are identified as significant challenges?\n/ Selon le texte, quelles sont les deux préoccupations éthiques identifiées comme défis majeurs ?", "answer": "data privacy and algorithmic bias", "level": "B2", "error_type": "Reading comprehension — information retrieval" }, { "passage": "The Industrial Revolution, which began in Britain in the late 18th century, fundamentally altered the nature of work and society. The shift from agrarian economies to industrial ones displaced millions of workers while simultaneously creating new forms of employment. This period of transition was marked by both unprecedented economic growth and severe social inequalities.", "question": "What two contrasting outcomes of the Industrial Revolution does the author describe?\n/ Quels deux résultats contrastés de la Révolution industrielle l'auteur décrit-il ?", "answer": "unprecedented economic growth and severe social inequalities", "level": "C1", "error_type": "Reading comprehension — synthesis and contrast" }, ] } store = {"ref": "", "display": "", "type": "", "level": "B1", "error_type": "", "pct": 0, "g_kan": 0, "g_lin": 0, "cos": [0,0,0]} def compute_similarity(s1, s2): cos = [] for enc in encoders.values(): e1 = enc.encode([s1], convert_to_numpy=True) e2 = enc.encode([s2], convert_to_numpy=True) e1 /= np.maximum(np.linalg.norm(e1,axis=1,keepdims=True),1e-8) e2 /= np.maximum(np.linalg.norm(e2,axis=1,keepdims=True),1e-8) cos.append(float((e1*e2).sum())) c1,c2,c3 = cos feat = np.array([[c1,c2,c3,abs(c1-c2),abs(c1-c3),abs(c2-c3),(c1+c2+c3)/3,max(cos),min(cos)]],dtype=np.float32) feat_t = torch.tensor(feat) with torch.no_grad(): score = torch.sigmoid(tinykan(feat_t)).item() gates = tinykan.get_gates(feat_t) return round(score*100,1), round(float(gates[0])*100,1), round(float(gates[1])*100,1), [round(c*100,1) for c in cos] def analyze_errors(user_ans, reference): import re def clean(s): return set(re.sub(r'[.,!?;:\-]', '', s.lower()).split()) u = clean(user_ans); r = clean(reference) return r - u, u - r, u & r def get_gemini_feedback(display, user_ans, reference, pct, level, ex_type, error_type): if not gemini_model: return "" try: prompt = f"""You are an academic English language instructor assessing a {level}-level learner. Exercise type: {ex_type} | Linguistic focus: {error_type} Task: {display} Learner response: "{user_ans}" Reference answer: "{reference}" TinyKAN semantic similarity score: {pct}% Provide concise structured bilingual feedback: **English Assessment:** - Accuracy: [correct/incorrect elements] - Errors identified: [grammar / spelling / vocabulary / structure] - Linguistic rule: [relevant rule explained briefly] - Recommendation: [concrete improvement strategy] **Évaluation en français :** - Précision: [éléments corrects/incorrects] - Erreurs: [grammaire / orthographe / vocabulaire / structure] - Règle linguistique: [règle pertinente expliquée brièvement] - Recommandation: [stratégie d'amélioration concrète]""" r = gemini_model.generate_content(prompt) return r.text except: return "" def new_exercise(level, exercise_type, name): pool = [e for e in EXERCISES.get(exercise_type, EXERCISES["translation"]) if e.get("level") == level] if not pool: pool = EXERCISES.get(exercise_type, EXERCISES["translation"]) ex = random.choice(pool) store["level"] = level; store["type"] = exercise_type cfg = { "translation": ("#eff6ff","#1d4ed8","#bfdbfe","🔤 Translation (FR → EN)"), "error_correction": ("#fff7ed","#b45309","#fde68a","✏️ Error Correction"), "fill_blank": ("#f0fdf4","#15803d","#bbf7d0","📝 Fill in the Blank"), "vocabulary": ("#faf5ff","#6d28d9","#ddd6fe","📖 Vocabulary"), "comprehension": ("#fff1f2","#be123c","#fecdd3","📄 Reading Comprehension"), } bg,accent,border,label = cfg.get(exercise_type, ("#f8fafc","#1d4ed8","#e2e8f0","Exercise")) if exercise_type == "translation": store["ref"] = ex["en"]; store["display"] = ex["fr"]; store["error_type"] = "Semantic similarity — FR to EN translation" body = f"""
Translate the following sentence into English. / Traduisez la phrase suivante en anglais.
Rewrite the sentence correctly, correcting all linguistic errors. / Réécrivez la phrase correctement en corrigeant toutes les erreurs linguistiques.
Complete the sentence with the appropriate word or verb form. / Complétez la phrase avec le mot ou la forme verbale appropriée.
Grammatical hint / Indice grammatical : {ex['hint']}
""" elif exercise_type == "vocabulary": store["ref"] = ex["answer"]; store["display"] = ex["fr_def"]; store["error_type"] = ex["error_type"] body = f"""Identify the English term corresponding to the definition below. / Identifiez le terme anglais correspondant à la définition ci-dessous.
Related terms / Termes associés : {ex['synonyms']}
""" else: store["ref"] = ex["answer"]; store["display"] = ex["passage"]; store["error_type"] = ex["error_type"] body = f"""Read the following passage carefully and answer the question below. / Lisez attentivement le texte suivant et répondez à la question ci-dessous.
| Category / Catégorie | Words / Mots | N |
|---|---|---|
| ✅ Correct vocabulary Vocabulaire correct |
{', '.join(sorted(correct)[:12]) if correct else '—'} | {len(correct)} |
| ❌ Missing words Mots manquants |
{', '.join(sorted(missing)[:12]) if missing else '—'} | {len(missing)} |
| ⚠️ Extraneous words Mots en trop |
{', '.join(sorted(extra)[:12]) if extra else '—'} | {len(extra)} |
| 🎯 Linguistic category Catégorie d'erreur |
{store['error_type']} | |
| Dataset | Pearson r ↑ | 95% CI | Spearman ρ ↑ | N pairs |
|---|---|---|---|---|
| ⭐ STS-Benchmark (primary) | 0.8791 | [0.8662, 0.8909] | 0.8752 | 1,379 |
| STS-2012 | 0.8813 | [0.8699, 0.8911] | 0.7949 | 3,108 |
| STS-2013 | 0.9046 | [0.8949, 0.9149] | 0.8936 | 1,500 |
| STS-2014 | 0.9050 | [0.8984, 0.9116] | 0.8815 | 3,750 |
| STS-2015 🏆 | 0.9137 | [0.9063, 0.9207] | 0.9158 | 3,000 |
| STS-2016 | 0.8541 | [0.8386, 0.8690] | 0.8582 | 1,186 |
| STS-2017 (en-en) | 0.8917 | [0.8600, 0.9164] | 0.8837 | 250 |
| SICK-R | 0.8315 | [0.8244, 0.8381] | 0.8066 | 9,927 |
| STS22 (en) harder benchmark | 0.6706 | [0.5675, 0.7638] | 0.6557 | 197 |
| Model type | Hybrid KAN (spline + linear + gate) |
| Parameters | 32,080 |
| Input features | 9 (cosine similarity) |
| KAN width | [9 → 16 → 8 → 1] |
| Compression ratio | 8,012× vs. teacher ensemble |
| Distillation | α = 0.7 | T = 3.0 |
| Teacher 1 | all-MiniLM-L6-v2 (22M) |
| Teacher 2 | all-mpnet-base-v2 (110M) |
| Teacher 3 | stsb-roberta-base (125M) |
| Total capacity | 257M parameters |
| Weighting | Inverse-variance |
| Ensemble Pearson r | 0.9146 |
Intelligent language assessment via Ensemble Knowledge Distillation into Kolmogorov–Arnold Networks (TinyKAN-Distill)
Developed by Mouna Khlifi · University of Kairouan, Tunisia · 2026