Avainsana-arkisto: oppimisanalytiikka

Oppimisanalytiikan kokeiluja: the data is not enough – tehtävien vaikeusasteen visualisointia

Aiemmassa blogimerkinnässäni Oppimisanalytiikan kokeiluja: kuinka lakkasin olemasta huolissani ja rakastamaan LRS:ää  käsittelin kokemuksia Learning Lockerin hyödyntämisestä. Mainitsin myös, että Learning Lockeria kokeiltiin Karelia-ammattikorkeakoulun kielten opintojen verkkokurssien datan avulla. Seuraavaksi avaan tarkemmin, miten Learning Lockerin ulkopuolisia työkaluja hyödyntäen saatiin aikaan tehtävien vaikeusasetetta kuvaava kaavio Rautalankaruotsia-verkkokurssille.

Miksi tähän on ryhdytty?

Rautalankaruotsia on itseopiskeltava ruotsin kielen verkkokurssi, joka keskittyy kielioppiin ja sisältää runsaasti automaattisesti arvioitavia tehtäviä. Automaattinen arviointi kielten yhteydessä on monilta osin haastavaa, sillä oikean vastauksen voi periaatteessa antaa monella tavalla. Moodlen automaattiset arviointityökalut sen sijaan toimivat yleensä paremmin, kun kysymykseen on tarjolla yksi ainoa oikea vastaus. Tämän vuoksi automaattisen arvioinnin tekeminen vaatii runsaasti työtä, ja siltikin joku vaihtoehto voi jäädä huomiotta. Lisäksi itseopiskeluun suunnatun verkkokurssin tehtävien on oltava selkeitä, etteivät opiskelijat turhaudu.

Rautalankaruotsin ensimmäisten toteutusten yhteydessä tehtävätarkistuksiin oli luonnollisesti jäänyt pieniä virheitä, jotka aiheuttivat opiskelijoille ylimääräistä päänvaivaa. Mutta varmuuden saamiseksi oli syytä visualisoida, mitkä tehtävistä oikeasti vaativat kehittämistä.

Tässä blogimerkinnässä esittelen, millaisella kyselyllä tieto saadaan ulos Learning Lockerin tietokannasta, sekä käyn läpi visualisoinnissa hyödynnetyn dc.js-kirjaston pääpiirteet teknisen toteutuksen näkökulmasta.

Datan hakeminen Learning Lockerin tietovarastosta

Koska Learning Locker on oppimisen tietovarasto, ja se erottelee erilaiset oppimistapahtumat xAPI-rekisterin avulla, voimme saada selville kurssitoteutuskohtaisesti kunkin opiskelijan tehtäväpalautustapahtumat.  Tieto siirrettiin moodlen lokeista Learning Lockeriin erityisen xAPI-työkalun avulla.  Learning Locker on rakennettu MongoDB-tietokannan päälle, joten voimme tehdä kyselyjä sen tietokantaan MongoDB:n tarjoaman ohjelmointirajapinnan avulla.

Hyvä! Lähdemme liikkeelle siitä millaista tietoa haluamme taulukkomuodossa, eli jotain tämän suuntaista:

Tehtävät Ongelmissa (kpl) OK (kpl)
Tehtävä 1 0 54
Tehtävä 2 30 24

Pienen aivojumpan jälkeen on hahmotettavissa, että alkuvaiheessa kaikki tehtäväpalautustiedot on ryhmiteltävä opiskelijan sekä tehtävän nimen perusteella, sekä laskettava tietueeseen yhteen summa opiskelija-tehtävä-palautustapahtumapareista. Tämän avulla saamme selville, montako kertaa opiskelija on yrittänyt kutakin tehtävää. Tästä pääsemmekin jo rakentamaan kyselyä!

Koska joudumme laskemaan tietoja yhteen sekä ryhmittelemään niitä, käytämme apuna MongoDB:n ns. aggregaatio-operaatioita. Se tapahtuu mongoDB:n yhteydessä seuraavalla tavalla:


