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

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

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

В трубопроводе установлен учебный комплект.

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

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

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

Ответ 1

Боюсь, что у меня нет хорошего способа сделать это. Либо
  • отфильтруйте примеры тестов с помощью неизвестных меток перед применением 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)))

Ответ 2

В Spark 1.6 есть путь вокруг.

Здесь jira: 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"

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

Вам понадобится это позже в конвейере, когда вы конвертируете 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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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