基于智能手机传感器数据的人类行为识别

原文链接: http://www.infoq.com/cn/articles/human-behavior-recognition-based-on-smart-phone-sensor-data?utm_source=tuicool&utm_medium=referral


人类行为识别的目的是通过一系列的观察,对人类的动作类型、行为模式进行分析和识别,并使用自然语言等方式对其进行描述的计算机技术。由于人类行为的复杂性和多样性,往往识别出的结果是多样性的,并且连带着行为类型的概率输出的。随着信息技术的发展,各种移动设备和可穿戴设备正在以加速度的方式增长,其性能和嵌入的传感器也变的多样化,例如:高清相机、光传感器、陀螺仪传感器、加速度传感器、GPS以及温度传感器等。各种各样的传感器都在时刻的记录着使用者的信息,这些记录信息不仅可以用于用户位置的预测,也可以进行用户行为的识别等。

本文使用了智能设备加速度传感器的数据,结合支持向量机的特性进行人类行为识别模型的设计和应用。 []

![在这里插入图片描述](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZzEudHVpY29vbC5jb20vVUJ2UUJmQS5qcGchd2Vi?x-oss-process=image/format,png)

如上图所示,信号数据的采集来自于嵌入在智能手机中的加速度传感器,实验选用了人类日常行为中的六类常见行为,分别为:走路、慢跑、上楼梯、下楼梯、坐、站立,数据收集后,对数据进行特征抽取,抽取后的特征使用支持向量机的分类功能对特征进行分类,最后识别出人类的六类行为。

关于支持向量机(SVM)

支持向量机(Support Vector Machine)是Cortes和Vapnik于1995年首先提出的,它在解决小样本、非线性及高维模式识别中表现出许多特有的优势,并能够推广应用到函数拟合等其他机器学习问题中。SVM算法是基于间隔最大化的一种监督学习算法,包含线性和非线性两种模型,对于线性不可分问题,通常会加入核函数进行处理。

支持向量机本质上是一个二类分类方法,它的基本模型是定义在特征空间上的间隔最大化的线性分类器,间隔最大化使它有别于感知机。对于线性可分的训练集,感知机的分离超平面是不唯一的,会有无穷个,而支持向量机会对分离超平面增加约束条件,使得分类超平面唯一。

假设我们有一组分属于两类的二维点,分别用星和圆表示,这些点可以通过直线分割,我们需要找到一条最优的分割线:

  • 找到正确的超平面(场景1) :这里,我们有三个超平面(A、B、C),我们需要找到正确的超平面来分割星和圆:

我们的目的是 选择更好地分割两个类的超平面 ,因此上图中可以看到超平面 B 已经能够完成分割的工作。

  • 找到正确的超平面(场景2) :同样有三个超平面(A、B、C),我们需要找到正确的超平面来分割星和圆:

上图中,针对任意一个类,最大化最近的数据点和超平面之间的距离将有助于我们选择正确的超平面,这个距离称为 边距 ,如下图:

可以看到,超平面 C 距离两个类别的边缘比A和B都要高,因此我们将超平面 C 定为最优的超平面。选择边距最高的超平面的另一个重要的原因是 鲁棒性 ,假设我们选择最低边距的超平面,那么分类结果的错误率将会极大的升高。

  • 找到正确的超平面(场景3) :同样有三个超平面(A、B、C),我们使用场景2的规则寻找正确的超平面来分割星和圆:

可能看到上图,第一印象最优的超平面是 B ,因为它比超平面A有更高的边距。但是,这里是一种意外情况, 支持向量机会选择在将边距最大化之前对类进行精确分类的超平面。 这里,超平面B具有分类误差,超平面A已经正确的分类,因此此情况下,最优超平面则是 A

  • 能够分类两个类别(场景4) :针对利群点情况,寻找最优超平面:

上图中,一个星出现在了圆所在的区域内,此星可称为利群点。但是支持向量机具有忽略异常点并找到具有最大边距的超平面的特征,因此,可以说,支持向量机是鲁棒性的。最终最优超平面如下图所示:

  • 找到超平面并分类(场景5) :以上的场景均是线性超平面。在下面的场景中,我们无法直接在两个类之间找到线性超平面,那么支持向量机如何分类这两个类呢?

支持向量机可以轻松的解决,它引入了一些附加的特性来解决此类问题。这里,我们添加一个新的特征。重新绘制坐标轴上的数据点如下:

在支持向量机中,已经很容易在这两个类直接找到线性超平面了,但是,出现的另一个重要的问题是,我们是否要手动处理这样的问题呢?当然不需要,在支持向量机中,有一个 核函数 的技术,它会将低维空间的输入转换为高维空间,形成映射。由此会将某些不可分的问题转换为可分问题,主要用于一些非线性分类问题中。

当我们查看场景5中的超平面是,可能会如下图所示:

以上仅仅是关于支持向量机的一点介绍,支持向量机有这复杂的算法以及完备的证明,这里不再累述,可参考 Support_vector_machine 查看学习。

对于支持向量机来说,比较有名的类库当属台湾大学林智仁(LinChih-Jen)教授所构建的 LIBSVM 类库,由于LIBSVM程序小,运用灵活,输入参数少,并且是开源的,易于扩展,因此成为目前应用最多的支持向量机的库。 另外还提供了多种语言的接口,便于在不同的平台下使用,本文中使用的也是这个类库。 关于Mac下此类库的编译安装,请参考文档 Install libsvm on Mac OSX ,本文会在Mac下进行训练数据预处理、模型训练、参数调优等,最终得到模型会使用在iOS项目中,当然该模型也可以使用在Android以及其他任何可以使用的地方。

针对支持向量机以及LIBSVM详细的介绍,可查看官方给出的文档: PDF

