基于MapReduce的频繁项集挖掘方法

http://www.cstor.cn/textdetail.asp?id=1023


云计算是分布式计算技术的一种,其最基本的概念是透过网络将庞大的计算处理程序自动拆成无数个较小的子程序,再交由多服务崧所组成的庞大系统经搜寻、计算分析之后将处理结果回传给用户。云计算具有超大规模、虚拟化、高可靠性、高可扩展性、通用性等特点,在海量数据的处理中有着重要的地位和发展空间。云计算普遍采用的编程模式是MapReduce,它由Google提出,为编写需要大规模并行处理的代码提供了简单模式L。

    Hadoop最早是作为一个开源搜索引擎项目Nutch的基础平台而开发的,是MapReduce的实现。作为一个开源的软件平台,它使用了一个分布式文件系统(HDFS),将应用切分为许多小任务块去执行。使得编写和运用于处理海量数据的应用程序更加容易。

    Aproiir算法是一种为布尔关联规则挖掘频繁项集的算法,是关联规则挖掘的重要算法。目前,海量数据挖掘提出了经典算法在分布式计算环境下并行执行的需求。而云计算提供了海量数挖掘所需的并行执行环境。

    为此,本文对基于Apriori算法的思想,通过改进,设计了基于云计算的Map/Reduce机制的频繁项集挖掘方法,并在Hadooop平台上对之进行了实现和性能分析。

1 MapReduce编程模型..

    MapReduce是一个将大型分布式计算表达为一个对数据键/值(key/value)对集合进行串行化分布式操作的编程模型。一个Map/Reduce计算包括两个阶段,map阶段和reduce阶段。用MapReduce来处理的数据集(或任务)有一个基本要求:待处理的数据集可以分解成许多小的数据集,而且各个小数据集都可以完全并行地进行处理。

    在map阶段,MapReduce框架将输人数据拆分为大量的数据片段,并将每一个数据片段分配给一个map任务。每一个.. map任务会将对其分配到的key/value对进行计算,生成一个中间结果,然后将中间结果中所有具有相同key值的value经过计算后传递给reduce函数。

    在reduce阶段,每一个reduce任务会将分配到的二元组key/value集合的片段作为输入。对于每一个这样的二元组都会调用一个用户定义的reduce函数将value值合并,形成一个较小的value的集合,每次reduce函数调用只产生0或1个value值输出。每个阶段的任务执行都是支持容错的,如果任一个或多个节点在计算过程中出现错误都会将任务自动重新分配到其他节点。同时运行多个map和reduce任务提供了很好的负载均衡并且保证了运行中失败的任务被重新运行的代价降到尽可能的小。

2 Aproiir算法分析..

    Aproiir算法使用一种称为逐层搜索的迭代方法,k项集用于探索(k+1)项集。首先扫描一次数据库,产生频繁.. 1项集.. L;然后进行循环,在第k次循环中,首先由频繁k一1项集进行自连接和剪枝产生候选频繁k项集Ck,然后使用.. Hash函数把.. Ck存储到一棵树上,扫描数据库,对每一个交易rf使用同样的Hash函数,计算出该交易T内包含哪些候选频繁k项集,并对这些候选频繁项集的支持度加1,如果某个候选频繁项集的支持度大于最小支持度,则该候选项集为频繁项集;如此下去,直到不能再找到频繁项集为止。为了提高生成频繁项集的效率,Apriori算法利用了两个重要的性质来压缩搜索空间:

(1)若X是频繁项集,则.. X的所有子集是频繁项集。
(2)若X是非频繁项集,则.. X的所有超集都是非频繁项集。

3基于MapReduce的频繁项集挖掘方法设计

    Aproi67通过对数据库的多趟扫描来发ir算法c,j现所有的频繁项集,在海量数据的条件下,对数据库的扫描将会耗费大量的时间和内存。本文充分利用云计算提供的分布式并行计算功能,对Apir算法加以改进,得到新的适用于云计算的频繁项集挖掘方法,该方法使查找和L+的过程独立,能够提高海量数据挖掘的效率。新方法的基本思想如下:

(1)把数据库分成规模相当的.. M个数据子集,把数据子集发送到M个站点;
(2)每个站点扫描它的数据子集,产生一个局部的候选k项集的集合,记作C2,每个候选项集的支持度计数为1;
(3)利用hash函数把M个站点的C2中相同的项集和它的支持度计数发送到R个站点;..
(4)R个站点中的每个站点把相同项集的计数累加起来,产生最后的实际支持度,与最小支持度计数rnsp比较确定局部频繁k项集的集合ai_u,;
(5)把R个站点的输出合并即产生全局频繁k项集的集合上咄。将以上思想运用于云计算的MapReduce框架中,由Map函数对输入的候选k项集进行扫描,产生中间.. key/value对,经过combiner函数处理之后交给Reduce函数,Reduce函数将相同候选k项集的支持度计数累加得到候选k项集在整个事务数据库中的实际支持度计数。K值从.. 1开始递增,经过数次计算之后,就能得到所有频繁项集。

4 Hadoop平台上的实验与结果分析

    为了验证以上方法的可行性和有效性,本文做了所设计的新方法与传统.. Aproi算法的对比ir实验。

4.1实验环境与实验数据
    实验中采用了由bnuelpe和hdooop构成的软件环境,硬件环境由1O台.. PC机互联构成。实验数据是事务数据库数据,格式为[TID,I1、I2、I3],其中TID为事务标识,I1、I2为项的列表,项数在2—8之问随机选取。实验数据由JAVA编写的数据生成器生成,数据量在数十到数百K不等。

4.2 Hadoop平台的搭建
实验使用的10台PC机中1台作为NameNode,9台作为DataNode。平台搭建大致分以下几步完成。

(1)完成.. ubuntu、jdk、ssh以及hadoop的安装。
(2)配置JDK环境变量,添加CLASSPATH及JAVA_HOME,并设置路径值。
(3)配置SSH,在每台机器上新建一个超级用户,并建立SSHKEY用来远程登录。
(4)配置hadoop,导人JAVA—HOME环境变量值,指定默认文件系统名和tracker默认路径端口。
(5)使用-format命令格式化NameNode;使用start—al1.sh命令启动所有Hadoop进程。完成以上步骤之后,通过jps命令检查进程是否启动成功,如成功,则Hadooop平台搭建完成。

4.3传统Apriori算法的实现
    实验中对传统的Aproiir算法进行了实现,其运行结果与本文提出的分布式一次扫描方法的运行结果进行了对比。传统Apriori算法的主要实现流程如下图。

 

 

    在整个算法流程中,需要重复扫描数据库,对算法性能会产生比较大的影响。改进后的算法则避免了这一问题。

4.4新频繁项集挖掘方法的实现
    新频繁项集挖掘方法的实现流程如下图所示。

 


    改进后的算法对数据库的扫描次数大大减少,基于Map/Reduce的运算方法使得改进后算法在处理大规模数据时更具有优势。根据需要也可以通过一次扫描数据库求出所有频繁项集。伪代码如下:
k=1;
while k<max   //k下雨最长项的项数
  for each data in  事物数据库
    Map(data,k); //产生k项集的<key,value>对;
  Combiner(itemset,sup) //本地合并
  Reduce(itemset,result); //产生频繁k项集的最终结果
  k=k+1;
 

4.5结果与分析
    传统的Apriori算法和改进后的新频繁项集挖掘方法的运行结果如下图。

 

 

    可以看出,本文设计的新方法在处理大规模数据时所需时间少于传统的Apriori算法,时间复杂度较低,数据量越大优势将越明显。


5结束语

    针对关联规则挖掘中频繁项集的并行挖掘问题,基于传统的Apriori算法,通过改进,设计了新的能适用于MapReduce编程模型的并行的频繁项集挖掘方法。并通过在Hadooop平台上的对比实验,验证了新方法对云计算的适应性和较之传统Apriori算法的优越性。

