【scikit-multiflow】使用 scikit-multiflow 的流数据生成器生成概念漂移数据流

说在前面

scikit-multiflow 是一个专注于多流学习(multi-stream learning)的Python库,它为数据流挖掘和在线学习提供了丰富的工具集。这个库的设计灵感来源于著名的scikit-learn,旨在为研究人员和从业者提供一个易于使用且功能强大的框架来处理不断变化的数据流。

下面将记录如何使用 scikit-multiflow 0.5.3 生成不同类型的概念漂移数据流。

概念漂移是什么?

概念漂移是指目标域的数据分布随着时间的推移以任意方式发生变化的现象。例如,在过去二十年中,移动电话通话的主导类别从音频变为摄像头到移动互联网。更正式地说,概念漂移可以表示为为一个不断演化的分布 D ( X , y ) D(X,y) D(X,y),其中 X X X 是特征向量集, y y y 是标签。一般认为概念漂移有四种类型:

  • Sudden drift: 新的概念在短时间内出现。
  • Gradual drift:随着时间的推移,新概念逐渐取代旧概念。
  • Incremental drift:旧概念随着时间的流逝逐渐变成新概念。
  • Recurrent drift:旧概念在消失一段时间后再次出现。

在这里插入图片描述

scikit-multiflow 环境配置

1.创建一个 python 版本为 3.8 的环境。

conda create -n datagenerator python=3.8
conda activate datagenerator

2.在该环境中按序安装下面的包。

pip install numpy==1.21.5
pip install scipy==1.10.1
pip install scikit-learn==1.0.2
pip install pandas==2.0.0
pip install scikit-multiflow==0.5.3

数据流生成器的介绍

  • AGRAWALGenerator
  • SEAGenerator
  • LEDGenerator
  • HyperplaneGenerator

AGRAWALGenerator

数据流生成器 AGRAWALGenerator 生成的数据流包含9个特征,其中为六个数值属性和三个类别属性(six numeric and three categorical)。

feature namefeature descriptionvaluesclass
salary薪水均匀分布在 20k 到 150k 之间numeric
commission佣金如果薪水小于 75 k, 则为0;否则,在 10k 至 75k 之间均匀分布numeric
age年龄均匀分布在 20 到 80 之间numeric
elevel教育水平从0到4之间均匀选择,表示不同的教育等级,如 高中、学士等categorical
car汽车制造商从1到20之间均匀选择。用数字代表不同的汽车品牌categorical
zipcode邮政编码从0到8之间均匀选择。这里的邮政编码是一个简化表示,用于模拟不同地区的经济差异categorical
hvalue房屋价值均匀分布在 50k 乘以邮政编码至 100k 乘以邮政编码之间numeric
hyears拥有房屋的年数均匀分布在1到30之间numeric
loan总贷款金额均匀分布在0到500k之间numeric

AGRAWALGenerator 除了能够生成数据以外,还有十个分类函数(编号从0到9)为这些特征值分配类别标签。下面是这十个函数的示意。

if function_id == 0:
        return 0 if salary < 65000 else 1
    elif function_id == 1:
        return 0 if commission > salary else 1
    elif function_id == 2:
        return 0 if age >= 40 else 1
    elif function_id == 3:
        return 0 if elevel in [0, 1] else 1
    elif function_id == 4:
        return 0 if zipcode in [0, 1, 2] else 1
    elif function_id == 5:
        return 0 if hvalue < 150000 else 1
    elif function_id == 6:
        return 0 if loan > 250000 else 1
    elif function_id == 7:
        return 0 if age < 25 or elevel == 4 else 1
    elif function_id == 8:
        return 0 if car in [1, 2, 3] else 1
    elif function_id == 9:
        return 0 if hyears < 10 else 1

AGRAWALGenerator 的参数说明

  • classification_function(Int, default=0)
    • 指定使用的分类函数编号(从 0 到 9)。每个分类函数定义了一种不同的规则来根据输入特征生成二元类别标签。
  • random_state (int, RandomState instance or None, default=None)
    • 控制伪随机数生成器的种子。如果设置为整数,则保证了实验结果的可重复性。若为 None,则使用 numpy 全局随机状态。
  • balance_classes(bool, default=False)
    • 如果设置为 True,将尝试平衡每个类别的样本数量,使得每个类别的出现频率大致相同。默认情况下是不平衡的。
  • perturbation (float, default=0.0)
    • 表示添加到特征值上的扰动比例。增加这个值可以在一定程度上模拟噪声或不确定性。取值范围通常在 [0.0, 1.0] 之间。
from skmultiflow.data import AGRAWALGenerator

# 创建 AGRAWALGenerator 实例
generator = AGRAWALGenerator(
    classification_function=5,  # 使用第6个分类函数
    random_state=42,            # 设置随机种子以确保结果可重复
    balance_classes=True,       # 平衡类别分布
    perturbation=0.1            # 添加10%的扰动
)

