当前位置: 首页 > article >正文

【机器学习-模型评估】

“评估”已建立的模型

在进行回归和分类时,为了进行预测,定义了预测函数fθ(x)
然后根据训练数据求出了预测函数的参数θ(即对目标函数进行微分,然后求出参数更新表达式的操作)
之前求出参数更新表达式之后就结束了。但是,其实我们真正想要的是通过预测函数得到预测值。
所以我们希望fθ(x)对未知数据x输出的预测值尽可能正确:

测量预测函数fθ(x)的正确性:精度

对于一元二元问题可以画图观测:
在这里插入图片描述
但是像多重回归这样的问题,变量增加后就不能在图上展示了,而且特意去画图也很麻烦
还有一点,是从训练数据中得到的参数,训练结束之后得到的就是只针对于训练集的正确参数
所以我们需要能够定量地表示机器学习模型的精度即模型评估

交叉验证

回归问题

把获取的全部训练数据分成两份:一份用于测试,一份用于训练。然后用前者来评估模型。
即让出一部分训练集不用于学习而是用于学习后对模型的评估测验
在这里插入图片描述
先观测一下同样的数据集用一元和二元分别训练后的预测函数
在这里插入图片描述
在这里插入图片描述
发现二元函数在训练集上的精度更高,但是测试集上的精度反而降低
模型评估就是像这样检查训练好的模型对测试数据的拟合情况
但还是那个问题,现实生活中的问题时,大多数情况不能像现在这样画图来看,所以我们需要定量地衡量精度
对于回归的情况,只要在训练好的模型上计算测试数据的误差的平方,再取其平均值就可以了。

均方误差MSE

假设测试数据有n个,那么可以这样计算:
在这里插入图片描述

这个值被称为均方误差或者MSE,全称Mean Square Error。这个误差越小,精度就越高,模型也就越好。
在这里插入图片描述
可知回归的目标函数也是误差函数,这与为了让误差函数的值变小而更新参数时所做的事情是一样的

分类问题

在这里插入图片描述

假设在逻辑回归的情况下,预测函数θTx是简单的一次函数,那么只根据训练数据进行训练后,决策边界应该是这样的

在这里插入图片描述
但是假如预测函数f=θTx更加复杂,可能就会像这样紧贴着训练数据进行分类
在这里插入图片描述
可以对训练数据完美地进行分类,却完全忽视了测试数据
所以在分类的时候,我们还必须检查模型是否正确。
由于回归是连续值,所以可以从误差入手,但是在分类中我们必须要考虑分类的类别是否正确
即在回归中要考虑的是答案不完全一致时的误差,而分类中要考虑的是答案是否正确(完全一致)
比如一个横纵向的二分类问题,无非有四个结果:
在这里插入图片描述
设分类结果为正的情况是Positive、为负的情况是Negative。分类成功为True、分类失败为False
那么一般来说,二分类的结果可以用这张表来表示:
在这里插入图片描述
使用表里的4个记号来计算分类的精度(表示的是在整个数据集中,被正确分类的数据TP和TN所占
的比例):
在这里插入图片描述
假如100个数据中80个被正确地分类了,那么精度就是
在这里插入图片描述
用测试数据来计算这个值,值越高精度越高,也就意味着模型越好。

精确率和召回率

一般来说,只要计算出这个Accuracy值,基本上就可以掌握分类结果整体的精度了。
但是有时候只看这个结果会有问题,所以还有别的指标。
假设图中的圆点是Positive数据、叉号是Negative 数据,考虑数据集极其不平衡的情况:
在这里插入图片描述
那么假设有100个数据,其中95个是Negative。
此时哪怕出现模型把数据全部分类为Negative的极端情况,Accuracy值也为0.95,
也就是说模型的精度是95%。
但是不管精度多高,一个把所有数据都分类为某一个类的模型(尽管这个类极端的多),但不能说它是好模型
考虑这个图:
在这里插入图片描述
这个例子看上去对Positive数据分类得不够好。

精确率(Precision):

在这里插入图片描述

这个指标只关注TP和FP
根据表达式来看,它的含义是在被分类为Positive 的数据中,实际就是Positive的数据所占的比例:
在这里插入图片描述
那么有:
在这里插入图片描述
这个值越高,说明分类错误越少
拿这个例子来说,虽然被分类为Positive 的数据有3个,但其中只有1个是分类正确的。所以计算得出的精确率很低。

召回率(recall)

