Regression回归分析
什么是Regression?
所谓的机器学习,其实就是一个找函数的过程。但是这个函数往往很复杂,靠人是找不出来的,要依靠机器采用某种方法才能找出这个函数。例如以下几种情况:
- 音频识别:输入为一段音频信号,输出为这段音频的内容是什么?
- 视频识别:输入为一张图片,输出为图片的内容是什么?
这节的Regression(回归分析),作为机器学习算法的一种,其面向于:函数的输出为一个数值(标量,scalar)
机器学习怎么玩?
进行机器学习一般的三步走:
-
定义带有未知数的函数(Model)
例如函数式,y是待预测的值,x是已知值(feature),w(weight)和b(bias)是未知数
-
定义损失函数L,L是关于w和b的函数(即输入是weight和bias)。L用于衡量当前设定的(weight,bias)这组数值好还是不好。L的计算来源于训练数据(Training Data)。
在进行预测时,我们把预测值记为,把真实值记为(称为Label)。
- 我们可以用来表示预测与真实的差距,即L为mean absolute error(MAE)。
- 也可以用来表示,即L为mean square error(MSE)。
- 这里的mean是指L应取所有训练数据的平均
显然L越大表明当前参数(weight,bias)越烂;L越小就越好。
我们可以穷举所有w和b的值,然后分别以w和b为x、y轴,L为指标绘图,就能直观看出不同的(w,b)对应的L怎么样,这样的图叫error surface
-
优化(采用梯度下降法)
注:这里的意思是使得L最小时变量的取值;当然有就是最大时的取值
下面说一下梯度下降的基本思路:
-
简单起见,这里只考虑w参数
-
首先随机选取一个w值,记为w0
-
计算函数L在w0点的偏导。如果偏导值为正,说明该点出的L曲线左低右高,要找L的最小值就要把w往左移;偏导为负同理。
-
w左右移动的距离,一方面取决于偏导的大小(偏导大肯定动的多),另一方面取决于我为其设定的超参数:学习率。
-
那这样移动w到什么时候才结束呢?
- 设定一个更新次数上限,比如更新1w次就不干了,当然这个次数也是一个超参数
- 非常理 想的情况下,w左右移动的距离正好就是0,那w也不会再动了。这种情况下,就有可能陷入局部最优(local minima)
同样的,当考虑上w和b两个参数时,情况就是这样的:
-
这三步走就构成了机器学习的**训练(Training)**过程。
我们的feature也可以取更多的值,如下图:

形如这样的模型,我们称为线性模型(Linear Model)。很明显线性模型非常简单,没有办法模拟真实的情况。我们把线性模型与真实情况的差距称为Model的Bias(注意不是线性函数截距的那个bias哦)
进化版
一种简单的进化版就是用分段函数(Piecewise Linear Curve)

而这条蓝色的折线,我们采用曲线Sigmoid Function来进行逼近,其曲线方程为
显然该函数当时,函数趋向于常量c;当时,函数趋向于0。
因此称那条蓝色的折线为Hard Sigmoid,可以通过选取不同的c、b、w的值来实现曲线向折线的逼近。总结一下,分段函数的表示记为:
也就是说,这一项,其实是让原来的更加细腻,更有弹性(难绷),如下图所示。显然这个函数项越多,能拟合的曲线就越曲折,具体要用多少个函数项也是一个超参数。

当然我们可以把这些未知参数写成矩阵、向量的形式:可以写作向量,可以写作一个矩阵。我们可以把所有的未知数都统一到向量中,具体操作如下:

到此,我们就完成了进化版的第一步:定义带有未知数的函数。
下面就要定义损失函数L了。L与$\theta L(\theta)\theta$后,和原先一样,可以采用MAE或MSE来表示损失函数。
同样的,这里的优化梯度下降怎么go?

但是,在实际操作中,我们不会拿所有的数据去算Loss,而是会把数据分成若干个Batch(随机分)(这里的Batch Size也是一个超参数),然后用每个Batch中的数据去计算,具体操作如下:

这里的两个名词:
- update:每次更新未知向量叫一次update
- epoch:完成所有Batch的一轮叫一个epoch
激活函数(Activation Function):譬如这里的函数,就是我选用什么样的基本单元去拟合真实数据
还有神人操作:把得到的结果再做一次运算,然后再做……具体要做几次(有几层Layer)?这又是一个超参数。如下图所示,注意每一次运算的参数(b、W这种)都不一样,相当于增加了更多的参数。

我们把每一个基本运算(比如)称为神经元(Neuron),整个系统称为神经网络(Neural Network)。因为系统中有很多层(Layer),许多层构成深度(Deep),所以这种结构也称为深度学习(Deep Learning)。
过拟合(Overfitting):在训练集上效果明明变好了,在测试集上却变烂了

HM1
直接运行示例代码,结果在Private Score为1.61450,在Public Score上为1.56370。
第一次进化是考虑了feature的选择。
在函数select_feat(train_data, valid_data, test_data, config['select_all'])
中,原本的情况是这样的:
1 | if select_all: |
此时feature的取值是训练集中的全部列,也就是把诸如hh_cmnty_cli
、work_outside_home
等参数全部考虑进去。一般而言,我只需要前4天的tested_positive
参数放进去训练就行了,即下面这个函数
1 | def getpositivecolumnnumber(): |
1 | else: |
这样子训练出的结果如下
第二次进化是考虑模型架构(激活函数)和优化器选择。
优化器:How to choose optimizer ?训练时,如何选择优化器?_训练模型 更换优化器-CSDN博客
激活函数:神经网络中的激活函数及其各自的优缺点、以及如何选择激活函数_神经网络的激活函数有哪些?他们对神经网络的性能有何影响。-CSDN博客
对于优化器而言,原代码选取的是SGD
:
1 | optimizer = torch.optim.SGD(model.parameters(), lr=config['learning_rate'], momentum=0.9) |
先是更换了Adagrad
优化器
1 | optimizer = torch.optim.Adagrad(model.parameters(), lr=config['learning_rate']) |
但是效果依托,Loss始终居高不下;听从AI建议选取Adam
优化器,训练后结果如下:
发现在训练过程中,Loss减小非常缓慢,就加大了学习率为'learning_rate': 1e-4
,再次训练后的结果:
效果还是有的!后续再提高一些学习率,效果有微小的提升,但是好像是摸到了这个优化器的瓶颈。
对于激活函数选择方面,原代码中是采用ReLU
和2层神经元:
1 | self.layers = nn.Sequential( |
先是尝试了增加神经元层数到3层,也增加了神经元数量:
1 | self.layers = nn.Sequential( |
但是效果不佳。听从AI建议选用了LeakyReLU
和ELU
激活函数:
1 | # 换用LeakyReLU激活函数 |
LeakyReLU
ELU
第三次进化考虑的就多了。
- 之前的特征选择只有前4天的
tested_positive
参数,现在希望考虑更多参数(考虑“影响力更大”的参数) - 为了防止过拟合,可以使用L2正规化或者增加Dropout层
- 学习率可以在训练过程中动态变化(逐渐降低),防止在目标点反复横跳。
首先是做了第一条:
1 | # 使用 sklearn 中的特征选择方法选择最有影响的特征 |
即选取了24个最有“代表性”的数据作为feature,结果是有进步的:
第二条:
1 | # 优化器更换为Adam |
这个挺有用的,但是在改进模型架构时,并没有非常好的效果:
1 | # 换用LeakyReLU激活函数 |
第三条加上去后效果也一般:
1 | # 学习率调度器更换为余弦退火调度器 |
后续再改进,当前的最好结果是:
