- 25 Februarie 2017
- Vasilis Vryniotis
- . 3 comentarii
Algoritmul ALS introdus de Hu și colab., este o tehnică foarte populară folosită în problemele sistemului de recomandare, mai ales când avem seturi de date implicite (de exemplu clicuri, aprecieri etc.). Poate gestiona destul de bine volume mari de date și putem găsi multe implementări bune în diferite cadre de învățare automată. Spark include algoritmul în componenta MLlib, care a fost recent refactorizat pentru a îmbunătăți lizibilitatea și arhitectura codului.
Implementarea Spark necesită ca Elementul și ID-ul utilizatorului să fie numere în intervalul întreg (fie tip întreg, fie lung în intervalul întreg), ceea ce este rezonabil deoarece acest lucru poate ajuta la accelerarea operațiunilor și la reducerea consumului de memorie. Un lucru pe care l-am observat totuși în timp ce citesc codul este că acele coloane de id sunt turnate în duble și apoi în numere întregi la începutul metodelor de potrivire/predire. Acest lucru pare puțin hacker și am văzut că pune o presiune inutilă pe colectorul de gunoi. Iată rândurile de pe Cod ALS care aruncă id-urile în duble:
Pentru a înțelege de ce se face acest lucru, trebuie să citiți checkedCast():
Acest UDF primește un Double și își verifică intervalul și apoi îl transformă în număr întreg. Acest UDF este utilizat pentru validarea Schemei. Întrebarea este: putem realiza acest lucru fără a folosi piese turnate duble urâte? eu cred ca da:
protected val checkedCast = udf { (n: Any) => n match { case v: Int => v // Avoid unnecessary casting case v: Number => val intV = v.intValue() // True for Byte/Short, Long within the Int range and Double/Float with no fractional part. if (v.doubleValue == intV) { intV } else { throw new IllegalArgumentException(s"ALS only supports values in Integer range " + s"for columns ${$(userCol)} and ${$(itemCol)}. Value $n was out of Integer range.") } case _ => throw new IllegalArgumentException(s"ALS only supports values in Integer range " + s"for columns ${$(userCol)} and ${$(itemCol)}. Value $n is not numeric.") } }
Codul de mai sus arată un checkedCast() modificat care primește intrarea, verifică afirmă că valoarea este numerică și ridică excepții în caz contrar. Deoarece intrarea este Any, putem elimina în siguranță toate declarațiile cast to Double din restul codului. În plus, este rezonabil să ne așteptăm că, deoarece ALS necesită ID-uri în intervalul întreg, majoritatea oamenilor folosesc de fapt tipuri de numere întregi. Ca rezultat, pe linia 3, această metodă tratează în mod explicit numerele întregi pentru a evita orice turnare. Pentru toate celelalte valori numerice, verifică dacă intrarea este în intervalul întreg. Această verificare are loc pe linia 7.
S-ar putea scrie acest lucru diferit și s-ar putea gestiona în mod explicit toate tipurile permise. Din păcate, acest lucru ar duce la un cod duplicat. În schimb, ceea ce fac aici este să convertesc numărul în întreg și să-l compar cu numărul original. Dacă valorile sunt identice, una dintre următoarele este adevărată:
- Valoarea este Byte sau Short.
- Valoarea este lungă, dar în intervalul întreg.
- Valoarea este Double sau Float, dar fără nicio parte fracțională.
Pentru a mă asigura că codul rulează bine, l-am testat cu testele unitare standard ale Spark și manual, verificând comportamentul metodei pentru diferite valori legale și ilegale. Pentru a ne asigura că soluția este cel puțin la fel de rapidă ca cea originală, am testat de mai multe ori folosind fragmentul de mai jos. Acesta poate fi plasat în Clasa ALSSuite în Spark:
test("Speed difference") { val (training, test) = genExplicitTestData(numUsers = 200, numItems = 400, rank = 2, noiseStd = 0.01) val runs = 100 var totalTime = 0.0 println("Performing "+runs+" runs") for(i <- 0 until runs) { val t0 = System.currentTimeMillis testALS(training, test, maxIter = 1, rank = 2, regParam = 0.01, targetRMSE = 0.1) val secs = (System.currentTimeMillis - t0)/1000.0 println("Run "+i+" executed in "+secs+"s") totalTime += secs } println("AVG Execution Time: "+(totalTime/runs)+"s") }
După câteva teste, putem vedea că noua remediere este puțin mai rapidă decât cea originală:
Cod |
Numărul de rulări |
Timpul total de execuție |
Timp mediu de execuție per rundă |
Original | 100 | Anii 588.458 | Anii 5.88458 |
Fixed | 100 | Anii 566.722 | Anii 5.66722 |
Am repetat experimentele de mai multe ori pentru a confirma și rezultatele sunt consistente. Aici puteți găsi rezultatul detaliat al unui experiment pentru codul original si stabili. Diferența este mică pentru un set de date mic, dar în trecut am reușit să obțin o reducere vizibilă a supraîncărcării GC folosind această remediere. Putem confirma acest lucru rulând Spark local și atașând un profiler Java pe instanța Spark. am deschis un bilet și Pull-Request pe depozitul oficial Spark dar pentru că nu este sigur dacă va fi fuzionat, m-am gândit să vi-l împărtășesc aici și acum face parte din Spark 2.2.
Orice gânduri, comentarii sau critici sunt binevenite! 🙂
- AI
- ai art
- ai art generator
- ai robot
- inteligență artificială
- certificare de inteligență artificială
- robot cu inteligență artificială
- roboți cu inteligență artificială
- software de inteligență artificială
- blockchain
- conferință blockchain ai
- coingenius
- inteligența artificială conversațională
- criptoconferință ai
- dall-e
- Datumbox
- învățare profundă
- google ai
- masina de învățare
- Învățare automată și statistici
- Plato
- platoul ai
- Informații despre date Platon
- Jocul lui Platon
- PlatoData
- platogaming
- Programare
- scara ai
- sintaxă
- zephyrnet