Advertisement

李宏毅2020机器学习作业1——Linear Regression

阅读量:

这篇文章详细描述了如何利用Python和Jupyter Notebook完成空气质量预测任务。以下是总结:
环境与数据获取
使用Google Colab作为运行环境,数据集从“李宏毅机器学习作业说明”资源中获取,路径为“E:/jupyter/data/hw1/train.csv”和“E:/jupyter/data/hw1/test.csv”。需要下载网盘链接获取数据。
数据预处理

  • 数据读取与转换:使用pandas读取数据,将PM2.5列转换为数值类型,处理缺失值(用“NR”表示,转换为0)。
  • 归一化处理:对数据进行标准化,确保特征之间的尺度相近,以提高模型训练效果。
  • 数据重组:将一个月的数据分成12个样本,每个样本包含18个变量,每个变量有9个时间点的数据。一个月的数据被分解为12个样本,每个样本有162个数据点(18变量×9时间点)。
  • 拆分数据集:将数据拆分成训练集和验证集,比例为80:20。
    模型构建与训练
  • 线性回归模型:使用最小二乘法拟合数据,损失函数为均方根误差。
  • 优化算法:使用RMSprop优化算法,但在代码中未正确应用,而是直接使用梯度平方进行优化,可能导致梯度消失问题。
  • 超参数设置:学习率、迭代次数等参数被设置,训练30000次,损失值逐步收敛。

课程背景

数据准备

实验环境

任务要求

代码实现

  • sys:提供对解释器的使用和维护所需的变量访问权限,同时支持与解释器的深度交互。
  • pandas:提供了一套高效处理和分析结构化数据的强大工具集。
  • numpy:作为Python的一个扩展库,支持高效处理多维数组和矩阵运算。
  • math:专门用于执行数学运算的基础库。

无需库的用户请自行安装(Jupyter Notebook的安装方法:进入自己的环境后,使用conda命令安装名为 命名词 的库即可完成安装。)

复制代码
    import sys
    import pandas as pd
    import numpy as np
    import math

现在导入train数据

复制代码
    #导入数据(前面‘’为数据存放路径,后面big5对字符串进行编码转换)
    data = pd.read_csv('E:/jupyter/data/hw1/train.csv',encoding='big5')

我们的数据是csv格式用excel打开会出现乱码,可以用Notepad++打开

在这里插入图片描述

对数据进行处理,取第4列开始的数据

复制代码
    #分割出前3列,从第4列开始将数据存到data
    data = data.iloc[:, 3:]
    data[data == 'NR'] = 0
    raw_data = data.to_numpy()
    print(raw_data)

运行之后结果,对照train数据可以看出,前3列数据已经被删掉了

复制代码
    [['14' '14' '14' ... '15' '15' '15']
     ['1.8' '1.8' '1.8' ... '1.8' '1.8' '1.8']
     ['0.51' '0.41' '0.39' ... '0.35' '0.36' '0.32']
     ...
     ['36' '55' '72' ... '118' '100' '105']
     ['1.9' '2.4' '1.9' ... '1.5' '2' '2']
     ['0.7' '0.8' '1.8' ... '1.6' '1.8' '2']]

我们再对数据进行重组,将原始的4320×24的数据按照每月重组成12个18×480的数据

在这里插入图片描述
在这里插入图片描述
复制代码
    #对data进行调整,将4320*24重组为12*18*480
    month_data = {}
    for month in range(12):
    sample = np.empty([18,480])
    for day in range(20):
        sample[:,day * 24 : ( day + 1 ) * 24] = raw_data [ 18 * ( 20 * month + day ) : 18 * ( 20 * month + day + 1 ),: ]
    month_data[month] = sample

按照作业要求,遵循每9个小时的数据集来构建预测模型,用于预测第10个小时的PM2.5浓度。每日24小时中,每9小时构成一个数据样本,第10小时作为目标变量(Label)。这样,每日可生成24-9=15个数据样本和目标变量。每个自然月可提供300个数据样本,因此一年将拥有12×20×15=3600个数据样本。由于一个月的20天是连续的,可以将20天的480个小时视为连续时间,因此一个月将产生480-9=471个数据样本,全年则有471×12=5652个数据样本。同时,每个数据样本对应一个Label,因此全年也将有5652个目标变量(第10个小时的PM2.5浓度)。通过这种方法,可以有效构建充足的训练数据集。每个数据样本中包含9×18个测量数据。

在这里插入图片描述

使用如下代码实现:

复制代码
    x = np.empty([12*471,18*9],dtype = float)
    y = np.empty([12*471,1],dtype = float)
    for month in range(12):
    for day in range(20):
        for hour in range(24):
            if day == 19 and hour>14:
                continue
            x[month * 471 + day * 24 + hour, :] = month_data[month][:,day * 24 + hour : day * 24 + hour + 9].reshape(1,-1)
            y[month * 471 + day * 24 + hour, 0] = month_data[month][9,day * 24 + hour + 9]
    print(x)
    print(y)

我们可以看一下运行结果,

