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

引言

原文最后更新时间: 2019年8月5日

人类活动识别是一个将加速度计数据序列分类的问题,这些数据是由专门的线束或智能手机记录到已知定义明确的运动。

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

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

在本教程中,您将发现如何开发一维卷积神经网络以对人类活动识别问题进行时间序列分类。

完成本教程后,您将知道:

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

在我的新书中,通过25个循序渐进的教程和完整的源代码,探索如何使用LSTM和更多内容构建用于多变量和多步时间序列预测的模型。

教程概述

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

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

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

人类活动识别,或简称为HAR,是指使用传感器根据其运动轨迹预测人在做什么的问题。

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

它由Davide Anguita等人准备并提供。由意大利热那亚大学(University of Genova)提供,并在其2013年的论文“ A Public Domain Dataset for Human Activity Recognition Using Smartphones(使用智能手机进行人类活动识别的公共领域数据集) ”中进行了完整描述。该数据集在其2012年题为“Human Activity Recognition on Smartphones using a Multiclass Hardware-Friendly Support Vector Machine(使用多类硬件友好支持向量机的智能手机上的人类活动识别)”的机器学习算法中进行了建模。

该数据集已经可用,可以从UCI机器学习存储库免费下载:使用智能手机数据集,UCI机器学习存储库进行人类活动识别

该数据是从30位年龄在19至48岁之间的受试者收集的,他们戴着腰部安装的智能手机来执行六项标准活动之一,并记录了运动数据。记录了执行活动的每个主题的视频,并从这些视频中手动标记了运动数据。

下面是一个示例视频,对象在记录活动数据的同时执行活动。

进行的六项活动如下:

  1. 步行(Walking)
  2. 上楼走(Walking Upstairs)
  3. 下楼走(Walking Downstairs)
  4. 坐着(Sitting)
  5. 站立(Standing)
  6. 铺设(Laying)

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

原始数据不可用。而是提供了数据集的预处理版本。预处理步骤包括:

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

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

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

基于受试者的数据将数据集分为训练(70%)和测试(30%)集,例如,训练为21个受试者,测试为9个。

旨在用于智能手机的支持向量机的实验结果(例如定点算术)在测试数据集上的预测精度为89%,与未经修改的SVM实施方式获得的结果相似。

该数据集可免费获得,并可从UCI机器学习存储库下载。

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

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

开发一维卷积神经网络

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

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

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

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

本节分为四个部分:他们是:

  1. 载入资料
  2. 拟合和评估模型
  3. 总结结果
  4. 完整的例子

载入数据

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

原始数据中有三种主要信号类型:总加速度,身体加速度和身体陀螺仪。每个都有三个数据轴( total acceleration, body acceleration, and body gyroscope)。这意味着每个时间步共有9个变量。

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

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

信号存储在训练和测试子目录下的*/ 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() 函数实现了此行为。所述 NumPy中的函数 dstack() 允许我们堆叠每个加载的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() 函数使用Train和Test目录之间的一致命名约定来加载单个组的所有输入信号数据和输出数据。

# 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

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

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

下面的 load_dataset() 函数实现了此行为,并返回训练和测试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

拟合和评估模型

现在我们已经将数据加载到内存中以备建模了,我们可以定义,拟合和评估一维CNN模型。

我们可以定义一个名为 validate_model() 的函数,该函数接受训练和测试数据集,将模型拟合到训练数据集上,在测试数据集上对其进行评估,然后返回模型性能的估计值。

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

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

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

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

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

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

我们将模型定义为具有两个1D CNN层,然后是用于规范化的辍学层,然后是池化层。通常,以两个为一组来定义CNN层,以便使模型有很好的机会从输入数据中学习特征。CNN的学习速度非常快,因此辍学层旨在帮助减慢学习过程,并有望产生更好的最终模型。池化层将学习到的特征减少到其大小的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个数据窗口暴露给模型。

模型拟合后,将在测试数据集上对其进行评估,并返回测试数据集上的拟合模型的准确性。

下面列出了完整的 valuate_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)

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

下面的函数 summary_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)

现在我们已经了解了如何加载数据并拟合一维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]))

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

# 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. 身体加速度
  6. 身体加速度z
  7. 人体陀螺仪x
  8. 人体陀螺仪
  9. 人体陀螺仪z

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

总加速度数据的分布比车身数据更平坦,这一点更为明确。

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

训练数据集中每个变量的直方图
数据具有足够的高斯性质,以探索标准化变换是否将有助于模型从原始观测值中提取显着信号。

在拟合和评估模型之前,可以使用下面名为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

我们可以更新valuate_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)

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

我们将更新summary_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%。

我们可以看到,具有标准化结果的分布与没有标准化结果的分布有很大不同。这可能是真实的效果。

带有和不带有标准化的一维CNN的箱须图
过滤器数量
现在我们有了一个实验框架,我们可以探索该模型的其他超参数。