把精确率分母上的FP换成FN:
在这里插入图片描述
这个指标只关注TP和FN
根据表达式来看,它的含义是在Positive 数据中,实际被分类为Positive的数据所占的比例:
在这里插入图片描述
在这里插入图片描述
基于这两个指标来考虑精度是比较好的。
不过一般来说,精确率和召回率会一个高一个低,需要我们取舍,精确率和召回率都很高的模型就是一个好模型.

另外,并不一定将准确/召回都以TP为主,也可以TN为主
在这里插入图片描述
到底以什么为主,当数据不平衡时,使用数量少的那个会更好
如Positive 极少,所以我们使用了Positive来计算,反之如果Negative较少,那就使用Negative

如何取舍?

假如就两个值都计算,然后取它们的平均值,考虑极端情况:
在这里插入图片描述
模型B召回率是1.0,也就是说所有的Positive数据都被分类为Positive了(很好)
但是精确率特别低
如果将所有的数据都分类为Positive,那么召回率就是1.0。但是这样一来,Negative数据也会被分类为Positive,所以精确率会变得很低。
看一下两个模型的平均值,会发现模型B的更高。但它是把所有数据都分类为Positive的模型,精确率极低,仅为0.02,并不能说它是好模型。
所以只看平均值确实无法知道模型的好坏,由此引出对Precision和Recall的综合取舍指标F值

F值(Fmeasure/F1值)

Precision是前面说的精确率,Recall是召回率,考虑数学上精确率和召回率的调和平均值:
在这里插入图片描述
异或(都可以完全一样):
在这里插入图片描述

不难得出精确率和召回率只要有一个低,就会拉低F值。
回顾前面的极端情况:
在这里插入图片描述
发现B模型的F值更高,因为F指标考虑到了精确率和召回率的平衡。

除F1值之外,还有一个带权重的F值指标:
在这里插入图片描述
我们可以认为F值指的是带权重的F值,当权重为1时才是刚才介绍的F1值

把全部训练数据分为测试数据和训练数据的做法称为交叉验证。
对于回归和分类,我们都可以这样来评估模型
并且在交叉验证的方法中,尤为有名的是K折交叉验证:

  • 把全部训练数据分为K份
  • 将K−1份数据用作训练数据,剩下的1份用作测试数据
  • 每次更换训练数据和测试数据,重复进行K次交叉验证
  • 最后计算K个精度的平均值,把它作为最终的精度
    在这里插入图片描述
    如果全部训练数据的量较大,这种方法必须训练多次,不切实际地增加K值会非常耗费时间,所以我们必须要确定一个合适的K值。

正则化

过拟合(overfitting)

有几种方法可以避免过拟合:

  • 增加全部训练数据的数量
  • 使用简单的模型
  • 正则化

首先,重要的是增加全部训练数据的数量。
因为学习是从数据中学习的,所以数据最重要。
另外,使用更简单的模型也有助于防止过拟合(例如过过度增加函数fθ(x)的次数会导致过拟合)。

回归正则化方法:

先回顾回归的目标函数
在这里插入图片描述
增加正则化项(m是参数个数):
L2正则化方法:
在这里插入图片描述
那么:
在这里插入图片描述
对这个新的目标函数进行最小化,这种方法就称为正则化
θ0这种只有参数的项称为偏置项,一般不对它进行正则化(j的取值是从1开始的,所以实际上只对m-1个参数θ正则化)
假如预测函数的表达式为fθ(x)=θ0+θ1x+θ2x2,那么m=2就意味着正则化的对象参数为θ1和θ2
λ 是决定正则化项影响程度的正的常数。这个值需要我们自己来定。

正则化效果

将目标函数分为两个部分CR
C(θ) 是本来就有的目标函数项,R(θ)是正则化项:

在这里插入图片描述
C(θ)和R(θ)相加之后就是新的目标函数
我们实际地把这两个函数的图形画出来(这个目标函数大致开口向上),加起来看看。
不过参数太多就画不出图来了,所以这里我们只关注θ1:
在这里插入图片描述
从这个目标函数在没有正则化项时的形状来看,θ1=4.5附近是最小值。
接下来是R(θ),它就相当于1/2*θ1^2,是过原点的简单二次函数
实际的目标函数是这两个函数之和E(θ)=C(θ)+R(θ),要画函数相加后的图即把θ1各点上的C(θ)和R(θ)的高相加,然后用线把它们相连:
在这里插入图片描述

