Python机器学习实战(十四)

其他工具 

利用PCA来简化数据

对数据进行简化的原因:

1.使得数据集更易使用

2.降低很多算法的计算开销

3.去除噪声

4.使得结果易懂

这里我们先关注未标注数据上的降维技术。该技术同时也可以用用于已标注的数据。

第一种降维的方法称为主成分分析(PCA)在PCA中,数据从原来的坐标系转换到了新的坐标系,新坐标系的选择是有数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二新坐标轴的选择和第一个坐标轴正交且具有最大方差的方向。该过程一直重复,重复次数为原始数据中特征的数目。

另外一种降维技术是因子分析(Factor Analysis)。在因子分析中,假设在观察数据的生成中有一些观察不到隐变量。假设观察数据是这些隐变量和某些噪声的线性组合。那么隐变量的数据可能比观察数据的数目少,也就是说通过找到隐变量就可以实现数据的降维。

还有一种是独立成分分析(ICA)。假设数据是从N个数据源生成的,假设数据为多个数据源的混合观察结果,这些数据源之间是相互独立的,而在PCA中只假设数据是不相关的。如果数据源的数目少于观察数据的数目,可以实现降维过程。


PCA

通过PCA进行降维处理,我们就可以同时获得SVM和决策树的优点:一方面,得到了和决策树一样简单的分类器,同时分隔和SVM一样好。

在NumPy中实现PCA

伪代码如下:

去除平均值
计算协方差矩阵
计算协方差矩阵的特征值和特征向量
将特征值从大到小排序
保留最上面的N个特征向量
将数据转换到上述N个特征向量构建的新空间中
from numpy import *

def loadDataSet(fileName, delim='\t'):
    fr = open(fileName)
    stringArr = [line.strip().split(delim) for line in fr.readlines()]
    datArr = [list(map(float,line)) for line in stringArr]
    return mat(datArr)

def pca(dataMat, topNfeat=9999999):
    '''
    
    :param dataMat: 第一个参数是用于进行PCA操作的数据集
    :param topNfeat: 可选参数,即应用的N个特征值。如果不指定值,那么函数就会返回前9999999个特征
    # 或者原始的数据中的额全部的特征。
    :return: 
    '''
    meanVals = mean(dataMat, axis=0)
    meanRemoved = dataMat - meanVals 
    # 首先计算并减去原始数据集的平均值
    covMat = cov(meanRemoved, rowvar=0)
    eigVals,eigVects = linalg.eig(mat(covMat))
    eigValInd = argsort(eigVals)            
    # 计算协方差矩阵及其特征值,接着利用argsort()函数对特征值从小到大进行排序
    eigValInd = eigValInd[:-(topNfeat+1):-1]  
    # 根据特征值排序结果的逆序就可以得到topNfeat个最大的特征向量。
    redEigVects = eigVects[:,eigValInd]       
    lowDDataMat = meanRemoved * redEigVects
    reconMat = (lowDDataMat * redEigVects.T) + meanVals
    # 利用N个特征将原始数据转换到新空间中
    return lowDDataMat, reconMat
    # 原始数据被重构后返回用于调试,同时降维之后的数据集也被返回了。

dataMat = loadDataSet('F:\python\machinelearninginaction\Ch13\\testSet.txt')
lowDMat,reconMat = pca(dataMat,1)
print(shape(lowDMat))
print('--------------------')
# 上面的结果包含了降维之后的矩阵,接下来将降维后的数据和原始数据一起绘制出来
import matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(dataMat[:,0].flatten().A[0],dataMat[:,1].flatten().A[0],marker='^',s=90)
ax.scatter(reconMat[:,0].flatten().A[0],reconMat[:,1].flatten().A[0],marker='o',s=50,c='red')
plt.show()



(1000, 1)
--------------------

使用如下命令来替换原来的PCA调用,并重复上述过程: 

lowDMat,reconMat = pca(dataMat,2)

 


