اسپارک کے ALS سفارشی الگورتھم پلاٹو بلاکچین ڈیٹا انٹیلی جنس میں سوراخ کرنا۔ عمودی تلاش۔ عی

اسپارک کے ALS سفارشی الگورتھم میں سوراخ کرنا

ALS الگورتھم نے متعارف کرایا ہوٹ ایٹ., Recommender System کے مسائل میں استعمال ہونے والی ایک بہت مشہور تکنیک ہے، خاص طور پر جب ہمارے پاس مضمر ڈیٹا سیٹس ہوں (مثال کے طور پر کلکس، لائکس وغیرہ)۔ یہ ڈیٹا کی بڑی مقدار کو مناسب طریقے سے سنبھال سکتا ہے اور ہمیں مشین لرننگ کے مختلف فریم ورکس میں بہت سے اچھے نفاذات مل سکتے ہیں۔ اسپارک میں MLlib جزو میں الگورتھم شامل ہے جسے حال ہی میں پڑھنے کی اہلیت اور کوڈ کے فن تعمیر کو بہتر بنانے کے لیے ری فیکٹر کیا گیا ہے۔

اسپارک کے نفاذ کے لیے ضروری ہے کہ آئٹم اور یوزر آئی ڈی انٹیجر رینج کے اندر نمبر ہوں (یا تو انٹیجر قسم یا انٹیجر رینج کے اندر لمبی)، جو کہ معقول ہے کیونکہ اس سے آپریشنز کو تیز کرنے اور میموری کی کھپت کو کم کرنے میں مدد مل سکتی ہے۔ کوڈ کو پڑھتے ہوئے ایک چیز جو میں نے محسوس کی وہ یہ ہے کہ ان آئی ڈی کالموں کو فٹ/پیش گوئی کے طریقوں کے آغاز میں ڈبلز اور پھر انٹیجرز میں کاسٹ کیا جا رہا ہے۔ یہ تھوڑا سا ہیکی لگتا ہے اور میں نے دیکھا ہے کہ اس نے کوڑا اٹھانے والے پر غیر ضروری دباؤ ڈالا ہے۔ یہاں پر لائنیں ہیں ALS کوڈ جو آئی ڈی کو ڈبلز میں ڈالتا ہے:
اسپارک کے ALS سفارشی الگورتھم پلاٹو بلاکچین ڈیٹا انٹیلی جنس میں سوراخ کرنا۔ عمودی تلاش۔ عی
اسپارک کے ALS سفارشی الگورتھم پلاٹو بلاکچین ڈیٹا انٹیلی جنس میں سوراخ کرنا۔ عمودی تلاش۔ عی

یہ سمجھنے کے لیے کہ ایسا کیوں کیا جاتا ہے، کسی کو checkedCast():
اسپارک کے ALS سفارشی الگورتھم پلاٹو بلاکچین ڈیٹا انٹیلی جنس میں سوراخ کرنا۔ عمودی تلاش۔ عی

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

اوپر کا کوڈ ایک ترمیم شدہ checkedCast() دکھاتا ہے جو ان پٹ وصول کرتا ہے، چیک اس بات پر زور دیتا ہے کہ قدر عددی ہے اور دوسری صورت میں مستثنیات کو بڑھاتا ہے۔ چونکہ ان پٹ کوئی بھی ہے، ہم بقیہ کوڈ سے تمام کاسٹ ٹو ڈبل اسٹیٹمنٹس کو محفوظ طریقے سے ہٹا سکتے ہیں۔ مزید یہ کہ یہ توقع کرنا مناسب ہے کہ چونکہ ALS کو عدد کی حد کے اندر ids کی ضرورت ہوتی ہے، اس لیے لوگوں کی اکثریت دراصل عددی اقسام کا استعمال کرتی ہے۔ لائن 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 2.2 کا حصہ ہے۔

کسی بھی خیالات، تبصرے یا تنقید کا خیرمقدم ہے! 🙂

ٹائم اسٹیمپ:

سے زیادہ ڈیٹا باکس