本来是在θ1=4.5处最小,现在是在θ1=0.9处最小,与加正则化项之前相比,θ1更接近0了的确更接近0了。
这就是正则化的效果:
它可以防止参数变得过大,有助于参数接近较小的值。
虽然我们只考虑了θ1,但其他θj参数的情况也是类似的。这样就能防止过拟合(参数的值变小,意味着该参数的影响也会相应地变小):
比如考虑二次预测方程:
在这里插入图片描述
极端一点,假设θ2=0,这个表达式就从二次变为一次了,这就意味着本来是曲线的预测函数变为直线了
在这里插入图片描述
正是通过减小不需要的参数的影响,将复杂模型替换为简单模型来防止过拟合的方式
不过只是个例子,并不一定非要减小次数最高项的参数值。
整体思路就是:
为了防止参数的影响过大,在训练时要对参数施加一些惩罚,而λ就是可以控制正则化惩罚的强度

比如令λ=0,那就相当于不使用正则化:
在这里插入图片描述

反过来λ越大,正则化的惩罚也就越严厉(横向压缩更靠近原点)
在这里插入图片描述

分类正则化方法:

同样考虑目标函数
逻辑回归的目标函数是对数似然函数
在这里插入图片描述
原来的目标函数上加上负号是因为:
对数似然函数本来以最大化为目标。但是,这次想让它变成和回归的目标函数一样的最小化问题,所以加了负号。
这样就可以像处理回归一样处理它,所以只要加上正则化项就可以了。

反转符号是为了将最大化问题替换为最小化问题
反转了符号之后,在更新参数时就要像回归一样,与微分的函数的符号反方向移动

又因为反转了符号,目标函数发生改变,那么参数更新表达式也会改变
但是只需要再把正则化项的部分也微分了就行

包含正则化项的表达式的微分

在这里插入图片描述
在这里插入图片描述
C(θ)是原来的目标函数,讲回归的时候已经求过它的微分形式了:
在这里插入图片描述
以接下来只要对正则化项进行微分,正则化项只是参数平方的和,所以它的微分也很好求:
在这里插入图片描述
在这里插入图片描述

最终有:
在这里插入图片描述
目标函数对该参数的微分完成后,带入该参数更新表达式(参数±学习率×目标函数微分):
在这里插入图片描述
这就是加入了正则化项的参数更新表达式。
因为一般不对θ0应用正则化。R(θ)对θ0微分的结果为0,所以j=0时表达式中的λθj就消失了。
因此,实际上我们需要像这样区分两种情况:
在这里插入图片描述

逻辑回归正则化后的微分

原来的目标函数是C(θ),正则化项是R(θ),现在对E(θ)进行微分:
在这里插入图片描述在这里插入图片描述
之前已经求过逻辑回归原来的目标函数C(θ)的微分,不过现在考虑的是最小化问题,所以要注意在
前面加上负号。也就是要进行符号的反转:
在这里插入图片描述
我们已经求过正则化项R(θ)的微分:
在这里插入图片描述

所以参数更新表达式:
在这里插入图片描述

除了L2,还有L1正则化方法:
在这里插入图片描述
L1正则化的特征是被判定为不需要的参数会变为0,从而减少不要的变量个数
L2正则化不会把参数变为0。而是会抑制参数,使变量的影响不会过大

欠拟合(underfitting)

一个是过度训练(过拟合),一个是过度不训练(欠拟合):
欠拟合是与过拟合相反的状态,即没有拟合训练数据的状态
比如用直线对图中这种拥有复杂边界线的数据进行分类的情况,无论怎样做都不能很好地分类,最终的精度会很差:
在这里插入图片描述
出现这种情况的主要原因也和过拟合的情况相反就是模型相对于要解决的问题来说太简单了

区分过拟合与欠拟合

只根据精度不能判断是哪种不好的拟合
对于数据集,采取只选两个点学习训练:
在这里插入图片描述
那么:
在这里插入图片描述
在这个状态下,2个点都完美拟合,误差为0,因为一次函数肯定会以通过这2点为目标去训练
但是若全部用来训练:
在这里插入图片描述
在这种情况下,fθ(x)是一次函数,误差已经无法为0了。

如果模型过于简单,那么随着数据量的增加,误差也会一点点变大。换句话说就是精度会一点点下降。
把这种情况画在刚才所说的以数据的数量为横轴、以精度为纵轴的图上:
在这里插入图片描述
一开始精度很高,但随着数据量的增加,精度一点点地变低了。

接下来用测试数据来评估一下:
假设在刚才的10个训练数据之外,还有测试数据。我们用这些测试数据来评估各个模型,之后用同样的方法求出精度,并画成图
即用测试数据先评估根据2个训练数据训练好的模型,再评估根据10 个训练数据训练好的模型……然后依次求出精度

