Flink DataSet API - Itreation

迭代

迭代算法出现在数据分析的许多领域,如机器学习或图分析。这些算法对于实现大数据从数据中提取有意义信息的承诺至关重要。随着人们越来越有兴趣将这些算法在非常大的数据集上运行,则需要以大规模并行的方式执行迭代。

Flink通过定义一个迭代函数(step function)并将其嵌入到一个特殊的迭代操作符中来实现迭代算法。主要分为以下两类:Iterate和Delta Iterate。这两个操作符都在当前迭代状态上反复调用迭代函数,直到达到终止条件。

Iterate Operator

Iterate Operator是迭代的简单形式:在每个迭代中,step function使用整个输入(前一个迭代的结果,或初始数据集),并计算得到局部结果(例如map、reduce、join等)。
在这里插入图片描述

  1. Iteration Input(初始迭代输入):来自数据源或数据源第一次通过迭代器运算的结果。
  2. Step Function(迭代函数): 每一轮迭代将调用迭代函数操纵输入的数据集,如map,reduce。
  3. Next Partial Solution(迭代输出): 在每次迭代中,step funtion的输出将作为下一轮迭代的输入.
  4. Iteration Result(迭代结果): 迭代终止时的输出,因此必须定义迭代终止条件,通常有以下两种形式:
    • Maximum number of iterations(最大迭代次数): 如果没有其他终止条件,迭代将在最大次数以内结束.
    • Custom aggregator convergence(自定义收敛聚合函数):可自定义聚合函数和收敛条件。
示例:用蒙特卡罗方法计算π

蒙洛卡特思想的核心就是:假设这里有一个半径为1的圆,它的面积S=PiR2=Pi,所以我们只要计算出这个圆的面积就可以计算出圆周率了。这里我们可以在一个边长为1的正方形中计算圆的四分之一扇形的面积,这样扇形的面积的4倍就是整个圆的面积了。如何计算扇形的面积?可以使用概率的方法,假设在这个正方形中有n个点,那么有m个点落在了扇形中,那么S扇形:S正方形=m:n。这样就可以计算出扇形的面积,最终计算出圆周率了。

package com.ztland.flink_demo.dataSet;

import java.util.Random;

import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.IterativeDataSet;

public class ItreationDemo {

	public static void main(String[] args) throws Exception {

		final ExecutionEnvironment env=ExecutionEnvironment.getExecutionEnvironment();

		Random random=new Random(1);
		// 设置迭代次数
		IterativeDataSet<Integer> iterativeDataSet=env.fromElements(0).iterate(100000);

		DataSet<Integer> mapResult=iterativeDataSet.map(new MapFunction<Integer, Integer>() {
			@Override
			public Integer map(Integer value) throws Exception {
				double x=random.nextDouble();
				double y=random.nextDouble();
				value+=(x*x+y*y<=1)?1:0;
				return value;
			}
		});

		//迭代结束的条件
		DataSet<Integer> result=iterativeDataSet.closeWith(mapResult);
		result.map(new MapFunction<Integer, Double>() {
			@Override
			public Double map(Integer count) throws Exception {
				return count/(double)100000*4;
			}
		}).print();
	}
}

示例:递增数字

在下面的例子中,我们迭代地增加一个集合中的数值:
在这里插入图片描述

  1. Iteration Input:初始数据从数据源读取,由5条单字段记录组成(整数1到5)。

  2. Step Function: step function是一个map(),它将数据源中每个integer字段从i递增到i+1。

  3. Next Partial Solution: step function的输出将是map()的输出,即递增整数。

  4. Iteration Result: 经过10次迭代,初始数将增加10,得到整数11到15.

    // 1st           2nd                       10th
    map(1) -> 2      map(2) -> 3      ...      map(10) -> 11
    map(2) -> 3      map(3) -> 4      ...      map(11) -> 12
    map(3) -> 4      map(4) -> 5      ...      map(12) -> 13
    map(4) -> 5      map(5) -> 6      ...      map(13) -> 14
    map(5) -> 6      map(6) -> 7      ...      map(14) -> 15
    

Delta Iterate Operator

这种迭代方式称为增量迭代,它并不是每次去迭代全量的数据,而只是迭代一部分热点数据。通常热点数据相对数据总数来书较小,如下图所示:
在这里插入图片描述

  1. Iteration Input(初始迭代输入):来自数据源或数据源第一次通过迭代器运算的结果。
  2. Step Function(迭代函数): 每一轮迭代将调用迭代函数操纵输入的数据集,如map,reduce。
  3. Next Workset/Update Solution Set(迭代输出Workset、更新Solution Set): 每一轮将迭代作用于Workset,并迭代输出的Workset作为下一轮迭代的Workset。此外,每一轮跌打计算过程中,均可能更新迭代输入的Solution Set。
  4. Iteration Result(迭代结果): 迭代终止时的输出Solution Set,终止条件默认为Workset为空或达到最大迭代次数。