传感器数据集

本文使用了 WISDM (Wireless Sensor Data Mining) Lab 实验室公开的 Actitracker 的数据集。 WISDM 公开了两个数据集,一个是在实验室环境采集的;另一个是在真实使用场景中采集的,这里使用的是实验室环境采集的数据。

  • 测试记录:1,098,207 条
  • 测试人数:36 人
  • 采样频率:20 Hz
  • 行为类型:6 种
    • 走路
    • 慢跑
    • 上楼梯
    • 下楼梯
    • 站立
  • 传感器类型:加速度
  • 测试场景:手机放在衣兜里面

数据分析

从 实验室采集数据下载地址 下载数据集压缩包,解压后可以看到下面这些文件:

  • readme.txt
  • WISDM_ar_v1.1_raw_about.txt
  • WISDM_ar_v1.1_trans_about.txt
  • WISDM_ar_v1.1_raw.txt
  • WISDM_ar_v1.1_transformed.arff

我们需要的是包含 RAW 数据的 WISDM_ar_v1.1_raw.txt 文件,其他的是转换后的或者说明文件。先看看这些数据的分布情况:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, roc_curve, auc

if name == “main”:
column_names = [‘user-id’, ‘activity’, ‘timestamp’, ‘x-axis’, ‘y-axis’, ‘z-axis’]
df = pd.read_csv(“WISDM_ar_v1.1_raw.txt”, header=None, names=column_names)
n = 10
print df.head(n)
subject = pd.DataFrame(df[“user-id”].value_counts(), columns=[“Count”])
subject.index.names = [‘Subject’]
print subject.head(n)
activities = pd.DataFrame(df[“activity”].value_counts(), columns=[“Count”])
activities.index.names = [‘Activity’]
print activities.head(n)
activity_of_subjects = pd.DataFrame(df.groupby(“user-id”)[“activity”].value_counts())
print activity_of_subjects.unstack().head(n)
activity_of_subjects.unstack().plot(kind=‘bar’, stacked=True, colormap=‘Blues’, title=“Distribution”)
plt.show()

WISDM_ar_v1.1_raw.txt文件不是合法的 CSV 文件,每行后面有个 ; 号,如果使用 Pandasread_csv 方法直接加载会出错,需要先将这些分号全部删除。

查看数据集各个行为的占比情况,绘制饼图如下:

可以看到此数据集是一个不平衡的数据集,但是这里暂时忽略其不平衡性。

数据预处理

在LIBSVM的官方文档中可以看到,LIBSVM所使用的数据集是有严格的格式规范:

<label> <index1>:<value1> <index2>:<value2> ...

<label> :对于分类问题代表样本的类别,使用整数表示,支持多个类别;对于回归问题代表目标变量,可以是任意实数。

<index1>:<value1> :表示特征项。其中 <index> 代表特征项的编号,使用从 1 开始的整数表示,可以不连续; <value> 代表该特征项对应的特征值,使用实数表示。在实际的操作中,如果样本缺少某个特征项,可以直接省略,LIBSVM 会自动把该项的特征值赋为 0

标签和每项特征之间使用 空格 分割,每行数据使用 \n 分割。

只有符合这样格式的数据,才能够被LIBSVM使用,否则会直接报错。这对准备好的数据,此类库还提供了一个 tools/checkdata.py 核查工具,以便核查数据集是否符合要求。针对特征的提取,为了简单,这里仅提取五类特征:

  • 平均值
  • 最大值
  • 最小值
  • 方差
  • 组合三轴的加速度值 math.sqrt(math.pow(acc_x, 2)+math.pow(acc_y, 2)+math.pow(acc_z, 2))

了解了所需要的数据格式后,开始进行数据的预处理,并转换为所需要的格式文件。接下来分别将训练和测试数据集进行特征抽取并按照LIBSVM的数据格式重组,代码如下:

import ast
import math
import numpy as np 

FEATURE = ("mean", "max", "min", "std")
STATUS = ("Sitting", "Walking", "Upstairs", "Downstairs", "Jogging", "Standing")

def preprocess(file_dir, Seg_granularity):
gravity_data = []
with open(file_dir) as f:
index = 0
for line in f:
clear_line = line.strip().lstrip().rstrip(’;’)
raw_list = clear_line.split(’,’)
index = index + 1
if len(raw_list) < 5:
continue
status = raw_list[1]
acc_x = float(raw_list[3])
acc_y = float(raw_list[4])
print index
acc_z = float(raw_list[5])

        if acc_x == 0 or acc_y == 0 or acc_z == 0:continuegravity = math.sqrt(math.pow(acc_x, 2)+math.pow(acc_y, 2)+math.pow(acc_z, 2))gravity_tuple = {&quot;gravity&quot;: gravity, &quot;status&quot;: status}gravity_data.append(gravity_tuple)# split data sample of gravity
