Apache Spark MLLib — запуск KMeans с векторами IDF-TF — пространство кучи Java

Я пытаюсь запустить KMeans на MLLib из (большой) коллекции текстовых документов (векторов TF-IDF). Документы отправляются через анализатор английского языка Lucene, а разреженные векторы создаются с помощью функции HashingTF.transform(). Какую бы степень параллелизма я ни использовал (через функцию объединения), KMeans.train всегда возвращает исключение OutOfMemory, указанное ниже. Любая мысль о том, как решить эту проблему?

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at scala.reflect.ManifestFactory$$anon$12.newArray(Manifest.scala:138)
at scala.reflect.ManifestFactory$$anon$12.newArray(Manifest.scala:136)
at breeze.linalg.Vector$class.toArray(Vector.scala:80)
at breeze.linalg.SparseVector.toArray(SparseVector.scala:48)
at breeze.linalg.Vector$class.toDenseVector(Vector.scala:75)
at breeze.linalg.SparseVector.toDenseVector(SparseVector.scala:48)
at breeze.linalg.Vector$class.toDenseVector$mcD$sp(Vector.scala:74)
at breeze.linalg.SparseVector.toDenseVector$mcD$sp(SparseVector.scala:48)
at org.apache.spark.mllib.clustering.BreezeVectorWithNorm.toDense(KMeans.scala:422)
at org.apache.spark.mllib.clustering.KMeans$$anonfun$initKMeansParallel$1.apply(KMeans.scala:285)
at org.apache.spark.mllib.clustering.KMeans$$anonfun$initKMeansParallel$1.apply(KMeans.scala:284)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:108)
at org.apache.spark.mllib.clustering.KMeans.initKMeansParallel(KMeans.scala:284)
at org.apache.spark.mllib.clustering.KMeans.runBreeze(KMeans.scala:143)
at org.apache.spark.mllib.clustering.KMeans.run(KMeans.scala:126)
at org.apache.spark.mllib.clustering.KMeans$.train(KMeans.scala:338)
at org.apache.spark.mllib.clustering.KMeans$.train(KMeans.scala:348)

person Antoine Amend    schedule 19.10.2014    source источник
comment
Можете ли вы проверить, связана ли проблема с тем, что памяти больше нет или вектор пытается создать слишком большой массив (например, что-то близкое к Integer.MAX_VALUE)? Это последовательно из метода newArray?   -  person Gábor Bakos    schedule 19.10.2014
comment
Он всегда исходит из одного и того же метода newArray. Используя HashingTF, векторы чертовски большие, но разреженные. Интересно, почему MLLib пытается преобразовать их в DenseVectors (это может быть проблемой)   -  person Antoine Amend    schedule 19.10.2014
comment
В соответствии с этим: github.com/apache/spark/blob/master/mllib/src/main/scala/org/ кажется, ваш размер слишком велик. Пробовали ли вы раньше уменьшать размеры? (Хотя для этого может потребоваться еще больше памяти, я не уверен.)   -  person Gábor Bakos    schedule 19.10.2014
comment
Я заметил то же самое. Реализации initKMeansParallel и initRandom создают плотную копию вектора моих центров, что вызывает ошибку нехватки памяти. Буду смотреть возможное уменьшение габаритов типа СВД. Спасибо, Габор.   -  person Antoine Amend    schedule 19.10.2014
comment
В зависимости от вашего варианта использования, вероятно, фильтрация наименее частых слов или сохранение только слов по теме может быть более дешевым вариантом, хотя SVD может дать лучшие (беспристрастные) результаты. См. также: stats.stackexchange.com/questions/24493/   -  person Gábor Bakos    schedule 21.10.2014
comment
Уменьшение размерности не решило проблему (см. ответ ниже) из-за природы векторов HashingTF. Я вручную создал свои разреженные векторы TF-IDF, теперь это работает как шарм. Ваше здоровье !   -  person Antoine Amend    schedule 21.10.2014
comment
Не могли бы вы поделиться своим способом создания векторов для TF-IDF? Я все еще сталкиваюсь с проблемой OutOfMemory при использовании SparseVectors в KMeans. An error occurred while calling o379.trainKMeansModel. : java.lang.OutOfMemoryError: Java heap space at org.apache.spark.mllib.linalg.SparseVector.toArray(Vectors.scala:523) at org.apache.spark.mllib.clustering.KMeans$$anonfun$initRandom$1$$anonfun$apply$7.apply(KMeans.scala:267)   -  person fanfabbb    schedule 22.04.2015


Ответы (1)


После некоторых расследований выясняется, что эта проблема была связана с методом new HashingTF().transform(v). Хотя создание разреженных векторов с помощью трюка с хешированием действительно полезно (особенно когда количество объектов неизвестно), вектор должен оставаться разреженным. Размер по умолчанию для векторов HashingTF — 2^20. Учитывая 64-битную двойную точность, для каждого вектора теоретически потребуется 8 МБ при преобразовании в плотный вектор — независимо от того, какое уменьшение размерности мы можем применить.

К сожалению, KMeans использует метод toDense (по крайней мере, для центров кластера), поэтому вызывает ошибку OutOfMemory (представьте, что k = 1000).

  private def initRandom(data: RDD[BreezeVectorWithNorm]) : Array[Array[BreezeVectorWithNorm]] = {
    val sample = data.takeSample(true, runs * k, new XORShiftRandom().nextInt()).toSeq
    Array.tabulate(runs)(r => sample.slice(r * k, (r + 1) * k).map { v =>
      new BreezeVectorWithNorm(v.vector.toDenseVector, v.norm)
    }.toArray)
  }
person Antoine Amend    schedule 21.10.2014