示例:连通体最小传播值
  1. 初始输入为两个数据集,每个数据集对应一个连通域,分别称为上连通域和下连通域。
  2. 每一个元素都有自己的颜色特征,且各不相同。
  3. 每个元素对应图中的一个顶点,由三元组组成,即顶点ID、顶点数值、顶点特征。其中顶点数值和顶点ID同为图中的数字
    在这里插入图片描述

整个迭代过程如下:

  1. 第一次迭代中,ID为1的顶点将特征传播给ID为2的顶点,ID为2的顶点将特征传播给ID为3、4的顶点;ID为5的顶点传递给ID为6、7的顶点。ID1、5已经特征传递出去,所以不再参加后续迭代,即不再是wordSet的元素。
  2. 第二次迭代时下连通域的workSet仅有6、7两个顶点,进行一轮迭代传播特征就收敛了。由于此时两个顶点的特征都来自于5,所以进行传播的过程并没有发生实质替换。
  3. 第三次迭代将上连通域ID为2的顶点的特征,即初始状态为1的顶点特征,传播给ID为3、4的顶点。

上述过程中workset元素的数量是递减的,为空时迭代结束。
代码如下:

package com.ztland.flink_demo.dataSet;

import org.apache.flink.api.common.functions.FlatJoinFunction;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.JoinFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.aggregation.Aggregations;
import org.apache.flink.api.java.operators.DeltaIteration;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.util.Collector;

public class ItreationDemo {

    public static void main(String[] args) throws Exception {
    	
        final ExecutionEnvironment env=ExecutionEnvironment.getExecutionEnvironment();
        int iterativeNum=100;

        //顶点
        DataSet<Long> vertix=env.fromElements(1L,2L,3L,4L,5L,6L,7L);
        //边
        DataSet<Tuple2<Long,Long>> edges=env.fromElements(
                Tuple2.of(1L, 2L),
                Tuple2.of(2L, 3L),
                Tuple2.of(2L, 4L),
                Tuple2.of(3L, 4L),
                Tuple2.of(5L, 6L),
                Tuple2.of(5L, 7L),
                Tuple2.of(6L, 7L)
        );
        //单向边转为双向边
        edges=edges.flatMap(new FlatMapFunction<Tuple2<Long,Long>, Tuple2<Long,Long>>() {
            @Override
            public void flatMap(Tuple2<Long, Long> tuple, Collector<Tuple2<Long, Long>> collector) throws Exception {
                collector.collect(tuple);
                collector.collect(Tuple2.of(tuple.f1,tuple.f0));
            }
        });

        //initialSolutionSet,将顶点映射为(vertix,vertix)的形式
        DataSet<Tuple2<Long,Long>> initialSolutionSet=vertix.map(new MapFunction<Long, Tuple2<Long, Long>>() {
            @Override
            public Tuple2<Long, Long> map(Long vertix) throws Exception {
                return Tuple2.of(vertix,vertix);
            }
        });
        //initialWorkSet
        DataSet<Tuple2<Long,Long>> initialWorkSet=vertix.map(new MapFunction<Long, Tuple2<Long, Long>>() {
            @Override
            public Tuple2<Long, Long> map(Long vertix) throws Exception {
                return Tuple2.of(vertix,vertix);
            }
        });
        //第一个字段做迭代运算
        DeltaIteration<Tuple2<Long,Long>,Tuple2<Long,Long>> iterative=
        initialSolutionSet.iterateDelta(initialWorkSet,iterativeNum,0);
        //数据集合边做join操作,然后求出当前顶点的邻居顶点的最小ID值
        DataSet<Tuple2<Long,Long>> changes=iterative.getWorkset().join(edges).where(0).equalTo(0).with(new NeighborWithComponentIDJoin())
                .groupBy(0).aggregate(Aggregations.MIN,1)
                //和solution set进行join操作,更新solution set,如果当前迭代结果中的最小ID小于solution中的ID值,则发送到下一次迭代运算中继续运算,否则不发送
                .join(iterative.getSolutionSet()).where(0).equalTo(0)
                .with(new ComponetIDFilter());

        //关闭迭代计算
        DataSet<Tuple2<Long,Long>> result=iterative.closeWith(changes,changes);

        result.print();
    }


    public static class NeighborWithComponentIDJoin implements JoinFunction<Tuple2<Long,Long>,Tuple2<Long,Long>,Tuple2<Long,Long>>{

        @Override
        public Tuple2<Long, Long> join(Tuple2<Long, Long> t1, Tuple2<Long, Long> t2) throws Exception {
            return Tuple2.of(t2.f1,t1.f1);
        }
    }

    public static class ComponetIDFilter implements FlatJoinFunction<Tuple2<Long,Long>,Tuple2<Long,Long>,Tuple2<Long,Long>> {

        @Override
        public void join(Tuple2<Long, Long> t1, Tuple2<Long, Long> t2, Collector<Tuple2<Long, Long>> collector) throws Exception {
            if(t1.f1<t2.f1){
                collector.collect(t1);
            }
        }
    }
}

结果:
(3,1)
(7,5)
(6,5)
(1,1)
(5,5)
(2,1)
(4,1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值