splited_data = []
cur_cluster  = []
counter      = 0
last_status  = gravity_data[0][&quot;status&quot;]
for gravity_tuple in gravity_data:if not (counter &lt; Seg_granularity and gravity_tuple[&quot;status&quot;] == last_status):seg_data = {&quot;status&quot;: last_status, &quot;values&quot;: cur_cluster}# print seg_datasplited_data.append(seg_data)cur_cluster = []counter = 0cur_cluster.append(gravity_tuple[&quot;gravity&quot;])last_status = gravity_tuple[&quot;status&quot;]counter += 1
# compute statistics of gravity data
statistics_data = []
for seg_data in splited_data:np_values = np.array(seg_data.pop(&quot;values&quot;))seg_data[&quot;max&quot;]  = np.amax(np_values)seg_data[&quot;min&quot;]  = np.amin(np_values)seg_data[&quot;std&quot;]  = np.std(np_values)seg_data[&quot;mean&quot;] = np.mean(np_values)statistics_data.append(seg_data)
# write statistics result into a file in format of LibSVM
with open(&quot;WISDM_ar_v1.1_raw_svm.txt&quot;, &quot;a&quot;) as the_file:for seg_data in statistics_data:row = str(STATUS.index(seg_data[&quot;status&quot;])) + &quot; &quot; + \str(FEATURE.index(&quot;mean&quot;)) + &quot;:&quot; + str(seg_data[&quot;mean&quot;]) + &quot; &quot; + \str(FEATURE.index(&quot;max&quot;)) + &quot;:&quot; + str(seg_data[&quot;max&quot;]) + &quot; &quot; + \str(FEATURE.index(&quot;min&quot;)) + &quot;:&quot; + str(seg_data[&quot;min&quot;]) + &quot; &quot; + \str(FEATURE.index(&quot;std&quot;)) + &quot;:&quot; + str(seg_data[&quot;std&quot;]) + &quot;\n&quot;# print rowthe_file.write(row)        

if name == "main":
preprocess("WISDM_ar_v1.1_raw.txt", 100)
pass

成功转换后的数据格式形如:

.
.
.
5 0:9.73098373254 1:10.2899465499 2:9.30995703535 3:0.129482033438
5 0:9.74517171235 1:10.449291842 2:9.15706284788 3:0.161143714697
5 0:9.71565678822 1:10.4324206204 2:9.41070666847 3:0.136704694206
5 0:9.70622803003 1:9.7882020821 2:9.60614907234 3:0.0322246639852
5 0:9.74443440742 1:10.2915256401 2:9.28356073929 3:0.165543789197
0 0:9.28177794859 1:9.47500395778 2:8.92218583084 3:0.0700079500015
0 0:9.27218416165 1:9.40427562335 2:9.14709243421 3:0.0433805537826
0 0:9.27867211792 1:9.39755287296 2:9.1369415014 3:0.037533026091
0 0:9.27434585368 1:9.33462907672 2:9.21453200114 3:0.0263815511773
.
.
.

由于该数据集并未区分训练和测试数据集,因此为了最终的模型验证,首先需要分割该数据集为两份,分别进行训练和模型验证,分割方法就使用最简单的2\8原则,使用LIBSVM提供的工具 tools/subset.py 进行数据分割:

工具使用介绍:

Usage: subset.py [options] dataset subset_size [output1] [output2]

This script randomly selects a subset of the dataset.

options:
-s method : method of selection (default 0)
0 – stratified selection (classification only)
1 – random selection

output1 : the subset (optional)
output2 : rest of the data (optional)
If output1 is omitted, the subset will be printed on the screen.

使用工具进行数据分割:

python subset.py -s 0 WISDM_ar_v1.1_raw_svm.txt 2190 raw_test.txt raw_train.txt

**

!! 注意 !!

上面代码段中的 2190 就是subset.py工具子数据集的大小,该大小并不是文件的大小,而是根据原始文件中的行数进行2\8分后的行数。subset.py会随机抽取所设置行数的数据到指定的文件中。

**

完成后,我们就得到了训练数据集 raw_train.txt 和测试数据集 raw_test.txt

到此,所需要使用的数据集已经完全转换为LIBSVM所需要的格式,如果不放心数据格式,可以使用 tools/checkdata.py 工具进行检查。

模型创建与训练

在关于支持向量机部分,如果已经在Mac上安装好了libsvm,那么在你的命令行工具中输入 svm-train ,即可看到此命令的使用方式和参数说明,假设我们使用默认的参数进行模型训练:

svm-train -b 1 raw_train.txt raw_trained.model

其中 -b 的含义是probability_estimates,是否训练一个SVC或者SVR模型用于概率统计,设置为 1 ,以便最终的模型评估使用。

训练过程的可能会消耗一点时间,主要在于所使用的训练数据集的大小,训练时的日志输出形如:

.
.
.
optimization finished, #iter = 403
nu = 0.718897
obj = -478.778647, rho = -0.238736
nSV = 508, nBSV = 493
Total nSV = 508
*
optimization finished, #iter = 454
nu = 0.734417
obj = -491.057723, rho = -0.318206
nSV = 518, nBSV = 507
Total nSV = 518
*
optimization finished, #iter = 469
nu = 0.722888
obj = -604.608449, rho = -0.360926
nSV = 636, nBSV = 622
Total nSV = 4136
.
.
.

其中: #iter 是迭代次数, nu 是选择的核函数类型的参数, obj 为 SVM 文件转换为的二次规划求解得到的最小值, rho 为判决函数的偏置项 b, nSV 是标准支持向量个数(0 < a[i] < c), nBSV 是边界上的支持向量个数(a[i] = c), Total nSV 是支持向量总个数。

这样我们就得到了模型文件 raw_trained.model ,首先使用你所熟悉的文本编译工具打开此文件,让我们查看一下此文件中的内容:

svm_type c_svc      //所选择的 svm 类型,默认为 c_svc
kernel_type rbf     //训练采用的核函数类型,此处为 RBF 核
gamma 0.333333      //RBF 核的 gamma 系数
nr_class 6      //类别数,此处为六元分类问题
total_sv 4136       //支持向量总个数
rho -0.369589 -0.28443 -0.352834 -0.852275 -0.831555 0.267266 0.158289 -0.777357 -0.725441 -0.271317 
-0.856933 -0.798849 -0.807448 -0.746674 -0.360926       //判决函数的偏置项 b
label 4 1 2 3 0 5       //类别标识
probA -3.11379 -3.0647 -3.2177 -5.78365 -5.55416 -2.30133 -2.26373 -6.05582 -5.99505 -1.07317 -4.50318 
-4.51436 -4.48257 -4.71033 -1.18804
probB 0.099704 -0.00543388 -0.240146 -0.43331 -1.01639 0.230949 0.342831 -0.249265 -0.817104 -0.0249471 
-0.209852 -0.691243 -0.0803133 -0.940074 0.272984
nr_sv 558 1224 880 825 325 324      //每个类的支持向量机的个数
SV
//以下为各个类的权系数及相应的支持向量
1 0 0 0 0 0:14.384883 1:24.418964 2:2.5636304 3:5.7143112 
1 1 1 0 0 0:11.867873 1:23.548919 2:4.5479318 3:4.5074937 
1 0 0 0 0 0:14.647238 1:24.192184 2:4.0759445 3:5.367968 
1 0 0 0 0 0:14.374831 1:24.286867 2:2.0045062 3:5.5710882 
1 0 0 0 0 0:14.099495 1:24.03442 2:2.42664 3:5.7580063 
1 0 0 0 0 0:14.313538 1:25.393975 2:1.9496137 3:5.6174387  
...

得到模型文件之后,首先要进行的就是模型的测试验证,还记得开始进行数据准备的时候,我们分割了训练和测试数据集吗?训练数据集进行了模型的训练,接下来就是测试数据集发挥作用的时候了。

验证模型,LIBSVM提供了另一个命令方法 svm-predict ,使用介绍如下:

Usage: svm-predict [options] test_file model_file output_file
options:
-b probability_estimates: whether to predict probability estimates, 0 or 1 (default 0); for one-class SVM only 0 is supported
-q : quiet mode (no outputs)

使用测试数据集进行模型验证:

svm-predict -b 1 raw_test.txt raw_trained.model predict.out

执行此命令后,LIBSVM会进行识别预测,由于我们使用了 -b 1 参数,因此最终会输出各个类别的识别概率到predict.out文件中,并且会输出一个总体的正确率:

Accuracy = 78.4932% (1719/2190) (classification)

可以看到此时我们训练的模型的识别正确率为78.4932%。

predict.out文件内容形如:

labels 4 1 2 3 0 5
4 0.996517 0.000246958 0.00128824 0.00123075 0.000414204 0.000303014
4 0.993033 0.000643327 0.00456298 0.00103339 0.000427387 0.000299934
1 0.0117052 0.773946 0.128394 0.0848292 0.00065714 0.0004682
1 0.0135437 0.484226 0.343907 0.156548 0.00105013 0.0007251
1 0.0117977 0.885448 0.0256842 0.0761578 0.000513167 0.000399136
3 0.00581106 0.380545 0.120613 0.490377 0.00179286 0.000861917
1 0.0117571 0.91544 0.0145561 0.0573158 0.000524352 0.000406782
1 0.0122297 0.811546 0.0824789 0.0924932 0.000704449 0.000547972
...

其中,第一行为表头,第一列是识别出的类别标签,后面依次跟着各个标签的识别概率。

那么问题来了,难道模型的识别正确率就只能到这个程度了吗?我们再次回顾 svm-train 命令,其中有很多的参数我们都使用了默认的设置,并没有进行特定的设置。通过查看LIBSVM官方的文档,发现竟然提供了 参数寻优 的工具 tools/grid.py ,通过此工具可以自动寻找训练数据集中的最优参数C系数和gamma系数,以在训练的时候使用。具体用法如下:

Usage: grid.py [grid_options] [svm_options] dataset

grid_options :
-log2c {begin,end,step | "null"} : set the range of c (default -5,15,2)
begin,end,step – c_range = 2^{begin,…,begin+kstep,…,end}
"null" – do not grid with c
-log2g {begin,end,step | "null"} : set the range of g (default 3,-15,-2)
begin,end,step – g_range = 2^{begin,…,begin+k
step,…,end}
"null" – do not grid with g
-v n : n-fold cross validation (default 5)
-svmtrain pathname : set svm executable path and name
-gnuplot {pathname | "null"} :
pathname – set gnuplot executable path and name
"null" – do not plot
-out {pathname | "null"} : (default dataset.out)
pathname – set output file path and name
"null" – do not output file
-png pathname : set graphic output file path and name (default dataset.png)
-resume [pathname] : resume the grid task using an existing output file (default pathname is dataset.out)
This is experimental. Try this option only if some parameters have been checked for the SAME data.

svm_options : additional options for svm-train

又是一堆的参数,但是不必担心,对于初学者来说,这里的大部分参数都可以不用设置,直接使用默认值即可,如果你需要查看参数寻优的过程,还需要安装 gnuplot 并按照 官方说明 配置。

python /tools/grid.py -b 1 raw_train.txt

执行此命令后,会不断的输出不同的C系数和gamma系数取值情况下的分类准确率,并在最后一行输出最优的参数选择:

... 
[local] 13 -15 73.1217 (best c=8192.0, g=0.03125, rate=79.3446)
[local] 13 3 72.8477 (best c=8192.0, g=0.03125, rate=79.3446)
[local] 13 -9 77.8488 (best c=8192.0, g=0.03125, rate=79.3446)
[local] 13 -3 78.3741 (best c=8192.0, g=0.03125, rate=79.3446)
8192.0 0.03125 79.3446

并且会在当前目录下生成输出文件raw_train.txt.out和对应的图形文件raw_train.txt.png:

经过最优参数的寻找,最终给出了C系数为8192.0,gamma系数为0.03125的情况下,模型分类的准确率最高,为79.3446。

接下来我们再次使用 svm-train 方法,并设置当前最优C系数值和gamma系数值,重新训练我们的模型:

svm-train -b 1 -c  -g   raw_train.txt raw_bestP_trained.model

训练完成后,得到新的模型文件raw_bestP_trained.model,再次使用测试数据集进行验证:

svm-predict -b 1 raw_test.txt raw_bestP_trained.model bestP_predict.out

最终输出结果如下:

Accuracy = 79.1324% (1733/2190) (classification)

可以看到模型的预测正确率明显提升了不少。上面的参数寻优仅仅是使用了默认的参数进行寻找,你也可以继续尝试设置各个参数进行参数寻优,以进一步提升模型识别正确率,这里不在进行进一步的参数寻优。

小结

可以看到SVM进行用户行为识别,可以得到较好的效果,本文中使用的数据是实验室数据,并且特征也仅仅提取了基本的几个,准确率即可达到79%以上,此方案可以继续进行优化,使用真实世界采集的数据,进行更加详细的特征准备,提高训练时的迭代次数等,进行模型重新训练优化,最终达到更好的分类效果。

下面,我们将在iOS平台下构建应用,并使用LIBSVM和本文中训练所得到的模型,进行准实时人类行为识别。

前面,我们简单介绍了支持向量机以及如何使用LIBSVM类库和加速度传感器数据进行特征的抽取、模型的训练、参数的调优和模型的测试等,在本文中,将使用上篇最终得到的模型文件,以及LIBSVM类库,在iOS平台下构建一个能够识别当前客户端用户的行为类型的应用。

随着移动终端设备的性能越来越高,其集成的传感器设备也越来越多,侦测精度越来越高的情况,应用于移动终端设备上的机器学习应用也多了起来。在iOS平台下,苹果官方的很多应用中也呈现出了机器学习的影子。例如iOS 10 系统中的相册,能够进行人脸识别并进行照片的自动分类、邮件中的自动垃圾邮件归类、Siri智能助理、健康应用中的用户运动类型分类等。

用户的运动类型

在iOS系统的健康应用中,可以看到你的运行类型,其中包含了行走、跑步、爬楼梯、步数、骑自行车等类型。

在iOS SDK中也提供了一个专用于运动类型获取的类 CMMotionActivityManager ,其中包含了

stationary 
walking 
running 
automotive 
cycling 
unknown

几种行为类型,但是在使用的过程中,可能会遇到当前行为和此类给出的结果不相同或者同一时刻有东中类型的情况,这里引用苹果给出的一段结论:

An estimate of the user's activity based on the motion of the device.

The activity is exposed as a set of properties, the properties are not

mutually exclusive.

For example, if you're in a car stopped at a stop sign the state might

look like:

stationary = YES, walking = NO, running = NO, automotive = YES

Or a moving vehicle,

stationary = NO, walking = NO, running = NO, automotive = YES

Or the device could be in motion but not walking or in a vehicle.

stationary = NO, walking = NO, running = NO, automotive = NO.

Note in this case all of the properties are NO.

因此用户行为的识别并不是严格意义上的准确的,在机器学习领域,预测都会有一个概率的输出,引申出的就是正确率, 正确率 也是评估一个机器学习模型的标准之一。

关于加速度传感器

苹果的移动设备中,集成了 多种传感器 ,本文所演示的仅仅使用加速度传感器,你也可以增加传感器类型,提高数据的维度等。

加速度传感器数据 CMAccelerometerData 的类型为CMAcceleration,提供了三轴加速度值,如下:

typedef struct {double x;double y;double z;
} CMAcceleration;
// A structure containing 3-axis acceleration data.

此加速度值是当前设备总的加速度值,想要获取加速度分量的时候,可以使用 CMDeviceMotion 进行获取。

构建iOS项目,收集传感器数据

在上篇中,我们已经知道,LIBSVM具有多种语言的接口,这里我们直接使用其C语言接口,在iOS项目中构建SVM分类器。

1. 传感器数据收集

首先需要收集加速度传感器数据,并进行数据特征抽取和数据准备,以便SVM算法识别使用。在iOS的 CoreMotion 框架中,已经提供了获取加速度传感器的API,开发者可以直接调用接口获取加速度传感器数据:

CMMotionManager *motionManager = [[CMMotionManager alloc] init];if ([motionManager isAccelerometerAvailable]) {[motionManager setAccelerometerUpdateInterval:0.02];
    startTime = [[NSDate date] timeIntervalSince1970];[motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {if (error) {NSLog(@&quot;%@&quot;, error.description);}else{[self handleDeviceAcc:accelerometerData];}}];
}</pre> 

2. 数据批量化处理

我们在本文开始介绍训练数据集的时候,提到了数据的采集频率是 20 Hz ,因此我们在进行数据采集的时候也需要同样的频率,并且将传感器数据进行批量化处理,以便于模型识别时具有合适数量的数据。

NSArray *valueArr = @[@(accelerometerData.acceleration.x * g_value),@(accelerometerData.acceleration.y * g_value),@(accelerometerData.acceleration.z * -g_value)];
NSMutableDictionary *sample = [NSMutableDictionary dictionary];
[sample setValue:currenStatus forKey:@&quot;status&quot;];
[sample setValue:@&quot;acc&quot; forKey:@&quot;sensorName&quot;];
[sample setValue:@([self getTimeStampByMiliSeconds]) forKey:@&quot;timestamp&quot;];
[sample setValue:valueArr forKey:@&quot;values&quot;];if (sampleDatas == nil) {sampleDatas = [NSMutableArray array];
}if ([sampleDatas count] == 256) {NSArray *readySamples = [NSArray arrayWithArray:sampleDatas];sampleDatas = nil;[self stopMotionAccelerometer];[self recognitionData:[readySamples copy]];
}else{[sampleDatas addObject:sample];
}</pre> 

