Machine Learning Mastery 深度学习时间序列教程(十二)
如何根据环境因素预测房间占用率
原文:
machinelearningmastery.com/how-to-predict-room-occupancy-based-on-environmental-factors/
诸如 Arduino 设备之类的小型计算机可以在建筑物内用于记录环境变量,从中可以预测简单和有用的属性。
一个示例是基于诸如温度,湿度和相关措施的环境措施来预测房间是否被占用。
这是一种称为房间占用分类的常见时间序列分类问题。
在本教程中,您将发现一个标准的多变量时间序列分类问题,用于使用环境变量的测量来预测房间占用率。
完成本教程后,您将了解:
- 机器学习中的占用检测标准时间序列分类问题。
- 如何加载和可视化多变量时间序列分类数据。
- 如何开发简单的幼稚和逻辑回归模型,以达到近乎完美的技能。
让我们开始吧。
- 更新 Oct / 2018 :更新了数据集来源的描述(我真的搞砸了),谢谢 Luis Candanedo。
教程概述
本教程分为四个部分;他们是:
- 占用检测问题描述
- 数据可视化
- 连接数据集
- 简单的预测模型
占用检测问题描述
标准时间序列分类数据集是 UCI 机器学习库中可用的“_ 占用检测 _”问题。
这是一个二分类问题,需要使用诸如温度和湿度的环境因素的观察来分类房间是否被占用或未被占用。
该数据集在 2016 年论文“使用统计学习模型”的 Luis M. Candanedo 和 VéroniqueFeldheim 的光,温度,湿度和 CO2 测量的办公室精确占用检测中进行了描述。
通过使用一套环境传感器监控办公室并使用摄像机确定房间是否被占用来收集数据集。
监测办公室房间的以下变量:温度,湿度,光照和二氧化碳水平。采用微控制器来获取数据。 ZigBee 无线电连接到它并用于将信息传输到记录站。数码相机用于确定房间是否被占用。相机时间每分钟标记图片,并且手动研究这些图片以标记数据。
- 使用统计学习模型,2016 年,通过光照,温度,湿度和 CO2 测量精确占用办公室。
数据提供日期时间信息和多天每分钟采取的六项环境措施,具体为:
- 温度以摄氏度为单位。
- 相对湿度百分比。
- 以勒克斯测量的光。
- 二氧化碳以百万分之一计。
- 湿度比,源自温度和相对湿度,以千克水蒸气/千克空气测量。
- 占用率为 1 表示占用,0 表示未占用。
该数据集已用于许多简单的建模机器学习论文中。例如,参见文章“基于可见光的占用推断使用集合学习,”2018 以获得进一步的参考。
数据可视化
这些数据以 CSV 格式提供三个文件,声称是用于训练,验证和测试的数据分割。
这三个文件如下:
- datatest.txt (测试):从 2015-02-02 14:19:00 到 2015-02-04 10:43:00
- datatraining.txt (训练):从 2015-02-04 17:51:00 到 2015-02-10 09:33:00
- datatest2.txt (val):从 2015-02-11 14:48:00 到 2015-02-18 09:19:00
最初显而易见的是,数据中的分割在时间上并不是连续的,并且存在差距。
测试数据集在训练和验证数据集之前及时。也许这是文件命名约定中的错误。我们还可以看到数据从 2 月 2 日延伸到 2 月 18 日,该日期跨越 17 个日历日,而不是 20 个日历日。
从这里下载文件并将它们放在当前的工作目录中:
每个文件都包含一个标题行,但包含一个行号的列,该列不包括标题行中的条目。
为了正确加载数据文件,请更新每个文件的标题行,如下所示:
从:
"date","Temperature","Humidity","Light","CO2","HumidityRatio","Occupancy"
至:
"no","date","Temperature","Humidity","Light","CO2","HumidityRatio","Occupancy"
下面是带有修改的datatraining.txt文件的前五行示例。
"no","date","Temperature","Humidity","Light","CO2","HumidityRatio","Occupancy"
"1","2015-02-04 17:51:00",23.18,27.272,426,721.25,0.00479298817650529,1
"2","2015-02-04 17:51:59",23.15,27.2675,429.5,714,0.00478344094931065,1
"3","2015-02-04 17:53:00",23.15,27.245,426,713.5,0.00477946352442199,1
"4","2015-02-04 17:54:00",23.15,27.2,426,708.25,0.00477150882608175,1
"5","2015-02-04 17:55:00",23.1,27.2,426,704.5,0.00475699293331518,1
...
然后我们可以使用 Pandasread_csv()函数加载数据文件,如下所示:
# load all data
data1 = read_csv('datatest.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
data2 = read_csv('datatraining.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
data3 = read_csv('datatest2.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
加载后,我们可以为六个系列中的每一个创建一个图,清楚地显示三个数据集的及时分离。
下面列出了完整的示例。
from pandas import read_csv
from matplotlib import pyplot
# load all data
data1 = read_csv('datatest.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
data2 = read_csv('datatraining.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
data3 = read_csv('datatest2.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
# determine the number of features
n_features = data1.values.shape[1]
pyplot.figure()
for i in range(1, n_features):
# specify the subpout
pyplot.subplot(n_features, 1, i)
# plot data from each set
pyplot.plot(data1.index, data1.values[:, i])
pyplot.plot(data2.index, data2.values[:, i])
pyplot.plot(data3.index, data3.values[:, i])
# add a readable name to the plot
pyplot.title(data1.columns[i], y=0.5, loc='right')
pyplot.show()
运行该示例会为每个数据集创建一个具有不同颜色的绘图:
datatest.txt(测试):蓝色datatraining.txt(训练):橙色datatest2.txt(val):绿色
我们可以看到测试和训练集之间的小差距以及训练和验证集之间的较大差距。
我们还可以看到每个变量的系列中相应的结构(峰值)和房间占用率。
线图显示所有变量和每个数据集的时间序列图
连接数据集
我们可以通过保留数据的时间一致性并将所有三个集合连接成一个数据集来简化数据集,删除“no”列。
这将允许临时测试问题的简单直接框架(在下一部分中),该临时框架可以在临时一致的方式上使用特殊训练/测试装置尺寸进行测试。
注意:这种简化不考虑数据中的时间间隙,并且依赖于先前时间步骤的一系列观察的算法可能需要不同的数据组织。
下面的示例加载数据,将其连接到时间上一致的数据集,并将结果保存到名为“combined.csv”的新文件中。
from pandas import read_csv
from pandas import concat
# load all data
data1 = read_csv('datatest.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
data2 = read_csv('datatraining.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
data3 = read_csv('datatest2.txt', header=0, index_col=1, parse_dates=True, squeeze=True)
# vertically stack and maintain temporal order
data = concat([data1, data2, data3])
# drop row number
data.drop('no', axis=1, inplace=True)
# save aggregated dataset
data.to_csv('combined.csv')
运行该示例将连接的数据集保存到新文件'combined.csv'。
简单的预测模型
该问题最简单的表述是根据当前的环境条件预测占用率。
我将此称为直接模型,因为它没有利用先前时间步骤的环境措施的观察结果。从技术上讲,这不是序列分类,它只是一个直接的分类问题,其中观察是在时间上排序的。
这似乎是我从文献中略读的问题的标准表达,令人失望的是,论文似乎使用了 UCI 网站上标注的训练/验证/测试数据。
我们将使用上一节中描述的组合数据集,并通过将最后 30%的数据作为测试集保留来评估模型技能。例如:
# load the dataset
data = read_csv('combined.csv', header=0, index_col=0, parse_dates=True, squeeze=True)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.3, shuffle=False, random_state=1)
接下来,我们可以从一个朴素的预测模型开始评估数据集的一些模型。
朴素的模型
这个问题的一个简单模型是预测最突出的阶级结果。
这称为零规则,或原始预测算法。我们将评估测试集中每个示例的所有 0(未占用)和全 1(占用)的预测,并使用精度度量来评估方法。
下面是一个函数,它将根据测试集和选择的结果变量执行这种朴素的预测
def naive_prediction(testX, value):
return [value for x in range(len(testX))]
下面列出了完整的示例。
# naive prediction model
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# load the dataset
data = read_csv('../datasets/occupancy_data/combined.csv', header=0, index_col=0, parse_dates=True, squeeze=True)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.3, shuffle=False, random_state=1)
# make a naive prediction
def naive_prediction(testX, value):
return [value for x in range(len(testX))]
# evaluate skill of predicting each class value
for value in [0, 1]:
# forecast
yhat = naive_prediction(testX, value)
# evaluate
score = accuracy_score(testy, yhat)
# summarize
print('Naive=%d score=%.3f' % (value, score))
运行该示例打印朴素预测和相关分数。
通过预测全部 0,我们可以看到基线分数约为 82%的准确度。都没有入住。
对于任何被认为对该问题熟练的模型,它必须达到 82%或更高的技能。
Naive=0 score=0.822
Naive=1 score=0.178
逻辑回归
文献的摘要显示了应用于该问题的一系列复杂的神经网络模型。
首先,让我们尝试一个简单的逻辑回归分类算法。
下面列出了完整的示例。
# logistic regression
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
# load the dataset
data = read_csv('combined.csv', header=0, index_col=0, parse_dates=True, squeeze=True)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.3, shuffle=False, random_state=1)
# define the model
model = LogisticRegression()
# fit the model on the training set
model.fit(trainX, trainy)
# predict the test set
yhat = model.predict(testX)
# evaluate model skill
score = accuracy_score(testy, yhat)
print(score)
运行该示例在训练数据集上拟合逻辑回归模型并预测测试数据集。
该模型的技能大约 99%准确,显示出朴素方法的技巧。
通常,我建议在建模之前对数据进行居中和规范化,但是一些试验和错误表明,未缩放数据的模型更加熟练。
0.992704280155642
乍一看这是一个令人印象深刻的结果。
尽管测试设置与研究文献中的测试设置不同,但是报告的非常简单模型的技能优于更复杂的神经网络模型。
特征选择和 逻辑回归
仔细观察时间序列图可以看出房间被占用的时间与环境措施的峰值之间存在明显的关系。
这是有道理的,并解释了为什么这个问题实际上很容易建模。
我们可以通过单独测试每个环境度量的简单逻辑回归模型来进一步简化模型。我们的想法是,我们不需要所有数据来预测占用率;或许只是其中一项措施就足够了。
这是最简单的特征选择类型,其中创建模型并单独评估每个特征。更高级的方法可以考虑每个特征子组。
下面列出了使用五个输入功能中的每一个单独测试逻辑模型的完整示例。
# logistic regression feature selection
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
# load the dataset
data = read_csv('combined.csv', header=0, index_col=0, parse_dates=True, squeeze=True)
values = data.values
# basic feature selection
features = [0, 1, 2, 3, 4]
for f in features:
# split data into inputs and outputs
X, y = values[:, f].reshape((len(values), 1)), values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.3, shuffle=False, random_state=1)
# define the model
model = LogisticRegression()
# fit the model on the training set
model.fit(trainX, trainy)
# predict the test set
yhat = model.predict(testX)
# evaluate model skill
score = accuracy_score(testy, yhat)
print('feature=%d, name=%s, score=%.3f' % (f, data.columns[f], score))
运行该示例将打印特征索引,名称以及在该特征上训练的逻辑模型的技能,并在测试集上进行评估。
我们可以看到只需要“Light”变量就可以在此数据集上实现 99%的准确率。
记录环境变量的实验室很可能有一个光传感器,当房间被占用时,它会打开内部灯。
或者,也许在白天记录光(例如通过窗户的阳光),并且房间在每天或每周工作日被占用。
至少,本教程的结果会询问有关使用此数据集的任何研究论文的一些难题,因为显然它不是一个具有挑战性的预测问题。
feature=0, name=Temperature, score=0.799
feature=1, name=Humidity, score=0.822
feature=2, name=Light, score=0.991
feature=3, name=CO2, score=0.763
feature=4, name=HumidityRatio, score=0.822
扩展
这些数据可能仍然有待进一步调查。
一些想法包括:
- 如果移除光柱,问题可能会更具挑战性。
- 也许问题可以被描述为真正的多变量时间序列分类,其中在模型中使用滞后观察。
- 也许在预测中可以利用环境变量中的明显峰值。
我简单地尝试了这些模型而没有令人兴奋的结果。
如果您浏览任何这些扩展或在线查找一些示例,请在下面的评论中告诉我们。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
- 占用检测数据集,UCI 机器学习库
- GitHub 上的占用检测数据
- 支持需求驱动的 HVAC 运行的基于多传感器的占用估计模型,2012。
- 使用统计学习模型,2016 年对光线,温度,湿度和 CO2 测量的办公室进行精确的占用检测。
- 基于可见光的占用推断使用集成学习,2018。
摘要
在本教程中,您发现了一个标准的多变量时间序列分类问题,用于使用环境变量的度量来预测房间占用率。
具体来说,你学到了:
- 机器学习中的占用检测标准时间序列分类问题。
- 如何加载和可视化多变量时间序列分类数据。
- 如何开发简单的幼稚和逻辑回归模型,以达到近乎完美的技能。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何使用脑波预测人眼是打开还是关闭
原文:
machinelearningmastery.com/how-to-predict-whether-eyes-are-open-or-closed-using-brain-waves/
评估机器学习方法进行时间序列预测时如何避免方法错误的案例研究。
评估关于时间序列预测问题的机器学习模型具有挑战性。
在问题框架或模型评估中很容易产生一个小错误,这会产生令人印象深刻的结果,但会导致无效的发现。
一个有趣的时间序列分类问题是仅基于他们的脑波数据(EEG)来预测受试者的眼睛是开放还是闭合。
在本教程中,您将发现在评估时间序列预测模型时,基于脑电波和常见方法陷阱预测眼睛是开放还是闭合的问题。
通过本教程,您将了解在评估时间序列预测问题的机器学习算法时如何避免常见陷阱。这些陷阱既吸引了初学者,也吸引了专业从业者和学者。
完成本教程后,您将了解:
- 眼睛状态预测问题和您可以使用的标准机器学习数据集。
- 如何重现熟练的结果来预测 Python 中脑波的眼睛状态。
- 如何在评估预测模型时发现一个有趣的方法缺陷。
让我们开始吧。
教程概述
本教程分为七个部分;他们是:
- 从脑波预测开/闭眼
- 数据可视化和删除异常值
- 开发预测模型
- 模型评估方法的问题
- 用时间顺序训练分裂训练
- 前瞻性验证
- 外卖和重点课程
从脑波预测开/闭眼
在这篇文章中,我们将仔细研究一个问题,即根据脑波数据预测受试者眼睛是开放还是闭合。
Oliver Rosler 和 David Suendermann 为他们的 2013 年论文题为“迈向使用 EEG 的眼睛状态预测的第一步”描述了这个问题。
我看到了这个数据集,我不得不知道更多。
具体地,当对象打开和闭合眼睛时,通过摄像机记录,由单个人进行脑电图(EEG)记录 117 秒(刚好不到两分钟)。然后手动记录 EEG 迹线中每个时间步的打开/关闭状态。
使用 Emotiv EEG Neuroheadset 记录 EEG,产生 14 条痕迹。
EEG 传感器位于受试者身上的卡通 取自“使用 EEG 进行眼状态预测的第一步”,2013 年。
输出变量是二进制的,这意味着这是一个两类分类问题。
在 117 秒内总共进行了 14,980 次观测(行),这意味着每秒约有 128 次观测。
语料库由 14,977 个实例组成,每个实例具有 15 个属性(14 个属性表示电极和眼睛状态的值)。实例按时间顺序存储在语料库中,以便能够分析时间依赖性。语料库的 8,255(55.12%)个实例对应于睁眼,6,722(44.88%)个实例对应于闭眼状态。
- 使用脑电图进行眼状态预测的第一步,2013。
还有一些脑电图观察具有远大于预期的幅度。这些可能是异常值,可以使用简单的统计方法识别和删除,例如删除与平均值有 3 到 4 个标准偏差的行。
该问题的最简单的框架是在当前时间步长给出 EEG 迹线的情况下预测眼睛状态(打开/关闭)。该问题的更高级框架可以寻求对每个 EEG 迹线的多变量时间序列建模以便预测当前眼睛状态。
数据可视化和删除异常值
数据集可以从 UCI 机器学习库免费下载:
原始数据采用 ARFF 格式(在 Weka 中使用),但可以通过删除 ARFF 标头转换为 CSV。
下面是删除了 ARFF 标题的前五行数据的示例。
4329.23,4009.23,4289.23,4148.21,4350.26,4586.15,4096.92,4641.03,4222.05,4238.46,4211.28,4280.51,4635.9,4393.85,0
4324.62,4004.62,4293.85,4148.72,4342.05,4586.67,4097.44,4638.97,4210.77,4226.67,4207.69,4279.49,4632.82,4384.1,0
4327.69,4006.67,4295.38,4156.41,4336.92,4583.59,4096.92,4630.26,4207.69,4222.05,4206.67,4282.05,4628.72,4389.23,0
4328.72,4011.79,4296.41,4155.9,4343.59,4582.56,4097.44,4630.77,4217.44,4235.38,4210.77,4287.69,4632.31,4396.41,0
4326.15,4011.79,4292.31,4151.28,4347.69,4586.67,4095.9,4627.69,4210.77,4244.1,4212.82,4288.21,4632.82,4398.46,0
...
我们可以将数据作为 DataFrame 加载,并绘制每个 EEG 轨迹和输出变量(打开/关闭状态)的时间序列。
完整的代码示例如下所示。
该示例假定您具有 CSV 格式的数据集副本,文件名为“EEG_Eye_State.csv”,与代码位于同一目录中。
# visualize dataset
from pandas import read_csv
from matplotlib import pyplot
# load the dataset
data = read_csv('EEG_Eye_State.csv', header=None)
# retrieve data as numpy array
values = data.values
# create a subplot for each time series
pyplot.figure()
for i in range(values.shape[1]):
pyplot.subplot(values.shape[1], 1, i+1)
pyplot.plot(values[:, i])
pyplot.show()
运行该示例会为每个 EEG 跟踪和输出变量创建一个线图。
我们可以看到异常值清除每条迹线中的数据。我们还可以分别以 0/1 看到眼睛的开/关状态。
每个 EEG 轨迹和输出变量的线图
去除异常值以更好地理解 EEG 痕迹与眼睛的开/闭状态之间的关系是有用的。
下面的示例将删除所有具有 EEG 观测值的行,这些行是平均值的四个标准偏差或更多。数据集将保存到名为“EEG_Eye_State_no_outliers.csv”的新文件中。
这是离群值检测和删除的快速而肮脏的实现,但是完成了工作。我相信你可以设计出更高效的实现方案。
# remove outliers from the EEG data
from pandas import read_csv
from numpy import mean
from numpy import std
from numpy import delete
from numpy import savetxt
# load the dataset.
data = read_csv('EEG_Eye_State.csv', header=None)
values = data.values
# step over each EEG column
for i in range(values.shape[1] - 1):
# calculate column mean and standard deviation
data_mean, data_std = mean(values[:,i]), std(values[:,i])
# define outlier bounds
cut_off = data_std * 4
lower, upper = data_mean - cut_off, data_mean + cut_off
# remove too small
too_small = [j for j in range(values.shape[0]) if values[j,i] < lower]
values = delete(values, too_small, 0)
print('>deleted %d rows' % len(too_small))
# remove too large
too_large = [j for j in range(values.shape[0]) if values[j,i] > upper]
values = delete(values, too_large, 0)
print('>deleted %d rows' % len(too_large))
# save the results to a new file
savetxt('EEG_Eye_State_no_outliers.csv', values, delimiter=',')
运行该示例总结了删除的行,因为 EEG 数据中的每一列都针对平均值之上和之下的异常值进行处理。
>deleted 0 rows
>deleted 1 rows
>deleted 2 rows
>deleted 1 rows
>deleted 0 rows
>deleted 142 rows
>deleted 0 rows
>deleted 48 rows
>deleted 0 rows
>deleted 153 rows
>deleted 0 rows
>deleted 43 rows
>deleted 0 rows
>deleted 0 rows
>deleted 0 rows
>deleted 15 rows
>deleted 0 rows
>deleted 5 rows
>deleted 10 rows
>deleted 0 rows
>deleted 21 rows
>deleted 53 rows
>deleted 0 rows
>deleted 12 rows
>deleted 58 rows
>deleted 53 rows
>deleted 0 rows
>deleted 59 rows
我们现在可以通过加载新的'EEG_Eye_State_no_outliers.csv'文件来显示没有异常值的数据。
# visualize dataset without outliers
from pandas import read_csv
from matplotlib import pyplot
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
# retrieve data as numpy array
values = data.values
# create a subplot for each time series
pyplot.figure()
for i in range(values.shape[1]):
pyplot.subplot(values.shape[1], 1, i+1)
pyplot.plot(values[:, i])
pyplot.show()
运行该示例可创建更好的绘图,清晰显示眼睛闭合时的正峰值(1)和眼睛打开时的负峰值(0)。
每个 EEG 轨迹的线图和没有异常值的输出变量
开发预测模型
最简单的预测模型是基于当前的 EEG 观察来预测眼睛开/闭状态,忽略跟踪信息。
直观地说,人们不会期望这是有效的,然而,这是 Rosler 和 Suendermann 2013 年论文中使用的方法。
具体来说,他们使用这种问题框架的 10 倍交叉验证评估了 Weka 软件中的一大套分类算法。他们使用多种方法实现了超过 90%的准确度,包括基于实例的方法,如 k-最近邻和 KStar。
然而,基于实例的学习器,如 IB1 和 KStar,再次大大超过了决策树。后者实现了明显的最佳表现,分类错误率仅为 3.2%。
- 使用 EEG 进行眼状态预测的第一步,2013。
在许多其他论文中,类似的方法和发现与相同和相似的数据集一起使用。
当我读到这篇文章时,我很惊讶,因此转载了结果。
完整示例如下所列,k = 3 KNN。
# knn for predicting eye state
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
from sklearn.neighbors import KNeighborsClassifier
from numpy import mean
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
values = data.values
# evaluate knn using 10-fold cross-validation
scores = list()
kfold = KFold(10, shuffle=True, random_state=1)
for train_ix, test_ix in kfold.split(values):
# define train/test X/y
trainX, trainy = values[train_ix, :-1], values[train_ix, -1]
testX, testy = values[test_ix, :-1], values[test_ix, -1]
# define model
model = KNeighborsClassifier(n_neighbors=3)
# fit model on train set
model.fit(trainX, trainy)
# forecast test set
yhat = model.predict(testX)
# evaluate predictions
score = accuracy_score(testy, yhat)
# store
scores.append(score)
print('>%.3f' % score)
# calculate mean score across each run
print('Final Score: %.3f' % (mean(scores)))
运行该示例打印交叉验证的每个折叠的得分,并且在所有 10 倍中平均得分为 97%。
>0.970
>0.975
>0.978
>0.977
>0.973
>0.979
>0.978
>0.976
>0.974
>0.969
Final Score: 0.975
非常令人印象深刻!
但是感觉有些不对劲。
我有兴趣了解在开放到封闭和关闭到开放的每个过渡期间如何考虑数据中清晰峰值的模型。
我尝试使用我自己的测试工具来考虑数据的时间顺序的每个模型表现得更糟。
为什么?
提示:考虑所选模型评估策略和表现最佳的算法类型。
模型评估方法的问题
免责声明:我没有打电话给论文或相关论文的作者。我不在乎。根据我的经验,大多数发表的论文都无法复制或存在重大的方法缺陷(包括我写的很多东西)。我只对学习和教学感兴趣。
时间序列模型的评估方法存在方法上的缺陷。
我教导了这个缺陷,但在阅读了论文并重现结果之后,它仍然让我绊倒了。
我希望通过这个例子来说明它将帮助你解决自己的预测问题。
模型评估中的方法缺陷是使用 k 折交叉验证。具体而言,以不遵守观察的时间顺序的方式评估模型。
这个问题的关键是找到基于实例的方法,比如 k-最近邻,因为它对这个问题非常熟练。 KNN 将在数据集中寻找k最相似的行,并计算输出状态的模式作为预测。
通过在评估模型时不考虑实例的时间顺序,它允许模型使用来自未来的信息做出预测。这在 KNN 算法中特别明显。
由于观测频率高(每秒 128 次),最相似的行将是在过去和未来的预测实例中及时相邻的行。
我们可以通过一些小实验来更清楚地说明这一点。
用时间顺序训练分裂训练
我们可以做的第一个测试是评估 KNN 模型的技能,当数据集被洗牌时,以及当数据集不是时,训练/测试分割。
在分割之前对数据进行混洗的情况下,我们期望结果类似于上一节中的交叉验证结果,特别是如果测试集是数据集的 10%。
如果关于时间排序和基于实例的方法在未来使用相邻示例的重要性的理论是正确的,那么我们期望在拆分之前数据集未被洗牌的测试更糟糕。
首先,下面的示例将数据集拆分为训练/测试拆分,分别为 90%/ 10%的数据。在拆分之前对数据集进行洗牌。
# knn for predicting eye state
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.1, shuffle=True, random_state=1)
# define model
model = KNeighborsClassifier(n_neighbors=3)
# fit model on train set
model.fit(trainX, trainy)
# forecast test set
yhat = model.predict(testX)
# evaluate predictions
score = accuracy_score(testy, yhat)
print(score)
运行该示例,我们可以看到,确实,该技能与我们在交叉验证示例中看到的或与其接近的技能相匹配,准确率为 96%。
0.9699510831586303
接下来,我们重复实验,而不是在拆分之前对数据集进行洗牌。
这意味着训练数据是关于观测的时间排序的前 90%的数据,并且测试数据集是数据的最后 10%或约 1,400 个观测值。
# knn for predicting eye state
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.1, shuffle=False, random_state=1)
# define model
model = KNeighborsClassifier(n_neighbors=3)
# fit model on train set
model.fit(trainX, trainy)
# forecast test set
yhat = model.predict(testX)
# evaluate predictions
score = accuracy_score(testy, yhat)
print(score)
运行该示例显示模型技能更差,为 52%。
0.5269042627533194
这是一个好的开始,但不是确定的。
考虑到我们可以在结果变量图上看到非常短的开/关间隔,有可能最后 10%的数据集难以预测。
我们可以重复实验并及时使用前 10%的数据进行测试,最后 90%的数据用于训练。我们可以通过在使用 flip()函数分割数据之前反转行的顺序来实现。
# knn for predicting eye state
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from numpy import flip
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
values = data.values
# reverse order of rows
values = flip(values, 0)
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.1, shuffle=False, random_state=1)
# define model
model = KNeighborsClassifier(n_neighbors=3)
# fit model on train set
model.fit(trainX, trainy)
# forecast test set
yhat = model.predict(testX)
# evaluate predictions
score = accuracy_score(testy, yhat)
print(score)
运行实验产生类似的结果,准确度约为 52%。
这提供了更多的证据,证明不是特定的连续观察块导致模型技能差。
0.5290006988120196
看起来需要立即相邻的观察来做出良好的预测。
前瞻性验证
模型可能需要过去(但不是未来)的相邻观察,以便进行熟练的预测。
这听起来很合理,但也有问题。
然而,我们可以使用测试集上的前向验证来实现这一点。这是允许模型在预测时间步骤之前使用所有观察的地方,因为我们在测试数据集中的每个新时间步骤验证新模型。
有关前进验证的更多信息,请参阅帖子:
下面的示例使用前向验证评估 KNN 的技能,使用最后 10%的数据集(约 10 秒),遵守时间顺序。
# knn for predicting eye state
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from numpy import array
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.1, shuffle=False, random_state=1)
# walk-forward validation
historyX, historyy = [x for x in trainX], [x for x in trainy]
predictions = list()
for i in range(len(testy)):
# define model
model = KNeighborsClassifier(n_neighbors=3)
# fit model on train set
model.fit(array(historyX), array(historyy))
# forecast the next time step
yhat = model.predict([testX[i, :]])[0]
# store prediction
predictions.append(yhat)
# add real observation to history
historyX.append(testX[i, :])
historyy.append(testy[i])
# evaluate predictions
score = accuracy_score(testy, predictions)
print(score)
运行该示例可提供令人印象深刻的模型技能,准确率约为 95%。
0.9531795946890287
我们可以进一步推进此测试,并且在做出预测时仅将先前的 10 个观测值用于模型。
下面列出了完整的示例。
# knn for predicting eye state
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from numpy import array
# load the dataset
data = read_csv('EEG_Eye_State_no_outliers.csv', header=None)
values = data.values
# split data into inputs and outputs
X, y = values[:, :-1], values[:, -1]
# split the dataset
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.1, shuffle=False, random_state=1)
# walk-forward validation
historyX, historyy = [x for x in trainX], [x for x in trainy]
predictions = list()
for i in range(len(testy)):
# define model
model = KNeighborsClassifier(n_neighbors=3)
# fit model on a small subset of the train set
tmpX, tmpy = array(historyX)[-10:,:], array(historyy)[-10:]
model.fit(tmpX, tmpy)
# forecast the next time step
yhat = model.predict([testX[i, :]])[0]
# store prediction
predictions.append(yhat)
# add real observation to history
historyX.append(testX[i, :])
historyy.append(testy[i])
# evaluate predictions
score = accuracy_score(testy, predictions)
print(score)
运行该示例可以进一步提高模型技能,准确率接近 99%。
我预计,当迹线从开放到闭合或从闭合到开放转变时,唯一出现的错误是 EEG 系列拐点处的错误,这是问题的实际难点部分。这方面需要进一步调查。
0.9923130677847659
实际上,我们已经确认该模型需要相邻的观测结果及其结果才能做出预测,并且它只能在过去而不是未来的相邻观测中做得很好。
这是有趣的。但这一发现在实践中没有用。
如果部署了该模型,则需要模型知道最近过去的眼睛打开/关闭状态,例如之前的 128 秒。
这将无法使用。
基于脑波预测眼睛状态的模型的整体思想是让它在没有这种确认的情况下运行。
外卖和重点课程
让我们回顾一下到目前为止我们学到的东西:
1.模型评估方法必须考虑到观测的时间顺序。
这意味着使用 k-fold 交叉验证在方法上无效,该交叉验证不按时间分层(例如,随机抽取或使用随机选择的行)。
这也意味着使用在分割之前混洗数据的训练/测试分割在方法上是无效的。
我们在模型的高技能评估中看到了这一点,与模型的低技能相比,在模型的低技能时,在预测时间无法获得直接相邻的观测时间。
2.模型评估方法必须对最终模型的使用有意义。
这意味着即使您使用的方法尊重观察的时间顺序,模型也应该只有在实际使用模型时可用的信息。
我们在模型的高技能下看到了这一点,这种方法遵循了观察顺序的前瞻性验证方法,但提供了可用的信息,例如眼睛状态,如果模型在实践中使用则无法获得。
关键是从使用最终模型的问题框架开始,然后向后工作到可用的数据,以及在框架中评估模型的方法,该框架仅在可用的信息下运行取景。
当您试图了解其他人的工作时,这会倍加适用。
前进
希望,无论是在评估自己的预测模型时,还是在评估其他模型时,这都会有所帮助。
那么,如果提供原始数据,您将如何解决此问题?
我认为这个问题的关键是在从开眼到闭眼或闭眼到开眼的过渡时 EEG 数据中明显的正/负峰值。我希望有效的模型可以利用这个特征,可能使用半秒钟或类似的先前脑电图观察。
甚至可以使用单个迹线而不是 15 个,以及来自信号处理的简单峰值检测方法,而不是机器学习方法。
如果你对此有所了解,请告诉我。我很想看看你发现了什么。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
摘要
在本教程中,您发现了在评估时间序列预测模型时基于脑电波和常见方法陷阱预测眼睛是开放还是闭合的问题。
具体来说,你学到了:
- 眼睛状态预测问题和您可以使用的标准机器学习数据集。
- 如何重现熟练的结果来预测 Python 中脑波的眼睛状态。
- 如何在评估预测模型时发现一个有趣的方法缺陷。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何在 Python 中扩展长短期记忆网络的数据
原文:
machinelearningmastery.com/how-to-scale-data-for-long-short-term-memory-networks-in-python/
在训练神经网络时,可能需要缩放序列预测问题的数据,例如长期短期记忆复现神经网络。
当网络适合具有一系列值的非缩放数据(例如,数量在 10 到 100 之间)时,大输入可能会减慢网络的学习和收敛速度,并且在某些情况下会妨碍网络有效地学习问题。
在本教程中,您将了解如何规范化和标准化序列预测数据,以及如何确定将哪个用于输入和输出变量。
完成本教程后,您将了解:
- 如何在 Python 中规范化和标准化序列数据。
- 如何为输入和输出变量选择适当的缩放。
- 缩放序列数据时的实际考虑因素
让我们开始吧。
如何在 Python 中扩展长短期记忆网络的数据 图片来自 Mathias Appel ,保留一些权利。
教程概述
本教程分为 4 个部分;他们是:
- 缩放系列数据
- 缩放输入变量
- 缩放输出变量
- 缩放时的实际考虑因素
在 Python 中扩展系列数据
您可能需要考虑两种类型的系列缩放:规范化和标准化。
这些都可以使用 scikit-learn 库来实现。
规范化系列数据
归一化是对原始范围内的数据进行重新缩放,以使所有值都在 0 和 1 的范围内。
标准化要求您知道或能够准确估计最小和最大可观察值。您可以从可用数据中估算这些值。如果您的时间序列趋势向上或向下,估计这些预期值可能会很困难,并且规范化可能不是用于解决问题的最佳方法。
值按如下标准化:
y = (x - min) / (max - min)
其中最小值和最大值与值 x 被归一化有关。
例如,对于数据集,我们可以将 min 和 max 可观察值猜测为 30 和-10。然后我们可以将任何值标准化,如 18.8,如下所示:
y = (x - min) / (max - min)
y = (18.8 - (-10)) / (30 - (-10))
y = 28.8 / 40
y = 0.72
您可以看到,如果提供的 x 值超出最小值和最大值的范围,则结果值将不在 0 和 1 的范围内。您可以在做出预测之前检查这些观察值并删除它们来自数据集或将它们限制为预定义的最大值或最小值。
您可以使用 scikit-learn 对象 MinMaxScaler 来规范化数据集。
使用 MinMaxScaler 和其他缩放技术的良好实践用法如下:
- 使用可用的训练数据调整定标器。对于归一化,这意味着训练数据将用于估计最小和最大可观察值。这是通过调用 fit()函数完成的。
- 将比例应用于训练数据。这意味着您可以使用标准化数据来训练模型。这是通过调用 transform()函数完成的。
- 将比例应用于前进的数据。这意味着您可以在将来准备要预测的新数据。
如果需要,可以反转变换。这对于将预测转换回其原始比例以进行报告或绘图非常有用。这可以通过调用 inverse_transform()函数来完成。
下面是一个标准化 10 个量的人为序列的例子。
缩放器对象要求将数据作为行和列的矩阵提供。加载的时间序列数据作为 Pandas 系列加载。
from pandas import Series
from sklearn.preprocessing import MinMaxScaler
# define contrived series
data = [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]
series = Series(data)
print(series)
# prepare data for normalization
values = series.values
values = values.reshape((len(values), 1))
# train the normalization
scaler = MinMaxScaler(feature_range=(0, 1))
scaler = scaler.fit(values)
print('Min: %f, Max: %f' % (scaler.data_min_, scaler.data_max_))
# normalize the dataset and print
normalized = scaler.transform(values)
print(normalized)
# inverse transform and print
inversed = scaler.inverse_transform(normalized)
print(inversed)
运行该示例打印序列,打印从序列估计的最小值和最大值,打印相同的标准化序列,然后使用逆变换将值返回到其原始比例。
我们还可以看到数据集的最小值和最大值分别为 10.0 和 100.0。
0 10.0
1 20.0
2 30.0
3 40.0
4 50.0
5 60.0
6 70.0
7 80.0
8 90.0
9 100.0
Min: 10.000000, Max: 100.000000
[[ 0\. ]
[ 0.11111111]
[ 0.22222222]
[ 0.33333333]
[ 0.44444444]
[ 0.55555556]
[ 0.66666667]
[ 0.77777778]
[ 0.88888889]
[ 1\. ]]
[[ 10.]
[ 20.]
[ 30.]
[ 40.]
[ 50.]
[ 60.]
[ 70.]
[ 80.]
[ 90.]
[ 100.]]
标准化系列数据
标准化数据集涉及重缩放值的分布,以便观察值的平均值为 0,标准差为 1。
这可以被认为是减去平均值或使数据居中。
与标准化一样,当您的数据具有不同比例的输入值时,标准化可能很有用,甚至在某些机器学习算法中也是必需的。
标准化假定您的观察结果符合高斯分布(钟形曲线),具有良好的平均值和标准偏差。如果不满足此期望,您仍然可以标准化时间序列数据,但可能无法获得可靠的结果。
标准化要求您知道或能够准确估计可观察值的均值和标准差。您可以从训练数据中估算这些值。
值标准化如下:
y = (x - mean) / standard_deviation
平均值计算如下:
mean = sum(x) / count(x)
而 standard_deviation 计算如下:
standard_deviation = sqrt( sum( (x - mean)² ) / count(x))
我们可以猜测平均值为 10,标准偏差约为 5.使用这些值,我们可以将第一个值 20.7 标准化如下:
y = (x - mean) / standard_deviation
y = (20.7 - 10) / 5
y = (10.7) / 5
y = 2.14
数据集的均值和标准差估计值对于新数据可能比最小值和最大值更稳健。
您可以使用 scikit-learn 对象 StandardScaler 标准化数据集。
from pandas import Series
from sklearn.preprocessing import StandardScaler
from math import sqrt
# define contrived series
data = [1.0, 5.5, 9.0, 2.6, 8.8, 3.0, 4.1, 7.9, 6.3]
series = Series(data)
print(series)
# prepare data for normalization
values = series.values
values = values.reshape((len(values), 1))
# train the normalization
scaler = StandardScaler()
scaler = scaler.fit(values)
print('Mean: %f, StandardDeviation: %f' % (scaler.mean_, sqrt(scaler.var_)))
# normalize the dataset and print
standardized = scaler.transform(values)
print(standardized)
# inverse transform and print
inversed = scaler.inverse_transform(standardized)
print(inversed)
运行该示例打印序列,打印从序列估计的平均值和标准差,打印标准化值,然后以原始比例打印值。
我们可以看到估计的平均值和标准偏差分别约为 5.3 和 2.7。
0 1.0
1 5.5
2 9.0
3 2.6
4 8.8
5 3.0
6 4.1
7 7.9
8 6.3
Mean: 5.355556, StandardDeviation: 2.712568
[[-1.60569456]
[ 0.05325007]
[ 1.34354035]
[-1.01584758]
[ 1.26980948]
[-0.86838584]
[-0.46286604]
[ 0.93802055]
[ 0.34817357]]
[[ 1\. ]
[ 5.5]
[ 9\. ]
[ 2.6]
[ 8.8]
[ 3\. ]
[ 4.1]
[ 7.9]
[ 6.3]]
缩放输入变量
输入变量是网络在输入或可见层上进行的预测。
一个好的经验法则是输入变量应该是小值,可能在 0-1 范围内,或者标准化为零均值和标准差为 1。
输入变量是否需要缩放取决于问题和每个变量的具体情况。我们来看一些例子。
分类输入
您可能有一系列分类输入,例如字母或状态。
通常,分类输入首先是整数编码,然后是单热编码。也就是说,为每个不同的可能输入分配唯一的整数值,然后使用 1 和 0 的二进制向量来表示每个整数值。
根据定义,单热编码将确保每个输入是一个小的实际值,在这种情况下为 0.0 或 1.0。
实值输入
您可能有一系列数量作为输入,例如价格或温度。
如果数量的分布是正常的,则应该标准化,否则系列应该标准化。如果数值范围很大(10s 100s 等)或小(0.01,0.0001),则适用。
如果数量值很小(接近 0-1)并且分布有限(例如标准偏差接近 1),那么也许你可以在没有缩放系列的情况下逃脱。
其他输入
问题可能很复杂,如何最好地扩展输入数据可能并不清楚。
如果有疑问,请将输入序列标准化。如果您拥有这些资源,请使用原始数据,标准化数据和标准化来探索建模,并查看是否存在有益的差异。
如果输入变量是线性组合的,就像在 MLP [多层感知机]中一样,那么至少在理论上很少有必要对输入进行标准化。 ......但是,有很多实际的原因可以解释为什么标准化输入可以使训练更快,并减少陷入局部最优的机会。
- 我应该规范化/标准化/重新缩放数据吗? 神经网络常见问题解答
缩放输出变量
输出变量是网络预测的变量。
您必须确保输出变量的比例与网络输出层上的激活功能(传递函数)的比例相匹配。
如果输出激活函数的范围为[0,1],那么显然您必须确保目标值位于该范围内。但通常最好选择适合目标分布的输出激活函数,而不是强制数据符合输出激活函数。
- 我应该规范化/标准化/重新缩放数据吗? 神经网络常见问题解答
以下启发式方法应涵盖大多数序列预测问题:
二分类问题
如果您的问题是二分类问题,那么输出将是类值 0 和 1.这最好使用输出层上的 sigmoid 激活函数建模。输出值将是介于 0 和 1 之间的实数值,可以捕捉到清晰的值。
多分类问题
如果您的问题是多分类问题,那么输出将是一个介于 0 和 1 之间的二进制类值的向量,每个类值一个输出。这最好使用输出层上的 softmax 激活功能建模。同样,输出值将是介于 0 和 1 之间的实数值,可以捕捉到清晰的值。
回归问题
如果您的问题是回归问题,那么输出将是实际值。这最好使用线性激活功能建模。如果值的分布正常,则可以标准化输出变量。否则,可以对输出变量进行标准化。
其他问题
可以在输出层上使用许多其他激活函数,并且您的问题的细节可能会增加混淆。
经验法则是确保网络输出与数据规模相匹配。
缩放时的实际考虑因素
缩放序列数据时有一些实际考虑因素。
- 估算系数。您可以从训练数据中估算系数(标准化的最小值和最大值或标准化的平均值和标准偏差)。检查这些首先估算并使用领域知识或领域专家来帮助改进这些估算,以便将来对所有数据进行有用的更正。
- 保存系数。您将需要以与用于训练模型的数据完全相同的方式对未来的新数据进行标准化。保存用于存档的系数,并在需要在做出预测时扩展新数据时加载它们。
- 数据分析。使用数据分析可以帮助您更好地了解数据。例如,一个简单的直方图可以帮助您快速了解数量的分布情况,看看标准化是否有意义。
- 缩放每个系列。如果您的问题有多个系列,请将每个系列视为单独的变量,然后分别对其进行缩放。
- 在合适的时间缩放。在正确的时间应用任何缩放变换非常重要。例如,如果您有一系列非静止的数量,则在首次使数据静止后进行缩放可能是合适的。在将系列转换为监督学习问题后对其进行扩展是不合适的,因为每个列的处理方式不同,这是不正确的。
- 如果怀疑则缩放。您可能需要重缩放输入和输出变量。如果有疑问,至少要对数据进行标准化。
进一步阅读
本节列出了扩展时要考虑的一些其他资源。
- 我应该规范化/标准化/重缩放数据吗? 神经网络常见问题解答
- MinMaxScaler scikit-learn API 文档
- StandardScaler scikit-learn API 文档
- 如何使用 Python 从零开始扩展机器学习数据
- 如何在 Python 中标准化和标准化时间序列数据
- 如何使用 Scikit-Learn 为 Python 机器学习准备数据
摘要
在本教程中,您了解了在使用长短期记忆复现神经网络时如何扩展序列预测数据。
具体来说,你学到了:
- 如何在 Python 中规范化和标准化序列数据。
- 如何为输入和输出变量选择适当的缩放。
- 缩放序列数据时的实际考虑因素
您对缩放序列预测数据有任何疑问吗? 在评论中提出您的问题,我会尽力回答。
如何将 Keras TimeseriesGenerator用于时间序列预测
原文:
machinelearningmastery.com/how-to-use-the-timeseriesgenerator-for-time-series-forecasting-in-keras/
必须将时间序列数据转换为具有输入和输出组件的样本结构,然后才能用于拟合监督学习模型。
如果您必须手动执行此转换,这可能具有挑战性。 Keras 深度学习库提供了 TimeseriesGenerator,可以将单变量和多变量时间序列数据自动转换为样本,随时可以训练深度学习模型。
在本教程中,您将了解如何使用 Keras TimeseriesGenerator 准备时间序列数据,以便使用深度学习方法进行建模。
完成本教程后,您将了解:
- 如何定义 TimeseriesGenerator 生成器并将其用于适合深度学习模型。
- 如何为单变量时间序列准备发电机并适合 MLP 和 LSTM 模型。
- 如何为多变量时间序列准备生成器并适合 LSTM 模型。
让我们开始吧。
如何在 Keras 中使用 TimeseriesGenerator 进行时间序列预测 照片由 Chris Fithall 拍摄,保留一些权利。
教程概述
本教程分为六个部分;他们是:
- 监督学习的时间序列问题
- 如何使用 TimeseriesGenerator
- 单变量时间序列示例
- 多变量时间序列示例
- 多变量输入和相关系列示例
- 多步预测示例
注:本教程假设您使用的是 Keras v2.2.4 或更高版本。
监督学习的时间序列问题
时间序列数据需要准备才能用于训练监督学习模型,例如深度学习模型。
例如,单变量时间序列表示为观察向量:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
监督学习算法要求数据作为样本集合提供,其中每个样本具有输入分量(X)和输出分量(y)。
X, y
example input, example output
example input, example output
example input, example output
...
该模型将学习如何将输入映射到所提供示例的输出。
y = f(X)
必须将时间序列转换为具有输入和输出组件的样本。该变换既可以告知模型将要学习什么,也可以在做出预测时告知将来如何使用该模型,例如:做出预测需要什么(X)和做出预测(y)。
对于对一步预测感兴趣的单变量时间序列,在先前时间步骤的观察,即所谓的滞后观察,被用作输入,输出是在当前时间步骤的观察。
例如,上述 10 步单变量系列可以表示为监督学习问题,其中输入的三个时间步长和输出的一个步骤如下:
X, y
[1, 2, 3], [4]
[2, 3, 4], [5]
[3, 4, 5], [6]
...
您可以编写代码来自行执行此转换;例如,看帖子:
或者,当您对使用 Keras 训练神经网络模型感兴趣时,可以使用 TimeseriesGenerator 类。
如何使用 TimeseriesGenerator
Keras 提供 TimeseriesGenerator ,可用于将单变量或多变量时间序列数据集自动转换为监督学习问题。
使用 TimeseriesGenerator 有两个部分:定义它并使用它来训练模型。
定义 TimeseriesGenerator
您可以创建类的实例并指定时间序列问题的输入和输出方面,它将提供序列类的实例,然后可以使用它来迭代输入和输出系列。
在大多数时间序列预测问题中,输入和输出系列将是同一系列。
例如:
# load data
inputs = ...
outputs = ...
# define generator
generator = TimeseriesGenerator(inputs, outputs, ...)
# iterator generator
for i in range(len(generator)):
...
从技术上讲,该类不是一个生成器,因为它不是 Python 生成器,你不能在它上面使用next()函数。
除了指定时间序列问题的输入和输出方面外,还应该配置一些其他参数;例如:
- 长度:在每个样本的输入部分中使用的滞后观察的数量(例如 3)。
- batch_size :每次迭代时返回的样本数(例如 32)。
您必须根据设计的问题框架定义长度参数。这是用作输入的所需滞后观察数。
您还必须在训练期间将批量大小定义为模型的批量大小。如果数据集中的样本数小于批量大小,则可以通过计算其长度,将生成器和模型中的批量大小设置为生成器中的样本总数;例如:
print(len(generator))
还有其他参数,例如定义数据的开始和结束偏移,采样率,步幅等。您不太可能使用这些功能,但您可以查看完整 API 以获取更多详细信息。
默认情况下,样本不会被洗牌。这对于像 LSTM 这样的一些循环神经网络很有用,这些神经网络在一批中的样本之间保持状态。
它可以使其他神经网络(例如 CNN 和 MLP)在训练时对样本进行混洗。可以通过将'shuffle'参数设置为 True 来启用随机播放。这将具有为每个批次返回的洗样样本的效果。
在撰写本文时,TimeseriesGenerator 仅限于一步输出。不支持多步时间序列预测。
使用 TimeseriesGenerator 训练模型
一旦定义了 TimeseriesGenerator 实例,它就可以用于训练神经网络模型。
可以使用 TimeseriesGenerator 作为数据生成器来训练模型。这可以通过使用fit_generator()函数拟合定义的模型来实现。
此函数将生成器作为参数。它还需要steps_per_epoch参数来定义每个时期中使用的样本数。可以将此值设置为 TimeseriesGenerator 实例的长度,以使用生成器中的所有样本。
例如:
# define generator
generator = TimeseriesGenerator(...)
# define model
model = ...
# fit model
model.fit_generator(generator, steps_per_epoch=len(generator), ...)
类似地,生成器可用于通过调用evaluate_generator()函数来评估拟合模型,并使用拟合模型使用predict_generator()函数对新数据做出预测。
与数据生成器匹配的模型不必使用 evaluate 和 predict 函数的生成器版本。只有在您希望数据生成器为模型准备数据时,才能使用它们。
单变量时间序列示例
我们可以使用一个小的设计单变量时间序列数据集的工作示例使 TimeseriesGenerator 具体化。
首先,让我们定义我们的数据集。
# define dataset
series = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
我们将选择框架问题,其中最后两个滞后观察将用于预测序列中的下一个值。例如:
X, y
[1, 2] 3
现在,我们将使用批量大小为 1,以便我们可以探索生成器中的数据。
# define generator
n_input = 2
generator = TimeseriesGenerator(series, series, length=n_input, batch_size=1)
接下来,我们可以看到数据生成器将为此时间序列准备多少样本。
# number of samples
print('Samples: %d' % len(generator))
最后,我们可以打印每个样本的输入和输出组件,以确认数据是按照我们的预期准备的。
for i in range(len(generator)):
x, y = generator[i]
print('%s => %s' % (x, y))
下面列出了完整的示例。
# univariate one step problem
from numpy import array
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
series = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# define generator
n_input = 2
generator = TimeseriesGenerator(series, series, length=n_input, batch_size=1)
# number of samples
print('Samples: %d' % len(generator))
# print each sample
for i in range(len(generator)):
x, y = generator[i]
print('%s => %s' % (x, y))
首先运行该示例打印生成器中的样本总数,即 8。
然后我们可以看到每个输入数组的形状为[1,2],每个输出的形状为[1,]。
观察结果按照我们的预期进行准备,其中两个滞后观察结果将用作输入,序列中的后续值作为输出。
Samples: 8
[[1\. 2.]] => [3.]
[[2\. 3.]] => [4.]
[[3\. 4.]] => [5.]
[[4\. 5.]] => [6.]
[[5\. 6.]] => [7.]
[[6\. 7.]] => [8.]
[[7\. 8.]] => [9.]
[[8\. 9.]] => [10.]
现在我们可以在这个数据上拟合模型,并学习将输入序列映射到输出序列。
我们将从一个简单的多层感知机或 MLP 模型开始。
将定义发生器,以便在给定少量样品的情况下,将在每批中使用所有样品。
# define generator
n_input = 2
generator = TimeseriesGenerator(series, series, length=n_input, batch_size=8)
我们可以定义一个简单的模型,其中一个隐藏层有 50 个节点,一个输出层将做出预测。
# define model
model = Sequential()
model.add(Dense(100, activation='relu', input_dim=n_input))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
然后我们可以使用fit_generator()函数将模型与生成器拟合。我们在生成器中只有一批数据,因此我们将steps_per_epoch设置为 1.该模型适用于 200 个时期。
# fit model
model.fit_generator(generator, steps_per_epoch=1, epochs=200, verbose=0)
一旦适合,我们将进行样本预测。
给定输入[9,10],我们将做出预测并期望模型预测[11]或接近它。该模型没有调整;这只是如何使用发电机的一个例子。
# make a one step prediction out of sample
x_input = array([9, 10]).reshape((1, n_input))
yhat = model.predict(x_input, verbose=0)
下面列出了完整的示例。
# univariate one step problem with mlp
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
series = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# define generator
n_input = 2
generator = TimeseriesGenerator(series, series, length=n_input, batch_size=8)
# define model
model = Sequential()
model.add(Dense(100, activation='relu', input_dim=n_input))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit_generator(generator, steps_per_epoch=1, epochs=200, verbose=0)
# make a one step prediction out of sample
x_input = array([9, 10]).reshape((1, n_input))
yhat = model.predict(x_input, verbose=0)
print(yhat)
运行该示例准备生成器,拟合模型,并进行样本预测,正确预测接近 11 的值。
[[11.510406]]
我们还可以使用生成器来适应循环神经网络,例如长短期记忆网络或 LSTM。
LSTM 期望数据输入具有[_ 样本,时间步长,特征 ]的形状,而到目前为止描述的生成器提供滞后观察作为特征或形状[ 样本,特征 _]。
我们可以在从[10,]到[10,1]准备发电机之前重新设计单变量时间序列 10 个时间步长和 1 个特征;例如:
# reshape to [10, 1]
n_features = 1
series = series.reshape((len(series), n_features))
然后,TimeseriesGenerator 将系列分为样品,其形状为[ batch,n_input,1 ]或[8,2,1],用于生成器中的所有八个样品,两个滞后观察用作时间步长。
下面列出了完整的示例。
# univariate one step problem with lstm
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
series = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# reshape to [10, 1]
n_features = 1
series = series.reshape((len(series), n_features))
# define generator
n_input = 2
generator = TimeseriesGenerator(series, series, length=n_input, batch_size=8)
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', input_shape=(n_input, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit_generator(generator, steps_per_epoch=1, epochs=500, verbose=0)
# make a one step prediction out of sample
x_input = array([9, 10]).reshape((1, n_input, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)
再次,运行该示例准备数据,拟合模型,并预测序列中的下一个样本外值。
[[11.092189]]
多变量时间序列示例
TimeseriesGenerator 还支持多变量时间序列问题。
这些是您有多个并行系列的问题,每个系列中的观察步骤同时进行。
我们可以用一个例子来证明这一点。
首先,我们可以设计两个并行系列的数据集。
# define dataset
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95, 105])
具有多变量时间序列格式的标准结构使得每个时间序列是单独的列,行是每个时间步的观察值。
我们定义的系列是向量,但我们可以将它们转换为列。我们可以将每个系列重塑为具有 10 个时间步长和 1 个特征的形状[10,1]的数组。
# reshape series
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
我们现在可以通过调用hstack()NumPy 函数将列水平堆叠到数据集中。
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2))
我们现在可以直接将此数据集提供给 TimeseriesGenerator。我们将使用每个系列的前两个观测值作为输入,并将每个序列的下一个观测值作为输出。
# define generator
n_input = 2
generator = TimeseriesGenerator(dataset, dataset, length=n_input, batch_size=1)
然后,对于 1 个样本,2 个时间步长和 2 个特征或平行序列,每个样本将是[1,2,2]的三维数组。对于 1 个样本和 2 个特征,输出将是[1,2]的二维系列。第一个样本将是:
X, y
[[10, 15], [20, 25]] [[30, 35]]
下面列出了完整的示例。
# multivariate one step problem
from numpy import array
from numpy import hstack
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95, 105])
# reshape series
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2))
print(dataset)
# define generator
n_input = 2
generator = TimeseriesGenerator(dataset, dataset, length=n_input, batch_size=1)
# number of samples
print('Samples: %d' % len(generator))
# print each sample
for i in range(len(generator)):
x, y = generator[i]
print('%s => %s' % (x, y))
运行该示例将首先打印准备好的数据集,然后打印数据集中的总样本数。
接下来,打印每个样品的输入和输出部分,确认我们的预期结构。
[[ 10 15]
[ 20 25]
[ 30 35]
[ 40 45]
[ 50 55]
[ 60 65]
[ 70 75]
[ 80 85]
[ 90 95]
[100 105]]
Samples: 8
[[[10\. 15.]
[20\. 25.]]] => [[30\. 35.]]
[[[20\. 25.]
[30\. 35.]]] => [[40\. 45.]]
[[[30\. 35.]
[40\. 45.]]] => [[50\. 55.]]
[[[40\. 45.]
[50\. 55.]]] => [[60\. 65.]]
[[[50\. 55.]
[60\. 65.]]] => [[70\. 75.]]
[[[60\. 65.]
[70\. 75.]]] => [[80\. 85.]]
[[[70\. 75.]
[80\. 85.]]] => [[90\. 95.]]
[[[80\. 85.]
[90\. 95.]]] => [[100\. 105.]]
样本的三维结构意味着生成器不能直接用于像 MLP 这样的简单模型。
这可以通过首先将时间序列数据集展平为一维向量,然后将其提供给 TimeseriesGenerator 并将长度设置为用作输入的步数乘以系列中的列数(n_steps * n_features)来实现。。
这种方法的局限性在于生成器只允许您预测一个变量。几乎可以肯定,编写自己的函数来为 MLP 准备多变量时间序列比使用 TimeseriesGenerator 更好。
样品的三维结构可以由 CNN 和 LSTM 模型直接使用。下面列出了使用 TimeseriesGenerator 进行多变量时间序列预测的完整示例。
# multivariate one step problem with lstm
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95, 105])
# reshape series
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2))
# define generator
n_features = dataset.shape[1]
n_input = 2
generator = TimeseriesGenerator(dataset, dataset, length=n_input, batch_size=8)
# define model
model = Sequential()
model.add(LSTM(100, activation='relu', input_shape=(n_input, n_features)))
model.add(Dense(2))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit_generator(generator, steps_per_epoch=1, epochs=500, verbose=0)
# make a one step prediction out of sample
x_input = array([[90, 95], [100, 105]]).reshape((1, n_input, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)
运行该示例准备数据,拟合模型,并预测每个输入时间序列中的下一个值,我们期望[110,115]。
[[111.03207 116.58153]]
多变量输入和相关系列示例
存在多变量时间序列问题,其中存在一个或多个输入序列和要预测的单独输出序列,其取决于输入序列。
为了使这个具体,我们可以设计一个带有两个输入时间序列和一个输出系列的例子,它是输入系列的总和。
# define dataset
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95, 105])
out_seq = array([25, 45, 65, 85, 105, 125, 145, 165, 185, 205])
其中输出序列中的值是输入时间序列中同一时间步长的值的总和。
10 + 15 = 25
这与先前的示例不同,在给定输入的情况下,我们希望预测下一时间步的目标时间序列中的值,而不是与输入相同的时间步长。
例如,我们想要样本:
X, y
[10, 15], 25
[20, 25], 45
[30, 35], 65
...
我们不希望样品如下:
X, y
[10, 15], 45
[20, 25], 65
[30, 35], 85
...
尽管如此,TimeseriesGenerator 类假定我们正在预测下一个时间步骤,并将提供数据,如上面的第二种情况。
例如:
# multivariate one step problem
from numpy import array
from numpy import hstack
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95, 105])
out_seq = array([25, 45, 65, 85, 105, 125, 145, 165, 185, 205])
# reshape series
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2))
# define generator
n_input = 1
generator = TimeseriesGenerator(dataset, out_seq, length=n_input, batch_size=1)
# print each sample
for i in range(len(generator)):
x, y = generator[i]
print('%s => %s' % (x, y))
运行该示例打印样本的输入和输出部分,其具有下一时间步的输出值而不是当前时间步,因为我们可能期望这种类型的问题。
[[[10\. 15.]]] => [[45.]]
[[[20\. 25.]]] => [[65.]]
[[[30\. 35.]]] => [[85.]]
[[[40\. 45.]]] => [[105.]]
[[[50\. 55.]]] => [[125.]]
[[[60\. 65.]]] => [[145.]]
[[[70\. 75.]]] => [[165.]]
[[[80\. 85.]]] => [[185.]]
[[[90\. 95.]]] => [[205.]]
因此,我们可以修改目标序列(out_seq)并在开头插入一个额外的值,以便将所有观察值向下推一步。
这种人为的转变将允许优选的问题框架。
# shift the target sample by one step
out_seq = insert(out_seq, 0, 0)
下面提供了这种转变的完整示例。
# multivariate one step problem
from numpy import array
from numpy import hstack
from numpy import insert
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95, 105])
out_seq = array([25, 45, 65, 85, 105, 125, 145, 165, 185, 205])
# reshape series
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2))
# shift the target sample by one step
out_seq = insert(out_seq, 0, 0)
# define generator
n_input = 1
generator = TimeseriesGenerator(dataset, out_seq, length=n_input, batch_size=1)
# print each sample
for i in range(len(generator)):
x, y = generator[i]
print('%s => %s' % (x, y))
运行该示例显示问题的首选框架。
无论输入样本的长度如何,此方法都将起作用。
[[[10\. 15.]]] => [25.]
[[[20\. 25.]]] => [45.]
[[[30\. 35.]]] => [65.]
[[[40\. 45.]]] => [85.]
[[[50\. 55.]]] => [105.]
[[[60\. 65.]]] => [125.]
[[[70\. 75.]]] => [145.]
[[[80\. 85.]]] => [165.]
[[[90\. 95.]]] => [185.]
多步预测示例
与许多其他类型的经典和机器学习模型相比,神经网络模型的一个好处是它们可以进行多步预测。
也就是说,模型可以学习将一个或多个特征的输入模式映射到多于一个特征的输出模式。这可用于时间序列预测,以直接预测多个未来时间步骤。
这可以通过直接从模型输出向量,通过将所需数量的输出指定为输出层中的节点数来实现,或者可以通过诸如编解码器模型的专用序列预测模型来实现。
TimeseriesGenerator 的一个限制是它不直接支持多步输出。具体而言,它不会创建目标序列中可能需要的多个步骤。
然而,如果您准备目标序列有多个步骤,它将尊重并使用它们作为每个样本的输出部分。这意味着你有责任为每个时间步骤准备预期的输出。
我们可以通过一个简单的单变量时间序列来证明这一点,在输出序列中有两个时间步长。
您可以看到目标序列中的行数必须与输入序列中的行数相同。在这种情况下,我们必须知道输入序列中的值之外的值,或者将输入序列修剪为目标序列的长度。
# define dataset
series = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
target = array([[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9],[9,10],[10,11]])
下面列出了完整的示例。
# univariate multi-step problem
from numpy import array
from keras.preprocessing.sequence import TimeseriesGenerator
# define dataset
series = array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
target = array([[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9],[9,10],[10,11]])
# define generator
n_input = 2
generator = TimeseriesGenerator(series, target, length=n_input, batch_size=1)
# print each sample
for i in range(len(generator)):
x, y = generator[i]
print('%s => %s' % (x, y))
运行该示例打印样本的输入和输出部分,显示两个滞后观察值作为输入,两个步骤作为多步骤预测问题的输出。
[[1\. 2.]] => [[3\. 4.]]
[[2\. 3.]] => [[4\. 5.]]
[[3\. 4.]] => [[5\. 6.]]
[[4\. 5.]] => [[6\. 7.]]
[[5\. 6.]] => [[7\. 8.]]
[[6\. 7.]] => [[8\. 9.]]
[[7\. 8.]] => [[ 9\. 10.]]
[[8\. 9.]] => [[10\. 11.]]
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
- 如何将时间序列转换为 Python 中的监督学习问题
- TimeseriesGenerator Keras API
- 序列 Keras API
- 顺序模型 Keras API
- Python Generator
摘要
在本教程中,您了解了如何使用 Keras TimeseriesGenerator 准备时间序列数据,以便使用深度学习方法进行建模。
具体来说,你学到了:
- 如何定义 TimeseriesGenerator 生成器并将其用于适合深度学习模型。
- 如何为单变量时间序列准备发电机并适合 MLP 和 LSTM 模型。
- 如何为多变量时间序列准备生成器并适合 LSTM 模型。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
基于机器学习算法的室内运动时间序列分类
室内运动预测涉及使用无线传感器强度数据来预测建筑物内的对象的位置和运动。
这是一个具有挑战性的问题,因为没有直接的分析模型将来自多个传感器的信号强度数据的可变长度轨迹转换为用户行为。
'_ 室内用户移动 _'数据集是标准且免费提供的时间序列分类问题。
在本教程中,您将发现室内运动预测时间序列分类问题以及如何设计功能并评估问题的机器学习算法。
完成本教程后,您将了解:
- 基于传感器强度预测房间运动的时间序列分类问题。
- 如何调查数据以便更好地理解问题以及如何从原始数据中设计特征以做出预测性建模。
- 如何检查一套分类算法并调整一种算法以进一步提高问题的表现。
让我们开始吧。
- 更新 Sept / 2018 :添加了指向数据集镜像的链接。
室内运动时间序列分类与机器学习算法 照片由 Nola Tularosa ,保留一些权利。
教程概述
本教程分为五个部分;他们是:
- 室内用户运动预测
- 室内运动预测数据集
- 模型评估
- 数据准备
- 算法抽查
室内用户运动预测
“_ 室内用户移动 _”预测问题涉及基于由环境中的无线检测器测量的信号强度的变化来确定个体是否已经在房间之间移动。
收集数据集并由 Davide Bacciu 等人提供。来自意大利比萨大学的 2011 年论文“通过油藏计算预测异构室内环境中的用户运动”作为探索一种类似于反复神经网络称为“油藏计算”的方法论的数据集。 “
问题是预测室内用户定位和运动模式的更普遍问题的特殊情况。
通过在环境中定位四个无线传感器并在主题上定位一个来收集数据。当四个无线传感器检测到并记录传感器强度的时间序列时,受试者穿过环境。
结果是由可变长度时间序列组成的数据集,其中四个变量描述通过明确定义的静态环境的轨迹,以及运动是否导致环境中的主题更衣室的分类。
这是一个具有挑战性的问题,因为没有明显和通用的方法将信号强度数据与环境中的主题位置相关联。
RSS 与被跟踪对象的位置之间的关系不能容易地形成分析模型,因为它强烈依赖于环境的特征以及所涉及的无线设备。一世
- 通过油藏计算预测异构室内环境中的用户移动,2011。
在受控的实验条件下收集数据。
传感器被放置在三对两个连接的房间中,其中包含典型的办公家具。两个传感器放置在两个房间的每个房间的角落中,并且受试者走过房间中的六条预定路径中的一条。预测是在每条路径的某个点上进行的,这可能会也可能不会导致房间的变化。
下面的动画片清楚地表明了传感器位置(A1-A4),可能行走的六条可能路径,以及将做出预测的两个点(M)。
两个房间概述,传感器位置和 6 个预定义路径。 取自“通过油藏计算预测异构室内环境中的用户移动”。
从三对两个房间收集三个数据集,其中走路径并进行传感器测量,称为数据集 1,数据集 2 和数据集 3。
下表摘自论文,总结了三个数据集中每个数据集中走的路径数,房间更改总数和非房间更改(类标签)以及时间序列输入的长度。
从三对两个房间收集的传感器数据摘要。 取自“通过油藏计算预测异构室内环境中的用户移动”。
从技术上讲,数据由多变量时间序列输入和分类输出组成,可以描述为时间序列分类问题。
来自四个锚的 RSS 值被组织成对应于从起始点到标记 M 的轨迹测量的不同长度的序列。目标分类标签与每个输入序列相关联以指示用户是否将要改变其位置(房间)或不。
- 通过油藏计算预测异构室内环境中的用户移动,2011。
室内运动预测数据集
数据集可从 UCI 机器学习库免费获得:
如果上述网站出现故障(可能发生),这里是指向数据集镜像的直接链接:
数据可以下载为包含以下显着文件的.zip 文件:
- dataset / MovementAAL_RSS _ ???。csv 每个动作的 RSS 曲线,文件名中的'???'标记从 1 到 311 的曲目编号。
- dataset / MovementAAL_target.csv 跟踪号到输出类值或目标的映射。
- groups / MovementAAL_DatasetGroup.csv 跟踪编号到数据集组 1,2 或 3 的映射,标记记录跟踪的一对房间。
- groups / MovementAAL_Paths.csv 跟踪号码到路径类型 1-6 的映射,在上面的卡通图中标记。
提供的数据已经标准化。
具体来说,每个输入变量被标准化为每个数据集(一对房间)的范围[-1,1],输出类变量标记为-1 表示房间之间没有过渡,而+1 表示通过房间过渡。
[...]放置数据包括 4 维 RSS 测量的时间序列(NU = 4),对应于每个数据集独立地在[-1,1]范围内归一化的 4 个锚点[...]
- 通过油藏计算预测异构室内环境中的用户移动,2011。
如果预标准化分布差异很大,则在跨数据集组合观察时,按数据集缩放数据可能(或可能不)引入额外的挑战。
给定跟踪文件中的一个跟踪的时间序列按时间顺序提供,其中一行记录单个时间步的观察结果。数据以 8Hz 记录,这意味着数据中的八个时间步长经过了一秒的时钟时间。
下面是一个跟踪示例,取自' dataset / MovementAAL_RSS_1.csv ',其输出目标为'1'(发生房间转换),来自第 1 组(第一对房间)和是路径 1(房间之间从左到右的直射)。
#RSS_anchor1, RSS_anchor2, RSS_anchor3, RSS_anchor4
-0.90476,-0.48,0.28571,0.3
-0.57143,-0.32,0.14286,0.3
-0.38095,-0.28,-0.14286,0.35
-0.28571,-0.2,-0.47619,0.35
-0.14286,-0.2,0.14286,-0.2
-0.14286,-0.2,0.047619,0
-0.14286,-0.16,-0.38095,0.2
-0.14286,-0.04,-0.61905,-0.2
-0.095238,-0.08,0.14286,-0.55
-0.047619,0.04,-0.095238,0.05
-0.19048,-0.04,0.095238,0.4
-0.095238,-0.04,-0.14286,0.35
-0.33333,-0.08,-0.28571,-0.2
-0.2381,0.04,0.14286,0.35
0,0.08,0.14286,0.05
-0.095238,0.04,0.095238,0.1
-0.14286,-0.2,0.14286,0.5
-0.19048,0.04,-0.42857,0.3
-0.14286,-0.08,-0.2381,0.15
-0.33333,0.16,-0.14286,-0.8
-0.42857,0.16,-0.28571,-0.1
-0.71429,0.16,-0.28571,0.2
-0.095238,-0.08,0.095238,0.35
-0.28571,0.04,0.14286,0.2
0,0.04,0.14286,0.1
0,0.04,-0.047619,-0.05
-0.14286,-0.6,-0.28571,-0.1
如第一篇论文所述,数据集以两种特定方式(实验设置或 ES)使用,以评估问题的预测模型,指定为 ES1 和 ES2。
- ES1 :合并数据集 1 和 2,将其分为训练(80%)和测试(20%)集以评估模型。
- ES2 :合并用作训练集的数据集 1 和 2(66%),数据集 3 用作测试集(34%)以评估模型。
ES1 案例评估模型以概括两对已知房间内的移动,即具有已知几何形状的房间。 ES2 案例试图推广从两个房间到第三个看不见的房间的运动:一个更难的问题。
2011 年的论文报告了 ES1 上大约 95%的分类准确率和 ES2 大约 89%的表现,经过对一套算法的一些测试后,我非常令人印象深刻。
加载和探索数据集
在本节中,我们将数据加载到内存中并使用摘要和可视化进行探索,以帮助更好地了解问题的建模方式。
首先,下载数据集并将下载的存档解压缩到当前工作目录中。
加载数据集
目标,组和路径文件可以直接作为 Pandas DataFrames 加载。
# load mapping files
from pandas import read_csv
target_mapping = read_csv('dataset/MovementAAL_target.csv', header=0)
group_mapping = read_csv('groups/MovementAAL_DatasetGroup.csv', header=0)
paths_mapping = read_csv('groups/MovementAAL_Paths.csv', header=0)
信号强度曲线存储在 _ 数据集/_ 目录中的单独文件中。
这些可以通过迭代目录中的所有文件并直接加载序列来加载。因为每个序列都有一个可变长度(可变行数),我们可以为列表中的每个跟踪存储 NumPy 数组。
# load sequences and targets into memory
from pandas import read_csv
from os import listdir
sequences = list()
directory = 'dataset'
target_mapping = None
for name in listdir(directory):
filename = directory + '/' + name
if filename.endswith('_target.csv'):
continue
df = read_csv(filename, header=0)
values = df.values
sequences.append(values)
我们可以将所有这些绑定到一个名为load_dataset()的函数中,并将数据加载到内存中。
下面列出了完整的示例。
# load user movement dataset into memory
from pandas import read_csv
from os import listdir
# return list of traces, and arrays for targets, groups and paths
def load_dataset(prefix=''):
grps_dir, data_dir = prefix+'groups/', prefix+'dataset/'
# load mapping files
targets = read_csv(data_dir + 'MovementAAL_target.csv', header=0)
groups = read_csv(grps_dir + 'MovementAAL_DatasetGroup.csv', header=0)
paths = read_csv(grps_dir + 'MovementAAL_Paths.csv', header=0)
# load traces
sequences = list()
target_mapping = None
for name in listdir(data_dir):
filename = data_dir + name
if filename.endswith('_target.csv'):
continue
df = read_csv(filename, header=0)
values = df.values
sequences.append(values)
return sequences, targets.values[:,1], groups.values[:,1], paths.values[:,1]
# load dataset
sequences, targets, groups, paths = load_dataset()
# summarize shape of the loaded data
print(len(sequences), targets.shape, groups.shape, paths.shape)
运行该示例加载数据并显示已从磁盘正确加载 314 条跟踪及其相关输出(目标为-1 或+1),数据集编号(组为 1,2 或 3)和路径编号(路径为 1) -6)。
314 (314,) (314,) (314,)
基本信息
我们现在可以仔细查看加载的数据,以更好地理解或确认我们对问题的理解。
我们从论文中了解到,数据集在两个类别方面是合理平衡的。我们可以通过总结所有观察的类别细分来证实这一点。
# summarize class breakdown
class1,class2 = len(targets[targets==-1]), len(targets[targets==1])
print('Class=-1: %d %.3f%%' % (class1, class1/len(targets)*100))
print('Class=+1: %d %.3f%%' % (class2, class2/len(targets)*100))
接下来,我们可以通过绘制原始值的直方图来查看四个锚点中每一个的传感器强度值的分布。
这要求我们创建一个包含所有观察行的数组,以便我们可以绘制每列的分布。vstack()NumPy 函数将为我们完成这项工作。
# histogram for each anchor point
all_rows = vstack(sequences)
pyplot.figure()
variables = [0, 1, 2, 3]
for v in variables:
pyplot.subplot(len(variables), 1, v+1)
pyplot.hist(all_rows[:, v], bins=20)
pyplot.show()
最后,另一个有趣的方面是跟踪长度的分布。
我们可以使用直方图来总结这种分布。
# histogram for trace lengths
trace_lengths = [len(x) for x in sequences]
pyplot.hist(trace_lengths, bins=50)
pyplot.show()
综合这些,下面列出了加载和汇总数据的完整示例。
# summarize simple information about user movement data
from os import listdir
from numpy import array
from numpy import vstack
from pandas import read_csv
from matplotlib import pyplot
# return list of traces, and arrays for targets, groups and paths
def load_dataset(prefix=''):
grps_dir, data_dir = prefix+'groups/', prefix+'dataset/'
# load mapping files
targets = read_csv(data_dir + 'MovementAAL_target.csv', header=0)
groups = read_csv(grps_dir + 'MovementAAL_DatasetGroup.csv', header=0)
paths = read_csv(grps_dir + 'MovementAAL_Paths.csv', header=0)
# load traces
sequences = list()
target_mapping = None
for name in listdir(data_dir):
filename = data_dir + name
if filename.endswith('_target.csv'):
continue
df = read_csv(filename, header=0)
values = df.values
sequences.append(values)
return sequences, targets.values[:,1], groups.values[:,1], paths.values[:,1]
# load dataset
sequences, targets, groups, paths = load_dataset()
# summarize class breakdown
class1,class2 = len(targets[targets==-1]), len(targets[targets==1])
print('Class=-1: %d %.3f%%' % (class1, class1/len(targets)*100))
print('Class=+1: %d %.3f%%' % (class2, class2/len(targets)*100))
# histogram for each anchor point
all_rows = vstack(sequences)
pyplot.figure()
variables = [0, 1, 2, 3]
for v in variables:
pyplot.subplot(len(variables), 1, v+1)
pyplot.hist(all_rows[:, v], bins=20)
pyplot.show()
# histogram for trace lengths
trace_lengths = [len(x) for x in sequences]
pyplot.hist(trace_lengths, bins=50)
pyplot.show()
首先运行该示例总结了观察的类分布。
结果证实了我们对完整数据集的期望在两个阶段结果的观察方面几乎完全平衡。
Class=-1: 156 49.682%
Class=+1: 158 50.318%
接下来,创建每个锚点的传感器强度的直方图,总结数据分布。
我们可以看到每个变量的分布接近正常,显示出类似高斯的形状。我们也可以看到围绕-1 的观测数据太多。这可能表示可以标记甚至从序列中过滤掉的通用“无强度”观察结果。
调查分布是否按路径类型甚至数据集编号更改可能会很有趣。
每个锚点的传感器强度值的直方图
最后,创建序列长度的直方图。
我们可以看到长度在 25,40 和 60 之间的序列簇。我们还可以看到,如果我们想要修剪长序列,那么最长约 70 个时间步长可能是合适的。最小长度似乎是 19。
传感器强度序列长度的直方图
时间序列图
我们正在处理时间序列数据,因此我们实际查看序列的一些示例非常重要。
我们可以按路径对轨迹进行分组,并为每条路径绘制一条轨迹的示例。期望不同路径的迹线在某些方面可能看起来不同。
# group sequences by paths
paths = [1,2,3,4,5,6]
seq_paths = dict()
for path in paths:
seq_paths[path] = [sequences[j] for j in range(len(paths)) if paths[j]==path]
# plot one example of a trace for each path
pyplot.figure()
for i in paths:
pyplot.subplot(len(paths), 1, i)
# line plot each variable
for j in [0, 1, 2, 3]:
pyplot.plot(seq_paths[i][0][:, j], label='Anchor ' + str(j+1))
pyplot.title('Path ' + str(i), y=0, loc='left')
pyplot.show()
我们还可以绘制一个迹线的每个系列以及线性回归模型预测的趋势。这将使该系列中的任何趋势变得明显。
我们可以使用 lstsq()NumPy 函数对给定系列拟合线性回归。
下面的函数regress()将一系列作为单个变量,通过最小二乘拟合线性回归模型,并预测每个时间步的输出返回捕获数据趋势的序列。
# fit a linear regression function and return the predicted values for the series
def regress(y):
# define input as the time step
X = array([i for i in range(len(y))]).reshape(len(y), 1)
# fit linear regression via least squares
b = lstsq(X, y)[0][0]
# predict trend on time step
yhat = b * X[:,0]
return yhat
我们可以使用该函数绘制单个迹线中每个变量的时间序列的趋势。
# plot series for a single trace with trend
seq = sequences[0]
variables = [0, 1, 2, 3]
pyplot.figure()
for i in variables:
pyplot.subplot(len(variables), 1, i+1)
# plot the series
pyplot.plot(seq[:,i])
# plot the trend
pyplot.plot(regress(seq[:,i]))
pyplot.show()
将所有这些结合在一起,下面列出了完整的示例。
# plot series data
from os import listdir
from numpy import array
from numpy import vstack
from numpy.linalg import lstsq
from pandas import read_csv
from matplotlib import pyplot
# return list of traces, and arrays for targets, groups and paths
def load_dataset(prefix=''):
grps_dir, data_dir = prefix+'groups/', prefix+'dataset/'
# load mapping files
targets = read_csv(data_dir + 'MovementAAL_target.csv', header=0)
groups = read_csv(grps_dir + 'MovementAAL_DatasetGroup.csv', header=0)
paths = read_csv(grps_dir + 'MovementAAL_Paths.csv', header=0)
# load traces
sequences = list()
target_mapping = None
for name in listdir(data_dir):
filename = data_dir + name
if filename.endswith('_target.csv'):
continue
df = read_csv(filename, header=0)
values = df.values
sequences.append(values)
return sequences, targets.values[:,1], groups.values[:,1], paths.values[:,1]
# fit a linear regression function and return the predicted values for the series
def regress(y):
# define input as the time step
X = array([i for i in range(len(y))]).reshape(len(y), 1)
# fit linear regression via least squares
b = lstsq(X, y)[0][0]
# predict trend on time step
yhat = b * X[:,0]
return yhat
# load dataset
sequences, targets, groups, paths = load_dataset()
# group sequences by paths
paths = [1,2,3,4,5,6]
seq_paths = dict()
for path in paths:
seq_paths[path] = [sequences[j] for j in range(len(paths)) if paths[j]==path]
# plot one example of a trace for each path
pyplot.figure()
for i in paths:
pyplot.subplot(len(paths), 1, i)
# line plot each variable
for j in [0, 1, 2, 3]:
pyplot.plot(seq_paths[i][0][:, j], label='Anchor ' + str(j+1))
pyplot.title('Path ' + str(i), y=0, loc='left')
pyplot.show()
# plot series for a single trace with trend
seq = sequences[0]
variables = [0, 1, 2, 3]
pyplot.figure()
for i in variables:
pyplot.subplot(len(variables), 1, i+1)
# plot the series
pyplot.plot(seq[:,i])
# plot the trend
pyplot.plot(regress(seq[:,i]))
pyplot.show()
运行该示例将创建一个包含六个图形的图表,每个图形对应六个路径中的每一个。给定的图显示了单个迹线的线图,其中包含迹线的四个变量,每个锚点一个。
也许所选择的迹线代表每条路径,也许不是。
我们可以看到一些明显的差异:
- 随时间变化的变量分组。成对变量可以组合在一起,或者所有变量可以在给定时间组合在一起。
- 随时间变化的趋势。变量聚集在一起向中间或分散到极端。
理想情况下,如果行为的这些变化是预测性的,则预测模型必须提取这些特征,或者将这些特征的摘要作为输入呈现。
六条路径中每条路径的一条迹线(4 个变量)的线图。
创建第二个图,显示单个迹线中四个系列的线图以及趋势线。
我们可以看到,至少对于这种迹线,当用户在环境中移动时,传感器强度数据有明显的趋势。这可能表明有机会在建模之前使数据静止或使用迹线中的每个系列的趋势(观察或系数)作为预测模型的输入。
具有趋势线的单个迹线中的时间序列的线图
模型评估
有许多方法可以拟合和评估此数据的模型。
鉴于类的平衡,分类准确率似乎是一个良好的首先评估指标。通过预测概率和探索 ROC 曲线上的阈值,可以在将来寻求更多的细微差别。
我看到使用这些数据的两个主要主题:
- 同房:一个房间里的痕迹训练的模型可以预测那个房间里新痕迹的结果吗?
- 不同的房间:一个或两个房间的痕迹训练模型可以预测不同房间的新痕迹的结果吗?
本文中描述并总结的 ES1 和 ES2 案例探讨了这些问题,并提供了一个有用的起点。
首先,我们必须将加载的跟踪和目标分成三组。
# separate traces
seq1 = [sequences[i] for i in range(len(groups)) if groups[i]==1]
seq2 = [sequences[i] for i in range(len(groups)) if groups[i]==2]
seq3 = [sequences[i] for i in range(len(groups)) if groups[i]==3]
print(len(seq1),len(seq2),len(seq3))
# separate target
targets1 = [targets[i] for i in range(len(groups)) if groups[i]==1]
targets2 = [targets[i] for i in range(len(groups)) if groups[i]==2]
targets3 = [targets[i] for i in range(len(groups)) if groups[i]==3]
print(len(targets1),len(targets2),len(targets3))
在 ES1 的情况下,我们可以使用 k 折交叉验证,其中 k = 5,使用与论文相同的比率,并且重复评估为评估提供了一些稳健性。
我们可以使用 scikit-learn 中的 cross_val_score()函数来评估模型,然后计算得分的均值和标准差。
# evaluate model for ES1
from numpy import mean
from numpy import std
from sklearn.model_selection import cross_val_score
...
scores = cross_val_score(model, X, y, scoring='accuracy', cv=5, n_jobs=-1)
m, s = mean(scores), std(scores)
在 ES2 的情况下,我们可以将模型拟合到数据集 1 和 2 上,并直接测试数据集 3 上的模型技能。
数据准备
输入数据如何为预测问题构建灵活性。
我想到了两种方法:
- 自动特征学习。深度神经网络能够自动进行特征学习,而循环神经网络可以直接支持多变量多步输入数据。可以使用循环神经网络,例如 LSTM 或 1D CNN。可以将序列填充为相同的长度,例如 70 个时间步长,并且可以使用掩蔽层来忽略填充的时间步长。
- 特色工程。或者,可变长度序列可以概括为单个固定长度向量,并提供给标准机器学习模型用于预测。这需要仔细的特征工程,以便为模型提供足够的跟踪描述,以学习到输出类的映射。
两者都是有趣的方法。
作为第一步,我们将通过手动特征工程准备更传统的固定长度向量输入。
以下是有关可以包含在向量中的功能的一些想法:
- 变量的第一个,中间或最后 n 个观测值。
- 变量的第一个,中间或最后 n 个观测值的平均值或标准差。
- 最后和第 n 个观察结果之间的差异
- 对变量的第一,中或最后 n 个观察值的差异。
- 变量的所有,第一,中间或最后 n 个观测值的线性回归系数。
- 线性回归预测变量的第一,中间或最后 n 个观测值的趋势。
此外,原始值可能不需要数据缩放,因为数据已经缩放到-1 到 1 的范围。如果添加了不同单位的新功能,则可能需要缩放。
一些变量确实显示出一些趋势,这表明可能差异变量可能有助于梳理信号。
每个变量的分布接近高斯分布,因此一些算法可能会受益于标准化,甚至可能是 Box-Cox 变换。
算法抽查
在本节中,我们将对具有不同工程特性集的一套标准机器学习算法的默认配置进行抽查。
现场检查是一种有用的技术,可快速清除输入和输出之间的映射是否有任何信号需要学习,因为大多数测试方法都会提取一些东西。该方法还可以提出可能值得进一步研究的方法。
缺点是每种方法都没有给出它最好的机会(配置)以显示它可以对问题做什么,这意味着任何进一步研究的方法都会受到第一批结果的偏见。
在这些测试中,我们将研究一套六种不同类型的算法,具体来说:
- 逻辑回归。
- k-最近邻居。
- 决策树。
- 支持向量机。
- 随机森林。
- 梯度增压机。
我们将在关注时间序列变量末尾的特征上测试这些方法的默认配置,因为它们可能最能预测房间转换是否会发生。
最后n观察
最后n观察结果可能预示着运动是否会导致房间过渡。
跟踪数据中最小的时间步长为 19,因此,我们将使用 n = 19 作为起点。
下面名为create_dataset()的函数将使用平面一维向量中每条迹线的最后n观测值创建一个固定长度向量,然后将目标添加为最后一个元素向量。
简单的机器学习算法需要对跟踪数据进行扁平化。
# create a fixed 1d vector for each trace with output variable
def create_dataset(sequences, targets):
# create the transformed dataset
transformed = list()
n_vars = 4
n_steps = 19
# process each trace in turn
for i in range(len(sequences)):
seq = sequences[i]
vector = list()
# last n observations
for row in range(1, n_steps+1):
for col in range(n_vars):
vector.append(seq[-row, col])
# add output
vector.append(targets[i])
# store
transformed.append(vector)
# prepare array
transformed = array(transformed)
transformed = transformed.astype('float32')
return transformed
我们可以像以前一样加载数据集,并将其分类到数据集 1,2 和 3 中,如“_ 模型评估 _”部分所述。
然后我们可以调用create_dataset()函数来创建 ES1 和 ES2 案例所需的数据集,特别是 ES1 组合数据集 1 和 2,而 ES2 使用数据集 1 和 2 作为训练集,数据集 3 作为测试集。
下面列出了完整的示例。
# prepare fixed length vector dataset
from os import listdir
from numpy import array
from numpy import savetxt
from pandas import read_csv
# return list of traces, and arrays for targets, groups and paths
def load_dataset(prefix=''):
grps_dir, data_dir = prefix+'groups/', prefix+'dataset/'
# load mapping files
targets = read_csv(data_dir + 'MovementAAL_target.csv', header=0)
groups = read_csv(grps_dir + 'MovementAAL_DatasetGroup.csv', header=0)
paths = read_csv(grps_dir + 'MovementAAL_Paths.csv', header=0)
# load traces
sequences = list()
target_mapping = None
for name in listdir(data_dir):
filename = data_dir + name
if filename.endswith('_target.csv'):
continue
df = read_csv(filename, header=0)
values = df.values
sequences.append(values)
return sequences, targets.values[:,1], groups.values[:,1], paths.values[:,1]
# create a fixed 1d vector for each trace with output variable
def create_dataset(sequences, targets):
# create the transformed dataset
transformed = list()
n_vars = 4
n_steps = 19
# process each trace in turn
for i in range(len(sequences)):
seq = sequences[i]
vector = list()
# last n observations
for row in range(1, n_steps+1):
for col in range(n_vars):
vector.append(seq[-row, col])
# add output
vector.append(targets[i])
# store
transformed.append(vector)
# prepare array
transformed = array(transformed)
transformed = transformed.astype('float32')
return transformed
# load dataset
sequences, targets, groups, paths = load_dataset()
# separate traces
seq1 = [sequences[i] for i in range(len(groups)) if groups[i]==1]
seq2 = [sequences[i] for i in range(len(groups)) if groups[i]==2]
seq3 = [sequences[i] for i in range(len(groups)) if groups[i]==3]
# separate target
targets1 = [targets[i] for i in range(len(groups)) if groups[i]==1]
targets2 = [targets[i] for i in range(len(groups)) if groups[i]==2]
targets3 = [targets[i] for i in range(len(groups)) if groups[i]==3]
# create ES1 dataset
es1 = create_dataset(seq1+seq2, targets1+targets2)
print('ES1: %s' % str(es1.shape))
savetxt('es1.csv', es1, delimiter=',')
# create ES2 dataset
es2_train = create_dataset(seq1+seq2, targets1+targets2)
es2_test = create_dataset(seq3, targets3)
print('ES2 Train: %s' % str(es2_train.shape))
print('ES2 Test: %s' % str(es2_test.shape))
savetxt('es2_train.csv', es2_train, delimiter=',')
savetxt('es2_test.csv', es2_test, delimiter=',')
运行该示例创建三个新的 CSV 文件,特别是'es1.csv','es2_train.csv'和'es2_test.csv'用于 ES1 和分别为 ES2 病例。
还总结了这些数据集的形状。
ES1: (210, 77)
ES2 Train: (210, 77)
ES2 Test: (104, 77)
接下来,我们可以评估 ES1 数据集上的模型。
经过一些测试后,似乎标准化数据集会为那些依赖距离值(KNN 和 SVM)的方法带来更好的模型技能,并且通常对其他方法没有影响。因此,使用管道来评估首先标准化数据集的每个算法。
下面列出了新数据集上的采样检查算法的完整示例。
# spot check for ES1
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
# load dataset
dataset = read_csv('es1.csv', header=None)
# split into inputs and outputs
values = dataset.values
X, y = values[:, :-1], values[:, -1]
# create a list of models to evaluate
models, names = list(), list()
# logistic
models.append(LogisticRegression())
names.append('LR')
# knn
models.append(KNeighborsClassifier())
names.append('KNN')
# cart
models.append(DecisionTreeClassifier())
names.append('CART')
# svm
models.append(SVC())
names.append('SVM')
# random forest
models.append(RandomForestClassifier())
names.append('RF')
# gbm
models.append(GradientBoostingClassifier())
names.append('GBM')
# evaluate models
all_scores = list()
for i in range(len(models)):
# create a pipeline for the model
s = StandardScaler()
p = Pipeline(steps=[('s',s), ('m',models[i])])
scores = cross_val_score(p, X, y, scoring='accuracy', cv=5, n_jobs=-1)
all_scores.append(scores)
# summarize
m, s = mean(scores)*100, std(scores)*100
print('%s %.3f%% +/-%.3f' % (names[i], m, s))
# plot
pyplot.boxplot(all_scores, labels=names)
pyplot.show()
运行该示例打印每个算法的估计表现,包括超过 5 倍交叉验证的平均值和标准差。
结果表明 SVM 可能值得以 58%的准确度更详细地查看。
LR 55.285% +/-5.518
KNN 50.897% +/-5.310
CART 50.501% +/-10.922
SVM 58.551% +/-7.707
RF 50.442% +/-6.355
GBM 55.749% +/-5.423
结果也显示为显示分数分布的盒须图。
同样,SVM 似乎具有良好的平均表现和紧密的方差。
使用最近 19 次观察在 ES1 上进行采样检查算法
最后n使用填充进行观察
我们可以将每条迹线填充到固定长度。
这将提供在每个序列中包括更多先前n观察结果的灵活性。n的选择也必须与添加到较短序列的填充值的增加相平衡,这反过来可能对模型在这些序列上的表现产生负面影响。
我们可以通过将 0.0 值添加到每个变量序列的开头来填充每个序列,直到最大长度,例如,达到了 200 个时间步长。我们可以使用 pad()NumPy 函数来完成此操作。
from numpy import pad
...
# pad sequences
max_length = 200
seq = pad(seq, ((max_length-len(seq),0),(0,0)), 'constant', constant_values=(0.0))
具有填充支持的create_dataset()功能的更新版本如下。
我们将尝试 n = 25 以包括每个载体中每个序列中的 25 个最后观察结果。虽然您可能想要探索其他配置是否会带来更好的技能,但可以通过一些试验和错误找到此值。
# create a fixed 1d vector for each trace with output variable
def create_dataset(sequences, targets):
# create the transformed dataset
transformed = list()
n_vars, n_steps, max_length = 4, 25, 200
# process each trace in turn
for i in range(len(sequences)):
seq = sequences[i]
# pad sequences
seq = pad(seq, ((max_length-len(seq),0),(0,0)), 'constant', constant_values=(0.0))
vector = list()
# last n observations
for row in range(1, n_steps+1):
for col in range(n_vars):
vector.append(seq[-row, col])
# add output
vector.append(targets[i])
# store
transformed.append(vector)
# prepare array
transformed = array(transformed)
transformed = transformed.astype('float32')
return transformed
使用新功能再次运行脚本会创建更新的 CSV 文件。
ES1: (210, 101)
ES2 Train: (210, 101)
ES2 Test: (104, 101)
同样,重新运行数据上的采样检查脚本会导致 SVM 模型技能的小幅提升,并且还表明 KNN 可能值得进一步调查。
LR 54.344% +/-6.195
KNN 58.562% +/-4.456
CART 52.837% +/-7.650
SVM 59.515% +/-6.054
RF 50.396% +/-7.069
GBM 50.873% +/-5.416
KNN 和 SVM 的箱形图显示出良好的表现和相对紧密的标准偏差。
使用最近 25 次观察对 ES1 上的采样检查算法
我们可以更新点检查到网格搜索 KNN 算法的一组 k 值,看看是否可以通过一点调整进一步改进模型的技能。
下面列出了完整的示例。
# spot check for ES1
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
# load dataset
dataset = read_csv('es1.csv', header=None)
# split into inputs and outputs
values = dataset.values
X, y = values[:, :-1], values[:, -1]
# try a range of k values
all_scores, names = list(), list()
for k in range(1,22):
# evaluate
scaler = StandardScaler()
model = KNeighborsClassifier(n_neighbors=k)
pipeline = Pipeline(steps=[('s',scaler), ('m',model)])
names.append(str(k))
scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=5, n_jobs=-1)
all_scores.append(scores)
# summarize
m, s = mean(scores)*100, std(scores)*100
print('k=%d %.3f%% +/-%.3f' % (k, m, s))
# plot
pyplot.boxplot(all_scores, labels=names)
pyplot.show()
运行该示例打印精度的均值和标准差,k 值从 1 到 21。
我们可以看到 k = 7 导致最佳技能为 62.872%。
k=1 49.534% +/-4.407
k=2 49.489% +/-4.201
k=3 56.599% +/-6.923
k=4 55.660% +/-6.600
k=5 58.562% +/-4.456
k=6 59.991% +/-7.901
k=7 62.872% +/-8.261
k=8 59.538% +/-5.528
k=9 57.633% +/-4.723
k=10 59.074% +/-7.164
k=11 58.097% +/-7.583
k=12 58.097% +/-5.294
k=13 57.179% +/-5.101
k=14 57.644% +/-3.175
k=15 59.572% +/-5.481
k=16 59.038% +/-1.881
k=17 59.027% +/-2.981
k=18 60.490% +/-3.368
k=19 60.014% +/-2.497
k=20 58.562% +/-2.018
k=21 58.131% +/-3.084
k值的准确度得分的框和胡须图显示,k值约为 7,例如 5 和 6,也在数据集上产生稳定且表现良好的模型。
通过最后 25 次观察,对 ES1 上的 KNN 邻居进行抽查
在 ES2 上评估 KNN
现在我们已经了解了一个表示( n = 25 )和一个模型(KNN, k = 7 ),它们具有一定的随机预测技能,我们可以测试该方法在更难的 ES2 数据集上。
每个模型都在数据集 1 和 2 的组合上进行训练,然后在数据集 3 上进行评估。不使用 k 折交叉验证程序,因此我们希望得分是有噪声的。
下面列出了 ES2 算法的完整采样检查。
# spot check for ES2
from pandas import read_csv
from matplotlib import pyplot
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
# load dataset
train = read_csv('es2_train.csv', header=None)
test = read_csv('es2_test.csv', header=None)
# split into inputs and outputs
trainX, trainy = train.values[:, :-1], train.values[:, -1]
testX, testy = test.values[:, :-1], test.values[:, -1]
# create a list of models to evaluate
models, names = list(), list()
# logistic
models.append(LogisticRegression())
names.append('LR')
# knn
models.append(KNeighborsClassifier())
names.append('KNN')
# knn
models.append(KNeighborsClassifier(n_neighbors=7))
names.append('KNN-7')
# cart
models.append(DecisionTreeClassifier())
names.append('CART')
# svm
models.append(SVC())
names.append('SVM')
# random forest
models.append(RandomForestClassifier())
names.append('RF')
# gbm
models.append(GradientBoostingClassifier())
names.append('GBM')
# evaluate models
all_scores = list()
for i in range(len(models)):
# create a pipeline for the model
scaler = StandardScaler()
model = Pipeline(steps=[('s',scaler), ('m',models[i])])
# fit
# model = models[i]
model.fit(trainX, trainy)
# predict
yhat = model.predict(testX)
# evaluate
score = accuracy_score(testy, yhat) * 100
all_scores.append(score)
# summarize
print('%s %.3f%%' % (names[i], score))
# plot
pyplot.bar(names, all_scores)
pyplot.show()
运行该示例报告 ES2 方案的模型准确率。
我们可以看到 KNN 表现良好,并且发现在 ES1 上表现良好的七个邻居的 KNN 在 ES2 上也表现良好。
LR 45.192%
KNN 54.808%
KNN-7 57.692%
CART 53.846%
SVM 51.923%
RF 53.846%
GBM 52.885%
精度分数的条形图有助于使方法之间的表现相对差异更加清晰。
ES2 上模型准确率的条形图
所选择的表示和模型配置确实具有超过预测的技能,准确度为 50%。
进一步调整可能会使模型具有更好的技能,我们距离 ES1 和 ES2 分别报告的 95%和 89%准确度还有很长的路要走。
扩展
本节列出了一些扩展您可能希望探索的教程的想法。
- 数据准备。有很多机会可以探索更多的数据准备方法,例如归一化,差分和功率变换。
- 特色工程。进一步的特征工程可以产生更好的表现模型,例如每个序列的开始,中间和结束的统计以及趋势信息。
- 调整。只有 KNN 算法才有机会进行调整;梯度增强等其他模型可以从超参数的微调中受益。
- RNNs 。该序列分类任务似乎非常适合于循环神经网络,例如支持可变长度多变量输入的 LSTM。对该数据集进行的一些初步测试(由我自己)显示出非常不稳定的结果,但更广泛的调查可能会给出更好甚至更好的结果。
如果你探索任何这些扩展,我很想知道。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
文件
- 通过油藏计算预测用户在异质室内环境中的移动,2011。
- 环境辅助生活应用中储层计算的实验表征,2014。
API
用品
- 来自 RSS 数据集的室内用户移动预测,UCI 机器学习库
- 通过油藏计算预测非均质室内环境中的用户移动,Paolo Barsocchi 主页。
- 来自 RSS 数据集的室内用户移动预测,Laurae [法语]。
摘要
在本教程中,您发现了室内运动预测时间序列分类问题以及如何设计功能并评估问题的机器学习算法。
具体来说,你学到了:
- 基于传感器强度预测房间运动的时间序列分类问题。
- 如何调查数据以便更好地理解问题以及如何从原始数据中设计特征以做出预测性建模。
- 如何检查一套分类算法并调整一种算法以进一步提高问题的表现。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。