db.statements.aggregate( [{

Ensimmäisenä haemme tietovarastosta kaikki palautustapahtumat. Verbimäärityksellä voimme rajata palautuvaan tulosjoukkoon pelkät palautukset. Tapahtumat generoinut työkalu käyttää tässä tehtäväpalautuksen yhteydessä xAPI-rekisterin submit-verbiä (http://activitystrea.ms/schema/1.0/submit). Lisäksi rajoitamme tapahtumat tiettyyn kurssiin sen URL-osoitteen perusteella, joka tallennettu tapahtuman kontekstitietotaulukkoon, ja varmistamme että tapahtuma tulee halutusta tietovarastosta.


{
$match: {
"statement.verb.id":"http://activitystrea.ms/schema/1.0/submit",
"statement.context.contextActivities.grouping.0.id" : "KURSSIN_URL_OSOITE",
"lrs_id" : mongoose.Types.ObjectId("TIETOVARASTON_ID")
}
},

Nyt meillä on haettuna kaikki tehtäväpalautusta kuvaavat oppimistapahtumat! Seuraavaksi ryhmittelemme ne opiskelijan ja tehtävän nimen perustella, sekä laskemme yhteen montako kertaa kukin opiskelija-tehtäväpari esiintyi, ja tallennamme tiedon “hits”-muuttujaan.


{
$group: {
_id: {
task: "$statement.object.definition.name.en-GB",
student: "$statement.actor.name"
},
hits: { $sum:1}}
},

Sen jälkeen tarvitsemme raja-arvon, josta päättelemme ketkä opiskelijoista ovat ylittäneet yrityskerroille hyväksyttävän kynnyksen. Käsittelemme muistissa olevan datajoukon, ja lisäämme kuhunkin problems-kentän arvolla tosi tai epätosi, riippuen siitä oliko ennalta määritetty raja ylitetty vai ei.


{
$project: {
"_id.task" : true,
hits: true,
problems : { $gt: ["$hits", RAJA_ARVO ]}
}
},

Tämän jälkeen voimme ryhmitellä tiedon pelkästään tehtävän nimen perusteella, sekä lisätä tehtävätietueeseen ongelmissa olleiden ja normaalisti edenneiden opiskelijoiden määrät laskemalla ne yhteen problems-kentän perusteella:

{
$group : {
_id: "$_id.task",
"numberOfStudentsInProblems": { "$sum": { "$cond": [ { "$eq": [ "$problems", true] }, 1, 0 ]}},
"numberOfStudentsOk": { "$sum": { "$cond": [ { "$eq": [ "$problems", false] }, 1, 0 ]}}
}
}
]);

Kysely palauttaa nyt halutun datan, joka voidaan siirtää HTML-sivulle jollakin menetelmällä, esimerkiksi express.js-kirjastoa hyödyntävän nodejs-sovelluksen avulla. Asioiden yksinkertaistamiseksi sen tekninen esittely, sekä kyselyä varten välitettävien parametrien hyödyntäminen ohitetaan toistaiseksi.

Datan visualisointi

Datasta muodostetaan siis kaavio ja taulukko. Siinä hyödynnämme apuna crossfilter-filtteröintikirjastoa moniulotteiseen datan käsittelyyn, sekä dc.js-kaavionvisualisointikirjastoa. Tätä varten tarvitsemme tietyt javascript-kirjastot mukaan sivustoon:

<script src="d3.js"></script>
<script src="crossfilter.js"></script>
<script src="dc.js"></script>
<script src="d3-queue.js"></script>
<script src="d3-fetch.v1.js"></script>

Rakennetaan seuraavaksi pohja. Ensiksi luodaan HTML-sivulle <div>-elementit palkkikaaviota sekä taulukkoa varten:

<body>
<div id="dc-bar-attempts"></div>
<div id="dc-table-attempts">
<body>

 

Kun data on saatu haettua, muodostetaan crossfilter-kirjaston avulla riippuvuudet sisältävät datajoukot. Määrittelemme tehtävädimension, sekä erilliset ryhmät ongelmatapauksille ja normaaleille seuraavasti:

var ndx = crossfilter(json);
var taskDim = ndx.dimension(d => d._id );
var problemGroup = taskDim.group().reduceSum(d => d.numberOfStudentsInProblems );
var expectedGroup = taskDim.group().reduceSum(d => d.numberOfStudentsOk );

Tämän jälkeen meillä on riittävät yhteydet datan käsittelyyn monissa eri kaavioissa.

Visualisointia varten luomme luomme datasta ensimmäisenä palkkikaavion. Dimensioksi (eli X-akselille tuleviksi arvoiksi) määritetään tehtävät, ja pinoamme kaavioiden y-akselille ongelmallisten sekä normaaleiden opiskelijoiden määrät. Tämä tuottaa meille kaavion, jossa yksi palkki esittää yhden tehtävän tilanteen graafisesti.


var barChart = dc.barChart('#dc-bar-attempts');
barChart
.width(990)
.height(400)
.mouseZoomable(true)
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.elasticX(true)
.elasticY(true)
.x(d3.scaleOrdinal())
.xUnits(dc.units.ordinal)
.xAxisLabel("Tehtävät")
.yAxisLabel("Odotetujen yrityskertojen ja niiden ylitysten suhde")
.dimension(taskDim)
.group( problemGroup, "Ongelmissa olleet")
.stack( expectedGroup, "Normaalit" )

Taulukoon rakennamme kolme saraketta: tehtävän nimi, ongelmallisten tapausten lukumäärä. sekä normaalien tapausten lukumäärä, sekä määritämme lajitteluksi laskevan järjestyksen ongelmallisten tapausten lukumäärän perusteella.


var table = dc.dataTable("#dc-table-attempts");
table
.dimension(taskDim)
.columns([
{ label: 'Tehtävä', format: function(d){ return d._id;} },
{ label: 'Ongelmissa (kpl)', format: function(d){ return d.numberOfStudentsInProblems;} },
{ label: 'OK (kpl)', format: function(d){ return d.numberOfStudentsOk;} }
])
.size(1000)
.group(function(d) {return "yes";})
.showGroups(false)
.sortBy(function(d) {return d.numberOfStudentsInProblems;})
.order(d3.descending);

Taulukon kukin rivi kertoo meille numeeriset arvot, millainen tilanne yksittäisen tehtävän osalta oli palautuskertojen suhteen – montako ongelmallista ja montako normaalia tapausta.
Loppujen lopuksi käskytämme dc.js-kirjastoa piirtämään kaaviot:
dc.renderAll();

Lopputulos palkkikaaviosta voi näyttää sopivalla datajoukolla esimerkiksi tältä. Oranssi palkki kuvaa normaalisti suorittaneiden määrää, ja sininen puolestaan raja-arvon ylittäneiden määrää. Ongelmalliset tehtävät on havaittavissa selkeästi.

Taulukko puolestaan esittää saman tiedon numeerisesti.

Miten kokeilu onnistui?

Visualisoinnin perusteella on helppo havaita mitkä tehtävistä ovat hankalia ja mitkä puolestaan helpompia. Myös yksityiskohtaiset tehtävänimet ja numeeriset arvot näkyvissä taulukosta. Kaavio ja taulukko tarjoavat suoraan tiedon, mitä tehtävistä on kehitettävä, joten kehitettyä visualisointia voi pitää varsin onnistuneena. Visualisointi on myös tarpeen mukaan suoraan siirrettävissä muihin opintojaksototeutuksiin, joissa on paljon automaattisesti tarkistettavia tehtäviä. Tämä tekee siitä varsin hyödyllisen työkalun suunnittelun tueksi.

Myöhemmin tänä keväänä esittelen erilaisen aktiivisuuskaavion toteutusta, jolla saadaan seurattua opiskelijan edistymistä vaikkapa tietyn aihealueen parissa.

Kirjoittaja Anssi Gröhn, tietojenkäsittelyn lehtori

(21.3.2019 muokattu kuvia ja tekstiä)

Oppimisanalytiikan kokeiluja: kuinka lakkasin olemasta huolissani ja rakastamaan LRS:ää

Learning Locker on Learning Record Store (LRS) -järjestelmä, jolle ehkäpä kuvaavin käännös on oppimistapahtumatietueiden tallennuspaikka. Learning Lockerin on kehittänyt HT2 Labs, ja järjestelmä on saatavilla niin avoimena lähdekoodina, kuin maksullisena pilvipalvelunakin. Learning Locker hyödyntää oppimistapahtumien tallentamisessa sähköistä oppimiseen liittyvää ohjelmointirajapintaa. Rajapinnasta käytetään nimitystä xAPI, tai Experience API tai TinCan API. XAPIn tapahtumat esitetään verkkosovellusten usein suosimassa tiedostomuodossa (JSON).

Learning Locker mahdollistaa erilaisista ulkoisista järjestelmistä koostetun tiedon yhdistämisen samaan tietovarastoon jatkokäsittelyä varten, sekä tarjoaa muutamia erilaisia visualisointityökaluja datan käsittelyyn. Maksullisessa versiossa visualisointityökaluja on tarjolla kattavammin. Learning Lockerin dokumentaation (2018) mukaan se on integroitavissa laajennosten avulla erilaisiin ulkoisiin oppimisalustoihin, kuten Moodleen, Blackboard Learniin ja ehkäpä Suomessa vähemmän tunnettuun Articulateen. Lisäksi tarjolla on Yammer-laajennos.

Miksi Learning Locker?

Learning Lockerin valintaan vaikuttivat erityisesti avoin lähdekoodi, ilmaisuus ja mahdollisuus asentaa se paikallisesti omille palvelimille – erityisesti EU-GDPR:n asettamien vaatimusten osalta pilvipalveluiden käyttäminen ei ole enää niin suoraviivaista. Myös Weissin (2017) mukaan Learning Locker on sijoittunut vuonna 2017 neljänneksi yhdeksän vertaillun LRS-järjestelmän kesken.

Learning Lockerin ominaisuuksista sai vaikutelman kattavasta ratkaisusta, jolla olisi mahdollista tallentaa oppimisanalytiikkadata sekä visualisoida se helposti. Lisäksi xAPIn avulla tapahtuva tiedon siirtäminen eri organisaatioiden LRS-järjestelmien kesken olisi helpompaa. Tästä olisi erityisesti hyötyä ristiinopiskelussa, sillä opiskelijoille esitettävän oppimisanalytiikkadatan on katettava kaikki hänen suorituksensa – riippumatta siitä, missä oppilaitoksissa hän opiskelee.

Learning Lockerin tietovarasto on toteutettu MongoDB:n avulla, joten sen sisältämää tietoa on mahdollista käyttää ulkoisissa visualisointityökaluissa kohtalaisen helposti – olettaen toki, että hallussa on ohjelmointiosaamista.

Kokemukset

Ensimmäiset kokemukset Learning Lockerista ovat varsin myönteisiä; sinällään järjestelmän käyttöönotto ja hyödyntäminen on varsin suoraviivaista. Hankalinta on ehkä saada järjestelmään merkityksellistä dataa järjestelmään sisälle. Kuten tulikin jo mainittua, Moodle-oppimisympäristöön on saatavilla erillinen laajennos, jonka voi konfiguroida lähettämään Moodlen keräämät tapahtumat Learning Locker-palvelimelle. Tämä vaatii myös palvelimen näkyviin julkisesti verkkoon, joten jätin tämän vaiheen pois ja rakensin erillisen työkalun, joka koostaa xAPI-tapahtumat Moodlen lokitiedostojen perusteella ja lähettää ne halutulle Learning Locker –palvelimelle, jota voi ajaa vaikkapa paikallisesti virtuaalikoneessa.

Pilotoinnin perusteella Learning Lockerin visualisointityökalut ovat erinomaisia nopeita testejä sekä yksinkertaisia visualisointeja varten, joissa dataa ei tarvitse yhdistellä tai käsitellä erityisesti – näkyvää saa aikaan muutamassa minuutissa, kuten esimerkiksi alla olevan aktiivisuutta esittävän kaavioyhdistelmän. Mikäli tarvetta on monimutkaisemmille kyselyille, on syytä käyttää muita työkaluja visualisoinnin tukena.

Tietojen siirrettävyys saa apuja Learning Lockerin käytöstä; minkä tahansa kyselyn tiedot voi siirtää CSV-muodossa olevaan tekstitiedostoon. Tekstitiedosto sisältää JSON-muodossa kunkin kyselyn palauttaman xAPI-tapahtuman, joten se on pienellä vaivalla mahdollista lukea ohjelmallisesti sekä siirtää toiseen xAPI-rajapinnan toteuttavaan järjestelmään.

Miten tästä eteenpäin?

Learning Lockeria on kokeiltu Karelia-ammattikorkeakoulun tietojenkäsittelyn sekä kielten opintojen verkkokurssien datan avulla. Vuonna 2018 olemme hyödyntäneet Learning Lockeria ja xAPIa visualisointiin liittyvissä piloteissa, ja oppimisanalytiikkadatan hallinnassa järjestelmä on osoittautunut varsin toimivaksi.

Teknisestä näkökulmasta olemme jo hyvin perillä Learning Lockerin ja xAPIn mahdollisuuksista. Jatkamme jo alkaneiden pilottien parissa, sekä jakamaan niistä saatuja tuloksia laajemmin. Toki edessä on vielä selvitystyötä rajanpintojen parissa, kuten esimerkiksi Peppi-järjestelmän sisältämän datan integrointi Learning Lockeriin.

Kirjoittaja Anssi Gröhn, tietojenkäsittelyn lehtori