CNN的重要超参数是滤波器映射的数量。我们可以尝试各种不同的值,从小于到大于我们开发的第一个模型中使用的64个值。

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

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

我们可以使用上一部分中的相同代码,并更新validate_model ()函数以将提供的参数用作Conv1D层中的过滤器数量。我们还可以更新summary_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是更稳定的配置。

具有不同数量过滤器图的一维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,可以在良好性能和低方差之间提供良好的平衡。

具有不同数量的内核大小的一维CNN的箱形图和晶须图
这只是调整模型的开始,尽管我们专注于也许更重要的元素。探索上述某些发现的组合以查看性能是否可以进一步提升可能会很有趣。

将重复数从10增加到30或更多可能很有趣,以查看其结果是否更稳定。

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

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

我们可以使用Keras功能API实现多头一维CNN。有关此API的简要介绍,请参见以下文章:

如何使用Keras功能API进行深度学习

下面列出了validate_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

创建模型后,将创建网络体系结构图。下文提供了有关构建模型如何拟合的清晰思路。

多头一维卷积神经网络的图解
该模型的其他方面可能会因头部而异,例如过滤器的数量,甚至数据本身的准备。

下面列出了带有多头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输入功能。
  • 诊断程序。使用简单的学习曲线诊断程序来解释模型在各个时期的学习方式,以及更多的正则化,不同的学习率,不同的批次大小或时期数是否可能导致性能更好或更稳定的模型。
    如果您探索这些扩展中的任何一个,我很想知道。

进一步阅读

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

文件

使用智能手机进行人类活动识别的公共领域数据集,2013年。
使用多类硬件友好支持向量机的智能手机上的人类活动识别,2012年。

文章

使用智能手机数据集,UCI机器学习存储库进行人类活动识别
活动识别,维基百科
使用智能手机传感器,视频进行活动识别实验。

摘要

在本教程中,您发现了如何开发一维卷积神经网络来对人类活动识别问题进行时间序列分类。

具体来说,您了解到:

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

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

推荐图书:Deep Learning for Time Series Forecasting


问答专区

Q:这篇文章和你的书都很棒!但我有一个问题:
就我而言,尽管数据库的结构与示例中的数据库类似,但由于环境的性质,数据集很小。
我一直在研究,但是对于研究案例为多元时间序列的数据扩充问题,我找不到很好的方法。
有什么建议吗?
A: 一个好的起点可能是将高斯噪声添加到输入样本中。


Q: 我很想了解一个我找不到以下答案的问题:

在多变量时间序列上进行Conv1D时–内核是在所有维度上卷积还是在每个维度上卷积?

事实是,我将900 x 10的时间序列输入到Conv1D(filter = 16,kernel_size = 6)中,我得到800 x 16的输出,而我希望得到800 x 16 x 10,因为每个时间序列维与过滤器分别卷积。

A: 跨每个时间序列与内存分开。


Q: 我制作了一个数据集,并使用CNN对其进行了训练。训练精度接近1,但验证精度保持在0.55左右。验证丢失率先下降然后上升。这是过度拟合的问题吗?但是,数据扩充和正则化无效。您有什么好的建议吗?

A: 听起来像过拟合。尝试大型模型并使用权重正则化,并针对验证数据集尽早停止。

Q: 权重正则化可以稳定验证的丢失,但验证准确性尚未提高,保持在约0.55。在整个训练过程中,验证准确性约为0.55,因此尽早停止也没有任何效果。如何提高验证准确性?
A: 我在这里有一些建议:http://machinelearningmastery.com/improve-deep-learning-performance/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
UCI人类活动识别数据集是一个广为人知的公开数据集,用于训练和评估机器学习算法在识别人类活动方面的能力。该数据集由对智能手机进行的实验收集而成,包含了来自30个不同志愿者的加速度计和陀螺仪传感器数据。 数据集包含六个不同的活动类别:走路、上楼、下楼、坐着、站立和躺着。加速度计和陀螺仪传感器数据被采样为固定频率的时间序列数据。每个传感器都测量了三个方向的运动(X、Y和Z轴)。数据集还提供了每个样本的标签,以便进行监督学习和模型的评估。 UCI人类活动识别数据集的目标是通过机器学习算法自动识别和分类人类活动。这对于智能手机或其他感测设备来说是一个重要的应用领域。识别人类活动可以用于许多应用,如健身追踪、安全监控和人机交互。 研究人员和开发者可以使用UCI人类活动识别数据集来训练自己的机器学习算法,并利用该数据集的丰富信息来提高模型的准确性和普适性。通过使用这个数据集,研究人员可以探索不同的特征提取方法、分类算法和特定领域的优化技术。 总而言之,UCI人类活动识别数据集是一个有助于推动机器学习算法在人类活动识别方面发展的重要资源。它提供了丰富的数据,可以用于开发和评估新的算法和应用程序。通过使用这个数据集,我们可以更好地理解和识别人类活动,并为未来的智能技术提供更多的可能性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

或许,这就是梦想吧!

如果对你有用,欢迎打赏。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值