示例:利用PCA对半导体制造数据降维

def replaceNanWithMean():
    datMat = loadDataSet('F:\python\machinelearninginaction\Ch13\secom.data', ' ')
    numFeat = shape(datMat)[1]
    # 首先打开了数据集并计算出其特征的数目
    for i in range(numFeat):
        # 在特征上进行循环
        meanVal = mean(datMat[nonzero(~isnan(datMat[:,i].A))[0],i])
        # 首先计算出所有非NaN的平均值
        datMat[nonzero(isnan(datMat[:,i].A))[0],i] = meanVal
        # 将所有NaN替换为该平均追
    return datMat

# 将数据集中所有的NaN替换成平均值
dataMat = replaceNanWithMean()
# 调用如下语句去除均值
meanVals = mean(dataMat,axis=0)
meanRemoved = dataMat - meanVals
# 计算协方差矩阵
covMat = cov(meanRemoved,rowvar=0)
# 对该矩阵进行特征分析
eigVals,eigVects = linalg.eig(mat(covMat))
print(eigVals)




[ 5.34151979e+07+0.00000000e+00j  2.17466719e+07+0.00000000e+00j
  8.24837662e+06+0.00000000e+00j  2.07388086e+06+0.00000000e+00j
  1.31540439e+06+0.00000000e+00j  4.67693557e+05+0.00000000e+00j
  2.90863555e+05+0.00000000e+00j  2.83668601e+05+0.00000000e+00j
  2.37155830e+05+0.00000000e+00j  2.08513836e+05+0.00000000e+00j
  1.96098849e+05+0.00000000e+00j  1.86856549e+05+0.00000000e+00j
                                .
                                .
                                .
  0.00000000e+00+0.00000000e+00j  0.00000000e+00+0.00000000e+00j
  0.00000000e+00+0.00000000e+00j  0.00000000e+00+0.00000000e+00j
  0.00000000e+00+0.00000000e+00j  0.00000000e+00+0.00000000e+00j
  0.00000000e+00+0.00000000e+00j  0.00000000e+00+0.00000000e+00j
  0.00000000e+00+0.00000000e+00j  0.00000000e+00+0.00000000e+00j]   

其中有超过20%的特征值都是0.这就意味着这些特征都是其他特征的副本,也就述说他们可以通过其他特征来表示,而本身并没有提供额外的信息。


利用SVD简化数据

SVD的应用

奇异值分解(SVD)优点是:简化数据,提高算法的结果。缺点是:数据的转换可能难以理解。

使用数据类型:数值型数据

利用SVD实现,能够用小得多的数据集来表示原始数据。实际上是去除了噪声和冗余信息。可以把SVD看成是从有噪声的数据中抽取相关特征。

首先介绍SVD是如何通过隐性语义索引应用于搜索和信息检索领域的。然后在介绍SVD在推荐系统中的应用。


隐性语义索引(LSI)

在LSI中,一个矩阵是由文档和词语组成的。我们在该矩阵上应用SVD时,就会构建出多个奇异值。这些奇异值代表了文档中的概念或主题,这一特点可以用于更搞笑的文档搜索。


推荐系统

SVD简单版本的推荐系统能够计算项或者人之间的相似度。更先进的方法则是利用SVD从数据中构建一个主题空间,然后再在该空间下计算其相似度。我们可以把奇异值想象成一个新空间。


矩阵分解

SVD将原始数据集矩阵Data分解成三个矩阵\sum,\bigcup,V^T,如果原始矩阵Data是m行n列,那么\bigcup

, \sumV^T分别是m行m列,m行n列和n行n列。也就是

                                                            Data_{m*n} = \bigcup _{m*m}\sum _{m*n}V^T_{n*n}

上述分解中会构建出一个矩阵\sum,该矩阵只有对角元素,其他元素均为0.另一个惯例就是\sum