# 获取单个样本
X, y = generator.next_sample()
print("Single sample:")
print("Features (X):", X)
print("Label (y):", y)

# 获取多个样本
X_batch, y_batch = generator.next_sample(5)  # 获取5个样本
print("\nBatch of samples:")
for i in range(5):
    print(f"Sample {i+1}:")
    print("Features:", X_batch[i])
    print("Label:", y_batch[i])

SEAGenerator

数据流生成器 SEAGenerator,生成的数据流包含三个数值型特征,和一个由分类函数确定的类别标签。该生成器常用于突然漂移的概念漂移数据流的生成实现,当然它默认情况下生成的数据流并不具备概念漂移。

三个数值属性(att1, att2. att3),其中只有前两个属性对分类任务是相关的。三个属性的取值范围都在 0 到 10 之间随机生成。

SEAGenerator 有四个标签分类函数。通过选择这四个分类函数来确定样本的类别标签。另外,SEAGenerator 生成的数据流的三个数值属性中,只有前两个与分类任务相关。下面是对这四个分类函数的示意。

def classify_function_0(att1, att2):
    return 0 if (att1 + att2) <= 8 else 1

def classify_function_1(att1, att2):
    return 0 if (att1 + att2) <= 9 else 1

def classify_function_2(att1, att2):
    return 0 if (att1 + att2) <= 7 else 1

def classify_function_3(att1, att2):
    return 0 if (att1 + att2) <= 9.5 else 1

SEAGenerator 的参数说明

  • classification_function (int, default=0)
    • 指定使用的分类函数编号(从 0 到 2)
  • random_state (int, RandomState instance or None, default=None)
    • 控制伪随机数生成器的种子。如果设置为整数,则保证了实验结果的可重复性。若为 None,则使用 numpy 全局随机状态。
  • noise_percentage (float, default=0.1)
    • 表示添加到输出标签上的噪声比例。取值范围通常在 [0.0, 1.0] 之间。较高的值意味着更多的噪声,即类别标签更可能被随机翻转。
from skmultiflow.data.sea_generator import SEAGenerator
# Setting up the stream
stream = SEAGenerator(classification_function = 2, random_state = 112,balance_classes = False, noise_percentage = 0.28)
# Retrieving one sample
stream.next_sample()
# Retrieving 10 samples
stream.next_sample(10)
# Generators will have infinite remaining instances, so it returns -1
stream.n_remaining_samples()

LEDGenerator

LEDGenerator 生成的数据流包含 24 个二进制属性和一个多分类标签,标签的取值范围是 0 到 9,对应显示器上显示的数字。LEDGenerator 的标签是根据预定义的 7 段显示器(LED 显示器)显示的数字生成的。

LEDGenerator 参数说明

  • random_state:用于随机数生成的种子,以确保结果的可重复性。
  • noise_percentage:添加到数据中的噪声百分比。默认值为 0.0。
  • has_noise:是否在生成的数据中添加噪声。默认值为 True。
from skmultiflow.data import LEDGenerator

# 创建一个 LEDGenerator 实例
stream = LEDGenerator(random_state=42, noise_percentage=0.1, has_noise=True)

# 获取一个样本
X, y = stream.next_sample()
print("Features:", X, "Label:", y)

HyperplaneGenerator

可以定制生成的概念漂移数据流的维度

HyperplaneGenerator 通过定义一个在多维空间内移动的超平面来生成数据。每个新到达的数据点都根据其相对于当前超平面的位置被分配到某个类别。随着时间推移,超平面会逐渐改变位置或方向,这种变化模拟了现实世界中的概念漂移现象。由于超平面的变化是渐进式的,因此它可以产生平滑且连续的概念漂移,这对评估自适应学习算法尤其有价值。

其包含多个数值特征(自己设定)和一个二元标签分类函数,标签的生成基于特征值与超平面的关系:如果特征值与超平面的点积大于等于 0,则标签为 1;否则标签为 0。

HyperplaneGenerator 参数说明

  • random_state:用于随机数生成的种子,以确保结果的可重复性。(default=None)
  • n_features:特征的数量。(default=10)
  • n_drift_features:发生漂移的特征数量。(default=2)
  • mag_change:漂移的幅度。(default=0.0)(0.0到1.0)
  • noise_percentage:添加到数据中的噪声百分比。(0.05)(0.0到1.0)
  • sigma_percentage:添加到数据中的高斯噪声的标准差百分比。(default=0.1)(0.0到1.0)
from skmultiflow.data import HyperplaneGenerator

# 创建一个 HyperplaneGenerator 实例
stream = HyperplaneGenerator(random_state=42, n_features=10, n_drift_features=2, mag_change=0.0, noise_percentage=0.05, sigma_percentage=0.1)

