स्पार्क के एएलएस अनुशंसा एल्गोरिदम प्लेटोब्लॉकचैन डेटा इंटेलिजेंस में ड्रिलिंग। लंबवत खोज। ऐ.

स्पार्क के एएलएस सिफारिश एल्गोरिथ्म में ड्रिलिंग

ALS एल्गोरिथ्म द्वारा पेश किया गया हू एट अल।, एक बहुत लोकप्रिय तकनीक है जिसका उपयोग अनुशंसित प्रणाली की समस्याओं में किया जाता है, खासकर जब हमारे पास अंतर्निहित डेटासेट होते हैं (उदाहरण के लिए क्लिक, पसंद आदि)। यह डेटा की बड़ी मात्रा को अच्छी तरह से संभाल सकता है और हम विभिन्न मशीन लर्निंग फ्रेमवर्क में कई अच्छे कार्यान्वयन पा सकते हैं। स्पार्क में एमएललिब घटक में एल्गोरिथ्म शामिल है जिसे हाल ही में कोड की पठनीयता और वास्तुकला में सुधार के लिए फिर से तैयार किया गया है।

स्पार्क के कार्यान्वयन के लिए आइटम और उपयोगकर्ता आईडी को पूर्णांक सीमा (या तो पूर्णांक श्रेणी या पूर्णांक सीमा के भीतर लंबे समय तक) के लिए नंबर की आवश्यकता होती है, जो उचित है क्योंकि यह संचालन को गति देने और स्मृति की खपत को कम करने में मदद कर सकता है। हालांकि कोड पढ़ते समय एक बात मुझे ध्यान में आई कि उन आईडी कॉलम को डबल्स में डाला जा रहा है और फिर फिट / प्रेडिक्ट मेथड्स की शुरुआत में इंटिजर्स में। यह थोड़ा हैकरी लगता है और मैंने देखा है कि यह कचरा संग्रहकर्ता पर अनावश्यक दबाव डालता है। यहाँ पर लाइनें हैं ALS कोड कि डबल्स में आईडी डाली:
स्पार्क के एएलएस अनुशंसा एल्गोरिदम प्लेटोब्लॉकचैन डेटा इंटेलिजेंस में ड्रिलिंग। लंबवत खोज। ऐ.
स्पार्क के एएलएस अनुशंसा एल्गोरिदम प्लेटोब्लॉकचैन डेटा इंटेलिजेंस में ड्रिलिंग। लंबवत खोज। ऐ.

यह समझने के लिए कि ऐसा क्यों किया जाता है, किसी को जाँच की आवश्यकता है ():
स्पार्क के एएलएस अनुशंसा एल्गोरिदम प्लेटोब्लॉकचैन डेटा इंटेलिजेंस में ड्रिलिंग। लंबवत खोज। ऐ.

यह यूडीएफ एक डबल प्राप्त करता है और इसकी सीमा की जांच करता है और फिर इसे पूर्णांक में डाल देता है। यह यूडीएफ स्कीमा सत्यापन के लिए उपयोग किया जाता है। सवाल यह है कि क्या हम बदसूरत दोहरी कास्टिंग का उपयोग किए बिना इसे प्राप्त कर सकते हैं? मुझे विश्वास है हाँ:

  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

मैंने पुष्टि करने के लिए कई बार प्रयोगों को दोहराया और परिणाम सुसंगत हैं। यहाँ आप के लिए एक प्रयोग का विस्तृत उत्पादन पा सकते हैं मूल कोड और स्थिर। यह अंतर एक छोटे डेटासेट के लिए छोटा है, लेकिन अतीत में मैंने इस फिक्स का उपयोग करके जीसी ओवरहेड में ध्यान देने योग्य कमी हासिल करने में कामयाबी हासिल की है। हम स्थानीय रूप से स्पार्क को चलाकर और स्पार्क उदाहरण पर जावा प्रोफाइलर को संलग्न करके इसकी पुष्टि कर सकते हैं। मैंने एक खोला टिकट और एक पुल अनुरोध आधिकारिक स्पार्क रेपो पर लेकिन क्योंकि यह अनिश्चित है अगर इसे विलय कर दिया जाएगा, तो मैंने सोचा कि इसे आपके साथ साझा करूं और यह अब स्पार्क 2.2 का हिस्सा है।

किसी भी विचार, टिप्पणी या आलोचना का स्वागत है! 🙂

समय टिकट:

से अधिक दातुनॉक्स