类状态在Flink中的函数调用之间丢失

时间:2018-07-02 11:34:26

标签: scala apache-flink

我上了这个课:

case class IDADiscretizer(
  nAttrs: Int,
  nBins: Int = 5,
  s: Int = 5) extends Serializable {

  private[this] val log = LoggerFactory.getLogger(this.getClass)
  private[this] val V = Vector.tabulate(nAttrs)(i => new IntervalHeapWrapper(nBins, i))
  private[this] val randomReservoir = SamplingUtils.reservoirSample((1 to s).toList.iterator, 1)

  def updateSamples(v: LabeledVector): Vector[IntervalHeapWrapper] = {
    val attrs = v.vector.map(_._2)
    val label = v.label
    // TODO: Check for missing values
    attrs
      .zipWithIndex
      .foreach {
        case (attr, i) =>
          if (V(i).getNbSamples < s) {
            V(i) insertValue attr // insert
          } else {
            if (randomReservoir(0) <= s / (i + 1)) {
              //val randVal = Random nextInt s
              //V(i) replace (randVal, attr)
              V(i) insertValue attr
            }
          }
      }
    V
  }

  /**
   * Return the cutpoints for the discretization
   *
   */
  def cutPoints: Vector[Vector[Double]] = V map (_.getBoundaries.toVector)

  def discretize(data: DataSet[LabeledVector]): (DataSet[Vector[IntervalHeapWrapper]], Vector[Vector[Double]]) = {
    val r = data map (x => updateSamples(x))
    val c = cutPoints

    (r, c)
  }
}

使用flink,我想在调用discretize之后获得切点,但是似乎存储在V中的信息会丢失。我是否必须像在this question中那样使用Broadcast?有没有更好的方法来访问类状态?

我尝试用两种方式致电cutpoints,一种方式是:

def discretize(data: DataSet[LabeledVector]) = data map (x => updateSamples(x))

然后,从外面打电话:

val a = IDADiscretizer(nAttrs = 4)
val r = a.discretize(dataSet)
r.print
val cuts = a.cutPoints

这里,cuts为空,因此我尝试计算离散化以及discretize内的cutpoints:

def discretize(data: DataSet[LabeledVector]) = {
    val r = data map (x => updateSamples(x))
    val c = cutPoints

    (r, c)
  }

并像这样使用它:

val a = IDADiscretizer(nAttrs = 4)
val (d, c) = a.discretize(dataSet)
c foreach println

但是同样的事情发生。

最后,我还试图将V完全公开:

val V = Vector.tabulate(nAttrs)(i => new IntervalHeapWrapper(nBins, i))

仍然为空

我在做什么错了?

相关问题:

答案

多亏@TillRohrmann,我终于做到了:

private[this] def computeCutPoints(x: LabeledVector) = {
    val attrs = x.vector.map(_._2)
    val label = x.label
    attrs
      .zipWithIndex
      .foldLeft(V) {
        case (iv, (v, i)) =>
          iv(i) insertValue v
          iv
      }
  }

  /**
   * Return the cutpoints for the discretization
   *
   */
  def cutPoints(data: DataSet[LabeledVector]): Seq[Seq[Double]] =
    data.map(computeCutPoints _)
      .collect
      .last.map(_.getBoundaries.toVector)

  def discretize(data: DataSet[LabeledVector]): DataSet[LabeledVector] =
data.map(updateSamples _)

然后像这样使用它:

val a = IDADiscretizer(nAttrs = 4)
val d = a.discretize(dataSet)
val cuts = a.cutPoints(dataSet)
d.print
cuts foreach println

我不知道这是否是最好的方法,但至少现在可以正常工作。

1 个答案:

答案 0 :(得分:1)

Flink的工作方式是用户定义运算符/用户定义的函数,这些函数对来自源函数的输入数据进行运算。为了执行程序,将用户代码发送到Flink集群,并在其中执行该代码。计算结果必须通过接收器功能输出到某些存储系统。

由于这个原因,在尝试解决方案时,不可能轻松地混合使用本地计算和分布式计算。 discretize的作用是定义一个map运算符,该运算符将转换输入DataSet data。例如,一旦您调用ExecutionEnvironment#executeDataSet#print,就会执行此操作。现在,用户代码和IDADiscretizer的定义被发送到实例化它们的集群。 Flink将更新IDADiscretizer实例中的值,该实例与您在客户端上的实例不同。