# 获取一个样本
X, y = stream.next_sample()
print("Features:", X, "Label:", y)

如何使用 scikit-multiflow 中的流数据生成器

官方文档介绍

我们此处将使用 AGRAWALGenerator 来示范如何在 scikit-multiflow 中使用流生成器。

1.初始化流数据生成器。

generator = AGRAWALGenerator()

2.生成数据流样本。

使用 .next_sample() 方法返回数据流样本。数据流将使用两个数据返回,分别是 特征数组 和 类别数组(分类)或 目标数组(回归)。

X, y = generator.next_sample()
print(X.shape, y.shape)

在这里插入图片描述

默认情况下只会返回一个样本,但是我们可以通过参数的设定来返回不同数量的样本。

X, y = generator.next_sample(10)
print(X.shape, y.shape)

在这里插入图片描述

3.检查是否还有更多的数据。

使用 .has_more_samples() 方法来判断是否还有剩余的数据。在处理数据流时,了解是否有更多剩余数据非常重要。

generator.has_more_samples()

在这里插入图片描述

4.重置生成器。

.restart() 方法用于将生成器重置到初始状态。这意味着所有内部状态(例如,当前的位置或指针在数据流中的位置)都会被重置,从而允许你从头开始重新生成数据样本。

generator.restart()

5.将生成的数据流保存到csv文件中。

import pandas

df = pd.DataFrame(np.hstack((X,np.array([y]).T)))
df.to_csv("file.csv")

生成概念漂移数据流

生成概念漂移数据流我们需要用到概念漂移生成器 skmultiflow.data.ConceptDriftStream。ConceptDriftStream 的主要作用是通过合并两个不同的数据流来模拟概念漂移现象:

  • 基础数据流(stream):概念漂移发生前的数据分布。
  • 漂移数据流(drift_stream):概念漂移发生后的数据分布。

通过指定概念漂移发生的精确位置(position)和过渡宽度(width),可以控制从一个数据分布到另一个数据分布的变化过程。

简单来说,就是在基础数据流(stream)中,选择特定的位置替换成漂移数据流(drift_stream),这样就能实现数据分布的变化,从而实现概念漂移。并且,通过对漂移宽度和位置的设定,我们可以实现不同类型的概念漂移。

参数说明

  • stream: 漂移前的基础数据流。
  • drift_stream: 漂移后的新数据流。
  • position: 数据流中概念漂移开始的位置。
  • width: 从基础数据流转移到新数据流的平滑程度或过渡宽度。如果宽度为1,则表示瞬时完成的概念漂移;如果宽度较大,则表示逐渐过渡的过程。
  • random_state: 控制随机数生成器的种子,以确保结果可重复。

这是一个生成概念漂移数据流的例子。

from skmultiflow.data import AGRAWALGenerator, ConceptDriftStream

# 创建主数据流和漂移数据流
stream = AGRAWALGenerator(classification_function=0, random_state=42, balance_classes=False, perturbation=0.0)
drift_stream = AGRAWALGenerator(classification_function=1, random_state=42, balance_classes=False, perturbation=0.0)

# 创建一个 ConceptDriftStream 实例,引入概念漂移
concept_drift_stream = ConceptDriftStream(stream=stream, drift_stream=drift_stream, position=5000, width=1000)

# 获取多个样本
for _ in range(10):
    X, y = concept_drift_stream.next_sample()
    print("Features:", X, "Label:", y)

下面将详细讲解如何生成各种类型的概念漂移数据流。

生成各种类型的概念漂移数据流

Sudden Drift

在生成 Sudden Drift/ Abrupt Drift (突然漂移) 数据流时,我们常用到的数据流生成器为 AGRAWALGenerator 和 SEAGenerator。这两个生成器的特点是能生成具有不同分类函数的合成数据流,我们可以通过突然改变分类函数来模拟数据分布的突然变化。

为模拟突然漂移数据流,我们需要设置漂移宽度 width = 1 以模拟突然出现的概念漂移。下面是分别使用这两个数据流生成器模拟突然漂移数据流的示例。

AGRAWALGenerator

  • 简单性: 代码相对简单,使用单一的 random_state 参数来控制数据流的生成。
  • 灵活性: 由于只使用一个 random_state 参数,灵活性较低,无法独立控制初始数据流和漂移数据流的随机性。
  • 噪声控制: AGRAWALGenerator 不提供直接的噪声控制参数,无法模拟带有噪声的数据流。
# 使用 AGRAWALGenerator 生成突然漂移数据流
def generateAbruptDriftStream_AGRAWALG(max_samples, first_func, second_func, random_state, drift_pos, window_len):
    stream = skmultiflow.data.ConceptDriftStream(
        stream=AGRAWALGenerator(classification_function=first_func, random_state=random_state),
        drift_stream=AGRAWALGenerator(classification_function=second_func, random_state=random_state),
        position=drift_pos, width=1, random_state=None, alpha=0.0
    )

