Spark ML处理样本类别不均衡问题

样本类别分布不均衡导致的危害?

  • 样本类别不均衡将导致样本量少的分类所包含的特征过少,并很难从中提取规律;
  • 即使得到分类模型,也容易产生过度依赖与有限的数据样本而导致过拟合问题,当模型应用到新的数据上时,模型的准确性会很差

解决类别不平衡数据

1 过采样代表性算法-SMOTE
1.1 算法思想
  • 为了解决随机过采样中造成模型过拟合问题,⼜能保证实现数据集均衡的⽬的,出现了过采样法代表性的算法SMOTE 算法。
  • SMOTE算法是对随机过采样⽅法的⼀个改进算法,由于随机过采样⽅法是直接对少数类进⾏重采⽤,会使训练集中有 很多重复的样本,容易造成产⽣的模型过拟合问题。
  1. SMOTE合成少数样本的思想:
  2. 先随机选定⼀个少数类样本i;
  3. 找出这个少数类样本i的K个近邻(假设K=5),5个近邻已经被圈出;
  4. 随机从这K个近邻中选出⼀个样本j;
  5. 在少数类样本i和被选中的这个近邻样本j之间的连线上,随机找⼀点。这个点就是⼈⼯合成的新的样本点(绿⾊正号标出)
1.2 spark实现
def generateSamples1(data: RDD[(Long, Vector)], k: Int, N: Int): RDD[Vector] = {
    
    
    // 1.k-neighbours: 先分组,再笛卡尔积,排除自己减少计算
    val groupedRDD = data.groupBy(_._1)
    val vecAndNeis: RDD[(Vector, Array[Vector])] = groupedRDD.flatMap {
    
     case (id, iter) =>
      val vecArr = iter.toArray.map(_._2)

      //2.对每个vector产生笛卡尔积
      val cartesianArr: Array[(Vector, Vector)] = vecArr.flatMap(vec1 => {
    
    
        vecArr.map(vec2 => (vec1, vec2))
      }).filter(tuple => tuple._1 != tuple._2)

      cartesianArr.groupBy(_._1).map {
    
     case (vec, vecArr) => {
    
    
        (vec, vecArr.sortBy(x => Vectors.sqdist(x._1, x._2)).take(k).map(_._2))
      }
      }
    }

    // 3.从这k个近邻中随机挑选一个样本,以该随机样本为基准生成N个新样本
    vecAndNeis.flatMap {
    
     case (vec, neighbours) =>
      (1 to N).map {
    
     i =>
        val rn = neighbours(Random.nextInt(k))
        val diff = rn.copy
        BLASUDF.axpy(-1.0, vec, diff)

        val newVec = vec.copy
        BLASUDF.axpy(Random.nextDouble(), diff, newVec)
        newVec
      }.iterator
    }
  } 
2 欠采样
2.1 算法思想
  1. 即从多数类S中随机选择⼀些样样本组成样本集E
  2. 然后将样本集从S中移除。新的数据集
2.2 spark实现
// 1 找出样本多的类别
val faudDF = creaditDf.where("Class=1")
val faudNumber = BigDecimal(faudDF.count())
val normalDF = creaditDf.where("Class=0")
val normalNumber = BigDecimal(normalDF.count())

// 2 按比例随机采样
val ratio = faudNumber./(normalNumber)
val sampleNormalDF = normalDF.sample(false, ratio.toDouble, 10)
val underDF = faudDF.union(sampleNormalDF)
//数据标准化
val vecUnder = new VectorAssembler()
	.setInputCols(Array("Amount"))
	.setOutputCol("vectorAmount")
	.transform(underDF)

val scaler = new StandardScaler()
	.setInputCol("vectorAmount")
	.setOutputCol("scaledAmount")
	.setWithStd(true)
	.setWithMean(false)

val scalerModel = scaler.fit(vecUnder)
val scaledUnder = scalerModel.transform(vecUnder).drop("vectorAmount")

猜你喜欢

转载自blog.csdn.net/wolfjson/article/details/122057969
今日推荐