《机器学习实战》学习笔记(八)

文章目录

  • 第八章 预测数值型数据:回归
    • 引言
      • 线性回归优缺点
      • 回归的一般方法 :
    • 8 . 1 用线性回归找到最佳拟合直线
      • 最佳估计回归系数表示
      • 给出数据的最佳拟合直线
      • 判断拟合曲线的拟合效果
    • 8.2 局部加权线性回归
    • 8.3 示例:预测鲍鱼的年龄
    • 8.4 缩减系数来“理解”数据
      • 岭回归
      • 前向逐步回归
    • 8.5 权衡偏差与方差
    • 8.6 示例:预测乐高玩具套装的价格
    • 8.7本章小结

第八章 预测数值型数据:回归

引言

分类的目标变量是标称型数据。

回归与分类的不同,就在于其目标变量是连续数值型。

线性回归优缺点

  • 优点:结果易于理解,计算上不复杂。
  • 缺点:对非线性的数据拟合不好。
  • 适用数据类型:数值型和标称型数据

回归的一般方法 :

  1. 收集数据:采用任意方法收集数据。
  2. 准备数据:回归需要数值型数据,标称型数据将被转成二值型数据。
  3. 分析数据:绘出数据的可视化二维图将有助于对数据做出理解和分析,在采用缩减法 求得新回归系数之后, 可以将新拟合线绘在图上作为对比。
  4. 训练算法:找到回归系数。
  5. 测试算法:使用幻或者预测值和数据的拟合度,来分析模型的效果。
  6. 使用算法:使用回归,可以在给定输入的时候预测出一个数值,这是对分类方法的提升,因为这样可以预测连续型数据而不仅仅是离散的类别标签。

与分类使用步骤基本一致。

8 . 1 用线性回归找到最佳拟合直线

回归的目的是预测数值型的目标值。

预测结果表示为:
Y = X T W Y = X^TW Y=XTW

矩阵X表示输入数据,向量W表示回归系数。

使误差最小的W通常为我们寻找的回归系数。

误差是指预测值和真实值之间的差值,使用该误差的简单累加将使得正差值和负差值相互抵消,所以我们采用平方误差。

最佳估计回归系数表示

w ^ = ( X T X ) − 1 X T y \hat{w} = (X^TX)^{-1}X^Ty w^=(XTX)1XTy

给出数据的最佳拟合直线

数据如下图所示:
《机器学习实战》学习笔记(八)_第1张图片

打印数据点图像代码如下:

import matplotlib.pyplot as plt
import numpy as np

# 加载数据
def loadDataSet(fileName):
    #测量特征个数
    numFeat = len(open(fileName).readline().split('\t')) - 1
    
    xArr = []; yArr = []
    fr = open(fileName)

    for line in fr.readlines():
        lineArr =[]
        curLine = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curLine[i]))
        xArr.append(lineArr)
        yArr.append(float(curLine[-1]))
    return xArr, yArr

# 绘制数据集
def plotDataSet():
    #加载数据集
    xArr, yArr = loadDataSet(r'./Ch08/ex0.txt')       
    #数据个数                             
    n = len(xArr)                                                   
    #样本点 
    xcord = []; ycord = []                                               
    for i in range(n):                                 
        #样本点                  
        xcord.append(xArr[i][1]); ycord.append(yArr[i])                
    #添加subplot    
    fig = plt.figure()
    ax = fig.add_subplot(111)                                            
    #绘制样本点
    ax.scatter(xcord, ycord, s = 20, c = 'blue',alpha = .5)            
    #绘制title   
    plt.title('DataSet')                                              
    plt.xlabel('X')
    plt.show()

plotDataSet()

根据散点图与回归系数求解方法得到拟合直线,实现代码如下:

#计算回归系数
def standRegres(xArr,yArr):
    
    xMat = np.mat(xArr); yMat = np.mat(yArr).T
    #根据文中推导的公示计算回归系数
    xTx = xMat.T * xMat                           
    if np.linalg.det(xTx) == 0.0:
        print("矩阵为奇异矩阵,不能求逆")
        return
    #求出回归系数
    ws = xTx.I * (xMat.T*yMat)
    return ws

