zoukankan      html  css  js  c++  java
  • 机器学习作业---偏差和方差(线性回归)错误反例,但是理清了代码思路,很重要

    重点思考排错:关于高级优化算法scipy.optimize.minimize

    (一)代价函数和梯度求解

    在代价函数和梯度求解中,我们要多次用到矩阵乘法。

    1.numpy.matrix(不推荐)

    所以一开始,我使用了numpy.matrix()方法,将我传入的θ、X、y等向量(是numpy.ndarray数组类型),全部转换为matrix类型,进行矩阵运算

    但是在使用过程中,发现编译器并不推荐使用该方法,因为:

    1.我们需要将传入的参数进行转换,降低了效率

    2.如果一个程序里面既有matrix 又有array,会让人脑袋大。但是如果只用array,你不仅可以实现matrix所有的功能,还减少了编程和阅读的麻烦。

    2.numpy.ndarray+@---解决部分问题,但是使用的方法有点问题

    使用过程中,我发现对于numpy.ndarray类型数据,如果要进行矩阵运算,那么数组必须是二维数组[[]]

    这里我经常用@将ndarray进行矩阵运算

    但是在使用过程中,发现一个错误用法。就是我们如果对于一个ndarray类型数据,是一维数组,那么似乎矩阵运算并不能按照我们的想法进行:

    1.比如进行转置T操作:

    2.比如进行矩阵乘法操作:

    可以看到,虽然结果是一样的,但是返回的分别是二维和一维数组。而且我们对一维数组的转置,存在部分疑问?因为转置后还是行向量显示,结果却是按照列向量计算

    3.numpy.ndarray+dot点乘处理,避免2中问题 

    当我们传入两个一维数组时:

    是进行点乘处理的。 

    当我们传入一个二维和一维数组时:---两个向量必须同维度(得到的结果是矩阵乘积)

    总结:推荐使用第三种用法---其中另一个原因看下面

    (二)高级优化算法scipy.optimize.minimize解析

    调用:

    scipy.optimize.minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)

    参数:

    fun :优化的目标函数

    x0 :初值,一维数组,shape (n,)

    args : 元组,可选,额外传递给优化函数的参数

    method:求解的算法,选择TNC则和fmin_tnc()类似

    jac:返回梯度向量的函数

    返回:

    返回优化结果对象,x:优化问题的目标数组。success: True表示成功与否,不成功会给出失败信息。

    注意重点:

    1.我们返回的参数θ向量就是返回的优化结果对象x。

    2.我们返回的优化结果对象x是一个numpy.ndarray类型的一维数组,所以我们可以认为,在我们中间进行计算过程中:

    我们使用梯度求解函数求解出来的θ参数向量也应该是一个一维数组,同时在优化函数中会将梯度求解结果作为参数传入到代价函数中,求解代价误差。所以在中间黑盒过程中,默认传递的就是一维数组(上面参数中说明了,因为我们传入的θ向量默认是放入x0中)。所以对于我们写的求解代价函数,传入的θ参数也应该是一维数组。

    3.我们只提供了一个x0和args参数,所以我们要保证我们的fun传入的代价函数和jac传入的梯度求解函数的参数保持一致

    4.我们可以使用一个函数,返回梯度和代价误差,只需要设置jac=True即可

    minimize(fun=backporp,x0=theta_param,args=(input_size,hidden_size,num_labels,X,y_onehot,1),method='TNC',jac=True,options={'maxiter':250})

    (三)补充fmin_tnc()

    有约束的多元函数问题,提供梯度信息,使用截断牛顿法。

    调用:

    scipy.optimize.fmin_tnc(func, x0, fprime=None, args=(), approx_grad=0, bounds=None, epsilon=1e-08, scale=None, offset=None, messages=15, maxCGit=-1, maxfun=None, eta=-1, stepmx=0, accuracy=0, fmin=0, ftol=-1, xtol=-1, pgtol=-1, rescale=-1, disp=None, callback=None)

    最常使用的参数:

    func:优化的目标函数

    x0:初值

    fprime:提供优化函数func的梯度函数,不然优化函数func必须返回函数值和梯度,或者设置approx_grad=True

    approx_grad :如果设置为True,会给出近似梯度

    args:元组,是传递给优化函数的参数

    返回:

    x : 数组,返回的优化问题目标值

    nfeval : 整数,function evaluations的数目

    在进行优化的时候,每当目标优化函数被调用一次,就算一个function evaluation。在一次迭代过程中会有多次function evaluation。这个参数不等同于迭代次数,而往往大于迭代次数。

    rc : int,Return code, see below 

    一:方差、偏差问题回顾

    (一)高偏差(欠拟合)

    1.当m较小时:比如m=1,这是完全拟合,所以训练误差从0开始。当逐渐增大样本数量,训练误差逐渐增大。同样,当m较小时,验证误差最大,当m逐渐增大,则验证误差减小。

    2.当我们增大样本容量时:对于这组数据这是拟合得最好得直线,当我们增大样本容量后,直线拟合得越来越好---即J_cv逐渐减小

     

    总结: 

    训练误差一开始也是很小的,而在高偏差的情况下,你会发现训练集误差会逐渐增大,最后接近交叉验证误差

    (二)高方差(过拟合)

    1.训练误差---当训练样本越多的时候,就越难把训练集数据拟合得更好。但总的来说训练集误差还是很小得

    2.交叉验证误差---过拟合下(泛化能力很差),验证误差将会一直很大,尽管随着样本增大,但是总的来说还是很大

    其特点是:在训练误差和交叉验证误差之间有一段很大的差距。

    当我们增大样本数量时,训练误差会增大,而验证误差会减少。所以高方差下,增大样本数量还是有用的。

    (三)简单总结

    训练集误差和交叉验证集误差近似时:偏差/欠拟合
    交叉验证集误差远大于训练集误差时:方差/过拟合

    二:正则化和方差、偏差 

    (一)当我们设置入较大时

    比如入=1000,这时θ_1,...,θ_m都将受到很大的惩罚。所以θ_1,...,θ_m几乎都等于0.这时H_θ(x)≈θ_0 

    因此这个假设处于高偏差,对数据集严重欠拟合。

    (二)设置入较小时

    与之对应的另一种情况是,如果我们的lambda值很小,比如说lambda的值等于0的时候,在这种情况下,如果我们要拟合一个高阶多项式的话,那么此时我们通常会处于过拟合的情况。

    (三)合适大小入

    只有当我们取一个中间大小的,既不大也不小的lambda值时,我们才会得到一组合理的,对数据刚好拟合的theta参数值

    (四)总结

     

     • 当入较小时,训练集误差较小(过拟合)而交叉验证集误差较大--高方差

     • 随着入的增加,训练集误差不断增加(欠拟合),而交叉验证集误差则是先减小后增加---高偏差

     注意:训练集误差是指内部数据集与模型的拟合程度(所以当入小的时候,模型虽然是过拟合,但是与原来训练集的拟合程度还是不错的),验证集误差是指新的数据集与原始模型的平均误差平方和(当入过小或者过大,都会导致误差太大)。

    三:数据加载、显示----后面可能都有问题

    (一)代码实现数据加载、显示

    import matplotlib.pyplot as plt
    import numpy as np
    import scipy.io as sio
    import scipy.optimize as opt
    
    
    data = sio.loadmat("ex5data1.mat")
    X = data['X']
    y = data['y']
    Xval = data['Xval']
    yval = data['yval']
    Xtest = data['Xtest']
    ytest = data['ytest']
    
    plt.figure()
    plt.scatter(X,y,c='r',marker='o')
    plt.xlabel("Change in water level (x)")
    plt.ylabel("Water flowing out of the dam (y)")
    plt.show()

    (二)数据展示

    print("X.shape X")
    print(X.shape)
    print(X)
    print("y.shape y")
    print(y.shape)
    print(y)
    print("----------------------")
    X.shape X
    (12, 1)
    [[-15.93675813]
     [-29.15297922]
     [ 36.18954863]
     [ 37.49218733]
     [-48.05882945]
     [ -8.94145794]
     [ 15.30779289]
     [-34.70626581]
     [  1.38915437]
     [-44.38375985]
     [  7.01350208]
     [ 22.76274892]]
    ----------------------
    y.shape y
    (12, 1)
    [[ 2.13431051]
     [ 1.17325668]
     [34.35910918]
     [36.83795516]
     [ 2.80896507]
     [ 2.12107248]
     [14.71026831]
     [ 2.61418439]
     [ 3.74017167]
     [ 3.73169131]
     [ 7.62765885]
     [22.7524283 ]]
    print("----------------------")
    print("Xval.shape yval.shape")
    print(Xval.shape,yval.shape)
    print("----------------------")
    print("Xtest.shape ytest.shape")
    print(Xtest.shape,ytest.shape)

    二:正则化线性回归代价函数

    (一)代价函数

    def reg_cost(theta,X,y,lamda=1):
        m = X.shape[0]
        return 1/(2*m)*np.sum(np.power(X@theta.T-y,2))+lamda/(2*m)*np.sum(np.power(theta,2))
    cost = reg_cost(theta,X,y,0)
    print(cost)

    (二)正则化梯度求解----主要出错地点

    def reg_gradient(theta,X,y,lamda=1):
    m = X.shape[0]
    error = X@theta.T-y
    theta_grad = error.T@X/m
    theta_grad[0,1:] += theta[0,1:]*lamda/m
    return theta_grad
    grad = reg_gradient(theta,X,y,0)
    print(grad)

    grad = reg_gradient(theta,X,y,1)
    print(grad)

    三:高级优化算法求解参数

    #导入数据
    data = sio.loadmat("ex5data1.mat")
    X = data['X']
    y = data['y']
    Xval = data['Xval']
    yval = data['yval']
    Xtest = data['Xtest']
    ytest = data['ytest']

    theta = np.array([np.ones(2)])
    X = np.c_[np.ones(X.shape[0]),X]

    res = opt.minimize(fun=reg_cost,x0=theta,args=(X,y,1),method="TNC",jac=reg_gradient,options={'disp':True})
    theta_new = res.get('x')
    plt.figure()
    plt.scatter(X[:,1],y,c='b',marker='o')
    plt.plot(X[:,1],X[:,1]*theta_new[1]+theta_new[0])
    plt.xlabel("Change in water level (x)")
    plt.ylabel("Water flowing out of the dam (y)")
    plt.show()

    这里就已经出错了,最后求解的参数向量不对!!! 

    四:绘制学习曲线---开始明显出错(只要出现错误是在梯度求解函数中)

    注意: 

    1.使用训练集的子集来拟合数据--获取本次循环最优参数

    2.在计算训练代价(误差)和交叉验证代价(误差)时,不需要使用正则化

    3.使用相同的验证集自己来计算训练代价

    (一)获取训练误差和验证误差

    def learning_curve(X,y,Xval,yval,lamda):
        m = X.shape[0]
    
        #训练集误差和验证集误差
        error_train = np.zeros(m)
        error_val = np.zeros(m)
    
        #从一个训练样本开始逐个增加
        for i in range(m):
            X_temp = X[:i+1,:]  #获取新的临时训练样本集---注意事项1
            y_temp = y[:i+1,:]
            #获取当前训练集样本下的最优参数θ向量
            res = opt.minimize(fun=reg_cost,x0=theta,args=(X_temp,y_temp,lamda),method="TNC",jac=reg_gradient,options={'disp':False})
            #根据上面获得的最优参数向量,我们获取训练集误差---注意:训练集误差的数据集来自于我们新的训练集
            theta = np.array([res.x])
            error_train[i] = reg_cost(theta,X_temp,y_temp,lamda)
            #验证集误差,数据集来自于全部验证集
            error_val[i] = reg_cost(theta,Xval,yval,lamda)  #每次都是将所有验证集进行计算误差----注意事项3
    
        return error_train,error_val
    #绘制学习曲线
    error_train,error_val = learning_curve(X,y,Xval,yval,0)  #设置lamda为0,不进行正则化处理---注意事项2
    print("error_train:
    ",error_train)
    print("error_val:
    ",error_val)

    (二)绘制学习曲线 

    #绘制学习曲线
    error_train,error_val = learning_curve(X,y,Xval,yval,0)
    plt.figure()
    plt.plot(np.arange(X.shape[0]),error_train,np.arange(X.shape[0]),error_val)
    plt.title("Learning Curve for linear regression")
    plt.xlabel("Number of Training Examples")
    plt.ylabel("Error")
    plt.legend(['Train','Cross Validation'])
    plt.show()

    这里就发现了,和我们真正要展示的学习曲线有所差异:

    太过混乱,不进行修改!!!!

  • 相关阅读:
    bzoj3527: [Zjoi2014]力 fft
    bzoj3295: [Cqoi2011]动态逆序对 cdq分治
    快速读入fread
    km板子(二分图最大权匹配)
    spfa毒瘤算法
    牛客网暑期ACM多校训练营(第三场)DEncrypted String Matching fft
    P4173 残缺的字符串 fft
    圆和多边形交模板
    hdu多校2C
    Codeforces Round #449 (Div. 1)C
  • 原文地址:https://www.cnblogs.com/ssyfj/p/12906694.html
Copyright © 2011-2022 走看看