的对角元素是从大到小排列的。这些对角元素称为奇异值,他们对应了原始数据集矩阵Data的奇异值。PCA得到的是矩阵的特征值,他们告诉我们数据集中的重要特征。\sum 中的奇异值也是如此。奇异值和特征值是有关系的。这里的奇异值就是矩阵Data*Data^T特征值的平方根。


实现SVD

U,Sigma,VT = linalg.svd([[1,1],[7,7]])
print(U)
print('------------------')
print(Sigma)
print('------------------')
print(VT)
print('------------------')


[[-0.14142136 -0.98994949]
 [-0.98994949  0.14142136]]
------------------
[10.  0.]
------------------
[[-0.70710678 -0.70710678]
 [-0.70710678  0.70710678]]
------------------


def loadExData():
    return [[0, 0, 0, 2, 2],
            [0, 0, 0, 3, 3],
            [0, 0, 0, 1, 1],
            [1, 1, 1, 0, 0],
            [2, 2, 2, 0, 0],
            [5, 5, 5, 0, 0],
            [1, 1, 1, 0, 0]]

Data = loadExData()
U,Sigma,Vt = linalg.svd(Data)
print(Sigma)


[9.64365076e+00 5.29150262e+00 7.40623935e-16 4.05103551e-16
 2.21838243e-32]

前三个数值比其他值大了很多,所以可以将后两个值去掉。接下来我们的原始数据集就可以用如下结果来近似:

                                         Data_{m*n}\approx U_{m*3}\sum _{3*3}V^T_{3*n}


相似度计算

一些物品之间的相似度的定量方法

例如下面的矩阵:

                                                           \begin{bmatrix} 2 &0 &0 &4 &4 \\ 5&5 &5 &3 &3 \\ 2 & 4 &2 &1 &2 \end{bmatrix}

列分别是:鳗鱼饭、日式炸鸡排、寿司饭、烤牛肉、手撕猪肉

行分别是:Jim,Jhon,Sally

我们计算一下手撕猪肉和烤牛肉之间的相似度。一开始我们用欧氏距离来计算。

                                          \sqrt{(4-4)^2+(3-3)^2+(2-1)^2} = 1

而手撕猪肉和鳗鱼饭的欧式距离为:

                                          \sqrt{(4-2)^2+(3-5)^2+(2-2)^2} = 2.83

该数据表明手撕猪肉与烤牛肉更相似。我们希望相似度在0到1之间变化,并且物品对越相似,他们的相似的值也就越大。我们可以用“相似度=1/(1+距离)”这样 的算式来计算相似度。当距离为0时,相似度为1.0.距离非常大,相似度也就趋近于0.

第二种计算方式是皮尔逊相关系数,它度量的是两个向量间的相似度。该方法相对于欧氏距离的一个优势在于,它对用户评级的量级并不敏感。该相关系数的计算是由函数corrcoef()进行的,该相关系数的取值范围从-1到+1,我们通过0.5+0.5*corrcoef()这个函数计算,并把其取值范围归一化到0到1之间。

另一种常用的距离计算方法就是余弦相似度,其计算的是两个向量夹角的余弦值。如果夹角为90度,则相似度为0;如果两个向量的方向相同,则相似度为1.0.同皮尔逊相关系数一样余弦相似度的取值范围也在-1到+1之间,因此我们也将它归一化到0到1直接拿。计算该相似度值,我们采用的两个向量A,B夹角的余弦相似度的定义如下:

                                                         cos\Theta = {\frac{A*B}{||A||||B||}}{}

其中||A||、||B||表示向量A,B的2范数,你可以定义向量的任一范数,但是如果不指定范数阶数,则都假设2范数。向量[4,2,2]的2范数为:

                                                               \sqrt{4^2+2^2+2^2}

接下来我们将上述各种相似度的计算方法写成函数。

def ecludSim(inA, inB):
    return 1.0 / (1.0 + la.norm(inA - inB))


