Spark, ML, StringIndexer: обработка невидимых меток

моя цель-создать классификатор multicalss.

Я построил конвейер для извлечения объектов, и он включает в себя в качестве первого шага трансформатор StringIndexer для сопоставления каждого имени класса с меткой, эта метка будет использоваться на этапе обучения классификатора.

трубопровод приспособлен набору тренировки.

тестовый набор должен обрабатываться установленным конвейером для извлечения тех же векторов объектов.

зная, что мои файлы набора тестов имеют одинаковую структуру тренировочного набора. Возможный сценарий здесь-столкнуться с невидимым именем класса в тестовом наборе, в этом случае StringIndexer не сможет найти метку, и возникнет исключение.

есть ли решение для этого случая? или как мы можем избежать этого?

5 ответов


боюсь, это не лучший способ сделать это. Либо

  • отфильтруйте тестовые примеры с неизвестными метками перед применением StringIndexer
  • или StringIndexer к соединению поезда и тестового фрейма данных, поэтому вы уверены, что все метки есть
  • или преобразовать тестовый пример с неизвестной меткой в известную метку

вот пример кода для выполнения вышеуказанных операций:

// get training labels from original train dataframe
val trainlabels = traindf.select(colname).distinct.map(_.getString(0)).collect  //Array[String]
// or get labels from a trained StringIndexer model
val trainlabels = simodel.labels 

// define an UDF on your dataframe that will be used for filtering
val filterudf = udf { label:String => trainlabels.contains(label)}

// filter out the bad examples 
val filteredTestdf = testdf.filter( filterudf(testdf(colname)))

// transform unknown value to some value, say "a"
val mapudf = udf { label:String => if (trainlabels.contains(label)) label else "a"}

// add a new column to testdf: 
val transformedTestdf = testdf.withColumn( "newcol", mapudf(testdf(colname)))

есть способ обойти это в Spark 1.6.

вот Джира:https://issues.apache.org/jira/browse/SPARK-8764

вот пример:

val categoryIndexerModel = new StringIndexer()
  .setInputCol("category")
  .setOutputCol("indexedCategory")
  .setHandleInvalid("skip") // new method.  values are "error" or "skip"

Я начал использовать это, но в конечном итоге вернулся ко второму пуле Криспа о подгонке этой конкретной оценки к полному набору данных.

вам понадобится это позже в конвейере при преобразовании IndexToString.

вот измененный пример:

val categoryIndexerModel = new StringIndexer()
  .setInputCol("category")
  .setOutputCol("indexedCategory")
  .fit(itemsDF) // Fit the Estimator and create a Model (Transformer)

... do some kind of classification ...

val categoryReverseIndexer = new IndexToString()
  .setInputCol(classifier.getPredictionCol)
  .setOutputCol("predictedCategory")
  .setLabels(categoryIndexerModel.labels) // Use the labels from the Model

С Spark 2.2 (выпущен 7-2017) вы можете использовать .setHandleInvalid("keep") опция при создании индексатора. С помощью этой опции индексатор добавляет новые индексы при появлении новых меток. Обратите внимание, что в предыдущих версиях у вас также есть "skip" опция, которая заставляет индексатор игнорировать (удалять) строки с новыми метками.

val categoryIndexerModel = new StringIndexer()
  .setInputCol("category")
  .setOutputCol("indexedCategory")
  .setHandleInvalid("keep") // options are "keep", "error" or "skip"

в моем случае я запускал spark ALS на большом наборе данных, и данные не были доступны на всех разделах, поэтому мне пришлось кэшировать () данные соответствующим образом, и это сработало как шарм


для меня, полностью игнорируя строки, задавая аргумент (https://issues.apache.org/jira/browse/SPARK-8764) на самом деле нецелесообразно решать эту проблему.

в итоге я создал свой собственный трансформатор CustomStringIndexer, который назначит новое значение для всех новых строк, которые не встречались во время обучения. Вы также можете сделать это, изменив соответствующие части кода функции spark(просто удалите условие if, явно проверяющее это, и сделайте вместо этого он возвращает длину массива) и перекомпилирует jar.

не очень простое исправление, но это, безусловно, исправление.

Я помню, что видел ошибку в JIRA, чтобы включить это:https://issues.apache.org/jira/browse/SPARK-17498

Он настроен на выпуск с Spark 2.2. Просто нужно подождать, я думаю: S