SEAGenerator

  • 灵活性: 使用 first_random_statesecond_random_state 参数分别控制初始数据流和漂移数据流的随机性,提供更高的灵活性。
  • 噪声控制: SEAGenerator 提供 noise_percentage 参数,可以生成带有噪声的数据流,适合模拟更复杂的现实场景。
  • 多样性: SEAGenerator 可以生成不同分类函数和噪声水平的合成数据流,适合模拟多种类型的概念漂移。
# 使用 SEAGenerator 生成突然漂移数据流
def generateAbruptDriftStream_SEAG(max_samples, first_func, second_func, first_random_state,second_random_state,all_random_state, drift_pos, window_len):
    stream = skmultiflow.data.ConceptDriftStream(
        stream=SEAGenerator(classification_function=first_func, random_state=first_random_state,noise_percentage=0.28),
        drift_stream=SEAGenerator(classification_function=second_func, random_state=second_random_state,noise_percentage=0.28), 
        position=drift_pos,width=1, random_state=all_random_state, alpha=0.0)

总结

  • 使用 AGRAWALGenerator 的函数 适合简单的突变漂移场景,代码简单,适用性强,但灵活性和噪声控制较弱。
  • 使用 SEAGenerator 的函数 适合更复杂的概念漂移场景,提供更高的灵活性和噪声控制,但代码复杂度和计算开销较高。

Gradual Drift

渐变漂移是指概念在数据流中逐渐发生变化,有一个过渡期。所以漂移宽度通常设置为较大的值(如 1000),表示漂移在多个样本内完成。在生成渐变漂移数据流时,我们通常使用 SEAGenerator 通过使用不同的分类函数来生成渐变漂移数据流,当然 AGRAWALGenerator 也可以,二者的区别在突然漂移处已做说明。

# 使用 AGRAWALGenerator 生成渐变漂移数据流
def generateGradualDriftStream_AGRAWAL(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len):

    stream = skmultiflow.data.ConceptDriftStream(
        stream=AGRAWALGenerator(classification_function=first_func, random_state=first_random_state, balance_classes=False),
        drift_stream=AGRAWALGenerator(classification_function=second_func, random_state=second_random_state, balance_classes=False),
        position=drift_pos, width=1000, random_state=all_random_state, alpha=0.0
    )

# 使用 SEAGenerator 生成渐变漂移数据流
def generateGradualDriftStream_SEA(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len):

    stream = skmultiflow.data.ConceptDriftStream(
        stream=SEAGenerator(classification_function=first_func, random_state=first_random_state, balance_classes=False, noise_percentage=0.28),
        drift_stream=SEAGenerator(classification_function=second_func, random_state=second_random_state, balance_classes=False, noise_percentage=0.28),
        position=drift_pos, width=1000, random_state=all_random_state, alpha=0.0
    )

Incremental Drift

增量漂移(Incremental Drift)是指概念在数据流中逐步发生变化,变化是连续的,上面的渐变漂移的变化是逐渐的,这是两者的区别。生成增量漂移数据流,我们使用的是 HyperplaneGenerator ,因为 HyperplaneGenerator 可以通过改变特征和漂移特征来模拟逐渐变化的概念,这种变化在数据流中是逐步发生的,区别于AGRAWALGenerator 和 SEAGenerator 通过改变分类函数来模拟概念漂移 。另外,为表示逐步变化,我们需要将漂移宽度设置的比较长(如1000).

def generateIncrementalDriftStream(max_samples, random_state, first_mag_change, second_mag_change,
                                   first_sig, sec_sig, drift_pos, window_len):

	stream = skmultiflow.data.ConceptDriftStream(
    	stream=HyperplaneGenerator(random_state=random_state, n_features=10, n_drift_features=10, mag_change=first_mag_change, noise_percentage=0.05, sigma_percentage=first_sig),
    	drift_stream=HyperplaneGenerator(random_state=random_state, n_features=10, n_drift_features=10, mag_change=second_mag_change, noise_percentage=0.05, sigma_percentage=sec_sig),
    	position=drift_pos, width=1000, random_state=None, alpha=0.0
	)

Recurrent Drift

重复漂移是指概念在数据流中多次出现和消失。在生成重复漂移数据流时,我们依然是先创建初始数据流和漂移数据流。然后在指定的位置插入漂移数据流。

def generateRecurringDriftStream(max_samples, first_func, second_func, first_random_state, second_random_state, drift_positions, window_len):

    # 创建初始数据流
    stream = SEAGenerator(classification_function=first_func, random_state=first_random_state, balance_classes=False, noise_percentage=0.28)
    drift_stream = SEAGenerator(classification_function=second_func, random_state=second_random_state, balance_classes=False, noise_percentage=0.28)

    # 创建 ConceptDriftStream 并在指定位置插入漂移
    for drift_pos in drift_positions:
        stream = ConceptDriftStream(stream=stream, drift_stream=drift_stream, position=drift_pos, width=1, random_state=None, alpha=0.0)

