Liigu sisu juurde

Vaadake ridade kaupa läbi veebi/lib/bkt.ts

Sukelduge põhjalikult teemasse web/lib/bkt.ts. Eesmärk: avage fail ja tehke iga otsus ~10 minuti jooksul.

import {
BktParams,
DEFAULT_BKT,
MicroSkill,
MicroSkillId,
StudentState,
Task,
} from "./microskills";

„DEFAULT_BKT” koondab kirjanduse vaikeväärtused (P(L_0)=0,2, P(T)=0,1, P(S)=0,1, P(G)=0,2). Funktsioonid aktsepteerivad valikulisi parameetreid, nii et testid/vidinad kohandavad käitumist ilma allkirjade katkemiseta.

pSolve — ennustusvalem

Jaotis «pSolve — ennustusvalem»
export function pSolve(pL: number, params: BktParams = DEFAULT_BKT): number {
return pL * (1 - params.pSlip) + (1 - pL) * params.pGuess;
}

Üks rida – kogu P(lahendada) arvutus (peatükk 8). Numbriliselt taltsas (pole jaotust/logisid), mida nimetatakse tugevalt punktiarvestussilmusteks – hoidke pingul.

bktUpdate – Bayesi tuum

Jaotis «bktUpdate – Bayesi tuum»
export function bktUpdate(
pL: number,
observedCorrect: boolean,
params: BktParams = DEFAULT_BKT
): number {
const { pSlip, pGuess, pTransit } = params;
const posterior = observedCorrect
? (pL * (1 - pSlip)) / (pL * (1 - pSlip) + (1 - pL) * pGuess)
: (pL * pSlip) / (pL * pSlip + (1 - pL) * (1 - pGuess));
return posterior + (1 - posterior) * pTransit;
}

BKT süda. Ternaarne hoiab oksad tihedana; samaväärne “kui/muidu” oleks õhupall.

Arvuline ohutus: nimetajad jäävad P(L)[0,1]P(L)\in[0,1], P(S),P(G)(0,1)P(S),P(G)\in(0,1) puhul positiivseks – 0/0 pole.

ensureMastery — laisk lähtestamine

Jaotis «ensureMastery — laisk lähtestamine»
export function ensureMastery(
state: StudentState,
skillId: MicroSkillId,
params: BktParams = DEFAULT_BKT
): number {
const cur = state.mastery[skillId];
if (cur === undefined) {
state.mastery[skillId] = params.pInit;
return params.pInit;
}
return cur;
}

Esimene kohtumine oskusega seemned pInit. Muteerib olek sihilikult – soojuskaardid/kasutajaliides vajavad konkreetseid numbreid, mitte defineerimata.

Alternatiivne muutumatu cur?? pInit lükkab raamatupidamise väljapoole – esimese puudutusega muteerumine jääb lihtsamaks.

applyAttempt — katsejärgne värskendus

Jaotis «applyAttempt — katsejärgne värskendus»
export function applyAttempt(
state: StudentState,
task: Task,
correct: boolean,
perSkill?: Record<MicroSkillId, boolean>,
params: BktParams = DEFAULT_BKT
): StudentState {
for (const skillId of task.microskills) {
const observed = perSkill?.[skillId] ?? correct;
const prior = ensureMastery(state, skillId, params);
state.mastery[skillId] = bktUpdate(prior, observed, params);
}
state.history.push({
task_id: task.id,
correct,
per_skill: perSkill,
ts: new Date().toISOString(),
});
return state;
}

“perSkill” vastab:

Õpilane ei suutnud probleemi lahendada – millised mikrooskused ebaõnnestusid?

Sammutasandi jäädvustamine (tahvelarvuti töövoog / skanner) võimaldavad lahtris expand_brackets positiivseid tulemusi, karistades samas arith.signsi – palju teravamalt kui ühtsed +/- värskendused.

Varundus ilma perSkillita: ühtne signaal – MVP sans skanneri jaoks sobib.

history.push võimaldab navigeerimist, auditiloge ja kordusvastast heuristikat.

scoreTaskForStudent — valija tuum