def pearsSim(inA, inB):
    if len(inA) < 3: return 1.0
    return 0.5 + 0.5 * corrcoef(inA, inB, rowvar=0)[0][1]


def cosSim(inA, inB):
    num = float(inA.T * inB)
    denom = la.norm(inA) * la.norm(inB)
    return 0.5 + 0.5 * (num / denom)

# 函数假定inA和inB都是列向量。pearsSim()函数会检查是否存在3个或更多的点,如果不存在,该函数返回1.0,
# 这是因为此时两个向量完全相关
myMat = mat(loadExData())
print(ecludSim(myMat[:,0],myMat[:,4]))
print('-----------------------')
print(ecludSim(myMat[:,0],myMat[:,0]))
# 接下来测试余弦相似度
print('-----------------------')
print(cosSim(myMat[:,0],myMat[:,4]))
print('-----------------------')
print(cosSim(myMat[:,0],myMat[:,0]))
# 余弦相似度也还可以,再试一下皮尔逊相关系数
print('-----------------------')
print(pearsSim(myMat[:,0],myMat[:,4]))
print('-----------------------')
print(pearsSim(myMat[:,0],myMat[:,0]))


0.12973190755680383
-----------------------
1.0
-----------------------
0.5
-----------------------
1.0
-----------------------
0.20596538173840329
-----------------------
1.0

进程已结束,退出代码为 0

上面的相似度计算都是假设数据采用了列向量方式进行表示。如果利用上述函数来计算两个行向量的相似度就会遇到问题。这里采用列向量的表示方法,暗示着我们将利用基于物品的相似度计算方法。


示例:餐馆菜肴推荐引擎

现在构建一个推荐引擎,该引擎关注的是餐馆事务的推荐。首先构建一个基本的推荐引擎,能够寻找用户没有尝过的菜肴。然后通过SVD来减少特征空间并提高推荐的效果。之后将程序打包并通过用户可读的人机界面提供给人们使用。最后介绍一些构建推荐系统时面临的问题。

推荐未尝过的菜肴

推荐系统的工作过程是:给定一个用户,系统会为此用户返回N个最好的推荐菜。我们需要做到:

1、寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值;

2、在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。也就是我们认为用户可能会对物品的打分。

3、对这些物品的评分从高到低进行排序,返回前N个物品。

def standEst(dataMat, user, simMeas, item):
    '''

    :param dataMat: 数据矩阵
    :param user: 用户编号
    :param simMeas: 物品编号
    :param item: 相似度计算方法
    :return:
    用来计算在给定相似度计算方法的条件下,用户对物品的估计评分值。
    '''
    n = shape(dataMat)[1]
    # 首先得到数据集中的物品数目
    simTotal = 0.0
    ratSimTotal = 0.0
    # 对两个后面用于计算估计评分值得变量进行初始化
    for j in range(n):
        # 遍历行中的每个物品
        # 该循环大体上是对用户评分过的每个物品进行遍历,并将它和其它物品进行比较
        userRating = dataMat[user, j]
        if userRating == 0: continue
        # 如果某个物品的评分值为0,跳过该物品
        overLap = nonzero(logical_and(dataMat[:, item].A > 0,
                                      dataMat[:, j].A > 0))[0]
        # 给出的是两个物品当中已经被评分的额那个元素
        if len(overLap) == 0:
            # 如果两者没有任何重合元素,则相似度为0且终止本次循环
            similarity = 0
        else:
            # 如果存在重合的物品,则基于这些重合物品计算相似度。
            similarity = simMeas(dataMat[overLap, item],
                                 dataMat[overLap, j])
        print('the %d and %d similarity is: %f' % (item, j, similarity))
        simTotal += similarity
        ratSimTotal += similarity * userRating
        # 相似度会不断累加,每次计算时还考虑相似度和当前用户评分的成绩。
    if simTotal == 0:
        return 0
    else:
        return ratSimTotal / simTotal
    # 通过除以所有的评分总和,对上述相似度评分的成绩进行归一化。
    # 使得左后的评分值在0到5之间,这些评分值用于对预测值进行保护。