3. 特征抽取

在开始此步骤之前,我们需要导入LIBSVM的类库到项目工程中,这里仅需要导入 svm.hsvm.cpp 两个文件即可。

在训练模型的时候,我们使用了五种特征,最终生成所需要的数据格式,这里同样,我们也需要针对数据进行特征提取,并重新组合数据成为LIBSVM所要求的数据格式:

for (NSUInteger index = 0; index < [raw_datas count]; index++) {NSDictionary *jsonObject = raw_datas[index];NSArray *valuesArray = jsonObject[@"values"];if (!valuesArray || valuesArray.count <= 0) {break;}id acc_x_num = valuesArray[0];id acc_y_num = valuesArray[1];id acc_z_num = valuesArray[2];
    acc_x_axis[index] = acc_x_num;acc_y_axis[index] = acc_y_num;acc_z_axis[index] = acc_z_num;gravity[index] = @(sqrt(pow([acc_x_num doubleValue], 2) + pow([acc_y_num doubleValue], 2) + pow([acc_z_num doubleValue], 2)));}NSMutableArray *values = [NSMutableArray array];
/* mean Feature */{struct svm_node node_x_mean = {0, [StatisticFeature mean:acc_x_axis]};NSValue *node_x_mean_value = [NSValue valueWithBytes:&amp;node_x_mean objCType:@encode(struct svm_node)];[values addObject:node_x_mean_value];struct svm_node node_y_mean = {1, [StatisticFeature mean:acc_y_axis]};NSValue *node_y_mean_value = [NSValue valueWithBytes:&amp;node_y_mean objCType:@encode(struct svm_node)];[values addObject:node_y_mean_value];struct svm_node node_z_mean = {2, [StatisticFeature mean:acc_z_axis]};NSValue *node_z_mean_value = [NSValue valueWithBytes:&amp;node_z_mean objCType:@encode(struct svm_node)];[values addObject:node_z_mean_value];struct svm_node node0 = {3, [StatisticFeature mean:gravity]};NSValue *value0 = [NSValue valueWithBytes:&amp;node0 objCType:@encode(struct svm_node)];[values addObject:value0];
}
/* max Feature */{struct svm_node node_x_max = {4, [StatisticFeature max:acc_x_axis]};NSValue *node_x_max_value = [NSValue valueWithBytes:&amp;node_x_max objCType:@encode(struct svm_node)];[values addObject:node_x_max_value];struct svm_node node_y_max = {5, [StatisticFeature max:acc_y_axis]};NSValue *node_y_max_value = [NSValue valueWithBytes:&amp;node_y_max objCType:@encode(struct svm_node)];[values addObject:node_y_max_value];struct svm_node node_z_max = {6, [StatisticFeature max:acc_z_axis]};NSValue *node_z_max_value = [NSValue valueWithBytes:&amp;node_z_max objCType:@encode(struct svm_node)];[values addObject:node_z_max_value];struct svm_node node1 = {7, [StatisticFeature max:gravity]};NSValue *value1 = [NSValue valueWithBytes:&amp;node1 objCType:@encode(struct svm_node)];[values addObject:value1];
}
/* min Feature */{struct svm_node node_x_min = {8, [StatisticFeature min:acc_x_axis]};NSValue *node_x_min_value = [NSValue valueWithBytes:&amp;node_x_min objCType:@encode(struct svm_node)];[values addObject:node_x_min_value];struct svm_node node_y_min = {9, [StatisticFeature min:acc_y_axis]};NSValue *node_y_min_value = [NSValue valueWithBytes:&amp;node_y_min objCType:@encode(struct svm_node)];[values addObject:node_y_min_value];struct svm_node node_z_min = {10, [StatisticFeature min:acc_z_axis]};NSValue *node_z_min_value = [NSValue valueWithBytes:&amp;node_z_min objCType:@encode(struct svm_node)];[values addObject:node_z_min_value];struct svm_node node2 = {11, [StatisticFeature min:gravity]};NSValue *value2 = [NSValue valueWithBytes:&amp;node2 objCType:@encode(struct svm_node)];[values addObject:value2];
}
/* stev Feature */{struct svm_node node_x_stev = {12, [StatisticFeature stev:acc_x_axis]};NSValue *node_x_stev_value = [NSValue valueWithBytes:&amp;node_x_stev objCType:@encode(struct svm_node)];[values addObject:node_x_stev_value];struct svm_node node_y_stev = {13, [StatisticFeature stev:acc_y_axis]};NSValue *node_y_stev_value = [NSValue valueWithBytes:&amp;node_y_stev objCType:@encode(struct svm_node)];[values addObject:node_y_stev_value];struct svm_node node_z_stev = {14, [StatisticFeature stev:acc_z_axis]};NSValue *node_z_stev_value = [NSValue valueWithBytes:&amp;node_z_stev objCType:@encode(struct svm_node)];[values addObject:node_z_stev_value];struct svm_node node3 = {15, [StatisticFeature stev:gravity]};NSValue *value3 = [NSValue valueWithBytes:&amp;node3 objCType:@encode(struct svm_node)];[values addObject:value3];
}</pre> 

这里需要注意的是,特征的顺序必须和模型训练时训练数据集中的特征顺序一致,否则预测的结果将出现严重的偏差。

4. 导入模型文件并加载

完成了数据准备之后,我们导入之前训练好的模型文件 raw_bestP_trained.model 到项目中,然后使用LIBSVM提供的模型加载方法,加载模型到 svm_model 结构体对象:

struct svm_model * model = svm_load_model([model_dir UTF8String]);
if (model == NULL) {NSLog(@&quot;Can't open model file: %@&quot;,model_dir);return nil;
}
if (svm_check_probability_model(model) == 0) {NSLog(@&quot;Model does not support probabiliy estimates&quot;);return nil;
}</pre> 

4. 行为识别

LIBSVM提供了多个方法进行预测,为了最终看到预测的概率,我们使用

double svm_predict_probability(const struct svm_model *model, const struct svm_node *x, double* prob_estimates);

方法,在输出预测结果的时候,会带有对应的概率:

//Type of svm modelint svm_type = svm_get_svm_type(model);//Count of labelsint nr_class = svm_get_nr_class(model);//Label of svm modelint *labels = (int *) malloc(nr_class*sizeof(int));svm_get_labels(model, labels);
// Probability of each possible label in result
double *prob_estimates = (double *) malloc(nr_class*sizeof(double));
// Predicting
// result of prediction including:
// - Most possible label
// - Probability of each possible label
double label = 0.0;
if (svm_type == C_SVC || svm_type == NU_SVC) {label = svm_predict_probability(model, X, prob_estimates); NSLog(@&quot;svm_predict_probability label: %f&quot;,label);
}else{NSLog(@&quot;svm_type is not support !!!&quot;);return nil;
}</pre> 

通过以上的预测之后,最终的预测结果就是 label ,并在会在 prob_estimates 中输出各个分类标签的预测概率。

!!注意 !!

** prob_estimates 中仅仅会输出概率,并不会输出概率和标签的对应关系。prob_estimates中的概率顺序是和模型中的输入标签顺序一致的,需要注意! **

最终的预测结果如下:

label: 4 -- prob: 0.491513 
label: 1 -- prob: 0.285421 
label: 2 -- prob: 0.119973 
label: 3 -- prob: 0.096848 
label: 0 -- prob: 0.002580 
label: 5 -- prob: 0.003665

关于模型的评估

分类模型的度量有很多方式,例如混淆矩阵(Confusion Matrix)、ROC曲线、AUC面积、Lift(提升)和Gain(增益)、K-S图、基尼系数等,这里我们使用ROC曲线评估我们最终得到的模型,以查看模型的质量,最终的ROC曲线图如下:

可以看到该模型针对某些行为的识别能力较好,例如站立、慢跑,但是对另一些行为的识别却不怎么好了,例如下楼梯。

总结

可以看到SVM在分类问题上能够很好的识别特征进行类别区分。由于篇幅原因,本文中并没有对数据的特征进行更加细致的选择和抽取,可能会导致一些行为类型的识别不能达到理想的效果,但是相信在足量的数据下,进行更加细致的特征工程后,利用SVM在分类能力上的优势,能够构建出更加优秀的人类行为类型识别的智能应用。

感谢徐川对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/54982.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android手机连nas,安卓手机照样行 NAS存储器大盘点_群晖 USB Station 2_移动存储新闻-中关村在线...

笔者在发布厅外面的走廊&#xff0c;使用安卓系统的HTC手机连接发布厅里的群晖小型家用NAS网络存储器&#xff0c;实验一下效果如何&#xff1f; 使用安卓系统的HTC手机&#xff0c;安装好DSM4.0系统 同样需要到网上下载相应的安卓版DSM4.0软件&#xff0c;安装后&#xff0c;和…

手机壳释放致癌物质“苯”吗?专家:确实会

央视视屏截图 □记者 宁田甜 贺笑天 文图 核心提示 | 手机壳真的会释放出致癌物质苯吗&#xff1f;在运行中的电梯和地铁里打电话&#xff0c;辐射会是平时的多倍&#xff0c;这是真的吗&#xff1f;针对这些网络传言&#xff0c;大河报《生活实验》栏目&#xff0c;联手央视财…

测试手机速度的软件叫什么,手机测速网速软件哪个最好(手把手教你自测网速的方法)...

每个人每天都离不开网络&#xff0c;无论是家庭宽带还是公共WiFi&#xff0c;网速都是极为重要的一部分&#xff0c;那能够清晰地知道自己的网速如何就显得尤为重要了&#xff01;今天&#xff0c;就为大家带来了几款测速的软件&#xff0c;大家可以测一下自己的网速到没到心理…

手机视频连接计算机显示器播放,教您使用手机/平板电脑直接在计算机上播放视频...

我相信&#xff0c;在阅读了前面的教程之后&#xff0c;您已经在计算机上下载了许多电影和电视节目. 因此&#xff0c;是时候在国庆假期呆在家里了. 好吧&#xff0c;开个玩笑. 但是&#xff0c;本教程是为了使主要参与者更容易. 不必将视频复制到“手机/平板电脑”上&#xff…

练习英语口语的方法

练习英语口语的方法&#xff08;知乎精华问答&#xff09;整理版 1初期&#xff1a;我应该可以跳过去了&#xff0c;但是据说这个 eslpod 很不错&#xff0c;用来磨磨耳朵&#xff08;还推荐了&#xff1a;Englishpod和BBC Learning English&#xff09; 2.提高期&#xff1a;A…

InsCode AI 创作助手:解锁高效写作新姿势,让你的创作助手总在身边!

# 前言 CSDN AI写作助手上线了&#xff01;InsCode AI 创作助手不仅能够帮助用户高效创作文章&#xff0c;而且能够作为对话式AI回答你想知道的问题。成倍提高生产力&#xff01;欢迎大家使用新功能后分享自己的使用心得与建议&#xff01; 目录 一、你平时会使用这类AI工具吗…

“中国版”马斯克被本尊翻牌:“如果是真的,我想见见他”

近日&#xff0c;推特上有网友发布了特斯拉CEO埃隆马斯克&#xff08;Elon Musk&#xff09;与“中国版”马斯克的对比图片&#xff0c;并马斯克问到&#xff1a;“我们是否应该部署一个‘中国版’马斯克作为诱饵&#xff1f;” 对此&#xff0c;马斯克本人在评论区做出了回应…

马斯克被曝正在“招兵买马”,准备进军AI赛道

知情人士透露&#xff0c;马斯克最近几周接触了人工智能研究人员&#xff0c;打算成立一个新的研究实验室&#xff0c;开发聊天机器人ChatGPT的竞品。 为了开展这项工作&#xff0c;马斯克一直在向Igor Babuschkin抛橄榄枝&#xff0c;这位研究人员曾经在谷歌和OpenAI任职&…

技术狂们设计的独特组织模式,成为OpenAI吸引“梦之队”的利器

全文字数&#xff1a;3000字&#xff0c;阅读时间&#xff1a;10分钟 点击上方蓝色文字 关注我们 这是我的第276篇专栏文章&#xff0c;我在【数字原生组织】写的第3篇文章。 按照上周的约定&#xff0c;今天这篇文章&#xff0c;我们来探讨企业如何迈向业务成果即服务的转型之…

神器!基于ChatGPT模型的IDA插件

插件介绍 基于与 ChatGPT 相同模型的IDA 插件&#xff0c;使用 OpenAI 发布的 gpt-3.5-turbo 模型&#xff0c;可以有助于分析师们快速分析二进制文件。 关注【Hack分享吧】公众号&#xff0c;回复关键字【230719】获取下载链接 当前 WPeChatGPT 支持的功能包括&#xff1a; …

R语言作图代码总结

目录 条形图普通条形图分组簇状条形图 分布密度图 条形图 普通条形图 ggplot(df,],aes(xgroup,yvalue)) geom_bar(stat "identity",width 0.57) #width是条形宽度 theme_bw() #去掉灰色的背景 scale_x_discrete(labelsc("AUC-B","AUC-P")) #…