# Recurring Drift
drift_positions = [1000, 2000, 3000, 4000]  # 指定漂移位置
for i in pair:
	first_func = i[0]
    second_func = i[1]
    for first_random_state in range(100, 105):
    	for second_random_state in range(100, 102):
        	generateRecurringDriftStream(max_samples, first_func, second_func, first_random_state, second_random_state, drift_positions, window_len)

Normal Stream

下面提供一个生成没有概念漂移的正常数据流的示例,可以选择不同的生成器类型生成各自的正常数据流。

注: 其中生成器 LEDGenerator 往往只用于生成正常的没有概念漂移的数据流。

def generateNormalStream(max_samples, GeneratorType, random_state, window_len):
    if GeneratorType == 0:
        stream = AGRAWALGenerator(classification_function=0, random_state=random_state, balance_classes=False, perturbation=0.0)
        file_name_prefix = "AGRAWALGenerator"
    elif GeneratorType == 1:
        stream = HyperplaneGenerator(random_state=random_state, n_features=10, n_drift_features=2, mag_change=0.0, noise_percentage=0.05, sigma_percentage=0.1)
        file_name_prefix = "HyperplaneGenerator"
    elif GeneratorType == 2:
        stream = SEAGenerator(classification_function=0, random_state=random_state, balance_classes=False, noise_percentage=0.0)
        file_name_prefix = "SEAGenerator"
    elif GeneratorType == 3:
        stream = LEDGenerator(random_state=random_state, noise_percentage=0.0, has_noise=False)
        file_name_prefix = "LEDGenerator"
    else:
        raise ValueError("Unsupported GeneratorType")

计算概念漂移数据流的错误率

我们对漂移检测方法相关文献的调查表明,一般来说,漂移检测方法可以基于两个指标之一,即误差率或数据分布。所以,数据流的错误率是一个重要指标,下面将讲解如何在生成概念漂移数据流时同时计算对应的错误率。

注: 此处计算的错误率是平均窗口错误率。

此处我们使用一个本地贝叶斯 (Naive Bayesian) ,直接应用于数据流,以确定每个时间戳 t 的错误率 e t e_t et 。有一个长度为 n 的窗口, 有 n 个样本在这个窗口中,所以这个窗口的平均错误率 e ^ t \hat{e}_t e^t:
e ^ j = ∑ t = 1 n e t n ( 1 ) \hat{e}_j ={{\sum_{t=1}^ne_t}\over n}\qquad(1) e^j=nt=1net(1)
假设在每个数据流中都有 m m m 个窗口。两个窗口的平均错误率的差异为:
g a p j = e ^ j + 1 − e ^ j , j ∈ [ 1 , m − 1 ] ( 2 ) gap_j=\hat{e}_{j+1}-\hat{e}_j,j\in[1,m-1]\qquad (2) gapj=e^j+1e^j,j[1,m1](2)
训练样本可以表示为 ( G i , v i ) ∈ R m (G_i,v_i)\in R^m (Gi,vi)Rm ,其中 G i = ( g a p j , j ∈ [ 1 , m − 1 ] ) G_i = (gap_j,j\in [1,m-1]) Gi=(gapj,j[1,m1]) 是错误特征(error features) , v i v_i vi 是漂移类型。所以,数据流的基本数据分布 D ( X , y ) D(X,y) D(X,y) 映射为 ( G i , v i ) (G_i,v_i) (Gi,vi)

下面是一个示例,设定的窗口长度为 50, 样本总量为 4800。最后得到的是一个记录了错误率和漂移点位置的csv文件。(示例不包含重复漂移)

import random
import skmultiflow
from pandas import DataFrame
from skmultiflow.bayes import NaiveBayes
from skmultiflow.data import AGRAWALGenerator, LEDGenerator, SEAGenerator, HyperplaneGenerator