def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
    '''

    :param dataMat:
    :param user:
    :param N:
    :param simMeas: 相似度计算方法
    :param estMethod: 估计方法
    :return:
    推荐引擎,调用上一个函数。产生了最高的N个推荐结果
    如果不指定N的大小,则默认为3.
    '''
    unratedItems = nonzero(dataMat[user, :].A == 0)[1]
    # 给用户建立一个未评分的物品列表
    if len(unratedItems) == 0: return 'you rated everything'
    # 如果不存在未评分物品,退出函数。
    itemScores = []
    for item in unratedItems:
        # 否则在所有的未评分物品上进行循环。
        estimatedScore = estMethod(dataMat, user, simMeas, item)
        itemScores.append((item, estimatedScore))
        # 对每个未评分物品,通过调用standEst()来产生该物品的预测得分
        # 该物品的编号和估计值会放在一个元素列表中。
    return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
# 按估计得分对该列表进行排序并返回。该列表是从大到小逆序排列,因此第一个值就是最大值。

myMat = mat(loadExData())
# 该矩阵对于展示SVD的作用非常好,对它其中的一些值进行更改。
myMat[0,1] = myMat[0,0] = myMat[1,0] = myMat[2,0] = 4
myMat[3,3] = 2
print(myMat)
print('-----------------------')
# 现在已经可以做一些推荐了,先尝试一下默认的推荐
print(recommend(myMat,2))
print('-----------------------')
print(recommend(myMat,2,simMeas=ecludSim))
print('-----------------------')
print(recommend(myMat,2,simMeas=pearsSim))
# 这表明了用户2对物品2 的预测评分值为2.5
# 对物品1 的预测评分值为2.05,下面利用其他的相似度计算方法进行推荐

[[4 4 0 2 2]
 [4 0 0 3 3]
 [4 0 0 1 1]
 [1 1 1 2 0]
 [2 2 2 0 0]
 [5 5 5 0 0]
 [1 1 1 0 0]]
-----------------------
[(2, 2.5), (1, 2.0243290220056256)]
-----------------------
[(2, 3.0), (1, 2.8266504712098603)]
-----------------------
[(2, 2.5), (1, 2.0)]

进程已结束,退出代码为 0


下面将介绍如何将SVD应用于推荐。通过计算新矩阵的SVD来了解其到底需要多少维特征

U,Sigma,VT = la.svd(mat(loadExData2()))
print(Sigma)
print('----------------------')
# 接下来看看到底有多少奇异值能达到总能量的90%
# 首先对Sigma中的值得平方:
Sig2 = Sigma**2
# 在计算一下总能量
print(sum(Sig2))
print('----------------------')
# 总能量的90%
print(sum(Sig2)*0.9)
print('----------------------')
# 计算前两个元素包含的能量:
print(sum(Sig2[:2]))
print('----------------------')
# 该值低于总能量的90%,计算前三个元素包含的能量
print(sum(Sig2[:3]))
print('----------------------')
# 也就是说我们讲一个11维的矩阵转换成了3维的矩阵。


[15.77075346 11.40670395 11.03044558  4.84639758  3.09292055  2.58097379
  1.00413543  0.72817072  0.43800353  0.22082113  0.07367823]
----------------------
541.9999999999995
----------------------
487.7999999999996
----------------------
378.8295595113579
----------------------
500.50028912757926
----------------------

进程已结束,退出代码为 0

下面对转换后的三维空间构造出一个相似度计算函数。利用SVD将所有的菜肴映射到一个低维空间去。在低维空间下,可以利用前面相同的相似度计算方法来进行推荐。

