Spark의 ALS 권장 알고리즘 PlatoBlockchain Data Intelligence를 자세히 살펴봅니다. 수직 검색. 일체 포함.

Spark의 ALS 권장 사항 알고리즘으로 드릴

에 의해 도입 된 ALS 알고리즘 Huet al., 특히 암시 적 데이터 집합 (예 : 클릭, 좋아요 등)이있는 경우 Recommender 시스템 문제에 사용되는 매우 인기있는 기술입니다. 대량의 데이터를 합리적으로 잘 처리 할 수 ​​있으며 다양한 머신 러닝 프레임 워크에서 많은 훌륭한 구현을 찾을 수 있습니다. Spark에는 코드의 가독성과 아키텍처를 개선하기 위해 최근 리팩토링 된 MLlib 구성 요소의 알고리즘이 포함되어 있습니다.

Spark를 구현하려면 Item 및 User ID가 정수 범위 (정수 유형 또는 정수 범위 내의 Long) 내의 숫자 여야합니다. 이는 연산 속도를 높이고 메모리 소비를 줄이는 데 도움이되므로 합리적입니다. 코드를 읽는 동안 알아 차린 한 가지 사항은 해당 id 열이 적합 / 예측 방법의 시작 부분에서 Doubles로 캐스팅 된 다음 정수로 캐스트된다는 것입니다. 이것은 약간 해키처럼 보이고 가비지 수집기에 불필요한 부담을주는 것을 보았습니다. 여기에 라인이 있습니다 ALS 코드 ID를 두 배로 캐스트합니다.
Spark의 ALS 권장 알고리즘 PlatoBlockchain Data Intelligence를 자세히 살펴봅니다. 수직 검색. 일체 포함.
Spark의 ALS 권장 알고리즘 PlatoBlockchain Data Intelligence를 자세히 살펴봅니다. 수직 검색. 일체 포함.

이것이 왜 이루어지는 지 이해하려면 checkedCast ()를 읽어야합니다.
Spark의 ALS 권장 알고리즘 PlatoBlockchain Data Intelligence를 자세히 살펴봅니다. 수직 검색. 일체 포함.

이 UDF는 Double을 받고 범위를 확인한 다음 정수로 캐스트합니다. 이 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 ()를 보여주고, 값이 숫자인지 확인하고 그렇지 않으면 예외를 발생시킵니다. 입력이 Any이므로 나머지 코드에서 모든 캐스트를 Double 문으로 안전하게 제거 할 수 있습니다. 또한 ALS에는 정수 범위 내의 ID가 필요하기 때문에 대부분의 사람들이 실제로 정수 유형을 사용한다고 기대하는 것이 합리적입니다. 3 행의 결과로이 메소드는 캐스팅을 피하기 위해 정수를 명시 적으로 처리합니다. 다른 모든 숫자 값의 경우 입력이 정수 범위 내에 있는지 확인합니다. 이 점검은 7 행에서 이루어집니다.

허용되는 모든 유형을 다르게 작성하고 명시 적으로 처리 할 수 ​​있습니다. 불행히도 이로 인해 코드가 중복 될 수 있습니다. 대신 내가 여기서하는 일은 숫자를 정수로 변환하고 원래 숫자와 비교하는 것입니다. 값이 동일하면 다음 중 하나에 해당합니다.

  1. 값은 바이트 또는 짧은입니다.
  2. 값이 Long이지만 정수 범위 내에 있습니다.
  3. 값은 Double 또는 Float이지만 소수 부분은 없습니다.

코드가 제대로 실행되도록하기 위해 Spark의 표준 단위 테스트를 사용하여 코드를 테스트하고 다양한 법률 및 불법 값에 대한 메소드 동작을 확인하여 수동으로 테스트했습니다. 솔루션이 최소한 원본보다 빠른지 확인하기 위해 아래 스 니펫을 사용하여 여러 번 테스트했습니다. 이것은에 배치 할 수 있습니다 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
Fixed 100 566.722s 5.66722s

확인을 위해 실험을 여러 번 반복했으며 결과는 일관됩니다. 여기에서 하나의 실험에 대한 자세한 결과를 확인할 수 있습니다. 원래 코드 그리고 고정 된. 작은 데이터 세트의 경우 그 차이는 작지만 과거에는이 수정을 사용하여 GC 오버 헤드를 눈에 띄게 줄였습니다. Spark를 로컬로 실행하고 Spark 인스턴스에 Java 프로파일 러를 연결하여이를 확인할 수 있습니다. 나는 열었다 풀 요청 공식 Spark Repo에서 병합 될지 확실하지 않기 때문에 여기에서 공유 할 것으로 생각했습니다. 이제 Spark 2.2의 일부입니다.

모든 생각, 의견 또는 비난은 환영합니다! 🙂

타임 스탬프 :

더보기 데이텀 박스