本文来自:中国云计算网


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我知道你的问题了。不用担心,我会保密的。对于你的问题,可以使用Hadoop的MapReduce框架来进行处理。以下是实现这两个任务的步骤: 1. 统计数据文件中每一年每个州的总人口数量 首先,需要编写一个Mapper程序,将数据文件中的每一行按照年份和州分组,输出键值对<年份_州, 人口数量>。代码示例如下: ```java public class PopulationMapper extends Mapper<LongWritable, Text, Text, LongWritable> { private Text outputKey = new Text(); private LongWritable outputValue = new LongWritable(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); String year = fields[0]; String state = fields[1]; long population = Long.parseLong(fields[2]); outputKey.set(year + "_" + state); outputValue.set(population); context.write(outputKey, outputValue); } } ``` 然后,需要编写一个Reducer程序,将Mapper输出的键值对按照年份和州再次分组,并将人口数量相加得到每一年每个州的总人口数量。代码示例如下: ```java public class PopulationReducer extends Reducer<Text, LongWritable, Text, LongWritable> { private LongWritable outputValue = new LongWritable(); @Override protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { long sum = 0; for (LongWritable value : values) { sum += value.get(); } outputValue.set(sum); context.write(key, outputValue); } } ``` 最后,在Driver程序中配置MapReduce作业的输入输出路径、Mapper和Reducer类,并提交作业到Hadoop集群运行即可。代码示例如下: ```java public class PopulationJob { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "PopulationJob"); job.setJarByClass(PopulationJob.class); job.setMapperClass(PopulationMapper.class); job.setReducerClass(PopulationReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(LongWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } } ``` 2. 统计2008年所有州人口密度,并按降序排列 同样地,需要编写一个Mapper程序,将数据文件中的每一行按照州分组,输出键值对<州, 人口数量>。代码示例如下: ```java public class DensityMapper extends Mapper<LongWritable, Text, Text, LongWritable> { private Text outputKey = new Text(); private LongWritable outputValue = new LongWritable(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); String year = fields[0]; String state = fields[1]; long population = Long.parseLong(fields[2]); if ("2008".equals(year)) { outputKey.set(state); outputValue.set(population); context.write(outputKey, outputValue); } } } ``` 然后,需要编写一个Reducer程序,将Mapper输出的键值对按照州分组,并将人口数量相加得到每个州的总人口数量。代码示例如下: ```java public class DensityReducer extends Reducer<Text, LongWritable, Text, DoubleWritable> { private DoubleWritable outputValue = new DoubleWritable(); @Override protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { long sum = 0; for (LongWritable value : values) { sum += value.get(); } double density = (double) sum / 1000000; // 人口密度 = 总人口数量 / 平方英里 outputValue.set(density); context.write(key, outputValue); } } ``` 最后,在Driver程序中配置MapReduce作业的输入输出路径、Mapper和Reducer类,并将Reducer输出的键值对按照值降序排列输出到文件中。代码示例如下: ```java public class DensityJob { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "DensityJob"); job.setJarByClass(DensityJob.class); job.setMapperClass(DensityMapper.class); job.setReducerClass(DensityReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(DoubleWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); job.waitForCompletion(true); Job sortJob = Job.getInstance(conf, "DensitySortJob"); sortJob.setJarByClass(DensityJob.class); sortJob.setMapperClass(InverseMapper.class); sortJob.setReducerClass(DescendingSortReducer.class); sortJob.setOutputKeyClass(DoubleWritable.class); sortJob.setOutputValueClass(Text.class); FileInputFormat.addInputPath(sortJob, new Path(args[1])); FileOutputFormat.setOutputPath(sortJob, new Path(args[2])); sortJob.waitForCompletion(true); } } ``` 注意,为了按照降序排列,还需要编写一个InverseMapper类和一个DescendingSortReducer类,代码示例如下: ```java public class InverseMapper extends Mapper<LongWritable, Text, DoubleWritable, Text> { private DoubleWritable outputKey = new DoubleWritable(); private Text outputValue = new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split("\t"); String state = fields[0]; double density = Double.parseDouble(fields[1]); outputKey.set(-density); // 取相反数以实现降序排列 outputValue.set(state); context.write(outputKey, outputValue); } } public class DescendingSortReducer extends Reducer<DoubleWritable, Text, Text, DoubleWritable> { private Text outputKey = new Text(); @Override protected void reduce(DoubleWritable key, Iterable<Text> values, Context context) throws IOException, InterruptedException { double density = -key.get(); // 取相反数还原为原来的值 for (Text value : values) { outputKey.set(value); context.write(outputKey, new DoubleWritable(density)); } } } ``` 这样,就完成了两个任务的处理。希望这些代码对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值