Je vais vous présenter un exemple d'objet connecté et comment analyser ses données. L'objectif est de voir comment collecter les données depuis un capteur du smartphone, stocker ces données dans Cassandra et enfin, analyser ces données pour prédire notre activité avec Spark.
Une démonstration sera faite pour monter la solution en temps réel.
Speaker
Amira LAKHAL est Java Champion et développeur chez Actyx. Elle est passionnée par l’agilité et les langages fonctionnels. Elle s’intéresse aussi aux problématiques de Big Data et diverses solutions de stockage des données volumineuses. Elle a animé des conférences sur la base NoSql Cassandra. Elle aide aussi à la mise en avant des femmes dans l’IT.
Meetup : https://www.meetup.com/JUGL-the-Java-User-Group-of-Lausanne/events/237954272/
YouTube : https://youtu.be/QIhK8OSamKM
12. @Miralak Lausanne JUG 23.03.201712
Objectif
La reconnaissance de l’activité physique en se basant sur les données d’un
capteur: l’accéléromètre
Activitées:
● Marcher,
● Courir,
● Debout,
● Assis.
➔ Utilisation pour des suivis médicaux
13. @Miralak Lausanne JUG 23.03.201713
Acceleromètre
➔ Capteur de mouvement
➔ Mesure l’accélération
➔ Usages multiples
14. @Miralak Lausanne JUG 23.03.201714
Acceleromètre
Chaque accélération contient:
● un timestamp (eg, 1428773040488)
● la force d’accélération selon l’axe x (unité m/s²)
● la force d’accélération selon l’axe y (unité m/s²)
● la force d’accélération selon l’axe z (unité m/s²)
20. @Miralak Lausanne JUG 23.03.201720
Historique
● Créé par Facebook
● Open-source en 2008
● current version 3.10
● Orienté colonne ☞ table distribuée
21. @Miralak Lausanne JUG 23.03.201721
Caractéristiques
● Scalabilité linéaire
● Master-less: peer to peer
● Simplicité opérationnelle
● Multi-datacenter
● No SPOF ☞ Haute disponibilité (≈100% up-time)
22. @Miralak Lausanne JUG 23.03.201722
Last Write Win
INSERT INTO users(login, name, age) VALUES (‘DuchessFr’ , ‘Duchess France’, ‘5’);
DuchessFr
name(t1) age(t1)
Duchess France 5
auto-generated timestamp
23. @Miralak Lausanne JUG 23.03.201723
Last Write Win
UPDATE users SET age = ‘6’ WHERE login=‘DuchessFr’ ;
DuchessFr
name(t1) age(t1)
Duchess
France
5
DuchessFr
age(t2)
6
SSTable1 SSTable2
24. @Miralak Lausanne JUG 23.03.201724
Last Write Win
DELETE age from users WHERE login=‘DuchessFr’ ;
DuchessFr
name(t1) age(t1)
Duchess
France
5
SSTable1
DuchessFr
age(t2)
6
SSTable2
DuchessFr
age(t3)
X
SSTable3
25. @Miralak Lausanne JUG 23.03.201725
Last Write Win
SELECT age from users WHERE login=‘DuchessFr’ ;
DuchessFr
name(t1) age(t1)
Duchess
France
5
DuchessFr
age(t2)
6
SSTable2
DuchessFr
age(t3)
X
SSTable3SSTable1 SSTable3
26. @Miralak Lausanne JUG 23.03.201726
Compaction
DuchessFr
name(t1) age(t1)
Duchess
France
5
DuchessFr
age(t2)
6
SSTable2
DuchessFr
age(t3)
X
SSTable3SSTable1
SSTable4
DuchessFr
name(t1) age(t3)
Duchess
France
27. @Miralak Lausanne JUG 23.03.201727
Timeseries avec Cassandra
Nous allons utiliser des données historiques
➢ Timeseries data model
L’utilisateur et le timestamp sont uniques
➢ Sauvegarder autant que nécessaire
28. @Miralak Lausanne JUG 23.03.201728
Timeseries data model
CREATE TABLE accelerometer (
user_id text,
timestamp bigint,
x double, y double, z double,
PRIMARY KEY (( user_id ),timestamp)
);
Clustering column
Partition key
29. @Miralak Lausanne JUG 23.03.201729
Vue logique
user-id timestamp x y z
TEST_USER 1447034733210 10.9 34.5 12.6
TEST_USER 1447034733344 15.4 39.9 16.3
TEST_USER 1447034733462 13.5 36.1 20.3
TEST_USER 1447034733556 13.0 41.3 22.8”
30. @Miralak Lausanne JUG 23.03.201730
Vue sur disque
TEST_USER
1447034733210 1447034733344 1447034733462
x Y Z x y z x y z
10.9 34.5 12.6 15.4 39.9 16.3 13.5 36.1 20.3
31. @Miralak Lausanne JUG 23.03.201731
Timeseries data model
CREATE TABLE accelerometer (
user_id text,
date text,
timestamp bigint,
x double, y double, z double,
PRIMARY KEY ((user_id, date),timestamp)
);
32. @Miralak Lausanne JUG 23.03.201732
Vue logique
user-id:date timestamp x y z
TEST_USER:
24-09-2015
1446034733566
10.9 34.5 12.6
TEST_USER:
24-09-2015
1446034733631 15.4 39.9 16.3
TEST_USER:
24-09-2015
1446034733740 13.5 36.1 20.3
TEST_USER:
24-09-2015
1446034733830 13.0 41.3 22.8
33. @Miralak Lausanne JUG 23.03.201733
Vue sur disque
TEST_USER
:24-09-2015
1446034733566 1446034733631 .. 1446034734120
x Y Z x y z .. x y z
10.9 34.5 12.6 15.4 39.9 16.3 .. 13.5 36.1 20.3
TEST_USER
:25-09-2015
1447034733210 1447034733300 .. 1447034733810
x Y Z x y z .. x y z
13.3 30.5, 12.1 25.2 34.9 16.1 .. 15.0 32.1 12.4
34. @Miralak Lausanne JUG 23.03.201734
Requêtage
Range queries ☞ Slice on disk
Select user_id, date, timestamp, x, y, z
From accelerometer
Where user_id =”TEST_USER”
and date = “24-04-2015”
and timestamp > “1447034733462” and timestamp < “1447034733890”
35. @Miralak Lausanne JUG 23.03.201735
Ordre d’aggrégation
CREATE TABLE latest_accelerations (
user_id text,
timestamp bigint,
x double, y double, z double,
PRIMARY KEY (user_id, timestamp)
WITH CLUSTERING ORDER BY (timestamp, DESC);
);
36. @Miralak Lausanne JUG 23.03.201736
Expiration: TTL
INSERT INTO latest_accelerations( user_id, timestamp, x, y, z )
VALUES ( ’TEST_USER’, 1447034733462, 10.9, 34.5, 12.6 )
USING TTL 20;
39. @Miralak Lausanne JUG 23.03.201739
Historique
● Créé par AMPLab
● Open-source en 2010
● Version actuelle 2.1.0
● Totalement écrit en Scala
● Calcul distribué en mémoire vive
40. @Miralak Lausanne JUG 23.03.201740
Ecosystème
Spark Core
Spark SQL
Spark
Streaming
ML lib GraphX
49. @Miralak Lausanne JUG 23.03.201749
Spark Cassandra Connector
● Open source
● Version 2.0
● Ecrit en Scala
● Charge les données de Cassandra vers Spark
● Ecrit les données de Spark vers Cassandra
https://github.com/datastax/spark-cassandra-connector
50. @Miralak Lausanne JUG 23.03.201750
Spark Cassandra Connector
Exposes les tables Cassandra comme des Spark RDD + Spark DStreams
c*
Cassandra
driver
Spark
Cassandra
Connector
Spark
51. @Miralak Lausanne JUG 23.03.2017
Setup Spark Connection
val sparkConf:SparkConf = new SparkConf()
.setAppName("activityRecognition")
.set("spark.cassandra.connection.host", "127.0.0.1"))
.set("spark.cassandra.connection.native.port", "9142")
.setMaster("local[*]");
val sc:SparkContext = new SparkContext(sparkConf);
51
52. @Miralak Lausanne JUG 23.03.2017
De Cassandra vers Spark
val cassandraRowsRDD:CassandraRDD[CassandraRow] =
new SparkContextFunctions(sc)
.cassandraTable("activityrecognition", "training");
val users:Array[String] = cassandraRowsRDD.select("user_id")
.distinct
.map(row => row.toMap)
.map(entry => entry.get("user_id"))
.collect();
52
54. @Miralak Lausanne JUG 23.03.2017
Spark Streaming
// declare a streaming context
val ssc = new StreamingContext(sparkConf, Durations.seconds(2))
val cassandraReceiver: ReceiverInputDStream[RDD[CassandraRow]] =
ssc.receiverStream(
new CassandraReceiver(StorageLevel.MEMORY_ONLY)) // custom Cassandra receiver
// The transformations are done here then print the receiver value
cassandraReceiver.map(rdd => computePrediction(model, rdd)).print(0)
ssc.start
ssc.awaitTermination // Wait for the computation to terminate
54
55. @Miralak Lausanne JUG 23.03.2017
Spark Streaming
class CassandraReceiver(storage: StorageLevel) extends
Receiver[RDD[CassandraRow]](storage: StorageLevel) with Logging{
override def onStart() = { // Start the thread that receives data over a connection
new Thread() {
override def run() = {
receive() //Read data from Cassandra
}
}.start()
}
override def onStop() = { //Nothing to do }
}
55
57. @Miralak Lausanne JUG 23.03.2017
Reconnaissance d’activité
Activités possibles: marcher, courir, debout ou assis
Mesures depuis un acceleromètre : timeseries
Classification d’un timeserie en des
classes d’activités physiques
Machine Learning
57
58. @Miralak Lausanne JUG 23.03.2017
Classification multiclasses
Labélisé un pattern inconnu à partir de patterns connus
Régression logistique Naive Bayes Random forest
58
60. @Miralak Lausanne JUG 23.03.2017
Apprentissage supervisé
features label
features label
features label
features label
Features Label
Features ?
Train
Predict
60
61. @Miralak Lausanne JUG 23.03.2017
Modèle prédictif
● Collecter les données labélisées
● Identifier les caractéristiques (features)
● Calculer les caractérisques
● Créer le modèle Random Forest et l’entrainer
● Prédire l’activité
61
68. @Miralak Lausanne JUG 23.03.2017
Identification des caractéristiques
Calculer la différence X < Y ou X > Y
68
69. @Miralak Lausanne JUG 23.03.2017
Identification des caractéristiques
Calculer l’amplitude entre les pics de Y
69
70. @Miralak Lausanne JUG 23.03.2017
Caractéristiques
● Accélération moyenne pour chaque axe
● La différence moyenne entre l’axe X et l’axe Y
● La variance (pour chaque axe)
● L’écart type (pour chaque axe) 1/n * ∑ (x - mean_x)
● La difference absolue moyenne (pour chaque axe)
● Le résultant moyen d’accélération 1/n * ∑ √(x² + y² + z²)
● L'amplitude moyenne pour l’axe des Y
70
71. @Miralak Lausanne JUG 23.03.2017
Calcul des caractéristiques
//Data comes from ReceiverStream
val accelerationData: RDD[Array[Double]] = data.map(row => row.toMap)
.map(row => Array(row.getOrElse("x",0), row.getOrElse("y",0) ,row.getOrElse("z",0))
.map(row => row.map(_.doubleValue()))
val vectorsXYZ: RDD[Vector] = accelerationData.map(Vectors.dense)
var summary: MultivariateStatisticalSummary = Statistics.colStats(data)
val mean: Array[Double] = summary.mean.toArray
val variance: Array[Double] = summary.variance.toArray
val difference: Double = feature.computeDifferenceBetweenAxes(mean)
//build LabeledPoint with an activity label
val labeledPoint:LabeledPoint = new LabeledPoint(label, Vectors.dense(features))
71
72. @Miralak Lausanne JUG 23.03.2017
Random Forest Model
// parameters
val categoricalFeaturesInfo: Map[Int, Int] = Map.empty
val numTree: Int = 10
val numClasses: Int = 4
val featureSubsetStrategy: String = "auto"//nb features for splits at each node
val impurity: String = "gini"
val maxDepth: Int = 20
val maxBins: Int = 32
val randomSeeds: Int = 12345 //Random seed for bootstrapping and choosing subsets
// create model
val model = RandomForest.trainClassifier(trainingData, numClasses,
categoricalFeaturesInfo, numTree, featureSubsetStrategy, impurity, maxDepth,
maxBins, randomSeeds)
model.save(sc, "randomForestModel")
72
73. @Miralak Lausanne JUG 23.03.2017
Précision
● 1.6 Mo mesures d’accélération
● 15 caractéristiques calculées
Random forest accuracy 92.3%
73
75. @Miralak Lausanne JUG 23.03.2017
Conclusion
● Collecter les données depuis un objet et l’analyser
● Les capteurs combinés?
● Une infinité de possibilité avec l’IOT