#绘制回归曲线和数据点
def plotRegression():
    #加载数据集
    xArr, yArr = loadDataSet(r'./Ch08/ex0.txt')                                    
    #求出回归系数
    ws = standRegres(xArr, yArr)                                  
    #创建xMat矩阵      
    xMat = np.mat(xArr)      
    #创建yMat矩阵                                              
    yMat = np.mat(yArr)         
    #深拷贝xMat矩阵                                           
    xCopy = xMat.copy()   
    #排序                                                 
    xCopy.sort(0)           
    #计算对应的y值                                             
    yHat = xCopy * ws                                                     
    fig = plt.figure()
    #添加subplot
    ax = fig.add_subplot(111)              
    #绘制回归曲线                              
    ax.plot(xCopy[:, 1], yHat, c = 'red')      
    #绘制样本点                          
    ax.scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue',alpha = .5)  
    #绘制title           
    plt.title('DataSet')                                                
    plt.xlabel('X')
    plt.show()


plotRegression()

得到图像如下所示:
《机器学习实战》学习笔记(八)_第2张图片
如上图所示拟合直线穿过散点图,大致穿过散点图中心。

判断拟合曲线的拟合效果

计算预测值yHat序列和真实值y序列的匹配程度,可以通过计算这两个序列的相关系数得到。

使用,Numpy库提供的相关系数的计算方法corrcoef()来得到相关系数。
代码实现如下:

#加载数据集
xArr, yArr = loadDataSet(r'./Ch08/ex0.txt')    
#计算回归系数                              
ws = standRegres(xArr, yArr)           
#创建xMat矩阵                            
xMat = np.mat(xArr)   
#创建yMat矩阵                                                 
yMat = np.mat(yArr)                                                  
yHat = xMat * ws
print(np.corrcoef(yHat.T, yMat))

输出结果如下所示:
在这里插入图片描述
从上图可知对角线上的数据是1.0,因为yMat和自己的匹
配是最完美的,而yHat和yMat相关系数为0.98。

8.2 局部加权线性回归

线性回归的一个问题是有可能出现欠拟合现象,因为它求的是具有最小均方误差的无偏估计。在估计中引入一
些偏差,从而降低预测的均方误差可以改善欠拟合现象。

局部加权线性回归(Locally Weighted Linear Regression, LWLR )是引入偏差的算法。在该算法中,我们给待预测点附近的每个点赋予一定的权重。在这个子集上基于最小均方差来进行普通的回归。与kNN一样 , 这种算法每次预测均需要事先选取出对应的数据子集。
该算法解出回归系数w的形式如下:
w ^ = ( X T W X ) − 1 X T W y \hat{w} = (X^TWX)^{-1}X^TWy w^=(XTWX)1XTWy

W是一个矩阵,用来给每个数据点赋予权重

LWLR使用“核”(与支持向量机中的核类似)来对附近的点赋予更高的权重。常用的高斯核对应的权重如下:
w ( i , i ) = e x p ( ∣ x ( i ) − x ∣ − 2 k 2 ) w(i,i) = exp(\frac{|x^{(i)}-x|}{-2k^2}) w(i,i)=exp(2k2x(i)x)

K为用户指定的参数、 它决定了对附近的点赋予多大的权重 。
W为只含对角元素的权重矩阵,并且点x与x(i)越近,w(i,i)将会越大。

局部加权线性回归函数实现代码如下:

# 局部加权线性回归测试
def lwlrTest(testArr, xArr, yArr, k=1.0):  
    #计算测试数据集大小
    m = np.shape(testArr)[0]                      
    yHat = np.zeros(m)    
    #对每个样本点进行预测
    for i in range(m):                            
        yHat[i] = lwlr(testArr[i],xArr,yArr,k)
    return yHat

