1. 软件版本:
Hadoop2.6.0(IDEA中源码编译使用CDH5.7.3,对应Hadoop2.6.0),集群使用原生Hadoop2.6.4,JDK1.8,Intellij IDEA 14 。源码可以在https://github.com/fansy1990/linear_regression 下载。
2. 实现思路:
本博客实现的是一元一次线性方程,等于是最简单的线性方程了,采用的是Couresa里面的机器学习中的大数据线性方程的方法来更新参数值的(即随机梯度下降方法,当然也可以使用批量梯度下降方法来实现,只是在LinearRegressionJob中实现的不一样而已),如果对随机梯度下降或者批量梯度下降不了解的话,需要先去看看。下面是实现思路:
2.1 Shuffle Data(打乱数据):
如果要采用随机梯度下降的话,那么需要保持原始数据随机,所以这里的第一步就是随机打乱原始数据。采用的思路是:在Mapper端输出随机值作为key,输出当前记录作为value,在Reducer端直接遍历每个key的所有values,直接输出value以及NullWritable.get即可。
在这里添加一个额外的参数randN,这个参数表示在Mapper端随机值时,多少个原始数据使用同一个随机值,如果randN为1,那么每个原始数据都会使用一个随机值作为key,如果randN为2,那么每两个原始数据使用一个随机值,如果randN为0或小于0,那么所有数据都使用同一个随机值(注意,这个时候其实在Reducer端的values其实也是乱序的,请读者思考为什么?)。
其Mapper中map核心实现如下所示
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
if(randN <= 0) { // 如果randN 比0小,那么不再次打乱数据
context.write(randFloatKey,value);
return ;
}
if(++countI >= randN){// 如果randN等于1,那么每次随机的值都是不一样的
randFloatKey.set(random.nextFloat());
countI =0;
}
context.write(randFloatKey,value);
}
2.2 Linear Regression(线性回归):
线性回归采用随机梯度下降的方法来更新theta0和theta1 (只实现了一元一次,所以只有两个参数),每个Mapper都会使用同样的初始化参数(theta0=1和theta1=0),在每个Mapper中使用自己的数据来更新theta0和theta1,更新的公式为:
theta0 = theta0 -alpha*(h(x)-y)x
theta1 = theta1 -alpha*(h(x)-y)x
其中,h(x)= theta0 + theta1 * x ;同时,需要注意这里的更新是同步更新,其核心代码如下所示:
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
float[] xy = Utils.str2float(value.toString().split(splitter));
float x = xy[0];
float y = xy[1];
/