本文从“牛顿第二定律”入手,介绍机器学习的思考过程,以及在过程中如何确定模型参数,模型三个关键部分(假设、评价、优化)该如何应用。
机器学习的方法论和人类科研的过程有异曲同工之妙,下面以“**机器从牛顿第二定律实验中学习知识**”为例,帮助读者更加深入理解机器学习(监督学习)的方法论本质。
一、机器学习的方法论
机器学习的过程是一个漫长的训练过程,换句话说,这是一个归纳总结的过程,因此有很多开发者把这一过程称为“炼丹”。
机器学习的实现可以分成两步:训练和预测,类似于我们熟悉的归纳和演绎:
归纳: 从具体案例中抽象一般规律,机器学习中的“训练”亦是如此。从一定数量的样本(已知模型输入X和模型输出Y)中,学习输出Y与输入X的关系(可以想象成是某种表达式)。
演绎: 从一般规律推导出具体案例的结果,机器学习中的“预测”亦是如此。基于训练得到的Y与X之间的关系,如出现新的输入X,计算出输出Y。通常情况下,如果通过模型计算的输出和真实场景的输出一致,则说明模型是有效的。
牛顿第二定律
牛顿第二定律是艾萨克·牛顿在1687年于《自然哲学的数学原理》一书中提出的,其常见表述:物体加速度的大小跟作用力成正比,跟物体的质量成反比,与物体质量的倒数成正比。牛顿第二运动定律和第一、第三定律共同组成了牛顿运动定律,阐述了经典力学中基本的运动规律。
在中学课本中,牛顿第二定律有两种实验设计方法:倾斜滑动法和水平拉线法,如下图所示。
观察实验数据不难猜测,物体的加速度a和作用力之间的关系应该是线性关系。因此我们提出假设 a=w⋅F,其中,a代表加速度,F代表作用力,w是待确定的参数。
通过大量实验数据的训练,确定参数w是物体质量的倒数(1/m),即得到完整的模型公式a=F⋅(1/m)。当已知作用到某个物体的力时,基于模型可以快速预测物体的加速度。例如:燃料对火箭的推力F=10,火箭的质量m=2,可快速得出火箭的加速度a=5。
二、让机器从牛顿第二定律实验中学习知识
上面这个有趣的案例演示了机器学习的基本过程,但其中有一个关键点的实现尚不清晰,即:如何确定模型参数(w=1/m)?
下面我将带领大家一步一步地寻找这个模型参数。
1.从实验中获取大量数据
我们可以从大量地实验中获取大量的数据,有了数据,我们就可以从中挖掘价值。
这里我们用到的是numpy里生成随机数的函数:
numpy.random.randint(low, high=None, size=None, dtype='l')
函数的作用是,返回一个随机整型数,范围从低(包括)到高(不包括),即[low, high)。如果没有写参数high的值,则返回[0,low)的值。
参数如下:
- low: int
生成的数值最低要大于等于low。
(hign = None时,生成的数值要在$[0, low)$区间内) - high: int (可选)
如果使用这个值,则生成的数值在[low, high)区间。 - size: int or tuple of ints(可选)
输出随机数的尺寸,比如size = (m * n* k)则输出同规模即m * n* k个随机数。默认是None的,仅仅返回满足要求的单一随机数。 - dtype: dtype(可选)
想要输出的格式。如int64、int等等
输出:
- out: int or ndarray of ints
返回一个随机数或随机数数组
# 生成输入数据,作用力X
import numpy
num_inputs = 1
num_examples = 100
features = numpy.random.randint(1, 200, (num_examples, num_inputs)).astype('float32') # 生成含有100个1~200的元素的二维列表
# 实验得到的加速度Y
true_w = 0.05 # 假设物体质量为20g
labels = features[:,0] * true_w
labels = labels + numpy.random.normal(0,0.001,labels.shape[0]) # 加入噪声,实际实验时难免会有误差
labels = labels.astype('float32')
labels = numpy.expand_dims(labels,axis=-1) #注意:需要在最后增加一个维度
# 将数据转换成tensor
import paddle
train_datas = paddle.to_tensor(features)
y_true = paddle.to_tensor(labels)
2.确定假设空间
机器需确定预测的基本假设,即加速度a(Y)和作用力F(x)之间是线性关系。“预测值Y与特征X之间关系”的基本假设的称为“假设空间”,它圈定了预测模型能够表示的关系范围。比如,如果实际关系是非线性的(比如圆的周长与半径之间的关系),却让机器使用“线性假设”去学习,最优结果只能找到一条与该非线性曲线最贴近的直线,而不能突破“直线关系”的表达范畴。在实践中,往往机器学习之前,人们已经对业务问题有较深刻的认知和理解,并不期望机器漫无边际的实验Y与X之间的关系可能,而是根据业务理解圈定一个更可能的关系范围,降低机器学习的难度、提高其学习效率。这个事先假定的关系范围即为假设空间。具体到上述场景:通过对实验数据的观测,假设“加速度a”与“作用力F”之间是线性关系。至于是怎样的线性关系,仍需要机器进一步去确定。
线性关系可以直接使用最简单的线性变换层来做,这一步也被称为模型组网。
飞桨框架2.0中,组网相关的API都在paddle.nn目录下,你可以通过 Sequential 或 SubClass 的方式构建具体的模型。
这里我们使用最简单的线性变换层组网:class paddle.nn.Linear(in_features, out_features, weight_attr=None, bias_attr=None, name=None)
- 参数:
in_features (int) – 线性变换层输入单元的数目。 - out_features (int) – 线性变换层输出单元的数目。
- weight_attr (ParamAttr, 可选) – 指定权重参数的属性。默认值为None,表示使用默认的权重参数属性,将权重参数初始化为0。
- bias_attr (ParamAttr|bool, 可选) – 指定偏置参数的属性。 bias_attr 为bool类型且设置为False时,表示不会为该层添加偏置。 bias_attr 如果设置为True或者None,则表示使用默认的偏置参数属性,将偏置参数初始化为0。
- name (str,可选) – 一般无需设置,默认值为None。
import paddle
model = paddle.nn.Linear(in_features=1, out_features=1) # 使用最简单的线性变换层
3.选择优化目标
在上一步中,我们确定了“假设空间”,机器仍需要一个可计算的评价标准,告诉它什么样的预测值是好的,什么样的预测值是坏的。从直观感觉上讲,预测值与实际值完全一样是最好的,相差不大是次好的,相差很大是不好的。基于此,设计最直接的评价指标Loss“在已知的样本集合上,计算每个样本的预测值与实际值的误差,加和全部误差得到的指标”,公式写为:Σ|实际值-预测值|。使用该方法的效果如下图所示:如果输入只有一个特征X(一维特征),学习到Y~X的关系为左图中的直线。对于每个样本,误差为该样本|实际值-预测值|,即为图中点到直线的线段(平行于Y轴)。评价指标是所有线段长度的累加和。
可以想象,随着拟合直线的上下移动或左右转动,Loss的大小(所有线段长度之和)会发生变化。如果输入有两个特征X1、X2(二维特征),预测值Y与输入的关系则是一个平面,每一个点的预测误差为点到平面的线段(平行于Y轴),评价指标依然是所有线段的长度加和,如上面右侧图形所示。同样,随着预测Y~X关系的变化(移动图中的直线或平面),评价指标的大小也会随之变化。这个评价模型预测效果的指标称为“**优化目标**”,是代表模型预测效果的标尺,即为模型学习优化的方向。设定了优化目标后,最后的问题是:如何在假设空间中明确下一种关系(确定Y=wx中的参数w),使优化目标达到最小。
这里我们使用均方误差来作为我们的优化目标。
import paddle
mse_loss = paddle.nn.MSELoss() # 计算预测值和目标值的均方差误差
4.通过寻解算法学习
在假设空间中寻找使得优化目标最小的参数取值,称为“寻解算法”或“优化过程”。什么“寻解方法”最简单呢?从试验中可见,在所有可能的预测值中逐一尝试,是最容易想到的。这种方法虽然容易想到,但也比较笨拙。如果假设空间太大,要全部尝试一遍岂不是要很久?!所以,在实际项目中几乎没人使用这种方法,但用于演示机器学习基本过程是足够的。
另外,最优化问题是计算数学中最为重要的研究方向之一。在深度学习领域,优化算法同样是关键环节之一。即使完全相同的数据集与模型架构,不同的优化算法也很可能导致不同的训练结果,甚至有的模型出现不收敛现象。
梯度下降是目前神经网络中使用最为广泛的优化算法之一。为弥补基本梯度下降的种种缺陷,研究者们提出了一系列的变种算法,从最初的SGD逐步演变到Adam、RMSprop等。
# 定义Adam优化器
lr = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=0.01, T_max=10)
optimizer = paddle.optimizer.Adam(learning_rate=lr,
parameters=model.parameters(),
weight_decay=paddle.regularizer.L2Decay(0.01))
上述的机器学习步骤,依然有一个逻辑漏洞。我们设计的优化目标是在训练样本上进行计算的,即我们选择的参数以及形成的Y~x关系会在已经掌握训练数据上十分有效。
但在现实中,我们期望模型对未来没有见过的样本做出正确预测。
在已知训练样本上表现有效的函数规律,会在未知的样本上依然有效么?
5.让机器从数据中学习
这一步通常也称为模型训练,即将刚刚准备好的数据给机器去学习,就好像我们会通过刷题的方式掌握知识点。
模型训练主要有下面几个步骤:
- 前向传播
- 计算误差
- 反向传播
- 更新参数
- 梯度清零
多次训练后,模型的误差已经可以降到小数点后3位了,我们可以看一下模型找到的这个参数值是多少:
接下来我们再来试一下模型的效果:
给一个20g的物体施加289N的力,计算机给出的答案是:物体能产生14.41132641米每秒的加速度,我们来验证一下:
- 加速度应该等于给物体施加的力除以物体的质量,289除以20,不难得到加速度为14.45,计算机给出的答案与实际答案已经非常接近了
三、总结与升华
总结一下刚刚确定模型参数的过程。刚刚这个有趣的案例演示了机器学习的基本过程,确定模型参数(w=1/m)的过程与科学家提出假说的方式类似,合理的假说至少可以解释所有的已知观测数据。如果未来观测到不符合理论假说的新数据,科学家会尝试提出新的假说。如天文史上,使用大圆和小圆组合的方式计算天体运行在中世纪是可以拟合观测数据的。但随着欧洲机械工业的进步,天文观测设备逐渐强大,越来越多的观测数据无法套用已有的理论,这促进了使用椭圆计算天体运行的理论假说出现。因此,模型有效的基本条件是能够拟合已知的样本,这给我们提供了学习有效模型的实现方案。
Hi!
很高兴阅读了您的笔记,很有意思
但是我觉得模型“学会了”牛顿第二定律。毕竟给模型输入的并不是现实生活中的事件。这个模型顶多学会了两个输入之间的关系。如果这个模型能通过视频输入学会牛顿第二定律那就会很棒的。
https://aistudio.baidu.com/projectdetail/7198463?contributionType=1 谁能帮忙做一下这个实验