def svdEst(dataMat, user, simMeas, item):
    '''

    :param dataMat:
    :param user:
    :param simMeas:
    :param item:
    :return:
    该函数对给定用户给定物品购进了一个评分估计值。不同之处在于它在第三行对数据集进行了SVD分解
    SVD分解后,我们只利用了包含90%能量值的奇异值。以数组形式得以保存
    因此如果要进行矩阵计算,就必须用这些奇异值构建出一个对角矩阵。
    然后利用U矩阵将物品转换到低维空间中。
    '''
    n = shape(dataMat)[1]
    simTotal = 0.0;
    ratSimTotal = 0.0
    U, Sigma, VT = la.svd(dataMat)
    Sig4 = mat(eye(4) * Sigma[:4])
    xformedItems = dataMat.T * U[:, :4] * Sig4.I
    for j in range(n):
        # 在用户对用行的所有元素上进行遍历,这里的相似度计算是在低维空间下进行的
        # 相似度的计算方法作为一个参数传递给该函数
        userRating = dataMat[user, j]
        if userRating == 0 or j == item: continue
        similarity = simMeas(xformedItems[item, :].T,
                             xformedItems[j, :].T)
        print('the %d and %d similarity is: %f' % (item, j, similarity))
        simTotal += similarity
        ratSimTotal += similarity * userRating
        # 对相似度求和,同时对相似度及对应评分值的成绩求和
        # 这些值返回之后则用于估计评分的计算。
    if simTotal == 0:
        return 0
    else:
        return ratSimTotal / simTotal

print(recommend(myMat,1,estMethod=svdEst))
print('---------------------')
# 下面再尝试另一种相似度计算方法
print(recommend(myMat,1,estMethod=svdEst,simMeas=pearsSim))


[(2, 3.4177569186592387), (1, 3.330717154558564)]
---------------------
[(2, 3.4992661245386794), (1, 3.327232428061366)]

示例:基于SVD的图像压缩

使用SVD来对数据进行降维,从而实现图像的压缩。

def printMat(inMat, thresh=0.8):
    '''

    :param inMat:
    :param thresh:
    :return:
    打印矩阵。由于矩阵包含了浮点数,因此必须定义浅色深色。这里通过一个阈值来界定。
    '''
    for i in range(32):
        # 遍历所有的矩阵元素,当元素大于阈值时打印1,否则打印0
        for k in range(32):
            if float(inMat[i, k]) > thresh:
                print(1,end='')
            else:
                print(0,end='')
        print('')


def imgCompress(numSV=3, thresh=0.8):
    '''

    :param numSV:
    :param thresh:
    :return:
    实现图像的压缩。基于任意给定的奇异值数目重构图像。
    '''
    myl = []
    # 构建了一个列表,然后打开文本文件
    for line in open('F:\python\machinelearninginaction\Ch14\\0_5.txt').readlines():
        # 从文件中以数值方式读入字符。
        newRow = []
        # 在矩阵调入后,可以在屏幕上输出该矩阵
        for i in range(32):
            newRow.append(int(line[i]))
        myl.append(newRow)
    myMat = mat(myl)
    print("****original matrix******")
    printMat(myMat, thresh)
    U, Sigma, VT = la.svd(myMat)
    # 通过Sigma重新构成SigRecon来实现这一点
    SigRecon = mat(zeros((numSV, numSV)))
    for k in range(numSV):
        SigRecon[k, k] = Sigma[k]
    reconMat = U[:, :numSV] * SigRecon * VT[:numSV, :]
    print(
    "****reconstructed matrix using %d singular values******" % numSV)
    printMat(reconMat, thresh)

print(imgCompress(2))