因为训练数据较少时训练好的模型难以预测未知的数据,所以精度很低;
反过来说,训练数据变多时(慢慢地将测试数据融入训练数据),预测精度就会一点点地变高。用图来展示就是这样的:
在这里插入图片描述
将两份数据的精度用图来展示后,如果是这种形状,就说明出现了欠拟合的状态。也有一种说法叫作高偏差
这是一种
即使增加数据的数量,无论是使用训练数据还是测试数据,精度也都会很差的状态

在这里插入图片描述
过拟合的情况下,也叫作高方差:
在这里插入图片描述
随着数据量的增加,使用训练数据时的精度一直很高,而使用测试数据时的精度一直没有上升到它的水准,只对训练数据拟合得较好,这就是过拟合的特征
在这里插入图片描述

像这样展示了数据数量和精度的图称为学习曲线
知道模型精度低,却不知道是过拟合还是欠拟合时画一下学习曲线就好了
通过学习曲线判断出是过拟合还是欠拟合之后,就可以采取相应的对策以便改进模型了。

实现

应用正则化(只需修改一下学习部分)

并且通过比较过拟合时图的状态和应用了正则化后图的状态,具体总结出正则化对模型施加了什么样的影响(特意弄出一个过拟合的状态,减少训练数据、增加训练次数)
比如这个函数(在此之前先在训练集加入一点噪声的训练数据)
在这里插入图片描述

import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

# 真正的函数
def g(x):
     return 0.1 * (x ** 3 + x ** 2 + x)
# 随意准备一些向真正的函数加入了一点噪声的训练数据
train_x = np.linspace(-2, 2, 8)
train_y = g(train_x) + np.random.randn(train_x.size) * 0.05
# 绘图确认
x = np.linspace(-2, 2, 100)
plt.plot(train_x, train_y, 'o')
plt.plot(x, g(x), linestyle='dashed')
plt.ylim(-1, 2)
plt.savefig('AI15.png')

在这里插入图片描述
虚线就是正确的g(x)的图形,圆点就是加入了一点噪声的训练数据(8个)

假设用10次多项式来学习这个训练数据。首先编写从创建训练数据的矩阵到预测函数的定义为止的代码:

# 标准化
mu = train_x.mean()
sigma = train_x.std()
def standardize(x):
     return (x - mu) / sigma
 
train_z = standardize(train_x)
 
# 创建训练数据的矩阵
def to_matrix(x):
     return np.vstack([
          np.ones(x.size),
          x,
          x ** 2,
          x ** 3,
          x ** 4,
          x ** 5,
          x ** 6,
          x ** 7,
          x ** 8,
          x ** 9,
          x ** 10,
      ]).T
 
X = to_matrix(train_z)
 
# 参数初始化
theta = np.random.randn(X.shape[1])

# 预测函数
def f(x):
     return np.dot(x, theta)

不应用正则化的实现

实现学习部分:
首先是不应用正则化的状态, η 值和学习的结束条件是根据之前多次尝试的结果来决定的

# 目标函数
def E(x, y):
     return 0.5 * np.sum((y - f(x)) ** 2)

# 学习率
ETA = 1e-4
# 误差
diff = 1
# 重复学习
error = E(X, train_y)
while diff > 1e-6:
     theta = theta - ETA * np.dot(f(X) - train_y, X)
     current_error = E(X, train_y)
     diff = error - current_error
     error = current_error

# 对结果绘图
z = standardize(x)
plt.plot(train_z, train_y, 'o')
plt.plot(z, f(to_matrix(z)))
plt.savefig('AI16.png')

在这里插入图片描述

图像看上去歪歪扭扭是因为发生了过拟合的状态。
由于参数的初始值是随机数,所以每次执行时这个图的形状都不一样。
但是,从该图中也能看出它与g(x) 相差很远。
如果应用了正则化,这个图就会变好一点:( λ的值也是根据我之前多次尝试的结果来决定的。)

# 保存未正则化的参数,然后再次参数初始化
theta1 = theta
theta = np.random.randn(X.shape[1])

# 正则化常量
LAMBDA = 1

# 误差
diff = 1

# 重复学习(包含正则化项)
error = E(X, train_y)
while diff > 1e-6:
     # 正则化项。偏置项不适用正则化,所以为0
     reg_term = LAMBDA * np.hstack([0, theta[1:]])
     # 应用正则化项,更新参数
     theta = theta - ETA * (np.dot(f(X) - train_y, X) + reg_term)
     current_error = E(X, train_y)
     diff = error - current_error
     error = current_error