def generateAbruptDriftStream(max_samples, first_func, second_func, random_state, drift_pos, window_len):
    """
    生成突变漂移数据流并进行增量训练和评估。

    参数:
    max_samples (int): 最大样本数
    first_func (int): 初始数据流的分类函数
    second_func (int): 漂移数据流的分类函数
    random_state (int): 随机状态
    drift_pos (int): 漂移开始的位置
    window_len (int): 每个窗口的样本数
    """
    resultList = []

    # 创建 ConceptDriftStream,用于生成突变漂移数据流
    stream = skmultiflow.data.ConceptDriftStream(
        stream=AGRAWALGenerator(balance_classes=False, classification_function=first_func, perturbation=0.0, random_state=random_state),
        drift_stream=AGRAWALGenerator(balance_classes=False, classification_function=second_func, perturbation=0.0, random_state=random_state),
        position=drift_pos, width=1, random_state=None, alpha=0.0
    )

    naive_bayes = NaiveBayes()
    n_samples = 0

    while n_samples < max_samples and stream.has_more_samples():
        iter_max_samples = max_samples / window_len
        iter_n_samples = 0
        correct_cnt = 0

        while iter_n_samples < iter_max_samples and stream.has_more_samples():
            X, y = stream.next_sample()
            y_pred = naive_bayes.predict(X)
            if y[0] == y_pred[0]:
                correct_cnt += 1
            naive_bayes.partial_fit(X, y)
            iter_n_samples += 1
            n_samples += 1

        # 记录漂移位置
        if n_samples == drift_pos + 1:
            resultList.append([correct_cnt / iter_n_samples, 1])
        else:
            resultList.append([correct_cnt / iter_n_samples, 0])

    file_name = "./Data/drift-50-1-4800/abrupt/AGRAWALGenerator_" + str(first_func) + "_" + str(second_func) + "_" + str(random_state) + "_" + str(drift_pos) + ".csv"
    DataFrame(resultList).to_csv(file_name)
    print("abrupt processing has been completed:", file_name)

def generateAbruptDriftStream_plus(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len):
    """
    生成突变漂移数据流并进行增量训练和评估(使用 SEAGenerator)。

    参数:
    max_samples (int): 最大样本数
    first_func (int): 初始数据流的分类函数
    second_func (int): 漂移数据流的分类函数
    first_random_state (int): 初始数据流的随机状态
    second_random_state (int): 漂移数据流的随机状态
    all_random_state (int): 总随机状态
    drift_pos (int): 漂移开始的位置
    window_len (int): 每个窗口的样本数
    """
    resultList = []

    # 创建 ConceptDriftStream,用于生成突变漂移数据流
    stream = skmultiflow.data.ConceptDriftStream(
        stream=SEAGenerator(classification_function=first_func, random_state=first_random_state, balance_classes=False, noise_percentage=0.28),
        drift_stream=SEAGenerator(classification_function=second_func, random_state=second_random_state, balance_classes=False, noise_percentage=0.28),
        position=drift_pos, width=1, random_state=all_random_state, alpha=0.0
    )

    naive_bayes = NaiveBayes()
    n_samples = 0

    while n_samples < max_samples and stream.has_more_samples():
        iter_max_samples = max_samples / window_len
        iter_n_samples = 0
        correct_cnt = 0

        while iter_n_samples < iter_max_samples and stream.has_more_samples():
            X, y = stream.next_sample()
            y_pred = naive_bayes.predict(X)
            if y[0] == y_pred[0]:
                correct_cnt += 1
            naive_bayes.partial_fit(X, y)
            iter_n_samples += 1
            n_samples += 1

        # 记录漂移位置
        if n_samples == drift_pos + 1:
            resultList.append([correct_cnt / iter_n_samples, 1])
        else:
            resultList.append([correct_cnt / iter_n_samples, 0])

    file_name = "./Data/drift-50-1-4800/abrupt/SEAGenerator_" + str(first_func) + "_" + str(second_func) + "_" + str(first_random_state) + "_" + str(second_random_state) + "_" + str(all_random_state) + "_" + str(drift_pos) + ".csv"
    DataFrame(resultList).to_csv(file_name)
    print("abrupt processing has been completed:", file_name)

def generateGradualDriftStream(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len):
    """
    生成渐变漂移数据流并进行增量训练和评估。

    参数:
    max_samples (int): 最大样本数
    first_func (int): 初始数据流的分类函数
    second_func (int): 漂移数据流的分类函数
    first_random_state (int): 初始数据流的随机状态
    second_random_state (int): 漂移数据流的随机状态
    all_random_state (int): 总随机状态
    drift_pos (int): 漂移开始的位置
    window_len (int): 每个窗口的样本数
    """
    resultList = []

    # 创建 ConceptDriftStream,用于生成渐变漂移数据流
    stream = skmultiflow.data.ConceptDriftStream(
        stream=SEAGenerator(classification_function=first_func, random_state=first_random_state, balance_classes=False, noise_percentage=0.28),
        drift_stream=SEAGenerator(classification_function=second_func, random_state=second_random_state, balance_classes=False, noise_percentage=0.28),
        position=drift_pos, width=1000, random_state=all_random_state, alpha=0.0
    )

    naive_bayes = NaiveBayes()
    n_samples = 0

    while n_samples < max_samples and stream.has_more_samples():
        iter_max_samples = max_samples / window_len
        iter_n_samples = 0
        correct_cnt = 0

        while iter_n_samples < iter_max_samples and stream.has_more_samples():
            X, y = stream.next_sample()
            y_pred = naive_bayes.predict(X)
            if y[0] == y_pred[0]:
                correct_cnt += 1
            naive_bayes.partial_fit(X, y)
            iter_n_samples += 1
            n_samples += 1

        # 记录漂移位置
        if n_samples == drift_pos + 1:
            resultList.append([correct_cnt / iter_n_samples, 1])
        else:
            resultList.append([correct_cnt / iter_n_samples, 0])

    file_name = "./Data/drift-50-1-4800/gradual/SEAGenerator_" + str(first_func) + "_" + str(second_func) + "_" + str(first_random_state) + "_" + str(second_random_state) + "_" + str(all_random_state) + "_" + str(drift_pos) + ".csv"
    DataFrame(resultList).to_csv(file_name)
    print("gradual processing has been completed:", file_name)

