Préface
Pendant lequel cet étude, moi-même décrirons pardon exciper les circonstance de l’accéléromètre et du gyropilote des smartphones à cause supposer les activités physiques des entités somme les téléphones. Les circonstance utilisées chez cet étude proviennent de l’chorus de circonstance sur la déclaration des activités humaines et des transitions posturales par smartphone distribué par l’Gymnase de Californie à Irvine. Trente entités ont été chargées d’fabriquer diverses activités de squelette pour un smartphone connecté enregistrant le devenir à l’coadjuteur d’un accéléromètre et d’un gyropilote.
Entrée de entreprendre, chargeons les disparates bibliothèques que moi-même utiliserons chez l’montré :
library(keras) # Neural Networks
library(tidyverse) # Data cleaning / Visualization
library(knitr) # Établissement printing
library(rmarkdown) # Misc. produit utilities
library(ggridges) # Visualization
Jeu de circonstance sur les activités
Les circonstance utilisées chez cet étude proviennent de l’chorus de circonstance sur la déclaration des activités humaines et des transitions posturales par smartphone(Reyes-Ortiz et al. 2016) distribué par l’Gymnase de Californie, Irvine.
Lorsqu’elles-mêmes sont téléchargées à commencer du proximité plus haut, les circonstance contiennent double “parties” disparates. Une qui a été prétraitée à l’coadjuteur de diverses techniques d’avulsion de caractéristiques équivalentes que la transformée de Fourier bref, et une étrange RawData
quartier qui prête sincèrement les commandements X,Y,Z brutes d’un accéléromètre et d’un gyropilote. Annulé des filtres de bain ou d’avulsion de caractéristiques normalisé utilisés chez les circonstance d’accéléromètre n’a été appliqué. C’est l’chorus de circonstance que moi-même utiliserons.
La tendance à cause fermenter pour les circonstance brutes chez cet étude est de aplanir la métamorphose du justice/des concepts environs des circonstance de séries chronologiques chez des domaines moins avoir caractérisés. Avoir qu’un modèle surtout palpable puisse concerner forgé en utilisant les circonstance filtrées/nettoyées fournies, le filtration et la rectification peuvent onduler notablement d’une tâche à l’étrange ; nécessitant abondamment d’efforts manuels et de connaissances du seigneurie. L’une des belles choses opportunément de l’ébauche en proportion est que l’avulsion de caractéristiques est apprise à commencer des circonstance, et non de connaissances extérieures.
Libellés d’passage
Les circonstance ont des encodages intégraux à cause les activités qui, avoir que non importantes à cause le modèle celui-ci, sont utiles à aviser. Chargeons-les d’apparence.
activityLabels <- read.cuistance("data/activity_labels.txt",
col.names = c("number", "marque"))
activityLabels %>% kable(align = c("c", "l"))
1 | MARCHE |
2 | WALKING_UPSTAIRS |
3 | WALKING_DOWNSTAIRS |
4 | SÉANCE |
5 | DEBOUT |
6 | POSE |
7 | STAND_TO_SIT |
8 | SIT_TO_STAND |
9 | SIT_TO_LIE |
dix | LIE_TO_SIT |
11 | STAND_TO_LIE |
12 | LIE_TO_STAND |
Derrière, moi-même chargeons la clé labels à cause le RawData
. Ce classeur est une relevé de toutes les notes, ou enregistrements d’activités individuelles, contenues chez l’chorus de circonstance. La clé des colonnes est tirée des circonstance README.txt
.
Column 1: experiment number ID,
Column 2: râper number ID,
Column 3: activity number ID
Column 4: Montre start conclusion
Column 5: Montre end conclusion
Les points de appât et de fin sont en rassemblement d’échantillons de biographie de branle-bas (enregistrés à 50 Hz).
Examinons les 50 premières échelons :
labels <- read.cuistance(
"data/RawData/labels.txt",
col.names = c("experiment", "userId", "activity", "startPos", "endPos")
)
labels %>%
head(50) %>%
paged_table()
Noms de fichiers
Examinons puis les fichiers réels des circonstance client qui moi-même ont été fournis chez RawData/
dataFiles <- list.files("data/RawData")
dataFiles %>% head()
(1) "acc_exp01_user01.txt" "acc_exp02_user01.txt"
(3) "acc_exp03_user02.txt" "acc_exp04_user02.txt"
(5) "acc_exp05_user03.txt" "acc_exp06_user03.txt"
Il existe un diagramme de nommage de fichiers en trio parties. La ouverture section est le sorte de circonstance que contient le classeur : paradoxe acc
à cause accéléromètre ou gyro
à cause gyropilote. Vient puis le matricule de l’habitude et bref l’identifiant de l’client à cause l’ratification. Chargeons-les chez une squelette de circonstance à cause en aplanir l’manipulation derrière.
fileInfo <- data_frame(
filePath = dataFiles
) %>%
filter(filePath != "labels.txt") %>%
separate(filePath, sep = '_',
into = c("sorte", "experiment", "userId"),
remove = FALSE) %>%
mutate(
experiment = str_remove(experiment, "exp"),
userId = str_remove_all(userId, "râper|.txt")
) %>%
spread(sorte, filePath)
fileInfo %>% head() %>% kable()
01 | 01 | acc_exp01_user01.txt | gyro_exp01_user01.txt |
02 | 01 | acc_exp02_user01.txt | gyro_exp02_user01.txt |
03 | 02 | acc_exp03_user02.txt | gyro_exp03_user02.txt |
04 | 02 | acc_exp04_user02.txt | gyro_exp04_user02.txt |
05 | 03 | acc_exp05_user03.txt | gyro_exp05_user03.txt |
06 | 03 | acc_exp06_user03.txt | gyro_exp06_user03.txt |
Culture et mendie de circonstance
Entrée de empire former desquels que ce paradoxe pour les circonstance fournies, moi-même redevons les construire chez un largeur idoine au modèle. Ceci signifie que moi-même voulons garder une relevé d’notes, à eux dignité (ou plaque d’passage), et les circonstance en accord à l’ratification.
Pile l’conserver, moi-même allons sauter chaque homme des fichiers d’ratification présents chez dataFiles
recherchez les notes contenues chez l’ratification, extrayez ces enregistrements et renvoyez le somme chez un modèle indulgent à modéliser pour une conspiration de circonstance.
# Read contents of single défilé to a dataframe with accelerometer and gyro data.
readInData <- function(experiment, userId){
genFilePath = function(sorte) {
paste0("data/RawData/", sorte, "_exp",experiment, "_user", userId, ".txt")
}
bind_cols(
read.cuistance(genFilePath("acc"), col.names = c("a_x", "a_y", "a_z")),
read.cuistance(genFilePath("gyro"), col.names = c("g_x", "g_y", "g_z"))
)
}
# Function to read a given défilé and get the notes contained along
# with their classes.
loadFileData <- function(curExperiment, curUserId) {
# load sensor data from défilé into dataframe
allData <- readInData(curExperiment, curUserId)
extractObservation <- function(startPos, endPos){
allData(startPos:endPos,)
}
# get glose locations in this défilé from labels dataframe
dataLabels <- labels %>%
filter(userId == as.integer(curUserId),
experiment == as.integer(curExperiment))
# extract notes as dataframes and save as a column in dataframe.
dataLabels %>%
mutate(
data = map2(startPos, endPos, extractObservation)
) %>%
select(-startPos, -endPos)
}
# scan through all experiment and userId combos and gather data into a dataframe.
allObservations <- map2_df(fileInfo$experiment, fileInfo$userId, loadFileData) %>%
right_join(activityLabels, by = c("activity" = "number")) %>%
rename(activityName = marque)
# décadence work.
write_rds(allObservations, "allObservations.rds")
allObservations %>% dim()
Avouer les circonstance
Gardant que moi-même avons toutes les circonstance chargées pour le experiment
, userId
et activity
écriteaux, moi-même pouvons inspecter l’chorus de circonstance.
Étape des enregistrements
Regardons d’apparence la persistance des enregistrements par passage.
allObservations %>%
mutate(recording_length = map_int(data,nrow)) %>%
ggplot(aes(x = recording_length, y = activityName)) +
geom_density_ridges(début = 0.8)
Le cataclysme qu’il y ait une équivalente contraste de persistance d’ratification pénétré les autres bonshommes d’activités moi-même vigueur à concerner un peu prudents chez la préparation laquelle moi-même procédons. Si moi-même entraînons le modèle sur tout dignité à la coup, moi-même devrons garnir toutes les notes à la format la surtout éternelle, ce qui laisserait une longue affranchissement des notes pour une exorbitant largeur de à elles circonstance n’accomplissant que des zéros de vide. Pile cette entendement, moi-même adapterons à nous modèle au surtout aristocrate “rapprochement” d’activités de format d’notes, celles-ci incluent STAND_TO_SIT
, STAND_TO_LIE
, SIT_TO_STAND
, SIT_TO_LIE
, LIE_TO_STAND
et LIE_TO_SIT
.
Une administration future intéressante serait d’tâtonner d’exciper une étrange armature équivalente qu’un RNN chevronné de conduire des tapas de format capricieux et de l’mener sur toutes les circonstance. Exclusivement, toi-même courriez le hasard que le modèle apprenne sincèrement que si l’glose est éternelle, il s’agit terriblement apparemment de l’une des quatre classes les surtout longues qui ne se généraliserait pas à un continuité où toi-même exécutiez ce modèle sur un dégoulinade de circonstance en moment concret. .
Purification des activités
Sur la squelette de à nous procréation plus haut, moi-même allons sous-ensemble les circonstance à cause concerner convenable des activités d’retard.
desiredActivities <- c(
"STAND_TO_SIT", "SIT_TO_STAND", "SIT_TO_LIE",
"LIE_TO_SIT", "STAND_TO_LIE", "LIE_TO_STAND"
)
filteredObservations <- allObservations %>%
filter(activityName %in% desiredActivities) %>%
mutate(observationId = 1:n())
filteredObservations %>% paged_table()
Pour, en conséquence à nous ébranchement querelleur des circonstance, il moi-même restera une collection élevé de circonstance sur lésiner à nous modèle pourra opter.
Classement quart/référence
Entrée d’aventurer surtout ailleurs chez l’aveu des circonstance de à nous modèle, chez le but d’concerner autant convenable que conciliable pour nos mesures de victoire, moi-même redevons classer les circonstance en un chorus d’spécimen et de référence. Existant donné que tout client n’a parfait toutes les activités qu’une individuelle coup (à l’monstruosité d’un qui n’a parfait que 10 des 12 activités) en se répartissant sur userId
moi-même veillerons à ce que à nous modèle truchement de nouvelles entités néanmoins lors moi-même le testerons.
# get all users
userIds <- allObservations$userId %>% jaloux()
# randomly choose 24 (80% of 30 individuals) for jogging
set.seed(42) # seed for reproducibility
trainIds <- sample(userIds, size = 24)
# set the rest of the users to the testing set
testIds <- setdiff(userIds,trainIds)
# filter data.
trainData <- filteredObservations %>%
filter(userId %in% trainIds)
testData <- filteredObservations %>%
filter(userId %in% testIds)
Voir les activités
Gardant que moi-même avons enfoncé nos circonstance en supprimant des activités et en divisant un chorus de tests, moi-même pouvons naïvement voir les circonstance de tout dignité à cause aviser s’il existe une tournure soudainement sensible que à nous modèle pourrait découvrir.
Commençons par déballer nos circonstance de sa squelette de circonstance d’une garniture par glose à une thème harmonieuse de toutes les notes.
unpackedObs <- 1:nrow(trainData) %>%
map_df(function(rowNum){
dataRow <- trainData(rowNum, )
dataRow$data((1)) %>%
mutate(
activityName = dataRow$activityName,
observationId = dataRow$observationId,
time = 1:n() )
}) %>%
gather(reading, value, -time, -activityName, -observationId) %>%
separate(reading, into = c("sorte", "administration"), sep = "_") %>%
mutate(sorte = ifelse(sorte == "a", "acceleration", "gyro"))
Gardant que moi-même avons un chorus décompressé de nos notes, visualisons-les !
unpackedObs %>%
ggplot(aes(x = time, y = value, color = administration)) +
geom_line(début = 0.2) +
geom_smooth(se = FALSE, début = 0.7, size = 0.5) +
facet_grid(sorte ~ activityName, scales = "free_y") +
theme_minimal() +
theme( axis.text.x = element_blank() )
Pour, au moins chez les modèles de circonstance de l’accéléromètre jaillissant irrémédiablement. On pourrait enfanter que le modèle puisse garder du mal pour les différences pénétré LIE_TO_SIT
et LIE_TO_STAND
car ils ont un aspect semblable en norme. C’est la même sujet à cause SIT_TO_STAND
et STAND_TO_SIT
.
Prétraitement
Entrée de empire mener le coiffure de neurones, moi-même redevons ensuivre un couple de étapes à cause prétraiter les circonstance.
Commentaire de matelassure
Nous-même allons d’apparence amener à quoi format garnir (et censurer) nos séquences en vivant quoi est la format du 98e centile. En n’utilisant pas la format d’glose la surtout éternelle, ceci moi-même aidera à détourner que les enregistrements extra-longs ne gâchent le matelassure.
padSize <- trainData$data %>%
map_int(nrow) %>%
quantile(p = 0.98) %>%
ceiling()
padSize
98%
334
Gardant, moi-même redevons sincèrement échanger à nous relevé d’notes en matrices, ensuite exciper le étonnant servant pad_sequences()
empile chez Keras à cause garnir toutes les notes et les produire en un tenseur 3D à cause moi-même.
convertToTensor <- . %>%
map(as.matrix) %>%
pad_sequences(maxlen = padSize)
trainObs <- trainData$data %>% convertToTensor()
testObs <- testData$data %>% convertToTensor()
dim(trainObs)
(1) 286 334 6
Agréable, moi-même avons imitation nos circonstance chez un élégant largeur conciliable pour les réseaux de neurones d’un tenseur 3D pour des dimensions (<num obs>, <sequence length>, <channels>)
.
Codification à exalté
Il y a une dernière sujet que moi-même redevons former façade de empire créer à nous modèle, et c’est produire nos classes d’glose d’intégraux en vecteurs codés factices. Bien, surtout une coup, Keras moi-même a buissonneux une empile terriblement essentiel à cause former à la lettre ceci.
oneHotClasses <- . %>%
{. - 7} %>% # bring integers down to 0-6 from 7-12
to_categorical() # One-hot encode
trainY <- trainData$activity %>% oneHotClasses()
testY <- testData$activity %>% oneHotClasses()
La modélisation
Armature
Vu que moi-même avons des circonstance de séries temporelles denses chez le moment, moi-même utiliserons des enfantement convolutives 1D. Plus des circonstance réellement denses, un RNN doit opter de terriblement longues dépendances pendant de dominer les modèles, les CNN peuvent sincèrement couvrir un couple de enfantement convolutives à cause créer des tableaux de modèles d’une format substantielle. Existant donné que moi-même recherchons comme sincèrement une individuelle catégorisation d’passage à cause tout glose, moi-même pouvons sincèrement exciper le rassemblement à cause «écourter» la vue CNN des circonstance chez une revêtement fort.
En surtout d’couvrir double layer_conv_1d()
enfantement, moi-même utiliserons la principe de lot et l’sacrifice (la version spatiale(Tompson et al. 2014) sur les enfantement convolutives et normalisé sur les denses) à cause normaliser le coiffure.
input_shape <- dim(trainObs)(-1)
num_classes <- dim(trainY)(2)
filters <- 24 # number of convolutional filters to learn
kernel_size <- 8 # how many time-steps each conv layer sees.
dense_size <- 48 # size of our penultimate fort layer.
# Initialize model
model <- keras_model_sequential()
model %>%
layer_conv_1d(
filters = filters,
kernel_size = kernel_size,
input_shape = input_shape,
padding = "valid",
accélération = "épelé"
) %>%
layer_batch_normalization() %>%
layer_spatial_dropout_1d(0.15) %>%
layer_conv_1d(
filters = filters/2,
kernel_size = kernel_size,
accélération = "épelé",
) %>%
# Apply average pooling:
layer_global_average_pooling_1d() %>%
layer_batch_normalization() %>%
layer_dropout(0.2) %>%
layer_dense(
dense_size,
accélération = "épelé"
) %>%
layer_batch_normalization() %>%
layer_dropout(0.25) %>%
layer_dense(
num_classes,
accélération = "softmax",
name = "dense_output"
)
summary(model)
______________________________________________________________________
Layer (sorte) Produit Shape Param #
======================================================================
conv1d_1 (Conv1D) (None, 327, 24) 1176
______________________________________________________________________
batch_normalization_1 (BatchNo (None, 327, 24) 96
______________________________________________________________________
spatial_dropout1d_1 (SpatialDr (None, 327, 24) 0
______________________________________________________________________
conv1d_2 (Conv1D) (None, 320, 12) 2316
______________________________________________________________________
global_average_pooling1d_1 (Gl (None, 12) 0
______________________________________________________________________
batch_normalization_2 (BatchNo (None, 12) 48
______________________________________________________________________
dropout_1 (Dropout) (None, 12) 0
______________________________________________________________________
dense_1 (Pâteux) (None, 48) 624
______________________________________________________________________
batch_normalization_3 (BatchNo (None, 48) 192
______________________________________________________________________
dropout_2 (Dropout) (None, 48) 0
______________________________________________________________________
dense_output (Pâteux) (None, 6) 294
======================================================================
Entier params: 4,746
Trainable params: 4,578
Non-trainable params: 168
______________________________________________________________________
Aperçu
Nous-même pouvons imitation mener le modèle à l’coadjuteur de nos circonstance de référence et d’spécimen. Notez que moi-même utilisons callback_model_checkpoint()
à cause moi-même placer que moi-même n’enregistrons que la meilleure oscillation du modèle (optative car à un données donné de la quart, le modèle peut entreprendre à sur-ajuster ou interrompre de s’progresser).
# Compile model
model %>% compile(
loss = "categorical_crossentropy",
optimizer = "rmsprop",
metrics = "accuracy"
)
trainHistory <- model %>%
fit(
x = trainObs, y = trainY,
epochs = 350,
validation_data = list(testObs, testY),
callbacks = list(
callback_model_checkpoint("best_model.h5",
save_best_only = TRUE)
)
)
Le modèle apprend afin sujet ! Nous-même obtenons une concision élevé de 94,4% sur les circonstance de maintien, pas mal pour six classes possibles au adoption. Examinons un peu surtout en proportion les performances de maintien à cause aviser où le modèle se dupe.
Comparaison
Gardant que moi-même avons un modèle formé, examinons les errata qu’il a commises sur nos circonstance de référence. Nous-même pouvons embâter le principal modèle de la quart en empile de la concision de la maintien, ensuite décomposer tout glose, ce que le modèle a juré, la pronostic qu’il a attribuée et la réel plaque d’passage.
# dataframe to get labels onto one-hot encoded prediction columns
oneHotToLabel <- activityLabels %>%
mutate(number = number - 7) %>%
filter(number >= 0) %>%
mutate(class = paste0("V",number + 1)) %>%
select(-number)
# Load our best model checkpoint
bestModel <- load_model_hdf5("best_model.h5")
tidyPredictionProbs <- bestModel %>%
predict(testObs) %>%
as_data_frame() %>%
mutate(obs = 1:n()) %>%
gather(class, prob, -obs) %>%
right_join(oneHotToLabel, by = "class")
predictionPerformance <- tidyPredictionProbs %>%
group_by(obs) %>%
summarise(
highestProb = max(prob),
predicted = marque(prob == highestProb)
) %>%
mutate(
truth = testData$activityName,
tolérable = truth == predicted
)
predictionPerformance %>% paged_table()
Intégral d’apparence, regardons quel nombre le modèle accomplissait “liant” si la présage accomplissait correcte ou non.
predictionPerformance %>%
mutate(result = ifelse(tolérable, 'Poli', 'Erroné')) %>%
ggplot(aes(highestProb)) +
geom_histogram(binwidth = 0.01) +
geom_rug(début = 0.5) +
facet_grid(result~.) +
ggtitle("Probabilities associated with prediction by correctness")
Il semble apaisant que le modèle accomplissait, en norme, moins liant relativement à ses classifications à cause les résultats incorrects que à cause les célestes. (Avoir que la longueur de l’aperçu paradoxe éperdument subtile à cause prétexte desquels que ce paradoxe de étudié définitive.)
Voyons quelles activités le modèle a eu le surtout de mal à exciper une utérus de alliance.
predictionPerformance %>%
group_by(truth, predicted) %>%
summarise(count = n()) %>%
mutate(good = truth == predicted) %>%
ggplot(aes(x = truth, y = predicted)) +
geom_point(aes(size = count, color = good)) +
geom_text(aes(marque = count),
hjust = 0, vjust = 0,
nudge_x = 0.1, nudge_y = 0.1) +
guides(color = FALSE, size = FALSE) +
theme_minimal()
On voit que, identique le suggérait la affichage précurseur, le modèle avait un peu de mal à former la agrément pénétré LIE_TO_SIT
et LIE_TO_STAND
promenade, donc que les SIT_TO_LIE
et STAND_TO_LIE
qui ont comme des profils visuels comparables.
États-majors futures
L’téléologie future la surtout ouverte à cause cette montré serait de hasarder de payer le modèle surtout mondial en amusant pour un surtout aristocrate rassemblement de bonshommes d’activités fournis. Une étrange administration intéressante serait de ne pas délimiter les enregistrements en “notes” divergentes, néanmoins de préférence de les classer identique un chorus de circonstance en perpétuel, un peu identique si un déploiement concret d’un modèle fonctionnerait, et de aviser chez quoi proportion un modèle pourrait attribuer les circonstance en perpétuel et découvrir changements d’passage.
Gal, Yarin et Zoubin Ghahramani. 2016. “Dévissage en beaucoup qu’expertise bayésienne : consulat de l’obscurité du modèle chez l’ébauche en proportion.” Pendant lequel Discussion internationale sur l’ébauche procédural1050–9.
Pourpre, Alex. 2012. “Étiquetage de suite supervisée.” Pendant lequel Étiquetage de suite supervisé pour des réseaux de neurones récurrents, 5–13. Springer.
Kononenko, Igor. 1989. “Réseaux de neurones bayésiens”. Cybernétique biotique 61 (5). Springer : 361–70.
LeCun, Yann, Yoshua Bengio et Geoffrey Hinton. 2015. “Dégrossissage en proportion”. Tempérament 521 (7553). Tend d’tirage Tempérament : 436.
Reyes-Ortiz, Jorge-L, Luca Oneto, Albert Samà, Xavier Parra et Davide Anguita. 2016. “Révélation de l’passage charitable détenant note de la métamorphose à l’coadjuteur de smartphones.” Neuroinformatique 171. Elsevier : 754–67.
Tompson, Jonathan, Ross Goroshin, Arjun Jain, Yann LeCun et Christoph Bregler. 2014. “Détection effective d’objets à l’coadjuteur de réseaux convolutifs”. CoRR abs/1411.4280. http://arxiv.org/abs/1411.4280.