# 使用局部加权线性回归计算回归系数w
def lwlr(testPoint, xArr, yArr, k = 1.0):
    
    xMat = np.mat(xArr); yMat = np.mat(yArr).T
    m = np.shape(xMat)[0]
    #创建权重对角矩阵
    weights = np.mat(np.eye((m)))  
    #遍历数据集计算每个样本的权重                                     
    for j in range(m):                                                  
        diffMat = testPoint - xMat[j, :]                                 
        weights[j, j] = np.exp(diffMat * diffMat.T/(-2.0 * k**2))
    xTx = xMat.T * (weights * xMat)                                        
    if np.linalg.det(xTx) == 0.0:
        print("矩阵为奇异矩阵,不能求逆")
        return
    #计算回归系数
    ws = xTx.I * (xMat.T * (weights * yMat))                            
    return testPoint * ws


# 绘制多条局部加权回归曲
def plotlwlrRegression():
    
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    #加载数据集
    xArr, yArr = loadDataSet(r'./Ch08/ex0.txt')    
    #根据局部加权线性回归计算yHat                                
    yHat_1 = lwlrTest(xArr, xArr, yArr, 1.0)    
    #根据局部加权线性回归计算yHat                        
    yHat_2 = lwlrTest(xArr, xArr, yArr, 0.01)          
     #根据局部加权线性回归计算yHat                  
    yHat_3 = lwlrTest(xArr, xArr, yArr, 0.003)               
    #创建xMat矩阵            
    xMat = np.mat(xArr)                                                    
    #创建yMat矩阵
    yMat = np.mat(yArr)                                                    
    #排序,返回索引值
    srtInd = xMat[:, 1].argsort(0)                                        
    xSort = xMat[srtInd][:,0,:]
    fig, axs = plt.subplots(nrows=3, ncols=1,sharex=False, sharey=False, figsize=(10,8))  
    #绘制回归曲线                                      
    axs[0].plot(xSort[:, 1], yHat_1[srtInd], c = 'red')                        
    #绘制回归曲线
    axs[1].plot(xSort[:, 1], yHat_2[srtInd], c = 'red')                        
    #绘制回归曲线
    axs[2].plot(xSort[:, 1], yHat_3[srtInd], c = 'red')                        
    axs[0].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5)                #绘制样本点
    axs[1].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5)                #绘制样本点
    axs[2].scatter(xMat[:,1].flatten().A[0], yMat.flatten().A[0], s = 20, c = 'blue', alpha = .5)                #绘制样本点
    #设置标题,x轴label,y轴label
    axs0_title_text = axs[0].set_title(u'局部加权回归曲线,k=1.0',fontproperties=font)
    axs1_title_text = axs[1].set_title(u'局部加权回归曲线,k=0.01',fontproperties=font)
    axs2_title_text = axs[2].set_title(u'局部加权回归曲线,k=0.003',fontproperties=font)
    plt.setp(axs0_title_text, size=8, weight='bold', color='red')  
    plt.setp(axs1_title_text, size=8, weight='bold', color='red')  
    plt.setp(axs2_title_text, size=8, weight='bold', color='red')  
    plt.xlabel('X')
    plt.show()

                                             
plotlwlrRegression()

输出结果如下:
《机器学习实战》学习笔记(八)_第3张图片
上图为在不同K值时,拟合的结果。将所有的数据视为等权重,得出的最佳拟合直线与标准的回归一致。当k越小,拟合效果越好。但是当k过小,会出现很多抖动毛刺的情况,与数据点过于贴近,产生过拟合。

8.3 示例:预测鲍鱼的年龄

鲍鱼年龄可以从鲍鱼壳的层数推算得到。
使用局部加权回归得到拟合曲线,之后比较预测精度。


# 误差大小评价函数
def rssError(yArr, yHatArr):
    return ((yArr - yHatArr) **2).sum()