# 对结果绘图
plt.plot(train_z, train_y, 'o')
plt.plot(z, f(to_matrix(z)))
plt.savefig('AI17.png')

在这里插入图片描述

为了便于比较,把

未应用和应用了正则化这两种情况展示在一张图上

虚线是未应用正则化的情况,而实线是应用了正则化的情况:
在这里插入图片描述
完整代码:

import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

# 真正的函数
def g(x):
     return 0.1 * (x ** 3 + x ** 2 + x)
# 随意准备一些向真正的函数加入了一点噪声的训练数据
train_x = np.linspace(-2, 2, 8)
train_y = g(train_x) + np.random.randn(train_x.size) * 0.05
# # 绘图确认
x = np.linspace(-2, 2, 100)
# plt.plot(train_x, train_y, 'o')
# plt.plot(x, g(x), linestyle='dashed')
# plt.ylim(-1, 2)
# plt.savefig('AI15.png')

# 标准化
mu = train_x.mean()
sigma = train_x.std()
def standardize(x):
     return (x - mu) / sigma

train_z = standardize(train_x)

# 创建训练数据的矩阵
def to_matrix(x):
     return np.vstack([
          np.ones(x.size),
          x,
          x ** 2,
          x ** 3,
          x ** 4,
          x ** 5,
          x ** 6,
          x ** 7,
          x ** 8,
          x ** 9,
          x ** 10,
      ]).T

X = to_matrix(train_z)

# 参数初始化
theta = np.random.randn(X.shape[1])

# 预测函数
def f(x):
     return np.dot(x, theta)


# 目标函数
def E(x, y):
     return 0.5 * np.sum((y - f(x)) ** 2)

# 学习率
ETA = 1e-4
# 误差
diff = 1
# 重复学习
error = E(X, train_y)
while diff > 1e-6:
     theta = theta - ETA * np.dot(f(X) - train_y, X)
     current_error = E(X, train_y)
     diff = error - current_error
     error = current_error

z = standardize(x)
# 对结果绘图
# plt.plot(train_z, train_y, 'o')
# plt.plot(z, f(to_matrix(z)))
# plt.savefig('AI16.png')


# 保存未正则化的参数,然后再次参数初始化
theta1 = theta
theta = np.random.randn(X.shape[1])

# 正则化常量
LAMBDA = 1

# 误差
diff = 1

# 重复学习(包含正则化项)
error = E(X, train_y)
while diff > 1e-6:
     # 正则化项。偏置项不适用正则化,所以为0
     reg_term = LAMBDA * np.hstack([0, theta[1:]])
     # 应用正则化项,更新参数
     theta = theta - ETA * (np.dot(f(X) - train_y, X) + reg_term)
     current_error = E(X, train_y)
     diff = error - current_error
     error = current_error

# 对结果绘图
# plt.plot(train_z, train_y, 'o')
# plt.plot(z, f(to_matrix(z)))
# plt.savefig('AI17.png')

# 保存应用了正则化的参数
theta2 = theta
plt.plot(train_z, train_y, 'o')
# 画出未应用正则化的结果
theta = theta1
plt.plot(z, f(to_matrix(z)), linestyle='dashed')
# 画出应用了正则化的结果
theta = theta2
plt.plot(z, f(to_matrix(z)))
plt.savefig('AI18.png')

http://www.kler.cn/a/595829.html

相关文章:

  • 老旧中控系统智能化改造方案:基于巨控OPC561Q-C模块实现多通道实时报警
  • 【css酷炫效果】纯CSS实现全屏万花筒效果
  • 八股文MYSQL
  • Centos7部署学之思考试系统
  • 新书速览|云原生Kubernetes自动化运维实践
  • 解决 uniapp 开发中权限申请同步告知目的问题| 华为应用商店上架审核问题解决
  • 初始EBP和ESP的设置
  • Android Compose 图像修饰深度解析(八)
  • 使用Python轻松拆分PDF,每页独立成文件
  • (一)丶Windows安装RabbitMQ可能会遇到的问题
  • JavaScript性能优化实战:深入探讨性能瓶颈与优化技巧
  • STM32 SPI总线驱动CH376T实现U盘/TF卡读写全解析—CH376数据手册分析(中上) | 零基础入门STM32第七十三步
  • Event driven agentic document workflows 笔记 - 3
  • 【Javascrip】Javascript练习01 REST API using Express.js.
  • NFS 安装与测试
  • MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part11
  • C++修炼:内存管理
  • 最质量实践Docker
  • Github 2025-03-21Java开源项目日报Top9
  • 【Linux 维测专栏 2 -- Deadlock detection介绍】