复制代码
    [[14.  14.  14.  ...  2.   2.   0.5]
     [14.  14.  13.  ...  2.   0.5  0.3]
     [14.  13.  12.  ...  0.5  0.3  0.8]
     ...
     [17.  18.  19.  ...  1.1  1.4  1.3]
     [18.  19.  18.  ...  1.4  1.3  1.6]
     [19.  18.  17.  ...  1.3  1.6  1.8]]
    [[30.]
     [41.]
     [44.]
     ...
     [17.]
     [24.]
     [29.]]

如下图所示,对比train.cxv数据可以看出data和label已经被构建出来

在这里插入图片描述

对数据实施归一化处理。从训练集中划分一部分作为验证集,其目的是为了验证模型的性能。

复制代码
    #归一化
    mean_x = np.mean(x,axis = 0)
    std_x = np.std(x,axis = 0)
    for i in range(len(x)):
    for j in range(len(x[0])):
        if std_x[j] != 0:
            x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]
    #将训练集分成训练-验证集,用来最后检验我们的模型
    x_train_set = x[: math.floor(len(x) * 0.8), :]
    y_train_set = y[: math.floor(len(y) * 0.8), :]
    x_validation = x[math.floor(len(x) * 0.8): , :]
    y_validation = y[math.floor(len(y) * 0.8): , :]
    print(x_train_set)
    print(y_train_set)
    print(x_validation)
    print(y_validation)
    print(len(x_train_set))
    print(len(y_train_set))
    print(len(x_validation))
    print(len(y_validation))

运行结果

复制代码
    [[-1.35825331 -1.35883937 -1.359222   ...  0.26650729  0.2656797
      -1.14082131]
     [-1.35825331 -1.35883937 -1.51819928 ...  0.26650729 -1.13963133
      -1.32832904]
     [-1.35825331 -1.51789368 -1.67717656 ... -1.13923451 -1.32700613
      -0.85955971]
     ...
     [ 0.86929969  0.70886668  0.38952809 ...  1.39110073  0.2656797
      -0.39079039]
     [ 0.71018876  0.39075806  0.07157353 ...  0.26650729 -0.39013211
      -0.39079039]
     [ 0.3919669   0.07264944  0.07157353 ... -0.38950555 -0.39013211
      -0.85955971]]
    [[30.]
     [41.]
     [44.]
     ...
     [ 7.]
     [ 5.]
     [14.]]
    [[ 0.07374504  0.07264944  0.07157353 ... -0.38950555 -0.85856912
      -0.57829812]
     [ 0.07374504  0.07264944  0.23055081 ... -0.85808615 -0.57750692
       0.54674825]
     [ 0.07374504  0.23170375  0.23055081 ... -0.57693779  0.54674191
      -0.1095288 ]
     ...
     [-0.88092053 -0.72262212 -0.56433559 ... -0.57693779 -0.29644471
      -0.39079039]
     [-0.7218096  -0.56356781 -0.72331287 ... -0.29578943 -0.39013211
      -0.1095288 ]
     [-0.56269867 -0.72262212 -0.88229015 ... -0.38950555 -0.10906991
       0.07797893]]
    [[13.]
     [24.]
     [22.]
     ...
     [17.]
     [24.]
     [29.]]
    4521
    4521
    1131
    1131

Training:

  • 设置超参数:学习率,迭代次数等
  • 计算损失L
  • 计算梯度gradient
  • 梯度下降

该损失函数遵循均方根误差公式,即root mean square error(RMS)损失函数。

在这里插入图片描述

对参数W计算梯度值

在这里插入图片描述

梯度下降,其中RMSprop(指数加权移动平均数)是一种优化算法,对于下面公式存在疑问的同学,建议先自行查阅相关资料以加深理解。

在这里插入图片描述

在原文中,似乎未考虑到上一次迭代计算的梯度值(可能遇到梯度消失现象,原因不明),仅基于本次迭代的梯度值平方进行优化。这正是Adagrad方法的特性,原理图位于文末。

在这里插入图片描述

代码实现如下:

复制代码
    #因为存在偏差bias,所以dim+1
    dim = 18 * 9 + 1
    # w维度为163*1
    w = np.zeros([dim,1])
    # x_train_set维度为 4521*163
    x_train_set= np.concatenate((np.ones([len(x_train_set),1]),x_train_set),axis = 1).astype(float)
    #设置学习率
    learning_rate = 10
    #设置迭代数
    iter_time = 30000
    #RMSprop参数初始化
    adagrad = np.zeros([dim,1])
    eps = 0.0000000001
    #beta = 0.9
    #迭代
    for t in range(iter_time):
    loss = np.sqrt(np.sum(np.power(np.dot(x_train_set,w)-y_train_set,2))/len(x_train_set))
    if(t%100 == 0):
        print("迭代的次数:%i , 损失值:%f"%(t,loss))
        #gradient = 2*np.dot(x.transpose(),np.dot(x,w)-y)
        #计算梯度值
        gradient = (np.dot(x_train_set.transpose(),np.dot(x_train_set,w)-y_train_set))/(loss*len(x_train_set))
        adagrad += (gradient ** 2)
        #更新参数w
        w = w - learning_rate * gradient / np.sqrt(adagrad + eps)
    #保存参数w
    np.save('weight.npy',w)