Jaotis «scoreTaskForStudent — valija tuum»
export function scoreTaskForStudent(
state: StudentState,
task: Task,
opts: SelectorOptions = {},
params: BktParams = DEFAULT_BKT
): ScoredTask {
const target = opts.target ?? 0.7;
const rareBonus = opts.rareSkillBonus ?? 0.15;
// Per-skill P(solve), then take the geometric mean as the joint —
// a task fails if *any* required skill fails, so geo-mean penalises
// missing one skill more than arithmetic mean.
const perSkillPL: Record<MicroSkillId, number> = {};
let logSum = 0;
for (const skillId of task.microskills) {
const pL = state.mastery[skillId] ?? params.pInit;
perSkillPL[skillId] = pL;
logSum += Math.log(Math.max(1e-6, pSolve(pL, params)));
}
const pSolveJoint = Math.exp(logSum / task.microskills.length);
// Closeness to target — Gaussian-ish, peaks at target=0.7
const closeness = Math.exp(-Math.pow(pSolveJoint - target, 2) / 0.03);
// Rarity bonus: how many of this task's skills are below 0.4?
const undertrained = task.microskills.filter(
(s) => (state.mastery[s] ?? params.pInit) < 0.4
).length;
const rarity = undertrained / task.microskills.length;
const score = closeness + rareBonus * rarity;
return { task, pSolve: pSolveJoint, perSkillPL, score };
}

Tihe plokk — lahti pakkida sektsioonide kaupa.

ZPD bullseye (peatükk 8). Katsetamisel alistamine valiku „opts” kaudu.

`Math.max(1e-6, pSolve(pL, params))

Jaotis «`Math.max(1e-6, pSolve(pL, params))»

Valvurid Math.log(0). Reaalsed jooksud jõuavad harva nulli tänu pGuess > 0le, kuid klamber on odav kindlustus.

Geomeetriline keskmine logide kaudu

Jaotis «Geomeetriline keskmine logide kaudu»
logSum = Σ log(P_i)
GM = exp(logSum / n)

Eelised:

  • väldib korrutavat allavoolu suurte n-de korral;
  • vastab IRT laiendustest tuttavatele logi-keskmistele mõõdikutele.
closeness = exp(-(p - 0.7)^2 / 0.03)

Width 0,03 arutelu — peatükk 8.

Alternatiivid:

  • 1 - |p - 0,7| — lineaarne, nõrk kontrast sihtmärgi lähedal;
  • Lorentzian - pehmemad sabad.

Gaussi joon jääb adaptiivse testimise kirjanduses sujuvaks, sümmeetriliseks ja tavapäraseks.

Harulduse boonus — uurimine

Jaotis «Harulduse boonus — uurimine»
rarity = (#skills with P(L) < 0.4) / (#skills required)
score = closeness + rareBonus * rarity

Ilma selleta püsivad valijad mugavustsoonides.

„rareBonus = 0,15” tasakaalustab uurimise ja pettumuse (valija peatükk).

Tagastage kasulik koormus

Jaotis «Tagastage kasulik koormus»
return { task, pSolve: pSolveJoint, perSkillPL, score };

“perSkillPL” voogude selgitatavus koopia.

soovita — lõplik tellimine

Jaotis «soovita — lõplik tellimine»
export function recommend(
state: StudentState,
pool: Task[],
topN = 5,
opts?: SelectorOptions,
params?: BktParams
): ScoredTask[] {
const recentIds = new Set(
state.history.slice(-5).map((h) => h.task_id)
);
const scored = pool
.filter((t) => !recentIds.has(t.id))
.map((t) => scoreTaskForStudent(state, t, opts, params));
scored.sort((a, b) => b.score - a.score);
return scored.slice(0, topN);
}

Eemaldage viimased viis ülesande ID-d – kordumise vastane:

  • identsete asjade kordamine tüütab õpilasi;
  • viieastmeline aken tasakaalustab uudsuse ja katvuse.

Alternatiivne eksponentsiaalne lagunemine:

const recencyPenalty = recent.findIndex(h => h.task_id === t.id);
score *= recencyPenalty < 0 ? 1 : Math.exp(-recencyPenalty / 3);

Raskem seletada; MVP ei vaja seda.

~150 rida. Null raskekaalu deps (ainult jagatud tüübid). Selgesõnalised valemid – läbipaistmatud sklearn-virnad puuduvad.

See on tahtlik – iga otsus on õpetajate ja välise auditooriumi jaoks sõnaliselt kaitstav.