#加载数据
abX, abY = loadDataSet(r'./Ch08/abalone.txt')
print(u'训练集与测试集相同:局部加权线性回归,核k的大小对预测的影响:')
#局部加权线性回归测试
yHat01 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 10)

print('k=0.1时,误差大小为:',rssError(abY[0:99], yHat01.T))
print('k=1  时,误差大小为:',rssError(abY[0:99], yHat1.T))
print('k=10 时,误差大小为:',rssError(abY[0:99], yHat10.T))
print('')
print(u'训练集与测试集不同:局部加权线性回归,核k的大小是越小越好吗?更换数据集,测试结果如下:')
yHat01 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 10)
print('k=0.1时,误差大小为:',rssError(abY[100:199], yHat01.T))
print('k=1  时,误差大小为:',rssError(abY[100:199], yHat1.T))
print('k=10 时,误差大小为:',rssError(abY[100:199], yHat10.T))
print('')
print(u'训练集与测试集不同:简单的线性归回与k=1时的局部加权线性回归对比:')
print('k=1时,误差大小为:', rssError(abY[100:199], yHat1.T))
ws = standRegres(abX[0:99], abY[0:99])
yHat = np.mat(abX[100:199]) * ws
print(u'简单的线性回归误差大小:', rssError(abY[100:199], yHat.T.A))


输出结果如下:
《机器学习实战》学习笔记(八)_第4张图片
当k=0.1时,训练集误差小,但是应用于新的数据集之后,误差反而变大了。这就是经常说道的过拟合现象。这也表明一点,必须在未知数据上比较效果才能选取到最佳模型。训练的模型要在测试集比较它们的效果,而不是在训练集上。

8.4 缩减系数来“理解”数据

如果特征比样本点还多(n > m ) , 也就是说输入数据的矩阵X不是满秩矩阵。非满秩矩阵在求逆时会出现问题。为了解决这个问题,统计学家引入了岭回归(ridge regression)的概念。

岭回归

岭回归就是在矩阵 X T X X^TX XTX上加一个 λ I \lambda I λI 从而使得矩阵非奇异,进而能对 X T X + λ I X^TX + \lambda I XTX+λI求逆。

矩阵I是一个m*m的单位矩阵,对角线上元素全为1 , 其他元素全为0。 λ \lambda λ是一个用户定义的数值。

回归系数的计算公式将变成:
w ^ = ( X T X + λ I ) − 1 X T y \hat{w} = (X^TX+\lambda I)^{-1}X^Ty w^=(XTX+λI)1XTy

岭回归最先用来处理特征数多于样本数的情况,现在也用于在估计中加人偏差,从而得到更好的估计。这里通过引入 λ I \lambda I λI来限制了所有w之和,通过引人该惩罚项,能够减少不重要的参数,这个技术在统计学中也叫做缩减(shrinkage )。

缩减方法可以去掉不重要的参数,因此能更好地理解数据。此外,与简单的线性回归相比,缩减法能取得更好的预测效果。

为了使用岭回归和缩减技术,首先需要对特征做标准化处理。使每维特征具有相同的重要性(不考虑特征代表什么)。具体的做法是所有特征都减去各自的均值并除以方差。

岭回归函数实现如下:

# 岭回归
def ridgeRegres(xMat, yMat, lam = 0.2):
    
    xTx = xMat.T * xMat
    denom = xTx + np.eye(np.shape(xMat)[1]) * lam
    if np.linalg.det(denom) == 0.0:
        print("矩阵为奇异矩阵,不能转置")
        return
    ws = denom.I * (xMat.T * yMat)
    return ws

