Machine-Learning-Mastery-深度学习时间序列教程-一-

68 阅读1小时+

Machine Learning Mastery 深度学习时间序列教程(一)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

如何开发用于人类活动识别的一维卷积神经网络模型

原文: machinelearningmastery.com/cnn-models-for-human-activity-recognition-time-series-classification/

人类活动识别是将由专用线束或智能电话记录的加速度计数据序列分类为已知的明确定义的运动的问题。

该问题的经典方法涉及基于固定大小的窗口和训练机器学习模型(例如决策树的集合)的时间序列数据中的手工制作特征。困难在于此功能工程需要该领域的深厚专业知识。

最近,已经证明,诸如循环神经网络和一维卷积神经网络(CNN)之类的深度学习方法可以在很少或没有数据特征工程的情况下提供具有挑战性的活动识别任务的最新结果,而不是使用特征学习原始数据。

在本教程中,您将了解如何开发一维卷积神经网络,用于人类活动识别问题的时间序列分类。

完成本教程后,您将了解:

  • 如何为标准人类活动识别数据集加载和准备数据,并开发单个 1D CNN 模型,以在原始数据上实现出色的表现。
  • 如何进一步调整模型的表现,包括数据转换,过滤器映射和内核大小。
  • 如何开发一种复杂的多头一维卷积神经网络模型,提供类似集合的结果。

让我们开始吧。

How to Develop 1D Convolutional Neural Network Models for Human Activity Recognition

如何开发用于人类活动识别的一维卷积神经网络模型 照片由 Wolfgang Staudt ,保留一些权利。

教程概述

本教程分为四个部分;他们是:

  1. 使用智能手机数据集进行活动识别
  2. 开发一维卷积神经网络
  3. 调谐一维卷积神经网络
  4. 多头一维卷积神经网络

使用智能手机数据集进行活动识别

人类活动识别,或简称为 HAR,是基于使用传感器的移动痕迹来预测人正在做什么的问题。

标准的人类活动识别数据集是 2012 年推出的“使用智能手机数据集的活动识别”。

它由 Davide Anguita 等人准备并提供。来自意大利热那亚大学的 2013 年论文“使用智能手机进行人类活动识别的公共领域数据集”中对该数据集进行了全面描述。该数据集在他们的 2012 年论文中用机器学习算法建模,标题为“使用多类硬件友好支持向量机在智能手机上进行人类活动识别。“

数据集可用,可以从 UCI 机器学习库免费下载:

该数据来自 30 名年龄在 19 至 48 岁之间的受试者,其执行六项标准活动中的一项,同时佩戴记录运动数据的腰部智能手机。记录执行活动的每个受试者的视频,并从这些视频手动标记移动数据。

以下是在记录其移动数据的同时执行活动的主体的示例视频。

<iframe allow="autoplay; encrypted-media" allowfullscreen="" frameborder="0" height="375" src="www.youtube.com/embed/XOEN9…" width="500"></iframe>

进行的六项活动如下:

  1. 步行
  2. 走上楼
  3. 走楼下
  4. 坐在
  5. 常设
  6. 铺设

记录的运动数据是来自智能手机的 x,y 和 z 加速度计数据(线性加速度)和陀螺仪数据(角速度),特别是三星 Galaxy S II。以 50Hz(即每秒 50 个数据点)记录观察结果。每个受试者进行两次活动,一次是左侧设备,另一次是右侧设备。

原始数据不可用。相反,可以使用预处理版本的数据集。预处理步骤包括:

  • 使用噪声滤波器预处理加速度计和陀螺仪。
  • 将数据拆分为 2.56 秒(128 个数据点)的固定窗口,重叠率为 50%。
  • 将加速度计数据分割为重力(总)和身体运动分量。

特征工程应用于窗口数据,并且提供具有这些工程特征的数据的副本。

从每个窗口提取在人类活动识别领域中常用的许多时间和频率特征。结果是 561 元素的特征向量。

根据受试者的数据,将数据集分成训练(70%)和测试(30%)组。训练 21 个,测试 9 个。

使用旨在用于智能手机的支持向量机(例如定点算术)的实验结果导致测试数据集的预测准确度为 89%,实现与未修改的 SVM 实现类似的结果。

该数据集是免费提供的,可以从 UCI 机器学习库下载。

数据以单个 zip 文件的形式提供,大小约为 58 兆字节。此下载的直接链接如下:

下载数据集并将所有文件解压缩到当前工作目录中名为“HARDataset”的新目录中。

开发一维卷积神经网络

在本节中,我们将为人类活动识别数据集开发一维卷积神经网络模型(1D CNN)。

卷积神经网络模型是为图像分类问题而开发的,其中模型在称为特征学习的过程中学习二维输入的内部表示。

可以在一维数据序列上利用相同的过程,例如在用于人类活动识别的加速和陀螺仪数据的情况下。该模型学习从观察序列中提取特征以及如何将内部特征映射到不同的活动类型。

使用 CNN 进行序列分类的好处是,他们可以直接从原始时间序列数据中学习,而不需要领域专业知识来手动设计输入功能。该模型可以学习时间序列数据的内部表示,并且理想地实现与适合具有工程特征的数据集版本的模型相当的表现。

本节分为 4 部分;他们是:

  1. 加载数据
  2. 拟合和评估模型
  3. 总结结果
  4. 完整的例子

加载数据

第一步是将原始数据集加载到内存中。

原始数据中有三种主要信号类型:总加速度,车身加速度和车身陀螺仪。每个都有三个数据轴。这意味着每个时间步长总共有九个变量。

此外,每个数据系列已被划分为 2.65 秒数据的重叠窗口,或 128 个时间步长。这些数据窗口对应于上一节中工程特征(行)的窗口。

这意味着一行数据具有(128 * 9)或 1,152 个元素。这比前一节中 561 个元素向量的大小小一倍,并且可能存在一些冗余数据。

信号存储在 train 和 test 子目录下的/ Inertial Signals /目录中。每个信号的每个轴都存储在一个单独的文件中,这意味着每个训练和测试数据集都有九个要加载的输入文件和一个要加载的输出文件。在给定一致的目录结构和文件命名约定的情况下,我们可以批量加载这些文件。

输入数据采用 CSV 格式,其中列由空格分隔。这些文件中的每一个都可以作为 NumPy 数组加载。下面的load_file()函数在给定文件的文件路径的情况下加载数据集,并将加载的数据作为 NumPy 数组返回。

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

然后,我们可以将给定组(训练或测试)的所有数据加载到单个三维 NumPy 数组中,其中数组的尺寸为[_ 样本,时间步长,特征 _]。

为了更清楚,有 128 个时间步和 9 个特征,其中样本数是任何给定原始信号数据文件中的行数。

下面的load_group()函数实现了这种行为。 dstack()NumPy 函数允许我们将每个加载的 3D 数组堆叠成单个 3D 数组,其中变量在第三维(特征)上分开。

# load a list of files into a 3D array of [samples, timesteps, features]
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

我们可以使用此功能加载给定组的所有输入信号数据,例如训练或测试。

下面的load_dataset_group()函数使用训练和测试目录之间的一致命名约定加载单个组的所有输入信号数据和输出数据。

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

最后,我们可以加载每个训练和测试数据集。

输出数据定义为类号的整数。我们必须对这些类整数进行热编码,以使数据适合于拟合神经网络多分类模型。我们可以通过调用 to_categorical()Keras 函数来实现。

下面的load_dataset()函数实现了这种行为,并返回训练并测试Xy元素,以便拟合和评估定义的模型。

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

拟合和评估模型

现在我们已将数据加载到内存中以便进行建模,我们可以定义,拟合和评估 1D CNN 模型。

我们可以定义一个名为evaluate_model()的函数,它接受训练和测试数据集,拟合训练数据集上的模型,在测试数据集上对其进行评估,并返回模型表现的估计值。

首先,我们必须使用 Keras 深度学习库来定义 CNN 模型。该模型需要使用[_ 样本,时间步长,特征 _]进行三维输入。

这正是我们加载数据的方式,其中一个样本是时间序列数据的一个窗口,每个窗口有 128 个时间步长,时间步长有九个变量或特征。

模型的输出将是一个六元素向量,包含属于六种活动类型中每种活动类型的给定窗口的概率。

在拟合模型时需要这些输入和输出维度,我们可以从提供的训练数据集中提取它们。

n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]

为简单起见,该模型被定义为顺序 Keras 模型。

我们将模型定义为具有两个 1D CNN 层,然后是用于正则化的丢失层,然后是池化层。通常以两个为一组定义 CNN 层,以便为模型提供从输入数据学习特征的良好机会。 CNN 非常快速地学习,因此 dropout 层旨在帮助减缓学习过程,并希望能够产生更好的最终模型。池化层将学习的特征减少到其大小的 1/4,将它们合并到最基本的元素。

在 CNN 和汇集之后,将学习的特征展平为一个长向量,并在用于做出预测的输出层之前通过完全连接的层。完全连接的层理想地在学习的特征和输出之间提供缓冲,目的是在做出预测之前解释学习的特征。

对于此模型,我们将使用 64 个并行要素图的标准配置和 3 的内核大小。要素图是处理或解释输入的次数,而内核大小是被视为输入时间步数的数量。输入序列被读取或处理到特征映射上。

随机梯度下降的有效 Adam 版本将用于优化网络,并且鉴于我们正在学习多类别分类问题,将使用分类交叉熵损失函数。

下面列出了该模型的定义。

