В машинном обучении при решении проблемы классификации с несбалансированным набором обучающих данных передискретизация и недостаточная выборка - два простых и часто эффективных способа улучшить результат.
Что такое несбалансированный набор данных
Количество примеров в одном классе в вашем наборе данных значительно больше, чем в другом классе. Это происходит во многих областях, например, в наборе данных по обнаружению мошенничества, у вас гораздо больше обычных транзакций, чем мошеннических. Или медицинская диагностика, где нормальных примеров больше, чем больных.
Такой набор данных может привести к тому, что ваша модель будет слепо предсказывать доминирующий класс, поскольку она в любом случае может достичь хорошей точности. Способы борьбы, которые включают избыточную выборку класса меньшинства, недостаточную выборку класса большинства, добавление веса класса, изменение алгоритма, создание синтетических выборок и т. Д.
В этой статье показано, как увеличить или уменьшить выборку в PySpark Dataframe.
Пример фрейма данных PySpark
Давайте настроим простой пример PySpark:
# code block 1 from pyspark.sql.functions import col, explode, array, lit df = spark.createDataFrame([['a',1],['b',1],['c',1],['d',1], ['e',1], ['f',1], ['x', 0], ['y', 0]], ['feature', 'label']) df.show() major_df = df.filter(col("label") == 1) minor_df = df.filter(col("label") == 0) ratio = int(major_df.count()/minor_df.count()) print("ratio: {}".format(ratio))
Выход:
+-------+-----+ |feature|label| +-------+-----+ | a| 1| | b| 1| | c| 1| | d| 1| | e| 1| | f| 1| | x| 0| | y| 0| +-------+-----+ ratio: 3
class (label) 1 содержит 6 примеров, тогда как class 0 имеет только 2 примера. Мы можем занижать выборку класса 1 или передискретизировать класс 0. Отношение равно 3.
Передискретизация
Идея передискретизации состоит в том, чтобы дублировать выборки из недостаточно представленного класса, увеличивать числа до тех пор, пока они не достигнут того же уровня, что и доминирующий класс. Вот как это сделать в PySpark Dataframe:
... skipped from code block 1 ... a = range(ratio) # duplicate the minority rows oversampled_df = minor_df.withColumn("dummy", explode(array([lit(x) for x in a]))).drop('dummy') # combine both oversampled minority rows and previous majority rows combined_df = major_df.unionAll(oversampled_df) combined_df.show()
Выход:
+-------+-----+ |feature|label| +-------+-----+ | a| 1| | b| 1| | c| 1| | d| 1| | e| 1| | f| 1| | x| 0| | x| 0| | x| 0| | y| 0| | y| 0| | y| 0| +-------+-----+
В приведенном выше коде мы используем функцию explode
Spark. Сначала мы создаем новый фиктивный столбец, содержащий буквальный массив чисел, причем размер массива является множителем, который мы хотим применить к строкам класса меньшинства. Затем функцияexplode
создает новую строку для каждого элемента в массиве. Последним опускаем фиктивный столбец.
Недостаточная выборка
Недостаточная выборка противоположна передискретизации, вместо того, чтобы создавать дубликаты класса меньшинства, она сокращает размер класса большинства. Для этого в PySpark есть встроенная функция sample
:
... skip from code block 1 ... sampled_majority_df = major_df.sample(False, 1/ratio) combined_df_2 = sampled_majority_df.unionAll(minor_df) combined_df_2.show()
Выход:
+-------+-----+ |feature|label| +-------+-----+ | a| 1| | b| 1| | x| 0| | y| 0| +-------+-----+
Недостаточная выборка уменьшает общий размер набора обучающих данных, поэтому вам следует попробовать этот подход, когда исходный набор данных довольно велик.