****original matrix******
00000000000000110000000000000000
00000000000011111100000000000000
00000000000111111110000000000000
00000000001111111111000000000000
00000000111111111111100000000000
00000001111111111111110000000000
00000000111111111111111000000000
00000000111111100001111100000000
00000001111111000001111100000000
00000011111100000000111100000000
00000011111100000000111110000000
00000011111100000000011110000000
00000011111100000000011110000000
00000001111110000000001111000000
00000011111110000000001111000000
00000011111100000000001111000000
00000001111100000000001111000000
00000011111100000000001111000000
00000001111100000000001111000000
00000001111100000000011111000000
00000000111110000000001111100000
00000000111110000000001111100000
00000000111110000000001111100000
00000000111110000000011111000000
00000000111110000000111111000000
00000000111111000001111110000000
00000000011111111111111110000000
00000000001111111111111110000000
00000000001111111111111110000000
00000000000111111111111000000000
00000000000011111111110000000000
00000000000000111111000000000000
(32,32)
****reconstructed matrix using 2 singular values******
00000000000000000000000000000000
00000000000000000000000000000000
00000000000001111100000000000000
00000000000011111111000000000000
00000000000111111111100000000000
00000000001111111111110000000000
00000000001111111111110000000000
00000000011110000000001000000000
00000000111100000000001100000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001110000000
00000000111100000000001100000000
00000000001111111111111000000000
00000000001111111111110000000000
00000000001111111111110000000000
00000000000011111111100000000000
00000000000011111111000000000000
00000000000000000000000000000000

总数字64+64+2 = 130。和原数目1024相比,获得了几乎10倍的压缩。

还剩最后一章,太爽了,我他妈卷。


大数据与MapReduce

分布式计算的框架

优点:可在短时间内完成大量工作

缺点:算法必须经过重写,需要对系统工程有一定的理解

适用数据类型:数值型和标称型数据

MapReduce是一个软件框架,可以将单个计算机作业分配给多台计算机执行。MapReduce在大量节点组成的集群上运行。工作流程是:单个作业被分成很多小份,输入数据也被切片分发到每个节点,每个节点只在本地数据上做运算,对应的运算代码称为mapper,这个过程被称为map阶段。每个mapper的输出通过某种方式组合。排序后的结果再被分成小份分发到各个节点进行下一步处理。第二部的出力阶段被称为reduce阶段,对应的运行代码被称为reducer。reducer的输出就是程序最终中运行结果。

MapReduce的优势在于,使得程序以并行的方式执行。

import sys
from numpy import mat, mean, power


def read_input(file):
    for line in file:
        yield line.rstrip()


input = read_input(sys.stdin)  # creates a list of input lines
input = [float(line) for line in input]  # overwrite with floats
numInputs = len(input)
input = mat(input)
sqInput = power(input, 2)

# output size, mean, mean(square values)
print("%d\t%f\t%f" % (numInputs, mean(input), mean(sqInput)) ) # calc mean of columns
print(sys.stderr,"report:still alive")

# 该mapper首先按行读取所有的输入并创建一组对应的浮点数,然后得到数组长度
# 并创建矩阵。在对所有的值进行平方,最后将均值和平方后的均值发送出去
# 这些值将用于计算全局的均值和方差。



def read_input(file):
    for line in file:
        yield line.rstrip()


input = read_input(sys.stdin)


mapperOut = [line.split('\t') for line in input]


cumVal = 0.0
cumSumSq = 0.0
cumN = 0.0
for instance in mapperOut:
    nj = float(instance[0])
    cumN += nj
    cumVal += nj * float(instance[1])
    cumSumSq += nj * float(instance[2])


mean = cumVal / cumN
meanSq = cumSumSq / cumN

print("%d\t%f\t%f" % (cumN, mean, meanSq))
print(sys.stderr, "report: still alive")

# 接受上一个程序的输出并将它们合成为全局的均值和方差。

示例:分布式SVM的Pegasos算法

Pegasos是指原始估计梯度求解器。使用某种形式的随机梯度下降方法来解决SVM所定义的优化问题,研究表明该算法所需迭代次数取决于用户所期望的精确度而不是数据集的大小。

SVM算法的目的是为了找到一个分类超平面。在二维情况下也就是要找到一条直线,将两类数据分隔开来。Pegasos算法的工作流程是:从训练集中随机挑选一些样本点添加到待处理列表中,之后按序判断每个样本点是否被正确分类;如果是则忽略,如果不是则将其加入到待更新集合。批处理完毕后,权重向量按照这些错分的样本进行更新。真个算法循环执行。