# 岭回归测试
def ridgeTest(xArr, yArr):
    
    xMat = np.mat(xArr); yMat = np.mat(yArr).T
    #数据标准化 #行与行操作,求均值
    yMean = np.mean(yMat, axis = 0)                       
    #数据减去均值
    yMat = yMat - yMean                                    
    #行与行操作,求均值
    xMeans = np.mean(xMat, axis = 0)                    
     #行与行操作,求方差
    xVar = np.var(xMat, axis = 0)                       
     #数据减去均值除以方差实现标准化
    xMat = (xMat - xMeans) / xVar                       
    #30个不同的lambda测试
    numTestPts = 30                                       
    #初始回归系数矩阵 
    wMat = np.zeros((numTestPts, np.shape(xMat)[1]))   
    #改变lambda计算回归系数
    for i in range(numTestPts):                          
        #lambda以e的指数变化,最初是一个非常小的数,
        ws = ridgeRegres(xMat, yMat, np.exp(i - 10))    
        #计算回归系数矩阵
        wMat[i, :] = ws.T                                 
    return wMat

# 绘制岭回归系数矩阵
def plotwMat():
    
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    abX, abY = loadDataSet('./Ch08/abalone.txt')
    redgeWeights = ridgeTest(abX, abY)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(redgeWeights)    
    ax_title_text = ax.set_title(u'log(lambada)与回归系数的关系', fontproperties = font)
    ax_xlabel_text = ax.set_xlabel(u'log(lambada)', fontproperties = font)
    ax_ylabel_text = ax.set_ylabel(u'回归系数', fontproperties = font)
    plt.setp(ax_title_text, size = 20, weight = 'bold', color = 'red')
    plt.setp(ax_xlabel_text, size = 10, weight = 'bold', color = 'black')
    plt.setp(ax_ylabel_text, size = 10, weight = 'bold', color = 'black')
    plt.show()

plotwMat()

输出结果如下:
《机器学习实战》学习笔记(八)_第5张图片
上图中,在最左边即 λ \lambda λ最小时,可以得到所有系数的原始值(与线性回归一致);而在右边,系数全部缩减成0;在中间部分的某值将可以取得最好的预测效果。为了定量地找到最佳参数值,还需要进行交叉验证。另外,要判断哪些变量对结果预测最具有影响力,在图中观察它们对应的系数大小就可以。

前向逐步回归

属于一种贪心算法,即每一步都尽可能减少误差。一开始,所有的权重都设为1,然后每一步所做的决策是对某个权重增加或减少一个很小的值。
该算法的伪代码如下所示:

数据标准化,使其分布满足0均值和单位方差
在每轮迭代过程中:
	设置当前最小误差1063七£2101为正无穷
	对每个特征:
		增大或缩小:
			改变一个系数得到一个新的时
			计算新时下的误差
			如果误差Error小于当前最小误差lowestError:设置Wbest等于当前的W
		将W设置为新的Wbest

前向逐步线性回归实现代码如下:

# 数据标准化
def regularize(xMat, yMat):
    #数据拷贝
    inxMat = xMat.copy()                                                       
    inyMat = yMat.copy()
    #行与行操作,求均值
    yMean = np.mean(yMat, 0)    
    #数据减去均值                                                
    inyMat = yMat - yMean            
    #行与行操作,求均值                                           
    inMeans = np.mean(inxMat, 0)                                
    #行与行操作,求方差                   
    inVar = np.var(inxMat, 0) 
    #数据减去均值除以方差实现标准化                                                   
    inxMat = (inxMat - inMeans) / inVar                                           
    return inxMat, inyMat

#  前向逐步线性回归
def stageWise(xArr, yArr, eps = 0.01, numIt = 100):
    
    #数据集
    xMat = np.mat(xArr); yMat = np.mat(yArr).T   
    #数据标准化                                      
    xMat, yMat = regularize(xMat, yMat)   
    #初始化numIt次迭代的回归系数矩阵                                            
    m, n = np.shape(xMat)
    #初始化回归系数矩阵
    returnMat = np.zeros((numIt, n))                                                
    ws = np.zeros((n, 1))                                                           
    wsTest = ws.copy()
    wsMax = ws.copy()
    #迭代numIt次
    for i in range(numIt):                                                            
        # print(ws.T)                                                                    #打印当前回归系数矩阵
        #正无穷
        lowestError = float('inf');                                                 
        #遍历每个特征的回归系数
        for j in range(n):                                                            
            for sign in [-1, 1]:
                wsTest = ws.copy()
                #微调回归系数
                wsTest[j] += eps * sign                                                
                #计算预测值
                yTest = xMat * wsTest                                                
                 #计算平方误差
                rssE = rssError(yMat.A, yTest.A)  
                #如果误差更小,则更新当前的最佳回归系数                                 
                if rssE < lowestError:                                                
                    lowestError = rssE
                    wsMax = wsTest
        ws = wsMax.copy()
        #记录numIt次迭代的回归系数矩阵
        returnMat[i,:] = ws.T                                                         
    return returnMat

# 绘制逐步线性回归系数矩阵
def plotstageWiseMat():
    
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    xArr, yArr = loadDataSet(r'./Ch08/abalone.txt')
    returnMat = stageWise(xArr, yArr, 0.005, 1000)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(returnMat)    
    ax_title_text = ax.set_title(u'前向逐步回归:迭代次数与回归系数的关系', fontproperties = font)
    ax_xlabel_text = ax.set_xlabel(u'迭代次数', fontproperties = font)
    ax_ylabel_text = ax.set_ylabel(u'回归系数', fontproperties = font)
    plt.setp(ax_title_text, size = 15, weight = 'bold', color = 'red')
    plt.setp(ax_xlabel_text, size = 10, weight = 'bold', color = 'black')
    plt.setp(ax_ylabel_text, size = 10, weight = 'bold', color = 'black')
    plt.show()

plotstageWiseMat()

输出如下:
《机器学习实战》学习笔记(八)_第6张图片
鲍鱼数据集上执行逐步线性回归法得到的系数与迭代次数间的关系。
逐步线性回归算法的主要的优点在于它可以帮助人们理解现有的模型并做出改进。当构建了一个模型后,可以运行该算法找出重要的特征,这样就有可能及时停止对那些不重要特征的收集。当应用缩减方法(如逐步线性回归或岭回归) 时 ,模型也就增加了偏差(bias),与此同时却减小了模型的方差。

8.5 权衡偏差与方差

对复杂的过程进行简化,这将导致在模型和测量值之间出现“噪声”或误差,若无法理解数据的真实生成过程,也会导致差异的发生。

如果降低核的大小,那么训练误差将变小。
《机器学习实战》学习笔记(八)_第7张图片
偏差方差折中与测试误差及训练误差的关系。上面的曲线就是测试误差,在中间部分最低。为了做出最好的预测,我们应该调整模型复杂度来达到测试误差的最小值。

8.6 示例:预测乐高玩具套装的价格

书里的数据集下不到,参考代码机器学习实战》学习笔记
数据来源是HTML文件。
载入数据代码如下:


from bs4 import BeautifulSoup
#从页面读取数据,生成retX和retY列表
def scrapePage(retX, retY, inFile, yr, numPce, origPrc):
     # 打开并读取HTML文件
    with open(inFile, encoding='utf-8') as f:
        html = f.read()
    soup = BeautifulSoup(html)
    i = 1
    # 根据HTML页面结构进行解析
    currentRow = soup.find_all('table', r = "%d" % i)
    while(len(currentRow) != 0):
        currentRow = soup.find_all('table', r = "%d" % i)
        title = currentRow[0].find_all('a')[1].text
        lwrTitle = title.lower()
        # 查找是否有全新标签
        if (lwrTitle.find('new') > -1) or (lwrTitle.find('nisb') > -1):
            newFlag = 1.0
        else:
            newFlag = 0.0
        # 查找是否已经标志出售,我们只收集已出售的数据
        soldUnicde = currentRow[0].find_all('td')[3].find_all('span')
        if len(soldUnicde) == 0:
            print("商品 #%d 没有出售" % i)
        else:
            # 解析页面获取当前价格
            soldPrice = currentRow[0].find_all('td')[4]
            priceStr = soldPrice.text
            priceStr = priceStr.replace('$','')
            priceStr = priceStr.replace(',','')
            if len(soldPrice) > 1:
                priceStr = priceStr.replace('Free shipping', '')
            sellingPrice = float(priceStr)
            # 去掉不完整的套装价格
            if  sellingPrice > origPrc * 0.5:
                print("%d\t%d\t%d\t%f\t%f" % (yr, numPce, newFlag, origPrc, sellingPrice))
                retX.append([yr, numPce, newFlag, origPrc])
                retY.append(sellingPrice)
        i += 1
        currentRow = soup.find_all('table', r = "%d" % i)
 
