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 dataFilesrecherchez 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, userIdet 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_STANDet 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_LIEqui 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.

By nsmaat