Abstract
回归问题普遍存在于现实应用中。一般来说,要建立一个具有良好泛化能力的回归模型,需要大量的标记样本。然而,很多时候收集大量的未标记样本相对容易,但是对其进行标记却耗时或昂贵。主动回归学习( active learning for regression,ALR )是一种减少标记样本数量的方法,通过选择最有利的样本进行标记,而不是随机选择。本文提出了两种新的基于贪婪采样(GS)的ALR方法。第一种方法(GSy)选择新样本以增加输出空间的多样性,第二种方法(iGS)选择新样本以增加输入和输出空间的多样性。在10个来自不同领域的UCI和CMU StatLib数据集上,以及15个基于EEG的驾驶人嗜睡评估受试者上的大量实验,验证了其有效性和鲁棒性。
1. Introduction
回归是从一个或多个自变量(预测因子、特征、输入)中估计一个因变量(输出)的值,是机器学习中常见的问题。为了建立一个准确的回归模型,需要有一些有标记的训练样本,其因变量和自变量的值都是已知的。一般而言,有标记训练样本越多,回归性能越好。然而,在现实世界中很多时候获取自变量的值是相对容易的,但是给它们贴标签是费时或昂贵的。例如,在效价、唤醒度和优势度3维空间的语音情感估计[30、31] [15]中,很容易记录到大量的语句,但评估其情感[2、12]却很耗时。另一个例子是利用脑电(EEG)等生理信号估计驾驶人的嗜睡程度[26-28]。收集大量的EEG试次相对容易,但要获得其真实的嗜睡却具有挑战性。
为了克服回归中的小样本问题,至少研究了四个不同的方向:
- 正则化[13、34],引入额外的信息来提高泛化性能。例如,岭回归(RR) [14]和LASSO [20]惩罚大回归系数。
- 迁移学习[16]利用相关领域/任务的数据或信息来提高回归性能。例如,对于一个只有少量EEG试验[24、28]的新受试者,可以利用大量来自其他受试者的带标签EEG数据来提高其困倦估计性能。
- 半监督学习[6]同时利用未标记数据进行训练,因为它们也包含非常有用的信息。例如,协同训练[33]构建两个不同的回归器,然后使用每个回归器对未标记数据的最有信心的预测来迭代地为另一个回归器构建额外的标记训练数据。
- 主动学习[18],即选择最有利的未标记样本进行标记,而不是随机选择。例如,批处理模式主动学习已被用于基于EEG的驾驶人嗜睡程度估计[26]。
本文重点研究第四个方向,即面向回归的主动学习(ALR)。特别地,我们考虑基于序列池(sequential pool-based)的ALR [ 19 ],其中给定一个未标记样本池,目标是顺序地选择一些样本进行标记,这样从它们训练的回归模型可以对剩余的未标记样本给出最准确的估计。与用于分类的主动学习的大型文献相比,基于序列池的ALR [3、5、32]只有少数方法。本文对ALR的主要贡献有:
- 受文献[32]中贪婪采样(GS)方法的启发,我们提出了两种新的ALR方法。它们易于理解和实现。
- 在10个来自不同领域的UCI和CMU StatLib数据集上,以及15个基于EEG的驾驶人嗜睡评估实验中,验证了所提方法的有效性和鲁棒性。
本文的剩余部分安排如下:第二部分介绍了原始的GS ALR方法,并提出了两个新的ALR算法。第3节介绍了用于评估不同ALR方法有效性的10个UCI和CMU StatLib数据集,以及相应的实验结果。第四部分介绍了基于离线EEG的驾驶人嗜睡评估实验,用于评估不同ALR方法的有效性,以及相应的实验结果。最后,第5节总结全文并指出未来的研究方向。
2. Greedy sampling ALR approaches
本节介绍文献中已有的GS ALR方法,并提出两种新的ALR方法。
2.1. Greedy sampling on the inputs (GSx)
Yu和Kim [32]提出了四种用于回归的被动采样方法(passive sampling approaches)。与大多数ALR方法通常需要在每次迭代中更新回归模型并计算对未标记样本的预测不同,被动采样完全基于样本在特征空间中的位置进行选择。因此,它独立于回归模型,且计算成本低。
在[32]的四种被动采样方法中,GS取得了最好的综合性能。然而,原始的GS方法并没有解释如何选择第一个样本。本小节介绍了GSx,它与GS本质上是相同的,除了它还包括选择第一个样本进行标记的策略。
假设池由N个样本组成 { X n } n = 1 N \{X_n\}^N_{n=1} {Xn}n=1N, 最初没有一个被标记。我们的目标是选择其中的 K K K个进行标注,然后从中构建一个精确的回归模型来估计剩余 N − K N - K N−K个样本的产出。GSx选择第一个样本作为所有 N N N个样本中离质心最近的一个(即与剩余N - 1个样本距离最短的那个),剩余的K - 1个样本递增。其思路是使第一种选择最具代表性。
不失一般性,假设前 k k k个样本已经被选中。对于剩余的 N − k N - k N−k个未标记样本 { X n } n = k + 1 N \{X_n\}^N_{n=k+1} {Xn}n=k+1N, GSx首先计算它与 k k k个标记样本的距离:
![](https://i-blog.csdnimg.cn/blog_migrate/be065b059a6deab91ff93762c80874ed.png)
则 d n x d_n^x dnx, x n x_n xn到所有 k k k个标记样本的最短距离:
![](https://i-blog.csdnimg.cn/blog_migrate/03b11297ff04eb7c6c47478fa5ed6c35.png)
最后选择 d n x d^x_n dnx最大的样本进行标注。
综上,GSx选择第一个样本作为距离池质心最近的样本,在随后的每次迭代中选择一个距离输入空间中所有先前选择的样本最远的新样本,以实现所选样本的多样性。其伪代码在算法1中给出。
![](https://i-blog.csdnimg.cn/blog_migrate/c547f0ce9bb957a07a44bf0f8b42fedf.png)
2.2. Greedy sampling on the output (GSy)
GSx在输入空间实现了多样性。我们提出的GSy旨在实现输出空间的多样性。
与GSx一样,在GSy中,初始池由N个未标记样本和零标记样本组成。为了评估输出空间的多样性,我们需要知道所有样本的输出(标签),无论是真实的还是估计的。也就是说,在得到 K 0 K_0 K0个标记样本之前,无法应用GSy,其中 K 0 K_0 K0为建立回归模型所需的最少标记样本数量。本文设定 K 0 K_0 K0为输入空间中特征的个数,使用GSx选择前 K 0 K_0 K0个样本进行标注。
假设前 k k k个( k ≥ K 0 k≥K_0 k≥K0)样本已经被标记为输出 { y m } m = 1 k \{y_m\}^k_{m = 1} {ym}m=1k,构建回归模型 f ( x ) f ( x ) f(x)。对于剩余的 N − k N - k N−k个未标记样本 { X n } n = k + 1 N \{X_n\}^N_{n=k+1} {Xn}n=k+1N`,GSy首先计算它到每个 k k k个输出的距离:
![](https://i-blog.csdnimg.cn/blog_migrate/2289b853cb14dd3f936439af5e41a095.png)
d n y , f ( x n ) d^y_n,f (x_n) dny,f(xn)到 { y m } m = 1 k \{y_m\}^k_{m = 1} {ym}m=1k 的最短距离:
![](https://i-blog.csdnimg.cn/blog_migrate/c4f08ac5673564ee13a7b04d6de7a251.png)
然后选取 d n y d^y_n dny最大的样本进行标注。
综上,GSy利用GSx选择前几个样本建立初始回归模型,然后在随后的每一次迭代中,在输出空间中选择一个新样本,使其距离所有先前选择的样本最远,从而实现选择样本的多样性。其伪代码在算法2中给出。注意到GSy不再是被动的采样方式,因为它需要在每次迭代中更新 f ( x ) f ( x ) f(x)。
![](https://i-blog.csdnimg.cn/blog_migrate/33d817a05731315fb547b64db35a736f.png)
GSy的基本原理可以用下面的简单例子来说明,如图1所示。假设输入空间只有两个维度,我们只有四个样本:
![](https://i-blog.csdnimg.cn/blog_migrate/383170f526b97fcd2bca944cd24629bc.png)
- 图1. Gsy的插图。如果第1个预测变量比第2个预测变量更敏感(重要),GSy将选择 x 3 x_3 x3进行标记,否则选择 x 4 x_4 x4。
其中前两者分别有标签 y 1 y_1 y1和 y 2 y_2 y2,后两者无标签, δ δ δ为小数。由 ( x 1 , y 1 ) (x_1 , y_1) (x1,y1)和 ( x 2 , y 2 ) (x_2 , y_2) (x2,y2)构建回归函数 f ( x ) f ( x ) f(x)。我们希望选取 x 3 x_3 x3或 x 4 x_4 x4进行标记,使得 f ( x ) f(x) f(x)在四个样本上的估计误差最大程度的减小。
为了简单起见,假设
f
(
x
3
)
f(x_3)
f(x3)和
f
(
x
4
)
f(x_4)
f(x4)都比
y
2
y_2
y2更靠近
y
1
y_1
y1,所以我们只需要考虑它们在GSy中与
y
1
y_1
y1的距离。
f
(
x
)
f (x)
f(x)对第1个预测器的灵敏度,在
x
1
x_1
x1附近取值,可近似为
同理,
f
(
x
)
f (x)
f(x)对同样在
x
1
x_1
x1附近取值的第二个预报因子的灵敏度可近似为
当
∣
s
1
∣
>
∣
s
2
∣
| s_1 | > | s_2 |
∣s1∣>∣s2∣,即
f
(
x
)
f ( x )
f(x)对第一个预测因子比对第二个预测因子更敏感时,我们应该选择一个有助于细化第一个回归系数的样本进行标记。一般而言,第一个预测变量的取值越多样,其回归系数的确定就越准确。因此,在这种情况下应该选择
x
3
x_3
x3进行标注。注意到
∣
s
1
∣
>
∣
s
2
∣
| s_1 | > | s_2 |
∣s1∣>∣s2∣意味着
∣
f
(
x
3
)
−
y
1
∣
>
∣
f
(
x
4
)
−
y
1
∣
| f ( x_3 ) - y_1 | > | f ( x_4 ) - y_1 |
∣f(x3)−y1∣>∣f(x4)−y1∣.按照GSy的步骤,确实会选择
x
3
x_3
x3。类似地,当
∣
s
1
∣
<
∣
s
2
∣
| s_1 | < | s_2 |
∣s1∣<∣s2∣时,GSy将正确选择
x
4
x_4
x4进行标记。
2.3. Improved greedy sampling (iGS) on both inputs and output
GSx只考虑输入(特征)空间的多样性,通过计算一个未标记样本与所有已有标记样本之间的最小距离,利用所有特征。然而,也许并非所有的特征都是有用的;即使所有特征都是有用的,它们可能具有不同的重要性。GSx不考虑特征选择/加权。
GSy通过计算样本的估计输出与所有现有输出之间的最小距离,仅考虑输出(标签)空间中的多样性。我们在上一小节中的例子表明,GSy试图选择一个能显著增加最敏感预测变量多样性的新样本。因此,它隐式地考虑了特征选择/加权。然而,预测灵敏度是使用由极少量标记样本构造的 f ( x ) f ( x ) f(x)来评估的,因此它们可能不是很准确。换言之,GSy中的特征选择/加权可能并不可靠。
在本小节中,我们提出了一种改进的贪婪采样(iGS)方法,它结合了GSx和GSy,以确保我们将特征选择/加权考虑在内,但如果特征选择/加权具有误导性,也可以避免灾难性的失败。
与GSx和GSy一样,初始池由
N
N
N个未标记样本和零标记样本组成。在iGS中,我们再次设置
K
0
K_0
K0为输入空间中的特征数,并使用GSx选择前
K
0
K_0
K0个样本进行标注。假设前
k
k
k个样本已经被标记为标签
{
y
n
}
n
=
1
k
\{y_n\}^k_{n = 1}
{yn}n=1k。对于剩余的
N
−
k
N - k
N−k个未标记样本
{
x
n
}
n
=
k
+
1
N
\{x_n\}^N_{n = k + 1}
{xn}n=k+1N,iGS计算(1)中的前
d
n
m
x
d^x _{nm}
dnmx和(3)中的
d
n
m
y
d^y _{nm}
dnmy,以及
d
n
x
y
d^{xy}_n
dnxy:
然后选择
d
n
x
y
d^{xy}_n
dnxy最大的样本进行标记。注意,我们使用
d
n
m
x
d^x _{nm}
dnmx
d
n
m
y
d^y _{nm}
dnmy代替
d
n
m
x
d^x _{nm}
dnmx +
d
n
m
y
d^y _{nm}
dnmy或
(
d
n
m
x
)
2
(d^x _{nm}) ^2
(dnmx)2 +
(
d
n
m
y
)
2
(d^y _{nm}) ^2
(dnmy)2,因为
d
n
m
x
d^x _{nm}
dnmx和
d
n
m
y
d^y _{nm}
dnmy可能具有显著不同的尺度,在后两个公式中,一个尺度较大的项可能占主导地位,而
d
n
m
x
d^x _{nm}
dnmx
d
n
m
y
d^y _{nm}
dnmy对尺度不敏感。
综上,iGS利用GSx选择前几个样本建立初始回归模型,然后在随后的每一次迭代中,在输入空间和输出空间中选择一个距离所有先前选择的样本最远的新样本,以实现所选样本之间的多样性均衡。其伪代码在算法3中给出。注意,iGS不再是被动的采样方式,因为它需要在每次迭代中更新 f ( x ) f ( x ) f(x)。
![](https://i-blog.csdnimg.cn/blog_migrate/592df06769a3d55414b49a885f071e60.png)
3. Experiments on UCI and CMU statlib datasets
5. Conclusion and Future Research
通常需要大量有标签的训练样本才能建立准确的具有良好泛化能力的回归模型。然而,很多时候在现实世界的应用中我们可以收集大量的未标记样本,但是对它们进行标记是耗时的或昂贵的。ALR是一种选择最有利的无标签样本进行标注的方法,从而可以从少量的有标签样本中建立更好的回归模型。受文献中GS方法的启发,本文提出了两种新的ALR方法。在10个UCI和CMU StatLib数据集上以及15个被试上的基于EEG的驾驶疲劳估计实验验证了其有效性和鲁棒性。特别地,我们提出的iGS同时考虑了输入和输出空间的多样性,优于现有的几种ALR方法。
我们未来的研究将把GSx、GSy和iGS从回归推广到分类。此外,如引言所述,正则化、迁移学习和主动学习都可以用来处理没有足够标记训练数据的回归问题。在本文中我们将正则化和主动学习结合使用。未来我们还将研究如何将迁移学习和主动学习结合起来用于回归问题。我们之前的研究将迁移学习和主动学习集成到分类问题中,取得了很好的性能[22、25、29]。
References
Wu D, Lin C T, Huang J. Active learning for regression using greedy sampling[J]. Information Sciences, 2019, 474: 90-105.
[1] 论文复现: Active learning for regression using greeding sampling. Information Sciences
[2] Python:GSx缺点及改进
算法原理
GSx
GSx,完全基于样本在特征空间中的位置进行选择的被动采样方法
假设池由N个样本组成 { X n } n = 1 N \{X_n\}^N_{n=1} {Xn}n=1N, 最初没有一个被标记。我们的目标是选择其中的 K K K个进行标注,然后从中构建一个精确的回归模型来估计剩余 N − K N - K N−K个样本的产出。GSx选择第一个样本作为所有 N N N个样本中离质心最近的一个(即与剩余N - 1个样本距离最短的那个),剩余的K - 1个样本递增。其思路是使第一种选择最具代表性。
不失一般性,假设前 k k k个样本已经被选中。对于剩余的 N − k N - k N−k个未标记样本 { X n } n = k + 1 N \{X_n\}^N_{n=k+1} {Xn}n=k+1N, GSx首先计算它与 k k k个标记样本的距离:
![](https://i-blog.csdnimg.cn/blog_migrate/be065b059a6deab91ff93762c80874ed.png)
则 d n x d_n^x dnx, x n x_n xn到所有 k k k个标记样本的最短距离:
![](https://i-blog.csdnimg.cn/blog_migrate/03b11297ff04eb7c6c47478fa5ed6c35.png)
最后选择 d n x d^x_n dnx最大的样本进行标注。
综上,GSx选择第一个样本作为距离池质心最近的样本,在随后的每次迭代中选择一个距离输入空间中所有先前选择的样本最远的新样本,以实现所选样本的多样性。其伪代码在算法1中给出。
![](https://i-blog.csdnimg.cn/blog_migrate/3aa0dd5596db997c32be2dafc387d1a9.png)
GSy
GSx在输入空间实现了多样性。GSy旨在实现输出空间的多样性。
与GSx一样,在GSy中,初始池由N个未标记样本和零标记样本组成。为了评估输出空间的多样性,需要知道所有样本的输出(标签),无论是真实的还是估计的。也就是说,在得到 K 0 K_0 K0个标记样本之前,无法应用GSy,其中 K 0 K_0 K0为建立回归模型所需的最少标记样本数量。本文设定 K 0 K_0 K0为输入空间中特征的个数,使用GSx选择前 K 0 K_0 K0个样本进行标注。
假设前 k k k个( k ≥ K 0 k≥K_0 k≥K0)样本已经被标记为输出 { y m } m = 1 k \{y_m\}^k_{m = 1} {ym}m=1k,构建回归模型 f ( x ) f ( x ) f(x)。对于剩余的 N − k N - k N−k个未标记样本 { X n } n = k + 1 N \{X_n\}^N_{n=k+1} {Xn}n=k+1N`,GSy首先计算它到每个 k k k个输出的距离:
![](https://i-blog.csdnimg.cn/blog_migrate/2289b853cb14dd3f936439af5e41a095.png)
d n y , f ( x n ) d^y_n,f (x_n) dny,f(xn)到 { y m } m = 1 k \{y_m\}^k_{m = 1} {ym}m=1k 的最短距离:
![](https://i-blog.csdnimg.cn/blog_migrate/c4f08ac5673564ee13a7b04d6de7a251.png)
然后选取 d n y d^y_n dny最大的样本进行标注。
综上,GSy利用GSx选择前几个样本建立初始回归模型,然后在随后的每一次迭代中,在输出空间中选择一个新样本,使其距离所有先前选择的样本最远,从而实现选择样本的多样性。其伪代码在算法2中给出。注意到GSy不再是被动的采样方式,因为它需要在每次迭代中更新 f ( x ) f ( x ) f(x)。
![](https://i-blog.csdnimg.cn/blog_migrate/36152d9f07846e5bdb26884fff67d155.png)
iGS
GSx只考虑输入(特征)空间的多样性,通过计算一个未标记样本与所有已有标记样本之间的最小距离,利用所有特征。然而,也许并非所有的特征都是有用的;即使所有特征都是有用的,它们可能具有不同的重要性。GSx不考虑特征选择/加权。
GSy通过计算样本的估计输出与所有现有输出之间的最小距离,仅考虑输出(标签)空间中的多样性。我们在上一小节中的例子表明,GSy试图选择一个能显著增加最敏感预测变量多样性的新样本。因此,它隐式地考虑了特征选择/加权。然而,预测灵敏度是使用由极少量标记样本构造的 f ( x ) f (x) f(x)来评估的,因此它们可能不是很准确。换言之,GSy中的特征选择/加权可能并不可靠。
在本小节中,我们提出了一种改进的贪婪采样(iGS)方法,它结合了GSx和GSy,以确保我们将特征选择/加权考虑在内,但如果特征选择/加权具有误导性,也可以避免灾难性的失败。
与GSx和GSy一样,初始池由 N N N个未标记样本和零标记样本组成。在iGS中,我们再次设置 K 0 K_0 K0为输入空间中的特征数,并使用GSx选择前 K 0 K_0 K0个样本进行标注。假设前 k k k个样本已经被标记为标签 { y n } n = 1 k \{y_n\}^k_{n = 1} {yn}n=1k。对于剩余的 N − k N - k N−k个未标记样本 { x n } n = k + 1 N \{x_n\}^N_{n = k + 1} {xn}n=k+1N,iGS计算(1)中的前 d n m x d^x _{nm} dnmx和(3)中的 d n m y d^y _{nm} dnmy,以及 d n x y d^{xy}_n dnxy:
![](https://i-blog.csdnimg.cn/blog_migrate/7366775ebee478f528e0dd504d065abd.png)
然后选择 d n x y d^{xy}_n dnxy最大的样本进行标记。注意,我们使用 d n m x d^x _{nm} dnmx d n m y d^y _{nm} dnmy代替 d n m x d^x _{nm} dnmx + d n m y d^y _{nm} dnmy或 ( d n m x ) 2 (d^x _{nm}) ^2 (dnmx)2 + ( d n m y ) 2 (d^y _{nm}) ^2 (dnmy)2,因为 d n m x d^x _{nm} dnmx和 d n m y d^y _{nm} dnmy可能具有显著不同的尺度,在后两个公式中,一个尺度较大的项可能占主导地位,而 d n m x d^x _{nm} dnmx d n m y d^y _{nm} dnmy对尺度不敏感。
综上,iGS利用GSx选择前几个样本建立初始回归模型,然后在随后的每一次迭代中,在输入空间和输出空间中选择一个距离所有先前选择的样本最远的新样本,以实现所选样本之间的多样性均衡。其伪代码在算法3中给出。注意,iGS不再是被动的采样方式,因为它需要在每次迭代中更新 f ( x ) f ( x ) f(x)。
![](https://i-blog.csdnimg.cn/blog_migrate/bf51b426c18f61419813b91d2efd4c1b.png)
论文复现
GSx
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @author : 青年有志
# @time : 2023-05-19 18:55
# @function: Active learning for regression using greedy sampling
# @version : V1
import random
import sys
import numpy as np
import pandas as pd
from copy import deepcopy
from sklearn.linear_model import LinearRegression, Ridge
from sklearn import datasets
from sklearn import linear_model
from sklearn.metrics import accuracy_score, mean_absolute_error
import matplotlib.pyplot as plt
from collections import OrderedDict
from sklearn.model_selection import StratifiedKFold, KFold, cross_val_score
from sklearn.utils.random import check_random_state
from sklearn.ensemble import AdaBoostRegressor
import xgboost
class GSx():
def __init__(self, X_pool, y_pool, labeled, budget, X_test, y_test):
self.X_pool = X_pool # 样本
self.y_pool = y_pool # 样本标签
self.X_test = X_test # 测试集样本
self.y_test = y_test # 测试集标签
self.labeled = list(deepcopy(labeled)) # 已经标记的标签索引
self.unlabeled = self.initialization() # 未标记的标签索引
self.budgetLeft = deepcopy(budget) # 待标记的标签数量
self.model = AdaBoostRegressor() # 利用模型对选出的数据结果进行测试
self.MAEList = [] # 存储多次重复筛选的 MAE 结果,本代码只实现了一次,可在外层嵌套一层循环计算
def D(self, a, b):
return np.sqrt(sum((a - b) ** 2))
def initialization(self):
unlabeled = [i for i in range(len(self.y_pool))]
for j in self.labeled:
unlabeled.remove(j)
return unlabeled
def evaluation(self):
"""
使用 K 折交叉验证,K 中的验证集作为测试集,取平均作为泛化误差
:return:
"""
# ------------------------------------- 筛选的数据进行验证 ------------------------------------
X_train = self.X_pool[self.labeled]
y_train = self.y_pool[self.labeled]
# 在所有数据集上进行 K 折,将验证集作为测试集
AL_Train_scores = []
AL_Test_scores = []
kfold = KFold(n_splits=10, shuffle=True).split(X_train, y_train)
for k, (train, test) in enumerate(kfold):
self.model.fit(X_train[train], y_train[train])
AL_Train_score = mean_absolute_error(self.model.predict(X_train[train]), y_train[train])
AL_Test_score = mean_absolute_error(self.model.predict(X_train[test]),y_train[test])
AL_Train_scores.append(AL_Train_score)
AL_Test_scores.append(AL_Test_score)
# print('Fold: %2d, score: %.3f' % (k + 1, score))
AL_Train_MAE = np.mean(AL_Train_scores)
AL_Test_MAE = np.mean(AL_Test_scores)
print('训练集 MAE:', AL_Train_MAE, '测试集 MAE:', AL_Test_MAE)
return AL_Train_MAE, AL_Test_MAE
def select(self):
while self.budgetLeft > 0:
max_metric = OrderedDict() # 定义有序字典
for idx in self.unlabeled: # 遍历未被标记的样本
little = np.inf
for jdx in self.labeled: # 遍历所有标记的样本
dist = self.D(self.X_pool[idx], self.X_pool[jdx]) # 计算未标记样本 idx 已标记样本 jdx 的欧式距离
if dist < little: # 选择最小的距离
little = dist
max_metric[idx] = little # 记录未标记样本 idx 与所有标记样本的最短距离
tar_idx = max(max_metric, key=max_metric.get) # 选择最短距离最大的样本
self.labeled.append(tar_idx) # 将其标记,添加到标签中
self.unlabeled.remove(tar_idx) # 将其在未标记的标签中移除
self.budgetLeft -= 1
# ---------------------------------------- 产生数据集 --------------------------------------
# Training samples
X_train = np.random.uniform(-1, 1, 1000).reshape(500, 2)
y_train = X_train[:, 0]**2 - X_train[:, 1]**2 + X_train[:, 1] - 1
# Testing samples
X_test = np.random.uniform(-1, 1, 100).reshape(50, 2)
y_test = X_test[:, 0]**2 - X_test[:, 1]**2 + X_test[:, 1] - 1
# ----------------------------------------- 在所有数据集上进行训练评估 ----------------------------
model = AdaBoostRegressor()
# ----------------------------------------- K 折交叉验证,验证模型的性能 ------------------------
kfold = KFold(n_splits=10, shuffle=True).split(X_train, y_train)
# 在所有数据集上进行 K 折,将验证集作为测试集
Train_scores = []
Test_scores = []
for k, (train, test) in enumerate(kfold):
model.fit(X_train[train], y_train[train])
Train_score = mean_absolute_error(model.predict(X_train[train]), y_train[train])
Test_score = mean_absolute_error(model.predict(X_train[test]), y_train[test])
Train_scores.append(Train_score)
Test_scores.append(Test_score)
# print('Fold: %2d, score: %.3f' % (k + 1, score))
Train_MAE = np.mean(Train_scores)
Test_MAE = np.mean(Test_scores)
print('训练集 MAE:', Train_MAE, '测试集 MAE:', Test_MAE)
# --------------------------------------------------- 进行样本选择 ------------------------------------------------
labeled = [6, 16, 26, 36] # 未标记的数据索引
Budget = 100 # 需要标记的标签个数
ALmodel = GSx(X_pool=X_train, y_pool=y_train, labeled=labeled, budget=Budget, X_test=X_test, y_test=y_test)
ALmodel.select()
ALmodel.evaluation()
该算法是基于diversity原则的进行关键样本选择的。
iGS + 聚类去噪
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @author : 青年有志
# @time : 2023-05-31 13:43
# @function: the script is used to do something.
# @version : V1
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @author : 青年有志
# @time : 2023-05-22 19:01
# @function: 基于聚类初始化的输入输出空间多样性样本选择, Input-output spatial diversity sample selection based on clustering initialization
# @version : V1
import sys
import numpy as np
import pandas as pd
import xlwt
from pathlib import Path
from copy import deepcopy
from collections import OrderedDict
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score, mean_absolute_error, f1_score, \
recall_score, mean_squared_error, r2_score, davies_bouldin_score
from sklearn.cluster import KMeans, DBSCAN
import time
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.utils import check_random_state
from sklearn.ensemble import AdaBoostRegressor
import xgboost
from xgboost import XGBRegressor
from sklearn.decomposition import PCA
class IOCS():
def __init__(self, X_pool, y_pool, budget, X_test, y_test, Model):
self.X_pool = X_pool # 所有数据
self.X = StandardScaler().fit_transform(self.X_pool)
self.y_pool = y_pool # 所有的样本标签
self.X_test = X_test # 测试数据
self.y_test = y_test # 测试集标签
self.nSample, self.nAtt = self.X_pool.shape # 样本数量和特征数量
self.budgetLeft = deepcopy(budget) # 待选择的样本
# self.ATmodel = AdaBoostRegressor() # 模型
self.ATmodel = Model
self.labeled = [] # 初始时,以标记样本为空
self.unlabeled = []
self.initialization()
def initialization(self):
self.unlabeled = [i for i in range(self.nSample)] # 遍历所有样本
close_dist = np.inf
tar_idx = None
centroid = np.mean(self.X_pool, axis=0) # 计算 tar_cluster_ids 的中心位置
for idx in range(self.nSample):
if np.linalg.norm(self.X_pool[idx] - centroid) < close_dist:
close_dist = np.linalg.norm(self.X_pool[idx] - centroid) # 平方和开根号
tar_idx = idx
self.labeled.append(tar_idx)
self.unlabeled.remove(tar_idx)
def D(self, a, b):
return np.sqrt(sum((a - b) ** 2))
def evaluation(self):
"""
使用 K 折交叉验证,K 中的验证集作为测试集,取平均作为泛化误差
:return:
"""
X_train = self.X_pool[self.labeled]
y_train = self.y_pool[self.labeled]
# 在所有数据集上进行训练
time_start = time.time() # 计算起始时间
self.ATmodel.fit(X_train, y_train)
time_end = time.time() # 计算结束时间
traintime = time_end - time_start
y_pred = self.ATmodel.predict(self.X_test)
Test_MSE = mean_squared_error(self.y_test, y_pred)
R2 = r2_score(self.y_test, y_pred)
return Test_MSE, R2
def select(self):
# np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0, )
number = list()
base = 1
total = 0
while 1:
number.append(base)
base = 2 * base
total += base
if total > self.nSample:
number.append(base)
break
count = 0
while self.budgetLeft > 0:
# ----------------------------------------------- 计算 X 输入空间的多样性 -------------------------------------
X_max_metric = OrderedDict() # 定义有序字典
max_X = np.nan
min_X = np.inf
for idx in self.unlabeled: # 遍历未被标记的样本
little = np.inf
for jdx in self.labeled: # 遍历所有标记的样本
dist = self.D(self.X[idx], self.X[jdx]) # 计算未标记样本 idx 到已标记样本 jdx 的欧式距离
if dist < little: # 选择最小的距离
little = dist
X_max_metric[idx] = little # 记录未标记样本 idx 与所有标记样本的最短距离
if X_max_metric[idx] > max_X:
max_X = X_max_metric[idx]
elif X_max_metric[idx] < min_X:
min_X = X_max_metric[idx]
# for idx in self.unlabeled:
# X_max_metric[idx] = np.float(X_max_metric[idx] - min_X) / np.float(max_X - min_X)
# ----------------------------------------------- 计算 Y 输出空间的信息性 ---------------------------------------
self.ATmodel.fit(self.X_pool[self.unlabeled], self.y_pool[self.unlabeled])
Y_metric = OrderedDict()
max_Y = np.nan
min_Y = np.inf
for idx in self.unlabeled:
Y_metric[idx] = np.fabs(self.ATmodel.predict(self.X_pool[idx].reshape(1, -1))[0] - self.y_pool[idx])
if Y_metric[idx] > max_Y:
max_Y = Y_metric[idx]
elif Y_metric[idx] < min_Y:
min_Y = Y_metric[idx]
# for idx in self.unlabeled:
# Y_metric[idx] = np.float(Y_metric[idx] - min_Y) / np.float(max_Y - min_Y)
# --------------------------------------------- 计算总体多样性与信息性的总体 --------------------------------
result = OrderedDict()
for idx in self.unlabeled:
result[idx] = Y_metric[idx] * X_max_metric[idx]
# 一次性筛选的样本的个数
for i in range(number[count]):
tar_idx = max(result, key=result.get) # 选择最短距离最大的样本
# 将选出的样本进行标记
self.labeled.append(tar_idx)
self.unlabeled.remove(tar_idx)
del result[tar_idx]
self.budgetLeft -= 1
if self.budgetLeft == 0:
break
if self.budgetLeft == 0:
break
count += 1
def select_n_components(self, X, target_explained_variance_ratio):
"""
根据目标解释方差比例,选择合适的主成分数
参数:
X: ndarray, 待降维的数据矩阵,每行代表一个样本,每列代表一个特征
target_explained_variance_ratio: float, 目标解释方差比例
返回:
n_components: int, 选择的主成分数
"""
pca = PCA()
pca.fit(X)
# 计算累计贡献率
explained_variance_ratio = pca.explained_variance_ratio_
cumsum_explained_variance_ratio = np.cumsum(explained_variance_ratio)
# 找到第一个累计贡献率大于等于目标解释方差比例的位置,返回主成分数
n_components = np.argmax(cumsum_explained_variance_ratio >= target_explained_variance_ratio) + 1
return n_components, PCA(n_components=n_components).fit_transform(X)
def cluster(self):
import numpy as np
# 生成样本数据
# X = self.X_pool
# self.labeled = self.unlabeled
X = self.X[self.labeled]
print('原始数据维度:', np.shape(X))
# 标准化处理
X = StandardScaler().fit_transform(X)
n_components, X = self.select_n_components(X, 0.8)
print('降维后数据维度:', np.shape(X))
# 设置eps和min_samples的取值范围
eps_range = np.linspace(0.01, 1, 100)
min_samples_range = range(2, 11)
# 存储DB指数的结果
scores = np.zeros((len(eps_range), len(min_samples_range)))
# 进行调参实验
for i, eps in enumerate(eps_range):
for j, min_samples in enumerate(min_samples_range):
dbscan = DBSCAN(eps=eps, min_samples=min_samples)
labels = dbscan.fit_predict(X)
if len(set(labels)) > 1:
score = davies_bouldin_score(X, labels)
scores[i, j] = score
# 找到最优的eps和min_samples
i, j = np.unravel_index(np.argmin(scores), scores.shape)
best_eps = eps_range[i]
best_min_samples = min_samples_range[j]
print('最佳的参数:', best_eps, best_min_samples)
# 使用DBSCAN聚类算法
dbscan = DBSCAN(eps=best_eps, min_samples=best_min_samples)
labels = dbscan.fit_predict(X)
# 计算每个簇的大小
cluster_sizes = np.bincount(labels + 1)
print('簇的大小', cluster_sizes)
# 找到噪声簇的编号
noise_cluster = np.where(cluster_sizes < 5)[0]
print('噪声数据:', noise_cluster)
# 去除噪声数据
if len(noise_cluster) != 0:
X = X[labels != noise_cluster[0]]
for noise in noise_cluster:
self.labeled.remove(noise)
print('去噪后数据维度', np.shape(self.X_pool[self.labeled]))
if __name__ == '__main__':
rng = check_random_state(0) # 设计随机数种子,利用 rng.uniform 产生可复现的数据
X_train = rng.uniform(-1, 1, 1000).reshape(500, 2)
y_train = X_train[:, 0] ** 2 - X_train[:, 1] ** 2 + X_train[:, 1] - 1
budget = 300
model = XGBRegressor(max_depth=6,
learning_rate=0.1,
n_estimators=100,
booster='gbtree',)
# budget 需要标记的样本个数
model = IOCS(X_pool=X_train, y_pool=y_train, budget=budget, X_test=0, y_test=0, Model=model)
model.select()