#依次读取六种乐高套装的数据,并生成数据矩阵
def setDataCollect(retX, retY):
    scrapePage(retX, retY, './Ch08/setHtml/lego8288.html', 2006, 800, 49.99)  # 2006年的乐高8288,部件数目800,原价49.99
    scrapePage(retX, retY, './Ch08/setHtml/lego10030.html', 2002, 3096, 269.99)  # 2002年的乐高10030,部件数目3096,原价269.99
    scrapePage(retX, retY, './Ch08/setHtml/lego10179.html', 2007, 5195, 499.99)  # 2007年的乐高10179,部件数目5195,原价499.99
    scrapePage(retX, retY, './Ch08/setHtml/lego10181.html', 2007, 3428, 199.99)  # 2007年的乐高10181,部件数目3428,原价199.99
    scrapePage(retX, retY, './Ch08/setHtml/lego10189.html', 2008, 5922, 299.99)  # 2008年的乐高10189,部件数目5922,原价299.99
    scrapePage(retX, retY, './Ch08/setHtml/lego10196.html', 2009, 3263, 249.99)  # 2009年的乐高10196,部件数目3263,原价249.99


lgX = []
lgY = []
setDataCollect(lgX, lgY)

输出如下:
《机器学习实战》学习笔记(八)_第8张图片
需要安装一个bs4模块。安装过程没什么强调。

利用现有数据集构建模型,构建出的模型可以对售价做出预测:
实现代码如下:

# 使用简单的线性回归
def useStandRegres():
    
    lgX = []
    lgY = []
    #读取数据
    setDataCollect(lgX, lgY)
    data_num, features_num = np.shape(lgX)
    
    lgX1 = np.mat(np.ones((data_num, features_num + 1)))
    lgX1[:, 1:5] = np.mat(lgX)
    ws = standRegres(lgX1, lgY)
    print('%f%+f*年份%+f*部件数量%+f*是否为全新%+f*原价' % (ws[0],ws[1],ws[2],ws[3],ws[4]))     
 
useStandRegres()

输出结果如下:
《机器学习实战》学习笔记(八)_第9张图片
该模型认为套装的售价应该采用如下公式计算:5319.970081-27.592822年份-0.026839部件数量-11.220848是否为全新+2.576041原价

它对于数据拟合得很好,但看上去没有什么道理。从公式看,套装里零部件越多售价反而会越低。另外,该公式对新套装也有一定的惩罚。

使用缩减法中一种,即岭回归再进行一次实验。用缩减法确定最佳回归系数
实现代码如下:

import random