def generateIncrementalDriftStream(max_samples, random_state, first_mag_change, second_mag_change, first_sig, sec_sig, drift_pos, window_len):
    """
    生成增量漂移数据流并进行增量训练和评估。

    参数:
    max_samples (int): 最大样本数
    random_state (int): 随机状态
    first_mag_change (float): 初始数据流的漂移幅度
    second_mag_change (float): 漂移数据流的漂移幅度
    first_sig (float): 初始数据流的漂移标准差
    sec_sig (float): 漂移数据流的漂移标准差
    drift_pos (int): 漂移开始的位置
    window_len (int): 每个窗口的样本数
    """
    resultList = []

    # 创建 ConceptDriftStream,用于生成增量漂移数据流
    stream = skmultiflow.data.ConceptDriftStream(
        stream=HyperplaneGenerator(random_state=random_state, n_features=10, n_drift_features=10, mag_change=first_mag_change, noise_percentage=0.05, sigma_percentage=first_sig),
        drift_stream=HyperplaneGenerator(random_state=random_state, n_features=10, n_drift_features=10, mag_change=second_mag_change, noise_percentage=0.05, sigma_percentage=sec_sig),
        position=drift_pos, width=1000, random_state=None, alpha=0.0
    )

    naive_bayes = NaiveBayes()
    n_samples = 0

    while n_samples < max_samples and stream.has_more_samples():
        iter_max_samples = max_samples / window_len
        iter_n_samples = 0
        correct_cnt = 0

        while iter_n_samples < iter_max_samples and stream.has_more_samples():
            X, y = stream.next_sample()
            y_pred = naive_bayes.predict(X)
            if y[0] == y_pred[0]:
                correct_cnt += 1
            naive_bayes.partial_fit(X, y)
            iter_n_samples += 1
            n_samples += 1

        # 记录漂移位置
        if n_samples == drift_pos + 1:
            resultList.append([correct_cnt / iter_n_samples, 1])
        else:
            resultList.append([correct_cnt / iter_n_samples, 0])

    file_name = "./Data/drift-50-1-4800/incremental/HyperplaneGenerator_" + str(random_state) + "_" + str(first_mag_change) + "_" + str(second_mag_change) + "_" + str(first_sig) + "_" + str(sec_sig) + "_" + str(drift_pos) + ".csv"
    DataFrame(resultList).to_csv(file_name)
    print("incremental processing has been completed:", file_name)

def generateNormalStream(max_samples, GeneratorType, random_state, window_len):
    """
    生成正常数据流并进行增量训练和评估。

    参数:
    max_samples (int): 最大样本数
    GeneratorType (int): 生成器类型(0: AGRAWALGenerator, 1: HyperplaneGenerator, 2: SEAGenerator, 3: LEDGenerator)
    random_state (int): 随机状态
    window_len (int): 每个窗口的样本数
    """
    resultList = []

    if GeneratorType == 0:
        stream = AGRAWALGenerator(classification_function=0, random_state=random_state, balance_classes=False, perturbation=0.0)
        file_name_prefix = "AGRAWALGenerator"
    elif GeneratorType == 1:
        stream = HyperplaneGenerator(random_state=random_state, n_features=10, n_drift_features=2, mag_change=0.0, noise_percentage=0.05, sigma_percentage=0.1)
        file_name_prefix = "HyperplaneGenerator"
    elif GeneratorType == 2:
        stream = SEAGenerator(classification_function=0, random_state=random_state, balance_classes=False, noise_percentage=0.0)
        file_name_prefix = "SEAGenerator"
    elif GeneratorType == 3:
        stream = LEDGenerator(random_state=random_state, noise_percentage=0.0, has_noise=False)
        file_name_prefix = "LEDGenerator"
    else:
        raise ValueError("Unsupported GeneratorType")

    naive_bayes = NaiveBayes()
    n_samples = 0

    while n_samples < max_samples and stream.has_more_samples():
        iter_max_samples = max_samples / window_len
        iter_n_samples = 0
        correct_cnt = 0

        while iter_n_samples < iter_max_samples and stream.has_more_samples():
            X, y = stream.next_sample()
            y_pred = naive_bayes.predict(X)
            if y[0] == y_pred[0]:
                correct_cnt += 1
            naive_bayes.partial_fit(X, y)
            iter_n_samples += 1
            n_samples += 1

        resultList.append(correct_cnt / iter_n_samples)
        file_name = f"./Data/drift-50-1-4800/normal/{file_name_prefix}{random_state}.csv"
        DataFrame(resultList).to_csv(file_name)
        print(f"Normal processing has been completed: {file_name}")

