机器学习的过程本质上是一个不断通过数据训练来提升模型在对应评估指标上表现的过程。在此过程中,为模型提供有效的反馈并基于这些反馈进行持续的调整是至关重要的。只有当这个过程顺利进行时,模型才能得到有效的训练,机器才能真正实现学习。
虽然从广义上理解机器学习的概念并不复杂,但要实际构建一个机器学习模型却是一项复杂的任务。这既需要我们掌握包括数学原理在内的基本理论知识,又要求我们具备一定的编程实现能力。然而,正如古人所言,"合抱之木,生于毫末;九层之台,起于累土",首先我们将介绍几个机器学习的核心概念,然后我们尝试在Python环境下实现一个简单的模型------线性回归,并在此过程中深入了解机器学习建模的具体流程。
机器学习模型训练相关核心概念:
- 模型评估指标:用于评估模型实际效果的数值型指标,如准确率;
- 模型参数:对模型最终输出结果有影响的模型关键指标,如自变量加权求和汇总过程中的权重;
- 模型训练:指通过不断的数据输入、模型参数得到有效调整的过程,此处模型参数的有效调整指的是调整之后能够提升模型表现。
接下来,我们将按照机器学习的一般流程,实现一个简单的线性回归模型。但在正式进入建模阶段之前,我们还需要补充一些机器学习中的基本概念,为后续的建模工作做好铺垫
一、机器学习概念补充
1.经典统计分析方法与机器学习
线性回归模型是源自统计学领域的一类基础模型,它在整个线性模型大类中占据重要地位,是统计学中至关重要的模型之一。在经典的统计学领域,线性回归模型得益于其坚实的数学理论基础,曾长期作为数理统计分析建模的通用模型。然而,构建传统统计学意义上的线性回归模型并非易事,需要掌握随机变量的基本分布、变量间的相关性与独立性以及方差分析等统计学知识。
相比之下,在机器学习领域,构建线性回归模型的流程更为简洁。由于机器学习建模思路和经典统计分析存在显著差异,且线性回归模型本身具有较强的可解释性,因此它常被选为入门学习的第一个算法。
从历史发展角度看,"算法"或"模型"的概念早已存在,而机器学习算法与经典统计学中的统计分析算法在目标上具有一致性------都是围绕特定目标进行有效预测。然而,两者的建模流程和基本思路却大相径庭。有关这两者之间的详细区别,我们将在后续课程中深入探讨。
当然,关于机器学习是否属于统计学的范畴,学术界尚存争议。在此,我们所说的机器学习与统计方法的区别,主要是指与经典统计分析方法(特别是遵循古典假设的统计学模型)之间的差异。
从机器学习的角度来看,线性回归是指通过自变量(特征)的加权求和来得到因变量(标签)的过程。例如 y = w 1 x 1 + w 2 x 2 y = w_1x_1+w_2x_2 y=w1x1+w2x2就是一个简单的线性回归示例。在深入讨论线性回归之前,我们还需要补充一些关于机器学习中与数据相关的基本概念。
2.数据与数据集相关概念
- 数据与数据集
数据,是指能够用来描述某一事物属性或运行状态的数值信息。一个完整的数据集则是由多条这样的数据所构成的集合。以鸢尾花数据为例,它就是一个描述鸢尾花一般特性的数据集。我们可以通过从本地读取文件的方式来查看这一数据集的内容。鸢尾花的数据集将和代码统一打包后续上传;
python
# 导入相关包
import numpy as np
import pandas as pd
python
iris_df = pd.read_csv("iris.csv")
iris_df
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
... ... ... ... ... ...
145 6.7 3.0 5.2 2.3 Iris-virginica
146 6.3 2.5 5.0 1.9 Iris-virginica
147 6.5 3.0 5.2 2.0 Iris-virginica
148 6.2 3.4 5.4 2.3 Iris-virginica
149 5.9 3.0 5.1 1.8 Iris-virginica
150 rows × 5 columns
数据集描述鸢尾花基本信息如下:
在上述数据集中,每一行代表一朵花的记录结果,而其中每一列代表所有花的一项共同指标。类似这种二维表格数据,有时也被称为面板数据,属于结构化数据的一种。
- 特征与标签
在鸢尾花数据集中,每一列数据代表所有描述对象的一个共同指标。其中,前四列详细描述了鸢尾花的四个生物学性状,而最后一列则指明了每一朵鸢尾花所属的类别。如果我们的目标是基于鸢尾花的四个属性来判断其所属类别,那么这四列数据就被视为数据集的特征(features),而最后一列则被视为数据集的标签(labels)。在实际建模过程中,当我们希望模型进行预测时,我们会向模型输入一些样本的特征(即鸢尾花的四个属性取值),然后让模型判断每个样本的标签(即每朵花应该归入哪一类)。
然而,需要注意的是,特征和标签的划分是基于模型的预测目标而定的。如果模型的预测目标发生变化,数据集的特征和标签也会随之改变。例如,如果我们想要预测鸢尾花的花瓣宽度(petal width),那么原本数据集中的第1、2、3、5列就会转变为新的特征,而第4列则变为新的标签。因此,特征和标签的划分本质上是人为设定的,它们会根据预测目标的不同而有所变化。
一般来说,标签列需要放在最后一列。
注意:数据集中的列也被称为字段,鸢尾花数据集中总共有5列,也就总共有5个字段。
- 连续变量和离散变量
鸢尾花数据集详细记录了每一朵花的四个维度的属性及其所属类别。从随机变量的视角来看,每一组数据都可以视为这五个随机变量的一次具体取值。举例来说,花萼长度可以被视为一个随机变量,而数据集中第一条记录中的5.1cm则是这个随机变量在实际中的一个观测值。
随机变量可以进一步分为连续变量和离散变量。连续变量是指能够取到一系列连续数值的随机变量,如表示长度或距离的变量。相对而言,离散变量只能取到有限的、离散的整数值,比如用0和1来表示的性别变量。在鸢尾花数据集中,前四个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)均为连续型变量,因为它们能够取到一系列连续的数值。而最后一个特征,即花的类别,则是离散型变量,因为它只能取到几个特定的整数值(在这个数据集中是三种不同的类别)。
在传统统计分析领域,离散型变量可进一步细化为名义型变量和顺序性变量。名义变量是指随机变量在取不同的离散值时,这些取值的大小本身并没有数值上的意义,而仅仅具有指代作用。例如,当使用0和1来分别代表男性和女性时,这里的0和1并不代表数值上的大小关系,仅仅是为了区分两种不同的性别。
与名义变量不同,顺序变量则具有大小方面的数值意义。例如,当使用0、1和2来分别代表高中、本科和研究生学历时,这些数值可以用来表示学历层次的高低,其中2大于1,1大于0,这样的数值关系反映了学历水平的递增顺序。
3.模型类型
- 分类问题与回归问题
离散型变量和连续性变量在数理特性上存在显著差异,这对预测类机器学习建模来说至关重要。特别是标签------我们试图预测的指标------的变量类型(连续或离散)会极大地影响模型的预测过程。
据此,如果是围绕离散型标签进行建模预测,则称任务为分类预测任务,该模型为解决分类任务的分类(classification)模型,而如果是围绕连续型标签进行建模预测,则称该任务为回归预测任务,该模型为解决回归问题的回归类(regression)模型。
显然,基于鸢尾花数据集来构建一个预测花的类别的任务属于分类任务。要完成这个任务,我们需要构建相应的分类模型进行预测。此外,除了鸢尾花数据集,还有一个常用于回归问题建模的数据集,即abalone数据集。
由于abalone数据集是txt格式的,且各列数据通过空格分隔,第一行并没有列名,因此我们需要使用特定的语句来读取这个数据集。
python
ab_df = pd.read_csv("abalone.txt", sep='\t', header=None)
ab_df
0 1 2 3 4 5 6 7 8
0 1 0.455 0.365 0.095 0.5140 0.2245 0.1010 0.1500 15
1 1 0.350 0.265 0.090 0.2255 0.0995 0.0485 0.0700 7
2 -1 0.530 0.420 0.135 0.6770 0.2565 0.1415 0.2100 9
3 1 0.440 0.365 0.125 0.5160 0.2155 0.1140 0.1550 10
4 0 0.330 0.255 0.080 0.2050 0.0895 0.0395 0.0550 7
... ... ... ... ... ... ... ... ... ...
4172 -1 0.565 0.450 0.165 0.8870 0.3700 0.2390 0.2490 11
4173 1 0.590 0.440 0.135 0.9660 0.4390 0.2145 0.2605 10
4174 1 0.600 0.475 0.205 1.1760 0.5255 0.2875 0.3080 9
4175 -1 0.625 0.485 0.150 1.0945 0.5310 0.2610 0.2960 10
4176 1 0.710 0.555 0.195 1.9485 0.9455 0.3765 0.4950 12
4177 rows × 9 columns
同时,该数据各列名称如下:
Name | Description |
---|---|
Gender | 性别,1为Male、-1为Femel、0为infant |
Length | 最长外壳尺寸 |
Diameter | 垂直于长度的直径 |
Height | 带壳肉的高度 |
Whole weight | 整体重量 |
Shucked weight | 脱壳重量 |
Viscera weight | 内脏的重量 |
Shell weight | 壳的重量 |
Rings | (年轮)年龄 |
修改数据集列名称
python
ab_df.columns
Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype='int64')
python
ab_df.columns = ['Gender', 'Length',
'Diameter', 'Height',
'Whole weight', 'Shucked weight',
'Viscera weight', 'Shell weight',
'Rings']
查看修改结果
python
ab_df
Gender Length Diameter Height Whole weight Shucked weight Viscera weight Shell weight Rings
0 1 0.455 0.365 0.095 0.5140 0.2245 0.1010 0.1500 15
1 1 0.350 0.265 0.090 0.2255 0.0995 0.0485 0.0700 7
2 -1 0.530 0.420 0.135 0.6770 0.2565 0.1415 0.2100 9
3 1 0.440 0.365 0.125 0.5160 0.2155 0.1140 0.1550 10
4 0 0.330 0.255 0.080 0.2050 0.0895 0.0395 0.0550 7
... ... ... ... ... ... ... ... ... ...
4172 -1 0.565 0.450 0.165 0.8870 0.3700 0.2390 0.2490 11
4173 1 0.590 0.440 0.135 0.9660 0.4390 0.2145 0.2605 10
4174 1 0.600 0.475 0.205 1.1760 0.5255 0.2875 0.3080 9
4175 -1 0.625 0.485 0.150 1.0945 0.5310 0.2610 0.2960 10
4176 1 0.710 0.555 0.195 1.9485 0.9455 0.3765 0.4950 12
4177 rows × 9 columns
为了方便后续直接调用,我们可以将修改后的数据集保存在本地
python
ab_df.to_csv('abalone.csv', index=False)
Abalone数据集和鸢尾花数据集一样,都是由专业人士精心统计和整理的数据集,常被用于机器学习初学者入门练习。然而,值得注意的是,数据采集和获取的方式在不同时代有着显著的差异。在经典统计学模型盛行的时代,数据的获取主要依赖于手工测量和记录,通常由专业人士进行,因此数据量虽然相对较小,但整体数据质量较高。经典统计学算法正是在这种背景下应运而生,专门针对这类数据进行处理和分析。
然而,随着大数据时代的到来,数据的产生和应用方式发生了巨大的变革。现在,数据可以自动采集、传输和存储,使得数据采集的场景变得无处不在,数据量也呈现出爆炸式的增长。同时,由于数据来源的多样性和复杂性,数据质量也变得参差不齐。
在数据应用层面,实际应用中的机器学习算法需要能够实时运算、快速响应,并且能够在数据质量不高的情况下产生相对稳定的结果。因此,相较于经典统计学算法,机器学习算法在计算效率上更高,对数据质量的要求也相对较低。然而,这也导致了机器学习算法在计算精度上可能不如统计方法高,并且结果可能不如统计方法稳定。这也是机器学习在诞生之初受到一些批评的原因。
尽管如此,机器学习算法因其灵活性和普适性在大数据时代得到了广泛的应用。虽然它的计算结果可能不是最精准的,但它能够适用于各种场景,并且能够处理大规模的数据集。因此,机器学习算法成为了大数据时代最为普适的算法之一。
的确,鉴于以上原因,我们后续将不会过多使用那些数据质量极高但建模门槛相对较低的数据集。实际上,在后续的课程中,我们将主要利用三类数据集进行三个层次的教学。首先,我们会使用手动创建的数据集,这些数据集具备一定的规律和难度,并且可以根据需要进行自定义,非常适合用于前期的训练和练习。其次,我们将使用竞赛数据,这类数据更加综合和复杂,能够提供更全面的训练机会。最后,我们还会使用企业案例数据,这些数据结合实际应用情况,能够帮助学生更好地理解建模在实际问题中的应用。通过这样的教学方式,我们将能够逐步提升学生的建模能力和实际应用能力。
对于abalone数据集来说,Rings是标签,围绕Rings的预测任务是连续型变量的预测任务,因此是个回归类问题。
二、线性回归模型建模准备
接下来,我们尝试手动实现线性回归模型。并借此过程探究机器学习建模的基础理论和一般建模流程。
- 数据准备
当然,线性回归是属于回归类模型,是针对连续型变量进行数值预测的模型,因此我们需要选用abalone数据集进行建模。此处为了更加清晰的展示建模过程的内部计算细节,我们选取数据集中部分数据带入进行建模。
Whole weight | Rings |
---|---|
1 | 2 |
3 | 4 |
- 模型准备
不难看出,上述数据集是极端简化后的数据集,只有一个连续型特征和连续型标签,并且只包含两条数据。围绕只包含一个特征的数据所构建的线性回归模型,也被称为简单线性回归。简单线性回归的模型表达式为 y = w x + b y = wx + b y=wx+b其中x表示自变量,即数据集特征,w表示自变量系数,代表每次计算都需要相乘的某个数值,b表示截距项,代表每次计算都需要相加的某个数值,而y表示因变量,即模型输出结果。
除了简单线性回归外,线性回归主要还包括多元线性回归和多项式回归两类。
其中,
多元线性回归
用于解决包含多个特征的回归类问题,模型基本表达式为: y = w 1 x 1 + w 2 x + . . . + w n x n + b y = w_1x_1+w_2x+...+w_nx_n+b y=w1x1+w2x+...+wnxn+b其中 x 1... n x_{1...n} x1...n表示n个自变量,对应数据集的n个特征, w 1... n w_{1...n} w1...n表示n个自变量的系数,b表示截距。此处"加权求和汇总"的计算过程较为明显,简单线性回归也是多元线性回归的一个特例。此外,
多项式回归
则是在多元线性回归基础上,允许自变量最高次项超过1次,例如: y = w 1 x 1 2 + w 2 x 2 + b y = w_1x_1^2+w_2x_2+b y=w1x12+w2x2+b就代表着一个二元二次回归方程。准备好了数据和算法之后,接下来就是模型训练过程。
三、线性回归模型训练
1.模型训练的本质:有方向的参数调整
- 模型训练与模型参数调整
那到底什么是模型训练呢?我们曾在Lesson 0中提到,模型训练就是指对模型参数进行有效调整。模型参数是影响模型输出的关键变量,例如本例中的模型包含两个参数, w 1 和 b w_1和b w1和b,当参数取得不同值时,模型将输出完全不同的结果。
数据特征 | 参数组 | 模型输出 | 数据标签 |
---|---|---|---|
Whole weight(x) | ( w , b ) (w,b) (w,b) | y ^ \hat y y^ | Rings(y) |
1 | (1, -1) | 0 | 2 |
3 | (1, -1) | 2 | 4 |
1 | (1, 0) | 1 | 2 |
3 | (1, 0) | 3 | 4 |
其中简单线性回归计算过程为 y = w x + b y = wx+b y=wx+b。需要说明的是,在很多场景下,我们会使用更加简洁的记号用于代表模型训练过程中的各项数值,用 x i x_i xi表示某条数据第 i i i个特征的取值,使用 y y y作为某条数据的标签取值,使用 y ^ \hat y y^表示某条数据带入模型之后模型输出结果。
从上式不难看出,模型参数取值不同模型输出结果也不同,而不同组的参数取值似乎也有"好坏之分",当参数组取值为(1,0)时的模型输出结果,要比参数组取值为(1,-1)时输出结果更加贴近真实值。这其实也就说明第二组参数要好于第一组参数。而"机器"在"学习"的过程,或者说模型训练过程,就是需要找到一组最优参数。
- 模型评估指标与损失函数
既然有了模型输出结果"好与坏"的判别,根据Lesson 0中的观点,我们需要将这种反馈有效的传递给模型,才能够让模型在训练过程中逐渐朝向好的方向发展。而要在模型训练过程中建立这种有效的反馈,我们就必须先掌握两个基本概念,即模型评估指标与损失函数。
其中,模型评估指标是评估模型输出结果"好与坏"的标量计算结果,其最终结果一般由模型预测值 y ^ \hat y y^和真实值 y y y共同计算得出。例如Lesson 0中的准确率就是一个分类模型的评估指标,并且是通过比较模型预测正确的样本数占总样本数的比例最终得出。而对于回归类问题,最重要的模型评估指标就是SSE------残差平方和。
所谓残差平方和,指的是模型预测值 y ^ \hat y y^和真实值 y y y之间的差值的平方和,计算结果表示预测值和真实值之间的差距,结果越小表示二者差距越小,模型效果越好。SSE基本计算公式为 S S E = ∑ i = 1 n ( y ^ i − y i ) 2 SSE = \sum_{i=1}^{n}(\hat y_i-y_i)^2 SSE=i=1∑n(y^i−yi)2
其中n为样本数量。对应的,上述两组不同参数取值对应的模型残差平方和计算结果依次为:
S S E ( 1 , − 1 ) = ( 0 − 2 ) 2 + ( 2 − 4 ) 2 = 8 SSE_{(1,-1)} = (0-2)^2+(2-4)^2 = 8 SSE(1,−1)=(0−2)2+(2−4)2=8
S S E ( 1 , 0 ) = ( 1 − 2 ) 2 + ( 3 − 4 ) 2 = 2 SSE_{(1,0)} = (1-2)^2+(3-4)^2 = 2 SSE(1,0)=(1−2)2+(3−4)2=2
能够看出,第二组参数对应模型效果更好。据此我们就找到了能够量化评估模型效果好坏的指标。
有了模型评估指标之后,我们还需要将评估结果有效的反馈给模型。这时就需要引入另一个至关重要的概念:损失函数(Loss Function)。
和模型评估指标是真实值和预测值的计算过程不同,模型的损失函数都是关于模型参数的函数。损失函数本质上一个衡量模型预测结果和真是结果之间的差异的计算过程,例如在SSE中如果带入模型参数,则就能构成一个SSE损失函数,基本计算过程如下:
数据特征 | 参数组 | 模型输出 | 数据标签 |
---|---|---|---|
Whole weight(x) | ( w , b ) (w,b) (w,b) | y ^ \hat y y^ | Rings(y) |
1 | (w, b) | w+b | 2 |
3 | (w, b) | 3w+b | 4 |
S S E L o s s ( w , b ) = ( y 1 − y ^ 1 ) 2 + ( y 2 − y ^ 2 ) 2 = ( 2 − w − b ) 2 + ( 4 − 3 w − b ) 2 SSELoss(w, b) = (y_1 - ŷ_1)^2 + (y_2 - ŷ_2)^2 = (2 - w - b)^2 + (4 - 3w - b)^2 SSELoss(w,b)=(y1−y^1)2+(y2−y^2)2=(2−w−b)2+(4−3w−b)2
SSELoss的基本计算过程和SSE一致,只不过SSELoss中带入的是模型参数,而SSE带入的是确定参数值之后的计算结果,因此我们也可以认为对于SSELoss和SSE来说,一个是带参数的方程,一个是确定方程参数之后的计算结果。
既然SSE和SSELoss的计算过程类似,那为何要区别损失函数和模型评估指标呢?主要有以下几点原因:
其一:对于很多模型(尤其是分类模型)来说,模型评估指标和模型损失函数的计算过程并不一致,例如准确率就很难转化为一个以参数为变量的函数表达式;
其二:模型评估指标和损失函数构建的目标不同,模型评估指标的计算目标是给模型性能一个标量计算结果,而损失函数的构建则是为了找到一组最优的参数结果。
除了SSE以外,常用的回归类问题的评估指标还有MSE(均方误差)和RMSE(均方根误差),其中MSE就是在SSE的基础上除以样本总量:
M S E = 1 n S S E = 1 n ∑ i = 1 n ( y ^ i − y i ) 2 MSE = \frac{1}{n}SSE = \frac{1}{n}\sum_{i=1}^{n}(\hat y_i-y_i)^2 MSE=n1SSE=n1i=1∑n(y^i−yi)2而RMSE则是在MSE基础之上开平方算得的结果:
R M S E = M S E = 1 n ∑ i = 1 n ( y ^ i − y i ) 2 RMSE = \sqrt{MSE} = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(\hat y_i-y_i)^2} RMSE=MSE =n1i=1∑n(y^i−yi)2对应的,MSE和RMSE也有相对的损失函数。
- 损失函数与参数求解
一旦损失函数构建完成,我们就可以围绕损失函数去寻找损失函数的最小值,以及求出损失函数取得最小值时函数自变量(也就是模型参数)的取值,此时参数的取值就是原模型中参数的最优取值结果。这点从SSE和SSELoss彼此类似的计算过程能够很容易看出来,由于我们最终建模目标是希望模型预测结果和真实结果一致,也就是SSE的取值尽可能小,而SSE的值是SSELoss中的两个变量(w,b)取值决定的,因此如果我们能找到一组(w,b)使得SSE的最终计算结果尽可能的小,也就相当于找到了一组模型的最佳参数。
至此,我们就发现了损失函数的核心作用:搭建参数求解的桥梁,构建一个协助模型求解参数的方程。通过损失函数的构建,我们可以将求解模型最优参数的问题转化为求解损失函数最小值的问题。至此也就完成了此前所说的确定反馈传递反馈的过程。
值得注意的是,损失函数的计算方程和实际带入进行建模的数据直接相关,上述SSELoss是在带入两条数据的情况下构建的损失函数,而调整输入数据,损失函数实际计算方程也会发生变化。
此外,还有一个和损失函数非常类似的概念------目标函数。目标函数概念相对复杂,并且对当前介绍内容并无影响,因此将放在后续进行介绍。
2.利用最优化方法求解损失函数
- 损失函数的求解
在构建好损失函数之后,接下来就是如何求解损失函数的最小值(及损失函数取得最小值时w和b的取值)。值得注意的是,此时损失函数是一个关于模型参数的方程,也就是说模型参数此时成了损失函数的自变量。
而要求解损失函数最小值,就需要记住一些优化理论和优化算法。当然,此处的优化理论和算法都是一些无约束条件下进行函数极值求解的方法。利用优化方法求解损失函数最小值及其取得最小值时损失函数自变量(也就是模型参数)的取值过程,也被简称为损失函数求解。
- 图形展示损失函数
为了更好的讨论损失函数(SSELoss)求最小值的过程,对于上述二元损失函数来说,我们可以将其展示在三维空间内:三维空间坐标分别为w、b、SSELoss。此处我们可以使用Python中matplotlib包和Axes3D函数进行三维图像绘制
S S E L o s s ( w , b ) = ( y 1 − y ^ 1 ) 2 + ( y 2 − y ^ 2 ) 2 = ( 2 − w − b ) 2 + ( 4 − 3 w − b ) 2 SSELoss(w, b) = (y_1 - ŷ_1)^2 + (y_2 - ŷ_2)^2 = (2 - w - b)^2 + (4 - 3w - b)^2 SSELoss(w,b)=(y1−y^1)2+(y2−y^2)2=(2−w−b)2+(4−3w−b)2
python
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
python
x = np.arange(-1,3,0.05)
y = np.arange(-1,3,0.05)
w, b = np.meshgrid(x, y)
SSE = (2 - w - b) ** 2 + (4 - 3 * w - b) ** 2
python
ax = plt.axes(projection='3d')
ax.plot_surface(w, b, SSE, cmap='rainbow')
ax.contour(w, b, SSE, zdir='z', offset=0, cmap="rainbow") #生成z方向投影,投到x-y平面
plt.xlabel('w')
plt.ylabel('b')
plt.show()
<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x2e42f316108>
<matplotlib.contour.QuadContourSet at 0x2e42f3160c8>
Text(0.5, 0, 'w')
Text(0.5, 0, 'b')
根据图像,我们大概能判断损失函数最小值点位置。
- 函数的凹凸性
初步探索函数图像,不难看出,目标函数是个整体看起来"向下凸"的函数。从理论出发我们知道,函数的凹凸性是函数的重要性质,也是涉及到损失函数求解方法选取的重要性质。这里我们首先给出凸函数的一般定义,对于任意一个函数,如果函数f(x)上存在任意两个点, x 1 , x 2 x_1, x_2 x1,x2,且
f ( x 1 ) + f ( x 2 ) 2 > = f ( x 1 + x 2 2 ) \frac{f(x_1) + f(x_2)}{2} >= f(\frac{x_1 + x_2}{2}) 2f(x1)+f(x2)>=f(2x1+x2)
我们就判定,这个函数是凸函数。
这里需要注意的是,凸函数的定义存在一定的"不一致",和之前一样,此处我们不作过多学术讨论,仅以"向下凸"的函数作为凸函数的一般定义。
此外,除了函数定义法,我们还可以通过凸集/凹集来定义凸函数。同时,很多机器学习模型所构建的损失函数都是凸函数,因此关于凸函数的优化方法(找到最小值的方法)也就成了机器学习建模过程中最常用的优化方法。而凸优化的相关理论,也逐渐成为算法工程师们的必修课。
典型的凸函数如 y = x 2 y = x^2 y=x2,我们可以绘制函数图像如下:
python
x = np.arange(-10,10,0.1)
y = x ** 2
plt.plot(x, y, '-')
plt.show()
不难看出,函数上任意两个点的y取值的均值(函数值的均值)都不小于这两个点均值的y的值(均值的函数值)。
python
# 函数值的均值
# x1 = 1, x2 = 3
(1 ** 2 + 3 ** 2)/2
5.0
python
# 均值的函数值
# x1 = 1, x2 = 3
((1+3)/2) ** 2
4.0
而对于一个凸函数来说,全域最小值明显存在。求解凸函数的最小值有很多种方法,其中最为基础的方法叫做最小二乘法。并且,虽然此处略过了相关证明过程,但对于上述SSELoss,本质上也是一个凸函数。因此我们是可以通过最小二乘法对SSELoss进行求解的。
- 最小二乘法理论基础
我们先抛开公式、从一个简单的角度理解最小二乘法。
通过上例,也就是$ y = x^2 函数中不难看出,函数全域最小值点为 函数中不难看出,函数全域最小值点为 函数中不难看出,函数全域最小值点为x=0$点,同时该点对应的函数切线与x轴平行,也就是在最小值点,函数的导数为0。其实这并不难理解,在最小值点左边函数逐渐递减、而在最小值点右边函数逐渐递增,最小值点左右两边函数单调性相反。
而这种性质其实可以拓展为凸函数的一个关于求解最小值的一般性质,即:
(1)对于一元函数,如果存在导数为0的点,则该点就是最小值点;
(2)对于多元函数,如果存在某一点,使得函数的各个自变量的偏导数都为0,则该点就是最小值点。
据此,我们就找到了最小二乘法求解凸函数最小值的基本出发点:即通过寻找损失函数导函数(或者偏导函数联立的方程组)为0的点,来求解损失函数的最小值。
关于驻点、临界点、边界点和拐点的概念讨论:
其实从更严格的意义上来说,凸函数的最小值点其实是根据边界点和驻点(导数为0的点)决定,如果没有边界点且没有驻点,则函数没有最小值(例如y=x),如果存在边界点,但没有驻点,则边界点的一侧就是最小值点,如果存在驻点(且左右两边单调性相反),则驻点就是最小值点,例如,对于$ y = x^2 而言, 而言, 而言, y^{'} = 2x $,2x = 0时x取值为0,也就是0点就是最小值点。
值得注意的是,驻点也可以说是临界点,但不是拐点,拐点特指左右两边函数凹凸性发生变化的点,切勿和驻点混淆。
- 最小二乘法求解SSELoss
接下来,尝试利用最小二乘法求解SSELoss。根据上述理论,我们使用最小二乘法求解SSELoss,即
S S E L o s s = ( 2 − w − b ) 2 + ( 4 − 3 w − b ) 2 SSELoss= (2 - w - b)^2 + (4 - 3w - b)^2 SSELoss=(2−w−b)2+(4−3w−b)2,本质上就是在找到能够令损失函数偏导数取值都为零的一组 ( w , b ) (w,b) (w,b)。SSELoss的两个偏导数计算过程如下:
$ \begin{align} \frac{\partial{SSELoss}}{\partial{(w)}} & = 2(2-w-b)*(-1) + 2(4-3w-b)*(-3)\\ & = 20w+8b-28 \\ & = 0 \end{align} $ $ \begin{align} \frac{\partial{SSELoss}}{\partial{(b)}} & = 2(2-w-b)*(-1) + 2(4-3w-b)*(-1)\\ & = 8w+4b-12 \\ & = 0 \end{align} $ $ (1)式 - (2)式*2 可得: 4w-4 = 0,w=1$ <\center> $ 将w=1带入(2)式 可得: 4b-4 = 0,b=1$ <\center>
最终可得,损失函数最小值点为(1,1),即当 w = 1 , b = 1 w=1,b=1 w=1,b=1时模型损失函数计算结果最小、模型SSE取值最小、模型效果最好,此时SSE=0,线性回归模型计算结果为: y = x + 1 y = x + 1 y=x+1
我们也可对比此前参数计算结果:
数据特征 | 参数组 | 模型输出 | 数据标签 |
---|---|---|---|
Whole weight(x) | ( w , b ) (w,b) (w,b) | y ^ \hat y y^ | Rings(y) |
1 | (1, -1) | 0 | 2 |
3 | (1, -1) | 2 | 4 |
1 | (1, 0) | 1 | 2 |
3 | (1, 0) | 3 | 4 |
至此,我们就完成了一个机器学习建模的完整流程。
四、机器学习建模一般流程
作为本节重点学习对象,此处我们整体梳理下机器学习的一般建模流程:
-
Step 1:提出基本模型
如本节中,我们尝试利用简单线性回归去捕捉一个简单数据集中的基本数据规律,这里的 y = w x + b y=wx+b y=wx+b就是我们所提出的基本模型。当然,在后续的学习过程中,我们还将接触诸多不同种类的机器学习模型,而不同的模型也有对应的适用场景。值得注意的是,在提出模型时,我们往往会预设好一些影响模型结构或者实际判别性能的参数,如简单线性回归中的w和b;
-
Step 2:确定损失函数
接下来,围绕建模的目标构建评估指标,并且围绕评估指标设置损失函数。当然,在本例中,模型评估指标和损失函数的建模流程相同。这里尤其需要反复提醒的是,损失函数不是模型,而是模型参数所组成的一个函数。
-
Step 3:根据损失函数性质,选择优化方法
之前提到,损失函数既承载了我们优化的目标(让预测值和真实值尽可能接近),同时也是包含了模型参数的函数,当我们围绕目标函数求解最小值时,也就完成了模型参数的求解。当然,这个过程本质上就是一个数学的最优化过程,求解目标函数最小值本质上也就是一个最优化问题,而要解决这个问题,我们就需要灵活适用一些最优化方法。当然,在具体的最优化方法的选择上,函数本身的性质是重要影响因素,也就是说,不同类型、不同性质的函数会影响优化方法的选择。在简单线性回归中,由于目标函数是凸函数,我们根据凸函数性质,我们选取了最小二乘法作为该损失函数的优化算法。但实际上,简单线性回归的损失函数其实是所有机器学习模型中最简单的一类损失函数,后续我们还将介绍其他模型的更加复杂的损失函数,以及对应的损失函数求解方法。
-
Step 4.利用优化算法进行损失函数求解
在确定优化方法之后,我们就能够借助优化方法对损失函数进行求解,当然在大多数情况下我们都是求解损失函数的最小值。而伴随损失函数最小值点确定,我们也就找到了一组对应的损失函数自变量的取值,而改组自变量的取值也就是模型的最佳参数。在本例中,通过优化方法求解损失函数的过程还是非常简单的,后续我们在进行更加复杂的损失函数、并适用更加复杂的优化算法案进行求解时,我们会发现,损失函数的求解过程才是建模的主体。
截止目前,我们并未在数学理论和代码上展开讨论,此处我们也是希望能够在数学理论和代码难度不设门槛的情况下,先行介绍关于机器学习基础理论以及机器学习的一般建模流程。在后续的课程中,数学理论和代码难度都将逐渐提升。类似于最小二乘法严谨数学推导、手动实现复杂数据集上的线性回归建模、其他常用优化算法、调动Scikit-Learn完成相关建模工作等,都将在Lesson 2中进行详细讨论。