# 交叉验证岭回归
def crossValidation(xArr, yArr, numVal = 10):
    
    #统计样本个数 
    m = len(yArr)           
    #生成索引值列表                                                                                   
    indexList = list(range(m))                                                          
    #create error mat 30columns numVal rows
    errorMat = np.zeros((numVal,30))                                                    
    #交叉验证numVal次
    for i in range(numVal):                                                             
        #训练集   
        trainX = []; trainY = []                                                        
        #测试集
        testX = []; testY = []                                                          
         #打乱次序 
        random.shuffle(indexList)                                                       
        #划分数据集:90%训练集,10%测试集
        for j in range(m):                                                                
            if j < m * 0.9:
                trainX.append(xArr[indexList[j]])
                trainY.append(yArr[indexList[j]])
            else:
                testX.append(xArr[indexList[j]])
                testY.append(yArr[indexList[j]])
        #获得30个不同lambda下的岭回归系数
        wMat = ridgeTest(trainX, trainY)                                                
        #遍历所有的岭回归系数
        for k in range(30):                                                            
            #测试集  
            matTestX = np.mat(testX); matTrainX = np.mat(trainX)                       
            #测试集均值
            meanTrain = np.mean(matTrainX,0)                                           
            #测试集方差
            varTrain = np.var(matTrainX,0)                                               
            #测试集标准化 
            matTestX = (matTestX - meanTrain) / varTrain                                 
            #根据ws预测y值
            yEst = matTestX * np.mat(wMat[k,:]).T + np.mean(trainY)                        
            #统计误差
            errorMat[i, k] = rssError(yEst.T.A, np.array(testY))                       
    #计算每次交叉验证的平均误差     
    meanErrors = np.mean(errorMat,0)                                                   
    #找到最小误差
    minMean = float(min(meanErrors))                                                    
    #找到最佳回归系数
    bestWeights = wMat[np.nonzero(meanErrors == minMean)]                                
    xMat = np.mat(xArr); yMat = np.mat(yArr).T
    meanX = np.mean(xMat,0); varX = np.var(xMat,0)
    #数据经过标准化,因此需要还原
    unReg = bestWeights / varX                                                            
    print('%f%+f*年份%+f*部件数量%+f*是否为全新%+f*原价' % ((-1 * np.sum(np.multiply(meanX,unReg)) + np.mean(yMat)), unReg[0,0], unReg[0,1], unReg[0,2], unReg[0,3]))    


lgX = []
lgY = []
setDataCollect(lgX, lgY)
crossValidation(lgX, lgY)

输出如下图:
《机器学习实战》学习笔记(八)_第10张图片
修改后认为套装的售价应该采用如下公式计算:64939.280738-32.405045年份-0.003741部件数量-30.903002是否为全新+2.421093原价

比较值钱的公式,发现没有太大差异。公式对于部件数与是否为全新仍存在惩罚。

观察在缩减过程中回归系数是如何变化的。
编写代码如下:


lgX = []
lgY = []
setDataCollect(lgX, lgY)
# crossValidation(lgX, lgY)
print(ridgeTest(lgX, lgY))

借用之前的岭回归测试代码。

输出结果为:
《机器学习实战》学习笔记(八)_第11张图片

这些系数是经过不同程度的缩减得到的,看运行结果的第一行,可以看到最大的是第4项,第二大的是第2项。

因此,如果只选择一个特征来做预测的话,我们应该选择第4个特征,也就是原始加个。如果可以选择2个特征的话,应该选择第4个和第2个特征。(选择值较大的特征)

这种分析方法使得我们可以挖掘大量数据的内在规律。在仅有4个特征时,该方法的效果也许并不明显;但如果有100个以上的特征,该方法就会变得十分有效:它可以指出哪个特征是关键的,而哪些特征是不重要的。

8.7本章小结

与分类一样,回归也是预测目标值的过程。回归与分类的不同点在于,前者预测连续型变量,而后者预测离散型变量。

在回归方程里,求得特征对应的最佳回归系数的方法是最小化误差的平方和。

数据集上计算出的回归方程并不一定意味着它是最佳的,可以便用预测值yHat和原始值y的相关性来度量回归方程的好坏。

当数据的样本数比特征数还少时候,矩阵 X T X X^TX XTX的逆不能直接计算,可以考虑使用岭回归,因为当 X T X X^TX XTX的逆不能计算时,它仍保证能求得回归参数。

缩减法还可以看做是对一个模型增加偏差的同时减少方差。偏差方差折中是一个重要的概念 ,可以帮助我们理解现有模型并做出改进,从而得到更好的模型。

开始的过程我感觉和第四章逻辑回归有些类似。第四章是找到回归系数,并以此分类。相同的是都在找回归系数,不同的是,一个是为了划分数据,一个是为了预测数据。

你可能感兴趣的:(机器学习,学习,人工智能)