伪代码如下:

将w初始化为0
对每次批处理
    随机选择k个样本点(向量)
    对每个向量
        如果该向量被错分:
            更新权重向量w
    累加对w的更新
    
'''
Created on Feb 27, 2011
MapReduce version of Pegasos SVM
Using mrjob to automate job flow
@author: Peter
'''
from mrjob.job import MRJob

import pickle
from numpy import *


class MRsvm(MRJob):
    DEFAULT_INPUT_PROTOCOL = 'json_value'

    def __init__(self, *args, **kwargs):
        super(MRsvm, self).__init__(*args, **kwargs)
        self.data = pickle.load(open('F:\python\machinelearninginaction\Ch15\svmDat27'))
        self.w = 0
        self.eta = 0.69
        self.dataList = []
        self.k = self.options.batchsize
        self.numMappers = 1
        self.t = 1  # iteration number

    def configure_options(self):
        super(MRsvm, self).configure_options()
        self.add_passthrough_option(
            '--iterations', dest='iterations', default=2, type='int',
            help='T: number of iterations to run')
        self.add_passthrough_option(
            '--batchsize', dest='batchsize', default=100, type='int',
            help='k: number of data points in a batch')

    def map(self, mapperId, inVals):  # needs exactly 2 arguments
        # input: nodeId, ('w', w-vector) OR nodeId, ('x', int)
        if False: yield
        if inVals[0] == 'w':  # accumulate W-vector
            self.w = inVals[1]
        elif inVals[0] == 'x':
            self.dataList.append(inVals[1])  # accumulate data points to calc
        elif inVals[0] == 't':
            self.t = inVals[1]
        else:
            self.eta = inVals  # this is for debug, eta not used in map

    def map_fin(self):
        labels = self.data[:, -1];
        X = self.data[:, 0:-1]  # reshape data into X and Y
        if self.w == 0: self.w = [0.001] * shape(X)[1]  # init w on first iteration
        for index in self.dataList:
            p = mat(self.w) * X[index, :].T  # calc p=w*dataSet[key].T
            if labels[index] * p < 1.0:
                yield (1, ['u', index])  # make sure everything has the same key
        yield (1, ['w', self.w])  # so it ends up at the same reducer
        yield (1, ['t', self.t])

    def reduce(self, _, packedVals):
        for valArr in packedVals:  # get values from streamed inputs
            if valArr[0] == 'u':
                self.dataList.append(valArr[1])
            elif valArr[0] == 'w':
                self.w = valArr[1]
            elif valArr[0] == 't':
                self.t = valArr[1]
        labels = self.data[:, -1]
        X = self.data[:, 0:-1]
        wMat = mat(self.w)
        wDelta = mat(zeros(len(self.w)))
        for index in self.dataList:
            wDelta += float(labels[index]) * X[index, :]  # wDelta += label*dataSet
        eta = 1.0 / (2.0 * self.t)  # calc new: eta
        # calc new: w = (1.0 - 1/t)*w + (eta/k)*wDelta
        wMat = (1.0 - 1.0 / self.t) * wMat + (eta / self.k) * wDelta
        for mapperNum in range(1, self.numMappers + 1):
            yield (mapperNum, ['w', wMat.tolist()[0]])  # emit w
            if self.t < self.options.iterations:
                yield (mapperNum, ['t', self.t + 1])  # increment T
                for j in range(self.k / self.numMappers):  # emit random ints for mappers iid
                    yield (mapperNum, ['x', random.randint(shape(self.data)[0])])

    def steps(self):
        return ([self.mr(mapper=self.map, reducer=self.reduce,
                         mapper_final=self.map_fin)] * self.options.iterations)


if __name__ == '__main__':
    MRsvm.run()

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值