model = Sequential()
model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(n_timesteps,n_features)))
model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
model.add(Dropout(0.5))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(100, activation='relu'))
model.add(Dense(n_outputs, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

该模型适用于固定数量的时期,在这种情况下为 10,并且将使用 32 个样本的批量大小,其中在更新模型的权重之前将 32 个数据窗口暴露给模型。

模型拟合后,将在测试数据集上进行评估,并返回测试数据集上拟合模型的精度。

下面列出了完整的evaluate_model()函数。

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
	model = Sequential()
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(n_timesteps,n_features)))
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
	model.add(Dropout(0.5))
	model.add(MaxPooling1D(pool_size=2))
	model.add(Flatten())
	model.add(Dense(100, activation='relu'))
	model.add(Dense(n_outputs, activation='softmax'))
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit(trainX, trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate(testX, testy, batch_size=batch_size, verbose=0)
	return accuracy

网络结构或选择的超参数没有什么特别之处;它们只是这个问题的起点。

总结结果

我们无法从单一评估中判断模型的技能。

其原因是神经网络是随机的,这意味着当在相同数据上训练相同的模型配置时将产生不同的特定模型。

这是网络的一个特征,它为模型提供了自适应能力,但需要对模型进行稍微复杂的评估。

我们将多次重复对模型的评估,然后在每次运行中总结模型的表现。例如,我们可以调用evaluate_model()共 10 次。这将导致必须总结的模型评估分数。

# repeat experiment
scores = list()
for r in range(repeats):
	score = evaluate_model(trainX, trainy, testX, testy)
	score = score * 100.0
	print('>#%d: %.3f' % (r+1, score))
	scores.append(score)

我们可以通过计算和报告绩效的均值和标准差来总结得分样本。均值给出了数据集上模型的平均精度,而标准差给出了精度与平均值的平均方差。

下面的函数summarize_results()总结了运行的结果。

# summarize scores
def summarize_results(scores):
	print(scores)
	m, s = mean(scores), std(scores)
	print('Accuracy: %.3f%% (+/-%.3f)' % (m, s))

我们可以将重复评估,结果收集和结果汇总捆绑到实验的主要功能中,称为 run_experiment(),如下所示。

默认情况下,在报告模型表现之前,会对模型进行 10 次评估。

# run an experiment
def run_experiment(repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# repeat experiment
	scores = list()
	for r in range(repeats):
		score = evaluate_model(trainX, trainy, testX, testy)
		score = score * 100.0
		print('>#%d: %.3f' % (r+1, score))
		scores.append(score)
	# summarize results
	summarize_results(scores)

完整的例子

现在我们已经拥有了所有的部分,我们可以将它们组合成一个有效的例子。

完整的代码清单如下。

# cnn model
from numpy import mean
from numpy import std
from numpy import dstack
from pandas import read_csv
from matplotlib import pyplot
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.utils import to_categorical

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files and return as a 3d numpy array
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
	model = Sequential()
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(n_timesteps,n_features)))
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
	model.add(Dropout(0.5))
	model.add(MaxPooling1D(pool_size=2))
	model.add(Flatten())
	model.add(Dense(100, activation='relu'))
	model.add(Dense(n_outputs, activation='softmax'))
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit(trainX, trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate(testX, testy, batch_size=batch_size, verbose=0)
	return accuracy

# summarize scores
def summarize_results(scores):
	print(scores)
	m, s = mean(scores), std(scores)
	print('Accuracy: %.3f%% (+/-%.3f)' % (m, s))

# run an experiment
def run_experiment(repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# repeat experiment
	scores = list()
	for r in range(repeats):
		score = evaluate_model(trainX, trainy, testX, testy)
		score = score * 100.0
		print('>#%d: %.3f' % (r+1, score))
		scores.append(score)
	# summarize results
	summarize_results(scores)

# run the experiment
run_experiment()

运行该示例首先打印已加载数据集的形状,然后打印训练和测试集的形状以及输入和输出元素。这确认了样本数,时间步长和变量,以及类的数量。

接下来,创建和评估模型,并为每个模型打印调试消息。

最后,打印分数样本,然后是平均值和标准差。我们可以看到该模型表现良好,实现了在原始数据集上训练的约 90.9%的分类准确度,标准偏差约为 1.3。

这是一个很好的结果,考虑到原始论文发表了 89%的结果,在具有重域特定特征工程的数据集上进行了训练,而不是原始数据集。

注意:鉴于算法的随机性,您的具体结果可能会有所不同。

(7352, 128, 9) (7352, 1)
(2947, 128, 9) (2947, 1)
(7352, 128, 9) (7352, 6) (2947, 128, 9) (2947, 6)

>#1: 91.347
>#2: 91.551
>#3: 90.804
>#4: 90.058
>#5: 89.752
>#6: 90.940
>#7: 91.347
>#8: 87.547
>#9: 92.637
>#10: 91.890

[91.34713267729894, 91.55072955548015, 90.80420766881574, 90.05768578215134, 89.75229046487954, 90.93993892093654, 91.34713267729894, 87.54665761791652, 92.63657957244655, 91.89005768578215]

Accuracy: 90.787% (+/-1.341)

现在我们已经看到了如何加载数据并适合 1D CNN 模型,我们可以研究是否可以通过一些超参数调整来进一步提升模型的技能。

调谐一维卷积神经网络

在本节中,我们将调整模型,以进一步提高问题的表现。

我们将看三个主要方面:

  1. 数据准备
  2. 过滤器数量
  3. 内核的大小

数据准备

在上一节中,我们没有执行任何数据准备。我们按原样使用了数据。

每个主要数据集(身体加速度,身体陀螺仪和总加速度)已经缩放到-1,1 的范围。不清楚数据是按受试者缩放还是跨所有受试者。

可能导致改进的一种可能的变换是在拟合模型之前标准化观察。

标准化是指改变每个变量的分布,使其平均值为零,标准差为 1.只有每个变量的分布是高斯分布才真正有意义。

我们可以通过绘制训练数据集中每个变量的直方图来快速检查每个变量的分布。

这方面的一个小难点是数据被分成 128 个时间步长的窗口,重叠 50%。因此,为了更好地了解数据分布,我们必须首先删除重复的观察(重叠),然后删除数据的窗口。

我们可以使用 NumPy 来做到这一点,首先切割数组并仅保留每个窗口的后半部分,然后将窗口展平为每个变量的长向量。这很快且很脏,并且意味着我们在第一个窗口的前半部分丢失了数据。

# remove overlap
cut = int(trainX.shape[1] / 2)
longX = trainX[:, -cut:, :]
# flatten windows
longX = longX.reshape((longX.shape[0] * longX.shape[1], longX.shape[2]))

下面列出了加载数据,展平数据以及为九个变量中的每一个绘制直方图的完整示例。

# plot distributions
from numpy import dstack
from pandas import read_csv
from keras.utils import to_categorical
from matplotlib import pyplot

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files and return as a 3d numpy array
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# plot a histogram of each variable in the dataset
def plot_variable_distributions(trainX):
	# remove overlap
	cut = int(trainX.shape[1] / 2)
	longX = trainX[:, -cut:, :]
	# flatten windows
	longX = longX.reshape((longX.shape[0] * longX.shape[1], longX.shape[2]))
	print(longX.shape)
	pyplot.figure()
	xaxis = None
	for i in range(longX.shape[1]):
		ax = pyplot.subplot(longX.shape[1], 1, i+1, sharex=xaxis)
		ax.set_xlim(-1, 1)
		if i == 0:
			xaxis = ax
		pyplot.hist(longX[:, i], bins=100)
	pyplot.show()

# load data
trainX, trainy, testX, testy = load_dataset()
# plot histograms
plot_variable_distributions(trainX)

运行该示例将创建一个包含九个直方图的图形,一个用于训练数据集中的每个变量。

图的顺序与加载数据的顺序相匹配,具体如下:

  1. 总加速度 x
  2. 总加速度 y
  3. 总加速度 z
  4. 身体加速 x
  5. 身体加速度 y
  6. 身体加速度 z
  7. 身体陀螺仪 x
  8. 身体陀螺仪 y
  9. Body Gyroscope z

我们可以看到每个变量都具有类似高斯分布,除了第一个变量(总加速度 x)。

总加速度数据的分布比身体数据更平坦,这更加尖锐。

我们可以探索使用数据的幂变换来使分布更加高斯,尽管这是一个练习。

Histograms of each variable in the training data set

训练数据集中每个变量的直方图

数据具有足够的高斯类似性,以探索标准化变换是否有助于模型从原始观测中提取显着信号。

名为scale_data()的以下函数可用于在拟合和评估模型之前标准化数据。 StandardScaler scikit-learn 类将用于执行转换。它首先适合训练数据(例如,找到每个变量的平均值和标准差),然后应用于训练和测试集。

标准化是可选的,因此我们可以应用该过程并将结果与​​相同的代码路径进行比较,而无需在对照试验中进行标准化。

# standardize data
def scale_data(trainX, testX, standardize):
	# remove overlap
	cut = int(trainX.shape[1] / 2)
	longX = trainX[:, -cut:, :]
	# flatten windows
	longX = longX.reshape((longX.shape[0] * longX.shape[1], longX.shape[2]))
	# flatten train and test
	flatTrainX = trainX.reshape((trainX.shape[0] * trainX.shape[1], trainX.shape[2]))
	flatTestX = testX.reshape((testX.shape[0] * testX.shape[1], testX.shape[2]))
	# standardize
	if standardize:
		s = StandardScaler()
		# fit on training data
		s.fit(longX)
		# apply to training and test data
		longX = s.transform(longX)
		flatTrainX = s.transform(flatTrainX)
		flatTestX = s.transform(flatTestX)
	# reshape
	flatTrainX = flatTrainX.reshape((trainX.shape))
	flatTestX = flatTestX.reshape((testX.shape))
	return flatTrainX, flatTestX

我们可以更新evaluate_model()函数来获取参数,然后使用此参数来决定是否执行标准化。

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy, param):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
	# scale data
	trainX, testX = scale_data(trainX, testX, param)
	model = Sequential()
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(n_timesteps,n_features)))
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
	model.add(Dropout(0.5))
	model.add(MaxPooling1D(pool_size=2))
	model.add(Flatten())
	model.add(Dense(100, activation='relu'))
	model.add(Dense(n_outputs, activation='softmax'))
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit(trainX, trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate(testX, testy, batch_size=batch_size, verbose=0)
	return accuracy

我们还可以更新 run_experiment(),为每个参数重复实验 10 次;在这种情况下,只有两个参数将被评估[ False,True ],分别没有标准化和标准化。

# run an experiment
def run_experiment(params, repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# test each parameter
	all_scores = list()
	for p in params:
		# repeat experiment
		scores = list()
		for r in range(repeats):
			score = evaluate_model(trainX, trainy, testX, testy, p)
			score = score * 100.0
			print('>p=%d #%d: %.3f' % (p, r+1, score))
			scores.append(score)
		all_scores.append(scores)
	# summarize results
	summarize_results(all_scores, params)

这将产生两个可以比较的结果样本。

我们将更新summarize_results()函数,以汇总每个配置参数的结果样本,并创建一个箱形图来比较每个结果样本。

# summarize scores
def summarize_results(scores, params):
	print(scores, params)
	# summarize mean and standard deviation
	for i in range(len(scores)):
		m, s = mean(scores[i]), std(scores[i])
		print('Param=%d: %.3f%% (+/-%.3f)' % (params[i], m, s))
	# boxplot of scores
	pyplot.boxplot(scores, labels=params)
	pyplot.savefig('exp_cnn_standardize.png')

这些更新将允许我们直接比较之前模型拟合的结果和数据集标准化后的模型拟合。

它也是一个通用的更改,允许我们在以下部分中评估和比较其他参数集的结果。

完整的代码清单如下。

# cnn model with standardization
from numpy import mean
from numpy import std
from numpy import dstack
from pandas import read_csv
from matplotlib import pyplot
from sklearn.preprocessing import StandardScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.utils import to_categorical

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files and return as a 3d numpy array
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# standardize data
def scale_data(trainX, testX, standardize):
	# remove overlap
	cut = int(trainX.shape[1] / 2)
	longX = trainX[:, -cut:, :]
	# flatten windows
	longX = longX.reshape((longX.shape[0] * longX.shape[1], longX.shape[2]))
	# flatten train and test
	flatTrainX = trainX.reshape((trainX.shape[0] * trainX.shape[1], trainX.shape[2]))
	flatTestX = testX.reshape((testX.shape[0] * testX.shape[1], testX.shape[2]))
	# standardize
	if standardize:
		s = StandardScaler()
		# fit on training data
		s.fit(longX)
		# apply to training and test data
		longX = s.transform(longX)
		flatTrainX = s.transform(flatTrainX)
		flatTestX = s.transform(flatTestX)
	# reshape
	flatTrainX = flatTrainX.reshape((trainX.shape))
	flatTestX = flatTestX.reshape((testX.shape))
	return flatTrainX, flatTestX

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy, param):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
	# scale data
	trainX, testX = scale_data(trainX, testX, param)
	model = Sequential()
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(n_timesteps,n_features)))
	model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
	model.add(Dropout(0.5))
	model.add(MaxPooling1D(pool_size=2))
	model.add(Flatten())
	model.add(Dense(100, activation='relu'))
	model.add(Dense(n_outputs, activation='softmax'))
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit(trainX, trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate(testX, testy, batch_size=batch_size, verbose=0)
	return accuracy

# summarize scores
def summarize_results(scores, params):
	print(scores, params)
	# summarize mean and standard deviation
	for i in range(len(scores)):
		m, s = mean(scores[i]), std(scores[i])
		print('Param=%s: %.3f%% (+/-%.3f)' % (params[i], m, s))
	# boxplot of scores
	pyplot.boxplot(scores, labels=params)
	pyplot.savefig('exp_cnn_standardize.png')

# run an experiment
def run_experiment(params, repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# test each parameter
	all_scores = list()
	for p in params:
		# repeat experiment
		scores = list()
		for r in range(repeats):
			score = evaluate_model(trainX, trainy, testX, testy, p)
			score = score * 100.0
			print('>p=%s #%d: %.3f' % (p, r+1, score))
			scores.append(score)
		all_scores.append(scores)
	# summarize results
	summarize_results(all_scores, params)

# run the experiment
n_params = [False, True]
run_experiment(n_params)

运行该示例可能需要几分钟,具体取决于您的硬件。

为每个评估的模型打印表现。在运行结束时,总结了每个测试配置的表现,显示了平均值和标准偏差。

我们可以看到,在建模之前看起来确实标准化数据集确实导致表现的小提升,从大约 90.4%的准确度(接近我们在上一节中看到的)到大约 91.5%的准确度。

注意:鉴于算法的随机性,您的具体结果可能会有所不同。

(7352, 128, 9) (7352, 1)
(2947, 128, 9) (2947, 1)
(7352, 128, 9) (7352, 6) (2947, 128, 9) (2947, 6)

>p=False #1: 91.483
>p=False #2: 91.245
>p=False #3: 90.838
>p=False #4: 89.243
>p=False #5: 90.193
>p=False #6: 90.465
>p=False #7: 90.397
>p=False #8: 90.567
>p=False #9: 88.938
>p=False #10: 91.144
>p=True #1: 92.908
>p=True #2: 90.940
>p=True #3: 92.297
>p=True #4: 91.822
>p=True #5: 92.094
>p=True #6: 91.313
>p=True #7: 91.653
>p=True #8: 89.141
>p=True #9: 91.110
>p=True #10: 91.890

[[91.48286392941975, 91.24533423820834, 90.83814048184594, 89.24329826942655, 90.19341703427214, 90.46487953851374, 90.39701391245333, 90.56667797760434, 88.93790295215473, 91.14353579911774], [92.90804207668816, 90.93993892093654, 92.29725144214456, 91.82219205972176, 92.09365456396336, 91.31319986426874, 91.65252799457076, 89.14149983033593, 91.10960298608755, 91.89005768578215]] [False, True]

Param=False: 90.451% (+/-0.785)
Param=True: 91.517% (+/-0.965)

还创建了结果的框和胡须图。

这允许以非参数方式比较两个结果样本,显示每个样本的中值和中间 50%。

我们可以看到,标准化的结果分布与没有标准化的结果分布完全不同。这可能是一个真正的效果。

Box and whisker plot of 1D CNN with and without standardization

有和没有标准化的 1D CNN 的盒子和须状图

过滤器数量

现在我们有了一个实验框架,我们可以探索不同的模型的其他超参数。

CNN 的一个重要超参数是滤波器映射的数量。我们可以尝试一系列不同的值,从比我们开发的第一个模型中使用的 64 个更少到更多。

具体来说,我们将尝试以下数量的功能图:

n_params = [8, 16, 32, 64, 128, 256]

我们可以使用上一节中的相同代码并更新evaluate_model()函数,以使用提供的参数作为 Conv1D 层中的过滤器数量。我们还可以更新summarize_results()函数,将箱图保存为exp_cnn_filters.png

完整的代码示例如下所示。

# cnn model with filters
from numpy import mean
from numpy import std
from numpy import dstack
from pandas import read_csv
from matplotlib import pyplot
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.utils import to_categorical

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files and return as a 3d numpy array
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy, n_filters):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
	model = Sequential()
	model.add(Conv1D(filters=n_filters, kernel_size=3, activation='relu', input_shape=(n_timesteps,n_features)))
	model.add(Conv1D(filters=n_filters, kernel_size=3, activation='relu'))
	model.add(Dropout(0.5))
	model.add(MaxPooling1D(pool_size=2))
	model.add(Flatten())
	model.add(Dense(100, activation='relu'))
	model.add(Dense(n_outputs, activation='softmax'))
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit(trainX, trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate(testX, testy, batch_size=batch_size, verbose=0)
	return accuracy

# summarize scores
def summarize_results(scores, params):
	print(scores, params)
	# summarize mean and standard deviation
	for i in range(len(scores)):
		m, s = mean(scores[i]), std(scores[i])
		print('Param=%d: %.3f%% (+/-%.3f)' % (params[i], m, s))
	# boxplot of scores
	pyplot.boxplot(scores, labels=params)
	pyplot.savefig('exp_cnn_filters.png')

# run an experiment
def run_experiment(params, repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# test each parameter
	all_scores = list()
	for p in params:
		# repeat experiment
		scores = list()
		for r in range(repeats):
			score = evaluate_model(trainX, trainy, testX, testy, p)
			score = score * 100.0
			print('>p=%d #%d: %.3f' % (p, r+1, score))
			scores.append(score)
		all_scores.append(scores)
	# summarize results
	summarize_results(all_scores, params)

# run the experiment
n_params = [8, 16, 32, 64, 128, 256]
run_experiment(n_params)

运行该示例将为每个指定数量的过滤器重复实验。

在运行结束时,将显示每个过滤器数量的结果摘要。

随着滤波器图数量的增加,我们可以看到平均表现提升的趋势。方差保持不变,可能 128 个特征映射可能是网络的良好配置。

...
Param=8: 89.148% (+/-0.790)
Param=16: 90.383% (+/-0.613)
Param=32: 90.356% (+/-1.039)
Param=64: 90.098% (+/-0.615)
Param=128: 91.032% (+/-0.702)
Param=256: 90.706% (+/-0.997)

还创建了结果的框和胡须图,允许比较每个过滤器数量的结果。

从图中可以看出,随着特征图数量的增加,中值分类精度(框中的橙色线)的趋势向上。我们确实看到了 64 个特征图(我们的实验中的默认值或基线)的下降,这是令人惊讶的,并且可能在 32,128 和 256 个过滤器图中具有稳定性的平台。也许 32 将是一个更稳定的配置。

Box and whisker plot of 1D CNN with different numbers of filter maps

具有不同数量的滤波器映射的 1D CNN 的盒子和须状图

内核的大小

内核的大小是 1D CNN 调整的另一个重要的超参数。

内核大小控制输入序列的每个“_ 读取 _”中考虑的时间步数,然后将其投影到特征图上(通过卷积过程)。

较大的内核大小意味着不太严格的数据读取,但可能会导致输入的更通用的快照。

除了默认的三个时间步长之外,我们可以使用相同的实验设置并测试一套不同的内核大小。完整的值列表如下:

n_params = [2, 3, 5, 7, 11]

完整的代码清单如下:

# cnn model vary kernel size
from numpy import mean
from numpy import std
from numpy import dstack
from pandas import read_csv
from matplotlib import pyplot
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.utils import to_categorical

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files and return as a 3d numpy array
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy, n_kernel):
	verbose, epochs, batch_size = 0, 15, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
	model = Sequential()
	model.add(Conv1D(filters=64, kernel_size=n_kernel, activation='relu', input_shape=(n_timesteps,n_features)))
	model.add(Conv1D(filters=64, kernel_size=n_kernel, activation='relu'))
	model.add(Dropout(0.5))
	model.add(MaxPooling1D(pool_size=2))
	model.add(Flatten())
	model.add(Dense(100, activation='relu'))
	model.add(Dense(n_outputs, activation='softmax'))
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit(trainX, trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate(testX, testy, batch_size=batch_size, verbose=0)
	return accuracy

# summarize scores
def summarize_results(scores, params):
	print(scores, params)
	# summarize mean and standard deviation
	for i in range(len(scores)):
		m, s = mean(scores[i]), std(scores[i])
		print('Param=%d: %.3f%% (+/-%.3f)' % (params[i], m, s))
	# boxplot of scores
	pyplot.boxplot(scores, labels=params)
	pyplot.savefig('exp_cnn_kernel.png')

# run an experiment
def run_experiment(params, repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# test each parameter
	all_scores = list()
	for p in params:
		# repeat experiment
		scores = list()
		for r in range(repeats):
			score = evaluate_model(trainX, trainy, testX, testy, p)
			score = score * 100.0
			print('>p=%d #%d: %.3f' % (p, r+1, score))
			scores.append(score)
		all_scores.append(scores)
	# summarize results
	summarize_results(all_scores, params)

# run the experiment
n_params = [2, 3, 5, 7, 11]
run_experiment(n_params)

运行该示例依次测试每个内核大小。

结果总结在运行结束时。随着内核大小的增加,我们可以看到模型表现的普遍提高。

结果表明,籽粒大小为 5 可能是好的,平均技能为约 91.8%,但也许大小为 7 或 11 也可能同样好,标准偏差较小。

...
Param=2: 90.176% (+/-0.724)
Param=3: 90.275% (+/-1.277)
Param=5: 91.853% (+/-1.249)
Param=7: 91.347% (+/-0.852)
Param=11: 91.456% (+/-0.743)

还创建了结果的框和胡须图。

结果表明,较大的内核大小确实会产生更好的准确率,并且内核大小 7 可能在良好表现和低方差之间提供了良好的平衡。

Box and whisker plot of 1D CNN with different numbers of kernel sizes

具有不同内核大小的 1D CNN 的盒子和须状图

这只是调整模型的开始,尽管我们关注的可能是更重要的元素。探索上述某些发现的组合可能会很有趣,看看表现是否可以进一步提升。

将重复次数从 10 增加到 30 或更多以确定它是否会导致更稳定的结果也可能是有趣的。

多头卷积神经网络

CNN 的另一种流行方法是使用多头模型,其中模型的每个头使用不同大小的内核读取输入时间步长。

例如,三头模型可以具有三种不同的内核大小 3,5,11,允许模型以三种不同的分辨率读取和解释序列数据。然后,在做出预测之前,来自所有三个头的解释在模型内连接并由完全连接的层解释。

我们可以使用 Keras 功能 API 实现多头 1D CNN。有关此 API 的温和介绍,请参阅帖子:

下面列出了evaluate_model()函数的更新版本,它创建了一个三头 CNN 模型。

我们可以看到模型的每个头部都是相同的结构,尽管内核大小是变化的。然后,在做出预测之前,三个头在被解释之前进入单个合并层。

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
 	# head 1
	inputs1 = Input(shape=(n_timesteps,n_features))
	conv1 = Conv1D(filters=64, kernel_size=3, activation='relu')(inputs1)
	drop1 = Dropout(0.5)(conv1)
	pool1 = MaxPooling1D(pool_size=2)(drop1)
	flat1 = Flatten()(pool1)
	# head 2
	inputs2 = Input(shape=(n_timesteps,n_features))
	conv2 = Conv1D(filters=64, kernel_size=5, activation='relu')(inputs2)
	drop2 = Dropout(0.5)(conv2)
	pool2 = MaxPooling1D(pool_size=2)(drop2)
	flat2 = Flatten()(pool2)
	# head 3
	inputs3 = Input(shape=(n_timesteps,n_features))
	conv3 = Conv1D(filters=64, kernel_size=11, activation='relu')(inputs3)
	drop3 = Dropout(0.5)(conv3)
	pool3 = MaxPooling1D(pool_size=2)(drop3)
	flat3 = Flatten()(pool3)
	# merge
	merged = concatenate([flat1, flat2, flat3])
	# interpretation
	dense1 = Dense(100, activation='relu')(merged)
	outputs = Dense(n_outputs, activation='softmax')(dense1)
	model = Model(inputs=[inputs1, inputs2, inputs3], outputs=outputs)
	# save a plot of the model
	plot_model(model, show_shapes=True, to_file='multichannel.png')
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit([trainX,trainX,trainX], trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate([testX,testX,testX], testy, batch_size=batch_size, verbose=0)
	return accuracy

创建模型时,会创建网络架构图;如下所示,它清楚地说明了构建的模型如何组合在一起。

Plot of the Multi-Headed 1D Convolutional Neural Network

多头一维卷积神经网络的图

模型的其他方面可以在各个方面变化,例如过滤器的数量或甚至数据本身的准备。

下面列出了多头 1D CNN 的完整代码示例。

# multi-headed cnn model
from numpy import mean
from numpy import std
from numpy import dstack
from pandas import read_csv
from matplotlib import pyplot
from keras.utils import to_categorical
from keras.utils.vis_utils import plot_model
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.layers.merge import concatenate

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files and return as a 3d numpy array
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# zero-offset class values
	trainy = trainy - 1
	testy = testy - 1
	# one hot encode y
	trainy = to_categorical(trainy)
	testy = to_categorical(testy)
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# fit and evaluate a model
def evaluate_model(trainX, trainy, testX, testy):
	verbose, epochs, batch_size = 0, 10, 32
	n_timesteps, n_features, n_outputs = trainX.shape[1], trainX.shape[2], trainy.shape[1]
 	# head 1
	inputs1 = Input(shape=(n_timesteps,n_features))
	conv1 = Conv1D(filters=64, kernel_size=3, activation='relu')(inputs1)
	drop1 = Dropout(0.5)(conv1)
	pool1 = MaxPooling1D(pool_size=2)(drop1)
	flat1 = Flatten()(pool1)
	# head 2
	inputs2 = Input(shape=(n_timesteps,n_features))
	conv2 = Conv1D(filters=64, kernel_size=5, activation='relu')(inputs2)
	drop2 = Dropout(0.5)(conv2)
	pool2 = MaxPooling1D(pool_size=2)(drop2)
	flat2 = Flatten()(pool2)
	# head 3
	inputs3 = Input(shape=(n_timesteps,n_features))
	conv3 = Conv1D(filters=64, kernel_size=11, activation='relu')(inputs3)
	drop3 = Dropout(0.5)(conv3)
	pool3 = MaxPooling1D(pool_size=2)(drop3)
	flat3 = Flatten()(pool3)
	# merge
	merged = concatenate([flat1, flat2, flat3])
	# interpretation
	dense1 = Dense(100, activation='relu')(merged)
	outputs = Dense(n_outputs, activation='softmax')(dense1)
	model = Model(inputs=[inputs1, inputs2, inputs3], outputs=outputs)
	# save a plot of the model
	plot_model(model, show_shapes=True, to_file='multichannel.png')
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit network
	model.fit([trainX,trainX,trainX], trainy, epochs=epochs, batch_size=batch_size, verbose=verbose)
	# evaluate model
	_, accuracy = model.evaluate([testX,testX,testX], testy, batch_size=batch_size, verbose=0)
	return accuracy

# summarize scores
def summarize_results(scores):
	print(scores)
	m, s = mean(scores), std(scores)
	print('Accuracy: %.3f%% (+/-%.3f)' % (m, s))

# run an experiment
def run_experiment(repeats=10):
	# load data
	trainX, trainy, testX, testy = load_dataset()
	# repeat experiment
	scores = list()
	for r in range(repeats):
		score = evaluate_model(trainX, trainy, testX, testy)
		score = score * 100.0
		print('>#%d: %.3f' % (r+1, score))
		scores.append(score)
	# summarize results
	summarize_results(scores)

# run the experiment
run_experiment()

运行该示例在每次重复实验时打印模型的表现,然后将估计得分总结为平均值和标准差,正如我们在第一种情况下使用简单的 1D CNN 所做的那样。

我们可以看到该模型的平均表现约为 91.6%的分类精度,标准偏差约为 0.8。

该示例可以用作探索各种其他模型的基础,这些模型改变不同模型超参数甚至跨输入头的不同数据准备方案。

考虑到该模型中资源的相对三倍,将此结果与单头 CNN 进行比较并不是一个苹果对苹果的比较。也许苹果与苹果的比较将是具有相同架构的模型,并且在模型的每个输入头上具有相同数量的过滤器。

>#1: 91.788
>#2: 92.942
>#3: 91.551
>#4: 91.415
>#5: 90.974
>#6: 91.992
>#7: 92.162
>#8: 89.888
>#9: 92.671
>#10: 91.415

[91.78825924669155, 92.94197488971835, 91.55072955548015, 91.41499830335935, 90.97387173396675, 91.99185612487275, 92.16152019002375, 89.88802171700034, 92.67051238547675, 91.41499830335935]

Accuracy: 91.680% (+/-0.823)

扩展

本节列出了一些扩展您可能希望探索的教程的想法。

  • 日期准备。探索其他数据准备方案,例如数据标准化以及标准化后的标准化。
  • 网络架构。探索其他网络架构,例如更深入的 CNN 架构和更深层的全连接层,用于解释 CNN 输入功能。
  • 诊断。使用简单的学习曲线诊断来解释模型在时期中的学习方式,以及更多的正则化,不同的学习率,或不同的批量大小或时期数可能导致更好的表现或更稳定的模型。

如果你探索任何这些扩展,我很想知道。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

文件

用品

摘要

在本教程中,您了解了如何开发一维卷积神经网络,用于人类活动识别问题的时间序列分类。

具体来说,你学到了:

  • 如何为标准人类活动识别数据集加载和准备数据,并开发单个 1D CNN 模型,以在原始数据上实现出色的表现。
  • 如何进一步调整模型的表现,包括数据转换,过滤器映射和内核大小。
  • 如何开发一种复杂的多头一维卷积神经网络模型,提供类似集合的结果。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

用于人类活动识别的深度学习模型

原文: machinelearningmastery.com/deep-learning-models-for-human-activity-recognition/

人类活动识别(HAR)是具有挑战性的时间序列分类任务。

它涉及基于传感器数据预测人的移动,并且传统上涉及深度领域专业知识和来自信号处理的方法,以正确地设计来自原始数据的特征以适合机器学习模型。

最近,诸如卷积神经网络和循环神经网络的深度学习方法已经显示出通过自动学习原始传感器数据的特征而能够甚至实现最先进的结果。

在这篇文章中,您将发现人类活动识别的问题以及在这个问题上实现最先进表现的深度学习方法。

阅读这篇文章后,你会知道:

  • 活动识别是基于传感器数据(例如智能手机中的加速度计)预测人(通常在室内)的移动的问题。
  • 传感器数据流通常被分成称为窗口的子序列,并且每个窗口与更广泛的活动相关联,称为滑动窗口方法。
  • 卷积神经网络和长期短期记忆网络,或许两者结合在一起,最适合从原始传感器数据中学习特征并预测相关的运动。

让我们开始吧。

Deep Learning Models for Human Activity Recognition

用于人类活动识别的深度学习模型 照片由 Simon Harrod 拍摄,保留一些权利。

概观

这篇文章分为五个部分;他们是:

  1. 人类活动识别
  2. 神经网络建模的好处
  3. 监督学习数据表示
  4. 卷积神经网络模型
  5. 循环神经网络模型

人类活动识别

人类活动识别(简称 HAR)是涉及基于传感器数据识别人的特定运动或动作的广泛研究领域。

运动通常是在室内进行的典型活动,例如步行,说话,站立和坐着。它们也可能是更集中的活动,例如在厨房或工厂车间进行的那些类型的活动。

可以远程记录传感器数据,例如视频,雷达或其他无线方法。或者,可以将数据直接记录在对象上,例如通过携带具有加速度计和陀螺仪的定制硬件或智能电话。

基于传感器的活动识别从大量低水平传感器读数中寻找关于人类活动的深刻的高级知识

从历史上看,用于活动识别的传感器数据具有挑战性且收集成本高,需要定制硬件。现在,用于健身和健康监测的智能手机和其他个人跟踪设备便宜且无处不在。因此,来自这些设备的传感器数据收集起来更便宜,更常见,因此是一般活动识别问题的更常研究的版本。

问题是在给定传感器数据快照的情况下预测活动,通常是来自一个或少数传感器类型的数据。通常,该问题被构造为单变量或多变量时间序列分类任务。

这是一个具有挑战性的问题,因为没有明显或直接的方式将记录的传感器数据与特定的人类活动相关联,并且每个受试者可能执行具有显着变化的活动,导致所记录的传感器数据的变化。

目的是记录特定受试者的传感器数据和相应活动,根据该数据拟合模型,并推广模型以根据传感器数据对新看不见的受试者的活动进行分类。

神经网络建模的好处

传统上,来自信号处理领域的方法用于分析和提取收集的传感器数据。

这些方法用于特征工程,创建特定于域的,特定于传感器或特定于信号处理的特征以及原始数据的视图。然后对处理后的数据版本训练统计和机器学习模型。

此方法的局限性在于分析原始数据和设计拟合模型所需的功能所需的信号处理和领域专业知识。每个新数据集或传感器模态都需要这种专业知识。实质上,它昂贵且不可扩展。

然而,在大多数日常 HAR 任务中,这些方法可能严重依赖于启发式手工特征提取,其通常受到人类领域知识的限制。此外,这些方法只能学习浅层特征,导致无监督和增量任务的表现下降。由于这些限制,传统[模式识别]方法的表现在分类准确率和模型概括方面受到限制。

理想情况下,可以使用学习方法自动学习直接从原始数据进行准确预测所需的功能。这将允许快速且廉价地采用新问题,新数据集和新传感器模态。

最近,深度神经网络模型已经开始实现其特征学习的承诺,并且正在实现人类活动识别的最新结果。它们能够从原始传感器数据执行自动功能学习,并且能够在手工制作的特定于域的功能上执行优秀的模型。

[...],特征提取和模型构建过程通常在深度学习模型中同时执行。这些功能可以通过网络自动学习,而不是手动设计。此外,深层神经网络还可以提取深层的高层表示,使其更适合于复杂的活动识别任务。

神经网络有两种主要方法适用于时间序列分类,并且已经证明使用来自商品智能手机和健身追踪设备的传感器数据在活动识别方面表现良好。

它们是卷积神经网络模型和循环神经网络模型。

建议 RNN 和 LSTM 识别具有自然顺序的短活动,而 CNN 更好地推断长期重复活动。原因是 RNN 可以利用传感器读数之间的时间顺序关系,CNN 更能够学习递归模式中包含的深层特征。

监督学习数据表示

在我们深入研究可用于人类活动识别的特定神经网络之前,我们需要讨论数据准备。

适用于时间序列分类的两种类型的神经网络都需要以特定方式准备数据以便适合模型。也就是说,在 _ 无监督学习 _'方式中,允许模型将信号数据与活动类相关联。

一种直接的数据准备方法,既可用于手工制作特征的经典机器学习方法,也可用于神经网络,包括将输入信号数据划分为信号窗口,其中给定窗口可能有一到几秒的观察时间数据。这通常被称为'_ 滑动窗口 _。

人类活动识别旨在从传感器捕获的一组观察中推断出一个或多个人的行为。通常,这是通过遵循用于特征提取的固定长度滑动窗口方法来执行的,其中必须修复两个参数:窗口的大小和移位。

每个窗口还与特定活动相关联。给定的数据窗口可以具有多个变量,例如加速度计传感器的 x,y 和 z 轴。

让我们以一个例子来具体化。

我们有 10 分钟的传感器数据;可能看起来像:

x, 		y, 		z, 		activity
1.1,	2.1,	0.1,	1
1.2,	2.2,	0.2,	1
1.3,	2.3,	0.3,	1
...

如果数据以 8 Hz 记录,则意味着执行活动所用的一秒钟将有八行数据。

我们可以选择让一个数据窗口代表一秒钟的数据;这意味着 8 Hz 传感器有 8 行数据。如果我们有 x,y 和 z 数据,那意味着我们将有 3 个变量。因此,单个数据窗口将是具有八个时间步长和三个特征的二维数组。

一个窗口代表一个样本。一分钟的数据代表 480 个传感器数据点,或 60 个 8 个时间步长的窗口。总共 10 分钟的数据将代表 4,800 个数据点,或 600 个数据窗口。

可以方便地根据样本或窗口的数量,窗口中的时间步数以及在每个时间步骤观察到的特征的数量来描述我们准备的传感器数据的形状。

[samples, time steps, features]

我们以 8 Hz 记录的 10 分钟加速度计数据的示例将被概括为具有以下尺寸的三维数组:

[600, 8, 3]

没有最佳窗口大小,它实际上取决于所使用的特定模型,收集的传感器数据的性质以及被分类的活动。

窗户的大小和模型的大小都有张力。较大的窗户需要较大的模型,较慢的训练,而较小的窗户需要较小的模型,更容易适应。

直观地,减小窗口大小允许更快的活动检测,以及减少的资源和能量需求。相反,通常考虑使用大数据窗口来识别复杂的活动

然而,通常使用一到两秒的传感器数据以便对活动的当前片段进行分类。

从结果来看,减少的窗口(2 秒或更短)被证明可以提供最准确的检测表现。事实上,对于非常短的窗口(0.25-0.5 秒),可获得最精确的识别器,从而可以完美识别大多数活动。与通常认为的相反,本研究表明,大窗口尺寸不一定转化为更好的识别表现。

将传感器数据流分成窗口可能会导致窗口错过一个活动到另一个活动的转换。因此,传统上将数据分成具有重叠的窗口是常见的,使得窗口的前半部分包含来自前一窗口的后半部分的观察,在 50%重叠的情况下。

[...]不正确的长度可能会截断活动实例。在许多情况下,当窗口与一个活动的结尾和下一个活动的开头重叠时,错误会出现在活动的开头或结尾。在其他情况下,窗口长度可能太短而不能为识别过程提供最佳信息。

目前还不清楚给定问题是否需要具有重叠的窗口。

在采用神经网络模型时,使用重叠(例如 50%重叠)将使训练数据的大小加倍,这可能有助于建模较小的数据集,但也可能导致过拟合训练数据集的模型。

对于某些应用,允许相邻窗口之间的重叠;但是,这种使用频率较低。

卷积神经网络模型

卷积神经网络模型(简称 CNN)是一种深度神经网络,它被开发用于图像数据,例如,如手写识别。

事实证明,它们在大规模训练时可以有效地挑战计算机视觉问题,例如识别和定位图像中的对象以及自动描述图像内容。

它们是由两种主要类型的元素组成的模型:卷积层和池化层。

卷积层使用内核读取输入,例如 2D 图像或 1D 信号,该内核一次读取小段并跨越整个输入字段。每次读取都会生成投影到滤镜图上的输入,并表示输入的内部解释。

池化层采用特征图投影并将其提取到最基本的元素,例如使用信号平均或信号最大化过程。

卷积和合并层可以在深度重复,提供输入信号的多层抽象。

这些网络的输出通常是一个或多个完全连接的层,用于解释已读取的内容并将此内部表示映射到类值。

有关卷积神经网络的更多信息,可以看到帖子:

CNN 可以应用于人类活动识别数据。

CNN 模型学习将给定的信号数据窗口映射到模型读取每个数据窗口的活动,并准备窗口的内部表示。

当应用于像 HAR 这样的时间序列分类时,CNN 比其他模型具有两个优点:局部依赖性和尺度不变性。局部依赖性意味着 HAR 中的附近信号可能是相关的,而尺度不变性是指不同步幅或频率的尺度不变。

使用 CNN 进行 HAR 的第一项重要工作是 Ming Zeng 等人在他们的 2014 年论文“使用移动传感器进行人类活动识别的 Co nvolutional 神经网络”。

在本文中,作者为加速度计数据开发了一个简单的 CNN 模型,其中加速度计数据的每个轴被馈送到单独的卷积层,汇集层,然后在被隐藏的完全连接层解释之前连接。

从纸上得到的图清楚地显示了模型的拓扑结构。它提供了一个很好的模板,用于 CNN 如何用于 HAR 问题和一般的时间序列分类。

Depiction of CNN Model for Accelerompter Data

用于加速度计数据的 CNN 模型的描述 取自“使用移动传感器进行人类活动识别的卷积神经网络”

有许多方法可以模拟 CNN 的 HAR 问题。

一个有趣的例子是 Heeryon Cho 和 Sang Min Yoon 在他们的 2018 年论文题为“基于划分和征服的 1N CNN 人类活动识别使用测试数据锐化”。

在其中,他们将活动分为涉及运动的那些,称为“_ 动态 ”,以及那些主体静止的,称为“ 静态 ”,然后开发 CNN 模型来区分这两个主要类别。然后,在每个类中,开发模型以区分该类型的活动,例如动态的“ 行走 ”和静态的“ 坐 _”。

Separation of Activities as Dynamic or Static

将活动分离为动态或静态 取自“基于划分和征服的 1N CNN 人类活动识别使用测试数据锐化”

他们将此称为两阶段建模方法。

我们不是直接使用单个 6 级分类器识别单个活动,而是应用分而治之的方法并构建一个两阶段活动识别过程,其中首先使用 2-来识别抽象活动,即动态和静态活动。类或二分类器,然后使用两个 3 级分类器识别单个活动。

开发了相当大的 CNN 模型,这反过来又使作者能够在具有挑战性的标准人类活动识别数据集上获得最先进的结果。

另一个有趣的方法是由 Wenchao Jiang 和 Zhaozheng Yin 在其 2015 年题为“深度卷积神经网络使用可穿戴传感器的人类活动识别”的论文中提出的。

它们不是在信号数据上使用 1D CNN,而是将信号数据组合在一起以创建“_ 图像 _”,然后将其馈送到 2D CNN 并作为图像数据处理,其中信号的时间轴上有卷积,跨信号变量,特别是加速度计和陀螺仪数据。

首先,将原始信号逐行堆叠成信号图像[...]。在信号图像中,每个信号序列都有机会与每个其他序列相邻,这使得 DCNN 能够提取相邻信号之间的隐藏相关性。然后,将 2D 离散傅里叶变换(DFT)应用于信号图像,并选择其幅度作为我们的活动图像

下面描述了将原始传感器数据处理成图像,然后从图像处理成“_ 活动图像 _”,即离散傅立叶变换的结果。

Processing of Raw Sensor Data into an Image

将原始传感器数据处理成图像 取自“通过深度卷积神经网络使用可穿戴传感器的人类活动识别”

最后,关于这一主题的另一篇优秀论文是 Charissa Ann Ronao 和 Sung-Bae Cho 在 2016 年题为“使用深度学习神经网络的智能手机传感器进行人类活动识别”。

仔细研究 CNN 的使用表明,较大的内核大小的信号数据是有用的并且有限的汇集。

实验表明,尽管每个附加层的特征复杂度水平差异减小,但是对于每个附加层,回归实际上都会得到相关且更复杂的特征。可以利用更宽的时间局部相关时间跨度(1×9-1×14),并且低池化大小(1×2 -1×3)被证明是有益的。

有用的是,它们还为 CNN 模型提供了完整的超参数配置,可以为新的 HAR 和其他序列分类问题提供有用的起点,总结如下。

Table of CNN Model Hyperparameter Configuration

CNN 模型超参数配置表 取自“使用深度学习神经网络的智能手机传感器识别人体活动”。

循环神经网络模型

循环神经网络(简称 RNN)是一种神经网络,旨在从序列数据中学习,例如随时间观察的序列,或句子中的单词序列。

称为长短期记忆网络(或简称 LSTM)的特定类型的 RNN 可能是最广泛使用的 RNN,因为其精心设计克服了在序列数据上训练稳定的 RNN 的一般困难。

LSTM 在大规模训练手写识别,语言建模和机器翻译等任务时已证明对挑战序列预测问题有效。

LSTM 模型中的一个层由特殊单元组成,这些单元具有控制输入,输出和循环连接的门,其权重是学习的。每个 LSTM 单元还具有内部存储器或状态,当读取输入序列时,该内部存储器或状态被累积,并且可以由网络用作一种局部变量或存储器寄存器。

有关长期短期记忆网络的更多信息,请参阅帖子:

与可以读取输入序列的 CNN 一样,LSTM 读取输入观察序列并开发其自己的输入序列的内部表示。与 CNN 不同,LSTM 的训练方式应特别注意观察结果和输入序列中时间步长的预测误差,称为反向传播。

有关随时间反向传播的更多信息,请参阅帖子:

LSTM 可以应用于人类活动识别的问题。

LSTM 学习将传感器数据的每个窗口映射到活动,其中一次一个地读取输入序列中的观察,其中每个时间步骤可以包括一个或多个变量(例如,并行序列)。

简单的 LSTM 模型在 HAR 问题上的应用有限。

其中一个例子是 Abdulmajid Murad 和 Jae-Young Pyun 2017 年题为“用于人类活动识别的深度循环神经网络”的论文。

重要的是,在论文中,他们评论了 CNN 在传感器数据的固定大小窗口上操作的要求的限制,这是 LSTM 并不严格限制的。

但是,卷积内核的大小限制了捕获的数据样本之间的依赖关系范围。因此,典型的模型不适用于各种活动识别配置,并且需要固定长度的输入窗口。

他们探讨了 LSTM 的使用,它们既向前(正常)又向两个方向处理序列数据(双向 LSTM)。有趣的是,LSTM 预测传感器数据的子序列的每个输入时间步的活动,然后聚合这些活动以便预测窗口的活动。

每个时间步骤将有一个分数来预测在时间 t 发生的活动类型。通过将各个得分合并为单个预测来获得对整个窗口 T 的预测

下面的图表提供了 LSTM 模型的描述,后面是完全连接的层,用于解释原始传感器数据的内部表示。

Depiction of LSTM RNN for Activity Recognition

用于活动识别的 LSTM RNN 的描述 取自“用于人类活动识别的深度循环神经网络”。

在 CNN-LSTM 模型或 ConvLSTM 模型中,在 HAR 问题上结合 CNN 使用 LSTM 可能更常见。

这是使用 CNN 模型从原始样本数据的子序列中提取特征的地方,然后由 LSTM 聚合解释来自 CNN 的每个子序列的输出特征。

这方面的一个例子是 Francisco Javier Ordonez 和 Daniel Roggen 的 2016 年论文题为“用于多模式可穿戴活动识别的深度卷积和 LSTM 循环神经网络”。

我们为可穿戴活动识别引入了一个新的 DNN 框架,我们将其称为 DeepConvLSTM。该架构结合了卷积层和循环层。卷积层充当特征提取器,并在特征映射中提供输入传感器数据的抽象表示。循环层模拟特征图激活的时间动态。

深度网络架构与四个卷积层一起使用,没有任何池化层,接着是两个 LSTM 层,用于在多个时间步骤中解释提取的特征。

作者声称,移除池化层是其模型架构的关键部分,其中在卷积层之后使用池化层会干扰卷积层学习对原始传感器数据进行下采样的能力。

在文献中,CNN 框架通常连续地包括卷积和汇集层,作为降低数据复杂性和引入平移不变特征的措施。然而,这种方法并不是架构的严格组成部分,并且在时间序列域中,DeepConvLSTM 不包括池化操作,因为网络的输入受到滑动窗口机制的约束[...],这一事实限制了可能性假设 DeepConvLSTM 需要由循环层处理数据序列,则对数据进行下采样。然而,在没有滑动窗口要求的情况下,池化机制可用于覆盖更深层的不同传感器数据时间尺度。

下图取自纸张,使架构更清晰。请注意,图像中的层 6 和 7 实际上是 LSTM 层。

Depiction of CNN LSTM Model for Activity Recognition

用于活动识别的 CNN LSTM 模型的描述 取自“用于多模式可穿戴活动识别的深度卷积和 LSTM 循环神经网络”。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

一般

滑动窗户

细胞神经网络

RNNs

摘要

在这篇文章中,您发现了人类活动识别的问题以及深度学习方法的使用,这些方法在这个问题上实现了最先进的表现。

具体来说,你学到了:

  • 活动识别是基于传感器数据(例如智能手机中的加速度计)预测人(通常在室内)的移动的问题。
  • 传感器数据流通常被分成称为窗口的子序列,并且每个窗口与更广泛的活动相关联,称为滑动窗口方法。
  • 卷积神经网络和长期短期记忆网络,或许两者结合在一起,最适合从原始传感器数据中学习特征并预测相关的运动。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

如何评估用于人类活动识别的机器学习算法

原文: machinelearningmastery.com/evaluate-machine-learning-algorithms-for-human-activity-recognition/

人类活动识别是将由专用线束或智能电话记录的加速度计数据序列分类为已知的明确定义的运动的问题。

该问题的经典方法涉及基于固定大小的窗口和训练机器学习模型(例如决策树的集合)的时间序列数据中的手工制作特征。困难在于此功能工程需要该领域的深厚专业知识。

最近,已经证明,诸如循环神经网络和一维卷积神经网络(CNN)之类的深度学习方法可以在很少或没有数据特征工程的情况下提供具有挑战性的活动识别任务的最新结果,而不是使用特征学习原始数据。

在本教程中,您将了解如何在“_ 使用智能手机的活动识别 _”数据集上评估各种机器学习算法。

完成本教程后,您将了解:

  • 如何在特征设计版本的活动识别数据集上加载和评估非线性和集成机器学习算法。
  • 如何在活动识别数据集的原始信号数据上加载和评估机器学习算法。
  • 如何定义能够进行特征学习的更复杂算法的预期表现的合理上下界,例如深度学习方法。

让我们开始吧。

How to Evaluate Machine Learning Algorithms for Human Activity Recognition

如何评估用于人类活动识别的机器学习算法 照片由 Murray Foubister ,保留一些权利。

教程概述

本教程分为三个部分;他们是:

  1. 使用智能手机数据集进行活动识别
  2. 建模特征工程数据
  3. 建模原始数据

使用智能手机数据集进行活动识别

人类活动识别,或简称为 HAR,是基于使用传感器的移动痕迹来预测人正在做什么的问题。

标准人类活动识别数据集是 2012 年提供的“使用智能手机进行活动识别”数据集。

它由 Davide Anguita 等人准备并提供。来自意大利热那亚大学的 2013 年论文“使用智能手机进行人类活动识别的公共领域数据集”中对该数据集进行了全面描述。该数据集在他们的 2012 年论文中用机器学习算法建模,标题为“使用多类硬件友好支持向量机在智能手机上进行人类活动识别。“

数据集可用,可以从 UCI 机器学习库免费下载:

该数据来自 30 名年龄在 19 至 48 岁之间的受试者,其执行六项标准活动中的一项,同时佩戴记录运动数据的腰部智能手机。记录执行活动的每个受试者的视频,并从这些视频手动标记移动数据。

以下是在记录其移动数据的同时执行活动的主体的示例视频。

<iframe allow="autoplay; encrypted-media" allowfullscreen="" frameborder="0" height="375" src="www.youtube.com/embed/XOEN9…" width="500"></iframe>

进行的六项活动如下:

  1. 步行
  2. 走上楼
  3. 走楼下
  4. 坐在
  5. 常设
  6. 铺设

记录的运动数据是来自智能手机的 x,y 和 z 加速度计数据(线性加速度)和陀螺仪数据(角速度),特别是三星 Galaxy S II。以 50Hz(即每秒 50 个数据点)记录观察结果。每个受试者进行两次活动,一次是左侧设备,另一次是右侧设备。

原始数据不可用。相反,可以使用预处理版本的数据集。预处理步骤包括:

  • 使用噪声滤波器预处理加速度计和陀螺仪。
  • 将数据拆分为 2.56 秒(128 个数据点)的固定窗口,重叠率为 50%。
  • 将加速度计数据分割为重力(总)和身体运动分量。

特征工程应用于窗口数据,并且提供具有这些工程特征的数据的副本。

从每个窗口提取在人类活动识别领域中常用的许多时间和频率特征。结果是 561 元素的特征向量。

根据受试者的数据,将数据集分成训练(70%)和测试(30%)组。训练 21 个,测试 9 个。

使用旨在用于智能手机的支持向量机(例如定点算术)的实验结果导致测试数据集的预测准确度为 89%,实现与未修改的 SVM 实现类似的结果。

该数据集是免费提供的,可以从 UCI 机器学习库下载。

数据以单个 zip 文件的形式提供,大小约为 58 兆字节。此下载的直接链接如下:

下载数据集并将所有文件解压缩到当前工作目录中名为“HARDataset”的新目录中。

建模特征工程数据

在本节中,我们将开发代码来加载数据集的特征工程版本并评估一套非线性机器学习算法,包括原始论文中使用的 SVM。

目标是在测试数据集上实现至少 89%的准确率。

使用特征工程版数据集的方法的结果为为原始数据版本开发的任何方法提供了基线。

本节分为五个部分;他们是:

  • 加载数据集
  • 定义模型
  • 评估模型
  • 总结结果
  • 完整的例子

加载数据集

第一步是加载训练并测试输入(X)和输出(y)数据。

具体来说,以下文件:

  • HARDataset / train / X_train.txt
  • HARDataset / train / y_train.txt
  • HARDataset / test / X_test.txt
  • HARDataset / test / y_test.txt

输入数据采用 CSV 格式,其中列通过空格分隔。这些文件中的每一个都可以作为 NumPy 数组加载。

下面的load_file()函数在给定文件的文件路径的情况下加载数据集,并将加载的数据作为 NumPy 数组返回。

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

考虑到目录布局和文件名的相似性,我们可以调用此函数来加载给定训练或测试集组的Xy文件。下面的load_dataset_group()函数将为一个组加载这两个文件,并将 X 和 y 元素作为 NumPy 数组返回。然后,此函数可用于加载训练和测试组的 X 和 y 元素。

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	# load input data
	X = load_file(prefix + group + '/X_'+group+'.txt')
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

最后,我们可以加载 train 和 test 数据集,并将它们作为 NumPy 数组返回,以便为拟合和评估机器学习模型做好准备。

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# flatten y
	trainy, testy = trainy[:,0], testy[:,0]
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

我们可以调用这个函数来加载所有需要的数据;例如:

# load dataset
trainX, trainy, testX, testy = load_dataset()

定义模型

接下来,我们可以定义一个机器学习模型列表来评估这个问题。

我们将使用默认配置评估模型。我们目前不是在寻找这些模型的最佳配置,只是对具有默认配置的复杂模型在这个问题上表现如何的一般概念。

我们将评估一组不同的非线性和集成机器学习算法,具体来说:

非线性算法:

  • k-最近邻居
  • 分类和回归树
  • 支持向量机
  • 朴素贝叶斯

集成算法:

  • 袋装决策树
  • 随机森林
  • 额外的树木
  • 梯度增压机

我们将定义模型并将它们存储在字典中,该字典将模型对象映射到有助于分析结果的简短名称。

下面的define_models()函数定义了我们将评估的八个模型。

# create a dict of standard models to evaluate {name:object}
def define_models(models=dict()):
	# nonlinear models
	models['knn'] = KNeighborsClassifier(n_neighbors=7)
	models['cart'] = DecisionTreeClassifier()
	models['svm'] = SVC()
	models['bayes'] = GaussianNB()
	# ensemble models
	models['bag'] = BaggingClassifier(n_estimators=100)
	models['rf'] = RandomForestClassifier(n_estimators=100)
	models['et'] = ExtraTreesClassifier(n_estimators=100)
	models['gbm'] = GradientBoostingClassifier(n_estimators=100)
	print('Defined %d models' % len(models))
	return models

此功能非常易于扩展,您可以轻松更新以定义您希望的任何机器学习模型或模型配置。

评估模型

下一步是评估加载的数据集中定义的模型。

该步骤分为单个模型的评估和所有模型的评估。

我们将通过首先将其拟合到训练数据集上,对测试数据集做出预测,然后使用度量来评估预测来评估单个模型。在这种情况下,我们将使用分类精度来捕获模型的表现(或误差),给出六个活动(或类)的平衡观察。

下面的evaluate_model()函数实现了此行为,评估给定模型并将分类精度返回为百分比。

# evaluate a single model
def evaluate_model(trainX, trainy, testX, testy, model):
	# fit the model
	model.fit(trainX, trainy)
	# make predictions
	yhat = model.predict(testX)
	# evaluate predictions
	accuracy = accuracy_score(testy, yhat)
	return accuracy * 100.0

我们现在可以为每个定义的模型重复调用evaluate_model()函数。

下面的evaluate_models()函数实现此行为,获取已定义模型的字典,并返回映射到其分类精度的模型名称字典。

因为模型的评估可能需要几分钟,所以该函数在评估每个模型作为一些详细的反馈之后打印它们的表现。

# evaluate a dict of models {name:object}, returns {name:score}
def evaluate_models(trainX, trainy, testX, testy, models):
	results = dict()
	for name, model in models.items():
		# evaluate the model
		results[name] = evaluate_model(trainX, trainy, testX, testy, model)
		# show process
		print('>%s: %.3f' % (name, results[name]))
	return results

总结结果

最后一步是总结研究结果。

我们可以按降序排列分类准确度对所有结果进行排序,因为我们对最大化准确率感兴趣。

然后可以打印评估模型的结果,清楚地显示每个评估模型的相对等级。

下面的summarize_results()函数实现了这种行为。

# print and plot the results
def summarize_results(results, maximize=True):
	# create a list of (name, mean(scores)) tuples
	mean_scores = [(k,v) for k,v in results.items()]
	# sort tuples by mean score
	mean_scores = sorted(mean_scores, key=lambda x: x[1])
	# reverse for descending order (e.g. for accuracy)
	if maximize:
		mean_scores = list(reversed(mean_scores))
	print()
	for name, score in mean_scores:
		print('Name=%s, Score=%.3f' % (name, score))

完整的例子

我们知道我们已经完成了所有工作。

下面列出了在数据集的特征工程版本上评估一套八个机器学习模型的完整示例。

# spot check on engineered-features
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	# load input data
	X = load_file(prefix + group + '/X_'+group+'.txt')
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# flatten y
	trainy, testy = trainy[:,0], testy[:,0]
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# create a dict of standard models to evaluate {name:object}
def define_models(models=dict()):
	# nonlinear models
	models['knn'] = KNeighborsClassifier(n_neighbors=7)
	models['cart'] = DecisionTreeClassifier()
	models['svm'] = SVC()
	models['bayes'] = GaussianNB()
	# ensemble models
	models['bag'] = BaggingClassifier(n_estimators=100)
	models['rf'] = RandomForestClassifier(n_estimators=100)
	models['et'] = ExtraTreesClassifier(n_estimators=100)
	models['gbm'] = GradientBoostingClassifier(n_estimators=100)
	print('Defined %d models' % len(models))
	return models

# evaluate a single model
def evaluate_model(trainX, trainy, testX, testy, model):
	# fit the model
	model.fit(trainX, trainy)
	# make predictions
	yhat = model.predict(testX)
	# evaluate predictions
	accuracy = accuracy_score(testy, yhat)
	return accuracy * 100.0

# evaluate a dict of models {name:object}, returns {name:score}
def evaluate_models(trainX, trainy, testX, testy, models):
	results = dict()
	for name, model in models.items():
		# evaluate the model
		results[name] = evaluate_model(trainX, trainy, testX, testy, model)
		# show process
		print('>%s: %.3f' % (name, results[name]))
	return results

# print and plot the results
def summarize_results(results, maximize=True):
	# create a list of (name, mean(scores)) tuples
	mean_scores = [(k,v) for k,v in results.items()]
	# sort tuples by mean score
	mean_scores = sorted(mean_scores, key=lambda x: x[1])
	# reverse for descending order (e.g. for accuracy)
	if maximize:
		mean_scores = list(reversed(mean_scores))
	print()
	for name, score in mean_scores:
		print('Name=%s, Score=%.3f' % (name, score))

# load dataset
trainX, trainy, testX, testy = load_dataset()
# get model list
models = define_models()
# evaluate models
results = evaluate_models(trainX, trainy, testX, testy, models)
# summarize results
summarize_results(results)

运行该示例首先加载训练和测试数据集,显示每个输入和输出组件的形状。

然后依次评估八个模型,打印每个模型的表现。

最后,显示模型在测试集上的表现等级。

我们可以看到,ExtraTrees 集合方法和支持向量机非线性方法在测试集上实现了大约 94%的准确率。

这是一个很好的结果,超过原始论文中 SVM 报告的 89%。

考虑到算法的随机性,每次运行代码时,具体结果可能会有所不同。然而,考虑到数据集的大小,算法表现之间的相对关系应该相当稳定。

(7352, 561) (7352, 1)
(2947, 561) (2947, 1)
(7352, 561) (7352,) (2947, 561) (2947,)
Defined 8 models
>knn: 90.329
>cart: 86.020
>svm: 94.028
>bayes: 77.027
>bag: 89.820
>rf: 92.772
>et: 94.028
>gbm: 93.756

Name=et, Score=94.028
Name=svm, Score=94.028
Name=gbm, Score=93.756
Name=rf, Score=92.772
Name=knn, Score=90.329
Name=bag, Score=89.820
Name=cart, Score=86.020
Name=bayes, Score=77.027

这些结果显示了在准备数据和领域特定功能的工程中给定的领域专业知识的可能性。因此,这些结果可以作为通过更先进的方法可以追求的表现的上限,这些方法可以自动学习特征作为拟合模型的一部分,例如深度学习方法。

任何这样的先进方法都适合并评估从中得到工程特征的原始数据。因此,直接评估该数据的机器学习算法的表现可以提供任何更高级方法的表现的预期下限。

我们将在下一节中探讨这一点。

建模原始数据

我们可以使用相同的框架来评估原始数据上的机器学习模型。

原始数据确实需要更多工作才能加载。

原始数据中有三种主要信号类型:总加速度,车身加速度和车身陀螺仪。每个都有三个数据轴。这意味着每个时间步长总共有九个变量。

此外,每个数据系列已被划分为 2.65 秒数据的重叠窗口,或 128 个时间步长。这些数据窗口对应于上一节中工程特征(行)的窗口。

这意味着一行数据具有 128 * 9 或 1,152 个元素。这比前一节中 561 个元素向量的大小小一倍,并且可能存在一些冗余数据。

信号存储在训练和测试子目录下的 / Inertial Signals / 目录中。每个信号的每个轴都存储在一个单独的文件中,这意味着每个训练和测试数据集都有九个要加载的输入文件和一个要加载的输出文件。在给定一致的目录结构和文件命名约定的情况下,我们可以批量加载这些文件。

首先,我们可以将给定组的所有数据加载到单个三维 NumPy 数组中,其中数组的维数为[样本,时间步长,特征]。为了更清楚,有 128 个时间步和 9 个特征,其中样本数是任何给定原始信号数据文件中的行数。

下面的load_group()函数实现了这种行为。dstack()NumPy 函数允许我们将每个加载的 3D 数组堆叠成单个 3D 数组,其中变量在第三维(特征)上分开。

# load a list of files into a 3D array of [samples, timesteps, features]
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

我们可以使用此功能加载给定组的所有输入信号数据,例如训练或测试。

下面的load_dataset_group()函数使用目录之间的一致命名约定加载单个组的所有输入信号数据和输出数据。

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

最后,我们可以加载每个训练和测试数据集。

作为准备加载数据的一部分,我们必须将窗口和特征展平为一个长向量。

我们可以使用 NumPy 重塑功能执行此操作,并将[_ 样本,时间步长,特征 ]的三个维度转换为[ 样本,时间步长*特征 _]的两个维度。

下面的load_dataset()函数实现了这种行为,并返回训练并测试Xy元素,以便拟合和评估定义的模型。

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# flatten X
	trainX = trainX.reshape((trainX.shape[0], trainX.shape[1] * trainX.shape[2]))
	testX = testX.reshape((testX.shape[0], testX.shape[1] * testX.shape[2]))
	# flatten y
	trainy, testy = trainy[:,0], testy[:,0]
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

综合这些,下面列出了完整的例子。

# spot check on raw data
from numpy import dstack
from pandas import read_csv
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier

# load a single file as a numpy array
def load_file(filepath):
	dataframe = read_csv(filepath, header=None, delim_whitespace=True)
	return dataframe.values

# load a list of files into a 3D array of [samples, timesteps, features]
def load_group(filenames, prefix=''):
	loaded = list()
	for name in filenames:
		data = load_file(prefix + name)
		loaded.append(data)
	# stack group so that features are the 3rd dimension
	loaded = dstack(loaded)
	return loaded

# load a dataset group, such as train or test
def load_dataset_group(group, prefix=''):
	filepath = prefix + group + '/Inertial Signals/'
	# load all 9 files as a single array
	filenames = list()
	# total acceleration
	filenames += ['total_acc_x_'+group+'.txt', 'total_acc_y_'+group+'.txt', 'total_acc_z_'+group+'.txt']
	# body acceleration
	filenames += ['body_acc_x_'+group+'.txt', 'body_acc_y_'+group+'.txt', 'body_acc_z_'+group+'.txt']
	# body gyroscope
	filenames += ['body_gyro_x_'+group+'.txt', 'body_gyro_y_'+group+'.txt', 'body_gyro_z_'+group+'.txt']
	# load input data
	X = load_group(filenames, filepath)
	# load class output
	y = load_file(prefix + group + '/y_'+group+'.txt')
	return X, y

# load the dataset, returns train and test X and y elements
def load_dataset(prefix=''):
	# load all train
	trainX, trainy = load_dataset_group('train', prefix + 'HARDataset/')
	print(trainX.shape, trainy.shape)
	# load all test
	testX, testy = load_dataset_group('test', prefix + 'HARDataset/')
	print(testX.shape, testy.shape)
	# flatten X
	trainX = trainX.reshape((trainX.shape[0], trainX.shape[1] * trainX.shape[2]))
	testX = testX.reshape((testX.shape[0], testX.shape[1] * testX.shape[2]))
	# flatten y
	trainy, testy = trainy[:,0], testy[:,0]
	print(trainX.shape, trainy.shape, testX.shape, testy.shape)
	return trainX, trainy, testX, testy

# create a dict of standard models to evaluate {name:object}
def define_models(models=dict()):
	# nonlinear models
	models['knn'] = KNeighborsClassifier(n_neighbors=7)
	models['cart'] = DecisionTreeClassifier()
	models['svm'] = SVC()
	models['bayes'] = GaussianNB()
	# ensemble models
	models['bag'] = BaggingClassifier(n_estimators=100)
	models['rf'] = RandomForestClassifier(n_estimators=100)
	models['et'] = ExtraTreesClassifier(n_estimators=100)
	models['gbm'] = GradientBoostingClassifier(n_estimators=100)
	print('Defined %d models' % len(models))
	return models

# evaluate a single model
def evaluate_model(trainX, trainy, testX, testy, model):
	# fit the model
	model.fit(trainX, trainy)
	# make predictions
	yhat = model.predict(testX)
	# evaluate predictions
	accuracy = accuracy_score(testy, yhat)
	return accuracy * 100.0

# evaluate a dict of models {name:object}, returns {name:score}
def evaluate_models(trainX, trainy, testX, testy, models):
	results = dict()
	for name, model in models.items():
		# evaluate the model
		results[name] = evaluate_model(trainX, trainy, testX, testy, model)
		# show process
		print('>%s: %.3f' % (name, results[name]))
	return results

# print and plot the results
def summarize_results(results, maximize=True):
	# create a list of (name, mean(scores)) tuples
	mean_scores = [(k,v) for k,v in results.items()]
	# sort tuples by mean score
	mean_scores = sorted(mean_scores, key=lambda x: x[1])
	# reverse for descending order (e.g. for accuracy)
	if maximize:
		mean_scores = list(reversed(mean_scores))
	print()
	for name, score in mean_scores:
		print('Name=%s, Score=%.3f' % (name, score))

# load dataset
trainX, trainy, testX, testy = load_dataset()
# get model list
models = define_models()
# evaluate models
results = evaluate_models(trainX, trainy, testX, testy, models)
# summarize results
summarize_results(results)

首先运行该示例加载数据集。

我们可以看到原始序列和测试集具有与工程特征(分别为 7352 和 2947)相同数量的样本,并且正确加载了三维数据。我们还可以看到平面数据和将提供给模型的 1152 输入向量。

接下来依次评估八个定义的模型。

最终结果表明,决策树的集合在原始数据上表现最佳。梯度增强和额外树木以最高 87%和 86%的准确度表现最佳,比数据集的特征工程版本中表现最佳的模型低约 7 个点。

令人鼓舞的是,Extra Trees 集合方法在两个数据集上都表现良好;它表明它和类似的树集合方法可能适合这个问题,至少在这个简化的框架中。

我们还可以看到 SVM 下降到约 72%的准确度。

决策树集合的良好表现可能表明需要特征选择和集合方法能够选择与预测相关活动最相关的那些特征。

(7352, 128, 9) (7352, 1)
(2947, 128, 9) (2947, 1)
(7352, 1152) (7352,) (2947, 1152) (2947,)
Defined 8 models
>knn: 61.893
>cart: 72.141
>svm: 76.960
>bayes: 72.480
>bag: 84.527
>rf: 84.662
>et: 86.902
>gbm: 87.615

Name=gbm, Score=87.615
Name=et, Score=86.902
Name=rf, Score=84.662
Name=bag, Score=84.527
Name=svm, Score=76.960
Name=bayes, Score=72.480
Name=cart, Score=72.141
Name=knn, Score=61.893

如前一节所述,这些结果为可能尝试从原始数据自动学习更高阶特征(例如,通过深度学习方法中的特征学习)的任何更复杂的方法提供了精确度的下限。

总之,此类方法的界限在原始数据上从 GBM 的约 87%准确度扩展到高度处理的数据集上的额外树和 SVM 的约 94%,[87%至 94%]。

扩展

本节列出了一些扩展您可能希望探索的教程的想法。

  • 更多算法。在这个问题上只评估了八种机器学习算法;尝试一些线性方法,也许还有一些非线性和集合方法。
  • 算法调整。没有调整机器学习算法;主要使用默认配置。选择一种方法,例如 SVM,ExtraTrees 或 Gradient Boosting,并搜索一组不同的超参数配置,以查看是否可以进一步提升问题的表现。
  • 数据缩放。数据已经按比例缩放到[-1,1]。探索额外的扩展(例如标准化)是否可以带来更好的表现,可能是对这种扩展敏感的方法(如 kNN)。

如果你探索任何这些扩展,我很想知道。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

文件

用品

摘要

在本教程中,您了解了如何在'_ 使用智能手机的活动识别 _'数据集上评估各种机器学习算法。

具体来说,你学到了:

  • 如何在特征设计版本的活动识别数据集上加载和评估非线性和集成机器学习算法。
  • 如何在活动识别数据集的原始信号数据上加载和评估机器学习算法。
  • 如何定义能够进行特征学习的更复杂算法的预期表现的合理上下界,例如深度学习方法。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。