if __name__ == "__main__":
    window_len = 51
    max_samples = 51

    # 生成漂移位置列表,范围为[5, 95]
    position = []
    for r in range(50):
        if r % 1 == 0:
            if r >= 2 and r < 50:
                position.append(r)
    print(len(position))

    # 生成突变漂移数据流 (10*9*48)(5*2*48) 4800
    for i in range(10):
        first_func = i
        for j in range(10):
            second_func = j
            if first_func != second_func:
                for pos in range(len(position)):
                    random_state = 0
                    drift_pos = position[pos] * 1
                    generateAbruptDriftStream(max_samples, first_func, second_func, random_state, drift_pos, window_len)

    # 生成突变漂移数据流(使用 SEAGenerator)
    pair = [(2, 1), (2, 3), (1, 2), (3, 2), (0, 3)]  # (48*2*5)
    for i in pair:
        first_func = i[0]
        second_func = i[1]
        for first_random_state in range(100, 101):  # before(100, 110)
            for second_random_state in range(100, 102):  # before(100, 107)
                for random_state in range(len(position)):
                    drift_pos = position[random_state] * 1
                    all_random_state = None
                    generateAbruptDriftStream_plus(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len)

    # 生成渐变漂移数据流 (48*2*2*5*5)4800
    pair = [(2, 1), (2, 3), (1, 2), (3, 2), (0, 3)]
    for i in pair:
        first_func = i[0]
        second_func = i[1]
        for first_random_state in range(100, 105):  # (100, 112)
            for second_random_state in range(100, 102):  # (100, 112)
                for random_state in range(len(position)):
                    all_random_state = None
                    drift_pos = position[random_state] * 1
                    generateGradualDriftStream(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len)
                    all_random_state = 112
                    drift_pos = position[random_state] * 1
                    generateGradualDriftStream(max_samples, first_func, second_func, first_random_state, second_random_state, all_random_state, drift_pos, window_len)

    # 生成增量漂移数据流 (48*10*10) 4800
    for i in range(1):
        random_state = i
        for j in range(10):
            first_mag_change = j / 10
            first_sig = j / 50
            for k in range(10):
                second_mag_change = k / 10
                second_sig = k / 50
                for pos in range(len(position)):
                    drift_pos = position[pos] * 1
                    generateIncrementalDriftStream(max_samples, random_state, first_mag_change, second_mag_change, first_sig, second_sig, drift_pos, window_len)

    # 生成正常数据流(4*1200)4800
    for GeneratorType in range(0, 5):
        for random_state in range(1, 1201):  # before (0, 350)
            generateNormalStream(max_samples, GeneratorType, random_state, window_len)

利用本地的数据流文件生成概念漂移数据流

步骤1:加载现有数据流

使用 FileStream 或自定义方法加载您的数据文件。假设数据格式为 CSV:

from skmultiflow.data import FileStream

# 加载原始数据流
stream_original = FileStream("your_data.csv")

步骤 2:创建第二个数据流(漂移后数据)

您需要第二个数据流来模拟漂移后的新概念。这可以是:

  • 同一文件的不同子集(通过预处理分割)
  • 修改后的数据集(如标签翻转、特征扰动)
  • 完全不同的数据集
from skmultiflow.data import FileStream

# 假设后50%的数据经过处理代表新概念
stream_drift = FileStream("processed_drift_data.csv")

步骤 3:插入概念漂移

使用 ConceptDriftStream 合并两个流并定义漂移位置:

from skmultiflow.data import ConceptDriftStream

# 定义漂移位置(如第1000个样本后)和过渡宽度
drift_stream = ConceptDriftStream(
    stream=stream_original,
    drift_stream=stream_drift,
    position=1000,    # 漂移开始位置
    width=50          # 过渡平滑度(越小突变,越大渐变)
)

# 检查配置
print(drift_stream)

步骤 4:使用漂移数据流

逐样本或批量获取数据,用于模型训练/测试:

# 初始化模型(如HoeffdingTree)
from skmultiflow.trees import HoeffdingTree
model = HoeffdingTree()

# 逐步处理数据流
X, y = drift_stream.next_sample()
model.partial_fit(X, y)

# 或在循环中处理
while drift_stream.has_more_samples():
    X, y = drift_stream.next_sample()
    model.partial_fit(X, y)

Reference

1.skmultiflow.data.AGRAWALGenerator
2.skmultiflow.data.SEAGenerator
3.HyperplaneGenerator
4.LEDGenerator

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值