R语言编写代码的方式

大部分的代码都是基于控制台的交互式操作&#xff0c;但是对于那些要重复好多次的程序片段&#xff0c;将其保存为一段R程序文件是一个不错的选择。通常&#xff0c;R程序以ASCII格式保存&#xff0c;扩展名为“.R”。 可以在类似记事本、Sumblime Text等文本编辑器中编辑R语言…

chatgpt赋能python:如何用Python发短信:介绍

如何用Python发短信&#xff1a;介绍 Python是一种高级编程语言&#xff0c;被广泛用于开发各种类型的应用程序。其中一种应用程序就是发短信。在本篇文章中&#xff0c;我们将探讨如何用Python发送短信。 发送短信的技术 发送短信有几种技术可供选择&#xff0c;其中包括基…

chatgpt赋能python:Python发短信脚本:一次实现多种操作方式

Python 发短信脚本&#xff1a;一次实现多种操作方式 介绍 在现代社会&#xff0c;短信已经成为一种非常重要的信息交流方式。尤其是在紧急情况下&#xff0c;短信可以快速传达信息&#xff0c;帮助我们解决问题。对于开发人员&#xff0c;如果能够编写一些方便快捷的短信发送…

chatgpt赋能python:Python如何在拼多多9.9抢购活动中获得更好的SEO排名

Python如何在拼多多9.9抢购活动中获得更好的SEO排名 介绍 随着电商行业的迅猛发展&#xff0c;越来越多的商家开始通过拼多多等平台进行产品销售。在拼多多的9.9抢购活动中&#xff0c;想要获得更好的曝光和销售&#xff0c;拥有更好的SEO排名是必不可少的。而Python作为一种…

王小川大模型首亮相!70亿参数霸榜,清北抢先用

新智元报道 编辑&#xff1a;好困 桃子 【新智元导读】今天&#xff0c;百川智能正式发布70亿参数开源中英文大模型——baichuan-7B&#xff0c;一举拿下多个评测榜单最佳成绩。 时隔两个月&#xff0c;王小川组建的「百川智能」在6月15日正式推出首个70亿参数中英文预训练大…

微信小助手 for mac(WeChatPlugin)使用攻略

Mac Os微信多开助手软件特征 消息自动回复消息防撤回远程控制(已支持语音)微信多开第二次登录免认证聊天置底功能(类似置顶)微信窗口置顶会话多选删除自动登录开关通知中心快捷回复聊天窗口表情包复制 & 存储小助手检测更新提醒alfred 快捷发送消息 打开窗口 (需安装&…

WeChatTweak-微信小助手安装教程

github下载&#xff1a;https://github.com/Sunnyyoung/WeChatTweak-macOS CSDN下载&#xff1a;https://download.csdn.net/download/weixin_45477086/83895866 双击解压下载的WeChatTweak-macOS-***.zip在终端输入cd ,并敲一个空格&#xff0c;然后把解压的文件夹拖到终端 …

关于自己对像Chat-GPT的反应速度感悟

这几个月相信大家应该对ChatGPT都不陌生了吧&#xff0c;因为这个东西已经在各大社交媒体可以说是无限次曝光了&#xff0c;就连一些其他行业的(完全跟科技行业沾不上边的朋友们)都知道了。可想而知&#xff0c;这个是有多火了。 而我之所以发表这个感悟&#xff0c;其实也是自…

Swift 周报 第十九期 |技术汇总

前言 本期是 Swift 编辑组自主整理周报的第十期&#xff0c;每个模块已初步成型。各位读者如果有好的提议&#xff0c;欢迎在文末留言。 欢迎投稿或推荐内容。目前计划每两周周一发布&#xff0c;欢迎志同道合的朋友一起加入周报整理。 十期磨一剑&#xff0c;废铁亦有形&am…