观察运行结果时,我发现打印值仅部分展示,经过30000次迭代,损失值收敛至19。学习率设置为自定义参数可能有助于优化训练体验。观察到损失值出现波动,但好消息是,经过一段时间训练后,损失值趋于稳定。使用RMSprop优化方法时,可能会出现类似情况。如果仅使用第一个梯度进行优化,可能会出现波动现象。建议尝试采用更全面的梯度信息以改善训练效果。

复制代码
    迭代的次数:0 , 损失值:27.239592
    迭代的次数:100 , 损失值:598.991742
    迭代的次数:200 , 损失值:96.973083
    迭代的次数:300 , 损失值:240.807182
    迭代的次数:400 , 损失值:71.607934
    迭代的次数:500 , 损失值:212.116933
    迭代的次数:600 , 损失值:117.461546
    迭代的次数:700 , 损失值:189.660439
    迭代的次数:800 , 损失值:87.943008
    迭代的次数:900 , 损失值:158.851111
    迭代的次数:1000 , 损失值:74.318934
    迭代的次数:1100 , 损失值:138.784655
    迭代的次数:1200 , 损失值:67.418347
    迭代的次数:1300 , 损失值:124.302389
    迭代的次数:1400 , 损失值:63.235512
    迭代的次数:1500 , 损失值:113.160475
    迭代的次数:1600 , 损失值:60.299076

至此为止,模型已经完成训练。在验证集上进行模型验证,并完成对test.cxv数据集的预测任务。

先对测试集test.csv进行预处理

复制代码
    testdata = pd.read_csv('E:/jupyter/data/hw1/test.csv',header = None ,encoding = 'big5')
    test_data = testdata.iloc[:,2:]
    test_data[test_data == 'NR'] = 0
    test_data = test_data.to_numpy()
    test_x = np.empty([240,18*9],dtype = float)
    for i in range(240):
    test_x[i,:] = test_data[18*i:18*(i+1),:].reshape(1,-1)
    for i in range(len(test_x)):
    for j in range(len(test_x[0])):
        if std_x[j] != 0:
            test_x[i][j] = (test_x[i][j] - mean_x[j]) / std_x[j]
    test_x = np.concatenate((np.ones([240,1]),test_x),axis = 1).astype(float)
    print(test_x)

看看运行结果

复制代码
    array([[ 1.        , -0.24447681, -0.24545919, ..., -0.67065391,
        -1.04594393,  0.07797893],
       [ 1.        , -1.35825331, -1.51789368, ...,  0.17279117,
        -0.10906991, -0.48454426],
       [ 1.        ,  1.5057434 ,  1.34508393, ..., -1.32666675,
        -1.04594393, -0.57829812],
       ...,
       [ 1.        ,  0.3919669 ,  0.54981237, ...,  0.26650729,
        -0.20275731,  1.20302531],
       [ 1.        , -1.8355861 , -1.8360023 , ..., -1.04551839,
        -1.13963133, -1.14082131],
       [ 1.        , -1.35825331, -1.35883937, ...,  2.98427476,
         3.26367657,  1.76554849]])

验证模型并预测

复制代码
    #在验证集上进行验证
    w = np.load('weight.npy')
    x_validation= np.concatenate((np.ones([len(x_validation),1]),x_validation),axis = 1).astype(float)
    for m in range(len(x_validation)):
    Loss = np.sqrt(np.sum(np.power(np.dot(x_validation,w)-y_validation,2))/len(x_validation))
    print ("the Loss on val data is %f" % (Loss))
    #预测
    ans_y = np.dot(test_x, w)
    print('预测PM2.5值')
    print(ans_y)

运行结果

复制代码
    the Loss on val data is 18.427185
    预测PM2.5值
    [[-15.78367116]
     [ -2.32261409]
     [ 59.74234153]
     [ -2.69635112]
     [ 39.23820506]
     [ 13.8801302 ]
     [ 22.58641017]
     [ 31.11258594]
     [ 41.92474119]
     [ 68.36693984]
     [ 17.54723298]
     [ 42.69150518]
     [ 85.92726242]
     [ 64.53572169]
     [ 26.60792925]
     [ -7.59077676]

从实验结果来看,在我们设置的验证集上,误差值为18.4,这一数值表明模型在优化过程中仍存在明显的问题。值得注意的是,测试集上的预测结果出现负值,这进一步验证了模型的局限性,需要采取相应的优化工作以提升预测精度。

注:在程序运行过程中遇到维度不匹配的情况时,重新运行程序即可。发现错误的地方,欢迎指正交流,谢谢!


梯度下降算法存在误解:最近观看了Gradient Descent1的视频,adagrad方法如下图所示

在这里插入图片描述
在这里插入图片描述

全部评论 (0)

还没有任何评论哟~