קדיחה באלגוריתם המלצות ALS של Spark PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

קידוח באלגוריתם של המלצת ALS של Spark

אלגוריתם ה- ALS שהוצג על ידי הוא ואח '., היא טכניקה פופולרית מאוד המשמשת בבעיות מערכת ממליצים, במיוחד כאשר יש לנו מערכי נתונים מרומזים (למשל קליקים, סימוני 'אהבתי' וכו '). זה יכול לטפל בכמויות גדולות של נתונים בצורה סבירה ואנחנו יכולים למצוא יישומים טובים רבים במסגרות שונות של למידת מכונות. הניצוץ כולל את האלגוריתם ברכיב MLlib שוחזר לאחרונה כדי לשפר את הקריאות ואת הארכיטקטורה של הקוד.

יישום Spark מחייב שמזהה הפריט והמשתמש יהיו מספרים בטווח שלם (סוג מספר שלם או ארוך בטווח שלם), וזה סביר מכיוון שזה יכול לעזור להאיץ את הפעולות ולהפחית את צריכת הזיכרון. דבר אחד ששמתי לב אליו בזמן שקראתי את הקוד הוא שאותן מוטרפים עמודות זהות לדאבלס ואז למספרים שלמים בתחילת שיטות ההתאמה / החיזוי. זה נראה קצת פרוץ וראיתי שזה מעביר מאמץ מיותר על אספן האשפה. להלן השורות על קוד ALS שהטיל את המזהים לדאבל:
קדיחה באלגוריתם המלצות ALS של Spark PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.
קדיחה באלגוריתם המלצות ALS של Spark PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

כדי להבין מדוע הדבר נעשה, יש לקרוא את ה- CheckCast ():
קדיחה באלגוריתם המלצות ALS של Spark PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

UDF זה מקבל כפול ובודק את הטווח שלו ואז מעביר אותו למספר שלם. UDF זה משמש לאימות סכמות. השאלה היא האם ניתן להשיג זאת מבלי להשתמש ביציקות כפולות מכוערות? אני מאמין שכן:

  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.")
    }
  }

הקוד שלמעלה מציג צ'ק קסט שונה () שמקבל את הקלט, בודק טוען שהערך מספרי ומעלה חריגות אחרת. מכיוון שהקלט הוא כל, אנו יכולים להסיר בבטחה את כל ההצהרות לדוחות הכפולים משאר הקוד. יתרה מזאת, סביר להניח שמכיוון ש- ALS דורש תעודות זהות בטווח שלם שלם, רוב האנשים משתמשים בפועל בסוגי מספרים שלמים. כתוצאה מקו 3 שיטה זו מטפלת במלוא המספרים השלמים במפורש כדי להימנע מביצוע כל ליהוק. עבור כל שאר הערכים המספריים הוא בודק אם הקלט נמצא בטווח שלם שלם. בדיקה זו מתרחשת בקו 7.

אפשר לכתוב זאת בצורה אחרת ולהתמודד במפורש עם כל הסוגים המותרים. לרוע המזל זה יוביל לכפיל קוד. במקום זאת מה שאני עושה כאן הוא להמיר את המספר למספר שלם ולהשוות אותו למספר המקורי. אם הערכים זהים, אחד הדברים הבאים נכון:

  1. הערך הוא בייט או קצר.
  2. הערך ארוך אך בטווח מספר שלם.
  3. הערך כפול או צף אך ללא כל חלק שברירי.

כדי להבטיח שהקוד יפעל כראוי, בדקתי אותו במבחני היחידה הסטנדרטיים של ספארק ובאופן ידני על ידי בדיקת התנהגות השיטה לערכים חוקיים ולא חוקיים שונים. כדי להבטיח שהפתרון יהיה מהיר לפחות כמו המקור, בדקתי מספר פעמים באמצעות הקטע שלמטה. זה יכול להיות ממוקם ב כיתת ALSSuite בספארק:

  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")

  }

לאחר מספר בדיקות אנו יכולים לראות שהתיקון החדש מהיר מעט יותר מהמקור:

קופונים

מספר המירוצים

זמן ביצוע כולל

זמן ביצוע ממוצע לכל ריצה

מְקוֹרִי 100 588.458s 5.88458s
תוקן 100 566.722s 5.66722s

חזרתי על הניסויים מספר פעמים כדי לאשר והתוצאות עקביות. כאן תוכלו למצוא את הפלט המפורט של ניסוי אחד לניסוי קוד מקורי - - ו קבוע. ההבדל הוא קטן עבור מערך נתונים זעיר אבל בעבר הצלחתי להשיג צמצום בולט מעל התקנים של GC באמצעות תיקון זה. אנו יכולים לאשר זאת על ידי הפעלת Spark באופן מקומי וחיבור פרופיל Java במופע ה- Spark. פתחתי א כרטיס וכן בקשת משיכה ברפאו הניצוץ הרשמי אבל מכיוון שלא בטוח אם זה יתמזג, חשבתי לשתף אתכם כאן כאן וזה עכשיו חלק מ- Spark 2.2.

כל מחשבות, הערות או ביקורת יתקבלו בברכה! 🙂

בול זמן:

עוד מ דטומבוקס